diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8556832569b76..1e100505ec7e4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3561,6 +3561,12 @@ namespace ts { (isLiteralImportTypeNode(location) ? location : undefined)?.argument.literal; const mode = contextSpecifier && isStringLiteralLike(contextSpecifier) ? getModeForUsageLocation(currentSourceFile, contextSpecifier) : currentSourceFile.impliedNodeFormat; const resolvedModule = getResolvedModule(currentSourceFile, moduleReference, mode); + if (resolvedModule?.extension === Extension.Json) { + const mod = findMatchingPatternAmbientModule(moduleReference); + if (mod) { + return mod; + } + } const resolutionDiagnostic = resolvedModule && getResolutionDiagnostic(compilerOptions, resolvedModule); const sourceFile = resolvedModule && (!resolutionDiagnostic || resolutionDiagnostic === Diagnostics.Module_0_was_resolved_to_1_but_jsx_is_not_set) @@ -3594,18 +3600,11 @@ namespace ts { return undefined; } - if (patternAmbientModules) { - const pattern = findBestPatternMatch(patternAmbientModules, _ => _.pattern, moduleReference); - if (pattern) { - // If the module reference matched a pattern ambient module ('*.foo') but there's also a - // module augmentation by the specific name requested ('a.foo'), we store the merged symbol - // by the augmentation name ('a.foo'), because asking for *.foo should not give you exports - // from a.foo. - const augmentation = patternAmbientModuleAugmentations && patternAmbientModuleAugmentations.get(moduleReference); - if (augmentation) { - return getMergedSymbol(augmentation); - } - return getMergedSymbol(pattern.symbol); + // JSON files have been tried before, no need to try again. + if (resolvedModule?.extension !== Extension.Json) { + const mod = findMatchingPatternAmbientModule(moduleReference); + if (mod) { + return mod; } } @@ -3680,6 +3679,26 @@ namespace ts { return undefined; } + function findMatchingPatternAmbientModule(moduleReference: string): Symbol | undefined { + if (!patternAmbientModules) { + return; + } + const pattern = findBestPatternMatch(patternAmbientModules, _ => _.pattern, moduleReference); + if (!pattern) { + return; + } + // If the module reference matched a pattern ambient module ('*.foo') but there's also a + // module augmentation by the specific name requested ('a.foo'), we store the merged symbol + // by the augmentation name ('a.foo'), because asking for *.foo should not give you exports + // from a.foo. + const augmentation = patternAmbientModuleAugmentations && patternAmbientModuleAugmentations.get(moduleReference); + if (augmentation) { + return getMergedSymbol(augmentation); + } + return getMergedSymbol(pattern.symbol); + } + + function errorOnImplicitAnyModule(isError: boolean, errorNode: Node, { packageId, resolvedFileName }: ResolvedModuleFull, moduleReference: string): void { const errorInfo = !isExternalModuleNameRelative(moduleReference) && packageId ? typesPackageExists(packageId.name) diff --git a/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite.js b/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite.js new file mode 100644 index 0000000000000..dd1a3bf8a6694 --- /dev/null +++ b/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite.js @@ -0,0 +1,26 @@ +//// [tests/cases/compiler/moduleResolutionWithExtensions_jsonOverwrite.ts] //// + +//// [hello.locstring.json] +{ "hello": "Hello World" } + +//// [types.d.ts] +type ResourceId = string & { __ResourceId: void }; +declare module "*.locstring.json" { + const value: { [key: string]: ResourceId }; + export default value; +} + +//// [a.ts] +import strings from './hello.locstring.json'; + +strings.hello; + + +//// [a.js] +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +exports.__esModule = true; +var hello_locstring_json_1 = __importDefault(require("./hello.locstring.json")); +hello_locstring_json_1["default"].hello; diff --git a/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite.symbols b/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite.symbols new file mode 100644 index 0000000000000..8343eb5ed7979 --- /dev/null +++ b/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite.symbols @@ -0,0 +1,28 @@ +=== /hello.locstring.json === +{ "hello": "Hello World" } +>"hello" : Symbol("hello", Decl(hello.locstring.json, 0, 1)) + +=== /types.d.ts === +type ResourceId = string & { __ResourceId: void }; +>ResourceId : Symbol(ResourceId, Decl(types.d.ts, 0, 0)) +>__ResourceId : Symbol(__ResourceId, Decl(types.d.ts, 0, 28)) + +declare module "*.locstring.json" { +>"*.locstring.json" : Symbol("*.locstring.json", Decl(types.d.ts, 0, 50)) + + const value: { [key: string]: ResourceId }; +>value : Symbol(value, Decl(types.d.ts, 2, 9)) +>key : Symbol(key, Decl(types.d.ts, 2, 20)) +>ResourceId : Symbol(ResourceId, Decl(types.d.ts, 0, 0)) + + export default value; +>value : Symbol(value, Decl(types.d.ts, 2, 9)) +} + +=== /a.ts === +import strings from './hello.locstring.json'; +>strings : Symbol(strings, Decl(a.ts, 0, 6)) + +strings.hello; +>strings : Symbol(strings, Decl(a.ts, 0, 6)) + diff --git a/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite.trace.json b/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite.trace.json new file mode 100644 index 0000000000000..56eae81f3ab28 --- /dev/null +++ b/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite.trace.json @@ -0,0 +1,17 @@ +[ + "======== Resolving module './hello.locstring.json' from '/a.ts'. ========", + "Module resolution kind is not specified, using 'NodeJs'.", + "Loading module as file / folder, candidate module location '/hello.locstring.json', target file type 'TypeScript'.", + "File '/hello.locstring.json.ts' does not exist.", + "File '/hello.locstring.json.tsx' does not exist.", + "File '/hello.locstring.json.d.ts' does not exist.", + "File name '/hello.locstring.json' has a '.json' extension - stripping it.", + "File '/hello.locstring.json.d.ts' does not exist.", + "Directory '/hello.locstring.json' does not exist, skipping all lookups in it.", + "Loading module as file / folder, candidate module location '/hello.locstring.json', target file type 'JavaScript'.", + "File '/hello.locstring.json.js' does not exist.", + "File '/hello.locstring.json.jsx' does not exist.", + "File name '/hello.locstring.json' has a '.json' extension - stripping it.", + "File '/hello.locstring.json' exist - use it as a name resolution result.", + "======== Module name './hello.locstring.json' was successfully resolved to '/hello.locstring.json'. ========" +] \ No newline at end of file diff --git a/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite.types b/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite.types new file mode 100644 index 0000000000000..0c3c5d9a144ca --- /dev/null +++ b/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite.types @@ -0,0 +1,31 @@ +=== /hello.locstring.json === +{ "hello": "Hello World" } +>{ "hello": "Hello World" } : { hello: string; } +>"hello" : string +>"Hello World" : "Hello World" + +=== /types.d.ts === +type ResourceId = string & { __ResourceId: void }; +>ResourceId : string & { __ResourceId: void; } +>__ResourceId : void + +declare module "*.locstring.json" { +>"*.locstring.json" : typeof import("*.locstring.json") + + const value: { [key: string]: ResourceId }; +>value : { [key: string]: ResourceId; } +>key : string + + export default value; +>value : { [key: string]: ResourceId; } +} + +=== /a.ts === +import strings from './hello.locstring.json'; +>strings : { [key: string]: ResourceId; } + +strings.hello; +>strings.hello : ResourceId +>strings : { [key: string]: ResourceId; } +>hello : ResourceId + diff --git a/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite2.js b/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite2.js new file mode 100644 index 0000000000000..13e9053d536cd --- /dev/null +++ b/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite2.js @@ -0,0 +1,32 @@ +//// [tests/cases/compiler/moduleResolutionWithExtensions_jsonOverwrite2.ts] //// + +//// [hello.locstring.json] +{ "hello": "Hello World" } + +//// [hello.locstring.json.d.ts] +declare const _default: { + hello: number; +}; +export default _default; + +//// [types.d.ts] +type ResourceId = string & { __ResourceId: void }; +declare module "*.locstring.json" { + const value: { [key: string]: ResourceId }; + export default value; +} + +//// [a.ts] +import strings from './hello.locstring.json'; + +strings.hello; + + +//// [a.js] +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +exports.__esModule = true; +var hello_locstring_json_1 = __importDefault(require("./hello.locstring.json")); +hello_locstring_json_1["default"].hello; diff --git a/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite2.symbols b/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite2.symbols new file mode 100644 index 0000000000000..0f069e397da8a --- /dev/null +++ b/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite2.symbols @@ -0,0 +1,37 @@ +=== /hello.locstring.json.d.ts === +declare const _default: { +>_default : Symbol(_default, Decl(hello.locstring.json.d.ts, 0, 13)) + + hello: number; +>hello : Symbol(hello, Decl(hello.locstring.json.d.ts, 0, 25)) + +}; +export default _default; +>_default : Symbol(_default, Decl(hello.locstring.json.d.ts, 0, 13)) + +=== /types.d.ts === +type ResourceId = string & { __ResourceId: void }; +>ResourceId : Symbol(ResourceId, Decl(types.d.ts, 0, 0)) +>__ResourceId : Symbol(__ResourceId, Decl(types.d.ts, 0, 28)) + +declare module "*.locstring.json" { +>"*.locstring.json" : Symbol("*.locstring.json", Decl(types.d.ts, 0, 50)) + + const value: { [key: string]: ResourceId }; +>value : Symbol(value, Decl(types.d.ts, 2, 9)) +>key : Symbol(key, Decl(types.d.ts, 2, 20)) +>ResourceId : Symbol(ResourceId, Decl(types.d.ts, 0, 0)) + + export default value; +>value : Symbol(value, Decl(types.d.ts, 2, 9)) +} + +=== /a.ts === +import strings from './hello.locstring.json'; +>strings : Symbol(strings, Decl(a.ts, 0, 6)) + +strings.hello; +>strings.hello : Symbol(hello, Decl(hello.locstring.json.d.ts, 0, 25)) +>strings : Symbol(strings, Decl(a.ts, 0, 6)) +>hello : Symbol(hello, Decl(hello.locstring.json.d.ts, 0, 25)) + diff --git a/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite2.trace.json b/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite2.trace.json new file mode 100644 index 0000000000000..6a9da9b9c03b3 --- /dev/null +++ b/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite2.trace.json @@ -0,0 +1,9 @@ +[ + "======== Resolving module './hello.locstring.json' from '/a.ts'. ========", + "Module resolution kind is not specified, using 'NodeJs'.", + "Loading module as file / folder, candidate module location '/hello.locstring.json', target file type 'TypeScript'.", + "File '/hello.locstring.json.ts' does not exist.", + "File '/hello.locstring.json.tsx' does not exist.", + "File '/hello.locstring.json.d.ts' exist - use it as a name resolution result.", + "======== Module name './hello.locstring.json' was successfully resolved to '/hello.locstring.json.d.ts'. ========" +] \ No newline at end of file diff --git a/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite2.types b/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite2.types new file mode 100644 index 0000000000000..c44c0925e06ce --- /dev/null +++ b/tests/baselines/reference/moduleResolutionWithExtensions_jsonOverwrite2.types @@ -0,0 +1,36 @@ +=== /hello.locstring.json.d.ts === +declare const _default: { +>_default : { hello: number; } + + hello: number; +>hello : number + +}; +export default _default; +>_default : { hello: number; } + +=== /types.d.ts === +type ResourceId = string & { __ResourceId: void }; +>ResourceId : string & { __ResourceId: void; } +>__ResourceId : void + +declare module "*.locstring.json" { +>"*.locstring.json" : typeof import("*.locstring.json") + + const value: { [key: string]: ResourceId }; +>value : { [key: string]: ResourceId; } +>key : string + + export default value; +>value : { [key: string]: ResourceId; } +} + +=== /a.ts === +import strings from './hello.locstring.json'; +>strings : { hello: number; } + +strings.hello; +>strings.hello : number +>strings : { hello: number; } +>hello : number + diff --git a/tests/cases/compiler/moduleResolutionWithExtensions_jsonOverwrite.ts b/tests/cases/compiler/moduleResolutionWithExtensions_jsonOverwrite.ts new file mode 100644 index 0000000000000..e3c64f16c8947 --- /dev/null +++ b/tests/cases/compiler/moduleResolutionWithExtensions_jsonOverwrite.ts @@ -0,0 +1,19 @@ +// @resolveJsonModule: true +// @traceResolution: true +// @esModuleInterop: true + + +// @Filename: /hello.locstring.json +{ "hello": "Hello World" } + +// @Filename: /types.d.ts +type ResourceId = string & { __ResourceId: void }; +declare module "*.locstring.json" { + const value: { [key: string]: ResourceId }; + export default value; +} + +// @Filename: /a.ts +import strings from './hello.locstring.json'; + +strings.hello; diff --git a/tests/cases/compiler/moduleResolutionWithExtensions_jsonOverwrite2.ts b/tests/cases/compiler/moduleResolutionWithExtensions_jsonOverwrite2.ts new file mode 100644 index 0000000000000..13a82175bf5dd --- /dev/null +++ b/tests/cases/compiler/moduleResolutionWithExtensions_jsonOverwrite2.ts @@ -0,0 +1,25 @@ +// @resolveJsonModule: true +// @traceResolution: true +// @esModuleInterop: true + + +// @Filename: /hello.locstring.json +{ "hello": "Hello World" } + +// @Filename: /hello.locstring.json.d.ts +declare const _default: { + hello: number; +}; +export default _default; + +// @Filename: /types.d.ts +type ResourceId = string & { __ResourceId: void }; +declare module "*.locstring.json" { + const value: { [key: string]: ResourceId }; + export default value; +} + +// @Filename: /a.ts +import strings from './hello.locstring.json'; + +strings.hello;