mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 16:38:05 -06:00
Clean, etc
This commit is contained in:
parent
b1dedf2540
commit
7e0825a3e7
@ -3593,6 +3593,43 @@
|
||||
"category": "Error",
|
||||
"code": 6309
|
||||
},
|
||||
"Project '{0}' is out of date because oldest output '{1}' is older than newest input '{2}'": {
|
||||
"category": "Message",
|
||||
"code": 6350
|
||||
},
|
||||
"Project '{0}' is up to date because newest input '{1}' is older than oldest output '{2}'": {
|
||||
"category": "Message",
|
||||
"code": 6351
|
||||
},
|
||||
"Project '{0}' is out of date because output file '{1}' does not exist": {
|
||||
"category": "Message",
|
||||
"code": 6352
|
||||
},
|
||||
|
||||
"Project '{0}' is up to date with its upstream types": {
|
||||
"category": "Message",
|
||||
"code": 6353
|
||||
},
|
||||
"Sorted list of input projects: {0}": {
|
||||
"category": "Message",
|
||||
"code": 6354
|
||||
},
|
||||
"Would delete the following files:{0}": {
|
||||
"category": "Message",
|
||||
"code": 6355
|
||||
},
|
||||
"Would build project '{0}'": {
|
||||
"category": "Message",
|
||||
"code": 6356
|
||||
},
|
||||
"Building project '{0}'...": {
|
||||
"category": "Message",
|
||||
"code": 6357
|
||||
},
|
||||
"Updating output timestamps of project '{0}'...": {
|
||||
"category": "Message",
|
||||
"code": 6358
|
||||
},
|
||||
|
||||
"Variable '{0}' implicitly has an '{1}' type.": {
|
||||
"category": "Error",
|
||||
|
||||
@ -189,7 +189,10 @@ namespace ts {
|
||||
getEnvironmentVariable: name => sys.getEnvironmentVariable ? sys.getEnvironmentVariable(name) : "",
|
||||
getDirectories: (path: string) => sys.getDirectories(path),
|
||||
realpath,
|
||||
readDirectory: (path, extensions, include, exclude, depth) => sys.readDirectory(path, extensions, include, exclude, depth)
|
||||
readDirectory: (path, extensions, include, exclude, depth) => sys.readDirectory(path, extensions, include, exclude, depth),
|
||||
getModifiedTime: path => sys.getModifiedTime(path),
|
||||
setModifiedTime: (path, date) => sys.setModifiedTime(path, date),
|
||||
deleteFile: path => sys.deleteFile(path)
|
||||
};
|
||||
}
|
||||
|
||||
@ -2692,7 +2695,7 @@ namespace ts {
|
||||
/**
|
||||
* Returns the target config filename of a project reference
|
||||
*/
|
||||
function resolveProjectReferencePath(host: CompilerHost, ref: ProjectReference): string | undefined {
|
||||
export function resolveProjectReferencePath(host: CompilerHost, ref: ProjectReference): string | undefined {
|
||||
if (!host.fileExists(ref.path)) {
|
||||
return combinePaths(ref.path, "tsconfig.json");
|
||||
}
|
||||
|
||||
@ -432,6 +432,7 @@ namespace ts {
|
||||
readFile(path: string, encoding?: string): string | undefined;
|
||||
getFileSize?(path: string): number;
|
||||
writeFile(path: string, data: string, writeByteOrderMark?: boolean): void;
|
||||
|
||||
/**
|
||||
* @pollingInterval - this parameter is used in polling-based watchers and ignored in watchers that
|
||||
* use native OS file watching
|
||||
@ -447,6 +448,8 @@ namespace ts {
|
||||
getDirectories(path: string): string[];
|
||||
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[];
|
||||
getModifiedTime?(path: string): Date;
|
||||
setModifiedTime?(path: string, time: Date): void;
|
||||
deleteFile?(path: string): void;
|
||||
/**
|
||||
* This should be cryptographically secure.
|
||||
* A good implementation is node.js' `crypto.createHash`. (https://nodejs.org/api/crypto.html#crypto_crypto_createhash_algorithm)
|
||||
@ -589,6 +592,8 @@ namespace ts {
|
||||
},
|
||||
readDirectory,
|
||||
getModifiedTime,
|
||||
setModifiedTime,
|
||||
deleteFile,
|
||||
createHash: _crypto ? createMD5HashUsingNativeCrypto : generateDjb2Hash,
|
||||
getMemoryUsage() {
|
||||
if (global.gc) {
|
||||
@ -1063,6 +1068,22 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function setModifiedTime(path: string, time: Date) {
|
||||
try {
|
||||
_fs.utimesSync(path, time, time);
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
function deleteFile(path: string) {
|
||||
try {
|
||||
return _fs.unlinkSync(path);
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* djb2 hashing algorithm
|
||||
* http://www.cse.yorku.ca/~oz/hash.html
|
||||
|
||||
@ -8,6 +8,7 @@ namespace ts {
|
||||
* but unchanged, because this enables fast downstream updates
|
||||
*/
|
||||
interface BuildContext {
|
||||
options: BuildOptions;
|
||||
/**
|
||||
* Map from output file name to its pre-build timestamp
|
||||
*/
|
||||
@ -17,6 +18,8 @@ namespace ts {
|
||||
* Map from config file name to up-to-date status
|
||||
*/
|
||||
projectStatus: FileMap<UpToDateStatus>;
|
||||
|
||||
verbose(diag: DiagnosticMessage, ...args: any[]): void;
|
||||
}
|
||||
|
||||
type Mapper = ReturnType<typeof createDependencyMapper>;
|
||||
@ -25,6 +28,12 @@ namespace ts {
|
||||
dependencyMap: Mapper;
|
||||
}
|
||||
|
||||
interface BuildOptions {
|
||||
dry: boolean;
|
||||
force: boolean;
|
||||
verbose: boolean;
|
||||
}
|
||||
|
||||
enum BuildResultFlags {
|
||||
None = 0,
|
||||
|
||||
@ -51,7 +60,7 @@ namespace ts {
|
||||
UpToDate,
|
||||
/**
|
||||
* The project appears out of date because its upstream inputs are newer than its outputs,
|
||||
* but all of its outputs are actually newer than the previous identical outputs of its inputs.
|
||||
* but all of its outputs are actually newer than the previous identical outputs of its (.d.ts) inputs.
|
||||
* This means we can Pseudo-build (just touch timestamps), as if we had actually built this project.
|
||||
*/
|
||||
UpToDateWithUpstreamTypes,
|
||||
@ -286,11 +295,12 @@ namespace ts {
|
||||
const cache = createFileMap<ParsedCommandLine>();
|
||||
const configParseHost = parseConfigHostFromCompilerHost(host);
|
||||
|
||||
// TODO: Cache invalidation!
|
||||
// TODO: Cache invalidation under --watch!
|
||||
|
||||
function parseConfigFile(configFilePath: string) {
|
||||
const sourceFile = host.getSourceFile(configFilePath, ScriptTarget.JSON) as JsonSourceFile;
|
||||
const parsed = parseJsonSourceFileConfigFileContent(sourceFile, configParseHost, configFilePath);
|
||||
const parsed = parseJsonSourceFileConfigFileContent(sourceFile, configParseHost, getDirectoryPath(configFilePath));
|
||||
parsed.options.configFilePath = configFilePath;
|
||||
cache.setValue(configFilePath, parsed);
|
||||
return parsed;
|
||||
}
|
||||
@ -312,21 +322,109 @@ namespace ts {
|
||||
return fileExtensionIs(fileName, ".d.ts");
|
||||
}
|
||||
|
||||
function createSolutionBuilder(host: CompilerHost) {
|
||||
function createBuildContext(options: BuildOptions): BuildContext {
|
||||
const verboseDiag = options.verbose && createDiagnosticReporter(sys, /*pretty*/ false);
|
||||
return {
|
||||
options,
|
||||
projectStatus: createFileMap(),
|
||||
unchangedOutputs: createFileMap(),
|
||||
verbose: options.verbose ? (diag, ...args) => {
|
||||
verboseDiag(createCompilerDiagnostic(diag, ...args));
|
||||
} : () => undefined
|
||||
};
|
||||
}
|
||||
|
||||
export function performBuild(args: string[]) {
|
||||
const diagReporter = createDiagnosticReporter(sys, /*pretty*/true);
|
||||
const host = createCompilerHost({});
|
||||
|
||||
let verbose = false;
|
||||
let dry = false;
|
||||
let force = false;
|
||||
let clean = false;
|
||||
|
||||
const projects: string[] = [];
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
switch (args[i].toLowerCase()) {
|
||||
case "-v":
|
||||
case "--verbose":
|
||||
verbose = true;
|
||||
continue;
|
||||
case "-d":
|
||||
case "--dry":
|
||||
dry = true;
|
||||
continue;
|
||||
case "-f":
|
||||
case "--force":
|
||||
force = true;
|
||||
continue;
|
||||
case "--clean":
|
||||
clean = true;
|
||||
continue;
|
||||
}
|
||||
// Not a flag, parse as filename
|
||||
addProject(args[i]);
|
||||
}
|
||||
|
||||
if (projects.length === 0) {
|
||||
// tsc -b invoked with no extra arguments; act as if invoked with "tsc -b ."
|
||||
addProject(".");
|
||||
}
|
||||
|
||||
const context = createBuildContext({ verbose, dry, force });
|
||||
const builder = createSolutionBuilder(host, context);
|
||||
if (clean) {
|
||||
builder.cleanProjects(projects);
|
||||
}
|
||||
else {
|
||||
builder.buildProjects(projects);
|
||||
}
|
||||
|
||||
function addProject(projectSpecification: string) {
|
||||
const fileName = resolvePath(host.getCurrentDirectory(), projectSpecification);
|
||||
const refPath = resolveProjectReferencePath(host, { path: fileName });
|
||||
if (!host.fileExists(refPath)) {
|
||||
diagReporter(createCompilerDiagnostic(Diagnostics.File_0_does_not_exist, fileName));
|
||||
}
|
||||
projects.push(refPath);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export function createSolutionBuilder(host: CompilerHost, context: BuildContext) {
|
||||
const diagReporter = createDiagnosticReporter(sys, /*pretty*/true);
|
||||
const configFileCache = createConfigFileCache(host);
|
||||
|
||||
return {
|
||||
getUpToDateStatus,
|
||||
buildProjects,
|
||||
cleanProjects
|
||||
};
|
||||
|
||||
function getUpToDateStatus(project: ParsedCommandLine, context: BuildContext): UpToDateStatus {
|
||||
const prior = context.projectStatus.getValueOrUndefined(project.options.configFilePath);
|
||||
if (prior !== undefined) {
|
||||
return prior;
|
||||
}
|
||||
const actual = getUpToDateStatusWorker(project, context);
|
||||
const actual = getUpToDateStatusWorker(project);
|
||||
context.projectStatus.setValue(project.options.configFilePath, actual);
|
||||
return actual;
|
||||
}
|
||||
|
||||
function getUpToDateStatusWorker(project: ParsedCommandLine, context: BuildContext): UpToDateStatus {
|
||||
function getAllProjectOutputs(project: ParsedCommandLine): ReadonlyArray<string> {
|
||||
if (project.options.outFile) {
|
||||
return getOutFileOutputs(project);
|
||||
}
|
||||
else {
|
||||
const outputs: string[] = [];
|
||||
for (const inputFile of project.fileNames) {
|
||||
(outputs as string[]).push(...getOutputFileNames(inputFile, project));
|
||||
}
|
||||
return outputs;
|
||||
}
|
||||
}
|
||||
|
||||
function getUpToDateStatusWorker(project: ParsedCommandLine): UpToDateStatus {
|
||||
let newestInputFileName: string = undefined!;
|
||||
let newestInputFileTime = minimumDate;
|
||||
// Get timestamps of input files
|
||||
@ -338,7 +436,7 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
const inputTime = sys.getModifiedTime(inputFile);
|
||||
const inputTime = host.getModifiedTime(inputFile);
|
||||
if (inputTime > newestInputFileTime) {
|
||||
newestInputFileName = inputFile;
|
||||
newestInputFileTime = inputTime;
|
||||
@ -346,21 +444,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
// Collect the expected outputs of this project
|
||||
let outputs: ReadonlyArray<string>;
|
||||
if (project.options.outFile) {
|
||||
outputs = getOutFileOutputs(project);
|
||||
}
|
||||
else {
|
||||
outputs = [];
|
||||
for (const inputFile of project.fileNames) {
|
||||
(outputs as string[]).push(...getOutputFileNames(inputFile, project));
|
||||
}
|
||||
}
|
||||
const outputs = getAllProjectOutputs(project);
|
||||
|
||||
// Now see if all outputs are newer than the newest input
|
||||
let oldestOutputFileName: string = undefined!;
|
||||
let oldestOutputFileTime: Date = minimumDate;
|
||||
let newestOutputFileTime: Date = maximumDate;
|
||||
let oldestOutputFileTime: Date = maximumDate;
|
||||
let newestOutputFileTime: Date = minimumDate;
|
||||
let newestDeclarationFileContentChangedTime: Date = minimumDate;
|
||||
for (const output of outputs) {
|
||||
// Output is missing
|
||||
@ -371,7 +460,7 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
const outputTime = sys.getModifiedTime(output);
|
||||
const outputTime = host.getModifiedTime(output);
|
||||
// If an output is older than the newest input, we can stop checking
|
||||
if (outputTime < newestInputFileTime) {
|
||||
return {
|
||||
@ -391,13 +480,13 @@ namespace ts {
|
||||
// In addition to file timestamps, we also keep track of when a .d.ts file
|
||||
// had its file touched but not had its contents changed - this allows us
|
||||
// to skip a downstream typecheck
|
||||
if (fileExtensionIs(output, ".d.ts")) {
|
||||
if (isDeclarationFile(output)) {
|
||||
const unchangedTime = context.unchangedOutputs.getValueOrUndefined(output);
|
||||
if (unchangedTime !== undefined) {
|
||||
newestDeclarationFileContentChangedTime = newer(unchangedTime, newestDeclarationFileContentChangedTime);
|
||||
}
|
||||
else {
|
||||
newestDeclarationFileContentChangedTime = newer(newestDeclarationFileContentChangedTime, sys.getModifiedTime(output));
|
||||
newestDeclarationFileContentChangedTime = newer(newestDeclarationFileContentChangedTime, host.getModifiedTime(output));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -405,36 +494,40 @@ namespace ts {
|
||||
let pseudoUpToDate = false;
|
||||
// By here, we know the project is at least up-to-date with its own inputs.
|
||||
// See if any of its upstream projects are newer than it
|
||||
for (const ref of project.projectReferences) {
|
||||
const refStatus = getUpToDateStatus(configFileCache.parseConfigFile(ref.path), context);
|
||||
if (project.projectReferences) {
|
||||
for (const ref of project.projectReferences) {
|
||||
const resolvedRef = resolveProjectReferencePath(host, ref);
|
||||
const refStatus = getUpToDateStatus(configFileCache.parseConfigFile(resolvedRef), context);
|
||||
|
||||
// If the upstream project is out of date, then so are we (someone shouldn't have asked, though?)
|
||||
if (refStatus.type !== UpToDateStatusType.UpToDate) {
|
||||
// If the upstream project is out of date, then so are we (someone shouldn't have asked, though?)
|
||||
if (refStatus.type !== UpToDateStatusType.UpToDate) {
|
||||
return {
|
||||
type: UpToDateStatusType.UpstreamOutOfDate,
|
||||
upstreamProjectName: ref.path
|
||||
};
|
||||
}
|
||||
|
||||
// If the upstream project's newest file is older than our oldest output, we
|
||||
// can't be out of date because of it
|
||||
if (refStatus.newestInputFileTime < oldestOutputFileTime) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the upstream project has only change .d.ts files, and we've built
|
||||
// *after* those files, then we're "psuedo up to date" and eligible for a fast rebuild
|
||||
if (refStatus.newestDeclarationFileContentChangedTime < oldestOutputFileTime) {
|
||||
pseudoUpToDate = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// We have an output older than an upstream output - we are out of date
|
||||
Debug.assert(oldestOutputFileName !== undefined, "Should have an oldest output filename here");
|
||||
return {
|
||||
type: UpToDateStatusType.UpstreamOutOfDate,
|
||||
upstreamProjectName: ref.path
|
||||
type: UpToDateStatusType.OutOfDateWithUpstream,
|
||||
outOfDateOutputFileName: oldestOutputFileName,
|
||||
newerProjectName: ref.path
|
||||
};
|
||||
}
|
||||
|
||||
// If the upstream project's newest file is older than our oldest output, we
|
||||
// can't be out of date because of it
|
||||
if (refStatus.newestInputFileTime < oldestOutputFileTime) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the upstream project has only change .d.ts files, and we've built
|
||||
// *after* those files, then we're "psuedo up to date" and eligible for a fast rebuild
|
||||
if (refStatus.newestDeclarationFileContentChangedTime < oldestOutputFileTime) {
|
||||
pseudoUpToDate = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// We have an output older than an upstream output - we are out of date
|
||||
return {
|
||||
type: UpToDateStatusType.OutOfDateWithUpstream,
|
||||
outOfDateOutputFileName: oldestOutputFileName,
|
||||
newerProjectName: ref.path
|
||||
};
|
||||
}
|
||||
|
||||
// Up to date
|
||||
@ -446,6 +539,7 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Use the better algorithm
|
||||
function createDependencyGraph(roots: string[]): DependencyGraph {
|
||||
// This is a list of list of projects that need to be built.
|
||||
// The ordering here is "backwards", i.e. the first entry in the array is the last set of projects that need to be built;
|
||||
@ -480,10 +574,11 @@ namespace ts {
|
||||
if (refs === undefined) return;
|
||||
buildQueuePosition++;
|
||||
for (const ref of refs) {
|
||||
dependencyMap.addReference(fileName, ref.path);
|
||||
const resolvedRef = configFileCache.parseConfigFile(ref.path);
|
||||
const actualPath = resolveProjectReferencePath(host, ref);
|
||||
dependencyMap.addReference(fileName, actualPath);
|
||||
const resolvedRef = configFileCache.parseConfigFile(actualPath);
|
||||
if (resolvedRef === undefined) continue;
|
||||
enumerateReferences(normalizePath(ref.path), resolvedRef);
|
||||
enumerateReferences(normalizePath(actualPath), resolvedRef);
|
||||
}
|
||||
buildQueuePosition--;
|
||||
}
|
||||
@ -507,7 +602,14 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function buildSingleProject(proj: string, context: BuildContext) {
|
||||
// TODO Accept parsedCommandLine
|
||||
function buildSingleProject(proj: string) {
|
||||
if (context.options.dry) {
|
||||
diagReporter(createCompilerDiagnostic(Diagnostics.Would_build_project_0, proj));
|
||||
}
|
||||
|
||||
context.verbose(Diagnostics.Building_project_0, proj);
|
||||
|
||||
let resultFlags = BuildResultFlags.None;
|
||||
resultFlags |= BuildResultFlags.DeclarationOutputUnchanged;
|
||||
|
||||
@ -549,8 +651,8 @@ namespace ts {
|
||||
for (const diag of declDiagnostics) {
|
||||
diagReporter(diag);
|
||||
}
|
||||
return resultFlags;
|
||||
}
|
||||
return resultFlags;
|
||||
}
|
||||
|
||||
const semanticDiagnostics = [...program.getSemanticDiagnostics()];
|
||||
@ -562,47 +664,160 @@ namespace ts {
|
||||
return resultFlags;
|
||||
}
|
||||
|
||||
let newestDeclarationFileContentChangedTime = minimumDate;
|
||||
program.emit(undefined, (fileName, content, writeBom, onError) => {
|
||||
let priorChangeTime: Date | undefined;
|
||||
|
||||
if (isDeclarationFile(fileName) && host.fileExists(fileName)) {
|
||||
if (host.readFile(fileName) === content) {
|
||||
// Check for unchanged .d.ts files
|
||||
resultFlags &= ~BuildResultFlags.DeclarationOutputUnchanged;
|
||||
priorChangeTime = host.getLastWriteTime && host.getLastWriteTime(fileName);
|
||||
priorChangeTime = host.getModifiedTime && host.getModifiedTime(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
host.writeFile(fileName, content, writeBom, onError, emptyArray);
|
||||
if (priorChangeTime !== undefined) {
|
||||
newestDeclarationFileContentChangedTime = newer(priorChangeTime, newestDeclarationFileContentChangedTime);
|
||||
context.unchangedOutputs.setValue(fileName, priorChangeTime);
|
||||
}
|
||||
});
|
||||
|
||||
context.projectStatus.setValue(proj, { type: UpToDateStatusType.UpToDate, newestDeclarationFileContentChangedTime } as UpToDateStatus);
|
||||
|
||||
return resultFlags;
|
||||
}
|
||||
|
||||
function buildProjects(configFileNames: string[], context: BuildContext) {
|
||||
function updateOutputTimestamps(proj: ParsedCommandLine) {
|
||||
if (context.options.dry) {
|
||||
diagReporter(createCompilerDiagnostic(Diagnostics.Would_build_project_0, proj.options.configFilePath));
|
||||
return;
|
||||
}
|
||||
|
||||
context.verbose(Diagnostics.Updating_output_timestamps_of_project_0, proj.options.configFilePath);
|
||||
const now = new Date();
|
||||
const outputs = getAllProjectOutputs(proj);
|
||||
let priorNewestUpdateTime = minimumDate;
|
||||
for (const file of outputs) {
|
||||
if (isDeclarationFile(file)) {
|
||||
priorNewestUpdateTime = newer(priorNewestUpdateTime, host.getModifiedTime(file));
|
||||
}
|
||||
host.setModifiedTime(file, now);
|
||||
}
|
||||
|
||||
context.projectStatus.setValue(proj.options.configFilePath, { type: UpToDateStatusType.UpToDate, newestDeclarationFileContentChangedTime: priorNewestUpdateTime } as UpToDateStatus);
|
||||
}
|
||||
|
||||
function cleanProjects(configFileNames: string[]) {
|
||||
// Get the same graph for cleaning we'd use for building
|
||||
const graph = createDependencyGraph(configFileNames);
|
||||
|
||||
const fileReport: string[] = [];
|
||||
for (const level of graph.buildQueue) {
|
||||
for (const proj of level) {
|
||||
const parsed = configFileCache.parseConfigFile(proj);
|
||||
const outputs = getAllProjectOutputs(parsed);
|
||||
for (const output of outputs) {
|
||||
if (host.fileExists(output)) {
|
||||
if (context.options.dry) {
|
||||
fileReport.push(output);
|
||||
}
|
||||
else {
|
||||
host.deleteFile(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (context.options.dry) {
|
||||
diagReporter(createCompilerDiagnostic(Diagnostics.Would_delete_the_following_files_Colon_0, fileReport.map(f => `\r\n * ${f}`).join("")));
|
||||
}
|
||||
}
|
||||
|
||||
function buildProjects(configFileNames: string[]) {
|
||||
// Establish what needs to be built
|
||||
const graph = createDependencyGraph(configFileNames);
|
||||
|
||||
const queue = graph.buildQueue;
|
||||
while (queue.length > 0) {
|
||||
const next = queue[0].pop()!;
|
||||
reportBuildQueue(graph);
|
||||
|
||||
const result = buildSingleProject(next, context);
|
||||
let next: string;
|
||||
while (next = getNext()) {
|
||||
const proj = configFileCache.parseConfigFile(next);
|
||||
const status = getUpToDateStatus(proj, context);
|
||||
reportProjectStatus(next, status);
|
||||
|
||||
if (status.type === UpToDateStatusType.UpToDate && !context.options.force) {
|
||||
// Up to date, skip
|
||||
continue;
|
||||
}
|
||||
|
||||
if (status.type === UpToDateStatusType.UpToDateWithUpstreamTypes && !context.options.force) {
|
||||
// Fake build
|
||||
updateOutputTimestamps(proj);
|
||||
continue;
|
||||
}
|
||||
|
||||
const result = buildSingleProject(next);
|
||||
if (result & BuildResultFlags.AnyErrors) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (queue[0].length === 0) {
|
||||
queue.pop();
|
||||
function getNext(): string | undefined {
|
||||
if (queue.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
while (queue.length > 0) {
|
||||
const last = queue[queue.length - 1];
|
||||
if (last.length === 0) {
|
||||
queue.pop();
|
||||
continue;
|
||||
}
|
||||
return last.pop()!;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
getUpToDateStatus,
|
||||
buildProjects
|
||||
};
|
||||
function reportBuildQueue(graph: DependencyGraph) {
|
||||
if (!context.options.verbose) return;
|
||||
|
||||
const names: string[] = [];
|
||||
for (const level of graph.buildQueue) {
|
||||
for (const el of level) {
|
||||
names.push(el);
|
||||
}
|
||||
}
|
||||
names.reverse();
|
||||
context.verbose(Diagnostics.Sorted_list_of_input_projects_Colon_0, names.map(s => "\r\n * " + s).join(""));
|
||||
}
|
||||
|
||||
function reportProjectStatus(configFileName: string, status: UpToDateStatus) {
|
||||
if (!context.options.verbose) return;
|
||||
switch (status.type) {
|
||||
case UpToDateStatusType.OutOfDateWithSelf:
|
||||
context.verbose(Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, configFileName, status.outOfDateOutputFileName, status.newerInputFileName);
|
||||
return;
|
||||
case UpToDateStatusType.OutOfDateWithUpstream:
|
||||
context.verbose(Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, configFileName, status.outOfDateOutputFileName, status.newerProjectName);
|
||||
return;
|
||||
case UpToDateStatusType.OutputMissing:
|
||||
context.verbose(Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, configFileName, status.missingOutputFileName);
|
||||
return;
|
||||
case UpToDateStatusType.UpToDate:
|
||||
context.verbose(Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, configFileName, status.newestDeclarationFileContentChangedTime as any, status.newestOutputFileTime);
|
||||
return;
|
||||
case UpToDateStatusType.UpToDateWithUpstreamTypes:
|
||||
context.verbose(Diagnostics.Project_0_is_up_to_date_with_its_upstream_types, configFileName);
|
||||
return;
|
||||
case UpToDateStatusType.UpstreamOutOfDate:
|
||||
context.verbose(Diagnostics.Project_0_is_up_to_date_with_its_upstream_types, configFileName);
|
||||
return;
|
||||
default:
|
||||
throw new Error(`Invalid build status - ${UpToDateStatusType[status.type]}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,6 +47,10 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function executeCommandLine(args: string[]): void {
|
||||
if ((args[0].toLowerCase() === "--build") || (args[0].toLowerCase() === "-b")) {
|
||||
return performBuild(args.slice(1));
|
||||
}
|
||||
|
||||
const commandLine = parseCommandLine(args);
|
||||
|
||||
// Configuration file name (if any)
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
"resolutionCache.ts",
|
||||
"watch.ts",
|
||||
"commandLineParser.ts",
|
||||
"tsbuild.ts",
|
||||
"tsc.ts",
|
||||
"tsbuild.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@ -4769,7 +4769,9 @@ namespace ts {
|
||||
/* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean;
|
||||
createHash?(data: string): string;
|
||||
|
||||
getLastWriteTime?(fileName: string): Date;
|
||||
getModifiedTime?(fileName: string): Date;
|
||||
setModifiedTime?(fileName: string, date: Date): void;
|
||||
deleteFile?(fileName: string): void;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user