Keep track of why files are in the program (#40011)

* --explainFiles currently hardcoded

* Move configFileSpecs to configFile so it can be used in program later

* Explain root file inclusion reason and explain include files in the log

* Baseline explainFiles

* Fix incorrectly reporting of file list two times in --b mode

* Fix unnecessary new lines in output represented incorretly in the baseline

* More tests

* More cleaning up

* Keep listing files in same order as list files, just add explaination

* Fix double listing of file names when the program has errors

* Make diagnostic chains for file include reason

* Add explaination for the file include to diagnostics for program

* Harness ls incorrectly adding tsconfig as the root file

* Fix incorrect use of path for calculating absolute path

* Fix the root file in fourslash

* Test project service options merge

* Add config file name to matched by include explaination

* Add test for when the file changes and program is reused completely but related file information is reattached to correct location

* Handle file preprocessing diagnostics updates when program is reused and related information location changes

* Moved types to types.ts

* Refactoring and cleanup

* More cleanup

* More refatoring

* Handle synthetic imports

* Baselines after merge
This commit is contained in:
Sheetal Nandi
2020-12-08 16:10:05 -08:00
committed by GitHub
parent 49d7de17d6
commit 2eca17d7c1
84 changed files with 7013 additions and 723 deletions
+133 -40
View File
@@ -611,12 +611,22 @@ export = C;
"c.ts",
`/// <reference path="D.ts"/>`.indexOf(`D.ts`),
"D.ts".length,
Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
"D.ts",
"d.ts",
tscWatch.getDiagnosticMessageChain(
Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
["D.ts", "d.ts"],
[
tscWatch.getDiagnosticMessageChain(
Diagnostics.The_file_is_in_the_program_because_Colon,
emptyArray,
[
tscWatch.getDiagnosticMessageChain(Diagnostics.Referenced_via_0_from_file_1, ["D.ts", "c.ts"]),
tscWatch.getDiagnosticMessageChain(Diagnostics.Root_file_specified_for_compilation)
]
)
],
)
),
reportsUnnecessary: undefined,
reportsDeprecated: undefined
relatedInformation: undefined,
}]
);
});
@@ -638,12 +648,22 @@ export = C;
"c.ts",
`import {x} from "D"`.indexOf(`"D"`),
`"D"`.length,
Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
"/a/b/D.ts",
"d.ts",
tscWatch.getDiagnosticMessageChain(
Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
["/a/b/D.ts", "d.ts"],
[
tscWatch.getDiagnosticMessageChain(
Diagnostics.The_file_is_in_the_program_because_Colon,
emptyArray,
[
tscWatch.getDiagnosticMessageChain(Diagnostics.Imported_via_0_from_file_1, [`"D"`, "c.ts"]),
tscWatch.getDiagnosticMessageChain(Diagnostics.Root_file_specified_for_compilation)
]
)
],
)
),
reportsUnnecessary: undefined,
reportsDeprecated: undefined
relatedInformation: undefined,
}]
);
});
@@ -665,12 +685,22 @@ export = C;
"moduleA.ts",
`import {x} from "./ModuleB"`.indexOf(`"./ModuleB"`),
`"./ModuleB"`.length,
Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
"ModuleB.ts",
"moduleB.ts",
tscWatch.getDiagnosticMessageChain(
Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
["ModuleB.ts", "moduleB.ts"],
[
tscWatch.getDiagnosticMessageChain(
Diagnostics.The_file_is_in_the_program_because_Colon,
emptyArray,
[
tscWatch.getDiagnosticMessageChain(Diagnostics.Imported_via_0_from_file_1, [`"./ModuleB"`, "moduleA.ts"]),
tscWatch.getDiagnosticMessageChain(Diagnostics.Root_file_specified_for_compilation)
]
)
],
)
),
reportsUnnecessary: undefined,
reportsDeprecated: undefined
relatedInformation: undefined
}]
);
});
@@ -693,12 +723,22 @@ export = C;
"c.ts",
`import {x} from "D"`.indexOf(`"D"`),
`"D"`.length,
Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
"/a/b/D.ts",
"d.ts",
tscWatch.getDiagnosticMessageChain(
Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
["/a/b/D.ts", "d.ts"],
[
tscWatch.getDiagnosticMessageChain(
Diagnostics.The_file_is_in_the_program_because_Colon,
emptyArray,
[
tscWatch.getDiagnosticMessageChain(Diagnostics.Imported_via_0_from_file_1, [`"D"`, "c.ts"]),
tscWatch.getDiagnosticMessageChain(Diagnostics.Root_file_specified_for_compilation)
]
)
],
)
),
reportsUnnecessary: undefined,
reportsDeprecated: undefined
relatedInformation: undefined
}]
);
});
@@ -715,34 +755,65 @@ export = C;
"",
/*useCaseSensitiveFileNames*/ false,
["moduleA.ts", "moduleB.ts", "moduleC.ts"],
program => [
{
program => {
const importInA = {
...tscWatch.getDiagnosticOfFileFromProgram(
program,
"moduleA.ts",
`import a = require("./ModuleC")`.indexOf(`"./ModuleC"`),
`"./ModuleC"`.length,
Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
"ModuleC.ts",
"moduleC.ts",
Diagnostics.File_is_included_via_import_here,
),
reportsUnnecessary: undefined,
reportsDeprecated: undefined
},
{
};
const importInB = {
...tscWatch.getDiagnosticOfFileFromProgram(
program,
"moduleB.ts",
`import a = require("./moduleC")`.indexOf(`"./moduleC"`),
`"./moduleC"`.length,
Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing,
"moduleC.ts",
"ModuleC.ts"
Diagnostics.File_is_included_via_import_here,
),
reportsUnnecessary: undefined,
reportsDeprecated: undefined
}
]
};
const importHereInA = tscWatch.getDiagnosticMessageChain(Diagnostics.Imported_via_0_from_file_1, [`"./ModuleC"`, "moduleA.ts"]);
const importHereInB = tscWatch.getDiagnosticMessageChain(Diagnostics.Imported_via_0_from_file_1, [`"./moduleC"`, "moduleB.ts"]);
const details = [tscWatch.getDiagnosticMessageChain(
Diagnostics.The_file_is_in_the_program_because_Colon,
emptyArray,
[importHereInA, importHereInB, tscWatch.getDiagnosticMessageChain(Diagnostics.Root_file_specified_for_compilation)]
)];
return [
{
...tscWatch.getDiagnosticOfFileFrom(
importInA.file,
importInA.start,
importInA.length,
tscWatch.getDiagnosticMessageChain(
Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
["ModuleC.ts", "moduleC.ts" ],
details,
)
),
relatedInformation: [importInB]
},
{
...tscWatch.getDiagnosticOfFileFrom(
importInB.file,
importInB.start,
importInB.length,
tscWatch.getDiagnosticMessageChain(
Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing,
["moduleC.ts", "ModuleC.ts"],
details,
)
),
relatedInformation: [importInA]
}
];
}
);
});
@@ -768,12 +839,34 @@ import b = require("./moduleB");
"moduleB.ts",
`import a = require("./moduleC")`.indexOf(`"./moduleC"`),
`"./moduleC"`.length,
Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing,
"/a/B/c/moduleC.ts",
"/a/B/c/ModuleC.ts"
tscWatch.getDiagnosticMessageChain(
Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing,
["/a/B/c/moduleC.ts", "/a/B/c/ModuleC.ts"],
[
tscWatch.getDiagnosticMessageChain(
Diagnostics.The_file_is_in_the_program_because_Colon,
emptyArray,
[
tscWatch.getDiagnosticMessageChain(Diagnostics.Imported_via_0_from_file_1, [`"./ModuleC"`, "/a/B/c/moduleA.ts"]),
tscWatch.getDiagnosticMessageChain(Diagnostics.Imported_via_0_from_file_1, [`"./moduleC"`, "/a/B/c/moduleB.ts"])
]
)
],
)
),
reportsUnnecessary: undefined,
reportsDeprecated: undefined
relatedInformation: [
{
...tscWatch.getDiagnosticOfFileFromProgram(
program,
"moduleA.ts",
`import a = require("./ModuleC")`.indexOf(`"./ModuleC"`),
`"./ModuleC"`.length,
Diagnostics.File_is_included_via_import_here,
),
reportsUnnecessary: undefined,
reportsDeprecated: undefined
}
]
}]
);
});
@@ -1384,14 +1477,14 @@ import b = require("./moduleB");
},
};
const program1 = createProgram(names, {}, compilerHost);
const diagnostics1 = program1.getFileProcessingDiagnostics().getDiagnostics();
const diagnostics1 = program1.getOptionsDiagnostics();
assert.equal(diagnostics1.length, 1, "expected one diagnostic");
const program2 = createProgram(names, {}, compilerHost, program1);
assert.isTrue(program2.structureIsReused === StructureIsReused.Completely);
const diagnostics2 = program1.getFileProcessingDiagnostics().getDiagnostics();
const diagnostics2 = program2.getOptionsDiagnostics();
assert.equal(diagnostics2.length, 1, "expected one diagnostic");
assert.equal(diagnostics1[0].messageText, diagnostics2[0].messageText, "expected one diagnostic");
assert.deepEqual(diagnostics1[0].messageText, diagnostics2[0].messageText, "expected one diagnostic");
});
it("Modules in the same .d.ts file are preferred to external files", () => {
+1 -1
View File
@@ -123,7 +123,7 @@ namespace ts {
const program = createProgram(["test.ts"], { module: ModuleKind.ES2015 }, host);
assert(program.getSourceFiles().length === 1, "expected 'getSourceFiles' length to be 1");
assert(program.getMissingFilePaths().length === 0, "expected 'getMissingFilePaths' length to be 0");
assert(program.getFileProcessingDiagnostics().getDiagnostics().length === 0, "expected 'getFileProcessingDiagnostics' length to be 0");
assert((program.getFileProcessingDiagnostics()?.length || 0) === 0, "expected 'getFileProcessingDiagnostics' length to be 0");
});
});
+10 -2
View File
@@ -84,6 +84,7 @@ namespace ts {
ignoreDtsChanged?: true;
ignoreDtsUnchanged?: true;
baselineOnly?: true;
additionalCommandLineArgs?: string[];
}
function verifyOutFileScenario({
@@ -92,7 +93,8 @@ namespace ts {
modifyAgainFs,
ignoreDtsChanged,
ignoreDtsUnchanged,
baselineOnly
baselineOnly,
additionalCommandLineArgs,
}: VerifyOutFileScenarioInput) {
const incrementalScenarios: TscIncremental[] = [];
if (!ignoreDtsChanged) {
@@ -117,7 +119,7 @@ namespace ts {
subScenario,
fs: () => outFileFs,
scenario: "outfile-concat",
commandLineArgs: ["--b", "/src/third", "--verbose"],
commandLineArgs: ["--b", "/src/third", "--verbose", ...(additionalCommandLineArgs || [])],
baselineSourceMap: true,
modifyFs,
baselineReadFileCalls: !baselineOnly,
@@ -133,6 +135,12 @@ namespace ts {
subScenario: "baseline sectioned sourcemaps",
});
verifyOutFileScenario({
subScenario: "explainFiles",
additionalCommandLineArgs: ["--explainFiles"],
baselineOnly: true
});
// Verify baseline with build info + dts unChanged
verifyOutFileScenario({
subScenario: "when final project is not composite but uses project references",
@@ -13,21 +13,21 @@ namespace ts {
scenario: "resolveJsonModule",
subScenario: "include only",
fs: () => projFs,
commandLineArgs: ["--b", "/src/tsconfig_withInclude.json"],
commandLineArgs: ["--b", "/src/tsconfig_withInclude.json", "--v", "--explainFiles"],
});
verifyTsc({
scenario: "resolveJsonModule",
subScenario: "include of json along with other include",
fs: () => projFs,
commandLineArgs: ["--b", "/src/tsconfig_withIncludeOfJson.json"],
commandLineArgs: ["--b", "/src/tsconfig_withIncludeOfJson.json", "--v", "--explainFiles"],
});
verifyTsc({
scenario: "resolveJsonModule",
subScenario: "include of json along with other include and file name matches ts file",
fs: () => projFs,
commandLineArgs: ["--b", "/src/tsconfig_withIncludeOfJson.json"],
commandLineArgs: ["--b", "/src/tsconfig_withIncludeOfJson.json", "--v", "--explainFiles"],
modifyFs: fs => {
fs.rimrafSync("/src/src/hello.json");
fs.writeFileSync("/src/src/index.json", JSON.stringify({ hello: "world" }));
@@ -41,21 +41,21 @@ export default hello.hello`);
scenario: "resolveJsonModule",
subScenario: "files containing json file",
fs: () => projFs,
commandLineArgs: ["--b", "/src/tsconfig_withFiles.json"],
commandLineArgs: ["--b", "/src/tsconfig_withFiles.json", "--v", "--explainFiles"],
});
verifyTsc({
scenario: "resolveJsonModule",
subScenario: "include and files",
fs: () => projFs,
commandLineArgs: ["--b", "/src/tsconfig_withIncludeAndFiles.json"],
commandLineArgs: ["--b", "/src/tsconfig_withIncludeAndFiles.json", "--v", "--explainFiles"],
});
verifyTscSerializedIncrementalEdits({
scenario: "resolveJsonModule",
subScenario: "sourcemap",
fs: () => projFs,
commandLineArgs: ["--b", "src/tsconfig_withFiles.json", "--verbose"],
commandLineArgs: ["--b", "src/tsconfig_withFiles.json", "--verbose", "--explainFiles"],
modifyFs: fs => replaceText(fs, "src/tsconfig_withFiles.json", `"composite": true,`, `"composite": true, "sourceMap": true,`),
incrementalScenarios: noChangeOnlyRuns
});
@@ -75,7 +75,7 @@ export default hello.hello`);
scenario: "resolveJsonModule",
subScenario: "importing json module from project reference",
fs: () => loadProjectFromDisk("tests/projects/importJsonFromProjectReference"),
commandLineArgs: ["--b", "src/tsconfig.json", "--verbose"],
commandLineArgs: ["--b", "src/tsconfig.json", "--verbose", "--explainFiles"],
incrementalScenarios: noChangeOnlyRuns
});
});
@@ -386,6 +386,13 @@ class someClass2 { }`),
commandLineArgs: ["--b", "/src/tests", "--listEmittedFiles"],
incrementalScenarios: coreChanges
});
verifyTscSerializedIncrementalEdits({
scenario: "sample1",
subScenario: "explainFiles",
fs: () => projFs,
commandLineArgs: ["--b", "/src/tests", "--explainFiles", "--v"],
incrementalScenarios: coreChanges
});
});
describe("emit output", () => {
@@ -19,7 +19,7 @@ namespace ts {
scenario: "declarationEmit",
subScenario,
sys: () => tscWatch.createWatchedSystem(files, { currentDirectory: tscWatch.projectRoot }),
commandLineArgs: ["-p", rootProject, "--listFiles"],
commandLineArgs: ["-p", rootProject, "--explainFiles"],
changes: emptyArray
});
});
@@ -33,7 +33,7 @@ namespace ts {
files.map(f => changeCaseFile(f, changeCaseFileTestPath, str => str.replace("myproject", "myProject"))),
{ currentDirectory: tscWatch.projectRoot }
),
commandLineArgs: ["-p", rootProject, "--listFiles"],
commandLineArgs: ["-p", rootProject, "--explainFiles"],
changes: emptyArray
});
});
-3
View File
@@ -37,9 +37,6 @@ namespace ts {
getPrograms: () => readonly CommandLineProgram[];
}
function isBuilderProgram<T extends BuilderProgram>(program: Program | T): program is T {
return !!(program as T).getState;
}
function isAnyProgram(program: Program | EmitAndSemanticDiagnosticsBuilderProgram | ParsedCommandLine): program is Program | EmitAndSemanticDiagnosticsBuilderProgram {
return !!(program as Program | EmitAndSemanticDiagnosticsBuilderProgram).getCompilerOptions;
}
+1 -1
View File
@@ -16,7 +16,7 @@ namespace ts {
]
}`,
}),
commandLineArgs: ["--incremental", "--p", "src/project", "--tsBuildInfoFile", "src/project/.tsbuildinfo"],
commandLineArgs: ["--incremental", "--p", "src/project", "--tsBuildInfoFile", "src/project/.tsbuildinfo", "--explainFiles"],
incrementalScenarios: noChangeOnlyRuns
});
@@ -46,5 +46,78 @@ namespace ts.tscWatch {
}
]
});
verifyTscWatch({
scenario: "forceConsistentCasingInFileNames",
subScenario: "when relative information file location changes",
commandLineArgs: ["--w", "--p", ".", "--explainFiles"],
sys: () => {
const moduleA: File = {
path: `${projectRoot}/moduleA.ts`,
content: `import a = require("./ModuleC")`
};
const moduleB: File = {
path: `${projectRoot}/moduleB.ts`,
content: `import a = require("./moduleC")`
};
const moduleC: File = {
path: `${projectRoot}/moduleC.ts`,
content: `export const x = 10;`
};
const tsconfig: File = {
path: `${projectRoot}/tsconfig.json`,
content: JSON.stringify({ compilerOptions: { forceConsistentCasingInFileNames: true } })
};
return createWatchedSystem([moduleA, moduleB, moduleC, libFile, tsconfig], { currentDirectory: projectRoot });
},
changes: [
{
caption: "Prepend a line to moduleA",
change: sys => sys.prependFile(`${projectRoot}/moduleA.ts`, `// some comment
`),
timeouts: runQueuedTimeoutCallbacks,
}
],
});
verifyTscWatch({
scenario: "forceConsistentCasingInFileNames",
subScenario: "jsxImportSource option changed",
commandLineArgs: ["--w", "--p", ".", "--explainFiles"],
sys: () => createWatchedSystem([
libFile,
{
path: `${projectRoot}/node_modules/react/Jsx-runtime/index.d.ts`,
content: `export namespace JSX {
interface Element {}
interface IntrinsicElements {
div: {
propA?: boolean;
};
}
}
export function jsx(...args: any[]): void;
export function jsxs(...args: any[]): void;
export const Fragment: unique symbol;
`,
},
{
path: `${projectRoot}/node_modules/react/package.json`,
content: JSON.stringify({ name: "react", version: "0.0.1" })
},
{
path: `${projectRoot}/index.tsx`,
content: `export const App = () => <div propA={true}></div>;`
},
{
path: `${projectRoot}/tsconfig.json`,
content: JSON.stringify({
compilerOptions: { jsx: "react-jsx", jsxImportSource: "react", forceConsistentCasingInFileNames: true },
files: ["node_modules/react/Jsx-runtime/index.d.ts", "index.tsx"]
})
}
], { currentDirectory: projectRoot }),
changes: emptyArray,
});
});
}
+17 -12
View File
@@ -216,27 +216,32 @@ namespace ts.tscWatch {
);
}
export function getDiagnosticMessageChain(message: DiagnosticMessage, args?: (string | number)[], next?: DiagnosticMessageChain[]): DiagnosticMessageChain {
let text = getLocaleSpecificMessage(message);
if (args?.length) {
text = formatStringFromArgs(text, args);
}
return {
messageText: text,
category: message.category,
code: message.code,
next
};
}
function isDiagnosticMessageChain(message: DiagnosticMessage | DiagnosticMessageChain): message is DiagnosticMessageChain {
return !!(message as DiagnosticMessageChain).messageText;
}
export function getDiagnosticOfFileFrom(file: SourceFile | undefined, start: number | undefined, length: number | undefined, message: DiagnosticMessage | DiagnosticMessageChain, ..._args: (string | number)[]): Diagnostic {
let text: DiagnosticMessageChain | string;
if (isDiagnosticMessageChain(message)) {
text = message;
}
else {
text = getLocaleSpecificMessage(message);
if (arguments.length > 4) {
text = formatStringFromArgs(text, arguments, 4);
}
}
export function getDiagnosticOfFileFrom(file: SourceFile | undefined, start: number | undefined, length: number | undefined, message: DiagnosticMessage | DiagnosticMessageChain, ...args: (string | number)[]): Diagnostic {
return {
file,
start,
length,
messageText: text,
messageText: isDiagnosticMessageChain(message) ?
message :
getDiagnosticMessageChain(message, args).messageText,
category: message.category,
code: message.code,
};
@@ -277,8 +277,9 @@ export interface A {
modifyFs: host => host.deleteFile(`${project}/globals.d.ts`)
});
const jsxImportSourceOptions = { module: "commonjs", jsx: "react-jsx", incremental: true, jsxImportSource: "react" };
const jsxLibraryContent = `export namespace JSX {
describe("with option jsxImportSource", () => {
const jsxImportSourceOptions = { module: "commonjs", jsx: "react-jsx", incremental: true, jsxImportSource: "react" };
const jsxLibraryContent = `export namespace JSX {
interface Element {}
interface IntrinsicElements {
div: {
@@ -291,64 +292,66 @@ export function jsxs(...args: any[]): void;
export const Fragment: unique symbol;
`;
verifyIncrementalWatchEmit({
subScenario: "jsxImportSource option changed",
files: () => [
{ path: libFile.path, content: libContent },
{ path: `${project}/node_modules/react/jsx-runtime/index.d.ts`, content: jsxLibraryContent },
{ path: `${project}/node_modules/react/package.json`, content: JSON.stringify({ name: "react", version: "0.0.1" }) },
{ path: `${project}/node_modules/preact/jsx-runtime/index.d.ts`, content: jsxLibraryContent.replace("propA", "propB") },
{ path: `${project}/node_modules/preact/package.json`, content: JSON.stringify({ name: "preact", version: "0.0.1" }) },
{ path: `${project}/index.tsx`, content: `export const App = () => <div propA={true}></div>;` },
{ path: configFile.path, content: JSON.stringify({ compilerOptions: jsxImportSourceOptions }) }
],
modifyFs: host => host.writeFile(configFile.path, JSON.stringify({ compilerOptions: { ...jsxImportSourceOptions, jsxImportSource: "preact" } }))
});
verifyIncrementalWatchEmit({
subScenario: "jsxImportSource option changed",
files: () => [
{ path: libFile.path, content: libContent },
{ path: `${project}/node_modules/react/jsx-runtime/index.d.ts`, content: jsxLibraryContent },
{ path: `${project}/node_modules/react/package.json`, content: JSON.stringify({ name: "react", version: "0.0.1" }) },
{ path: `${project}/node_modules/preact/jsx-runtime/index.d.ts`, content: jsxLibraryContent.replace("propA", "propB") },
{ path: `${project}/node_modules/preact/package.json`, content: JSON.stringify({ name: "preact", version: "0.0.1" }) },
{ path: `${project}/index.tsx`, content: `export const App = () => <div propA={true}></div>;` },
{ path: configFile.path, content: JSON.stringify({ compilerOptions: jsxImportSourceOptions }) }
],
modifyFs: host => host.writeFile(configFile.path, JSON.stringify({ compilerOptions: { ...jsxImportSourceOptions, jsxImportSource: "preact" } })),
optionsToExtend: ["--explainFiles"]
});
verifyIncrementalWatchEmit({
subScenario: "jsxImportSource backing types added",
files: () => [
{ path: libFile.path, content: libContent },
{ path: `${project}/index.tsx`, content: `export const App = () => <div propA={true}></div>;` },
{ path: configFile.path, content: JSON.stringify({ compilerOptions: jsxImportSourceOptions }) }
],
modifyFs: host => {
host.createDirectory(`${project}/node_modules`);
host.createDirectory(`${project}/node_modules/react`);
host.createDirectory(`${project}/node_modules/react/jsx-runtime`);
host.writeFile(`${project}/node_modules/react/jsx-runtime/index.d.ts`, jsxLibraryContent);
host.writeFile(`${project}/node_modules/react/package.json`, JSON.stringify({ name: "react", version: "0.0.1" }));
}
});
verifyIncrementalWatchEmit({
subScenario: "jsxImportSource backing types added",
files: () => [
{ path: libFile.path, content: libContent },
{ path: `${project}/index.tsx`, content: `export const App = () => <div propA={true}></div>;` },
{ path: configFile.path, content: JSON.stringify({ compilerOptions: jsxImportSourceOptions }) }
],
modifyFs: host => {
host.createDirectory(`${project}/node_modules`);
host.createDirectory(`${project}/node_modules/react`);
host.createDirectory(`${project}/node_modules/react/jsx-runtime`);
host.writeFile(`${project}/node_modules/react/jsx-runtime/index.d.ts`, jsxLibraryContent);
host.writeFile(`${project}/node_modules/react/package.json`, JSON.stringify({ name: "react", version: "0.0.1" }));
}
});
verifyIncrementalWatchEmit({
subScenario: "jsxImportSource backing types removed",
files: () => [
{ path: libFile.path, content: libContent },
{ path: `${project}/node_modules/react/jsx-runtime/index.d.ts`, content: jsxLibraryContent },
{ path: `${project}/node_modules/react/package.json`, content: JSON.stringify({ name: "react", version: "0.0.1" }) },
{ path: `${project}/index.tsx`, content: `export const App = () => <div propA={true}></div>;` },
{ path: configFile.path, content: JSON.stringify({ compilerOptions: jsxImportSourceOptions }) }
],
modifyFs: host => {
host.deleteFile(`${project}/node_modules/react/jsx-runtime/index.d.ts`);
host.deleteFile(`${project}/node_modules/react/package.json`);
}
});
verifyIncrementalWatchEmit({
subScenario: "jsxImportSource backing types removed",
files: () => [
{ path: libFile.path, content: libContent },
{ path: `${project}/node_modules/react/jsx-runtime/index.d.ts`, content: jsxLibraryContent },
{ path: `${project}/node_modules/react/package.json`, content: JSON.stringify({ name: "react", version: "0.0.1" }) },
{ path: `${project}/index.tsx`, content: `export const App = () => <div propA={true}></div>;` },
{ path: configFile.path, content: JSON.stringify({ compilerOptions: jsxImportSourceOptions }) }
],
modifyFs: host => {
host.deleteFile(`${project}/node_modules/react/jsx-runtime/index.d.ts`);
host.deleteFile(`${project}/node_modules/react/package.json`);
}
});
verifyIncrementalWatchEmit({
subScenario: "importHelpers backing types removed",
files: () => [
{ path: libFile.path, content: libContent },
{ path: `${project}/node_modules/tslib/index.d.ts`, content: "export function __assign(...args: any[]): any;" },
{ path: `${project}/node_modules/tslib/package.json`, content: JSON.stringify({ name: "tslib", version: "0.0.1" }) },
{ path: `${project}/index.tsx`, content: `export const x = {...{}};` },
{ path: configFile.path, content: JSON.stringify({ compilerOptions: { importHelpers: true } }) }
],
modifyFs: host => {
host.deleteFile(`${project}/node_modules/tslib/index.d.ts`);
host.deleteFile(`${project}/node_modules/tslib/package.json`);
}
verifyIncrementalWatchEmit({
subScenario: "importHelpers backing types removed",
files: () => [
{ path: libFile.path, content: libContent },
{ path: `${project}/node_modules/tslib/index.d.ts`, content: "export function __assign(...args: any[]): any;" },
{ path: `${project}/node_modules/tslib/package.json`, content: JSON.stringify({ name: "tslib", version: "0.0.1" }) },
{ path: `${project}/index.tsx`, content: `export const x = {...{}};` },
{ path: configFile.path, content: JSON.stringify({ compilerOptions: { importHelpers: true } }) }
],
modifyFs: host => {
host.deleteFile(`${project}/node_modules/tslib/index.d.ts`);
host.deleteFile(`${project}/node_modules/tslib/package.json`);
}
});
});
});
}
@@ -117,7 +117,7 @@ namespace ts.tscWatch {
verifyTscWatch({
scenario,
subScenario: "handle recreated files correctly",
commandLineArgs: ["-w", "-p", configFilePath],
commandLineArgs: ["-w", "-p", configFilePath, "--explainFiles"],
sys: () => {
return createWatchedSystem([libFile, commonFile1, commonFile2, configFile]);
},
@@ -159,7 +159,7 @@ namespace ts.tscWatch {
verifyTscWatch({
scenario,
subScenario: "should reflect change in config file",
commandLineArgs: ["-w", "-p", configFilePath],
commandLineArgs: ["-w", "-p", configFilePath, "--explainFiles"],
sys: () => {
const configFile: File = {
path: configFilePath,
@@ -373,7 +373,7 @@ export class A {
verifyTscWatch({
scenario,
subScenario: "changes in files are reflected in project structure",
commandLineArgs: ["-w", "/a/b/f1.ts"],
commandLineArgs: ["-w", "/a/b/f1.ts", "--explainFiles"],
sys: () => {
const file1 = {
path: "/a/b/f1.ts",
@@ -72,7 +72,7 @@ namespace ts.projectSystem {
content: "{}"
};
const host = createServerHost([f1, libFile, configFile, configFile2]);
const service = createProjectService(host, { useSingleInferredProject: true }, { useInferredProjectPerProjectRoot: true });
const service = createProjectService(host, { useSingleInferredProject: true, useInferredProjectPerProjectRoot: true });
service.openClientFile(f1.path, /*fileContent*/ undefined, /*scriptKind*/ undefined, projectDir);
checkNumberOfProjects(service, { configuredProjects: 1 });
assert.isDefined(service.configuredProjects.get(configFile.path));
@@ -108,7 +108,7 @@ var x = 10;`
content: "const y = 10"
};
const host = createServerHost([config, file, libFile], { useCaseSensitiveFileNames: true });
const service = createProjectService(host, /*parameters*/ undefined, { useInferredProjectPerProjectRoot: true });
const service = createProjectService(host, { useInferredProjectPerProjectRoot: true });
service.openClientFile(untitledFile, "const x = 10;", /*scriptKind*/ undefined, tscWatch.projectRoot);
checkNumberOfProjects(service, { inferredProjects: 1 });
checkProjectActualFiles(service.inferredProjects[0], [untitledFile, libFile.path]);
@@ -146,8 +146,17 @@ namespace ts.projectSystem {
semantic: [createDiagnostic(
location.start,
location.end,
Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing,
[loggerFile.path.toLowerCase(), loggerFile.path]
{
message: Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing,
args: [loggerFile.path.toLowerCase(), loggerFile.path],
next: [{
message: Diagnostics.The_file_is_in_the_program_because_Colon,
next: [
{ message: Diagnostics.Matched_by_include_pattern_0_in_1, args: ["**/*", tsconfig.path] },
{ message: Diagnostics.Imported_via_0_from_file_1, args: [`"./logger"`, anotherFile.path] }
]
}]
}
)],
suggestion: []
}]
+40 -17
View File
@@ -383,17 +383,9 @@ namespace ts.projectSystem {
}
}
export interface CreateProjectServiceParameters {
cancellationToken?: HostCancellationToken;
logger?: server.Logger;
useSingleInferredProject?: boolean;
typingsInstaller?: server.ITypingsInstaller;
eventHandler?: server.ProjectServiceEventHandler;
}
export class TestProjectService extends server.ProjectService {
constructor(host: server.ServerHost, logger: server.Logger, cancellationToken: HostCancellationToken, useSingleInferredProject: boolean,
typingsInstaller: server.ITypingsInstaller, eventHandler: server.ProjectServiceEventHandler, opts: Partial<server.ProjectServiceOptions> = {}) {
typingsInstaller: server.ITypingsInstaller, opts: Partial<server.ProjectServiceOptions> = {}) {
super({
host,
logger,
@@ -402,7 +394,6 @@ namespace ts.projectSystem {
useInferredProjectPerProjectRoot: false,
typingsInstaller,
typesMapLocation: customTypesMap.path,
eventHandler,
...opts
});
}
@@ -411,11 +402,12 @@ namespace ts.projectSystem {
checkNumberOfProjects(this, count);
}
}
export function createProjectService(host: server.ServerHost, parameters: CreateProjectServiceParameters = {}, options?: Partial<server.ProjectServiceOptions>) {
const cancellationToken = parameters.cancellationToken || server.nullCancellationToken;
const logger = parameters.logger || createHasErrorMessageLogger().logger;
const useSingleInferredProject = parameters.useSingleInferredProject !== undefined ? parameters.useSingleInferredProject : false;
return new TestProjectService(host, logger, cancellationToken, useSingleInferredProject, parameters.typingsInstaller!, parameters.eventHandler!, options); // TODO: GH#18217
export function createProjectService(host: server.ServerHost, options?: Partial<server.ProjectServiceOptions>) {
const cancellationToken = options?.cancellationToken || server.nullCancellationToken;
const logger = options?.logger || createHasErrorMessageLogger().logger;
const useSingleInferredProject = options?.useSingleInferredProject !== undefined ? options.useSingleInferredProject : false;
return new TestProjectService(host, logger, cancellationToken, useSingleInferredProject, options?.typingsInstaller || server.nullTypingsInstaller, options);
}
export function checkNumberOfConfiguredProjects(projectService: server.ProjectService, expected: number) {
@@ -700,8 +692,39 @@ namespace ts.projectSystem {
checkNthEvent(session, server.toEvent(eventName, diagnostics), 0, isMostRecent);
}
export function createDiagnostic(start: protocol.Location, end: protocol.Location, message: DiagnosticMessage, args: readonly string[] = [], category = diagnosticCategoryName(message), reportsUnnecessary?: {}, relatedInformation?: protocol.DiagnosticRelatedInformation[], reportsDeprecated?: {}): protocol.Diagnostic {
return { start, end, text: formatStringFromArgs(message.message, args), code: message.code, category, reportsUnnecessary, reportsDeprecated, relatedInformation, source: undefined };
interface DiagnosticChainText {
message: DiagnosticMessage;
args?: readonly string[];
next?: DiagnosticChainText[];
}
function isDiagnosticMessage(diagnostic: DiagnosticMessage | DiagnosticChainText): diagnostic is DiagnosticMessage {
return !!diagnostic && isString((diagnostic as DiagnosticMessage).message);
}
function getTextForDiagnostic(diag: DiagnosticMessage | DiagnosticChainText | undefined, args?: readonly string[], indent = 0) {
if (diag === undefined) return "";
if (isDiagnosticMessage(diag)) return formatStringFromArgs(diag.message, args || emptyArray);
let result = "";
if (indent) {
result += "\n";
for (let i = 0; i < indent; i++) {
result += " ";
}
}
result += formatStringFromArgs(diag.message.message, diag.args || emptyArray);
indent++;
if (diag.next) {
for (const kid of diag.next) {
result += getTextForDiagnostic(kid, /*args*/ undefined, indent);
}
}
return result;
}
export function createDiagnostic(start: protocol.Location, end: protocol.Location, message: DiagnosticMessage | DiagnosticChainText, args?: readonly string[], category?: string, reportsUnnecessary?: {}, relatedInformation?: protocol.DiagnosticRelatedInformation[], reportsDeprecated?: {}): protocol.Diagnostic {
const diag = isDiagnosticMessage(message) ? message : message.message;
return { start, end, text: getTextForDiagnostic(message, args), code: diag.code, category: category || diagnosticCategoryName(diag), reportsUnnecessary, reportsDeprecated, relatedInformation, source: undefined };
}
export function checkCompleteEvent(session: TestSession, numberOfCurrentEvents: number, expectedSequenceId: number, isMostRecent = true): void {
@@ -78,7 +78,7 @@ namespace ts.projectSystem {
};
const host = createServerHost([file1]);
const projectService = createProjectService(host, { useSingleInferredProject: true }, { syntaxOnly: true });
const projectService = createProjectService(host, { useSingleInferredProject: true, syntaxOnly: true });
projectService.openClientFile(file1.path, file1.content);
@@ -250,7 +250,7 @@ namespace ts.projectSystem {
{ path: "/c/file3.ts", content: "let z = 4;" }
];
const host = createServerHost(files, { useCaseSensitiveFileNames });
const projectService = createProjectService(host, { useSingleInferredProject: true, }, { useInferredProjectPerProjectRoot: true });
const projectService = createProjectService(host, { useSingleInferredProject: true, useInferredProjectPerProjectRoot: true });
projectService.setCompilerOptionsForInferredProjects({
allowJs: true,
target: ScriptTarget.ESNext
@@ -118,7 +118,7 @@ namespace ts.projectSystem {
const projName = "proj1";
const host = createServerHost([file1, file2]);
const projectService = createProjectService(host, { useSingleInferredProject: true }, { eventHandler: noop });
const projectService = createProjectService(host, { useSingleInferredProject: true, eventHandler: noop });
projectService.openExternalProject({ rootFiles: toExternalFiles([file1.path, file2.path]), options: {}, projectFileName: projName });
const proj1 = projectService.findProject(projName)!;
@@ -145,7 +145,7 @@ namespace ts.projectSystem {
const externalProjectName = "externalproject";
const host = createServerHost([file1, config1]);
const projectService = createProjectService(host, { useSingleInferredProject: true }, { syntaxOnly: true });
const projectService = createProjectService(host, { useSingleInferredProject: true, syntaxOnly: true });
projectService.openExternalProject({
rootFiles: toExternalFiles([file1.path, config1.path]),
options: {},
@@ -175,7 +175,7 @@ namespace ts.projectSystem {
};
const host = createServerHost([file1, config1]);
const projectService = createProjectService(host, { useSingleInferredProject: true }, { syntaxOnly: true });
const projectService = createProjectService(host, { useSingleInferredProject: true, syntaxOnly: true });
projectService.openClientFile(file1.path, file1.content);
checkNumberOfProjects(projectService, { inferredProjects: 1 });
@@ -201,7 +201,7 @@ namespace ts.projectSystem {
};
const host = createServerHost([file1, config1]);
const projectService = createProjectService(host, { useSingleInferredProject: true }, { syntaxOnly: true });
const projectService = createProjectService(host, { useSingleInferredProject: true, syntaxOnly: true });
projectService.applyChangesInOpenFiles(singleIterator({ fileName: file1.path, content: file1.content }));
checkNumberOfProjects(projectService, { inferredProjects: 1 });
@@ -62,7 +62,7 @@ namespace ts.projectSystem {
it("inferred project", () => {
const host = createServerHost([libFile, file1, file2]);
const service = createProjectService(host, /*parameters*/ undefined, { useInferredProjectPerProjectRoot: true, });
const service = createProjectService(host, { useInferredProjectPerProjectRoot: true, });
service.setHostConfiguration({ watchOptions: { excludeFiles: [file2.path] } });
const timeoutId = host.getNextTimeoutId();
service.setCompilerOptionsForInferredProjects({ excludeDirectories: ["node_modules"] }, tscWatch.projectRoot);
@@ -700,7 +700,7 @@ namespace ts.projectSystem {
const { main, bar, foo } = setupFiles();
const files = [libFile, main, bar, foo];
const host = createServerHost(files, { currentDirectory: tscWatch.projectRoot });
const service = createProjectService(host, {}, { useInferredProjectPerProjectRoot: true });
const service = createProjectService(host, { useInferredProjectPerProjectRoot: true });
setupConfigureHost(service, configureHost);
service.setCompilerOptionsForInferredProjects({ excludeDirectories: ["node_modules"] }, tscWatch.projectRoot);
service.openClientFile(main.path, main.content, ScriptKind.TS, tscWatch.projectRoot);
@@ -743,7 +743,7 @@ namespace ts.projectSystem {
const { main, bar, foo } = setupFiles();
const files = [libFile, main, bar, foo];
const host = createServerHost(files, { currentDirectory: tscWatch.projectRoot });
const service = createProjectService(host, {}, { useInferredProjectPerProjectRoot: true });
const service = createProjectService(host, { useInferredProjectPerProjectRoot: true });
service.setCompilerOptionsForInferredProjects({ excludeDirectories: ["**/../*"] }, tscWatch.projectRoot);
service.openClientFile(main.path, main.content, ScriptKind.TS, tscWatch.projectRoot);
const project = service.inferredProjects[0];