Skip to content

Commit 3743fbc

Browse files
authored
Consistently avoid module resolution errors when using getSymbolAtLocation (#58668)
1 parent 5d70bf8 commit 3743fbc

8 files changed

+602
-21
lines changed

src/compiler/checker.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2783,7 +2783,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
27832783
const moduleNotFoundError = !(moduleName.parent.parent.flags & NodeFlags.Ambient)
27842784
? Diagnostics.Invalid_module_name_in_augmentation_module_0_cannot_be_found
27852785
: undefined;
2786-
let mainModule = resolveExternalModuleNameWorker(moduleName, moduleName, moduleNotFoundError, /*isForAugmentation*/ true);
2786+
let mainModule = resolveExternalModuleNameWorker(moduleName, moduleName, moduleNotFoundError, /*ignoreErrors*/ false, /*isForAugmentation*/ true);
27872787
if (!mainModule) {
27882788
return;
27892789
}
@@ -4547,17 +4547,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
45474547
const errorMessage = isClassic ?
45484548
Diagnostics.Cannot_find_module_0_Did_you_mean_to_set_the_moduleResolution_option_to_nodenext_or_to_add_aliases_to_the_paths_option
45494549
: Diagnostics.Cannot_find_module_0_or_its_corresponding_type_declarations;
4550-
return resolveExternalModuleNameWorker(location, moduleReferenceExpression, ignoreErrors ? undefined : errorMessage);
4550+
return resolveExternalModuleNameWorker(location, moduleReferenceExpression, ignoreErrors ? undefined : errorMessage, ignoreErrors);
45514551
}
45524552

4553-
function resolveExternalModuleNameWorker(location: Node, moduleReferenceExpression: Expression, moduleNotFoundError: DiagnosticMessage | undefined, isForAugmentation = false): Symbol | undefined {
4553+
function resolveExternalModuleNameWorker(location: Node, moduleReferenceExpression: Expression, moduleNotFoundError: DiagnosticMessage | undefined, ignoreErrors = false, isForAugmentation = false): Symbol | undefined {
45544554
return isStringLiteralLike(moduleReferenceExpression)
4555-
? resolveExternalModule(location, moduleReferenceExpression.text, moduleNotFoundError, moduleReferenceExpression, isForAugmentation)
4555+
? resolveExternalModule(location, moduleReferenceExpression.text, moduleNotFoundError, !ignoreErrors ? moduleReferenceExpression : undefined, isForAugmentation)
45564556
: undefined;
45574557
}
45584558

4559-
function resolveExternalModule(location: Node, moduleReference: string, moduleNotFoundError: DiagnosticMessage | undefined, errorNode: Node, isForAugmentation = false): Symbol | undefined {
4560-
if (startsWith(moduleReference, "@types/")) {
4559+
function resolveExternalModule(location: Node, moduleReference: string, moduleNotFoundError: DiagnosticMessage | undefined, errorNode: Node | undefined, isForAugmentation = false): Symbol | undefined {
4560+
if (errorNode && startsWith(moduleReference, "@types/")) {
45614561
const diag = Diagnostics.Cannot_import_type_declaration_files_Consider_importing_0_instead_of_1;
45624562
const withoutAtTypePrefix = removePrefix(moduleReference, "@types/");
45634563
error(errorNode, diag, withoutAtTypePrefix, moduleReference);
@@ -4581,7 +4581,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
45814581
const mode = contextSpecifier && isStringLiteralLike(contextSpecifier) ? host.getModeForUsageLocation(currentSourceFile, contextSpecifier) : currentSourceFile.impliedNodeFormat;
45824582
const moduleResolutionKind = getEmitModuleResolutionKind(compilerOptions);
45834583
const resolvedModule = host.getResolvedModule(currentSourceFile, moduleReference, mode)?.resolvedModule;
4584-
const resolutionDiagnostic = resolvedModule && getResolutionDiagnostic(compilerOptions, resolvedModule, currentSourceFile);
4584+
const resolutionDiagnostic = errorNode && resolvedModule && getResolutionDiagnostic(compilerOptions, resolvedModule, currentSourceFile);
45854585
const sourceFile = resolvedModule
45864586
&& (!resolutionDiagnostic || resolutionDiagnostic === Diagnostics.Module_0_was_resolved_to_1_but_jsx_is_not_set)
45874587
&& host.getSourceFile(resolvedModule.resolvedFileName);
@@ -4594,7 +4594,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
45944594
if (resolvedModule.resolvedUsingTsExtension && isDeclarationFileName(moduleReference)) {
45954595
const importOrExport = findAncestor(location, isImportDeclaration)?.importClause ||
45964596
findAncestor(location, or(isImportEqualsDeclaration, isExportDeclaration));
4597-
if (importOrExport && !importOrExport.isTypeOnly || findAncestor(location, isImportCall)) {
4597+
if (errorNode && importOrExport && !importOrExport.isTypeOnly || findAncestor(location, isImportCall)) {
45984598
error(
45994599
errorNode,
46004600
Diagnostics.A_declaration_file_cannot_be_imported_without_import_type_Did_you_mean_to_import_an_implementation_file_0_instead,
@@ -4605,17 +4605,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
46054605
else if (resolvedModule.resolvedUsingTsExtension && !shouldAllowImportingTsExtension(compilerOptions, currentSourceFile.fileName)) {
46064606
const importOrExport = findAncestor(location, isImportDeclaration)?.importClause ||
46074607
findAncestor(location, or(isImportEqualsDeclaration, isExportDeclaration));
4608-
if (!(importOrExport?.isTypeOnly || findAncestor(location, isImportTypeNode))) {
4608+
if (errorNode && !(importOrExport?.isTypeOnly || findAncestor(location, isImportTypeNode))) {
46094609
const tsExtension = Debug.checkDefined(tryExtractTSExtension(moduleReference));
46104610
error(errorNode, Diagnostics.An_import_path_can_only_end_with_a_0_extension_when_allowImportingTsExtensions_is_enabled, tsExtension);
46114611
}
46124612
}
46134613

46144614
if (sourceFile.symbol) {
4615-
if (resolvedModule.isExternalLibraryImport && !resolutionExtensionIsTSOrJson(resolvedModule.extension)) {
4615+
if (errorNode && resolvedModule.isExternalLibraryImport && !resolutionExtensionIsTSOrJson(resolvedModule.extension)) {
46164616
errorOnImplicitAnyModule(/*isError*/ false, errorNode, currentSourceFile, mode, resolvedModule, moduleReference);
46174617
}
4618-
if (moduleResolutionKind === ModuleResolutionKind.Node16 || moduleResolutionKind === ModuleResolutionKind.NodeNext) {
4618+
if (errorNode && (moduleResolutionKind === ModuleResolutionKind.Node16 || moduleResolutionKind === ModuleResolutionKind.NodeNext)) {
46194619
const isSyncImport = (currentSourceFile.impliedNodeFormat === ModuleKind.CommonJS && !findAncestor(location, isImportCall)) || !!findAncestor(location, isImportEqualsDeclaration);
46204620
const overrideHost = findAncestor(location, l => isImportTypeNode(l) || isExportDeclaration(l) || isImportDeclaration(l));
46214621
// An override clause will take effect for type-only imports and import types, and allows importing the types across formats, regardless of
@@ -4680,7 +4680,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
46804680
// merged symbol is module declaration symbol combined with all augmentations
46814681
return getMergedSymbol(sourceFile.symbol);
46824682
}
4683-
if (moduleNotFoundError) {
4683+
if (errorNode && moduleNotFoundError) {
46844684
// report errors only if it was requested
46854685
error(errorNode, Diagnostics.File_0_is_not_a_module, sourceFile.fileName);
46864686
}
@@ -4702,6 +4702,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
47024702
}
47034703
}
47044704

4705+
if (!errorNode) {
4706+
return undefined;
4707+
}
4708+
47054709
// May be an untyped module. If so, ignore resolutionDiagnostic.
47064710
if (resolvedModule && !resolutionExtensionIsTSOrJson(resolvedModule.extension) && resolutionDiagnostic === undefined || resolutionDiagnostic === Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type) {
47074711
if (isForAugmentation) {
@@ -33078,7 +33082,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3307833082
? Diagnostics.Cannot_find_module_0_Did_you_mean_to_set_the_moduleResolution_option_to_nodenext_or_to_add_aliases_to_the_paths_option
3307933083
: Diagnostics.Cannot_find_module_0_or_its_corresponding_type_declarations;
3308033084
const specifier = getJSXRuntimeImportSpecifier(file, runtimeImportSpecifier);
33081-
const mod = resolveExternalModule(specifier || location!, runtimeImportSpecifier, errorMessage, location!);
33085+
const mod = resolveExternalModule(specifier || location!, runtimeImportSpecifier, errorMessage, location);
3308233086
const result = mod && mod !== unknownSymbol ? getMergedSymbol(resolveSymbol(mod)) : undefined;
3308333087
if (links) {
3308433088
links.jsxImplicitImportContainer = result || false;

src/testRunner/unittests/tsc/composite.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,58 @@ describe("unittests:: tsc:: composite::", () => {
114114
},
115115
],
116116
});
117+
118+
verifyTsc({
119+
scenario: "composite",
120+
subScenario: "synthetic jsx import of ESM module from CJS module no crash no jsx element",
121+
fs: () =>
122+
loadProjectFromFiles({
123+
"/src/main.ts": "export default 42;",
124+
"/tsconfig.json": jsonToReadableText({
125+
compilerOptions: {
126+
composite: true,
127+
module: "Node16",
128+
jsx: "react-jsx",
129+
jsxImportSource: "solid-js",
130+
},
131+
}),
132+
"/node_modules/solid-js/package.json": jsonToReadableText({
133+
name: "solid-js",
134+
type: "module",
135+
}),
136+
"/node_modules/solid-js/jsx-runtime.d.ts": Utils.dedent`
137+
export namespace JSX {
138+
type IntrinsicElements = { div: {}; };
139+
}
140+
`,
141+
}),
142+
commandLineArgs: [],
143+
});
144+
145+
verifyTsc({
146+
scenario: "composite",
147+
subScenario: "synthetic jsx import of ESM module from CJS module error on jsx element",
148+
fs: () =>
149+
loadProjectFromFiles({
150+
"/src/main.tsx": "export default <div/>;",
151+
"/tsconfig.json": jsonToReadableText({
152+
compilerOptions: {
153+
composite: true,
154+
module: "Node16",
155+
jsx: "react-jsx",
156+
jsxImportSource: "solid-js",
157+
},
158+
}),
159+
"/node_modules/solid-js/package.json": jsonToReadableText({
160+
name: "solid-js",
161+
type: "module",
162+
}),
163+
"/node_modules/solid-js/jsx-runtime.d.ts": Utils.dedent`
164+
export namespace JSX {
165+
type IntrinsicElements = { div: {}; };
166+
}
167+
`,
168+
}),
169+
commandLineArgs: [],
170+
});
117171
});

src/testRunner/unittests/tscWatch/programUpdates.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2164,6 +2164,48 @@ import { x } from "../b";`,
21642164
],
21652165
});
21662166

2167+
verifyTscWatch({
2168+
scenario,
2169+
subScenario: "when changing `allowImportingTsExtensions` of config file 2",
2170+
commandLineArgs: ["-w", "-p", ".", "--extendedDiagnostics"],
2171+
sys: () => {
2172+
const module1: File = {
2173+
path: `/user/username/projects/myproject/a.ts`,
2174+
content: `export const foo = 10;`,
2175+
};
2176+
const module2: File = {
2177+
path: `/user/username/projects/myproject/b.ts`,
2178+
content: `export * as a from "./a.ts";`,
2179+
};
2180+
const config: File = {
2181+
path: `/user/username/projects/myproject/tsconfig.json`,
2182+
content: jsonToReadableText({
2183+
compilerOptions: {
2184+
noEmit: true,
2185+
allowImportingTsExtensions: false,
2186+
},
2187+
}),
2188+
};
2189+
return createWatchedSystem([module1, module2, config, libFile], { currentDirectory: "/user/username/projects/myproject" });
2190+
},
2191+
edits: [
2192+
{
2193+
caption: "Change allowImportingTsExtensions to true",
2194+
edit: sys =>
2195+
sys.writeFile(
2196+
`/user/username/projects/myproject/tsconfig.json`,
2197+
jsonToReadableText({
2198+
compilerOptions: {
2199+
noEmit: true,
2200+
allowImportingTsExtensions: true,
2201+
},
2202+
}),
2203+
),
2204+
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
2205+
},
2206+
],
2207+
});
2208+
21672209
verifyTscWatch({
21682210
scenario,
21692211
subScenario: "when changing checkJs of config file",

0 commit comments

Comments
 (0)