Skip to content

Commit 6369240

Browse files
authored
Store information in buildInfo to repopulate mode mismatch based on status of package.json (#59286)
1 parent bd54a6b commit 6369240

File tree

11 files changed

+404
-65
lines changed

11 files changed

+404
-65
lines changed

src/compiler/builder.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
concatenate,
2222
convertToOptionsWithAbsolutePaths,
2323
createGetCanonicalFileName,
24+
createModeMismatchDetails,
2425
createModuleNotFoundChain,
2526
createProgram,
2627
CustomTransformers,
@@ -70,7 +71,6 @@ import {
7071
ReadBuildProgramHost,
7172
ReadonlyCollection,
7273
RepopulateDiagnosticChainInfo,
73-
RepopulateModuleNotFoundDiagnosticChain,
7474
returnFalse,
7575
returnUndefined,
7676
sameMap,
@@ -111,8 +111,8 @@ export interface ReusableDiagnosticRelatedInformation {
111111
}
112112

113113
/** @internal */
114-
export interface ReusableRepopulateModuleNotFoundChain {
115-
info: RepopulateModuleNotFoundDiagnosticChain;
114+
export interface ReusableRepopulateInfoChain {
115+
info: RepopulateDiagnosticChainInfo;
116116
next?: ReusableDiagnosticMessageChain[];
117117
}
118118

@@ -122,7 +122,7 @@ export type SerializedDiagnosticMessageChain = Omit<DiagnosticMessageChain, "nex
122122
};
123123

124124
/** @internal */
125-
export type ReusableDiagnosticMessageChain = SerializedDiagnosticMessageChain | ReusableRepopulateModuleNotFoundChain;
125+
export type ReusableDiagnosticMessageChain = SerializedDiagnosticMessageChain | ReusableRepopulateInfoChain;
126126

127127
/**
128128
* Signature (Hash of d.ts emitted), is string if it was emitted using same d.ts.map option as what compilerOptions indicate, otherwise tuple of string
@@ -538,7 +538,13 @@ function convertOrRepopulateDiagnosticMessageChain<T extends DiagnosticMessageCh
538538
repopulateInfo: (chain: T) => RepopulateDiagnosticChainInfo | undefined,
539539
): DiagnosticMessageChain {
540540
const info = repopulateInfo(chain);
541-
if (info) {
541+
if (info === true) {
542+
return {
543+
...createModeMismatchDetails(sourceFile!),
544+
next: convertOrRepopulateDiagnosticMessageChainArray(chain.next as T[], sourceFile, newProgram, repopulateInfo),
545+
};
546+
}
547+
else if (info) {
542548
return {
543549
...createModuleNotFoundChain(sourceFile!, newProgram, info.moduleReference, info.mode, info.packageName || info.moduleReference),
544550
next: convertOrRepopulateDiagnosticMessageChainArray(chain.next as T[], sourceFile, newProgram, repopulateInfo),
@@ -600,7 +606,7 @@ function convertToDiagnosticRelatedInformation(
600606
file: sourceFile,
601607
messageText: isString(diagnostic.messageText) ?
602608
diagnostic.messageText :
603-
convertOrRepopulateDiagnosticMessageChain(diagnostic.messageText, sourceFile, newProgram, chain => (chain as ReusableRepopulateModuleNotFoundChain).info),
609+
convertOrRepopulateDiagnosticMessageChain(diagnostic.messageText, sourceFile, newProgram, chain => (chain as ReusableRepopulateInfoChain).info),
604610
};
605611
}
606612

src/compiler/checker.ts

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ import {
7979
classOrConstructorParameterIsDecorated,
8080
ClassStaticBlockDeclaration,
8181
clear,
82-
combinePaths,
8382
compareDiagnostics,
8483
comparePaths,
8584
compareValues,
@@ -116,6 +115,7 @@ import {
116115
createFlowNode,
117116
createGetSymbolWalker,
118117
createModeAwareCacheKey,
118+
createModeMismatchDetails,
119119
createModuleNotFoundChain,
120120
createMultiMap,
121121
createNameResolver,
@@ -4627,40 +4627,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
46274627
let diagnosticDetails;
46284628
const ext = tryGetExtensionFromPath(currentSourceFile.fileName);
46294629
if (ext === Extension.Ts || ext === Extension.Js || ext === Extension.Tsx || ext === Extension.Jsx) {
4630-
const scope = currentSourceFile.packageJsonScope;
4631-
const targetExt = ext === Extension.Ts ? Extension.Mts : ext === Extension.Js ? Extension.Mjs : undefined;
4632-
if (scope && !scope.contents.packageJsonContent.type) {
4633-
if (targetExt) {
4634-
diagnosticDetails = chainDiagnosticMessages(
4635-
/*details*/ undefined,
4636-
Diagnostics.To_convert_this_file_to_an_ECMAScript_module_change_its_file_extension_to_0_or_add_the_field_type_Colon_module_to_1,
4637-
targetExt,
4638-
combinePaths(scope.packageDirectory, "package.json"),
4639-
);
4640-
}
4641-
else {
4642-
diagnosticDetails = chainDiagnosticMessages(
4643-
/*details*/ undefined,
4644-
Diagnostics.To_convert_this_file_to_an_ECMAScript_module_add_the_field_type_Colon_module_to_0,
4645-
combinePaths(scope.packageDirectory, "package.json"),
4646-
);
4647-
}
4648-
}
4649-
else {
4650-
if (targetExt) {
4651-
diagnosticDetails = chainDiagnosticMessages(
4652-
/*details*/ undefined,
4653-
Diagnostics.To_convert_this_file_to_an_ECMAScript_module_change_its_file_extension_to_0_or_create_a_local_package_json_file_with_type_Colon_module,
4654-
targetExt,
4655-
);
4656-
}
4657-
else {
4658-
diagnosticDetails = chainDiagnosticMessages(
4659-
/*details*/ undefined,
4660-
Diagnostics.To_convert_this_file_to_an_ECMAScript_module_create_a_local_package_json_file_with_type_Colon_module,
4661-
);
4662-
}
4663-
}
4630+
diagnosticDetails = createModeMismatchDetails(currentSourceFile);
46644631
}
46654632
diagnostics.add(createDiagnosticForNodeFromMessageChain(
46664633
getSourceFileOfNode(errorNode),

src/compiler/moduleNameResolver.ts

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2382,17 +2382,11 @@ export interface PackageJsonInfoContents {
23822382
*
23832383
* @internal
23842384
*/
2385-
export function getPackageScopeForPath(fileName: string, state: ModuleResolutionState): PackageJsonInfo | undefined {
2386-
const parts = getPathComponents(fileName);
2387-
parts.pop();
2388-
while (parts.length > 0) {
2389-
const pkg = getPackageJsonInfo(getPathFromPathComponents(parts), /*onlyRecordFailures*/ false, state);
2390-
if (pkg) {
2391-
return pkg;
2392-
}
2393-
parts.pop();
2394-
}
2395-
return undefined;
2385+
export function getPackageScopeForPath(directory: string, state: ModuleResolutionState): PackageJsonInfo | undefined {
2386+
return forEachAncestorDirectory(
2387+
directory,
2388+
dir => getPackageJsonInfo(dir, /*onlyRecordFailures*/ false, state),
2389+
);
23962390
}
23972391

23982392
function getVersionPathsOfPackageJsonInfo(packageJsonInfo: PackageJsonInfo, state: ModuleResolutionState): VersionPaths | undefined {
@@ -2568,7 +2562,7 @@ function noKeyStartsWithDot(obj: MapLike<unknown>) {
25682562
}
25692563

25702564
function loadModuleFromSelfNameReference(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult<Resolved> {
2571-
const directoryPath = getNormalizedAbsolutePath(combinePaths(directory, "dummy"), state.host.getCurrentDirectory?.());
2565+
const directoryPath = getNormalizedAbsolutePath(directory, state.host.getCurrentDirectory?.());
25722566
const scope = getPackageScopeForPath(directoryPath, state);
25732567
if (!scope || !scope.contents.packageJsonContent.exports) {
25742568
return undefined;
@@ -2649,7 +2643,7 @@ function loadModuleFromImports(extensions: Extensions, moduleName: string, direc
26492643
}
26502644
return toSearchResult(/*value*/ undefined);
26512645
}
2652-
const directoryPath = getNormalizedAbsolutePath(combinePaths(directory, "dummy"), state.host.getCurrentDirectory?.());
2646+
const directoryPath = getNormalizedAbsolutePath(directory, state.host.getCurrentDirectory?.());
26532647
const scope = getPackageScopeForPath(directoryPath, state);
26542648
if (!scope) {
26552649
if (state.traceEnabled) {

src/compiler/moduleSpecifiers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -737,7 +737,7 @@ function getAllModulePathsWorker(info: Info, importedFileName: string, host: Mod
737737
// This should populate all the relevant symlinks in the symlink cache, and most, if not all, of these resolutions
738738
// should get (re)used.
739739
const state = getTemporaryModuleResolutionState(cache.getPackageJsonInfoCache(), host, {});
740-
const packageJson = getPackageScopeForPath(info.importingSourceFileName, state);
740+
const packageJson = getPackageScopeForPath(getDirectoryPath(info.importingSourceFileName), state);
741741
if (packageJson) {
742742
const toResolve = getAllRuntimeDependencies(packageJson.contents.packageJsonContent);
743743
for (const depName of (toResolve || emptyArray)) {

src/compiler/program.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1363,7 +1363,7 @@ export function getImpliedNodeFormatForFileWorker(
13631363
const packageJsonLocations: string[] = [];
13641364
state.failedLookupLocations = packageJsonLocations;
13651365
state.affectingLocations = packageJsonLocations;
1366-
const packageJsonScope = getPackageScopeForPath(fileName, state);
1366+
const packageJsonScope = getPackageScopeForPath(getDirectoryPath(fileName), state);
13671367
const impliedNodeFormat = packageJsonScope?.contents.packageJsonContent.type === "module" ? ModuleKind.ESNext : ModuleKind.CommonJS;
13681368
return { impliedNodeFormat, packageJsonLocations, packageJsonScope };
13691369
}

src/compiler/types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7042,7 +7042,10 @@ export interface RepopulateModuleNotFoundDiagnosticChain {
70427042
}
70437043

70447044
/** @internal */
7045-
export type RepopulateDiagnosticChainInfo = RepopulateModuleNotFoundDiagnosticChain;
7045+
export type RepopulateModeMismatchDiagnosticChain = true;
7046+
7047+
/** @internal */
7048+
export type RepopulateDiagnosticChainInfo = RepopulateModuleNotFoundDiagnosticChain | RepopulateModeMismatchDiagnosticChain;
70467049

70477050
/**
70487051
* A linked list of formatted diagnostic messages to be used as part of a multiline message.

src/compiler/utilities.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,38 @@ export function createModuleNotFoundChain(sourceFile: SourceFile, host: TypeChec
844844
return result;
845845
}
846846

847+
/** @internal */
848+
export function createModeMismatchDetails(currentSourceFile: SourceFile) {
849+
const ext = tryGetExtensionFromPath(currentSourceFile.fileName);
850+
const scope = currentSourceFile.packageJsonScope;
851+
const targetExt = ext === Extension.Ts ? Extension.Mts : ext === Extension.Js ? Extension.Mjs : undefined;
852+
const result = scope && !scope.contents.packageJsonContent.type ?
853+
targetExt ?
854+
chainDiagnosticMessages(
855+
/*details*/ undefined,
856+
Diagnostics.To_convert_this_file_to_an_ECMAScript_module_change_its_file_extension_to_0_or_add_the_field_type_Colon_module_to_1,
857+
targetExt,
858+
combinePaths(scope.packageDirectory, "package.json"),
859+
) :
860+
chainDiagnosticMessages(
861+
/*details*/ undefined,
862+
Diagnostics.To_convert_this_file_to_an_ECMAScript_module_add_the_field_type_Colon_module_to_0,
863+
combinePaths(scope.packageDirectory, "package.json"),
864+
) :
865+
targetExt ?
866+
chainDiagnosticMessages(
867+
/*details*/ undefined,
868+
Diagnostics.To_convert_this_file_to_an_ECMAScript_module_change_its_file_extension_to_0_or_create_a_local_package_json_file_with_type_Colon_module,
869+
targetExt,
870+
) :
871+
chainDiagnosticMessages(
872+
/*details*/ undefined,
873+
Diagnostics.To_convert_this_file_to_an_ECMAScript_module_create_a_local_package_json_file_with_type_Colon_module,
874+
);
875+
result.repopulateInfo = () => true;
876+
return result;
877+
}
878+
847879
function packageIdIsEqual(a: PackageId | undefined, b: PackageId | undefined): boolean {
848880
return a === b || !!a && !!b && a.name === b.name && a.subModuleName === b.subModuleName && a.version === b.version && a.peerDependencies === b.peerDependencies;
849881
}

src/server/session.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1712,7 +1712,7 @@ export class Session<TMessage = string> implements EventSender {
17121712
const packageDirectory = fileName.substring(0, nodeModulesPathParts.packageRootIndex);
17131713
const packageJsonCache = project.getModuleResolutionCache()?.getPackageJsonInfoCache();
17141714
const compilerOptions = project.getCompilationSettings();
1715-
const packageJson = getPackageScopeForPath(getNormalizedAbsolutePath(packageDirectory + "/package.json", project.getCurrentDirectory()), getTemporaryModuleResolutionState(packageJsonCache, project, compilerOptions));
1715+
const packageJson = getPackageScopeForPath(getNormalizedAbsolutePath(packageDirectory, project.getCurrentDirectory()), getTemporaryModuleResolutionState(packageJsonCache, project, compilerOptions));
17161716
if (!packageJson) return undefined;
17171717
// Use fake options instead of actual compiler options to avoid following export map if the project uses node16 or nodenext -
17181718
// Mapping from an export map entry across packages is out of scope for now. Returned entrypoints will only be what can be

src/testRunner/unittests/tsc/moduleResolution.ts

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import {
2+
ModuleKind,
3+
ScriptTarget,
4+
} from "../../_namespaces/ts.js";
15
import { dedent } from "../../_namespaces/Utils.js";
26
import { jsonToReadableText } from "../helpers.js";
37
import {
@@ -6,11 +10,17 @@ import {
610
getFsContentsForAlternateResultDts,
711
getFsContentsForAlternateResultPackageJson,
812
} from "../helpers/alternateResult.js";
9-
import { libContent } from "../helpers/contents.js";
13+
import {
14+
compilerOptionsToConfigJson,
15+
libContent,
16+
} from "../helpers/contents.js";
1017
import { verifyTsc } from "../helpers/tsc.js";
1118
import { verifyTscWatch } from "../helpers/tscWatch.js";
1219
import { loadProjectFromFiles } from "../helpers/vfs.js";
13-
import { createWatchedSystem } from "../helpers/virtualFileSystemWithWatch.js";
20+
import {
21+
createWatchedSystem,
22+
libFile,
23+
} from "../helpers/virtualFileSystemWithWatch.js";
1424

1525
describe("unittests:: tsc:: moduleResolution::", () => {
1626
verifyTsc({
@@ -271,4 +281,40 @@ describe("unittests:: tsc:: moduleResolution::", () => {
271281
},
272282
],
273283
});
284+
285+
verifyTsc({
286+
scenario: "moduleResolution",
287+
subScenario: "package json scope",
288+
fs: () =>
289+
loadProjectFromFiles({
290+
"/src/projects/project/src/tsconfig.json": jsonToReadableText({
291+
compilerOptions: compilerOptionsToConfigJson({
292+
target: ScriptTarget.ES2016,
293+
composite: true,
294+
module: ModuleKind.Node16,
295+
traceResolution: true,
296+
}),
297+
files: [
298+
"main.ts",
299+
"fileA.ts",
300+
"fileB.mts",
301+
],
302+
}),
303+
"/src/projects/project/src/main.ts": "export const x = 10;",
304+
"/src/projects/project/src/fileA.ts": dedent`
305+
import { foo } from "./fileB.mjs";
306+
foo();
307+
`,
308+
"/src/projects/project/src/fileB.mts": "export function foo() {}",
309+
"/src/projects/project/package.json": jsonToReadableText({ name: "app", version: "1.0.0" }),
310+
"/lib/lib.es2016.full.d.ts": libFile.content,
311+
}, { cwd: "/src/projects/project" }),
312+
commandLineArgs: ["-p", "src", "--explainFiles", "--extendedDiagnostics"],
313+
edits: [
314+
{
315+
caption: "Delete package.json",
316+
edit: fs => fs.unlinkSync(`/src/projects/project/package.json`),
317+
},
318+
],
319+
});
274320
});

tests/baselines/reference/tsc/composite/synthetic-jsx-import-of-ESM-module-from-CJS-module-error-on-jsx-element.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ exports.default = (0, jsx_runtime_1.jsx)("div", {});
6969

7070

7171
//// [/tsconfig.tsbuildinfo]
72-
{"fileNames":["./lib/lib.d.ts","./node_modules/solid-js/jsx-runtime.d.ts","./src/main.tsx"],"fileIdsList":[[2]],"fileInfos":[{"version":"3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedFormat":1},{"version":"-3511680495-export namespace JSX {\n type IntrinsicElements = { div: {}; };\n}\n","impliedFormat":99},{"version":"-359851309-export default <div/>;","signature":"2119670487-declare const _default: any;\nexport default _default;\n","impliedFormat":1}],"root":[1,3],"options":{"composite":true,"jsx":4,"jsxImportSource":"solid-js","module":100},"referencedMap":[[3,1]],"semanticDiagnosticsPerFile":[[3,[{"start":15,"length":6,"code":1479,"category":1,"messageText":{"messageText":"The current file is a CommonJS module whose imports will produce 'require' calls; however, the referenced file is an ECMAScript module and cannot be imported with 'require'. Consider writing a dynamic 'import(\"solid-js/jsx-runtime\")' call instead.","category":1,"code":1479,"next":[{"messageText":"To convert this file to an ECMAScript module, create a local package.json file with `{ \"type\": \"module\" }`.","category":3,"code":1483}]}}]]],"latestChangedDtsFile":"./src/main.d.ts","version":"FakeTSVersion"}
72+
{"fileNames":["./lib/lib.d.ts","./node_modules/solid-js/jsx-runtime.d.ts","./src/main.tsx"],"fileIdsList":[[2]],"fileInfos":[{"version":"3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedFormat":1},{"version":"-3511680495-export namespace JSX {\n type IntrinsicElements = { div: {}; };\n}\n","impliedFormat":99},{"version":"-359851309-export default <div/>;","signature":"2119670487-declare const _default: any;\nexport default _default;\n","impliedFormat":1}],"root":[1,3],"options":{"composite":true,"jsx":4,"jsxImportSource":"solid-js","module":100},"referencedMap":[[3,1]],"semanticDiagnosticsPerFile":[[3,[{"start":15,"length":6,"code":1479,"category":1,"messageText":{"messageText":"The current file is a CommonJS module whose imports will produce 'require' calls; however, the referenced file is an ECMAScript module and cannot be imported with 'require'. Consider writing a dynamic 'import(\"solid-js/jsx-runtime\")' call instead.","category":1,"code":1479,"next":[{"info":true}]}}]]],"latestChangedDtsFile":"./src/main.d.ts","version":"FakeTSVersion"}
7373

7474
//// [/tsconfig.tsbuildinfo.readable.baseline.txt]
7575
{
@@ -151,9 +151,7 @@ exports.default = (0, jsx_runtime_1.jsx)("div", {});
151151
"code": 1479,
152152
"next": [
153153
{
154-
"messageText": "To convert this file to an ECMAScript module, create a local package.json file with `{ \"type\": \"module\" }`.",
155-
"category": 3,
156-
"code": 1483
154+
"info": true
157155
}
158156
]
159157
}
@@ -163,6 +161,6 @@ exports.default = (0, jsx_runtime_1.jsx)("div", {});
163161
],
164162
"latestChangedDtsFile": "./src/main.d.ts",
165163
"version": "FakeTSVersion",
166-
"size": 1628
164+
"size": 1487
167165
}
168166

0 commit comments

Comments
 (0)