mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge pull request #24465 from RyanCavanaugh/tsbuild
"tsc -b" with minimal watch capabilities
This commit is contained in:
@@ -109,6 +109,14 @@ namespace ts {
|
||||
paramType: Diagnostics.FILE_OR_DIRECTORY,
|
||||
description: Diagnostics.Compile_the_project_given_the_path_to_its_configuration_file_or_to_a_folder_with_a_tsconfig_json,
|
||||
},
|
||||
{
|
||||
name: "build",
|
||||
type: "boolean",
|
||||
shortName: "b",
|
||||
showInSimplifiedHelpView: true,
|
||||
category: Diagnostics.Command_line_Options,
|
||||
description: Diagnostics.Build_one_or_more_projects_and_their_dependencies_if_out_of_date
|
||||
},
|
||||
{
|
||||
name: "pretty",
|
||||
type: "boolean",
|
||||
@@ -968,6 +976,125 @@ namespace ts {
|
||||
}
|
||||
|
||||
|
||||
function getDiagnosticText(_message: DiagnosticMessage, ..._args: any[]): string {
|
||||
const diagnostic = createCompilerDiagnostic.apply(undefined, arguments);
|
||||
return <string>diagnostic.messageText;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function printVersion() {
|
||||
sys.write(getDiagnosticText(Diagnostics.Version_0, version) + sys.newLine);
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function printHelp(optionsList: CommandLineOption[], syntaxPrefix = "") {
|
||||
const output: string[] = [];
|
||||
|
||||
// We want to align our "syntax" and "examples" commands to a certain margin.
|
||||
const syntaxLength = getDiagnosticText(Diagnostics.Syntax_Colon_0, "").length;
|
||||
const examplesLength = getDiagnosticText(Diagnostics.Examples_Colon_0, "").length;
|
||||
let marginLength = Math.max(syntaxLength, examplesLength);
|
||||
|
||||
// Build up the syntactic skeleton.
|
||||
let syntax = makePadding(marginLength - syntaxLength);
|
||||
syntax += `tsc ${syntaxPrefix}[${getDiagnosticText(Diagnostics.options)}] [${getDiagnosticText(Diagnostics.file)}...]`;
|
||||
|
||||
output.push(getDiagnosticText(Diagnostics.Syntax_Colon_0, syntax));
|
||||
output.push(sys.newLine + sys.newLine);
|
||||
|
||||
// Build up the list of examples.
|
||||
const padding = makePadding(marginLength);
|
||||
output.push(getDiagnosticText(Diagnostics.Examples_Colon_0, makePadding(marginLength - examplesLength) + "tsc hello.ts") + sys.newLine);
|
||||
output.push(padding + "tsc --outFile file.js file.ts" + sys.newLine);
|
||||
output.push(padding + "tsc @args.txt" + sys.newLine);
|
||||
output.push(padding + "tsc --build tsconfig.json" + sys.newLine);
|
||||
output.push(sys.newLine);
|
||||
|
||||
output.push(getDiagnosticText(Diagnostics.Options_Colon) + sys.newLine);
|
||||
|
||||
// We want our descriptions to align at the same column in our output,
|
||||
// so we keep track of the longest option usage string.
|
||||
marginLength = 0;
|
||||
const usageColumn: string[] = []; // Things like "-d, --declaration" go in here.
|
||||
const descriptionColumn: string[] = [];
|
||||
|
||||
const optionsDescriptionMap = createMap<string[]>(); // Map between option.description and list of option.type if it is a kind
|
||||
|
||||
for (const option of optionsList) {
|
||||
// If an option lacks a description,
|
||||
// it is not officially supported.
|
||||
if (!option.description) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let usageText = " ";
|
||||
if (option.shortName) {
|
||||
usageText += "-" + option.shortName;
|
||||
usageText += getParamType(option);
|
||||
usageText += ", ";
|
||||
}
|
||||
|
||||
usageText += "--" + option.name;
|
||||
usageText += getParamType(option);
|
||||
|
||||
usageColumn.push(usageText);
|
||||
let description: string;
|
||||
|
||||
if (option.name === "lib") {
|
||||
description = getDiagnosticText(option.description);
|
||||
const element = (<CommandLineOptionOfListType>option).element;
|
||||
const typeMap = <Map<number | string>>element.type;
|
||||
optionsDescriptionMap.set(description, arrayFrom(typeMap.keys()).map(key => `'${key}'`));
|
||||
}
|
||||
else {
|
||||
description = getDiagnosticText(option.description);
|
||||
}
|
||||
|
||||
descriptionColumn.push(description);
|
||||
|
||||
// Set the new margin for the description column if necessary.
|
||||
marginLength = Math.max(usageText.length, marginLength);
|
||||
}
|
||||
|
||||
// Special case that can't fit in the loop.
|
||||
const usageText = " @<" + getDiagnosticText(Diagnostics.file) + ">";
|
||||
usageColumn.push(usageText);
|
||||
descriptionColumn.push(getDiagnosticText(Diagnostics.Insert_command_line_options_and_files_from_a_file));
|
||||
marginLength = Math.max(usageText.length, marginLength);
|
||||
|
||||
// Print out each row, aligning all the descriptions on the same column.
|
||||
for (let i = 0; i < usageColumn.length; i++) {
|
||||
const usage = usageColumn[i];
|
||||
const description = descriptionColumn[i];
|
||||
const kindsList = optionsDescriptionMap.get(description);
|
||||
output.push(usage + makePadding(marginLength - usage.length + 2) + description + sys.newLine);
|
||||
|
||||
if (kindsList) {
|
||||
output.push(makePadding(marginLength + 4));
|
||||
for (const kind of kindsList) {
|
||||
output.push(kind + " ");
|
||||
}
|
||||
output.push(sys.newLine);
|
||||
}
|
||||
}
|
||||
|
||||
for (const line of output) {
|
||||
sys.write(line);
|
||||
}
|
||||
return;
|
||||
|
||||
function getParamType(option: CommandLineOption) {
|
||||
if (option.paramType !== undefined) {
|
||||
return " " + getDiagnosticText(option.paramType);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function makePadding(paddingLength: number): string {
|
||||
return Array(paddingLength + 1).join(" ");
|
||||
}
|
||||
}
|
||||
|
||||
export type DiagnosticReporter = (diagnostic: Diagnostic) => void;
|
||||
/**
|
||||
* Reports config file diagnostics
|
||||
|
||||
@@ -3620,6 +3620,95 @@
|
||||
"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 out of date because its dependency '{1}' is out of date": {
|
||||
"category": "Message",
|
||||
"code": 6353
|
||||
},
|
||||
|
||||
"Project '{0}' is up to date with .d.ts files from its dependencies": {
|
||||
"category": "Message",
|
||||
"code": 6354
|
||||
},
|
||||
"Projects in this build: {0}": {
|
||||
"category": "Message",
|
||||
"code": 6355
|
||||
},
|
||||
"A non-dry build would delete the following files: {0}": {
|
||||
"category": "Message",
|
||||
"code": 6356
|
||||
},
|
||||
"A non-dry build would build project '{0}'": {
|
||||
"category": "Message",
|
||||
"code": 6357
|
||||
},
|
||||
"Building project '{0}'...": {
|
||||
"category": "Message",
|
||||
"code": 6358
|
||||
},
|
||||
"Updating output timestamps of project '{0}'...": {
|
||||
"category": "Message",
|
||||
"code": 6359
|
||||
},
|
||||
"delete this - Project '{0}' is up to date because it was previously built": {
|
||||
"category": "Message",
|
||||
"code": 6360
|
||||
},
|
||||
"Project '{0}' is up to date": {
|
||||
"category": "Message",
|
||||
"code": 6361
|
||||
},
|
||||
"Skipping build of project '{0}' because its dependency '{1}' has errors": {
|
||||
"category": "Message",
|
||||
"code": 6362
|
||||
},
|
||||
"Project '{0}' can't be built because its dependency '{1}' has errors": {
|
||||
"category": "Message",
|
||||
"code": 6363
|
||||
},
|
||||
"Build one or more projects and their dependencies, if out of date": {
|
||||
"category": "Message",
|
||||
"code": 6364
|
||||
},
|
||||
"Delete the outputs of all projects": {
|
||||
"category": "Message",
|
||||
"code": 6365
|
||||
},
|
||||
"Enable verbose logging": {
|
||||
"category": "Message",
|
||||
"code": 6366
|
||||
},
|
||||
"Show what would be built (or deleted, if specified with '--clean')": {
|
||||
"category": "Message",
|
||||
"code": 6367
|
||||
},
|
||||
"Build all projects, including those that appear to be up to date": {
|
||||
"category": "Message",
|
||||
"code": 6368
|
||||
},
|
||||
"Option '--build' must be the first command line argument.": {
|
||||
"category": "Error",
|
||||
"code": 6369
|
||||
},
|
||||
"Options '{0}' and '{1}' cannot be combined.": {
|
||||
"category": "Error",
|
||||
"code": 6370
|
||||
},
|
||||
"Skipping clean because not all projects could be located": {
|
||||
"category": "Error",
|
||||
"code": 6371
|
||||
},
|
||||
|
||||
"Variable '{0}' implicitly has an '{1}' type.": {
|
||||
"category": "Error",
|
||||
|
||||
@@ -1026,7 +1026,7 @@ namespace ts {
|
||||
|
||||
// SyntaxKind.UnparsedSource
|
||||
function emitUnparsedSource(unparsed: UnparsedSource) {
|
||||
write(unparsed.text);
|
||||
writer.rawWrite(unparsed.text);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -2587,16 +2587,19 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
|
||||
export function createUnparsedSourceFile(text: string): UnparsedSource {
|
||||
export function createUnparsedSourceFile(text: string, map?: string): UnparsedSource {
|
||||
const node = <UnparsedSource>createNode(SyntaxKind.UnparsedSource);
|
||||
node.text = text;
|
||||
node.sourceMapText = map;
|
||||
return node;
|
||||
}
|
||||
|
||||
export function createInputFiles(javascript: string, declaration: string): InputFiles {
|
||||
export function createInputFiles(javascript: string, declaration: string, javascriptMapText?: string, declarationMapText?: string): InputFiles {
|
||||
const node = <InputFiles>createNode(SyntaxKind.InputFiles);
|
||||
node.javascriptText = javascript;
|
||||
node.javascriptMapText = javascriptMapText;
|
||||
node.declarationText = declaration;
|
||||
node.declarationMapText = declarationMapText;
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
+25
-17
@@ -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: sys.getModifiedTime && (path => sys.getModifiedTime!(path)),
|
||||
setModifiedTime: sys.setModifiedTime && ((path, date) => sys.setModifiedTime!(path, date)),
|
||||
deleteFile: sys.deleteFile && (path => sys.deleteFile!(path))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -615,25 +618,27 @@ namespace ts {
|
||||
// A parallel array to projectReferences storing the results of reading in the referenced tsconfig files
|
||||
const resolvedProjectReferences: (ResolvedProjectReference | undefined)[] | undefined = projectReferences ? [] : undefined;
|
||||
const projectReferenceRedirects: Map<string> = createMap();
|
||||
if (projectReferences) {
|
||||
for (const ref of projectReferences) {
|
||||
const parsedRef = parseProjectReferenceConfigFile(ref);
|
||||
resolvedProjectReferences!.push(parsedRef);
|
||||
if (parsedRef) {
|
||||
if (parsedRef.commandLine.options.outFile) {
|
||||
const dtsOutfile = changeExtension(parsedRef.commandLine.options.outFile, ".d.ts");
|
||||
processSourceFile(dtsOutfile, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
|
||||
}
|
||||
addProjectReferenceRedirects(parsedRef.commandLine, projectReferenceRedirects);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options);
|
||||
const structuralIsReused = tryReuseStructureFromOldProgram();
|
||||
if (structuralIsReused !== StructureIsReused.Completely) {
|
||||
processingDefaultLibFiles = [];
|
||||
processingOtherFiles = [];
|
||||
|
||||
if (projectReferences) {
|
||||
for (const ref of projectReferences) {
|
||||
const parsedRef = parseProjectReferenceConfigFile(ref);
|
||||
resolvedProjectReferences!.push(parsedRef);
|
||||
if (parsedRef) {
|
||||
if (parsedRef.commandLine.options.outFile) {
|
||||
const dtsOutfile = changeExtension(parsedRef.commandLine.options.outFile, ".d.ts");
|
||||
processSourceFile(dtsOutfile, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
|
||||
}
|
||||
addProjectReferenceRedirects(parsedRef.commandLine, projectReferenceRedirects);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
forEach(rootNames, name => processRootFile(name, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false));
|
||||
|
||||
// load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders
|
||||
@@ -1021,7 +1026,7 @@ namespace ts {
|
||||
|
||||
for (const oldSourceFile of oldSourceFiles) {
|
||||
let newSourceFile = host.getSourceFileByPath
|
||||
? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.path, options.target!, /*onError*/ undefined, shouldCreateNewSourceFile)
|
||||
? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.resolvedPath || oldSourceFile.path, options.target!, /*onError*/ undefined, shouldCreateNewSourceFile)
|
||||
: host.getSourceFile(oldSourceFile.fileName, options.target!, /*onError*/ undefined, shouldCreateNewSourceFile); // TODO: GH#18217
|
||||
|
||||
if (!newSourceFile) {
|
||||
@@ -1234,8 +1239,10 @@ namespace ts {
|
||||
|
||||
const dtsFilename = changeExtension(resolvedRefOpts.options.outFile, ".d.ts");
|
||||
const js = host.readFile(resolvedRefOpts.options.outFile) || `/* Input file ${resolvedRefOpts.options.outFile} was missing */\r\n`;
|
||||
const jsMap = host.readFile(resolvedRefOpts.options.outFile + ".map"); // TODO: try to read sourceMappingUrl comment from the js file
|
||||
const dts = host.readFile(dtsFilename) || `/* Input file ${dtsFilename} was missing */\r\n`;
|
||||
const node = createInputFiles(js, dts);
|
||||
const dtsMap = host.readFile(dtsFilename + ".map");
|
||||
const node = createInputFiles(js, dts, jsMap, dtsMap);
|
||||
nodes.push(node);
|
||||
}
|
||||
}
|
||||
@@ -2047,6 +2054,7 @@ namespace ts {
|
||||
if (file) {
|
||||
sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0);
|
||||
file.path = path;
|
||||
file.resolvedPath = toPath(fileName);
|
||||
|
||||
if (host.useCaseSensitiveFileNames()) {
|
||||
const pathLowerCase = path.toLowerCase();
|
||||
@@ -2781,7 +2789,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");
|
||||
}
|
||||
|
||||
+107
-11
@@ -99,6 +99,10 @@ namespace ts {
|
||||
let sourceMapDataList: SourceMapData[] | undefined;
|
||||
let disabled: boolean = !(compilerOptions.sourceMap || compilerOptions.inlineSourceMap);
|
||||
|
||||
let completedSections: SourceMapSectionDefinition[];
|
||||
let sectionStartLine: number;
|
||||
let sectionStartColumn: number;
|
||||
|
||||
return {
|
||||
initialize,
|
||||
reset,
|
||||
@@ -146,6 +150,9 @@ namespace ts {
|
||||
lastEncodedNameIndex = 0;
|
||||
|
||||
// Initialize source map data
|
||||
completedSections = [];
|
||||
sectionStartLine = 1;
|
||||
sectionStartColumn = 1;
|
||||
sourceMapData = {
|
||||
sourceMapFilePath,
|
||||
jsSourceMappingURL: !compilerOptions.inlineSourceMap ? getBaseFileName(normalizeSlashes(sourceMapFilePath)) : undefined!, // TODO: GH#18217
|
||||
@@ -214,6 +221,65 @@ namespace ts {
|
||||
lastEncodedNameIndex = undefined;
|
||||
sourceMapData = undefined!;
|
||||
sourceMapDataList = undefined!;
|
||||
completedSections = undefined!;
|
||||
sectionStartLine = undefined!;
|
||||
sectionStartColumn = undefined!;
|
||||
}
|
||||
|
||||
interface SourceMapSection {
|
||||
version: 3;
|
||||
file: string;
|
||||
sourceRoot?: string;
|
||||
sources: string[];
|
||||
names?: string[];
|
||||
mappings: string;
|
||||
sourcesContent?: string[];
|
||||
sections?: undefined;
|
||||
}
|
||||
|
||||
type SourceMapSectionDefinition =
|
||||
| { offset: { line: number, column: number }, url: string } // Included for completeness
|
||||
| { offset: { line: number, column: number }, map: SourceMap };
|
||||
|
||||
interface SectionalSourceMap {
|
||||
version: 3;
|
||||
file: string;
|
||||
sections: SourceMapSectionDefinition[];
|
||||
}
|
||||
|
||||
type SourceMap = SectionalSourceMap | SourceMapSection;
|
||||
|
||||
function captureSection(): SourceMapSection {
|
||||
return {
|
||||
version: 3,
|
||||
file: sourceMapData.sourceMapFile,
|
||||
sourceRoot: sourceMapData.sourceMapSourceRoot,
|
||||
sources: sourceMapData.sourceMapSources,
|
||||
names: sourceMapData.sourceMapNames,
|
||||
mappings: sourceMapData.sourceMapMappings,
|
||||
sourcesContent: sourceMapData.sourceMapSourcesContent,
|
||||
};
|
||||
}
|
||||
|
||||
function resetSectionalData(): void {
|
||||
sourceMapData.sourceMapSources = [];
|
||||
sourceMapData.sourceMapNames = [];
|
||||
sourceMapData.sourceMapMappings = "";
|
||||
sourceMapData.sourceMapSourcesContent = compilerOptions.inlineSources ? [] : undefined;
|
||||
}
|
||||
|
||||
function generateMap(): SourceMap {
|
||||
if (completedSections.length) {
|
||||
captureSectionalSpanIfNeeded(/*reset*/ false);
|
||||
return {
|
||||
version: 3,
|
||||
file: sourceMapData.sourceMapFile,
|
||||
sections: completedSections
|
||||
};
|
||||
}
|
||||
else {
|
||||
return captureSection();
|
||||
}
|
||||
}
|
||||
|
||||
// Encoding for sourcemap span
|
||||
@@ -284,8 +350,8 @@ namespace ts {
|
||||
sourceLinePos.line++;
|
||||
sourceLinePos.character++;
|
||||
|
||||
const emittedLine = writer.getLine();
|
||||
const emittedColumn = writer.getColumn();
|
||||
const emittedLine = writer.getLine() - sectionStartLine + 1;
|
||||
const emittedColumn = emittedLine === 0 ? (writer.getColumn() - sectionStartColumn + 1) : writer.getColumn();
|
||||
|
||||
// If this location wasn't recorded or the location in source is going backwards, record the span
|
||||
if (!lastRecordedSourceMapSpan ||
|
||||
@@ -320,6 +386,15 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function captureSectionalSpanIfNeeded(reset: boolean) {
|
||||
if (lastRecordedSourceMapSpan && lastRecordedSourceMapSpan === lastEncodedSourceMapSpan) { // If we've recorded some spans, save them
|
||||
completedSections.push({ offset: { line: sectionStartLine - 1, column: sectionStartColumn - 1 }, map: captureSection() });
|
||||
if (reset) {
|
||||
resetSectionalData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a node with possible leading and trailing source maps.
|
||||
*
|
||||
@@ -333,6 +408,35 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (node) {
|
||||
if (isUnparsedSource(node) && node.sourceMapText !== undefined) {
|
||||
captureSectionalSpanIfNeeded(/*reset*/ true);
|
||||
const text = node.sourceMapText;
|
||||
let parsed: {} | undefined;
|
||||
try {
|
||||
parsed = JSON.parse(text);
|
||||
}
|
||||
catch {
|
||||
// empty
|
||||
}
|
||||
const offset = { line: writer.getLine() - 1, column: writer.getColumn() - 1 };
|
||||
completedSections.push(parsed
|
||||
? {
|
||||
offset,
|
||||
map: parsed as SourceMap
|
||||
}
|
||||
: {
|
||||
offset,
|
||||
// This is just passes the buck on sourcemaps we don't really understand, instead of issuing an error (which would be difficult this late)
|
||||
url: `data:application/json;charset=utf-8;base64,${base64encode(sys, text)}`
|
||||
}
|
||||
);
|
||||
const emitResult = emitCallback(hint, node);
|
||||
sectionStartLine = writer.getLine();
|
||||
sectionStartColumn = writer.getColumn();
|
||||
lastRecordedSourceMapSpan = undefined!;
|
||||
lastEncodedSourceMapSpan = defaultLastEncodedSourceMapSpan;
|
||||
return emitResult;
|
||||
}
|
||||
const emitNode = node.emitNode;
|
||||
const emitFlags = emitNode && emitNode.flags || EmitFlags.None;
|
||||
const range = emitNode && emitNode.sourceMapRange;
|
||||
@@ -460,15 +564,7 @@ namespace ts {
|
||||
|
||||
encodeLastRecordedSourceMapSpan();
|
||||
|
||||
return JSON.stringify({
|
||||
version: 3,
|
||||
file: sourceMapData.sourceMapFile,
|
||||
sourceRoot: sourceMapData.sourceMapSourceRoot,
|
||||
sources: sourceMapData.sourceMapSources,
|
||||
names: sourceMapData.sourceMapNames,
|
||||
mappings: sourceMapData.sourceMapMappings,
|
||||
sourcesContent: sourceMapData.sourceMapSourcesContent,
|
||||
});
|
||||
return JSON.stringify(generateMap());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -433,6 +433,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
|
||||
@@ -448,6 +449,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;
|
||||
/**
|
||||
* A good implementation is node.js' `crypto.createHash`. (https://nodejs.org/api/crypto.html#crypto_crypto_createhash_algorithm)
|
||||
*/
|
||||
@@ -592,6 +595,8 @@ namespace ts {
|
||||
},
|
||||
readDirectory,
|
||||
getModifiedTime,
|
||||
setModifiedTime,
|
||||
deleteFile,
|
||||
createHash: _crypto ? createMD5HashUsingNativeCrypto : generateDjb2Hash,
|
||||
createSHA256Hash: _crypto ? createSHA256Hash : undefined,
|
||||
getMemoryUsage() {
|
||||
@@ -1069,6 +1074,24 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function setModifiedTime(path: string, time: Date) {
|
||||
try {
|
||||
_fs.utimesSync(path, time, time);
|
||||
}
|
||||
catch (e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function deleteFile(path: string) {
|
||||
try {
|
||||
return _fs.unlinkSync(path);
|
||||
}
|
||||
catch (e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* djb2 hashing algorithm
|
||||
* http://www.cse.yorku.ca/~oz/hash.html
|
||||
|
||||
@@ -180,7 +180,7 @@ namespace ts {
|
||||
}
|
||||
), mapDefined(node.prepends, prepend => {
|
||||
if (prepend.kind === SyntaxKind.InputFiles) {
|
||||
return createUnparsedSourceFile(prepend.declarationText);
|
||||
return createUnparsedSourceFile(prepend.declarationText, prepend.declarationMapText);
|
||||
}
|
||||
}));
|
||||
bundle.syntheticFileReferences = [];
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace ts {
|
||||
function transformBundle(node: Bundle) {
|
||||
return createBundle(node.sourceFiles.map(transformSourceFile), mapDefined(node.prepends, prepend => {
|
||||
if (prepend.kind === SyntaxKind.InputFiles) {
|
||||
return createUnparsedSourceFile(prepend.javascriptText);
|
||||
return createUnparsedSourceFile(prepend.javascriptText, prepend.javascriptMapText);
|
||||
}
|
||||
return prepend;
|
||||
}));
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+26
-123
@@ -12,11 +12,6 @@ namespace ts {
|
||||
return count;
|
||||
}
|
||||
|
||||
function getDiagnosticText(_message: DiagnosticMessage, ..._args: any[]): string {
|
||||
const diagnostic = createCompilerDiagnostic.apply(undefined, arguments);
|
||||
return <string>diagnostic.messageText;
|
||||
}
|
||||
|
||||
let reportDiagnostic = createDiagnosticReporter(sys);
|
||||
function updateReportDiagnostic(options: CompilerOptions) {
|
||||
if (shouldBePretty(options)) {
|
||||
@@ -46,9 +41,33 @@ namespace ts {
|
||||
return s;
|
||||
}
|
||||
|
||||
function getOptionsForHelp(commandLine: ParsedCommandLine) {
|
||||
// Sort our options by their names, (e.g. "--noImplicitAny" comes before "--watch")
|
||||
return !!commandLine.options.all ?
|
||||
sort(optionDeclarations, (a, b) => compareStringsCaseInsensitive(a.name, b.name)) :
|
||||
filter(optionDeclarations.slice(), v => !!v.showInSimplifiedHelpView);
|
||||
}
|
||||
|
||||
export function executeCommandLine(args: string[]): void {
|
||||
if (args.length > 0 && ((args[0].toLowerCase() === "--build") || (args[0].toLowerCase() === "-b"))) {
|
||||
const reportDiag = createDiagnosticReporter(sys, /*pretty*/ true);
|
||||
const report = (message: DiagnosticMessage, ...args: string[]) => reportDiag(createCompilerDiagnostic(message, ...args));
|
||||
const buildHost: BuildHost = {
|
||||
error: report,
|
||||
verbose: report,
|
||||
message: report,
|
||||
errorDiagnostic: d => reportDiag(d)
|
||||
};
|
||||
return performBuild(args.slice(1), createCompilerHost({}), buildHost, sys);
|
||||
}
|
||||
|
||||
const commandLine = parseCommandLine(args);
|
||||
|
||||
if (commandLine.options.build) {
|
||||
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Option_build_must_be_the_first_command_line_argument));
|
||||
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
|
||||
}
|
||||
|
||||
// Configuration file name (if any)
|
||||
let configFileName: string | undefined;
|
||||
if (commandLine.options.locale) {
|
||||
@@ -74,7 +93,7 @@ namespace ts {
|
||||
|
||||
if (commandLine.options.help || commandLine.options.all) {
|
||||
printVersion();
|
||||
printHelp(!!commandLine.options.all);
|
||||
printHelp(getOptionsForHelp(commandLine));
|
||||
return sys.exit(ExitStatus.Success);
|
||||
}
|
||||
|
||||
@@ -107,7 +126,7 @@ namespace ts {
|
||||
|
||||
if (commandLine.fileNames.length === 0 && !configFileName) {
|
||||
printVersion();
|
||||
printHelp(!!commandLine.options.all);
|
||||
printHelp(getOptionsForHelp(commandLine));
|
||||
return sys.exit(ExitStatus.Success);
|
||||
}
|
||||
|
||||
@@ -271,122 +290,6 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function printVersion() {
|
||||
sys.write(getDiagnosticText(Diagnostics.Version_0, version) + sys.newLine);
|
||||
}
|
||||
|
||||
function printHelp(showAllOptions: boolean) {
|
||||
const output: string[] = [];
|
||||
|
||||
// We want to align our "syntax" and "examples" commands to a certain margin.
|
||||
const syntaxLength = getDiagnosticText(Diagnostics.Syntax_Colon_0, "").length;
|
||||
const examplesLength = getDiagnosticText(Diagnostics.Examples_Colon_0, "").length;
|
||||
let marginLength = Math.max(syntaxLength, examplesLength);
|
||||
|
||||
// Build up the syntactic skeleton.
|
||||
let syntax = makePadding(marginLength - syntaxLength);
|
||||
syntax += "tsc [" + getDiagnosticText(Diagnostics.options) + "] [" + getDiagnosticText(Diagnostics.file) + " ...]";
|
||||
|
||||
output.push(getDiagnosticText(Diagnostics.Syntax_Colon_0, syntax));
|
||||
output.push(sys.newLine + sys.newLine);
|
||||
|
||||
// Build up the list of examples.
|
||||
const padding = makePadding(marginLength);
|
||||
output.push(getDiagnosticText(Diagnostics.Examples_Colon_0, makePadding(marginLength - examplesLength) + "tsc hello.ts") + sys.newLine);
|
||||
output.push(padding + "tsc --outFile file.js file.ts" + sys.newLine);
|
||||
output.push(padding + "tsc @args.txt" + sys.newLine);
|
||||
output.push(sys.newLine);
|
||||
|
||||
output.push(getDiagnosticText(Diagnostics.Options_Colon) + sys.newLine);
|
||||
|
||||
// Sort our options by their names, (e.g. "--noImplicitAny" comes before "--watch")
|
||||
const optsList = showAllOptions ?
|
||||
sort(optionDeclarations, (a, b) => compareStringsCaseInsensitive(a.name, b.name)) :
|
||||
filter(optionDeclarations.slice(), v => !!v.showInSimplifiedHelpView);
|
||||
|
||||
// We want our descriptions to align at the same column in our output,
|
||||
// so we keep track of the longest option usage string.
|
||||
marginLength = 0;
|
||||
const usageColumn: string[] = []; // Things like "-d, --declaration" go in here.
|
||||
const descriptionColumn: string[] = [];
|
||||
|
||||
const optionsDescriptionMap = createMap<string[]>(); // Map between option.description and list of option.type if it is a kind
|
||||
|
||||
for (const option of optsList) {
|
||||
// If an option lacks a description,
|
||||
// it is not officially supported.
|
||||
if (!option.description) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let usageText = " ";
|
||||
if (option.shortName) {
|
||||
usageText += "-" + option.shortName;
|
||||
usageText += getParamType(option);
|
||||
usageText += ", ";
|
||||
}
|
||||
|
||||
usageText += "--" + option.name;
|
||||
usageText += getParamType(option);
|
||||
|
||||
usageColumn.push(usageText);
|
||||
let description: string;
|
||||
|
||||
if (option.name === "lib") {
|
||||
description = getDiagnosticText(option.description);
|
||||
const element = (<CommandLineOptionOfListType>option).element;
|
||||
const typeMap = <Map<number | string>>element.type;
|
||||
optionsDescriptionMap.set(description, arrayFrom(typeMap.keys()).map(key => `'${key}'`));
|
||||
}
|
||||
else {
|
||||
description = getDiagnosticText(option.description);
|
||||
}
|
||||
|
||||
descriptionColumn.push(description);
|
||||
|
||||
// Set the new margin for the description column if necessary.
|
||||
marginLength = Math.max(usageText.length, marginLength);
|
||||
}
|
||||
|
||||
// Special case that can't fit in the loop.
|
||||
const usageText = " @<" + getDiagnosticText(Diagnostics.file) + ">";
|
||||
usageColumn.push(usageText);
|
||||
descriptionColumn.push(getDiagnosticText(Diagnostics.Insert_command_line_options_and_files_from_a_file));
|
||||
marginLength = Math.max(usageText.length, marginLength);
|
||||
|
||||
// Print out each row, aligning all the descriptions on the same column.
|
||||
for (let i = 0; i < usageColumn.length; i++) {
|
||||
const usage = usageColumn[i];
|
||||
const description = descriptionColumn[i];
|
||||
const kindsList = optionsDescriptionMap.get(description);
|
||||
output.push(usage + makePadding(marginLength - usage.length + 2) + description + sys.newLine);
|
||||
|
||||
if (kindsList) {
|
||||
output.push(makePadding(marginLength + 4));
|
||||
for (const kind of kindsList) {
|
||||
output.push(kind + " ");
|
||||
}
|
||||
output.push(sys.newLine);
|
||||
}
|
||||
}
|
||||
|
||||
for (const line of output) {
|
||||
sys.write(line);
|
||||
}
|
||||
return;
|
||||
|
||||
function getParamType(option: CommandLineOption) {
|
||||
if (option.paramType !== undefined) {
|
||||
return " " + getDiagnosticText(option.paramType);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function makePadding(paddingLength: number): string {
|
||||
return Array(paddingLength + 1).join(" ");
|
||||
}
|
||||
}
|
||||
|
||||
function writeConfigFile(options: CompilerOptions, fileNames: string[]) {
|
||||
const currentDirectory = sys.getCurrentDirectory();
|
||||
const file = normalizePath(combinePaths(currentDirectory, "tsconfig.json"));
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"moduleSpecifiers.ts",
|
||||
"watch.ts",
|
||||
"commandLineParser.ts",
|
||||
"tsc.ts"
|
||||
"tsbuild.ts",
|
||||
"tsc.ts",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2558,6 +2558,7 @@ namespace ts {
|
||||
fileName: string;
|
||||
/* @internal */ path: Path;
|
||||
text: string;
|
||||
/* @internal */ resolvedPath: Path;
|
||||
|
||||
/**
|
||||
* If two source files are for the same version of the same package, one will redirect to the other.
|
||||
@@ -2658,12 +2659,15 @@ namespace ts {
|
||||
export interface InputFiles extends Node {
|
||||
kind: SyntaxKind.InputFiles;
|
||||
javascriptText: string;
|
||||
javascriptMapText?: string;
|
||||
declarationText: string;
|
||||
declarationMapText?: string;
|
||||
}
|
||||
|
||||
export interface UnparsedSource extends Node {
|
||||
kind: SyntaxKind.UnparsedSource;
|
||||
text: string;
|
||||
sourceMapText?: string;
|
||||
}
|
||||
|
||||
export interface JsonSourceFile extends SourceFile {
|
||||
@@ -4297,6 +4301,9 @@ namespace ts {
|
||||
allowUnusedLabels?: boolean;
|
||||
alwaysStrict?: boolean; // Always combine with strict property
|
||||
baseUrl?: string;
|
||||
/** An error if set - this should only go through the -b pipeline and not actually be observed */
|
||||
/*@internal*/
|
||||
build?: boolean;
|
||||
charset?: string;
|
||||
checkJs?: boolean;
|
||||
/* @internal */ configFilePath?: string;
|
||||
@@ -4819,6 +4826,10 @@ namespace ts {
|
||||
/* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution;
|
||||
/* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean;
|
||||
createHash?(data: string): string;
|
||||
|
||||
getModifiedTime?(fileName: string): Date;
|
||||
setModifiedTime?(fileName: string, date: Date): void;
|
||||
deleteFile?(fileName: string): void;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
|
||||
+22
-10
@@ -2861,13 +2861,26 @@ namespace ts {
|
||||
let lineCount: number;
|
||||
let linePos: number;
|
||||
|
||||
function updateLineCountAndPosFor(s: string) {
|
||||
const lineStartsOfS = computeLineStarts(s);
|
||||
if (lineStartsOfS.length > 1) {
|
||||
lineCount = lineCount + lineStartsOfS.length - 1;
|
||||
linePos = output.length - s.length + last(lineStartsOfS);
|
||||
lineStart = (linePos - output.length) === 0;
|
||||
}
|
||||
else {
|
||||
lineStart = false;
|
||||
}
|
||||
}
|
||||
|
||||
function write(s: string) {
|
||||
if (s && s.length) {
|
||||
if (lineStart) {
|
||||
output += getIndentString(indent);
|
||||
s = getIndentString(indent) + s;
|
||||
lineStart = false;
|
||||
}
|
||||
output += s;
|
||||
updateLineCountAndPosFor(s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2881,21 +2894,14 @@ namespace ts {
|
||||
|
||||
function rawWrite(s: string) {
|
||||
if (s !== undefined) {
|
||||
if (lineStart) {
|
||||
lineStart = false;
|
||||
}
|
||||
output += s;
|
||||
updateLineCountAndPosFor(s);
|
||||
}
|
||||
}
|
||||
|
||||
function writeLiteral(s: string) {
|
||||
if (s && s.length) {
|
||||
write(s);
|
||||
const lineStartsOfS = computeLineStarts(s);
|
||||
if (lineStartsOfS.length > 1) {
|
||||
lineCount = lineCount + lineStartsOfS.length - 1;
|
||||
linePos = output.length - s.length + last(lineStartsOfS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2909,7 +2915,9 @@ namespace ts {
|
||||
}
|
||||
|
||||
function writeTextOfNode(text: string, node: Node) {
|
||||
write(getTextOfNodeFromSourceText(text, node));
|
||||
const s = getTextOfNodeFromSourceText(text, node);
|
||||
write(s);
|
||||
updateLineCountAndPosFor(s);
|
||||
}
|
||||
|
||||
reset();
|
||||
@@ -5487,6 +5495,10 @@ namespace ts {
|
||||
return node.kind === SyntaxKind.Bundle;
|
||||
}
|
||||
|
||||
export function isUnparsedSource(node: Node): node is UnparsedSource {
|
||||
return node.kind === SyntaxKind.UnparsedSource;
|
||||
}
|
||||
|
||||
// JSDoc
|
||||
|
||||
export function isJSDocTypeExpression(node: Node): node is JSDocTypeExpression {
|
||||
|
||||
+21
-1
@@ -51,6 +51,10 @@ namespace fakes {
|
||||
this.vfs.writeFileSync(path, writeByteOrderMark ? utils.addUTF8ByteOrderMark(data) : data);
|
||||
}
|
||||
|
||||
public deleteFile(path: string) {
|
||||
this.vfs.unlinkSync(path);
|
||||
}
|
||||
|
||||
public fileExists(path: string) {
|
||||
const stats = this._getStats(path);
|
||||
return stats ? stats.isFile() : false;
|
||||
@@ -131,6 +135,10 @@ namespace fakes {
|
||||
return stats ? stats.mtime : undefined!; // TODO: GH#18217
|
||||
}
|
||||
|
||||
public setModifiedTime(path: string, time: Date) {
|
||||
this.vfs.utimesSync(path, time, time);
|
||||
}
|
||||
|
||||
public createHash(data: string): string {
|
||||
return data;
|
||||
}
|
||||
@@ -244,6 +252,10 @@ namespace fakes {
|
||||
return this.sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
|
||||
}
|
||||
|
||||
public deleteFile(fileName: string) {
|
||||
this.sys.deleteFile(fileName);
|
||||
}
|
||||
|
||||
public fileExists(fileName: string): boolean {
|
||||
return this.sys.fileExists(fileName);
|
||||
}
|
||||
@@ -252,6 +264,14 @@ namespace fakes {
|
||||
return this.sys.directoryExists(directoryName);
|
||||
}
|
||||
|
||||
public getModifiedTime(fileName: string) {
|
||||
return this.sys.getModifiedTime(fileName);
|
||||
}
|
||||
|
||||
public setModifiedTime(fileName: string, time: Date) {
|
||||
return this.sys.setModifiedTime(fileName, time);
|
||||
}
|
||||
|
||||
public getDirectories(path: string): string[] {
|
||||
return this.sys.getDirectories(path);
|
||||
}
|
||||
@@ -312,7 +332,7 @@ namespace fakes {
|
||||
if (cacheKey) {
|
||||
const meta = this.vfs.filemeta(canonicalFileName);
|
||||
const sourceFileFromMetadata = meta.get(cacheKey) as ts.SourceFile | undefined;
|
||||
if (sourceFileFromMetadata) {
|
||||
if (sourceFileFromMetadata && sourceFileFromMetadata.getFullText() === content) {
|
||||
this._sourceFiles.set(canonicalFileName, sourceFileFromMetadata);
|
||||
return sourceFileFromMetadata;
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
"../compiler/resolutionCache.ts",
|
||||
"../compiler/moduleSpecifiers.ts",
|
||||
"../compiler/watch.ts",
|
||||
"../compiler/tsbuild.ts",
|
||||
"../compiler/commandLineParser.ts",
|
||||
|
||||
"../services/types.ts",
|
||||
|
||||
@@ -0,0 +1,425 @@
|
||||
namespace ts {
|
||||
let currentTime = 100;
|
||||
let lastDiagnostics: Diagnostic[] = [];
|
||||
const reportDiagnostic: DiagnosticReporter = diagnostic => lastDiagnostics.push(diagnostic);
|
||||
const report = (message: DiagnosticMessage, ...args: string[]) => reportDiagnostic(createCompilerDiagnostic(message, ...args));
|
||||
const buildHost: BuildHost = {
|
||||
error: report,
|
||||
verbose: report,
|
||||
message: report,
|
||||
errorDiagnostic: d => reportDiagnostic(d)
|
||||
};
|
||||
|
||||
export namespace Sample1 {
|
||||
tick();
|
||||
const projFs = loadProjectFromDisk("../../tests/projects/sample1");
|
||||
|
||||
const allExpectedOutputs = ["/src/tests/index.js",
|
||||
"/src/core/index.js", "/src/core/index.d.ts",
|
||||
"/src/logic/index.js", "/src/logic/index.d.ts"];
|
||||
|
||||
describe("tsbuild - sanity check of clean build of 'sample1' project", () => {
|
||||
it("can build the sample project 'sample1' without error", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.CompilerHost(fs);
|
||||
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: false });
|
||||
|
||||
clearDiagnostics();
|
||||
builder.buildAllProjects();
|
||||
assertDiagnosticMessages(/*empty*/);
|
||||
|
||||
// Check for outputs. Not an exhaustive list
|
||||
for (const output of allExpectedOutputs) {
|
||||
assert(fs.existsSync(output), `Expect file ${output} to exist`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsbuild - dry builds", () => {
|
||||
it("doesn't write any files in a dry build", () => {
|
||||
clearDiagnostics();
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.CompilerHost(fs);
|
||||
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: true, force: false, verbose: false });
|
||||
builder.buildAllProjects();
|
||||
assertDiagnosticMessages(Diagnostics.A_non_dry_build_would_build_project_0, Diagnostics.A_non_dry_build_would_build_project_0, Diagnostics.A_non_dry_build_would_build_project_0);
|
||||
|
||||
// Check for outputs to not be written. Not an exhaustive list
|
||||
for (const output of allExpectedOutputs) {
|
||||
assert(!fs.existsSync(output), `Expect file ${output} to not exist`);
|
||||
}
|
||||
});
|
||||
|
||||
it("indicates that it would skip builds during a dry build", () => {
|
||||
clearDiagnostics();
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.CompilerHost(fs);
|
||||
|
||||
let builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: false });
|
||||
builder.buildAllProjects();
|
||||
tick();
|
||||
|
||||
clearDiagnostics();
|
||||
builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: true, force: false, verbose: false });
|
||||
builder.buildAllProjects();
|
||||
assertDiagnosticMessages(Diagnostics.Project_0_is_up_to_date, Diagnostics.Project_0_is_up_to_date, Diagnostics.Project_0_is_up_to_date);
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsbuild - clean builds", () => {
|
||||
it("removes all files it built", () => {
|
||||
clearDiagnostics();
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.CompilerHost(fs);
|
||||
|
||||
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: false });
|
||||
builder.buildAllProjects();
|
||||
// Verify they exist
|
||||
for (const output of allExpectedOutputs) {
|
||||
assert(fs.existsSync(output), `Expect file ${output} to exist`);
|
||||
}
|
||||
builder.cleanAllProjects();
|
||||
// Verify they are gone
|
||||
for (const output of allExpectedOutputs) {
|
||||
assert(!fs.existsSync(output), `Expect file ${output} to not exist`);
|
||||
}
|
||||
// Subsequent clean shouldn't throw / etc
|
||||
builder.cleanAllProjects();
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsbuild - force builds", () => {
|
||||
it("always builds under --force", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.CompilerHost(fs);
|
||||
|
||||
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: true, verbose: false });
|
||||
builder.buildAllProjects();
|
||||
let currentTime = time();
|
||||
checkOutputTimestamps(currentTime);
|
||||
|
||||
tick();
|
||||
Debug.assert(time() !== currentTime, "Time moves on");
|
||||
currentTime = time();
|
||||
builder.buildAllProjects();
|
||||
checkOutputTimestamps(currentTime);
|
||||
|
||||
function checkOutputTimestamps(expected: number) {
|
||||
// Check timestamps
|
||||
for (const output of allExpectedOutputs) {
|
||||
const actual = fs.statSync(output).mtimeMs;
|
||||
assert(actual === expected, `File ${output} has timestamp ${actual}, expected ${expected}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsbuild - can detect when and what to rebuild", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.CompilerHost(fs);
|
||||
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: true });
|
||||
|
||||
it("Builds the project", () => {
|
||||
clearDiagnostics();
|
||||
builder.resetBuildContext();
|
||||
builder.buildAllProjects();
|
||||
assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
|
||||
Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
|
||||
Diagnostics.Building_project_0,
|
||||
Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
|
||||
Diagnostics.Building_project_0,
|
||||
Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
|
||||
Diagnostics.Building_project_0);
|
||||
tick();
|
||||
});
|
||||
|
||||
// All three projects are up to date
|
||||
it("Detects that all projects are up to date", () => {
|
||||
clearDiagnostics();
|
||||
builder.resetBuildContext();
|
||||
builder.buildAllProjects();
|
||||
assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
|
||||
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
|
||||
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
|
||||
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2);
|
||||
tick();
|
||||
});
|
||||
|
||||
// Update a file in the leaf node (tests), only it should rebuild the last one
|
||||
it("Only builds the leaf node project", () => {
|
||||
clearDiagnostics();
|
||||
fs.writeFileSync("/src/tests/index.ts", "const m = 10;");
|
||||
builder.resetBuildContext();
|
||||
builder.buildAllProjects();
|
||||
|
||||
assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
|
||||
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
|
||||
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
|
||||
Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2,
|
||||
Diagnostics.Building_project_0);
|
||||
tick();
|
||||
});
|
||||
|
||||
// Update a file in the parent (without affecting types), should get fast downstream builds
|
||||
it("Detects type-only changes in upstream projects", () => {
|
||||
clearDiagnostics();
|
||||
replaceText(fs, "/src/core/index.ts", "HELLO WORLD", "WELCOME PLANET");
|
||||
builder.resetBuildContext();
|
||||
builder.buildAllProjects();
|
||||
|
||||
assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
|
||||
Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2,
|
||||
Diagnostics.Building_project_0,
|
||||
Diagnostics.Project_0_is_up_to_date_with_d_ts_files_from_its_dependencies,
|
||||
Diagnostics.Updating_output_timestamps_of_project_0,
|
||||
Diagnostics.Project_0_is_up_to_date_with_d_ts_files_from_its_dependencies,
|
||||
Diagnostics.Updating_output_timestamps_of_project_0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsbuild - downstream-blocked compilations", () => {
|
||||
it("won't build downstream projects if upstream projects have errors", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.CompilerHost(fs);
|
||||
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: true });
|
||||
|
||||
clearDiagnostics();
|
||||
|
||||
// Induce an error in the middle project
|
||||
replaceText(fs, "/src/logic/index.ts", "c.multiply(10, 15)", `c.muitply()`);
|
||||
builder.buildAllProjects();
|
||||
assertDiagnosticMessages(
|
||||
Diagnostics.Projects_in_this_build_Colon_0,
|
||||
Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
|
||||
Diagnostics.Building_project_0,
|
||||
Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
|
||||
Diagnostics.Building_project_0,
|
||||
Diagnostics.Property_0_does_not_exist_on_type_1,
|
||||
Diagnostics.Project_0_can_t_be_built_because_its_dependency_1_has_errors,
|
||||
Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsbuild - project invalidation", () => {
|
||||
it("invalidates projects correctly", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.CompilerHost(fs);
|
||||
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: false });
|
||||
|
||||
clearDiagnostics();
|
||||
builder.buildAllProjects();
|
||||
assertDiagnosticMessages(/*empty*/);
|
||||
|
||||
// Update a timestamp in the middle project
|
||||
tick();
|
||||
touch(fs, "/src/logic/index.ts");
|
||||
// Because we haven't reset the build context, the builder should assume there's nothing to do right now
|
||||
const status = builder.getUpToDateStatusOfFile(builder.resolveProjectName("/src/logic")!);
|
||||
assert.equal(status.type, UpToDateStatusType.UpToDate, "Project should be assumed to be up-to-date");
|
||||
|
||||
// Rebuild this project
|
||||
tick();
|
||||
builder.invalidateProject("/src/logic");
|
||||
builder.buildInvalidatedProjects();
|
||||
// The file should be updated
|
||||
assert.equal(fs.statSync("/src/logic/index.js").mtimeMs, time(), "JS file should have been rebuilt");
|
||||
assert.isBelow(fs.statSync("/src/tests/index.js").mtimeMs, time(), "Downstream JS file should *not* have been rebuilt");
|
||||
|
||||
// Build downstream projects should update 'tests', but not 'core'
|
||||
tick();
|
||||
builder.buildDependentInvalidatedProjects();
|
||||
assert.equal(fs.statSync("/src/tests/index.js").mtimeMs, time(), "Downstream JS file should have been rebuilt");
|
||||
assert.isBelow(fs.statSync("/src/core/index.js").mtimeMs, time(), "Upstream JS file should not have been rebuilt");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export namespace OutFile {
|
||||
const outFileFs = loadProjectFromDisk("../../tests/projects/outfile-concat");
|
||||
|
||||
describe("tsbuild - baseline sectioned sourcemaps", () => {
|
||||
const fs = outFileFs.shadow();
|
||||
const host = new fakes.CompilerHost(fs);
|
||||
const builder = createSolutionBuilder(host, buildHost, ["/src/third"], { dry: false, force: false, verbose: false });
|
||||
clearDiagnostics();
|
||||
builder.buildAllProjects();
|
||||
assertDiagnosticMessages(/*none*/);
|
||||
|
||||
const files = [
|
||||
"/src/third/thirdjs/output/third-output.js",
|
||||
"/src/third/thirdjs/output/third-output.js.map"
|
||||
];
|
||||
|
||||
for (const file of files) {
|
||||
it(`Generates files matching the baseline - ${file}`, () => {
|
||||
Harness.Baseline.runBaseline(getBaseFileName(file), () => {
|
||||
return fs.readFileSync(file, "utf-8");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
it(`Generates files matching the baseline - file listing for outFile-concat`, () => {
|
||||
Harness.Baseline.runBaseline("outfile-concat-fileListing.txt", () => {
|
||||
return fs.getFileListing();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe("tsbuild - graph-ordering", () => {
|
||||
const fs = new vfs.FileSystem(false);
|
||||
const host = new fakes.CompilerHost(fs);
|
||||
const deps: [string, string][] = [
|
||||
["A", "B"],
|
||||
["B", "C"],
|
||||
["A", "C"],
|
||||
["B", "D"],
|
||||
["C", "D"],
|
||||
["C", "E"],
|
||||
["F", "E"]
|
||||
];
|
||||
|
||||
writeProjects(fs, ["A", "B", "C", "D", "E", "F", "G"], deps);
|
||||
|
||||
it("orders the graph correctly - specify two roots", () => {
|
||||
checkGraphOrdering(["A", "G"], ["A", "B", "C", "D", "E", "G"]);
|
||||
});
|
||||
|
||||
it("orders the graph correctly - multiple parts of the same graph in various orders", () => {
|
||||
checkGraphOrdering(["A"], ["A", "B", "C", "D", "E"]);
|
||||
checkGraphOrdering(["A", "C", "D"], ["A", "B", "C", "D", "E"]);
|
||||
checkGraphOrdering(["D", "C", "A"], ["A", "B", "C", "D", "E"]);
|
||||
});
|
||||
|
||||
it("orders the graph correctly - other orderings", () => {
|
||||
checkGraphOrdering(["F"], ["F", "E"]);
|
||||
checkGraphOrdering(["E"], ["E"]);
|
||||
checkGraphOrdering(["F", "C", "A"], ["A", "B", "C", "D", "E", "F"]);
|
||||
});
|
||||
|
||||
function checkGraphOrdering(rootNames: string[], expectedBuildSet: string[]) {
|
||||
const builder = createSolutionBuilder(host, buildHost, rootNames, { dry: true, force: false, verbose: false });
|
||||
|
||||
const projFileNames = rootNames.map(getProjectFileName);
|
||||
const graph = builder.getBuildGraph(projFileNames);
|
||||
if (graph === undefined) throw new Error("Graph shouldn't be undefined");
|
||||
|
||||
assert.sameMembers(graph.buildQueue, expectedBuildSet.map(getProjectFileName));
|
||||
|
||||
for (const dep of deps) {
|
||||
const child = getProjectFileName(dep[0]);
|
||||
if (graph.buildQueue.indexOf(child) < 0) continue;
|
||||
const parent = getProjectFileName(dep[1]);
|
||||
assert.isAbove(graph.buildQueue.indexOf(child), graph.buildQueue.indexOf(parent), `Expecting child ${child} to be built after parent ${parent}`);
|
||||
}
|
||||
}
|
||||
|
||||
function getProjectFileName(proj: string) {
|
||||
return `/project/${proj}/tsconfig.json` as ResolvedConfigFileName;
|
||||
}
|
||||
|
||||
function writeProjects(fileSystem: vfs.FileSystem, projectNames: string[], deps: [string, string][]): string[] {
|
||||
const projFileNames: string[] = [];
|
||||
for (const dep of deps) {
|
||||
if (projectNames.indexOf(dep[0]) < 0) throw new Error(`Invalid dependency - project ${dep[0]} does not exist`);
|
||||
if (projectNames.indexOf(dep[1]) < 0) throw new Error(`Invalid dependency - project ${dep[1]} does not exist`);
|
||||
}
|
||||
for (const proj of projectNames) {
|
||||
fileSystem.mkdirpSync(`/project/${proj}`);
|
||||
fileSystem.writeFileSync(`/project/${proj}/${proj}.ts`, "export {}");
|
||||
const configFileName = getProjectFileName(proj);
|
||||
const configContent = JSON.stringify({
|
||||
compilerOptions: { composite: true },
|
||||
files: [`./${proj}.ts`],
|
||||
references: deps.filter(d => d[0] === proj).map(d => ({ path: `../${d[1]}` }))
|
||||
}, undefined, 2);
|
||||
fileSystem.writeFileSync(configFileName, configContent);
|
||||
projFileNames.push(configFileName);
|
||||
}
|
||||
return projFileNames;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function replaceText(fs: vfs.FileSystem, path: string, oldText: string, newText: string) {
|
||||
if (!fs.statSync(path).isFile()) {
|
||||
throw new Error(`File ${path} does not exist`);
|
||||
}
|
||||
const old = fs.readFileSync(path, "utf-8");
|
||||
if (old.indexOf(oldText) < 0) {
|
||||
throw new Error(`Text "${oldText}" does not exist in file ${path}`);
|
||||
}
|
||||
const newContent = old.replace(oldText, newText);
|
||||
fs.writeFileSync(path, newContent, "utf-8");
|
||||
}
|
||||
|
||||
function assertDiagnosticMessages(...expected: DiagnosticMessage[]) {
|
||||
const actual = lastDiagnostics.slice();
|
||||
if (actual.length !== expected.length) {
|
||||
assert.fail<any>(actual, expected, `Diagnostic arrays did not match - got\r\n${actual.map(a => " " + a.messageText).join("\r\n")}\r\nexpected\r\n${expected.map(e => " " + e.message).join("\r\n")}`);
|
||||
}
|
||||
for (let i = 0; i < actual.length; i++) {
|
||||
if (actual[i].code !== expected[i].code) {
|
||||
assert.fail(actual[i].messageText, expected[i].message, `Mismatched error code - expected diagnostic ${i} "${actual[i].messageText}" to match ${expected[i].message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clearDiagnostics() {
|
||||
lastDiagnostics = [];
|
||||
}
|
||||
|
||||
export function printDiagnostics(header = "== Diagnostics ==") {
|
||||
const out = createDiagnosticReporter(sys);
|
||||
sys.write(header + "\r\n");
|
||||
for (const d of lastDiagnostics) {
|
||||
out(d);
|
||||
}
|
||||
}
|
||||
|
||||
function tick() {
|
||||
currentTime += 60_000;
|
||||
}
|
||||
|
||||
function time() {
|
||||
return currentTime;
|
||||
}
|
||||
|
||||
function touch(fs: vfs.FileSystem, path: string) {
|
||||
if (!fs.statSync(path).isFile()) {
|
||||
throw new Error(`File ${path} does not exist`);
|
||||
}
|
||||
fs.utimesSync(path, new Date(time()), new Date(time()));
|
||||
}
|
||||
|
||||
function loadProjectFromDisk(root: string): vfs.FileSystem {
|
||||
const fs = new vfs.FileSystem(/*ignoreCase*/ false, { time });
|
||||
const rootPath = resolvePath(__dirname, root);
|
||||
loadFsMirror(fs, rootPath, "/src");
|
||||
fs.mkdirpSync("/lib");
|
||||
const libs = ["es5", "dom", "webworker.importscripts", "scripthost"];
|
||||
for (const lib of libs) {
|
||||
const content = Harness.IO.readFile(combinePaths(Harness.libFolder, `lib.${lib}.d.ts`));
|
||||
if (content === undefined) {
|
||||
throw new Error(`Failed to read lib ${lib}`);
|
||||
}
|
||||
fs.writeFileSync(`/lib/lib.${lib}.d.ts`, content);
|
||||
}
|
||||
fs.writeFileSync("/lib/lib.d.ts", Harness.IO.readFile(combinePaths(Harness.libFolder, "lib.d.ts"))!);
|
||||
fs.meta.set("defaultLibLocation", "/lib");
|
||||
fs.makeReadonly();
|
||||
return fs;
|
||||
}
|
||||
|
||||
function loadFsMirror(vfs: vfs.FileSystem, localRoot: string, virtualRoot: string) {
|
||||
vfs.mkdirpSync(virtualRoot);
|
||||
for (const path of Harness.IO.readDirectory(localRoot)) {
|
||||
const file = getBaseFileName(path);
|
||||
vfs.writeFileSync(virtualRoot + "/" + file, Harness.IO.readFile(localRoot + "/" + file)!);
|
||||
}
|
||||
for (const dir of Harness.IO.getDirectories(localRoot)) {
|
||||
loadFsMirror(vfs, localRoot + "/" + dir, virtualRoot + "/" + dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
-9
@@ -5,6 +5,11 @@ namespace vfs {
|
||||
*/
|
||||
export const builtFolder = "/.ts";
|
||||
|
||||
/**
|
||||
* Posix-style path to additional mountable folders (./tests/projects in this repo)
|
||||
*/
|
||||
export const projectsFolder = "/.projects";
|
||||
|
||||
/**
|
||||
* Posix-style path to additional test libraries
|
||||
*/
|
||||
@@ -348,10 +353,7 @@ namespace vfs {
|
||||
if (!result.node) this._mkdir(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print diagnostic information about the structure of the file system to the console.
|
||||
*/
|
||||
public debugPrint(): void {
|
||||
public getFileListing(): string {
|
||||
let result = "";
|
||||
const printLinks = (dirname: string | undefined, links: collections.SortedMap<string, Inode>) => {
|
||||
const iterator = collections.getIterator(links);
|
||||
@@ -379,7 +381,14 @@ namespace vfs {
|
||||
}
|
||||
};
|
||||
printLinks(/*dirname*/ undefined, this._getRootLinks());
|
||||
console.log(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print diagnostic information about the structure of the file system to the console.
|
||||
*/
|
||||
public debugPrint(): void {
|
||||
console.log(this.getFileListing());
|
||||
}
|
||||
|
||||
// POSIX API (aligns with NodeJS "fs" module API)
|
||||
@@ -404,7 +413,25 @@ namespace vfs {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file status.
|
||||
* Change file access times
|
||||
*
|
||||
* NOTE: do not rename this method as it is intended to align with the same named export of the "fs" module.
|
||||
*/
|
||||
public utimesSync(path: string, atime: Date, mtime: Date) {
|
||||
if (this.isReadonly) throw createIOError("EROFS");
|
||||
if (!isFinite(+atime) || !isFinite(+mtime)) throw createIOError("EINVAL");
|
||||
|
||||
const entry = this._walk(this._resolve(path));
|
||||
if (!entry || !entry.node) {
|
||||
throw createIOError("ENOENT");
|
||||
}
|
||||
entry.node.atimeMs = +atime;
|
||||
entry.node.mtimeMs = +mtime;
|
||||
entry.node.ctimeMs = this.time();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file status. If `path` is a symbolic link, it is dereferenced.
|
||||
*
|
||||
* @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/lstat.html
|
||||
*
|
||||
@@ -414,9 +441,10 @@ namespace vfs {
|
||||
return this._stat(this._walk(this._resolve(path), /*noFollow*/ true));
|
||||
}
|
||||
|
||||
|
||||
private _stat(entry: WalkResult) {
|
||||
const node = entry.node;
|
||||
if (!node) throw createIOError("ENOENT");
|
||||
if (!node) throw createIOError(`ENOENT`, entry.realpath);
|
||||
return new Stats(
|
||||
node.dev,
|
||||
node.ino,
|
||||
@@ -1127,8 +1155,8 @@ namespace vfs {
|
||||
EROFS: "file system is read-only"
|
||||
});
|
||||
|
||||
export function createIOError(code: keyof typeof IOErrorMessages) {
|
||||
const err: NodeJS.ErrnoException = new Error(`${code}: ${IOErrorMessages[code]}`);
|
||||
export function createIOError(code: keyof typeof IOErrorMessages, details = "") {
|
||||
const err: NodeJS.ErrnoException = new Error(`${code}: ${IOErrorMessages[code]} ${details}`);
|
||||
err.code = code;
|
||||
if (Error.captureStackTrace) Error.captureStackTrace(err, createIOError);
|
||||
return err;
|
||||
@@ -1282,6 +1310,7 @@ namespace vfs {
|
||||
files: {
|
||||
[builtFolder]: new Mount(vpath.resolve(host.getWorkspaceRoot(), "built/local"), resolver),
|
||||
[testLibFolder]: new Mount(vpath.resolve(host.getWorkspaceRoot(), "tests/lib"), resolver),
|
||||
[projectsFolder]: new Mount(vpath.resolve(host.getWorkspaceRoot(), "tests/projects"), resolver),
|
||||
[srcFolder]: {}
|
||||
},
|
||||
cwd: srcFolder,
|
||||
|
||||
@@ -635,8 +635,8 @@ namespace ts.server {
|
||||
return this.rootFiles;
|
||||
}
|
||||
return map(this.program.getSourceFiles(), sourceFile => {
|
||||
const scriptInfo = this.projectService.getScriptInfoForPath(sourceFile.path);
|
||||
Debug.assert(!!scriptInfo, "getScriptInfo", () => `scriptInfo for a file '${sourceFile.fileName}' Path: '${sourceFile.path}' is missing.`);
|
||||
const scriptInfo = this.projectService.getScriptInfoForPath(sourceFile.resolvedPath || sourceFile.path);
|
||||
Debug.assert(!!scriptInfo, "getScriptInfo", () => `scriptInfo for a file '${sourceFile.fileName}' Path: '${sourceFile.path}' / '${sourceFile.resolvedPath}' is missing.`);
|
||||
return scriptInfo!;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -540,6 +540,7 @@ namespace ts {
|
||||
public _declarationBrand: any;
|
||||
public fileName: string;
|
||||
public path: Path;
|
||||
public resolvedPath: Path;
|
||||
public text: string;
|
||||
public scriptSnapshot: IScriptSnapshot;
|
||||
public lineMap: ReadonlyArray<number>;
|
||||
|
||||
+15
-2
@@ -1674,11 +1674,14 @@ declare namespace ts {
|
||||
interface InputFiles extends Node {
|
||||
kind: SyntaxKind.InputFiles;
|
||||
javascriptText: string;
|
||||
javascriptMapText?: string;
|
||||
declarationText: string;
|
||||
declarationMapText?: string;
|
||||
}
|
||||
interface UnparsedSource extends Node {
|
||||
kind: SyntaxKind.UnparsedSource;
|
||||
text: string;
|
||||
sourceMapText?: string;
|
||||
}
|
||||
interface JsonSourceFile extends SourceFile {
|
||||
statements: NodeArray<JsonObjectExpressionStatement>;
|
||||
@@ -2661,6 +2664,9 @@ declare namespace ts {
|
||||
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
|
||||
getEnvironmentVariable?(name: string): string | undefined;
|
||||
createHash?(data: string): string;
|
||||
getModifiedTime?(fileName: string): Date;
|
||||
setModifiedTime?(fileName: string, date: Date): void;
|
||||
deleteFile?(fileName: string): void;
|
||||
}
|
||||
interface SourceMapRange extends TextRange {
|
||||
source?: SourceMapSource;
|
||||
@@ -3015,6 +3021,8 @@ declare 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;
|
||||
/**
|
||||
* A good implementation is node.js' `crypto.createHash`. (https://nodejs.org/api/crypto.html#crypto_crypto_createhash_algorithm)
|
||||
*/
|
||||
@@ -3380,6 +3388,7 @@ declare namespace ts {
|
||||
function isEnumMember(node: Node): node is EnumMember;
|
||||
function isSourceFile(node: Node): node is SourceFile;
|
||||
function isBundle(node: Node): node is Bundle;
|
||||
function isUnparsedSource(node: Node): node is UnparsedSource;
|
||||
function isJSDocTypeExpression(node: Node): node is JSDocTypeExpression;
|
||||
function isJSDocAllType(node: JSDocAllType): node is JSDocAllType;
|
||||
function isJSDocUnknownType(node: Node): node is JSDocUnknownType;
|
||||
@@ -3831,8 +3840,8 @@ declare namespace ts {
|
||||
function createCommaList(elements: ReadonlyArray<Expression>): CommaListExpression;
|
||||
function updateCommaList(node: CommaListExpression, elements: ReadonlyArray<Expression>): CommaListExpression;
|
||||
function createBundle(sourceFiles: ReadonlyArray<SourceFile>, prepends?: ReadonlyArray<UnparsedSource | InputFiles>): Bundle;
|
||||
function createUnparsedSourceFile(text: string): UnparsedSource;
|
||||
function createInputFiles(javascript: string, declaration: string): InputFiles;
|
||||
function createUnparsedSourceFile(text: string, map?: string): UnparsedSource;
|
||||
function createInputFiles(javascript: string, declaration: string, javascriptMapText?: string, declarationMapText?: string): InputFiles;
|
||||
function updateBundle(node: Bundle, sourceFiles: ReadonlyArray<SourceFile>, prepends?: ReadonlyArray<UnparsedSource>): Bundle;
|
||||
function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray<Statement>): CallExpression;
|
||||
function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray<Statement>, param: ParameterDeclaration, paramValue: Expression): CallExpression;
|
||||
@@ -4053,6 +4062,10 @@ declare namespace ts {
|
||||
* @returns A 'Program' object.
|
||||
*/
|
||||
function createProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): Program;
|
||||
/**
|
||||
* Returns the target config filename of a project reference
|
||||
*/
|
||||
function resolveProjectReferencePath(host: CompilerHost, ref: ProjectReference): string | undefined;
|
||||
}
|
||||
declare namespace ts {
|
||||
interface EmitOutput {
|
||||
|
||||
+15
-2
@@ -1674,11 +1674,14 @@ declare namespace ts {
|
||||
interface InputFiles extends Node {
|
||||
kind: SyntaxKind.InputFiles;
|
||||
javascriptText: string;
|
||||
javascriptMapText?: string;
|
||||
declarationText: string;
|
||||
declarationMapText?: string;
|
||||
}
|
||||
interface UnparsedSource extends Node {
|
||||
kind: SyntaxKind.UnparsedSource;
|
||||
text: string;
|
||||
sourceMapText?: string;
|
||||
}
|
||||
interface JsonSourceFile extends SourceFile {
|
||||
statements: NodeArray<JsonObjectExpressionStatement>;
|
||||
@@ -2661,6 +2664,9 @@ declare namespace ts {
|
||||
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
|
||||
getEnvironmentVariable?(name: string): string | undefined;
|
||||
createHash?(data: string): string;
|
||||
getModifiedTime?(fileName: string): Date;
|
||||
setModifiedTime?(fileName: string, date: Date): void;
|
||||
deleteFile?(fileName: string): void;
|
||||
}
|
||||
interface SourceMapRange extends TextRange {
|
||||
source?: SourceMapSource;
|
||||
@@ -3015,6 +3021,8 @@ declare 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;
|
||||
/**
|
||||
* A good implementation is node.js' `crypto.createHash`. (https://nodejs.org/api/crypto.html#crypto_crypto_createhash_algorithm)
|
||||
*/
|
||||
@@ -3380,6 +3388,7 @@ declare namespace ts {
|
||||
function isEnumMember(node: Node): node is EnumMember;
|
||||
function isSourceFile(node: Node): node is SourceFile;
|
||||
function isBundle(node: Node): node is Bundle;
|
||||
function isUnparsedSource(node: Node): node is UnparsedSource;
|
||||
function isJSDocTypeExpression(node: Node): node is JSDocTypeExpression;
|
||||
function isJSDocAllType(node: JSDocAllType): node is JSDocAllType;
|
||||
function isJSDocUnknownType(node: Node): node is JSDocUnknownType;
|
||||
@@ -3831,8 +3840,8 @@ declare namespace ts {
|
||||
function createCommaList(elements: ReadonlyArray<Expression>): CommaListExpression;
|
||||
function updateCommaList(node: CommaListExpression, elements: ReadonlyArray<Expression>): CommaListExpression;
|
||||
function createBundle(sourceFiles: ReadonlyArray<SourceFile>, prepends?: ReadonlyArray<UnparsedSource | InputFiles>): Bundle;
|
||||
function createUnparsedSourceFile(text: string): UnparsedSource;
|
||||
function createInputFiles(javascript: string, declaration: string): InputFiles;
|
||||
function createUnparsedSourceFile(text: string, map?: string): UnparsedSource;
|
||||
function createInputFiles(javascript: string, declaration: string, javascriptMapText?: string, declarationMapText?: string): InputFiles;
|
||||
function updateBundle(node: Bundle, sourceFiles: ReadonlyArray<SourceFile>, prepends?: ReadonlyArray<UnparsedSource>): Bundle;
|
||||
function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray<Statement>): CallExpression;
|
||||
function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray<Statement>, param: ParameterDeclaration, paramValue: Expression): CallExpression;
|
||||
@@ -4053,6 +4062,10 @@ declare namespace ts {
|
||||
* @returns A 'Program' object.
|
||||
*/
|
||||
function createProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): Program;
|
||||
/**
|
||||
* Returns the target config filename of a project reference
|
||||
*/
|
||||
function resolveProjectReferencePath(host: CompilerHost, ref: ProjectReference): string | undefined;
|
||||
}
|
||||
declare namespace ts {
|
||||
interface EmitOutput {
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
*/
|
||||
/lib/
|
||||
/lib/lib.d.ts
|
||||
/lib/lib.dom.d.ts
|
||||
/lib/lib.es5.d.ts
|
||||
/lib/lib.scripthost.d.ts
|
||||
/lib/lib.webworker.importscripts.d.ts
|
||||
/src/
|
||||
/src/2/
|
||||
/src/2/second-output.d.ts
|
||||
/src/2/second-output.d.ts.map
|
||||
/src/2/second-output.js
|
||||
/src/2/second-output.js.map
|
||||
/src/first/
|
||||
/src/first/bin/
|
||||
/src/first/bin/first-output.d.ts
|
||||
/src/first/bin/first-output.d.ts.map
|
||||
/src/first/bin/first-output.js
|
||||
/src/first/bin/first-output.js.map
|
||||
/src/first/first_part1.ts
|
||||
/src/first/first_part2.ts
|
||||
/src/first/first_part3.ts
|
||||
/src/first/tsconfig.json
|
||||
/src/first_part1.ts
|
||||
/src/first_part2.ts
|
||||
/src/first_part3.ts
|
||||
/src/second/
|
||||
/src/second/second_part1.ts
|
||||
/src/second/second_part2.ts
|
||||
/src/second/tsconfig.json
|
||||
/src/second_part1.ts
|
||||
/src/second_part2.ts
|
||||
/src/third/
|
||||
/src/third/third_part1.ts
|
||||
/src/third/thirdjs/
|
||||
/src/third/thirdjs/output/
|
||||
/src/third/thirdjs/output/third-output.d.ts
|
||||
/src/third/thirdjs/output/third-output.d.ts.map
|
||||
/src/third/thirdjs/output/third-output.js
|
||||
/src/third/thirdjs/output/third-output.js.map
|
||||
/src/third/tsconfig.json
|
||||
/src/third_part1.ts
|
||||
/src/tsconfig.json
|
||||
@@ -0,0 +1,26 @@
|
||||
var s = "Hello, world";
|
||||
console.log(s);
|
||||
console.log(f());
|
||||
function f() {
|
||||
return "JS does hoists";
|
||||
}
|
||||
//# sourceMappingURL=first-output.js.map
|
||||
var N;
|
||||
(function (N) {
|
||||
function f() {
|
||||
console.log('testing');
|
||||
}
|
||||
f();
|
||||
})(N || (N = {}));
|
||||
var C = (function () {
|
||||
function C() {
|
||||
}
|
||||
C.prototype.doSomething = function () {
|
||||
console.log("something got done");
|
||||
};
|
||||
return C;
|
||||
}());
|
||||
//# sourceMappingURL=second-output.js.map
|
||||
var c = new C();
|
||||
c.doSomething();
|
||||
//# sourceMappingURL=third-output.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"third-output.js","sections":[{"offset":{"line":0,"column":0},"map":{"version":3,"file":"first-output.js","sourceRoot":"","sources":["first_part1.ts","first_part2.ts","first_part3.ts"],"names":[],"mappings":"AAIA,IAAM,CAAC,GAAG,cAAc,CAAC;AAMzB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;ACVf,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;ACAjB;IACI,OAAO,gBAAgB,CAAC;AAC5B,CAAC"}},{"offset":{"line":7,"column":0},"map":{"version":3,"file":"second-output.js","sourceRoot":"","sources":["second_part1.ts","second_part2.ts"],"names":[],"mappings":"AAIA,IAAU,CAAC,CAMV;AAND,WAAU,CAAC;IACP;QACI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC3B,CAAC;IAED,CAAC,EAAE,CAAC;AACR,CAAC,EANS,CAAC,KAAD,CAAC,QAMV;ACVD;IAAA;IAIA,CAAC;IAHG,uBAAW,GAAX;QACI,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IACtC,CAAC;IACL,QAAC;AAAD,CAAC,AAJD,IAIC"}},{"offset":{"line":22,"column":41},"map":{"version":3,"file":"third-output.js","sourceRoot":"","sources":["third_part1.ts"],"names":[],"mappings":";AAAA,IAAI,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;AAChB,CAAC,CAAC,WAAW,EAAE,CAAC"}}]}
|
||||
@@ -0,0 +1,26 @@
|
||||
var s = "Hello, world";
|
||||
console.log(s);
|
||||
console.log(f());
|
||||
function f() {
|
||||
return "JS does hoists";
|
||||
}
|
||||
//# sourceMappingURL=first-output.js.map
|
||||
var N;
|
||||
(function (N) {
|
||||
function f() {
|
||||
console.log('testing');
|
||||
}
|
||||
f();
|
||||
})(N || (N = {}));
|
||||
var C = (function () {
|
||||
function C() {
|
||||
}
|
||||
C.prototype.doSomething = function () {
|
||||
console.log("something got done");
|
||||
};
|
||||
return C;
|
||||
}());
|
||||
//# sourceMappingURL=second-output.js.map
|
||||
var c = new C();
|
||||
c.doSomething();
|
||||
//# sourceMappingURL=third-output.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"third-output.js","sections":[{"offset":{"line":0,"column":0},"map":{"version":3,"file":"first-output.js","sourceRoot":"","sources":["../first_part1.ts","../first_part2.ts","../first_part3.ts"],"names":[],"mappings":"AAIA,IAAM,CAAC,GAAG,cAAc,CAAC;AAMzB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;ACVf,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;ACAjB;IACI,OAAO,gBAAgB,CAAC;AAC5B,CAAC"}},{"offset":{"line":7,"column":0},"map":{"version":3,"file":"second-output.js","sourceRoot":"","sources":["../second/second_part1.ts","../second/second_part2.ts"],"names":[],"mappings":"AAIA,IAAU,CAAC,CAMV;AAND,WAAU,CAAC;IACP;QACI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC3B,CAAC;IAED,CAAC,EAAE,CAAC;AACR,CAAC,EANS,CAAC,KAAD,CAAC,QAMV;ACVD;IAAA;IAIA,CAAC;IAHG,uBAAW,GAAX;QACI,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IACtC,CAAC;IACL,QAAC;AAAD,CAAC,AAJD,IAIC"}},{"offset":{"line":22,"column":41},"map":{"version":3,"file":"third-output.js","sourceRoot":"","sources":["../../third_part1.ts"],"names":[],"mappings":";AAAA,IAAI,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;AAChB,CAAC,CAAC,WAAW,EAAE,CAAC"}}]}
|
||||
@@ -14,5 +14,5 @@ function foo() {
|
||||
}
|
||||
// Shouldn't see any errors down here
|
||||
var y = {a} 1 };
|
||||
</>;
|
||||
</>;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ define(["require", "exports", "react"], function (require, exports, React) {
|
||||
// Should be OK
|
||||
var MainMenu = function (props) { return (<div>
|
||||
<h3>Main Menu</h3>
|
||||
</div>); };
|
||||
</div>); };
|
||||
var App = function (_a) {
|
||||
var children = _a.children;
|
||||
return (<div>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
interface TheFirst {
|
||||
none: any;
|
||||
}
|
||||
|
||||
const s = "Hello, world";
|
||||
|
||||
interface NoJsForHereEither {
|
||||
none: any;
|
||||
}
|
||||
|
||||
console.log(s);
|
||||
@@ -0,0 +1 @@
|
||||
console.log(f());
|
||||
@@ -0,0 +1,3 @@
|
||||
function f() {
|
||||
return "JS does hoists";
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"composite": true,
|
||||
"removeComments": true,
|
||||
"strict": false,
|
||||
"sourceMap": true,
|
||||
"declarationMap": true,
|
||||
"declaration": true,
|
||||
"outFile": "./bin/first-output.js"
|
||||
},
|
||||
"references": [
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace N {
|
||||
// Comment text
|
||||
}
|
||||
|
||||
namespace N {
|
||||
function f() {
|
||||
console.log('testing');
|
||||
}
|
||||
|
||||
f();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
class C {
|
||||
doSomething() {
|
||||
console.log("something got done");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"composite": true,
|
||||
"removeComments": true,
|
||||
"strict": false,
|
||||
"sourceMap": true,
|
||||
"declarationMap": true,
|
||||
"declaration": true,
|
||||
"outFile": "../2/second-output.js"
|
||||
},
|
||||
"references": [
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
var c = new C();
|
||||
c.doSomething();
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"composite": true,
|
||||
"removeComments": true,
|
||||
"strict": false,
|
||||
"sourceMap": true,
|
||||
"declarationMap": true,
|
||||
"declaration": true,
|
||||
"outFile": "./thirdjs/output/third-output.js"
|
||||
},
|
||||
"references": [
|
||||
{ "path": "../first", "prepend": true },
|
||||
{ "path": "../second", "prepend": true },
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export const someString: string = "HELLO WORLD";
|
||||
export function leftPad(s: string, n: number) { return s + n; }
|
||||
export function multiply(a: number, b: number) { return a * b; }
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"declaration": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import * as c from '../core/index';
|
||||
export function getSecondsInDay() {
|
||||
return c.multiply(10, 15);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"declaration": true
|
||||
},
|
||||
"references": [
|
||||
{ "path": "../core" }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import * as c from '../core/index';
|
||||
import * as logic from '../logic/index';
|
||||
|
||||
c.leftPad("", 10);
|
||||
logic.getSecondsInDay();
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"references": [
|
||||
{ "path": "../core" },
|
||||
{ "path": "../logic" }
|
||||
],
|
||||
"files": ["index.ts"]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import * as logic from '../logic';
|
||||
|
||||
export function run() {
|
||||
console.log(logic.getSecondsInDay());
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"references": [
|
||||
{ "path": "../logic/index" }
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user