mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-13 16:34:39 -06:00
If emitting declaration file, update the d.ts signature in state to not have to save all files when different file changes without changing its shape (#37483)
* Add test that calling getAffectedFiles on two different files (even if the second one is saved) returns all files * If emitting declaration file, update the d.ts signature in state to not have to save all files when different file changes without changing its shape Fixes #30508
This commit is contained in:
parent
5e9c43607f
commit
8f64d667f2
@ -292,10 +292,12 @@ namespace ts {
|
||||
* This should be called whenever it is safe to commit the state of the builder
|
||||
*/
|
||||
export function updateSignaturesFromCache(state: BuilderState, signatureCache: Map<string>) {
|
||||
signatureCache.forEach((signature, path) => {
|
||||
state.fileInfos.get(path)!.signature = signature;
|
||||
state.hasCalledUpdateShapeSignature.set(path, true);
|
||||
});
|
||||
signatureCache.forEach((signature, path) => updateSignatureOfFile(state, signature, path as Path));
|
||||
}
|
||||
|
||||
export function updateSignatureOfFile(state: BuilderState, signature: string | undefined, path: Path) {
|
||||
state.fileInfos.get(path)!.signature = signature;
|
||||
state.hasCalledUpdateShapeSignature.set(path, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -603,6 +603,15 @@ namespace ts.server {
|
||||
const outputFileAbsoluteFileName = getNormalizedAbsolutePath(outputFile.name, this.currentDirectory);
|
||||
writeFile(outputFileAbsoluteFileName, outputFile.text, outputFile.writeByteOrderMark);
|
||||
}
|
||||
|
||||
// Update the signature
|
||||
if (this.builderState && getEmitDeclarations(this.compilerOptions)) {
|
||||
const dtsFiles = outputFiles.filter(f => fileExtensionIs(f.name, Extension.Dts));
|
||||
if (dtsFiles.length === 1) {
|
||||
const sourceFile = this.program!.getSourceFile(scriptInfo.fileName)!;
|
||||
BuilderState.updateSignatureOfFile(this.builderState, this.projectService.host.createHash!(dtsFiles[0].text), sourceFile.resolvedPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { emitSkipped, diagnostics };
|
||||
|
||||
@ -880,6 +880,131 @@ namespace ts.projectSystem {
|
||||
assert.isFalse(host.fileExists(`${tscWatch.projectRoot}/test/file2.d.ts`));
|
||||
}
|
||||
});
|
||||
|
||||
describe("compile on save in global files", () => {
|
||||
describe("when program contains module", () => {
|
||||
it("when d.ts emit is enabled", () => {
|
||||
verifyGlobalSave(/*declaration*/ true, /*hasModule*/ true);
|
||||
});
|
||||
it("when d.ts emit is not enabled", () => {
|
||||
verifyGlobalSave(/*declaration*/ false, /*hasModule*/ true);
|
||||
});
|
||||
});
|
||||
describe("when program doesnt have module", () => {
|
||||
it("when d.ts emit is enabled", () => {
|
||||
verifyGlobalSave(/*declaration*/ true, /*hasModule*/ false);
|
||||
});
|
||||
it("when d.ts emit is not enabled", () => {
|
||||
verifyGlobalSave(/*declaration*/ false, /*hasModule*/ false);
|
||||
});
|
||||
});
|
||||
function verifyGlobalSave(declaration: boolean,hasModule: boolean) {
|
||||
const config: File = {
|
||||
path: `${tscWatch.projectRoot}/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
compileOnSave: true,
|
||||
compilerOptions: {
|
||||
declaration,
|
||||
module: hasModule ? undefined : "none"
|
||||
},
|
||||
})
|
||||
};
|
||||
const file1: File = {
|
||||
path: `${tscWatch.projectRoot}/file1.ts`,
|
||||
content: `const x = 1;
|
||||
function foo() {
|
||||
return "hello";
|
||||
}`
|
||||
};
|
||||
const file2: File = {
|
||||
path: `${tscWatch.projectRoot}/file2.ts`,
|
||||
content: `const y = 2;
|
||||
function bar() {
|
||||
return "world";
|
||||
}`
|
||||
};
|
||||
const file3: File = {
|
||||
path: `${tscWatch.projectRoot}/file3.ts`,
|
||||
content: "const xy = 3;"
|
||||
};
|
||||
const module: File = {
|
||||
path: `${tscWatch.projectRoot}/module.ts`,
|
||||
content: "export const xyz = 4;"
|
||||
};
|
||||
const files = [file1, file2, file3, ...(hasModule ? [module] : emptyArray)];
|
||||
const host = createServerHost([...files, config, libFile]);
|
||||
const session = createSession(host);
|
||||
openFilesForSession([file1, file2], session);
|
||||
|
||||
const affectedFileResponse = session.executeCommandSeq<protocol.CompileOnSaveAffectedFileListRequest>({
|
||||
command: protocol.CommandTypes.CompileOnSaveAffectedFileList,
|
||||
arguments: { file: file1.path }
|
||||
}).response as protocol.CompileOnSaveAffectedFileListSingleProject[];
|
||||
assert.deepEqual(affectedFileResponse, [
|
||||
{ fileNames: files.map(f => f.path), projectFileName: config.path, projectUsesOutFile: false }
|
||||
]);
|
||||
verifyFileSave(file1);
|
||||
verifyFileSave(file2);
|
||||
verifyFileSave(file3);
|
||||
if (hasModule) {
|
||||
verifyFileSave(module);
|
||||
}
|
||||
|
||||
// Change file1 get affected file list
|
||||
verifyLocalEdit(file1, "hello", "world");
|
||||
|
||||
// Change file2 get affected file list = will return only file2 if --declaration otherwise all files
|
||||
verifyLocalEdit(file2, "world", "hello", /*returnsAllFilesAsAffected*/ !declaration);
|
||||
|
||||
function verifyFileSave(file: File) {
|
||||
const response = session.executeCommandSeq<protocol.CompileOnSaveEmitFileRequest>({
|
||||
command: protocol.CommandTypes.CompileOnSaveEmitFile,
|
||||
arguments: { file: file.path }
|
||||
}).response;
|
||||
assert.isTrue(response);
|
||||
assert.strictEqual(
|
||||
host.readFile(changeExtension(file.path, ".js")),
|
||||
file === module ?
|
||||
`"use strict";\nexports.__esModule = true;\nexports.xyz = void 0;\nexports.xyz = 4;\n` :
|
||||
`${file.content.replace("const", "var")}\n`
|
||||
);
|
||||
if (declaration) {
|
||||
assert.strictEqual(
|
||||
host.readFile(changeExtension(file.path, ".d.ts")),
|
||||
(file.content.substr(0, file.content.indexOf(" {") === -1 ? file.content.length : file.content.indexOf(" {"))
|
||||
.replace("const ", "declare const ")
|
||||
.replace("function ", "declare function ")
|
||||
.replace(")", "): string;")) + "\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function verifyLocalEdit(file: File, oldText: string, newText: string, returnsAllFilesAsAffected?: boolean) {
|
||||
// Change file1 get affected file list
|
||||
session.executeCommandSeq<protocol.UpdateOpenRequest>({
|
||||
command: protocol.CommandTypes.UpdateOpen,
|
||||
arguments: {
|
||||
changedFiles: [{
|
||||
fileName: file.path,
|
||||
textChanges: [{
|
||||
newText,
|
||||
...protocolTextSpanFromSubstring(file.content, oldText)
|
||||
}]
|
||||
}]
|
||||
}
|
||||
});
|
||||
const affectedFileResponse = session.executeCommandSeq<protocol.CompileOnSaveAffectedFileListRequest>({
|
||||
command: protocol.CommandTypes.CompileOnSaveAffectedFileList,
|
||||
arguments: { file: file.path }
|
||||
}).response as protocol.CompileOnSaveAffectedFileListSingleProject[];
|
||||
assert.deepEqual(affectedFileResponse, [
|
||||
{ fileNames: [file.path, ...(returnsAllFilesAsAffected ? files.filter(f => f !== file).map(f => f.path) : emptyArray)], projectFileName: config.path, projectUsesOutFile: false }
|
||||
]);
|
||||
file.content = file.content.replace(oldText, newText);
|
||||
verifyFileSave(file);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("unittests:: tsserver:: compileOnSave:: CompileOnSaveAffectedFileListRequest with and without projectFileName in request", () => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user