Modify api to emit affected files using callback instead of generating in memory output
Also marking few apis introduced during watch improvements changes that are suppose to be internal for now
This commit is contained in:
parent
f9c901ada7
commit
8fbfb5ffc0
|
@ -6,56 +6,36 @@ namespace ts {
|
|||
emitSkipped: boolean;
|
||||
}
|
||||
|
||||
export interface EmitOutputDetailed extends EmitOutput {
|
||||
diagnostics: Diagnostic[];
|
||||
sourceMaps: SourceMapData[];
|
||||
emittedSourceFiles: SourceFile[];
|
||||
}
|
||||
|
||||
export interface OutputFile {
|
||||
name: string;
|
||||
writeByteOrderMark: boolean;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export function getFileEmitOutput(program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean, isDetailed: boolean,
|
||||
cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): EmitOutput | EmitOutputDetailed {
|
||||
const outputFiles: OutputFile[] = [];
|
||||
let emittedSourceFiles: SourceFile[];
|
||||
const emitResult = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
|
||||
if (!isDetailed) {
|
||||
return { outputFiles, emitSkipped: emitResult.emitSkipped };
|
||||
}
|
||||
|
||||
return {
|
||||
outputFiles,
|
||||
emitSkipped: emitResult.emitSkipped,
|
||||
diagnostics: emitResult.diagnostics,
|
||||
sourceMaps: emitResult.sourceMaps,
|
||||
emittedSourceFiles
|
||||
};
|
||||
|
||||
function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, _onError: (message: string) => void, sourceFiles: SourceFile[]) {
|
||||
outputFiles.push({ name: fileName, writeByteOrderMark, text });
|
||||
if (isDetailed) {
|
||||
emittedSourceFiles = addRange(emittedSourceFiles, sourceFiles);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
export function getFileEmitOutput(program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean,
|
||||
cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): EmitOutput {
|
||||
const outputFiles: OutputFile[] = [];
|
||||
const emitResult = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
|
||||
return { outputFiles, emitSkipped: emitResult.emitSkipped };
|
||||
|
||||
function writeFile(fileName: string, text: string, writeByteOrderMark: boolean) {
|
||||
outputFiles.push({ name: fileName, writeByteOrderMark, text });
|
||||
}
|
||||
}
|
||||
|
||||
export interface Builder {
|
||||
/** Called to inform builder about new program */
|
||||
updateProgram(newProgram: Program): void;
|
||||
|
||||
/** Gets the files affected by the file path */
|
||||
getFilesAffectedBy(program: Program, path: Path): ReadonlyArray<SourceFile>;
|
||||
/** Emits the given file */
|
||||
emitFile(program: Program, path: Path): EmitOutput;
|
||||
|
||||
/** Emit the changed files and clear the cache of the changed files */
|
||||
emitChangedFiles(program: Program): EmitOutputDetailed[];
|
||||
emitChangedFiles(program: Program, writeFileCallback: WriteFileCallback): ReadonlyArray<EmitResult>;
|
||||
|
||||
/** When called gets the semantic diagnostics for the program. It also caches the diagnostics and manage them */
|
||||
getSemanticDiagnostics(program: Program, cancellationToken?: CancellationToken): Diagnostic[];
|
||||
|
||||
|
@ -98,9 +78,7 @@ namespace ts {
|
|||
|
||||
export interface BuilderOptions {
|
||||
getCanonicalFileName: (fileName: string) => string;
|
||||
getEmitOutput: (program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean, isDetailed: boolean) => EmitOutput | EmitOutputDetailed;
|
||||
computeHash: (data: string) => string;
|
||||
shouldEmitFile: (sourceFile: SourceFile) => boolean;
|
||||
}
|
||||
|
||||
export function createBuilder(options: BuilderOptions): Builder {
|
||||
|
@ -108,12 +86,13 @@ namespace ts {
|
|||
const fileInfos = createMap<FileInfo>();
|
||||
const semanticDiagnosticsPerFile = createMap<ReadonlyArray<Diagnostic>>();
|
||||
/** The map has key by source file's path that has been changed */
|
||||
const changedFileNames = createMap<true>();
|
||||
const changedFilesSet = createMap<true>();
|
||||
const hasShapeChanged = createMap<true>();
|
||||
let allFilesExcludingDefaultLibraryFile: ReadonlyArray<SourceFile> | undefined;
|
||||
let emitHandler: EmitHandler;
|
||||
return {
|
||||
updateProgram,
|
||||
getFilesAffectedBy,
|
||||
emitFile,
|
||||
emitChangedFiles,
|
||||
getSemanticDiagnostics,
|
||||
clear
|
||||
|
@ -127,6 +106,8 @@ namespace ts {
|
|||
fileInfos.clear();
|
||||
semanticDiagnosticsPerFile.clear();
|
||||
}
|
||||
hasShapeChanged.clear();
|
||||
allFilesExcludingDefaultLibraryFile = undefined;
|
||||
mutateMap(
|
||||
fileInfos,
|
||||
arrayToMap(program.getSourceFiles(), sourceFile => sourceFile.path),
|
||||
|
@ -142,7 +123,7 @@ namespace ts {
|
|||
}
|
||||
|
||||
function registerChangedFile(path: Path) {
|
||||
changedFileNames.set(path, true);
|
||||
changedFilesSet.set(path, true);
|
||||
// All changed files need to re-evaluate its semantic diagnostics
|
||||
semanticDiagnosticsPerFile.delete(path);
|
||||
}
|
||||
|
@ -185,101 +166,79 @@ namespace ts {
|
|||
ensureProgramGraph(program);
|
||||
|
||||
const sourceFile = program.getSourceFileByPath(path);
|
||||
const info = fileInfos.get(path);
|
||||
if (!info || !updateShapeSignature(program, sourceFile, info)) {
|
||||
return sourceFile && [sourceFile] || emptyArray;
|
||||
if (!sourceFile) {
|
||||
return emptyArray;
|
||||
}
|
||||
|
||||
Debug.assert(!!sourceFile);
|
||||
if (!updateShapeSignature(program, sourceFile)) {
|
||||
return [sourceFile];
|
||||
}
|
||||
return emitHandler.getFilesAffectedByUpdatedShape(program, sourceFile);
|
||||
}
|
||||
|
||||
function emitFile(program: Program, path: Path) {
|
||||
function emitChangedFiles(program: Program, writeFileCallback: WriteFileCallback): ReadonlyArray<EmitResult> {
|
||||
ensureProgramGraph(program);
|
||||
const sourceFile = program.getSourceFileByPath(path);
|
||||
if (!fileInfos.has(path) || options.shouldEmitFile(sourceFile)) {
|
||||
return { outputFiles: [], emitSkipped: true };
|
||||
}
|
||||
|
||||
return options.getEmitOutput(program, program.getSourceFileByPath(path), /*emitOnlyDtsFiles*/ false, /*isDetailed*/ false);
|
||||
}
|
||||
|
||||
function enumerateChangedFilesSet(
|
||||
program: Program,
|
||||
onAffectedFile: (sourceFile: SourceFile) => void
|
||||
) {
|
||||
changedFileNames.forEach((_true, path) => {
|
||||
const affectedFiles = getFilesAffectedBy(program, path as Path);
|
||||
for (const file of affectedFiles) {
|
||||
onAffectedFile(file);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function enumerateChangedFilesEmitOutput(
|
||||
program: Program,
|
||||
emitOnlyDtsFiles: boolean,
|
||||
onEmitOutput: (emitOutput: EmitOutputDetailed) => void
|
||||
) {
|
||||
const compilerOptions = program.getCompilerOptions();
|
||||
let result: EmitResult[] | undefined;
|
||||
const seenFiles = createMap<true>();
|
||||
enumerateChangedFilesSet(program, sourceFile => {
|
||||
if (!seenFiles.has(sourceFile.path)) {
|
||||
seenFiles.set(sourceFile.path, true);
|
||||
// Any affected file shouldnt have the cached diagnostics
|
||||
semanticDiagnosticsPerFile.delete(sourceFile.path);
|
||||
changedFilesSet.forEach((_true, path) => {
|
||||
const affectedFiles = getFilesAffectedBy(program, path as Path);
|
||||
affectedFiles.forEach(affectedFile => {
|
||||
// Affected files shouldnt have cached diagnostics
|
||||
semanticDiagnosticsPerFile.delete(affectedFile.path);
|
||||
|
||||
const emitOutput = options.getEmitOutput(program, sourceFile, emitOnlyDtsFiles, /*isDetailed*/ true) as EmitOutputDetailed;
|
||||
if (!seenFiles.has(affectedFile.path)) {
|
||||
seenFiles.set(affectedFile.path, true);
|
||||
|
||||
// mark all the emitted source files as seen
|
||||
if (emitOutput.emittedSourceFiles) {
|
||||
for (const file of emitOutput.emittedSourceFiles) {
|
||||
seenFiles.set(file.path, true);
|
||||
// With --out or --outFile all outputs go into single file, do it only once
|
||||
if (compilerOptions.outFile || compilerOptions.out) {
|
||||
if (!result) {
|
||||
result = [program.emit(affectedFile, writeFileCallback)];
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Emit the affected file
|
||||
(result || (result = [])).push(program.emit(affectedFile, writeFileCallback));
|
||||
}
|
||||
}
|
||||
|
||||
onEmitOutput(emitOutput);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function emitChangedFiles(program: Program): EmitOutputDetailed[] {
|
||||
ensureProgramGraph(program);
|
||||
const result: EmitOutputDetailed[] = [];
|
||||
enumerateChangedFilesEmitOutput(program, /*emitOnlyDtsFiles*/ false, emitOutput => result.push(emitOutput));
|
||||
changedFileNames.clear();
|
||||
return result;
|
||||
changedFilesSet.clear();
|
||||
return result || emptyArray;
|
||||
}
|
||||
|
||||
function getSemanticDiagnostics(program: Program, cancellationToken?: CancellationToken): Diagnostic[] {
|
||||
ensureProgramGraph(program);
|
||||
|
||||
// Ensure that changed files have cleared their respective
|
||||
enumerateChangedFilesSet(program, sourceFile => semanticDiagnosticsPerFile.delete(sourceFile.path));
|
||||
Debug.assert(changedFilesSet.size === 0);
|
||||
|
||||
let diagnostics: Diagnostic[];
|
||||
for (const sourceFile of program.getSourceFiles()) {
|
||||
const path = sourceFile.path;
|
||||
const cachedDiagnostics = semanticDiagnosticsPerFile.get(path);
|
||||
// Report the semantic diagnostics from the cache if we already have those diagnostics present
|
||||
if (cachedDiagnostics) {
|
||||
diagnostics = addRange(diagnostics, cachedDiagnostics);
|
||||
}
|
||||
else {
|
||||
// Diagnostics werent cached, get them from program, and cache the result
|
||||
const cachedDiagnostics = program.getSemanticDiagnostics(sourceFile, cancellationToken);
|
||||
semanticDiagnosticsPerFile.set(path, cachedDiagnostics);
|
||||
diagnostics = addRange(diagnostics, cachedDiagnostics);
|
||||
}
|
||||
diagnostics = addRange(diagnostics, getSemanticDiagnosticsOfFile(program, sourceFile, cancellationToken));
|
||||
}
|
||||
return diagnostics || emptyArray;
|
||||
}
|
||||
|
||||
function getSemanticDiagnosticsOfFile(program: Program, sourceFile: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic> {
|
||||
const path = sourceFile.path;
|
||||
const cachedDiagnostics = semanticDiagnosticsPerFile.get(path);
|
||||
// Report the semantic diagnostics from the cache if we already have those diagnostics present
|
||||
if (cachedDiagnostics) {
|
||||
cachedDiagnostics;
|
||||
}
|
||||
|
||||
// Diagnostics werent cached, get them from program, and cache the result
|
||||
const diagnostics = program.getSemanticDiagnostics(sourceFile, cancellationToken);
|
||||
semanticDiagnosticsPerFile.set(path, diagnostics);
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
function clear() {
|
||||
isModuleEmit = undefined;
|
||||
emitHandler = undefined;
|
||||
fileInfos.clear();
|
||||
semanticDiagnosticsPerFile.clear();
|
||||
changedFileNames.clear();
|
||||
changedFilesSet.clear();
|
||||
hasShapeChanged.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -300,7 +259,18 @@ namespace ts {
|
|||
/**
|
||||
* @return {boolean} indicates if the shape signature has changed since last update.
|
||||
*/
|
||||
function updateShapeSignature(program: Program, sourceFile: SourceFile, info: FileInfo) {
|
||||
function updateShapeSignature(program: Program, sourceFile: SourceFile) {
|
||||
Debug.assert(!!sourceFile);
|
||||
|
||||
// If we have cached the result for this file, that means hence forth we should assume file shape is uptodate
|
||||
if (hasShapeChanged.has(sourceFile.path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
hasShapeChanged.set(sourceFile.path, true);
|
||||
const info = fileInfos.get(sourceFile.path);
|
||||
Debug.assert(!!info);
|
||||
|
||||
const prevSignature = info.signature;
|
||||
let latestSignature: string;
|
||||
if (sourceFile.isDeclarationFile) {
|
||||
|
@ -308,7 +278,7 @@ namespace ts {
|
|||
info.signature = latestSignature;
|
||||
}
|
||||
else {
|
||||
const emitOutput = options.getEmitOutput(program, sourceFile, /*emitOnlyDtsFiles*/ true, /*isDetailed*/ false);
|
||||
const emitOutput = getFileEmitOutput(program, sourceFile, /*emitOnlyDtsFiles*/ true);
|
||||
if (emitOutput.outputFiles && emitOutput.outputFiles.length > 0) {
|
||||
latestSignature = options.computeHash(emitOutput.outputFiles[0].text);
|
||||
info.signature = latestSignature;
|
||||
|
@ -379,13 +349,20 @@ namespace ts {
|
|||
* Gets all files of the program excluding the default library file
|
||||
*/
|
||||
function getAllFilesExcludingDefaultLibraryFile(program: Program, firstSourceFile: SourceFile): ReadonlyArray<SourceFile> {
|
||||
// Use cached result
|
||||
if (allFilesExcludingDefaultLibraryFile) {
|
||||
return allFilesExcludingDefaultLibraryFile;
|
||||
}
|
||||
|
||||
let result: SourceFile[];
|
||||
addSourceFile(firstSourceFile);
|
||||
for (const sourceFile of program.getSourceFiles()) {
|
||||
if (sourceFile !== firstSourceFile) {
|
||||
addSourceFile(sourceFile);
|
||||
}
|
||||
}
|
||||
return result || emptyArray;
|
||||
allFilesExcludingDefaultLibraryFile = result || emptyArray;
|
||||
return allFilesExcludingDefaultLibraryFile;
|
||||
|
||||
function addSourceFile(sourceFile: SourceFile) {
|
||||
if (!program.isSourceFileDefaultLibrary(sourceFile)) {
|
||||
|
@ -505,7 +482,7 @@ namespace ts {
|
|||
if (!seenFileNamesMap.has(currentPath)) {
|
||||
const currentSourceFile = program.getSourceFileByPath(currentPath);
|
||||
seenFileNamesMap.set(currentPath, currentSourceFile);
|
||||
if (currentSourceFile && updateShapeSignature(program, currentSourceFile, fileInfos.get(currentPath))) {
|
||||
if (currentSourceFile && updateShapeSignature(program, currentSourceFile)) {
|
||||
queue.push(...getReferencedByPaths(currentPath));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -144,13 +144,14 @@ namespace ts {
|
|||
|
||||
function compileWatchedProgram(host: DirectoryStructureHost, program: Program, builder: Builder) {
|
||||
// First get and report any syntactic errors.
|
||||
let diagnostics = program.getSyntacticDiagnostics().slice();
|
||||
const diagnostics = program.getSyntacticDiagnostics().slice();
|
||||
let reportSemanticDiagnostics = false;
|
||||
|
||||
// If we didn't have any syntactic errors, then also try getting the global and
|
||||
// semantic errors.
|
||||
if (diagnostics.length === 0) {
|
||||
diagnostics = program.getOptionsDiagnostics().concat(program.getGlobalDiagnostics());
|
||||
addRange(diagnostics, program.getOptionsDiagnostics());
|
||||
addRange(diagnostics, program.getGlobalDiagnostics());
|
||||
|
||||
if (diagnostics.length === 0) {
|
||||
reportSemanticDiagnostics = true;
|
||||
|
@ -162,7 +163,7 @@ namespace ts {
|
|||
let sourceMaps: SourceMapData[];
|
||||
let emitSkipped: boolean;
|
||||
|
||||
const result = builder.emitChangedFiles(program);
|
||||
const result = builder.emitChangedFiles(program, writeFile);
|
||||
if (result.length === 0) {
|
||||
emitSkipped = true;
|
||||
}
|
||||
|
@ -171,14 +172,13 @@ namespace ts {
|
|||
if (emitOutput.emitSkipped) {
|
||||
emitSkipped = true;
|
||||
}
|
||||
diagnostics = concatenate(diagnostics, emitOutput.diagnostics);
|
||||
addRange(diagnostics, emitOutput.diagnostics);
|
||||
sourceMaps = concatenate(sourceMaps, emitOutput.sourceMaps);
|
||||
writeOutputFiles(emitOutput.outputFiles);
|
||||
}
|
||||
}
|
||||
|
||||
if (reportSemanticDiagnostics) {
|
||||
diagnostics = diagnostics.concat(builder.getSemanticDiagnostics(program));
|
||||
addRange(diagnostics, builder.getSemanticDiagnostics(program));
|
||||
}
|
||||
return handleEmitOutputAndReportErrors(host, program, emittedFiles, emitSkipped,
|
||||
diagnostics, reportDiagnostic);
|
||||
|
@ -191,31 +191,23 @@ namespace ts {
|
|||
}
|
||||
}
|
||||
|
||||
function writeFile(fileName: string, data: string, writeByteOrderMark: boolean) {
|
||||
function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, onError: (message: string) => void) {
|
||||
try {
|
||||
performance.mark("beforeIOWrite");
|
||||
ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));
|
||||
|
||||
host.writeFile(fileName, data, writeByteOrderMark);
|
||||
host.writeFile(fileName, text, writeByteOrderMark);
|
||||
|
||||
performance.mark("afterIOWrite");
|
||||
performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite");
|
||||
|
||||
if (emittedFiles) {
|
||||
emittedFiles.push(fileName);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
return createCompilerDiagnostic(Diagnostics.Could_not_write_file_0_Colon_1, fileName, e);
|
||||
}
|
||||
}
|
||||
|
||||
function writeOutputFiles(outputFiles: OutputFile[]) {
|
||||
if (outputFiles) {
|
||||
for (const outputFile of outputFiles) {
|
||||
const error = writeFile(outputFile.name, outputFile.text, outputFile.writeByteOrderMark);
|
||||
if (error) {
|
||||
diagnostics.push(error);
|
||||
}
|
||||
if (emittedFiles) {
|
||||
emittedFiles.push(outputFile.name);
|
||||
}
|
||||
if (onError) {
|
||||
onError(e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -308,7 +300,7 @@ namespace ts {
|
|||
getCurrentDirectory()
|
||||
);
|
||||
// There is no extra check needed since we can just rely on the program to decide emit
|
||||
const builder = createBuilder({ getCanonicalFileName, getEmitOutput: getFileEmitOutput, computeHash, shouldEmitFile: () => true });
|
||||
const builder = createBuilder({ getCanonicalFileName, computeHash });
|
||||
|
||||
synchronizeProgram();
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/// <reference path="core.ts" />
|
||||
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
/**
|
||||
* Updates the existing missing file watches with the new set of missing files after new program is created
|
||||
|
@ -72,10 +73,7 @@ namespace ts {
|
|||
existingWatchedForWildcards.set(directory, createWildcardDirectoryWatcher(directory, flags));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
export function addFileWatcher(host: System, file: string, cb: FileWatcherCallback): FileWatcher {
|
||||
return host.watchFile(file, cb);
|
||||
}
|
||||
|
|
|
@ -46,15 +46,14 @@ namespace ts {
|
|||
function makeAssertChanges(getProgram: () => Program): (fileNames: ReadonlyArray<string>) => void {
|
||||
const builder = createBuilder({
|
||||
getCanonicalFileName: identity,
|
||||
getEmitOutput: getFileEmitOutput,
|
||||
computeHash: identity,
|
||||
shouldEmitFile: returnTrue,
|
||||
computeHash: identity
|
||||
});
|
||||
return fileNames => {
|
||||
const program = getProgram();
|
||||
builder.updateProgram(program);
|
||||
const changedFiles = builder.emitChangedFiles(program);
|
||||
assert.deepEqual(changedFileNames(changedFiles), fileNames);
|
||||
const outputFileNames: string[] = [];
|
||||
builder.emitChangedFiles(program, fileName => outputFileNames.push(fileName));
|
||||
assert.deepEqual(outputFileNames, fileNames);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -63,11 +62,4 @@ namespace ts {
|
|||
updateProgramText(files, fileName, fileContent);
|
||||
});
|
||||
}
|
||||
|
||||
function changedFileNames(changedFiles: ReadonlyArray<EmitOutputDetailed>): string[] {
|
||||
return changedFiles.map(f => {
|
||||
assert.lengthOf(f.outputFiles, 1);
|
||||
return f.outputFiles[0].name;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1106,7 +1106,7 @@ namespace ts.tscWatch {
|
|||
createWatchForOut(/*out*/ undefined, outJs);
|
||||
});
|
||||
|
||||
it("with --outFile and multiple declaration files in the program", () => {
|
||||
function verifyFilesEmittedOnce(useOutFile: boolean) {
|
||||
const file1: FileOrFolder = {
|
||||
path: "/a/b/output/AnotherDependency/file1.d.ts",
|
||||
content: "declare namespace Common.SomeComponent.DynamicMenu { enum Z { Full = 0, Min = 1, Average = 2, } }"
|
||||
|
@ -1126,10 +1126,9 @@ namespace ts.tscWatch {
|
|||
const configFile: FileOrFolder = {
|
||||
path: "/a/b/project/tsconfig.json",
|
||||
content: JSON.stringify({
|
||||
compilerOptions: {
|
||||
outFile: "../output/common.js",
|
||||
target: "es5"
|
||||
},
|
||||
compilerOptions: useOutFile ?
|
||||
{ outFile: "../output/common.js", target: "es5" } :
|
||||
{ outDir: "../output", target: "es5" },
|
||||
files: [file1.path, file2.path, file3.path, file4.path]
|
||||
})
|
||||
};
|
||||
|
@ -1137,13 +1136,32 @@ namespace ts.tscWatch {
|
|||
const allfiles = files.concat(configFile);
|
||||
const host = createWatchedSystem(allfiles);
|
||||
const originalWriteFile = host.writeFile.bind(host);
|
||||
let numberOfTimesFileWritten = 0;
|
||||
const mapOfFilesWritten = createMap<number>();
|
||||
host.writeFile = (p: string, content: string) => {
|
||||
numberOfTimesFileWritten++;
|
||||
const count = mapOfFilesWritten.get(p);
|
||||
mapOfFilesWritten.set(p, count ? count + 1 : 1);
|
||||
return originalWriteFile(p, content);
|
||||
};
|
||||
createWatchModeWithConfigFile(configFile.path, host);
|
||||
assert.equal(numberOfTimesFileWritten, 1);
|
||||
if (useOutFile) {
|
||||
// Only out file
|
||||
assert.equal(mapOfFilesWritten.size, 1);
|
||||
}
|
||||
else {
|
||||
// main.js and main2.js
|
||||
assert.equal(mapOfFilesWritten.size, 2);
|
||||
}
|
||||
mapOfFilesWritten.forEach((value, key) => {
|
||||
assert.equal(value, 1, "Key: " + key);
|
||||
});
|
||||
}
|
||||
|
||||
it("with --outFile and multiple declaration files in the program", () => {
|
||||
verifyFilesEmittedOnce(/*useOutFile*/ true);
|
||||
});
|
||||
|
||||
it("without --outFile and multiple declaration files in the program", () => {
|
||||
verifyFilesEmittedOnce(/*useOutFile*/ false);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -443,18 +443,13 @@ namespace ts.server {
|
|||
if (!this.builder) {
|
||||
this.builder = createBuilder({
|
||||
getCanonicalFileName: this.projectService.toCanonicalFileName,
|
||||
getEmitOutput: (_program, sourceFile, emitOnlyDts, isDetailed) =>
|
||||
this.getFileEmitOutput(sourceFile, emitOnlyDts, isDetailed),
|
||||
computeHash: data =>
|
||||
this.projectService.host.createHash(data),
|
||||
shouldEmitFile: sourceFile =>
|
||||
!this.shouldEmitFile(sourceFile)
|
||||
computeHash: data => this.projectService.host.createHash(data)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private shouldEmitFile(sourceFile: SourceFile) {
|
||||
return !this.projectService.getScriptInfoForPath(sourceFile.path).isDynamicOrHasMixedContent();
|
||||
private shouldEmitFile(scriptInfo: ScriptInfo) {
|
||||
return scriptInfo && !scriptInfo.isDynamicOrHasMixedContent();
|
||||
}
|
||||
|
||||
getCompileOnSaveAffectedFileList(scriptInfo: ScriptInfo): string[] {
|
||||
|
@ -464,15 +459,17 @@ namespace ts.server {
|
|||
this.updateGraph();
|
||||
this.ensureBuilder();
|
||||
return mapDefined(this.builder.getFilesAffectedBy(this.program, scriptInfo.path),
|
||||
sourceFile => this.shouldEmitFile(sourceFile) ? sourceFile.fileName : undefined);
|
||||
sourceFile => this.shouldEmitFile(this.projectService.getScriptInfoForPath(sourceFile.path)) ? sourceFile.fileName : undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if emit was conducted
|
||||
*/
|
||||
emitFile(scriptInfo: ScriptInfo, writeFile: (path: string, data: string, writeByteOrderMark?: boolean) => void): boolean {
|
||||
this.ensureBuilder();
|
||||
const { emitSkipped, outputFiles } = this.builder.emitFile(this.program, scriptInfo.path);
|
||||
if (!this.languageServiceEnabled || !this.shouldEmitFile(scriptInfo)) {
|
||||
return false;
|
||||
}
|
||||
const { emitSkipped, outputFiles } = this.getLanguageService(/*ensureSynchronized*/ false).getEmitOutput(scriptInfo.fileName);
|
||||
if (!emitSkipped) {
|
||||
for (const outputFile of outputFiles) {
|
||||
const outputFileAbsoluteFileName = getNormalizedAbsolutePath(outputFile.name, this.currentDirectory);
|
||||
|
@ -598,13 +595,6 @@ namespace ts.server {
|
|||
});
|
||||
}
|
||||
|
||||
private getFileEmitOutput(sourceFile: SourceFile, emitOnlyDtsFiles: boolean, isDetailed: boolean) {
|
||||
if (!this.languageServiceEnabled) {
|
||||
return undefined;
|
||||
}
|
||||
return this.getLanguageService(/*ensureSynchronized*/ false).getEmitOutput(sourceFile.fileName, emitOnlyDtsFiles, isDetailed);
|
||||
}
|
||||
|
||||
getExcludedFiles(): ReadonlyArray<NormalizedPath> {
|
||||
return emptyArray;
|
||||
}
|
||||
|
|
|
@ -1511,12 +1511,12 @@ namespace ts {
|
|||
return ts.NavigateTo.getNavigateToItems(sourceFiles, program.getTypeChecker(), cancellationToken, searchValue, maxResultCount, excludeDtsFiles);
|
||||
}
|
||||
|
||||
function getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean, isDetailed?: boolean) {
|
||||
function getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean) {
|
||||
synchronizeHostData();
|
||||
|
||||
const sourceFile = getValidSourceFile(fileName);
|
||||
const customTransformers = host.getCustomTransformers && host.getCustomTransformers();
|
||||
return getFileEmitOutput(program, sourceFile, emitOnlyDtsFiles, isDetailed, cancellationToken, customTransformers);
|
||||
return getFileEmitOutput(program, sourceFile, emitOnlyDtsFiles, cancellationToken, customTransformers);
|
||||
}
|
||||
|
||||
// Signature help
|
||||
|
|
|
@ -287,7 +287,6 @@ namespace ts {
|
|||
getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string): RefactorEditInfo | undefined;
|
||||
|
||||
getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput;
|
||||
getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean, isDetailed?: boolean): EmitOutput | EmitOutputDetailed;
|
||||
|
||||
getProgram(): Program;
|
||||
|
||||
|
|
|
@ -3731,17 +3731,11 @@ declare namespace ts {
|
|||
outputFiles: OutputFile[];
|
||||
emitSkipped: boolean;
|
||||
}
|
||||
interface EmitOutputDetailed extends EmitOutput {
|
||||
diagnostics: Diagnostic[];
|
||||
sourceMaps: SourceMapData[];
|
||||
emittedSourceFiles: SourceFile[];
|
||||
}
|
||||
interface OutputFile {
|
||||
name: string;
|
||||
writeByteOrderMark: boolean;
|
||||
text: string;
|
||||
}
|
||||
function getFileEmitOutput(program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean, isDetailed: boolean, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): EmitOutput | EmitOutputDetailed;
|
||||
}
|
||||
declare namespace ts {
|
||||
function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName?: string): string;
|
||||
|
@ -3953,7 +3947,6 @@ declare namespace ts {
|
|||
getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange): ApplicableRefactorInfo[];
|
||||
getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string): RefactorEditInfo | undefined;
|
||||
getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput;
|
||||
getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean, isDetailed?: boolean): EmitOutput | EmitOutputDetailed;
|
||||
getProgram(): Program;
|
||||
dispose(): void;
|
||||
}
|
||||
|
@ -7043,23 +7036,6 @@ declare namespace ts.server {
|
|||
isJavaScript(): boolean;
|
||||
}
|
||||
}
|
||||
declare namespace ts {
|
||||
/**
|
||||
* Updates the existing missing file watches with the new set of missing files after new program is created
|
||||
*/
|
||||
function updateMissingFilePathsWatch(program: Program, missingFileWatches: Map<FileWatcher>, createMissingFileWatch: (missingFilePath: Path) => FileWatcher): void;
|
||||
interface WildcardDirectoryWatcher {
|
||||
watcher: FileWatcher;
|
||||
flags: WatchDirectoryFlags;
|
||||
}
|
||||
/**
|
||||
* Updates the existing wild card directory watches with the new set of wild card directories from the config file
|
||||
* after new program is created because the config file was reloaded or program was created first time from the config file
|
||||
* Note that there is no need to call this function when the program is updated with additional files without reloading config files,
|
||||
* as wildcard directories wont change unless reloading config file
|
||||
*/
|
||||
function updateWatchingWildcardDirectories(existingWatchedForWildcards: Map<WildcardDirectoryWatcher>, wildcardDirectories: Map<WatchDirectoryFlags>, watchDirectory: (directory: string, flags: WatchDirectoryFlags) => FileWatcher): void;
|
||||
}
|
||||
declare namespace ts.server {
|
||||
interface InstallPackageOptionsWithProjectRootPath extends InstallPackageOptions {
|
||||
projectRootPath: Path;
|
||||
|
@ -7204,6 +7180,7 @@ declare namespace ts.server {
|
|||
getAllProjectErrors(): ReadonlyArray<Diagnostic>;
|
||||
getLanguageService(ensureSynchronized?: boolean): LanguageService;
|
||||
private ensureBuilder();
|
||||
private shouldEmitFile(scriptInfo);
|
||||
getCompileOnSaveAffectedFileList(scriptInfo: ScriptInfo): string[];
|
||||
/**
|
||||
* Returns true if emit was conducted
|
||||
|
@ -7222,7 +7199,6 @@ declare namespace ts.server {
|
|||
getRootFiles(): NormalizedPath[];
|
||||
getRootScriptInfos(): ScriptInfo[];
|
||||
getScriptInfos(): ScriptInfo[];
|
||||
private getFileEmitOutput(sourceFile, emitOnlyDtsFiles, isDetailed);
|
||||
getExcludedFiles(): ReadonlyArray<NormalizedPath>;
|
||||
getFileNames(excludeFilesFromExternalLibraries?: boolean, excludeConfigFiles?: boolean): NormalizedPath[];
|
||||
hasConfigFile(configFilePath: NormalizedPath): boolean;
|
||||
|
|
|
@ -3678,17 +3678,11 @@ declare namespace ts {
|
|||
outputFiles: OutputFile[];
|
||||
emitSkipped: boolean;
|
||||
}
|
||||
interface EmitOutputDetailed extends EmitOutput {
|
||||
diagnostics: Diagnostic[];
|
||||
sourceMaps: SourceMapData[];
|
||||
emittedSourceFiles: SourceFile[];
|
||||
}
|
||||
interface OutputFile {
|
||||
name: string;
|
||||
writeByteOrderMark: boolean;
|
||||
text: string;
|
||||
}
|
||||
function getFileEmitOutput(program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean, isDetailed: boolean, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): EmitOutput | EmitOutputDetailed;
|
||||
}
|
||||
declare namespace ts {
|
||||
function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName?: string): string;
|
||||
|
@ -3953,7 +3947,6 @@ declare namespace ts {
|
|||
getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange): ApplicableRefactorInfo[];
|
||||
getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string): RefactorEditInfo | undefined;
|
||||
getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput;
|
||||
getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean, isDetailed?: boolean): EmitOutput | EmitOutputDetailed;
|
||||
getProgram(): Program;
|
||||
dispose(): void;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue