mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-22 22:55:36 -05:00
Handle getScriptVersion correctly to ensure program structure is checked correctly (#36808)
* Fix tests when there are project references but has disableSourceOfProjectReferenceRedirect * Handle getScriptVersion correctly to ensure program structure is checked correctly Fixes #36748 * Harness's language service host doesnt have getProjectVersion. This means earlier we were creating fresh program everytime we did LS operation Now we reuse same program, so quick info depends on order of quickinfo demands * Because same program is used, it unvails a bug that if `export=` is evaluated before finding references, it cant find all definitions from the merge * Update src/server/project.ts Co-Authored-By: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> * Make clearSourceMapperCache required Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
This commit is contained in:
@@ -80,5 +80,61 @@ export function Component(x: Config): any;`
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe("detects program upto date correctly", () => {
|
||||
function verifyProgramUptoDate(useProjectVersion: boolean) {
|
||||
let projectVersion = "1";
|
||||
const files = createMap<{ version: string, text: string; }>();
|
||||
files.set("/project/root.ts", { version: "1", text: `import { foo } from "./other"` });
|
||||
files.set("/project/other.ts", { version: "1", text: `export function foo() { }` });
|
||||
files.set("/lib/lib.d.ts", { version: "1", text: projectSystem.libFile.content });
|
||||
const host: LanguageServiceHost = {
|
||||
useCaseSensitiveFileNames: returnTrue,
|
||||
getCompilationSettings: getDefaultCompilerOptions,
|
||||
fileExists: path => files.has(path),
|
||||
getProjectVersion: !useProjectVersion ? undefined : () => projectVersion,
|
||||
getScriptFileNames: () => ["/project/root.ts"],
|
||||
getScriptVersion: path => files.get(path)?.version || "",
|
||||
getScriptSnapshot: path => {
|
||||
const text = files.get(path)?.text;
|
||||
return text ? ScriptSnapshot.fromString(text) : undefined;
|
||||
},
|
||||
getCurrentDirectory: () => "/project",
|
||||
getDefaultLibFileName: () => "/lib/lib.d.ts"
|
||||
};
|
||||
const ls = ts.createLanguageService(host);
|
||||
const program1 = ls.getProgram()!;
|
||||
const program2 = ls.getProgram()!;
|
||||
assert.strictEqual(program1, program2);
|
||||
verifyProgramFiles(program1);
|
||||
|
||||
// Change other
|
||||
projectVersion = "2";
|
||||
files.set("/project/other.ts", { version: "2", text: `export function foo() { } export function bar() { }` });
|
||||
const program3 = ls.getProgram()!;
|
||||
assert.notStrictEqual(program2, program3);
|
||||
verifyProgramFiles(program3);
|
||||
|
||||
// change root
|
||||
projectVersion = "3";
|
||||
files.set("/project/root.ts", { version: "2", text: `import { foo, bar } from "./other"` });
|
||||
const program4 = ls.getProgram()!;
|
||||
assert.notStrictEqual(program3, program4);
|
||||
verifyProgramFiles(program4);
|
||||
|
||||
function verifyProgramFiles(program: Program) {
|
||||
assert.deepEqual(
|
||||
program.getSourceFiles().map(f => f.fileName),
|
||||
["/lib/lib.d.ts", "/project/other.ts", "/project/root.ts"]
|
||||
);
|
||||
}
|
||||
}
|
||||
it("when host implements getProjectVersion", () => {
|
||||
verifyProgramUptoDate(/*useProjectVersion*/ true);
|
||||
});
|
||||
it("when host does not implement getProjectVersion", () => {
|
||||
verifyProgramUptoDate(/*useProjectVersion*/ false);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -470,7 +470,7 @@ fn5();
|
||||
interface VerifierAndWithRefs {
|
||||
withRefs: boolean;
|
||||
disableSourceOfProjectReferenceRedirect?: true;
|
||||
verifier: (withRefs: boolean) => readonly DocumentPositionMapperVerifier[];
|
||||
verifier: (withRefs: boolean, disableSourceOfProjectReferenceRedirect?: true) => readonly DocumentPositionMapperVerifier[];
|
||||
}
|
||||
|
||||
function openFiles(verifiers: readonly DocumentPositionMapperVerifier[]) {
|
||||
@@ -502,7 +502,7 @@ fn5();
|
||||
onHostCreate(host);
|
||||
}
|
||||
const session = createSession(host);
|
||||
const verifiers = verifier(withRefs && !disableSourceOfProjectReferenceRedirect);
|
||||
const verifiers = verifier(withRefs && !disableSourceOfProjectReferenceRedirect, disableSourceOfProjectReferenceRedirect);
|
||||
openFilesForSession([...openFiles(verifiers), randomFile], session);
|
||||
return { host, session, verifiers };
|
||||
}
|
||||
@@ -724,13 +724,14 @@ fn5();
|
||||
scenarioName,
|
||||
verifier,
|
||||
withRefs,
|
||||
disableSourceOfProjectReferenceRedirect,
|
||||
change,
|
||||
afterChangeActionKey
|
||||
}: VerifyScenarioWithChanges,
|
||||
timeoutBeforeAction: boolean,
|
||||
) {
|
||||
it(scenarioName, () => {
|
||||
const { host, session, verifiers } = openTsFile({ verifier, withRefs });
|
||||
const { host, session, verifiers } = openTsFile({ verifier, withRefs, disableSourceOfProjectReferenceRedirect });
|
||||
|
||||
// Create DocumentPositionMapper
|
||||
firstAction(session, verifiers);
|
||||
@@ -790,6 +791,7 @@ fn5();
|
||||
scenarioName,
|
||||
verifier,
|
||||
withRefs,
|
||||
disableSourceOfProjectReferenceRedirect,
|
||||
fileLocation,
|
||||
fileNotPresentKey,
|
||||
fileCreatedKey,
|
||||
@@ -801,6 +803,7 @@ fn5();
|
||||
const { host, session, verifiers } = openTsFile({
|
||||
verifier,
|
||||
withRefs,
|
||||
disableSourceOfProjectReferenceRedirect,
|
||||
onHostCreate: host => host.deleteFile(fileLocation)
|
||||
});
|
||||
checkProject(session, verifiers, noDts);
|
||||
@@ -813,6 +816,7 @@ fn5();
|
||||
const { host, session, verifiers } = openTsFile({
|
||||
verifier,
|
||||
withRefs,
|
||||
disableSourceOfProjectReferenceRedirect,
|
||||
onHostCreate: host => {
|
||||
fileContents = host.readFile(fileLocation);
|
||||
host.deleteFile(fileLocation);
|
||||
@@ -825,7 +829,7 @@ fn5();
|
||||
});
|
||||
|
||||
it("when file is deleted after actions on the projects", () => {
|
||||
const { host, session, verifiers } = openTsFile({ verifier, withRefs });
|
||||
const { host, session, verifiers } = openTsFile({ verifier, disableSourceOfProjectReferenceRedirect, withRefs });
|
||||
firstAction(session, verifiers);
|
||||
|
||||
// The dependency file is deleted when orphan files are collected
|
||||
@@ -967,31 +971,35 @@ ${dependencyTs.content}`);
|
||||
|
||||
interface VerifyScenario {
|
||||
mainScenario: string;
|
||||
verifier: (withRefs: boolean) => readonly DocumentPositionMapperVerifier[];
|
||||
verifier: (withRefs: boolean, disableSourceOfProjectReferenceRedirect?: true) => readonly DocumentPositionMapperVerifier[];
|
||||
}
|
||||
function verifyScenario(scenario: VerifyScenario) {
|
||||
describe("when main tsconfig doesnt have project reference", () => {
|
||||
verifyScenarioWorker(scenario, /*withRefs*/ false);
|
||||
});
|
||||
describe("when main tsconfig has project reference", () => {
|
||||
verifyScenarioWorker(scenario, /*withRefs*/ true);
|
||||
});
|
||||
describe("when main tsconfig has but has disableSourceOfProjectReferenceRedirect", () => {
|
||||
verifyScenarioWorker(scenario, /*withRefs*/ true);
|
||||
describe(scenario.mainScenario, () => {
|
||||
describe("when main tsconfig doesnt have project reference", () => {
|
||||
verifyScenarioWorker(scenario, /*withRefs*/ false);
|
||||
});
|
||||
describe("when main tsconfig has project reference", () => {
|
||||
verifyScenarioWorker(scenario, /*withRefs*/ true);
|
||||
});
|
||||
describe("when main tsconfig has disableSourceOfProjectReferenceRedirect along with project reference", () => {
|
||||
verifyScenarioWorker(scenario, /*withRefs*/ true, /*disableSourceOfProjectReferenceRedirect*/ true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe("from project that uses dependency", () => {
|
||||
verifyScenario({
|
||||
mainScenario: "can go to definition correctly",
|
||||
verifier: withRefs => [
|
||||
verifier: (withRefs, disableSourceOfProjectReferenceRedirect) => [
|
||||
{
|
||||
...goToDefFromMainTsProjectInfoVerifier(withRefs),
|
||||
main: () => ({
|
||||
action: goToDefFromMainTs,
|
||||
closedInfos: withRefs ?
|
||||
[dependencyTs.path, dependencyConfig.path, libFile.path] :
|
||||
[dependencyTs.path, libFile.path, dtsPath, dtsMapLocation],
|
||||
disableSourceOfProjectReferenceRedirect ?
|
||||
[dependencyTs.path, libFile.path, dtsPath, dtsMapLocation, dependencyConfig.path] :
|
||||
[dependencyTs.path, libFile.path, dtsPath, dtsMapLocation],
|
||||
otherWatchedFiles: [mainConfig.path],
|
||||
expectsDts: !withRefs, // Dts script info present only if no project reference
|
||||
expectsMap: !withRefs // Map script info present only if no project reference
|
||||
@@ -1097,7 +1105,7 @@ ${dependencyTs.content}`);
|
||||
describe("when opening depedency and usage project", () => {
|
||||
verifyScenario({
|
||||
mainScenario: "goto Definition in usage and rename locations from defining project",
|
||||
verifier: withRefs => [
|
||||
verifier: (withRefs, disableSourceOfProjectReferenceRedirect) => [
|
||||
{
|
||||
...goToDefFromMainTsProjectInfoVerifier(withRefs),
|
||||
main: () => ({
|
||||
@@ -1105,9 +1113,11 @@ ${dependencyTs.content}`);
|
||||
// DependencyTs is open, so omit it from closed infos
|
||||
closedInfos: withRefs ?
|
||||
[dependencyConfig.path, libFile.path] :
|
||||
[libFile.path, dtsPath, dtsMapLocation],
|
||||
otherWatchedFiles: withRefs ?
|
||||
[mainConfig.path] : // Its in closed info
|
||||
disableSourceOfProjectReferenceRedirect ?
|
||||
[libFile.path, dtsPath, dtsMapLocation, dependencyConfig.path] :
|
||||
[libFile.path, dtsPath, dtsMapLocation],
|
||||
otherWatchedFiles: withRefs || disableSourceOfProjectReferenceRedirect ?
|
||||
[mainConfig.path] : // dependencyConfig is in closed info
|
||||
[mainConfig.path, dependencyConfig.path],
|
||||
expectsDts: !withRefs, // Dts script info present only if no project reference
|
||||
expectsMap: !withRefs // Map script info present only if no project reference
|
||||
@@ -1179,9 +1189,11 @@ ${dependencyTs.content}`);
|
||||
// DependencyTs is open, so omit it from closed infos
|
||||
closedInfos: withRefs ?
|
||||
[dependencyConfig.path, libFile.path, dtsLocation, dtsMapLocation] :
|
||||
[libFile.path, dtsPath, dtsMapLocation],
|
||||
otherWatchedFiles: withRefs ?
|
||||
[mainConfig.path] : // Its in closed info
|
||||
disableSourceOfProjectReferenceRedirect ?
|
||||
[libFile.path, dtsPath, dtsMapLocation, dependencyConfig.path] :
|
||||
[libFile.path, dtsPath, dtsMapLocation],
|
||||
otherWatchedFiles: withRefs || disableSourceOfProjectReferenceRedirect ?
|
||||
[mainConfig.path] : // dependencyConfig is in closed info
|
||||
[mainConfig.path, dependencyConfig.path],
|
||||
expectsDts: true,
|
||||
expectsMap: true,
|
||||
|
||||
Reference in New Issue
Block a user