diff --git a/package.json b/package.json index f0547d2..02cc367 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "test:base": "mocha --require ts-node/register \"tests/src/**/*.ts\" --reporter dot --timeout 60000", "test": "npm run test:base", "test:nyc": "nyc --reporter=lcov npm run test:base", - "test:debug": "mocha --require ts-node/register/transpile-only --inspect \"tests/src/**/*.ts\" --reporter dot", + "test:debug": "mocha --require ts-node/register/transpile-only \"tests/src/**/*.ts\" --reporter dot", "update": "ts-node ./tools/update.ts && npm run eslint-fix && npm run test:nyc", "preversion": "npm test && npm run update && git add .", "version": "npm run eslint-fix && git add .", diff --git a/src/parser/convert.ts b/src/parser/convert.ts index 3b367c1..63b7113 100644 --- a/src/parser/convert.ts +++ b/src/parser/convert.ts @@ -11,45 +11,19 @@ import type { Token as AcornToken, tokTypes as AcornTokTypes } from "acorn" import { isStaticValueIdentifier } from "./validate" import { throwUnexpectedNodeError, throwUnexpectedTokenError } from "./errors" import { getAcorn } from "./modules/acorn" - -export type JSONSyntaxContext = { - trailingCommas: boolean - comments: boolean - // invalid JSON numbers - plusSigns: boolean - spacedSigns: boolean - leadingOrTrailingDecimalPoints: boolean - infinities: boolean - nans: boolean - numericSeparators: boolean - binaryNumericLiterals: boolean - octalNumericLiterals: boolean - legacyOctalNumericLiterals: boolean - invalidJsonNumbers: boolean - // statics - multilineStrings: boolean - unquoteProperties: boolean - singleQuotes: boolean - numberProperties: boolean - undefinedKeywords: boolean - sparseArrays: boolean - regExpLiterals: boolean - templateLiterals: boolean - bigintLiterals: boolean - unicodeCodepointEscapes: boolean - escapeSequenceInIdentifier: boolean - // JS-likes - // staticExpression: boolean -} +import type { JSONSyntaxContext } from "./syntax-context" export class TokenConvertor { + private readonly ctx: JSONSyntaxContext + private readonly code: string private readonly templateBuffer: AcornToken[] = [] private readonly tokTypes: typeof AcornTokTypes - public constructor(code: string) { + public constructor(ctx: JSONSyntaxContext, code: string) { + this.ctx = ctx this.code = code this.tokTypes = getAcorn().tokTypes } @@ -125,6 +99,12 @@ export class TokenConvertor { pattern: reValue.pattern, } value = `/${reValue.pattern}/${reValue.flags}` + } else if ( + this.ctx.parentheses && + (token.type === tokTypes.parenL || token.type === tokTypes.parenR) + ) { + type = "Punctuator" + value = this.code.slice(...token.range!) } else { // const key = Object.keys(tokTypes).find( // (k) => tokTypes[k] === token.type, diff --git a/src/parser/extend-parser.ts b/src/parser/extend-parser.ts index 5b7ac3c..e4d7732 100644 --- a/src/parser/extend-parser.ts +++ b/src/parser/extend-parser.ts @@ -1,5 +1,4 @@ import type { TokenStore } from "./token-store" -import type { JSONSyntaxContext } from "./validate" import { validateNode } from "./validate" import type { Parser, Options } from "acorn" import type { Comment } from "../types" @@ -7,6 +6,7 @@ import type { Node } from "estree" import { getAcorn } from "./modules/acorn" import { ParseError, throwUnexpectedCommentError } from "./errors" import { TokenConvertor } from "./convert" +import type { JSONSyntaxContext } from "./syntax-context" let parserCache: typeof Parser | undefined @@ -39,7 +39,7 @@ export function getParser(): typeof Parser { ) { super( ((): Options => { - const tokenConvertor = new TokenConvertor(code) + const tokenConvertor = new TokenConvertor(options.ctx, code) return { // do not use spread, because we don't want to pass any unknown options to acorn ecmaVersion: options.ecmaVersion, diff --git a/src/parser/parser.ts b/src/parser/parser.ts index 7f68898..ac27ce1 100644 --- a/src/parser/parser.ts +++ b/src/parser/parser.ts @@ -3,12 +3,12 @@ import type { AST, SourceCode } from "eslint" import type { ESPree } from "./modules/espree" import { getEspree } from "./modules/espree" import { getVisitorKeys } from "./visitor-keys" -import type { JSONSyntaxContext } from "./convert" import { convertProgramNode } from "./convert" import { TokenStore } from "./token-store" import type { JSONProgram } from "./ast" import { lte } from "semver" import { getParser } from "./extend-parser" +import type { JSONSyntaxContext } from "./syntax-context" const DEFAULT_ECMA_VERSION = 2019 @@ -99,6 +99,7 @@ function getJSONSyntaxContext(str?: string | null): JSONSyntaxContext { bigintLiterals: false, unicodeCodepointEscapes: false, escapeSequenceInIdentifier: false, + parentheses: false, } } if (upperCase === "JSONC") { @@ -126,6 +127,7 @@ function getJSONSyntaxContext(str?: string | null): JSONSyntaxContext { bigintLiterals: false, unicodeCodepointEscapes: false, escapeSequenceInIdentifier: false, + parentheses: false, } } if (upperCase === "JSON5") { @@ -153,6 +155,7 @@ function getJSONSyntaxContext(str?: string | null): JSONSyntaxContext { bigintLiterals: false, unicodeCodepointEscapes: false, escapeSequenceInIdentifier: false, + parentheses: false, } } return { @@ -179,6 +182,7 @@ function getJSONSyntaxContext(str?: string | null): JSONSyntaxContext { bigintLiterals: true, unicodeCodepointEscapes: true, escapeSequenceInIdentifier: true, + parentheses: true, } } diff --git a/src/parser/syntax-context.ts b/src/parser/syntax-context.ts new file mode 100644 index 0000000..e2b3c58 --- /dev/null +++ b/src/parser/syntax-context.ts @@ -0,0 +1,30 @@ +export type JSONSyntaxContext = { + trailingCommas: boolean + comments: boolean + // invalid JSON numbers + plusSigns: boolean + spacedSigns: boolean + leadingOrTrailingDecimalPoints: boolean + infinities: boolean + nans: boolean + numericSeparators: boolean + binaryNumericLiterals: boolean + octalNumericLiterals: boolean + legacyOctalNumericLiterals: boolean + invalidJsonNumbers: boolean + // statics + multilineStrings: boolean + unquoteProperties: boolean + singleQuotes: boolean + numberProperties: boolean + undefinedKeywords: boolean + sparseArrays: boolean + regExpLiterals: boolean + templateLiterals: boolean + bigintLiterals: boolean + unicodeCodepointEscapes: boolean + escapeSequenceInIdentifier: boolean + // JS-likes + parentheses: boolean + // staticExpression: boolean +} diff --git a/src/parser/validate.ts b/src/parser/validate.ts index 88a17f2..28bb9ce 100644 --- a/src/parser/validate.ts +++ b/src/parser/validate.ts @@ -27,6 +27,7 @@ import { requireFromCwd, requireFromLinter, } from "./modules/require-utils" +import type { JSONSyntaxContext } from "./syntax-context" const lineBreakPattern = /\r\n|[\n\r\u2028\u2029]/u const octalNumericLiteralPattern = /^0[Oo]/u @@ -73,36 +74,6 @@ function getCodePointEscapeMatcher(): eslintUtils.PatternMatcher { return cacheCodePointEscapeMatcher } -export type JSONSyntaxContext = { - trailingCommas: boolean - comments: boolean - // invalid JSON numbers - plusSigns: boolean - spacedSigns: boolean - leadingOrTrailingDecimalPoints: boolean - infinities: boolean - nans: boolean - numericSeparators: boolean - binaryNumericLiterals: boolean - octalNumericLiterals: boolean - legacyOctalNumericLiterals: boolean - invalidJsonNumbers: boolean - // statics - multilineStrings: boolean - unquoteProperties: boolean - singleQuotes: boolean - numberProperties: boolean - undefinedKeywords: boolean - sparseArrays: boolean - regExpLiterals: boolean - templateLiterals: boolean - bigintLiterals: boolean - unicodeCodepointEscapes: boolean - escapeSequenceInIdentifier: boolean - // JS-likes - // staticExpression: boolean -} - /** * Validate ES node */ diff --git a/tests/src/parser/errors.ts b/tests/src/parser/errors.ts index c491b26..c86e042 100644 --- a/tests/src/parser/errors.ts +++ b/tests/src/parser/errors.ts @@ -125,11 +125,11 @@ describe("Check that parsing error is correct.", () => { code: ` {method(){}} `, - message: "Unexpected token '('.", + message: "Unexpected token '{'.", lineNumber: 2, - column: 8, - index: 8, - char: "(", + column: 10, + index: 10, + char: "{", }, { code: ` @@ -145,31 +145,21 @@ describe("Check that parsing error is correct.", () => { code: ` {get foo(){}} `, - message: "Unexpected token '('.", + message: "Unexpected token '{'.", lineNumber: 2, - column: 9, - index: 9, - char: "(", + column: 11, + index: 11, + char: "{", }, { code: ` {set foo(p){}} `, - message: "Unexpected token '('.", - lineNumber: 2, - column: 9, - index: 9, - char: "(", - }, - { - code: ` -[('a')] -`, - message: "Unexpected token '('.", + message: "Unexpected token '{'.", lineNumber: 2, - column: 2, - index: 2, - char: "(", + column: 12, + index: 12, + char: "{", }, { code: ` @@ -185,11 +175,11 @@ describe("Check that parsing error is correct.", () => { code: ` [call()] `, - message: "Unexpected token '('.", + message: "Unexpected call expression.", lineNumber: 2, - column: 6, - index: 6, - char: "(", + column: 2, + index: 2, + char: "c", }, { code: ` @@ -255,11 +245,11 @@ typeof 123 code: ` +(+1) `, - message: "Unexpected token '('.", + message: "Unexpected unary expression.", lineNumber: 2, - column: 2, - index: 2, - char: "(", + column: 3, + index: 3, + char: "+", }, { code: ` diff --git a/tests/src/parser/syntaxes/json.ts b/tests/src/parser/syntaxes/json.ts index b46538d..ab69b13 100644 --- a/tests/src/parser/syntaxes/json.ts +++ b/tests/src/parser/syntaxes/json.ts @@ -111,6 +111,22 @@ describe("Check that parsing error is correct for JSON.", () => { index: 0, char: "0", }, + { + code: "(42)", + message: "Unexpected token '('.", + lineNumber: 1, + column: 1, + index: 0, + char: "(", + }, + { + code: "[('a')]", + message: "Unexpected token '('.", + lineNumber: 1, + column: 2, + index: 1, + char: "(", + }, ]) { it(`JSON parseForESLint error on ${JSON.stringify(code)}`, () => { const e = getParseError(code) diff --git a/tests/src/parser/syntaxes/json5.ts b/tests/src/parser/syntaxes/json5.ts index bb132af..529ea74 100644 --- a/tests/src/parser/syntaxes/json5.ts +++ b/tests/src/parser/syntaxes/json5.ts @@ -153,6 +153,22 @@ describe("Check that parsing error is correct for JSON5.", () => { index: 0, char: "0", }, + { + code: "(42)", + message: "Unexpected token '('.", + lineNumber: 1, + column: 1, + index: 0, + char: "(", + }, + { + code: "[('a')]", + message: "Unexpected token '('.", + lineNumber: 1, + column: 2, + index: 1, + char: "(", + }, ]) { it(`JSON5 parseForESLint error on ${JSON.stringify(code)}`, () => { const e = getParseError(code) diff --git a/tests/src/parser/syntaxes/jsonc.ts b/tests/src/parser/syntaxes/jsonc.ts index 7975692..8d87c72 100644 --- a/tests/src/parser/syntaxes/jsonc.ts +++ b/tests/src/parser/syntaxes/jsonc.ts @@ -145,6 +145,22 @@ describe("Check that parsing error is correct for JSONC.", () => { index: 0, char: "0", }, + { + code: "(42)", + message: "Unexpected token '('.", + lineNumber: 1, + column: 1, + index: 0, + char: "(", + }, + { + code: "[('a')]", + message: "Unexpected token '('.", + lineNumber: 1, + column: 2, + index: 1, + char: "(", + }, ]) { it(`JSONC parseForESLint error on ${JSON.stringify(code)}`, () => { const e = getParseError(code)