From 5eb64255698f3143150cfab0816c47d5d7fc70d9 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 2 Jun 2022 14:13:24 -0700 Subject: [PATCH 1/4] Expose import mode calculation functions --- src/compiler/program.ts | 35 +++++++++++++++---- .../reference/api/tsserverlibrary.d.ts | 33 +++++++++++++++++ tests/baselines/reference/api/typescript.d.ts | 33 +++++++++++++++++ 3 files changed, 94 insertions(+), 7 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index aa106a618731e..19db30ac7155e 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -536,19 +536,32 @@ namespace ts { return resolutions; } - /* @internal */ - interface SourceFileImportsList { - imports: SourceFile["imports"]; - moduleAugmentations: SourceFile["moduleAugmentations"]; + /** + * Subset of a SourceFile used to calculate index-based resolutions + * This includes some internal fields, so unless you have very good reason, + * (and are willing to use some less stable internals) you should probably just pass a SourceFile. + */ + export interface SourceFileImportsList { + /* @internal */ imports: SourceFile["imports"]; + /* @internal */ moduleAugmentations: SourceFile["moduleAugmentations"]; impliedNodeFormat?: SourceFile["impliedNodeFormat"]; }; - /* @internal */ + /** + * Calculates the resulting resolution mode for some reference in some file - this is generally the explicitly + * provided resolution mode in the reference, unless one is not present, in which case it is the mode of the containing file. + */ export function getModeForFileReference(ref: FileReference | string, containingFileMode: SourceFile["impliedNodeFormat"]) { return (isString(ref) ? containingFileMode : ref.resolutionMode) || containingFileMode; } - /* @internal */ + /** + * Calculates the final resolution mode for an import at some index within a file's imports list. This is generally the explicitly + * defined mode of the import if provided, or, if not, the mode of the containing file (with some exceptions: import=require is always commonjs, dynamic import is always esm). + * If you have an actual import node, prefer using getModeForUsageLocation on the reference string node. + * @param file File to fetch the resolution mode within + * @param index Index into the file's complete resolution list to get the resolution of - this is a concatenation of the file's imports and module augmentations + */ export function getModeForResolutionAtIndex(file: SourceFileImportsList, index: number) { if (file.impliedNodeFormat === undefined) return undefined; // we ensure all elements of file.imports and file.moduleAugmentations have the relevant parent pointers set during program setup, @@ -567,7 +580,15 @@ namespace ts { return false; } - /* @internal */ + /** + * Calculates the final resolution mode for a given module reference node. This is generally the explicitly provided resolution mode, if + * one exists, or the mode of the containing source file. (Excepting import=require, which is always commonjs, and dynamic import, which is always esm). + * Notably, this function always returns `undefined` if the containing file has an `undefined` `impliedNodeFormat` - this field is only set when + * `moduleResolution` is `node16`+. + * @param file The file the import or import-like reference is contained within + * @param usage The module reference string + * @returns The final resolution mode of the import + */ export function getModeForUsageLocation(file: {impliedNodeFormat?: SourceFile["impliedNodeFormat"]}, usage: StringLiteralLike) { if (file.impliedNodeFormat === undefined) return undefined; if ((isImportDeclaration(usage.parent) || isExportDeclaration(usage.parent))) { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index ab7ae0cde4997..295206967e49b 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -5102,6 +5102,39 @@ declare namespace ts { export function formatDiagnostic(diagnostic: Diagnostic, host: FormatDiagnosticsHost): string; export function formatDiagnosticsWithColorAndContext(diagnostics: readonly Diagnostic[], host: FormatDiagnosticsHost): string; export function flattenDiagnosticMessageText(diag: string | DiagnosticMessageChain | undefined, newLine: string, indent?: number): string; + /** + * Subset of a SourceFile used to calculate index-based resolutions + * This includes some internal fields, so unless you have very good reason, + * (and are willing to use some less stable internals) you should probably just pass a SourceFile. + */ + export interface SourceFileImportsList { + impliedNodeFormat?: SourceFile["impliedNodeFormat"]; + } + /** + * Calculates the resulting resolution mode for some reference in some file - this is generally the explicitly + * provided resolution mode in the reference, unless one is not present, in which case it is the mode of the containing file. + */ + export function getModeForFileReference(ref: FileReference | string, containingFileMode: SourceFile["impliedNodeFormat"]): ModuleKind.CommonJS | ModuleKind.ESNext | undefined; + /** + * Calculates the final resolution mode for an import at some index within a file's imports list. This is generally the explicitly + * defined mode of the import if provided, or, if not, the mode of the containing file (with some exceptions: import=require is always commonjs, dynamic import is always esm). + * If you have an actual import node, prefer using getModeForUsageLocation on the reference string node. + * @param file File to fetch the resolution mode within + * @param index Index into the file's complete resolution list to get the resolution of - this is a concatenation of the file's imports and module augmentations + */ + export function getModeForResolutionAtIndex(file: SourceFileImportsList, index: number): ModuleKind.CommonJS | ModuleKind.ESNext | undefined; + /** + * Calculates the final resolution mode for a given module reference node. This is generally the explicitly provided resolution mode, if + * one exists, or the mode of the containing source file. (Excepting import=require, which is always commonjs, and dynamic import, which is always esm). + * Notably, this function always returns `undefined` if the containing file has an `undefined` `impliedNodeFormat` - this field is only set when + * `moduleResolution` is `node16`+. + * @param file The file the import or import-like reference is contained within + * @param usage The module reference string + * @returns The final resolution mode of the import + */ + export function getModeForUsageLocation(file: { + impliedNodeFormat?: SourceFile["impliedNodeFormat"]; + }, usage: StringLiteralLike): ModuleKind.CommonJS | ModuleKind.ESNext | undefined; export function getConfigFileParsingDiagnostics(configFileParseResult: ParsedCommandLine): readonly Diagnostic[]; /** * A function for determining if a given file is esm or cjs format, assuming modern node module resolution rules, as configured by the diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 658786b63f18c..786e4bd91757c 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -5102,6 +5102,39 @@ declare namespace ts { export function formatDiagnostic(diagnostic: Diagnostic, host: FormatDiagnosticsHost): string; export function formatDiagnosticsWithColorAndContext(diagnostics: readonly Diagnostic[], host: FormatDiagnosticsHost): string; export function flattenDiagnosticMessageText(diag: string | DiagnosticMessageChain | undefined, newLine: string, indent?: number): string; + /** + * Subset of a SourceFile used to calculate index-based resolutions + * This includes some internal fields, so unless you have very good reason, + * (and are willing to use some less stable internals) you should probably just pass a SourceFile. + */ + export interface SourceFileImportsList { + impliedNodeFormat?: SourceFile["impliedNodeFormat"]; + } + /** + * Calculates the resulting resolution mode for some reference in some file - this is generally the explicitly + * provided resolution mode in the reference, unless one is not present, in which case it is the mode of the containing file. + */ + export function getModeForFileReference(ref: FileReference | string, containingFileMode: SourceFile["impliedNodeFormat"]): ModuleKind.CommonJS | ModuleKind.ESNext | undefined; + /** + * Calculates the final resolution mode for an import at some index within a file's imports list. This is generally the explicitly + * defined mode of the import if provided, or, if not, the mode of the containing file (with some exceptions: import=require is always commonjs, dynamic import is always esm). + * If you have an actual import node, prefer using getModeForUsageLocation on the reference string node. + * @param file File to fetch the resolution mode within + * @param index Index into the file's complete resolution list to get the resolution of - this is a concatenation of the file's imports and module augmentations + */ + export function getModeForResolutionAtIndex(file: SourceFileImportsList, index: number): ModuleKind.CommonJS | ModuleKind.ESNext | undefined; + /** + * Calculates the final resolution mode for a given module reference node. This is generally the explicitly provided resolution mode, if + * one exists, or the mode of the containing source file. (Excepting import=require, which is always commonjs, and dynamic import, which is always esm). + * Notably, this function always returns `undefined` if the containing file has an `undefined` `impliedNodeFormat` - this field is only set when + * `moduleResolution` is `node16`+. + * @param file The file the import or import-like reference is contained within + * @param usage The module reference string + * @returns The final resolution mode of the import + */ + export function getModeForUsageLocation(file: { + impliedNodeFormat?: SourceFile["impliedNodeFormat"]; + }, usage: StringLiteralLike): ModuleKind.CommonJS | ModuleKind.ESNext | undefined; export function getConfigFileParsingDiagnostics(configFileParseResult: ParsedCommandLine): readonly Diagnostic[]; /** * A function for determining if a given file is esm or cjs format, assuming modern node module resolution rules, as configured by the From 1f907ae6409aa1de8f9b06b9a587559cb24f3138 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Fri, 3 Jun 2022 00:25:31 +0000 Subject: [PATCH 2/4] Make `SourceFileImportsList` internal again. --- src/compiler/program.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 19db30ac7155e..a8b1066a9a25c 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -540,6 +540,8 @@ namespace ts { * Subset of a SourceFile used to calculate index-based resolutions * This includes some internal fields, so unless you have very good reason, * (and are willing to use some less stable internals) you should probably just pass a SourceFile. + * + * @internal */ export interface SourceFileImportsList { /* @internal */ imports: SourceFile["imports"]; @@ -562,7 +564,10 @@ namespace ts { * @param file File to fetch the resolution mode within * @param index Index into the file's complete resolution list to get the resolution of - this is a concatenation of the file's imports and module augmentations */ - export function getModeForResolutionAtIndex(file: SourceFileImportsList, index: number) { + export function getModeForResolutionAtIndex(file: SourceFile, index: number): ModuleKind.CommonJS | ModuleKind.ESNext | undefined; + /** @internal */ + export function getModeForResolutionAtIndex(file: SourceFileImportsList, index: number): ModuleKind.CommonJS | ModuleKind.ESNext | undefined; + export function getModeForResolutionAtIndex(file: SourceFileImportsList, index: number): ModuleKind.CommonJS | ModuleKind.ESNext | undefined { if (file.impliedNodeFormat === undefined) return undefined; // we ensure all elements of file.imports and file.moduleAugmentations have the relevant parent pointers set during program setup, // so it's safe to use them even pre-bind From 4e40185fe80a296a3f7b12d59b1e0ab14cfb13e2 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Fri, 3 Jun 2022 00:25:45 +0000 Subject: [PATCH 3/4] Accepted API baselines. --- tests/baselines/reference/api/tsserverlibrary.d.ts | 10 +--------- tests/baselines/reference/api/typescript.d.ts | 10 +--------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 295206967e49b..62c0b481fb22b 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -5102,14 +5102,6 @@ declare namespace ts { export function formatDiagnostic(diagnostic: Diagnostic, host: FormatDiagnosticsHost): string; export function formatDiagnosticsWithColorAndContext(diagnostics: readonly Diagnostic[], host: FormatDiagnosticsHost): string; export function flattenDiagnosticMessageText(diag: string | DiagnosticMessageChain | undefined, newLine: string, indent?: number): string; - /** - * Subset of a SourceFile used to calculate index-based resolutions - * This includes some internal fields, so unless you have very good reason, - * (and are willing to use some less stable internals) you should probably just pass a SourceFile. - */ - export interface SourceFileImportsList { - impliedNodeFormat?: SourceFile["impliedNodeFormat"]; - } /** * Calculates the resulting resolution mode for some reference in some file - this is generally the explicitly * provided resolution mode in the reference, unless one is not present, in which case it is the mode of the containing file. @@ -5122,7 +5114,7 @@ declare namespace ts { * @param file File to fetch the resolution mode within * @param index Index into the file's complete resolution list to get the resolution of - this is a concatenation of the file's imports and module augmentations */ - export function getModeForResolutionAtIndex(file: SourceFileImportsList, index: number): ModuleKind.CommonJS | ModuleKind.ESNext | undefined; + export function getModeForResolutionAtIndex(file: SourceFile, index: number): ModuleKind.CommonJS | ModuleKind.ESNext | undefined; /** * Calculates the final resolution mode for a given module reference node. This is generally the explicitly provided resolution mode, if * one exists, or the mode of the containing source file. (Excepting import=require, which is always commonjs, and dynamic import, which is always esm). diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 786e4bd91757c..61df249f1308e 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -5102,14 +5102,6 @@ declare namespace ts { export function formatDiagnostic(diagnostic: Diagnostic, host: FormatDiagnosticsHost): string; export function formatDiagnosticsWithColorAndContext(diagnostics: readonly Diagnostic[], host: FormatDiagnosticsHost): string; export function flattenDiagnosticMessageText(diag: string | DiagnosticMessageChain | undefined, newLine: string, indent?: number): string; - /** - * Subset of a SourceFile used to calculate index-based resolutions - * This includes some internal fields, so unless you have very good reason, - * (and are willing to use some less stable internals) you should probably just pass a SourceFile. - */ - export interface SourceFileImportsList { - impliedNodeFormat?: SourceFile["impliedNodeFormat"]; - } /** * Calculates the resulting resolution mode for some reference in some file - this is generally the explicitly * provided resolution mode in the reference, unless one is not present, in which case it is the mode of the containing file. @@ -5122,7 +5114,7 @@ declare namespace ts { * @param file File to fetch the resolution mode within * @param index Index into the file's complete resolution list to get the resolution of - this is a concatenation of the file's imports and module augmentations */ - export function getModeForResolutionAtIndex(file: SourceFileImportsList, index: number): ModuleKind.CommonJS | ModuleKind.ESNext | undefined; + export function getModeForResolutionAtIndex(file: SourceFile, index: number): ModuleKind.CommonJS | ModuleKind.ESNext | undefined; /** * Calculates the final resolution mode for a given module reference node. This is generally the explicitly provided resolution mode, if * one exists, or the mode of the containing source file. (Excepting import=require, which is always commonjs, and dynamic import, which is always esm). From 0de4ac1f9b6c8294d18023f5cdd404149b539da0 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Fri, 3 Jun 2022 00:44:52 +0000 Subject: [PATCH 4/4] Fix lints. --- src/compiler/program.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index a8b1066a9a25c..a9e271fce8bf1 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -540,7 +540,7 @@ namespace ts { * Subset of a SourceFile used to calculate index-based resolutions * This includes some internal fields, so unless you have very good reason, * (and are willing to use some less stable internals) you should probably just pass a SourceFile. - * + * * @internal */ export interface SourceFileImportsList { @@ -566,6 +566,7 @@ namespace ts { */ export function getModeForResolutionAtIndex(file: SourceFile, index: number): ModuleKind.CommonJS | ModuleKind.ESNext | undefined; /** @internal */ + // eslint-disable-next-line @typescript-eslint/unified-signatures export function getModeForResolutionAtIndex(file: SourceFileImportsList, index: number): ModuleKind.CommonJS | ModuleKind.ESNext | undefined; export function getModeForResolutionAtIndex(file: SourceFileImportsList, index: number): ModuleKind.CommonJS | ModuleKind.ESNext | undefined { if (file.impliedNodeFormat === undefined) return undefined;