From 868c4498ba13fd11d408242541f3436044a9d3e3 Mon Sep 17 00:00:00 2001 From: azu Date: Sun, 8 Jan 2023 19:32:08 +0900 Subject: [PATCH] refactor: convert to TypeScript (#7) ## Breaking Changes - Add `SourceRange`, `SourcePosition`, `SourceLocation` as types - Remove `SourceLocation` and `SourcePosition` class. It becomes just object - Make `source.indice` private ## Features - Support TypeScript - You can remove `@types/structured-source` fix #5 --- README.md | 11 +-- package-lock.json | 14 +-- package.json | 6 +- src/index.js | 29 ------ ...uctured-source.js => structured-source.ts} | 58 ++++++------ test/{source.js => structured-source.test.ts} | 90 ++++++++++--------- test/tsconfig.json | 2 +- tsconfig.json | 3 +- 8 files changed, 97 insertions(+), 116 deletions(-) delete mode 100644 src/index.js rename src/{structured-source.js => structured-source.ts} (70%) rename test/{source.js => structured-source.test.ts} (51%) diff --git a/README.md b/README.md index cab8c8f..0b4952a 100644 --- a/README.md +++ b/README.md @@ -14,24 +14,25 @@ npm install structured-source ## Usage ```js -const StructuredSource = require('structured-source'); +import assert from "node:assert"; +import { StructuredSource } from "structured-source"; -let src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n'); +const src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n'); // positionToIndex({ line: number, column: number) -> number assert(src.positionToIndex({ line: 1, column: 2 }) === 2); // indexToPosition(number) -> { line: number, column: number } -assert.deepEqual(src.indexToPosition(2), { line: 1, column: 2 }); +assert.deepStrictEqual(src.indexToPosition(2), { line: 1, column: 2 }); // rangeToLocation([ number, number ]) -> { start: { line: number, column: number}, end: { line: number, column: number } } -assert.deepEqual(src.rangeToLocation([0, 2]), { +assert.deepStrictEqual(src.rangeToLocation([0, 2]), { start: { line: 1, column: 0 }, end: { line: 1, column: 2 } }); // locationToRange({ start: { line: number, column: number}, end: { line: number, column: number } }) -> [ number, number ] -assert.deepEqual(src.locationToRange({ +assert.deepStrictEqual(src.locationToRange({ start: { line: 1, column: 0 }, end: { line: 1, column: 2 } }), [0, 2]); diff --git a/package-lock.json b/package-lock.json index e1dbe7c..ef673e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "3.0.2", "license": "BSD-2-Clause", "dependencies": { - "boundary": "^1.0.1" + "boundary": "^2.0.0" }, "devDependencies": { "@types/mocha": "^10.0.1", @@ -300,9 +300,9 @@ } }, "node_modules/boundary": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/boundary/-/boundary-1.0.1.tgz", - "integrity": "sha512-AaLhxHwYVh55iOTJncV3DE5o7RakEUSSj64XXEWRTiIhlp7aDI8qR0vY/k8Uw0Z234VjZi/iG/WxfrvqYPUCww==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/boundary/-/boundary-2.0.0.tgz", + "integrity": "sha512-rJKn5ooC9u8q13IMCrW0RSp31pxBCHE3y9V/tp3TdWSLf8Em3p6Di4NBpfzbJge9YjjFEsD0RtFEjtvHL5VyEA==" }, "node_modules/brace-expansion": { "version": "2.0.1", @@ -1677,9 +1677,9 @@ "dev": true }, "boundary": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/boundary/-/boundary-1.0.1.tgz", - "integrity": "sha512-AaLhxHwYVh55iOTJncV3DE5o7RakEUSSj64XXEWRTiIhlp7aDI8qR0vY/k8Uw0Z234VjZi/iG/WxfrvqYPUCww==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/boundary/-/boundary-2.0.0.tgz", + "integrity": "sha512-rJKn5ooC9u8q13IMCrW0RSp31pxBCHE3y9V/tp3TdWSLf8Em3p6Di4NBpfzbJge9YjjFEsD0RtFEjtvHL5VyEA==" }, "brace-expansion": { "version": "2.0.1", diff --git a/package.json b/package.json index 233e696..904ee00 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,8 @@ "web": "http://github.com/Constellation" } ], - "main": "lib/index.js", - "types": "lib/index.d.ts", + "main": "lib/structured-source.js", + "types": "lib/structured-source.d.ts", "files": [ "lib" ], @@ -38,7 +38,7 @@ "watch": "tsc -p . --watch" }, "dependencies": { - "boundary": "^1.0.1" + "boundary": "^2.0.0" }, "devDependencies": { "@types/mocha": "^10.0.1", diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 69b8413..0000000 --- a/src/index.js +++ /dev/null @@ -1,29 +0,0 @@ -/* - Copyright (C) 2014 Yusuke Suzuki - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -import StructuredSource from './structured-source.js' - -export default StructuredSource; - -/* vim: set sw=4 ts=4 et tw=80 : */ diff --git a/src/structured-source.js b/src/structured-source.ts similarity index 70% rename from src/structured-source.js rename to src/structured-source.ts index 25136ed..a3926f2 100644 --- a/src/structured-source.js +++ b/src/structured-source.ts @@ -24,33 +24,31 @@ import { upperBound } from 'boundary'; -export class Position { - constructor(line, column) { - this.line = line; - this.column = column; - } +export type SourceRange = readonly [number, number]; +export type SourcePosition = { + readonly line: number, + readonly column: number, } -export class SourceLocation { - constructor(start, end) { - this.start = start; - this.end = end; - } +export type SourceLocation = { + readonly end: SourcePosition, + readonly start: SourcePosition, } /** * StructuredSource - * @class */ -export default class StructuredSource { +export class StructuredSource { + private readonly indice: number[]; + /** * @constructs StructuredSource * @param {string} source - source code text. */ - constructor(source) { - this.indice = [ 0 ]; + constructor(source: string) { + this.indice = [0]; let regexp = /[\r\n\u2028\u2029]/g; - let length = source.length; + const length = source.length; regexp.lastIndex = 0; while (true) { let result = regexp.exec(source); @@ -59,7 +57,7 @@ export default class StructuredSource { } let index = result.index; if (source.charCodeAt(index) === 0x0D /* '\r' */ && - source.charCodeAt(index + 1) === 0x0A /* '\n' */) { + source.charCodeAt(index + 1) === 0x0A /* '\n' */) { index += 1; } let nextIndex = index + 1; @@ -73,7 +71,7 @@ export default class StructuredSource { } } - get line() { + get line(): number { return this.indice.length; } @@ -81,23 +79,26 @@ export default class StructuredSource { * @param {SourceLocation} loc - location indicator. * @return {[ number, number ]} range. */ - locationToRange(loc) { - return [ this.positionToIndex(loc.start), this.positionToIndex(loc.end) ]; + locationToRange(loc: SourceLocation): SourceRange { + return [this.positionToIndex(loc.start), this.positionToIndex(loc.end)]; } /** * @param {[ number, number ]} range - pair of indice. * @return {SourceLocation} location. */ - rangeToLocation(range) { - return new SourceLocation(this.indexToPosition(range[0]), this.indexToPosition(range[1])); + rangeToLocation(range: SourceRange): SourceLocation { + return { + start: this.indexToPosition(range[0]), + end: this.indexToPosition(range[1]) + }; } /** - * @param {Position} pos - position indicator. + * @param {SourcePosition} pos - position indicator. * @return {number} index. */ - positionToIndex(pos) { + positionToIndex(pos: SourcePosition): number { // Line number starts with 1. // Column number starts with 0. let start = this.indice[pos.line - 1]; @@ -106,11 +107,14 @@ export default class StructuredSource { /** * @param {number} index - index to the source code. - * @return {Position} position. + * @return {SourcePosition} position. */ - indexToPosition(index) { - let startLine = upperBound(this.indice, index); - return new Position(startLine, index - this.indice[startLine - 1]); + indexToPosition(index: number): SourcePosition { + const startLine = upperBound(this.indice, index); + return { + line: startLine, + column: index - this.indice[startLine - 1] + }; } } diff --git a/test/source.js b/test/structured-source.test.ts similarity index 51% rename from test/source.js rename to test/structured-source.test.ts index 696058e..366f5eb 100644 --- a/test/source.js +++ b/test/structured-source.test.ts @@ -22,51 +22,57 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import StructuredSource from '../src/index.js' +import { StructuredSource } from '../src/structured-source' import assert from 'assert' describe('StructuredSource', () => { it('constructor', () => { { - let src = new StructuredSource(''); - assert.deepEqual(src.indice, [ 0 ]); - assert(src.line === 1); + const src = new StructuredSource(''); + // @ts-expect-error: for testing + assert.deepStrictEqual(src.indice, [0]); + assert.strictEqual(src.line , 1); } { - let src = new StructuredSource('\n'); - assert.deepEqual(src.indice, [ 0, 1 ]); - assert(src.line === 2); + const src = new StructuredSource('\n'); + // @ts-expect-error: for testing + assert.deepStrictEqual(src.indice, [0, 1]); + assert.strictEqual(src.line , 2); } { - let src = new StructuredSource('\r\n'); - assert.deepEqual(src.indice, [ 0, 2 ]); - assert(src.line === 2); + const src = new StructuredSource('\r\n'); + // @ts-expect-error: for testing + assert.deepStrictEqual(src.indice, [0, 2]); + assert.strictEqual(src.line , 2); } { - let src = new StructuredSource('\n\r'); - assert.deepEqual(src.indice, [ 0, 1, 2 ]); - assert(src.line === 3); + const src = new StructuredSource('\n\r'); + // @ts-expect-error: for testing + assert.deepStrictEqual(src.indice, [0, 1, 2]); + assert.strictEqual(src.line , 3); } { - let src = new StructuredSource('aaa\naaaa\raaaaa'); - assert.deepEqual(src.indice, [ 0, 4, 9 ]); - assert(src.line === 3); + const src = new StructuredSource('aaa\naaaa\raaaaa'); + // @ts-expect-error: for testing + assert.deepStrictEqual(src.indice, [0, 4, 9]); + assert.strictEqual(src.line , 3); } { - let src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n'); - assert.deepEqual(src.indice, [ 0, 4, 9, 15 ]); - assert(src.line === 4); + const src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n'); + // @ts-expect-error: for testing + assert.deepStrictEqual(src.indice, [0, 4, 9, 15]); + assert.strictEqual(src.line , 4); } }); it('positionToIndex', () => { { - let src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n'); + const src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n'); assert(src.positionToIndex({ line: 1, column: 2 }) === 2); assert(src.positionToIndex({ line: 2, column: 2 }) === 6); assert(src.positionToIndex({ line: 2, column: 5 }) === 9); // out of source column is calculated. @@ -79,39 +85,39 @@ describe('StructuredSource', () => { it('indexToPosition', () => { { - let src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n'); - assert.deepEqual(src.indexToPosition(2), { line: 1, column: 2 }); - assert.deepEqual(src.indexToPosition(6), { line: 2, column: 2 }); - assert.deepEqual(src.indexToPosition(9), { line: 3, column: 0 }); - assert.deepEqual(src.indexToPosition(15), { line: 4, column: 0 }); - assert.deepEqual(src.indexToPosition(25), { line: 4, column: 10 }); - assert.deepEqual(src.indexToPosition(30), { line: 4, column: 15 }); - assert.deepEqual(src.indexToPosition(0), { line: 1, column: 0 }); + const src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n'); + assert.deepStrictEqual(src.indexToPosition(2), { line: 1, column: 2 }); + assert.deepStrictEqual(src.indexToPosition(6), { line: 2, column: 2 }); + assert.deepStrictEqual(src.indexToPosition(9), { line: 3, column: 0 }); + assert.deepStrictEqual(src.indexToPosition(15), { line: 4, column: 0 }); + assert.deepStrictEqual(src.indexToPosition(25), { line: 4, column: 10 }); + assert.deepStrictEqual(src.indexToPosition(30), { line: 4, column: 15 }); + assert.deepStrictEqual(src.indexToPosition(0), { line: 1, column: 0 }); } { - let src = new StructuredSource(''); - assert.deepEqual(src.indexToPosition(2), { line: 1, column: 2 }); - assert.deepEqual(src.indexToPosition(6), { line: 1, column: 6 }); - assert.deepEqual(src.indexToPosition(0), { line: 1, column: 0 }); + const src = new StructuredSource(''); + assert.deepStrictEqual(src.indexToPosition(2), { line: 1, column: 2 }); + assert.deepStrictEqual(src.indexToPosition(6), { line: 1, column: 6 }); + assert.deepStrictEqual(src.indexToPosition(0), { line: 1, column: 0 }); } }); it('rangeToLocation', () => { { - let src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n'); - assert.deepEqual(src.rangeToLocation([0, 2]), { + const src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n'); + assert.deepStrictEqual(src.rangeToLocation([0, 2]), { start: { line: 1, column: 0 }, end: { line: 1, column: 2 } }); - assert.deepEqual(src.rangeToLocation([0, 45]), { + assert.deepStrictEqual(src.rangeToLocation([0, 45]), { start: { line: 1, column: 0 }, end: { line: 4, column: 30 } }); } { - let src = new StructuredSource(''); - assert.deepEqual(src.rangeToLocation([0, 2]), { + const src = new StructuredSource(''); + assert.deepStrictEqual(src.rangeToLocation([0, 2]), { start: { line: 1, column: 0 }, end: { line: 1, column: 2 } }); @@ -120,19 +126,19 @@ describe('StructuredSource', () => { it('locationToRange', () => { { - let src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n'); - assert.deepEqual(src.locationToRange({ + const src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n'); + assert.deepStrictEqual(src.locationToRange({ start: { line: 1, column: 0 }, end: { line: 1, column: 2 } }), [0, 2]); - assert.deepEqual(src.locationToRange({ + assert.deepStrictEqual(src.locationToRange({ start: { line: 1, column: 0 }, end: { line: 4, column: 30 } }), [0, 45]); } { - let src = new StructuredSource(''); - assert.deepEqual(src.locationToRange({ + const src = new StructuredSource(''); + assert.deepStrictEqual(src.locationToRange({ start: { line: 1, column: 0 }, end: { line: 1, column: 2 } }), [0, 2]); diff --git a/test/tsconfig.json b/test/tsconfig.json index cb359a9..e71da16 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -8,4 +8,4 @@ "../src/**/*", "./**/*" ] -} \ No newline at end of file +} diff --git a/tsconfig.json b/tsconfig.json index 6e59769..ef980e6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,8 +19,7 @@ "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "allowJs": true + "noFallthroughCasesInSwitch": true }, "include": [ "src/**/*"