From 4a1e81be197f303405e3b9bcab5d46c8d9714ae5 Mon Sep 17 00:00:00 2001 From: WhitePiano Date: Sun, 19 May 2024 19:49:33 +0900 Subject: [PATCH 1/8] change mjs/mts files to always be parsed by ESM --- packages/typescript-estree/src/parser.ts | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/typescript-estree/src/parser.ts b/packages/typescript-estree/src/parser.ts index d724d81e379d..d15494710980 100644 --- a/packages/typescript-estree/src/parser.ts +++ b/packages/typescript-estree/src/parser.ts @@ -1,5 +1,6 @@ import debug from 'debug'; -import type * as ts from 'typescript'; +import path from 'path'; +import * as ts from 'typescript'; import { astConverter } from './ast-converter'; import { convertError } from './convert'; @@ -125,11 +126,28 @@ function parse( return ast; } +function isESM( + options: T | undefined, +): boolean { + const extension = path + .extname(options?.filePath ?? '') + .toLowerCase() as ts.Extension; + + return extension === ts.Extension.Mjs || extension === ts.Extension.Mts; +} + function parseWithNodeMapsInternal( code: ts.SourceFile | string, options: T | undefined, shouldPreserveNodeMaps: boolean, ): ParseWithNodeMapsResult { + /** + * Ensure that `mjs`/`mts` files are always treated as ESM + */ + if (typeof code === 'string' && isESM(options)) { + code += '\n' + 'export {}'; + } + /** * Reset the parse configuration */ From e98727bc475aa4ad6a1b185fa2566c134dafb7cc Mon Sep 17 00:00:00 2001 From: WhitePiano Date: Wed, 22 May 2024 13:04:34 +0900 Subject: [PATCH 2/8] revert wrong approach --- packages/typescript-estree/src/parser.ts | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/packages/typescript-estree/src/parser.ts b/packages/typescript-estree/src/parser.ts index d15494710980..d724d81e379d 100644 --- a/packages/typescript-estree/src/parser.ts +++ b/packages/typescript-estree/src/parser.ts @@ -1,6 +1,5 @@ import debug from 'debug'; -import path from 'path'; -import * as ts from 'typescript'; +import type * as ts from 'typescript'; import { astConverter } from './ast-converter'; import { convertError } from './convert'; @@ -126,28 +125,11 @@ function parse( return ast; } -function isESM( - options: T | undefined, -): boolean { - const extension = path - .extname(options?.filePath ?? '') - .toLowerCase() as ts.Extension; - - return extension === ts.Extension.Mjs || extension === ts.Extension.Mts; -} - function parseWithNodeMapsInternal( code: ts.SourceFile | string, options: T | undefined, shouldPreserveNodeMaps: boolean, ): ParseWithNodeMapsResult { - /** - * Ensure that `mjs`/`mts` files are always treated as ESM - */ - if (typeof code === 'string' && isESM(options)) { - code += '\n' + 'export {}'; - } - /** * Reset the parse configuration */ From 0027099d81673eadd465bfd52f82c32d163b560d Mon Sep 17 00:00:00 2001 From: WhitePiano Date: Wed, 22 May 2024 13:25:50 +0900 Subject: [PATCH 3/8] change mjs/mts files to always be parsed as ESM (using setExternalModuleIndicator) --- .../src/create-program/createSourceFile.ts | 1 + .../src/parseSettings/createParseSettings.ts | 21 +++++++++++++------ .../src/parseSettings/index.ts | 8 +++++++ .../typescript-estree/typings/typescript.d.ts | 2 +- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/packages/typescript-estree/src/create-program/createSourceFile.ts b/packages/typescript-estree/src/create-program/createSourceFile.ts index bb5bc9d7b8fc..096264c5844f 100644 --- a/packages/typescript-estree/src/create-program/createSourceFile.ts +++ b/packages/typescript-estree/src/create-program/createSourceFile.ts @@ -23,6 +23,7 @@ function createSourceFile(parseSettings: ParseSettings): ts.SourceFile { { languageVersion: ts.ScriptTarget.Latest, jsDocParsingMode: parseSettings.jsDocParsingMode, + setExternalModuleIndicator: parseSettings.setExternalModuleIndicator, }, /* setParentNodes */ true, getScriptKind(parseSettings.filePath, parseSettings.jsx), diff --git a/packages/typescript-estree/src/parseSettings/createParseSettings.ts b/packages/typescript-estree/src/parseSettings/createParseSettings.ts index b5d52ce569b8..4835fefab95d 100644 --- a/packages/typescript-estree/src/parseSettings/createParseSettings.ts +++ b/packages/typescript-estree/src/parseSettings/createParseSettings.ts @@ -1,4 +1,5 @@ import debug from 'debug'; +import path from 'path'; import * as ts from 'typescript'; import type { ProjectServiceSettings } from '../create-program/createProjectService'; @@ -46,6 +47,13 @@ export function createParseSettings( ? options.tsconfigRootDir : process.cwd(); const passedLoggerFn = typeof options.loggerFn === 'function'; + const filePath = ensureAbsolutePath( + typeof options.filePath === 'string' && options.filePath !== '' + ? options.filePath + : getFileName(options.jsx), + tsconfigRootDir, + ); + const extension = path.extname(filePath).toLowerCase() as ts.Extension; const jsDocParsingMode = ((): ts.JSDocParsingMode => { switch (options.jsDocParsingMode) { case 'all': @@ -96,12 +104,13 @@ export function createParseSettings( options.extraFileExtensions.every(ext => typeof ext === 'string') ? options.extraFileExtensions : [], - filePath: ensureAbsolutePath( - typeof options.filePath === 'string' && options.filePath !== '' - ? options.filePath - : getFileName(options.jsx), - tsconfigRootDir, - ), + filePath: filePath, + setExternalModuleIndicator: + extension === ts.Extension.Mjs || extension === ts.Extension.Mts + ? (file): void => { + file.externalModuleIndicator = true; + } + : undefined, jsDocParsingMode, jsx: options.jsx === true, loc: options.loc === true, diff --git a/packages/typescript-estree/src/parseSettings/index.ts b/packages/typescript-estree/src/parseSettings/index.ts index 3511e3be718d..c2a16d4805a5 100644 --- a/packages/typescript-estree/src/parseSettings/index.ts +++ b/packages/typescript-estree/src/parseSettings/index.ts @@ -93,6 +93,14 @@ export interface MutableParseSettings { */ filePath: string; + /** + * Sets the external module indicator on the source file. + * Used by Typescript to determine if a sourceFile is an external module. + * + * needed to always parsing `mjs`/`mts` files as ESM + */ + setExternalModuleIndicator?: (file: ts.SourceFile) => void; + /** * JSDoc parsing style to pass through to TypeScript */ diff --git a/packages/typescript-estree/typings/typescript.d.ts b/packages/typescript-estree/typings/typescript.d.ts index de19fdb5a447..bf67bca069a2 100644 --- a/packages/typescript-estree/typings/typescript.d.ts +++ b/packages/typescript-estree/typings/typescript.d.ts @@ -3,7 +3,7 @@ import 'typescript'; // these additions are marked as internal to typescript declare module 'typescript' { interface SourceFile { - externalModuleIndicator?: Node; + externalModuleIndicator?: Node | true; parseDiagnostics: DiagnosticWithLocation[]; } From 7172c055c3622b75c9e0bf3e0a7b0a104f40fbe3 Mon Sep 17 00:00:00 2001 From: WhitePiano Date: Mon, 16 Sep 2024 09:38:15 +0900 Subject: [PATCH 4/8] changed `sourceType` options to be imported from a config file --- packages/parser/src/parser.ts | 1 + .../src/parseSettings/createParseSettings.ts | 4 +++- packages/typescript-estree/src/parser-options.ts | 7 +++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/parser/src/parser.ts b/packages/parser/src/parser.ts index 6aa369134a07..f1aafb5b88b8 100644 --- a/packages/parser/src/parser.ts +++ b/packages/parser/src/parser.ts @@ -105,6 +105,7 @@ function parseForESLint( const parserOptions: TSESTreeOptions = {}; Object.assign(parserOptions, options, { + sourceType: options.sourceType, jsx: validateBoolean(options.ecmaFeatures.jsx), /** * Override errorOnTypeScriptSyntacticAndSemanticIssues and set it to false to prevent use from user config diff --git a/packages/typescript-estree/src/parseSettings/createParseSettings.ts b/packages/typescript-estree/src/parseSettings/createParseSettings.ts index 4835fefab95d..84da5e753a23 100644 --- a/packages/typescript-estree/src/parseSettings/createParseSettings.ts +++ b/packages/typescript-estree/src/parseSettings/createParseSettings.ts @@ -106,7 +106,9 @@ export function createParseSettings( : [], filePath: filePath, setExternalModuleIndicator: - extension === ts.Extension.Mjs || extension === ts.Extension.Mts + options.sourceType === 'module' || + (options.sourceType === undefined && extension === ts.Extension.Mjs) || + (options.sourceType === undefined && extension === ts.Extension.Mts) ? (file): void => { file.externalModuleIndicator = true; } diff --git a/packages/typescript-estree/src/parser-options.ts b/packages/typescript-estree/src/parser-options.ts index 8211f40f5446..02383bdefb14 100644 --- a/packages/typescript-estree/src/parser-options.ts +++ b/packages/typescript-estree/src/parser-options.ts @@ -2,6 +2,7 @@ import type { CacheDurationSeconds, DebugLevel, JSDocParsingMode, + SourceType, } from '@typescript-eslint/types'; import type * as ts from 'typescript'; @@ -12,6 +13,12 @@ import type { TSESTree, TSESTreeToTSNode, TSNode, TSToken } from './ts-estree'; ////////////////////////////////////////////////////////// interface ParseOptions { + /** + * Specify the `sourceType`. + * For more details, see https://github.com/typescript-eslint/typescript-eslint/pull/9121 + */ + sourceType?: SourceType; + /** * Prevents the parser from throwing an error if it receives an invalid AST from TypeScript. * This case only usually occurs when attempting to lint invalid code. From b34e53f70db2d71adc187da7e1a5356b957bb52f Mon Sep 17 00:00:00 2001 From: WhitePiano Date: Mon, 16 Sep 2024 14:03:48 +0900 Subject: [PATCH 5/8] write test code (`typescript-estree/tests/lib/parse.test.ts`) --- .../typescript-estree/tests/lib/parse.test.ts | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/packages/typescript-estree/tests/lib/parse.test.ts b/packages/typescript-estree/tests/lib/parse.test.ts index f973358d85a1..f6e6d8af30b1 100644 --- a/packages/typescript-estree/tests/lib/parse.test.ts +++ b/packages/typescript-estree/tests/lib/parse.test.ts @@ -340,6 +340,135 @@ describe('parseAndGenerateServices', () => { }); }); + describe('ESM parsing', () => { + describe('TLA(Top Level Await)', () => { + const config: TSESTreeOptions = { + EXPERIMENTAL_useProjectService: false, + comment: true, + tokens: true, + range: true, + loc: true, + }; + const code = 'await(1)'; + + const testParse = ({ + sourceType, + ext, + shouldAllowTLA = false, + }: { + sourceType?: 'module' | 'script'; + ext: '.js' | '.ts' | '.mjs' | '.mts'; + shouldAllowTLA?: boolean; + }): void => { + const ast = parser.parse(code, { + ...config, + sourceType, + filePath: `file${ext}`, + }); + const expressionType = ( + ast.body[0] as parser.TSESTree.ExpressionStatement + ).expression.type; + + it(`parse(): should ${ + shouldAllowTLA ? 'allow' : 'not allow' + } TLA for ${ext} file with sourceType = ${sourceType}`, () => { + expect(expressionType).toBe( + shouldAllowTLA + ? parser.AST_NODE_TYPES.AwaitExpression + : parser.AST_NODE_TYPES.CallExpression, + ); + }); + }; + const testParseAndGenerateServices = ({ + sourceType, + ext, + shouldAllowTLA = false, + }: { + sourceType?: 'module' | 'script'; + ext: '.js' | '.ts' | '.mjs' | '.mts'; + shouldAllowTLA?: boolean; + }): void => { + const result = parser.parseAndGenerateServices(code, { + ...config, + sourceType, + filePath: `file${ext}`, + project: './tsconfig.json', + }); + const expressionType = ( + result.ast.body[0] as parser.TSESTree.ExpressionStatement + ).expression.type; + + it(`parseAndGenerateServices(): should ${ + shouldAllowTLA ? 'allow' : 'not allow' + } TLA for ${ext} file with sourceType = ${sourceType}`, () => { + expect(expressionType).toBe( + shouldAllowTLA + ? parser.AST_NODE_TYPES.AwaitExpression + : parser.AST_NODE_TYPES.CallExpression, + ); + }); + }; + + testParse({ ext: '.js' }); + testParse({ ext: '.ts' }); + testParse({ ext: '.mjs', shouldAllowTLA: true }); + testParse({ ext: '.mts', shouldAllowTLA: true }); + + testParse({ sourceType: 'module', ext: '.js', shouldAllowTLA: true }); + testParse({ sourceType: 'module', ext: '.ts', shouldAllowTLA: true }); + testParse({ sourceType: 'module', ext: '.mjs', shouldAllowTLA: true }); + testParse({ sourceType: 'module', ext: '.mts', shouldAllowTLA: true }); + + testParse({ sourceType: 'script', ext: '.js' }); + testParse({ sourceType: 'script', ext: '.ts' }); + testParse({ sourceType: 'script', ext: '.mjs' }); + testParse({ sourceType: 'script', ext: '.mts' }); + + testParseAndGenerateServices({ ext: '.js' }); + testParseAndGenerateServices({ ext: '.ts' }); + testParseAndGenerateServices({ ext: '.mjs', shouldAllowTLA: true }); + testParseAndGenerateServices({ ext: '.mts', shouldAllowTLA: true }); + + testParseAndGenerateServices({ + sourceType: 'module', + ext: '.js', + shouldAllowTLA: true, + }); + testParseAndGenerateServices({ + sourceType: 'module', + ext: '.ts', + shouldAllowTLA: true, + }); + testParseAndGenerateServices({ + sourceType: 'module', + ext: '.mjs', + shouldAllowTLA: true, + }); + testParseAndGenerateServices({ + sourceType: 'module', + ext: '.mts', + shouldAllowTLA: true, + }); + + testParseAndGenerateServices({ + sourceType: 'script', + ext: '.js', + }); + testParseAndGenerateServices({ + sourceType: 'script', + ext: '.ts', + }); + testParseAndGenerateServices({ + sourceType: 'script', + ext: '.mjs', + }); + testParseAndGenerateServices({ + sourceType: 'script', + ext: '.mts', + }); + }); + }); + if (process.env.TYPESCRIPT_ESLINT_EXPERIMENTAL_TSSERVER !== 'true') { describe('invalid file error messages', () => { const PROJECT_DIR = resolve(FIXTURES_DIR, '../invalidFileErrors'); From d369ec8846cae1d5ac4bcff5d0bc257347077560 Mon Sep 17 00:00:00 2001 From: WhitePiano Date: Mon, 16 Sep 2024 17:01:36 +0900 Subject: [PATCH 6/8] change the test to simply not set the `project` property on the `parseAndGeneratedServices` test --- packages/typescript-estree/tests/lib/parse.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/typescript-estree/tests/lib/parse.test.ts b/packages/typescript-estree/tests/lib/parse.test.ts index f6e6d8af30b1..0f8d892f3b29 100644 --- a/packages/typescript-estree/tests/lib/parse.test.ts +++ b/packages/typescript-estree/tests/lib/parse.test.ts @@ -392,7 +392,6 @@ describe('parseAndGenerateServices', () => { ...config, sourceType, filePath: `file${ext}`, - project: './tsconfig.json', }); const expressionType = ( result.ast.body[0] as parser.TSESTree.ExpressionStatement From fa9d25b10e909be7b9eae54cdd680e5ef77d3824 Mon Sep 17 00:00:00 2001 From: WhitePiano Date: Mon, 30 Sep 2024 23:44:58 +0900 Subject: [PATCH 7/8] doc: update `TypesScript_ESTree`'s `ParseOptions` --- docs/packages/TypeScript_ESTree.mdx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/packages/TypeScript_ESTree.mdx b/docs/packages/TypeScript_ESTree.mdx index eb6a49c8a1bd..b5d22e3a08c5 100644 --- a/docs/packages/TypeScript_ESTree.mdx +++ b/docs/packages/TypeScript_ESTree.mdx @@ -31,6 +31,12 @@ Parses the given string of code with the options provided and returns an ESTree- ```ts interface ParseOptions { + /** + * Specify the `sourceType`. + * For more details, see https://github.com/typescript-eslint/typescript-eslint/pull/9121 + */ + sourceType?: SourceType; + /** * Prevents the parser from throwing an error if it receives an invalid AST from TypeScript. * This case only usually occurs when attempting to lint invalid code. From 3bdee134ea04664524c9e27bd5730664b060ffb5 Mon Sep 17 00:00:00 2001 From: WhitePiano Date: Wed, 2 Oct 2024 17:01:50 +0900 Subject: [PATCH 8/8] fix eslint error --- .../src/parseSettings/createParseSettings.ts | 5 +++-- packages/typescript-estree/src/parser-options.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/typescript-estree/src/parseSettings/createParseSettings.ts b/packages/typescript-estree/src/parseSettings/createParseSettings.ts index 971d87f7346d..35783e0acbe5 100644 --- a/packages/typescript-estree/src/parseSettings/createParseSettings.ts +++ b/packages/typescript-estree/src/parseSettings/createParseSettings.ts @@ -1,5 +1,6 @@ +import path from 'node:path'; + import debug from 'debug'; -import path from 'path'; import * as ts from 'typescript'; import type { ProjectServiceSettings } from '../create-program/createProjectService'; @@ -90,7 +91,7 @@ export function createParseSettings( tsestreeOptions.extraFileExtensions.every(ext => typeof ext === 'string') ? tsestreeOptions.extraFileExtensions : [], - filePath: filePath, + filePath, setExternalModuleIndicator: tsestreeOptions.sourceType === 'module' || (tsestreeOptions.sourceType === undefined && diff --git a/packages/typescript-estree/src/parser-options.ts b/packages/typescript-estree/src/parser-options.ts index 832868286d0b..44af134611c2 100644 --- a/packages/typescript-estree/src/parser-options.ts +++ b/packages/typescript-estree/src/parser-options.ts @@ -2,8 +2,8 @@ import type { CacheDurationSeconds, DebugLevel, JSDocParsingMode, - SourceType, ProjectServiceOptions, + SourceType, } from '@typescript-eslint/types'; import type * as ts from 'typescript'; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy