mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' into infer-from-usage/similarity-to-builtins
This commit is contained in:
+18
-8
@@ -137,7 +137,7 @@ namespace ts {
|
||||
*/
|
||||
emittedBuildInfo?: boolean;
|
||||
/**
|
||||
* Already seen affected files
|
||||
* Already seen emitted files
|
||||
*/
|
||||
seenEmittedFiles: Map<true> | undefined;
|
||||
/**
|
||||
@@ -329,7 +329,6 @@ namespace ts {
|
||||
handleDtsMayChangeOfAffectedFile(state, affectedFile, cancellationToken, computeHash);
|
||||
return affectedFile;
|
||||
}
|
||||
seenAffectedFiles.set(affectedFile.path, true);
|
||||
affectedFilesIndex++;
|
||||
}
|
||||
|
||||
@@ -549,7 +548,7 @@ namespace ts {
|
||||
* This is called after completing operation on the next affected file.
|
||||
* The operations here are postponed to ensure that cancellation during the iteration is handled correctly
|
||||
*/
|
||||
function doneWithAffectedFile(state: BuilderProgramState, affected: SourceFile | Program, isPendingEmit?: boolean, isBuildInfoEmit?: boolean) {
|
||||
function doneWithAffectedFile(state: BuilderProgramState, affected: SourceFile | Program, isPendingEmit?: boolean, isBuildInfoEmit?: boolean, isEmitResult?: boolean) {
|
||||
if (isBuildInfoEmit) {
|
||||
state.emittedBuildInfo = true;
|
||||
}
|
||||
@@ -559,6 +558,9 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
state.seenAffectedFiles!.set((affected as SourceFile).path, true);
|
||||
if (isEmitResult) {
|
||||
(state.seenEmittedFiles || (state.seenEmittedFiles = createMap())).set((affected as SourceFile).path, true);
|
||||
}
|
||||
if (isPendingEmit) {
|
||||
state.affectedFilesPendingEmitIndex!++;
|
||||
}
|
||||
@@ -576,6 +578,14 @@ namespace ts {
|
||||
return { result, affected };
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result with affected file
|
||||
*/
|
||||
function toAffectedFileEmitResult(state: BuilderProgramState, result: EmitResult, affected: SourceFile | Program, isPendingEmit?: boolean, isBuildInfoEmit?: boolean): AffectedFileResult<EmitResult> {
|
||||
doneWithAffectedFile(state, affected, isPendingEmit, isBuildInfoEmit, /*isEmitResult*/ true);
|
||||
return { result, affected };
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the semantic diagnostics either from cache if present, or otherwise from program and caches it
|
||||
* Note that it is assumed that the when asked about semantic diagnostics, the file has been taken out of affected files/changed file set
|
||||
@@ -849,7 +859,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
const affected = Debug.assertDefined(state.program);
|
||||
return toAffectedFileResult(
|
||||
return toAffectedFileEmitResult(
|
||||
state,
|
||||
// When whole program is affected, do emit only once (eg when --out or --outFile is specified)
|
||||
// Otherwise just affected file
|
||||
@@ -872,14 +882,14 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
return toAffectedFileResult(
|
||||
return toAffectedFileEmitResult(
|
||||
state,
|
||||
// When whole program is affected, do emit only once (eg when --out or --outFile is specified)
|
||||
// Otherwise just affected file
|
||||
Debug.assertDefined(state.program).emit(affected === state.program ? undefined : affected as SourceFile, writeFile || maybeBind(host, host.writeFile), cancellationToken, emitOnlyDtsFiles, customTransformers),
|
||||
affected,
|
||||
isPendingEmitFile
|
||||
);
|
||||
isPendingEmitFile,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1036,7 +1046,7 @@ namespace ts {
|
||||
compilerOptions: convertFromReusableCompilerOptions(program.options, toAbsolutePath),
|
||||
referencedMap: getMapOfReferencedSet(program.referencedMap, toPath),
|
||||
exportedModulesMap: getMapOfReferencedSet(program.exportedModulesMap, toPath),
|
||||
semanticDiagnosticsPerFile: program.semanticDiagnosticsPerFile && arrayToMap(program.semanticDiagnosticsPerFile, value => isString(value) ? value : value[0], value => isString(value) ? emptyArray : value[1]),
|
||||
semanticDiagnosticsPerFile: program.semanticDiagnosticsPerFile && arrayToMap(program.semanticDiagnosticsPerFile, value => toPath(isString(value) ? value : value[0]), value => isString(value) ? emptyArray : value[1]),
|
||||
hasReusableDiagnostic: true
|
||||
};
|
||||
return {
|
||||
|
||||
@@ -345,8 +345,13 @@ namespace ts.BuilderState {
|
||||
}
|
||||
else {
|
||||
const emitOutput = getFileEmitOutput(programOfThisState, sourceFile, /*emitOnlyDtsFiles*/ true, cancellationToken);
|
||||
if (emitOutput.outputFiles && emitOutput.outputFiles.length > 0) {
|
||||
latestSignature = computeHash(emitOutput.outputFiles[0].text);
|
||||
const firstDts = emitOutput.outputFiles &&
|
||||
programOfThisState.getCompilerOptions().declarationMap ?
|
||||
emitOutput.outputFiles.length > 1 ? emitOutput.outputFiles[1] : undefined :
|
||||
emitOutput.outputFiles.length > 0 ? emitOutput.outputFiles[0] : undefined;
|
||||
if (firstDts) {
|
||||
Debug.assert(fileExtensionIs(firstDts.name, Extension.Dts), "File extension for signature expected to be dts", () => `Found: ${getAnyExtensionFromPath(firstDts.name)} for ${firstDts.name}:: All output files: ${JSON.stringify(emitOutput.outputFiles.map(f => f.name))}`);
|
||||
latestSignature = computeHash(firstDts.text);
|
||||
if (exportedModulesMapCache && latestSignature !== prevSignature) {
|
||||
updateExportedModules(sourceFile, emitOutput.exportedModulesFromDeclarationEmit, exportedModulesMapCache);
|
||||
}
|
||||
|
||||
@@ -3525,7 +3525,7 @@ namespace ts {
|
||||
const sig = nodeBuilder.signatureToSignatureDeclaration(signature, sigOutput, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName);
|
||||
const printer = createPrinter({ removeComments: true, omitTrailingSemicolon: true });
|
||||
const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration);
|
||||
printer.writeNode(EmitHint.Unspecified, sig!, /*sourceFile*/ sourceFile, getTrailingSemicolonOmittingWriter(writer)); // TODO: GH#18217
|
||||
printer.writeNode(EmitHint.Unspecified, sig!, /*sourceFile*/ sourceFile, getTrailingSemicolonDeferringWriter(writer)); // TODO: GH#18217
|
||||
return writer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5120,6 +5120,10 @@
|
||||
"category": "Message",
|
||||
"code": 95089
|
||||
},
|
||||
"Extract to interface": {
|
||||
"category": "Message",
|
||||
"code": 95090
|
||||
},
|
||||
|
||||
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
|
||||
"category": "Error",
|
||||
|
||||
@@ -155,6 +155,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getOutputJSFileName(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean) {
|
||||
if (configFile.options.emitDeclarationOnly) return undefined;
|
||||
const isJsonFile = fileExtensionIs(inputFileName, Extension.Json);
|
||||
const outputFileName = changeExtension(
|
||||
getOutputPathWithoutChangingExt(inputFileName, configFile, ignoreCase, configFile.options.outDir),
|
||||
@@ -187,7 +188,7 @@ namespace ts {
|
||||
const js = getOutputJSFileName(inputFileName, configFile, ignoreCase);
|
||||
addOutput(js);
|
||||
if (fileExtensionIs(inputFileName, Extension.Json)) continue;
|
||||
if (configFile.options.sourceMap) {
|
||||
if (js && configFile.options.sourceMap) {
|
||||
addOutput(`${js}.map`);
|
||||
}
|
||||
if (getEmitDeclarations(configFile.options) && hasTSFileExtension(inputFileName)) {
|
||||
@@ -214,6 +215,10 @@ namespace ts {
|
||||
if (fileExtensionIs(inputFileName, Extension.Dts)) continue;
|
||||
const jsFilePath = getOutputJSFileName(inputFileName, configFile, ignoreCase);
|
||||
if (jsFilePath) return jsFilePath;
|
||||
if (fileExtensionIs(inputFileName, Extension.Json)) continue;
|
||||
if (getEmitDeclarations(configFile.options) && hasTSFileExtension(inputFileName)) {
|
||||
return getOutputDeclarationFileName(inputFileName, configFile, ignoreCase);
|
||||
}
|
||||
}
|
||||
const buildInfoPath = getOutputPathForBuildInfo(configFile.options);
|
||||
if (buildInfoPath) return buildInfoPath;
|
||||
@@ -1053,7 +1058,7 @@ namespace ts {
|
||||
|
||||
function setWriter(_writer: EmitTextWriter | undefined, _sourceMapGenerator: SourceMapGenerator | undefined) {
|
||||
if (_writer && printerOptions.omitTrailingSemicolon) {
|
||||
_writer = getTrailingSemicolonOmittingWriter(_writer);
|
||||
_writer = getTrailingSemicolonDeferringWriter(_writer);
|
||||
}
|
||||
|
||||
writer = _writer!; // TODO: GH#18217
|
||||
@@ -2511,7 +2516,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
emitWhileClause(node, node.statement.end);
|
||||
writePunctuation(";");
|
||||
writeTrailingSemicolon();
|
||||
}
|
||||
|
||||
function emitWhileStatement(node: WhileStatement) {
|
||||
|
||||
@@ -3374,7 +3374,11 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
export function getTrailingSemicolonOmittingWriter(writer: EmitTextWriter): EmitTextWriter {
|
||||
export interface TrailingSemicolonDeferringWriter extends EmitTextWriter {
|
||||
resetPendingTrailingSemicolon(): void;
|
||||
}
|
||||
|
||||
export function getTrailingSemicolonDeferringWriter(writer: EmitTextWriter): TrailingSemicolonDeferringWriter {
|
||||
let pendingTrailingSemicolon = false;
|
||||
|
||||
function commitPendingTrailingSemicolon() {
|
||||
@@ -3440,10 +3444,24 @@ namespace ts {
|
||||
decreaseIndent() {
|
||||
commitPendingTrailingSemicolon();
|
||||
writer.decreaseIndent();
|
||||
},
|
||||
resetPendingTrailingSemicolon() {
|
||||
pendingTrailingSemicolon = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function getTrailingSemicolonOmittingWriter(writer: EmitTextWriter): EmitTextWriter {
|
||||
const deferringWriter = getTrailingSemicolonDeferringWriter(writer);
|
||||
return {
|
||||
...deferringWriter,
|
||||
writeLine() {
|
||||
deferringWriter.resetPendingTrailingSemicolon();
|
||||
writer.writeLine();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function getResolvedExternalModuleName(host: EmitHost, file: SourceFile, referenceFile?: SourceFile): string {
|
||||
return file.moduleName || getExternalModuleNameFromPath(host, file.fileName, referenceFile && referenceFile.fileName);
|
||||
}
|
||||
|
||||
@@ -545,6 +545,10 @@ ${indentText}${text}`;
|
||||
super.writeFile(fileName, ts.getBuildInfoText(buildInfo), writeByteOrderMark);
|
||||
}
|
||||
|
||||
createHash(data: string) {
|
||||
return `${ts.generateDjb2Hash(data)}-${data}`;
|
||||
}
|
||||
|
||||
now() {
|
||||
return new Date(this.sys.vfs.time());
|
||||
}
|
||||
@@ -571,6 +575,15 @@ Actual: ${JSON.stringify(actual, /*replacer*/ undefined, " ")}
|
||||
Expected: ${JSON.stringify(expected, /*replacer*/ undefined, " ")}`);
|
||||
}
|
||||
|
||||
assertErrors(...expectedDiagnostics: ExpectedErrorDiagnostic[]) {
|
||||
const actual = this.diagnostics.filter(d => d.kind === DiagnosticKind.Error).map(diagnosticToText);
|
||||
const expected = expectedDiagnostics.map(expectedDiagnosticToText);
|
||||
assert.deepEqual(actual, expected, `Diagnostics arrays did not match:
|
||||
Actual: ${JSON.stringify(actual, /*replacer*/ undefined, " ")}
|
||||
Expected: ${JSON.stringify(expected, /*replacer*/ undefined, " ")}
|
||||
Actual All:: ${JSON.stringify(this.diagnostics.slice().map(diagnosticToText), /*replacer*/ undefined, " ")}`);
|
||||
}
|
||||
|
||||
printDiagnostics(header = "== Diagnostics ==") {
|
||||
const out = ts.createDiagnosticReporter(ts.sys);
|
||||
ts.sys.write(header + "\r\n");
|
||||
|
||||
@@ -8197,7 +8197,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[The type argument for type parameter '{0}' cannot be inferred from the usage. Consider specifying the type arguments explicitly.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[형식 매개 변수 '{0}'의 형식 인수를 유추할 수 없습니다. 형식 인수를 명시적으로 지정하세요.]]></Val>
|
||||
<Val><![CDATA[사용량에서 형식 매개 변수 '{0}'의 형식 인수를 유추할 수 없습니다. 형식 인수를 명시적으로 지정하세요.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
|
||||
@@ -539,10 +539,14 @@ namespace ts.formatting {
|
||||
return true;
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
case SyntaxKind.PropertyAssignment:
|
||||
case SyntaxKind.BinaryExpression:
|
||||
if (!settings.indentMultiLineObjectLiteralBeginningOnBlankLine && sourceFile && childKind === SyntaxKind.ObjectLiteralExpression) { // TODO: GH#18217
|
||||
return rangeIsOnOneLine(sourceFile, child!);
|
||||
}
|
||||
return true;
|
||||
if (parent.kind !== SyntaxKind.BinaryExpression) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.DoStatement:
|
||||
case SyntaxKind.WhileStatement:
|
||||
case SyntaxKind.ForInStatement:
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace ts.GoToDefinition {
|
||||
return [sigInfo];
|
||||
}
|
||||
else {
|
||||
const defs = getDefinitionFromSymbol(typeChecker, symbol, node) || emptyArray;
|
||||
const defs = getDefinitionFromSymbol(typeChecker, symbol, node, calledDeclaration) || emptyArray;
|
||||
// For a 'super()' call, put the signature first, else put the variable first.
|
||||
return node.kind === SyntaxKind.SuperKeyword ? [sigInfo, ...defs] : [...defs, sigInfo];
|
||||
}
|
||||
@@ -232,10 +232,11 @@ namespace ts.GoToDefinition {
|
||||
}
|
||||
}
|
||||
|
||||
function getDefinitionFromSymbol(typeChecker: TypeChecker, symbol: Symbol, node: Node): DefinitionInfo[] | undefined {
|
||||
function getDefinitionFromSymbol(typeChecker: TypeChecker, symbol: Symbol, node: Node, declarationNode?: Node): DefinitionInfo[] | undefined {
|
||||
// There are cases when you extend a function by adding properties to it afterwards,
|
||||
// we want to strip those extra properties
|
||||
const filteredDeclarations = filter(symbol.declarations, d => !isAssignmentDeclaration(d) || d === symbol.valueDeclaration) || undefined;
|
||||
// we want to strip those extra properties.
|
||||
// For deduping purposes, we also want to exclude any declarationNodes if provided.
|
||||
const filteredDeclarations = filter(symbol.declarations, d => d !== declarationNode && (!isAssignmentDeclaration(d) || d === symbol.valueDeclaration)) || undefined;
|
||||
return getConstructSignatureDefinition() || getCallSignatureDefinition() || map(filteredDeclarations, declaration => createDefinitionInfo(declaration, typeChecker, symbol, node));
|
||||
|
||||
function getConstructSignatureDefinition(): DefinitionInfo[] | undefined {
|
||||
@@ -258,8 +259,13 @@ namespace ts.GoToDefinition {
|
||||
return undefined;
|
||||
}
|
||||
const declarations = signatureDeclarations.filter(selectConstructors ? isConstructorDeclaration : isFunctionLike);
|
||||
const declarationsWithBody = declarations.filter(d => !!(<FunctionLikeDeclaration>d).body);
|
||||
|
||||
// declarations defined on the global scope can be defined on multiple files. Get all of them.
|
||||
return declarations.length
|
||||
? [createDefinitionInfo(find(declarations, d => !!(<FunctionLikeDeclaration>d).body) || last(declarations), typeChecker, symbol, node)]
|
||||
? declarationsWithBody.length !== 0
|
||||
? declarationsWithBody.map(x => createDefinitionInfo(x, typeChecker, symbol, node))
|
||||
: [createDefinitionInfo(last(declarations), typeChecker, symbol, node)]
|
||||
: undefined;
|
||||
}
|
||||
}
|
||||
|
||||
+237
-19
@@ -33,6 +33,9 @@ namespace ts.NavigationBar {
|
||||
let parentsStack: NavigationBarNode[] = [];
|
||||
let parent: NavigationBarNode;
|
||||
|
||||
const trackedEs5ClassesStack: (Map<boolean> | undefined)[] = [];
|
||||
let trackedEs5Classes: Map<boolean> | undefined;
|
||||
|
||||
// NavigationBarItem requires an array, but will not mutate it, so just give it this for performance.
|
||||
let emptyChildItemArray: NavigationBarItem[] = [];
|
||||
|
||||
@@ -112,10 +115,10 @@ namespace ts.NavigationBar {
|
||||
pushChild(parent, emptyNavigationBarNode(node));
|
||||
}
|
||||
|
||||
function emptyNavigationBarNode(node: Node): NavigationBarNode {
|
||||
function emptyNavigationBarNode(node: Node, name?: DeclarationName): NavigationBarNode {
|
||||
return {
|
||||
node,
|
||||
name: isDeclaration(node) || isExpression(node) ? getNameOfDeclaration(node) : undefined,
|
||||
name: name || (isDeclaration(node) || isExpression(node) ? getNameOfDeclaration(node) : undefined),
|
||||
additionalNodes: undefined,
|
||||
parent,
|
||||
children: undefined,
|
||||
@@ -123,16 +126,42 @@ namespace ts.NavigationBar {
|
||||
};
|
||||
}
|
||||
|
||||
function addTrackedEs5Class(name: string) {
|
||||
if (!trackedEs5Classes) {
|
||||
trackedEs5Classes = createMap();
|
||||
}
|
||||
trackedEs5Classes.set(name, true);
|
||||
}
|
||||
function endNestedNodes(depth: number): void {
|
||||
for (let i = 0; i < depth; i++) endNode();
|
||||
}
|
||||
function startNestedNodes(targetNode: Node, entityName: EntityNameExpression) {
|
||||
const names: Identifier[] = [];
|
||||
while (!isIdentifier(entityName)) {
|
||||
const name = entityName.name;
|
||||
entityName = entityName.expression;
|
||||
if (name.escapedText === "prototype") continue;
|
||||
names.push(name);
|
||||
}
|
||||
names.push(entityName);
|
||||
for (let i = names.length - 1; i > 0; i--) {
|
||||
const name = names[i];
|
||||
startNode(targetNode, name);
|
||||
}
|
||||
return [names.length - 1, names[0]] as const;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new level of NavigationBarNodes.
|
||||
* This pushes to the stack, so you must call `endNode` when you are done adding to this node.
|
||||
*/
|
||||
function startNode(node: Node): void {
|
||||
const navNode: NavigationBarNode = emptyNavigationBarNode(node);
|
||||
function startNode(node: Node, name?: DeclarationName): void {
|
||||
const navNode: NavigationBarNode = emptyNavigationBarNode(node, name);
|
||||
pushChild(parent, navNode);
|
||||
|
||||
// Save the old parent
|
||||
parentsStack.push(parent);
|
||||
trackedEs5ClassesStack.push(trackedEs5Classes);
|
||||
parent = navNode;
|
||||
}
|
||||
|
||||
@@ -143,10 +172,11 @@ namespace ts.NavigationBar {
|
||||
sortChildren(parent.children);
|
||||
}
|
||||
parent = parentsStack.pop()!;
|
||||
trackedEs5Classes = trackedEs5ClassesStack.pop();
|
||||
}
|
||||
|
||||
function addNodeWithRecursiveChild(node: Node, child: Node | undefined): void {
|
||||
startNode(node);
|
||||
function addNodeWithRecursiveChild(node: Node, child: Node | undefined, name?: DeclarationName): void {
|
||||
startNode(node, name);
|
||||
addChildrenRecursively(child);
|
||||
endNode();
|
||||
}
|
||||
@@ -236,8 +266,15 @@ namespace ts.NavigationBar {
|
||||
}
|
||||
break;
|
||||
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
const nameNode = (<FunctionLikeDeclaration>node).name;
|
||||
// If we see a function declaration track as a possible ES5 class
|
||||
if (nameNode && isIdentifier(nameNode)) {
|
||||
addTrackedEs5Class(nameNode.text);
|
||||
}
|
||||
addNodeWithRecursiveChild(node, (<FunctionLikeDeclaration>node).body);
|
||||
break;
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
addNodeWithRecursiveChild(node, (<FunctionLikeDeclaration>node).body);
|
||||
break;
|
||||
@@ -275,21 +312,94 @@ namespace ts.NavigationBar {
|
||||
addLeafNode(node);
|
||||
break;
|
||||
|
||||
case SyntaxKind.CallExpression:
|
||||
case SyntaxKind.BinaryExpression: {
|
||||
const special = getAssignmentDeclarationKind(node as BinaryExpression);
|
||||
switch (special) {
|
||||
case AssignmentDeclarationKind.ExportsProperty:
|
||||
case AssignmentDeclarationKind.ModuleExports:
|
||||
case AssignmentDeclarationKind.PrototypeProperty:
|
||||
case AssignmentDeclarationKind.Prototype:
|
||||
addNodeWithRecursiveChild(node, (node as BinaryExpression).right);
|
||||
return;
|
||||
case AssignmentDeclarationKind.ThisProperty:
|
||||
case AssignmentDeclarationKind.Property:
|
||||
case AssignmentDeclarationKind.None:
|
||||
case AssignmentDeclarationKind.Prototype:
|
||||
case AssignmentDeclarationKind.PrototypeProperty: {
|
||||
const binaryExpression = (node as BinaryExpression);
|
||||
const assignmentTarget = binaryExpression.left as PropertyAccessExpression;
|
||||
|
||||
const prototypeAccess = special === AssignmentDeclarationKind.PrototypeProperty ?
|
||||
assignmentTarget.expression as PropertyAccessExpression :
|
||||
assignmentTarget;
|
||||
|
||||
let depth = 0;
|
||||
let className: Identifier;
|
||||
// If we see a prototype assignment, start tracking the target as a class
|
||||
// This is only done for simple classes not nested assignments.
|
||||
if (isIdentifier(prototypeAccess.expression)) {
|
||||
addTrackedEs5Class(prototypeAccess.expression.text);
|
||||
className = prototypeAccess.expression;
|
||||
}
|
||||
else {
|
||||
[depth, className] = startNestedNodes(binaryExpression, prototypeAccess.expression as EntityNameExpression);
|
||||
}
|
||||
if (special === AssignmentDeclarationKind.Prototype) {
|
||||
if (isObjectLiteralExpression(binaryExpression.right)) {
|
||||
if (binaryExpression.right.properties.length > 0) {
|
||||
startNode(binaryExpression, className);
|
||||
forEachChild(binaryExpression.right, addChildrenRecursively);
|
||||
endNode();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (isFunctionExpression(binaryExpression.right) || isArrowFunction(binaryExpression.right)) {
|
||||
addNodeWithRecursiveChild(node,
|
||||
binaryExpression.right,
|
||||
className);
|
||||
}
|
||||
else {
|
||||
startNode(binaryExpression, className);
|
||||
addNodeWithRecursiveChild(node, binaryExpression.right, assignmentTarget.name);
|
||||
endNode();
|
||||
}
|
||||
endNestedNodes(depth);
|
||||
return;
|
||||
}
|
||||
case AssignmentDeclarationKind.ObjectDefinePropertyValue:
|
||||
case AssignmentDeclarationKind.ObjectDefinePrototypeProperty: {
|
||||
const defineCall = node as BindableObjectDefinePropertyCall;
|
||||
const className = special === AssignmentDeclarationKind.ObjectDefinePropertyValue ?
|
||||
defineCall.arguments[0] :
|
||||
(defineCall.arguments[0] as PropertyAccessExpression).expression as EntityNameExpression;
|
||||
|
||||
const memberName = defineCall.arguments[1];
|
||||
const [depth, classNameIdentifier] = startNestedNodes(node, className);
|
||||
startNode(node, classNameIdentifier);
|
||||
startNode(node, setTextRange(createIdentifier(memberName.text), memberName));
|
||||
addChildrenRecursively((node as CallExpression).arguments[2]);
|
||||
endNode();
|
||||
endNode();
|
||||
endNestedNodes(depth);
|
||||
return;
|
||||
}
|
||||
case AssignmentDeclarationKind.Property: {
|
||||
const binaryExpression = (node as BinaryExpression);
|
||||
const assignmentTarget = binaryExpression.left as PropertyAccessExpression;
|
||||
const targetFunction = assignmentTarget.expression;
|
||||
if (isIdentifier(targetFunction) && assignmentTarget.name.escapedText !== "prototype" &&
|
||||
trackedEs5Classes && trackedEs5Classes.has(targetFunction.text)) {
|
||||
if (isFunctionExpression(binaryExpression.right) || isArrowFunction(binaryExpression.right)) {
|
||||
addNodeWithRecursiveChild(node, binaryExpression.right, targetFunction);
|
||||
}
|
||||
else {
|
||||
startNode(binaryExpression, targetFunction);
|
||||
addNodeWithRecursiveChild(binaryExpression.left, binaryExpression.right, assignmentTarget.name);
|
||||
endNode();
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AssignmentDeclarationKind.ThisProperty:
|
||||
case AssignmentDeclarationKind.None:
|
||||
case AssignmentDeclarationKind.ObjectDefinePropertyExports:
|
||||
case AssignmentDeclarationKind.ObjectDefinePrototypeProperty:
|
||||
break;
|
||||
default:
|
||||
Debug.assertNever(special);
|
||||
@@ -315,8 +425,8 @@ namespace ts.NavigationBar {
|
||||
/** Merge declarations of the same kind. */
|
||||
function mergeChildren(children: NavigationBarNode[], node: NavigationBarNode): void {
|
||||
const nameToItems = createMap<NavigationBarNode | NavigationBarNode[]>();
|
||||
filterMutate(children, child => {
|
||||
const declName = getNameOfDeclaration(<Declaration>child.node);
|
||||
filterMutate(children, (child, index) => {
|
||||
const declName = child.name || getNameOfDeclaration(<Declaration>child.node);
|
||||
const name = declName && nodeText(declName);
|
||||
if (!name) {
|
||||
// Anonymous items are never merged.
|
||||
@@ -331,7 +441,7 @@ namespace ts.NavigationBar {
|
||||
|
||||
if (itemsWithSameName instanceof Array) {
|
||||
for (const itemWithSameName of itemsWithSameName) {
|
||||
if (tryMerge(itemWithSameName, child, node)) {
|
||||
if (tryMerge(itemWithSameName, child, index, node)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -340,7 +450,7 @@ namespace ts.NavigationBar {
|
||||
}
|
||||
else {
|
||||
const itemWithSameName = itemsWithSameName;
|
||||
if (tryMerge(itemWithSameName, child, node)) {
|
||||
if (tryMerge(itemWithSameName, child, index, node)) {
|
||||
return false;
|
||||
}
|
||||
nameToItems.set(name, [itemWithSameName, child]);
|
||||
@@ -348,8 +458,116 @@ namespace ts.NavigationBar {
|
||||
}
|
||||
});
|
||||
}
|
||||
const isEs5ClassMember: Record<AssignmentDeclarationKind, boolean> = {
|
||||
[AssignmentDeclarationKind.Property]: true,
|
||||
[AssignmentDeclarationKind.PrototypeProperty]: true,
|
||||
[AssignmentDeclarationKind.ObjectDefinePropertyValue]: true,
|
||||
[AssignmentDeclarationKind.ObjectDefinePrototypeProperty]: true,
|
||||
[AssignmentDeclarationKind.None]: false,
|
||||
[AssignmentDeclarationKind.ExportsProperty]: false,
|
||||
[AssignmentDeclarationKind.ModuleExports]: false,
|
||||
[AssignmentDeclarationKind.ObjectDefinePropertyExports]: false,
|
||||
[AssignmentDeclarationKind.Prototype]: true,
|
||||
[AssignmentDeclarationKind.ThisProperty]: false,
|
||||
};
|
||||
function tryMergeEs5Class(a: NavigationBarNode, b: NavigationBarNode, bIndex: number, parent: NavigationBarNode): boolean | undefined {
|
||||
|
||||
function tryMerge(a: NavigationBarNode, b: NavigationBarNode, parent: NavigationBarNode): boolean {
|
||||
function isPossibleConstructor(node: Node) {
|
||||
return isFunctionExpression(node) || isFunctionDeclaration(node) || isVariableDeclaration(node);
|
||||
}
|
||||
const bAssignmentDeclarationKind = isBinaryExpression(b.node) || isCallExpression(b.node) ?
|
||||
getAssignmentDeclarationKind(b.node) :
|
||||
AssignmentDeclarationKind.None;
|
||||
|
||||
const aAssignmentDeclarationKind = isBinaryExpression(a.node) || isCallExpression(a.node) ?
|
||||
getAssignmentDeclarationKind(a.node) :
|
||||
AssignmentDeclarationKind.None;
|
||||
|
||||
// We treat this as an es5 class and merge the nodes in in one of several cases
|
||||
if ((isEs5ClassMember[bAssignmentDeclarationKind] && isEs5ClassMember[aAssignmentDeclarationKind]) // merge two class elements
|
||||
|| (isPossibleConstructor(a.node) && isEs5ClassMember[bAssignmentDeclarationKind]) // ctor function & member
|
||||
|| (isPossibleConstructor(b.node) && isEs5ClassMember[aAssignmentDeclarationKind]) // member & ctor function
|
||||
|| (isClassDeclaration(a.node) && isEs5ClassMember[bAssignmentDeclarationKind]) // class (generated) & member
|
||||
|| (isClassDeclaration(b.node) && isEs5ClassMember[aAssignmentDeclarationKind]) // member & class (generated)
|
||||
|| (isClassDeclaration(a.node) && isPossibleConstructor(b.node)) // class (generated) & ctor
|
||||
|| (isClassDeclaration(b.node) && isPossibleConstructor(a.node)) // ctor & class (generated)
|
||||
) {
|
||||
|
||||
let lastANode = a.additionalNodes && lastOrUndefined(a.additionalNodes) || a.node;
|
||||
|
||||
if ((!isClassDeclaration(a.node) && !isClassDeclaration(b.node)) // If neither outline node is a class
|
||||
|| isPossibleConstructor(a.node) || isPossibleConstructor(b.node) // If either function is a constructor function
|
||||
) {
|
||||
const ctorFunction = isPossibleConstructor(a.node) ? a.node :
|
||||
isPossibleConstructor(b.node) ? b.node :
|
||||
undefined;
|
||||
|
||||
if (ctorFunction !== undefined) {
|
||||
const ctorNode = setTextRange(
|
||||
createConstructor(/* decorators */ undefined, /* modifiers */ undefined, [], /* body */ undefined),
|
||||
ctorFunction);
|
||||
const ctor = emptyNavigationBarNode(ctorNode);
|
||||
ctor.indent = a.indent + 1;
|
||||
ctor.children = a.node === ctorFunction ? a.children : b.children;
|
||||
a.children = a.node === ctorFunction ? concatenate([ctor], b.children || [b]) : concatenate(a.children || [a], [ctor]);
|
||||
}
|
||||
else {
|
||||
if (a.children || b.children) {
|
||||
a.children = concatenate(a.children || [a], b.children || [b]);
|
||||
if (a.children) {
|
||||
mergeChildren(a.children, a);
|
||||
sortChildren(a.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastANode = a.node = setTextRange(createClassDeclaration(
|
||||
/* decorators */ undefined,
|
||||
/* modifiers */ undefined,
|
||||
a.name as Identifier || createIdentifier("__class__"),
|
||||
/* typeParameters */ undefined,
|
||||
/* heritageClauses */ undefined,
|
||||
[]
|
||||
), a.node);
|
||||
}
|
||||
else {
|
||||
a.children = concatenate(a.children, b.children);
|
||||
if (a.children) {
|
||||
mergeChildren(a.children, a);
|
||||
}
|
||||
}
|
||||
|
||||
const bNode = b.node;
|
||||
// We merge if the outline node previous to b (bIndex - 1) is already part of the current class
|
||||
// We do this so that statements between class members that do not generate outline nodes do not split up the class outline:
|
||||
// Ex This should produce one outline node C:
|
||||
// function C() {}; a = 1; C.prototype.m = function () {}
|
||||
// Ex This will produce 3 outline nodes: C, a, C
|
||||
// function C() {}; let a = 1; C.prototype.m = function () {}
|
||||
if (parent.children![bIndex - 1].node.end === lastANode.end) {
|
||||
setTextRange(lastANode, { pos: lastANode.pos, end: bNode.end });
|
||||
}
|
||||
else {
|
||||
if (!a.additionalNodes) a.additionalNodes = [];
|
||||
a.additionalNodes.push(setTextRange(createClassDeclaration(
|
||||
/* decorators */ undefined,
|
||||
/* modifiers */ undefined,
|
||||
a.name as Identifier || createIdentifier("__class__"),
|
||||
/* typeParameters */ undefined,
|
||||
/* heritageClauses */ undefined,
|
||||
[]
|
||||
), b.node));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return bAssignmentDeclarationKind === AssignmentDeclarationKind.None ? false : true;
|
||||
}
|
||||
|
||||
function tryMerge(a: NavigationBarNode, b: NavigationBarNode, bIndex: number, parent: NavigationBarNode): boolean {
|
||||
// const v = false as boolean;
|
||||
if (tryMergeEs5Class(a, b, bIndex, parent)) {
|
||||
return true;
|
||||
}
|
||||
if (shouldReallyMerge(a.node, b.node, parent)) {
|
||||
merge(a, b);
|
||||
return true;
|
||||
@@ -444,7 +662,7 @@ namespace ts.NavigationBar {
|
||||
}
|
||||
|
||||
if (name) {
|
||||
const text = nodeText(name);
|
||||
const text = isIdentifier(name) ? name.text : nodeText(name);
|
||||
if (text.length > 0) {
|
||||
return cleanText(text);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
namespace ts.refactor {
|
||||
const refactorName = "Extract type";
|
||||
const extractToTypeAlias = "Extract to type alias";
|
||||
const extractToInterface = "Extract to interface";
|
||||
const extractToTypeDef = "Extract to typedef";
|
||||
registerRefactor(refactorName, {
|
||||
getAvailableActions(context): ReadonlyArray<ApplicableRefactorInfo> {
|
||||
@@ -11,23 +12,35 @@ namespace ts.refactor {
|
||||
return [{
|
||||
name: refactorName,
|
||||
description: getLocaleSpecificMessage(Diagnostics.Extract_type),
|
||||
actions: [info.isJS ? {
|
||||
actions: info.isJS ? [{
|
||||
name: extractToTypeDef, description: getLocaleSpecificMessage(Diagnostics.Extract_to_typedef)
|
||||
} : {
|
||||
name: extractToTypeAlias, description: getLocaleSpecificMessage(Diagnostics.Extract_to_type_alias)
|
||||
}]
|
||||
}] : append([{
|
||||
name: extractToTypeAlias, description: getLocaleSpecificMessage(Diagnostics.Extract_to_type_alias)
|
||||
}], info.typeElements && {
|
||||
name: extractToInterface, description: getLocaleSpecificMessage(Diagnostics.Extract_to_interface)
|
||||
})
|
||||
}];
|
||||
},
|
||||
getEditsForAction(context, actionName): RefactorEditInfo {
|
||||
Debug.assert(actionName === extractToTypeAlias || actionName === extractToTypeDef, "Unexpected action name");
|
||||
const { file } = context;
|
||||
const info = Debug.assertDefined(getRangeToExtract(context), "Expected to find a range to extract");
|
||||
Debug.assert(actionName === extractToTypeAlias && !info.isJS || actionName === extractToTypeDef && info.isJS, "Invalid actionName/JS combo");
|
||||
|
||||
const name = getUniqueName("NewType", file);
|
||||
const edits = textChanges.ChangeTracker.with(context, changes => info.isJS ?
|
||||
doTypedefChange(changes, file, name, info.firstStatement, info.selection, info.typeParameters) :
|
||||
doTypeAliasChange(changes, file, name, info.firstStatement, info.selection, info.typeParameters));
|
||||
const edits = textChanges.ChangeTracker.with(context, changes => {
|
||||
switch (actionName) {
|
||||
case extractToTypeAlias:
|
||||
Debug.assert(!info.isJS, "Invalid actionName/JS combo");
|
||||
return doTypeAliasChange(changes, file, name, info);
|
||||
case extractToTypeDef:
|
||||
Debug.assert(info.isJS, "Invalid actionName/JS combo");
|
||||
return doTypedefChange(changes, file, name, info);
|
||||
case extractToInterface:
|
||||
Debug.assert(!info.isJS && !!info.typeElements, "Invalid actionName/JS combo");
|
||||
return doInterfaceChange(changes, file, name, info as InterfaceInfo);
|
||||
default:
|
||||
Debug.fail("Unexpected action name");
|
||||
}
|
||||
});
|
||||
|
||||
const renameFilename = file.fileName;
|
||||
const renameLocation = getRenameLocation(edits, renameFilename, name, /*preferLastLocation*/ false);
|
||||
@@ -35,7 +48,15 @@ namespace ts.refactor {
|
||||
}
|
||||
});
|
||||
|
||||
interface Info { isJS: boolean; selection: TypeNode; firstStatement: Statement; typeParameters: ReadonlyArray<TypeParameterDeclaration>; }
|
||||
interface TypeAliasInfo {
|
||||
isJS: boolean; selection: TypeNode; firstStatement: Statement; typeParameters: ReadonlyArray<TypeParameterDeclaration>; typeElements?: ReadonlyArray<TypeElement>;
|
||||
}
|
||||
|
||||
interface InterfaceInfo {
|
||||
isJS: boolean; selection: TypeNode; firstStatement: Statement; typeParameters: ReadonlyArray<TypeParameterDeclaration>; typeElements: ReadonlyArray<TypeElement>;
|
||||
}
|
||||
|
||||
type Info = TypeAliasInfo | InterfaceInfo;
|
||||
|
||||
function getRangeToExtract(context: RefactorContext): Info | undefined {
|
||||
const { file, startPosition } = context;
|
||||
@@ -51,7 +72,32 @@ namespace ts.refactor {
|
||||
const typeParameters = collectTypeParameters(checker, selection, firstStatement, file);
|
||||
if (!typeParameters) return undefined;
|
||||
|
||||
return { isJS, selection, firstStatement, typeParameters };
|
||||
const typeElements = flattenTypeLiteralNodeReference(checker, selection);
|
||||
return { isJS, selection, firstStatement, typeParameters, typeElements };
|
||||
}
|
||||
|
||||
function flattenTypeLiteralNodeReference(checker: TypeChecker, node: TypeNode | undefined): ReadonlyArray<TypeElement> | undefined {
|
||||
if (!node) return undefined;
|
||||
if (isIntersectionTypeNode(node)) {
|
||||
const result: TypeElement[] = [];
|
||||
const seen = createMap<true>();
|
||||
for (const type of node.types) {
|
||||
const flattenedTypeMembers = flattenTypeLiteralNodeReference(checker, type);
|
||||
if (!flattenedTypeMembers || !flattenedTypeMembers.every(type => type.name && addToSeen(seen, getNameFromPropertyName(type.name) as string))) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
addRange(result, flattenedTypeMembers);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else if (isParenthesizedTypeNode(node)) {
|
||||
return flattenTypeLiteralNodeReference(checker, node.type);
|
||||
}
|
||||
else if (isTypeLiteralNode(node)) {
|
||||
return node.members;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function isStatementAndHasJSDoc(n: Node): n is (Statement & HasJSDoc) {
|
||||
@@ -107,7 +153,9 @@ namespace ts.refactor {
|
||||
}
|
||||
}
|
||||
|
||||
function doTypeAliasChange(changes: textChanges.ChangeTracker, file: SourceFile, name: string, firstStatement: Statement, selection: TypeNode, typeParameters: ReadonlyArray<TypeParameterDeclaration>) {
|
||||
function doTypeAliasChange(changes: textChanges.ChangeTracker, file: SourceFile, name: string, info: TypeAliasInfo) {
|
||||
const { firstStatement, selection, typeParameters } = info;
|
||||
|
||||
const newTypeNode = createTypeAliasDeclaration(
|
||||
/* decorators */ undefined,
|
||||
/* modifiers */ undefined,
|
||||
@@ -119,7 +167,24 @@ namespace ts.refactor {
|
||||
changes.replaceNode(file, selection, createTypeReferenceNode(name, typeParameters.map(id => createTypeReferenceNode(id.name, /* typeArguments */ undefined))));
|
||||
}
|
||||
|
||||
function doTypedefChange(changes: textChanges.ChangeTracker, file: SourceFile, name: string, firstStatement: Statement, selection: TypeNode, typeParameters: ReadonlyArray<TypeParameterDeclaration>) {
|
||||
function doInterfaceChange(changes: textChanges.ChangeTracker, file: SourceFile, name: string, info: InterfaceInfo) {
|
||||
const { firstStatement, selection, typeParameters, typeElements } = info;
|
||||
|
||||
const newTypeNode = createInterfaceDeclaration(
|
||||
/* decorators */ undefined,
|
||||
/* modifiers */ undefined,
|
||||
name,
|
||||
typeParameters,
|
||||
/* heritageClauses */ undefined,
|
||||
typeElements
|
||||
);
|
||||
changes.insertNodeBefore(file, firstStatement, newTypeNode, /* blankLineBetween */ true);
|
||||
changes.replaceNode(file, selection, createTypeReferenceNode(name, typeParameters.map(id => createTypeReferenceNode(id.name, /* typeArguments */ undefined))));
|
||||
}
|
||||
|
||||
function doTypedefChange(changes: textChanges.ChangeTracker, file: SourceFile, name: string, info: Info) {
|
||||
const { firstStatement, selection, typeParameters } = info;
|
||||
|
||||
const node = <JSDocTypedefTag>createNode(SyntaxKind.JSDocTypedefTag);
|
||||
node.tagName = createIdentifier("typedef"); // TODO: jsdoc factory https://github.com/Microsoft/TypeScript/pull/29539
|
||||
node.fullName = createIdentifier(name);
|
||||
|
||||
@@ -854,7 +854,7 @@ namespace ts.textChanges {
|
||||
const omitTrailingSemicolon = !!sourceFile && !probablyUsesSemicolons(sourceFile);
|
||||
const writer = createWriter(newLineCharacter, omitTrailingSemicolon);
|
||||
const newLine = newLineCharacter === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed;
|
||||
createPrinter({ newLine, neverAsciiEscape: true, omitTrailingSemicolon }, writer).writeNode(EmitHint.Unspecified, node, sourceFile, writer);
|
||||
createPrinter({ newLine, neverAsciiEscape: true }, writer).writeNode(EmitHint.Unspecified, node, sourceFile, writer);
|
||||
return { text: writer.getText(), node: assignPositionsToNode(node) };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
"unittests/tsbuild/amdModulesWithOut.ts",
|
||||
"unittests/tsbuild/containerOnlyReferenced.ts",
|
||||
"unittests/tsbuild/demo.ts",
|
||||
"unittests/tsbuild/emitDeclarationOnly.ts",
|
||||
"unittests/tsbuild/emptyFiles.ts",
|
||||
"unittests/tsbuild/graphOrdering.ts",
|
||||
"unittests/tsbuild/inferredTypeFromTransitiveModule.ts",
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace ts {
|
||||
[outputFiles[project.lib][ext.buildinfo], outputFiles[project.lib][ext.js], outputFiles[project.lib][ext.dts]],
|
||||
[outputFiles[project.app][ext.buildinfo], outputFiles[project.app][ext.js], outputFiles[project.app][ext.dts]]
|
||||
],
|
||||
lastProjectOutputJs: outputFiles[project.app][ext.js],
|
||||
lastProjectOutput: outputFiles[project.app][ext.js],
|
||||
initialBuild: {
|
||||
modifyFs
|
||||
},
|
||||
@@ -231,7 +231,7 @@ ${internal} export enum internalEnum { a, b, c }`);
|
||||
[libOutputFile[ext.buildinfo], libOutputFile[ext.js], libOutputFile[ext.dts]],
|
||||
[outputFiles[project.app][ext.buildinfo], outputFiles[project.app][ext.js], outputFiles[project.app][ext.dts]]
|
||||
],
|
||||
lastProjectOutputJs: outputFiles[project.app][ext.js],
|
||||
lastProjectOutput: outputFiles[project.app][ext.js],
|
||||
initialBuild: {
|
||||
modifyFs,
|
||||
expectedDiagnostics: [
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
namespace ts {
|
||||
describe("unittests:: tsbuild:: on project with emitDeclarationOnly set to true", () => {
|
||||
let projFs: vfs.FileSystem;
|
||||
const { time, tick } = getTime();
|
||||
before(() => {
|
||||
projFs = loadProjectFromDisk("tests/projects/emitDeclarationOnly", time);
|
||||
});
|
||||
after(() => {
|
||||
projFs = undefined!;
|
||||
});
|
||||
|
||||
function verifyEmitDeclarationOnly(disableMap?: true) {
|
||||
verifyTsbuildOutput({
|
||||
scenario: `only dts output in circular import project with emitDeclarationOnly${disableMap ? "" : " and declarationMap"}`,
|
||||
projFs: () => projFs,
|
||||
time,
|
||||
tick,
|
||||
proj: "emitDeclarationOnly",
|
||||
rootNames: ["/src"],
|
||||
lastProjectOutput: `/src/lib/index.d.ts`,
|
||||
outputFiles: [
|
||||
"/src/lib/a.d.ts",
|
||||
"/src/lib/b.d.ts",
|
||||
"/src/lib/c.d.ts",
|
||||
"/src/lib/index.d.ts",
|
||||
"/src/tsconfig.tsbuildinfo",
|
||||
...(disableMap ? emptyArray : [
|
||||
"/src/lib/a.d.ts.map",
|
||||
"/src/lib/b.d.ts.map",
|
||||
"/src/lib/c.d.ts.map",
|
||||
"/src/lib/index.d.ts.map"
|
||||
])
|
||||
],
|
||||
initialBuild: {
|
||||
modifyFs: disableMap ?
|
||||
(fs => replaceText(fs, "/src/tsconfig.json", `"declarationMap": true,`, "")) :
|
||||
noop,
|
||||
expectedDiagnostics: [
|
||||
getExpectedDiagnosticForProjectsInBuild("src/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/tsconfig.json", "src/lib/a.d.ts"],
|
||||
[Diagnostics.Building_project_0, "/src/tsconfig.json"]
|
||||
]
|
||||
},
|
||||
incrementalDtsChangedBuild: {
|
||||
modifyFs: fs => replaceText(fs, "/src/src/a.ts", "b: B;", "b: B; foo: any;"),
|
||||
expectedDiagnostics: [
|
||||
getExpectedDiagnosticForProjectsInBuild("src/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/tsconfig.json", "src/lib/a.d.ts", "src/src/a.ts"],
|
||||
[Diagnostics.Building_project_0, "/src/tsconfig.json"]
|
||||
]
|
||||
},
|
||||
baselineOnly: true,
|
||||
verifyDiagnostics: true
|
||||
});
|
||||
}
|
||||
verifyEmitDeclarationOnly();
|
||||
verifyEmitDeclarationOnly(/*disableMap*/ true);
|
||||
|
||||
verifyTsbuildOutput({
|
||||
scenario: `only dts output in non circular imports project with emitDeclarationOnly`,
|
||||
projFs: () => projFs,
|
||||
time,
|
||||
tick,
|
||||
proj: "emitDeclarationOnly",
|
||||
rootNames: ["/src"],
|
||||
lastProjectOutput: `/src/lib/a.d.ts`,
|
||||
outputFiles: [
|
||||
"/src/lib/a.d.ts",
|
||||
"/src/lib/b.d.ts",
|
||||
"/src/lib/c.d.ts",
|
||||
"/src/tsconfig.tsbuildinfo",
|
||||
"/src/lib/a.d.ts.map",
|
||||
"/src/lib/b.d.ts.map",
|
||||
"/src/lib/c.d.ts.map",
|
||||
],
|
||||
initialBuild: {
|
||||
modifyFs: fs => {
|
||||
fs.rimrafSync("/src/src/index.ts");
|
||||
replaceText(fs, "/src/src/a.ts", `import { B } from "./b";`, `export class B { prop = "hello"; }`);
|
||||
},
|
||||
expectedDiagnostics: [
|
||||
getExpectedDiagnosticForProjectsInBuild("src/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/tsconfig.json", "src/lib/a.d.ts"],
|
||||
[Diagnostics.Building_project_0, "/src/tsconfig.json"]
|
||||
]
|
||||
},
|
||||
incrementalDtsChangedBuild: {
|
||||
modifyFs: fs => replaceText(fs, "/src/src/a.ts", "b: B;", "b: B; foo: any;"),
|
||||
expectedDiagnostics: [
|
||||
getExpectedDiagnosticForProjectsInBuild("src/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/tsconfig.json", "src/lib/a.d.ts", "src/src/a.ts"],
|
||||
[Diagnostics.Building_project_0, "/src/tsconfig.json"]
|
||||
]
|
||||
},
|
||||
incrementalDtsUnchangedBuild: {
|
||||
modifyFs: fs => replaceText(fs, "/src/src/a.ts", "export interface A {", `class C { }
|
||||
export interface A {`),
|
||||
expectedDiagnostics: [
|
||||
getExpectedDiagnosticForProjectsInBuild("src/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/tsconfig.json", "src/lib/a.d.ts", "src/src/a.ts"],
|
||||
[Diagnostics.Building_project_0, "/src/tsconfig.json"],
|
||||
[Diagnostics.Updating_unchanged_output_timestamps_of_project_0, "/src/tsconfig.json"]
|
||||
]
|
||||
},
|
||||
baselineOnly: true,
|
||||
verifyDiagnostics: true
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -102,7 +102,22 @@ namespace ts {
|
||||
interface ReadonlyArray<T> {}
|
||||
declare const console: { log(msg: any): void; };`;
|
||||
|
||||
export function loadProjectFromDisk(root: string, time?: vfs.FileSystemOptions["time"]): vfs.FileSystem {
|
||||
export const symbolLibContent = `
|
||||
interface SymbolConstructor {
|
||||
readonly species: symbol;
|
||||
readonly toStringTag: symbol;
|
||||
}
|
||||
declare var Symbol: SymbolConstructor;
|
||||
interface Symbol {
|
||||
readonly [Symbol.toStringTag]: string;
|
||||
}
|
||||
`;
|
||||
|
||||
export function loadProjectFromDisk(
|
||||
root: string,
|
||||
time?: vfs.FileSystemOptions["time"],
|
||||
libContentToAppend?: string
|
||||
): vfs.FileSystem {
|
||||
const resolver = vfs.createResolver(Harness.IO);
|
||||
const fs = new vfs.FileSystem(/*ignoreCase*/ true, {
|
||||
files: {
|
||||
@@ -112,12 +127,31 @@ declare const console: { log(msg: any): void; };`;
|
||||
meta: { defaultLibLocation: "/lib" },
|
||||
time
|
||||
});
|
||||
fs.mkdirSync("/lib");
|
||||
fs.writeFileSync("/lib/lib.d.ts", libContent);
|
||||
fs.makeReadonly();
|
||||
addLibAndMakeReadonly(fs, libContentToAppend);
|
||||
return fs;
|
||||
}
|
||||
|
||||
export function loadProjectFromFiles(
|
||||
files: vfs.FileSet,
|
||||
time?: vfs.FileSystemOptions["time"],
|
||||
libContentToAppend?: string
|
||||
): vfs.FileSystem {
|
||||
const fs = new vfs.FileSystem(/*ignoreCase*/ true, {
|
||||
files,
|
||||
cwd: "/",
|
||||
meta: { defaultLibLocation: "/lib" },
|
||||
time
|
||||
});
|
||||
addLibAndMakeReadonly(fs, libContentToAppend);
|
||||
return fs;
|
||||
}
|
||||
|
||||
function addLibAndMakeReadonly(fs: vfs.FileSystem, libContentToAppend?: string) {
|
||||
fs.mkdirSync("/lib");
|
||||
fs.writeFileSync("/lib/lib.d.ts", libContentToAppend ? `${libContent}${libContentToAppend}` : libContent);
|
||||
fs.makeReadonly();
|
||||
}
|
||||
|
||||
export function verifyOutputsPresent(fs: vfs.FileSystem, outputs: readonly string[]) {
|
||||
for (const output of outputs) {
|
||||
assert(fs.existsSync(output), `Expect file ${output} to exist`);
|
||||
@@ -199,7 +233,7 @@ declare const console: { log(msg: any): void; };`;
|
||||
fs: vfs.FileSystem;
|
||||
tick: () => void;
|
||||
rootNames: ReadonlyArray<string>;
|
||||
expectedMapFileNames: ReadonlyArray<string>;
|
||||
expectedMapFileNames?: ReadonlyArray<string>;
|
||||
expectedBuildInfoFilesForSectionBaselines?: ReadonlyArray<BuildInfoSectionBaselineFiles>;
|
||||
modifyFs: (fs: vfs.FileSystem) => void;
|
||||
}
|
||||
@@ -221,7 +255,7 @@ declare const console: { log(msg: any): void; };`;
|
||||
return originalReadFile.call(host, path);
|
||||
};
|
||||
builder.build();
|
||||
generateSourceMapBaselineFiles(fs, expectedMapFileNames);
|
||||
if (expectedMapFileNames) generateSourceMapBaselineFiles(fs, expectedMapFileNames);
|
||||
generateBuildInfoSectionBaselineFiles(fs, expectedBuildInfoFilesForSectionBaselines || emptyArray);
|
||||
fs.makeReadonly();
|
||||
return { fs, actualReadFileMap, host, builder };
|
||||
@@ -268,9 +302,10 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
|
||||
tick: () => void;
|
||||
proj: string;
|
||||
rootNames: ReadonlyArray<string>;
|
||||
expectedMapFileNames: ReadonlyArray<string>;
|
||||
/** map file names to generate baseline of */
|
||||
expectedMapFileNames?: ReadonlyArray<string>;
|
||||
expectedBuildInfoFilesForSectionBaselines?: ReadonlyArray<BuildInfoSectionBaselineFiles>;
|
||||
lastProjectOutputJs: string;
|
||||
lastProjectOutput: string;
|
||||
initialBuild: BuildState;
|
||||
outputFiles?: ReadonlyArray<string>;
|
||||
incrementalDtsChangedBuild?: BuildState;
|
||||
@@ -282,7 +317,7 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
|
||||
|
||||
export function verifyTsbuildOutput({
|
||||
scenario, projFs, time, tick, proj, rootNames, outputFiles, baselineOnly, verifyDiagnostics,
|
||||
expectedMapFileNames, expectedBuildInfoFilesForSectionBaselines, lastProjectOutputJs,
|
||||
expectedMapFileNames, expectedBuildInfoFilesForSectionBaselines, lastProjectOutput,
|
||||
initialBuild, incrementalDtsChangedBuild, incrementalDtsUnchangedBuild, incrementalHeaderChangedBuild
|
||||
}: VerifyTsBuildInput) {
|
||||
describe(`tsc --b ${proj}:: ${scenario}`, () => {
|
||||
@@ -331,7 +366,7 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
|
||||
let beforeBuildTime: number;
|
||||
let afterBuildTime: number;
|
||||
before(() => {
|
||||
beforeBuildTime = fs.statSync(lastProjectOutputJs).mtimeMs;
|
||||
beforeBuildTime = fs.statSync(lastProjectOutput).mtimeMs;
|
||||
tick();
|
||||
newFs = fs.shadow();
|
||||
tick();
|
||||
@@ -343,7 +378,7 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
|
||||
expectedBuildInfoFilesForSectionBaselines,
|
||||
modifyFs: incrementalModifyFs,
|
||||
}));
|
||||
afterBuildTime = newFs.statSync(lastProjectOutputJs).mtimeMs;
|
||||
afterBuildTime = newFs.statSync(lastProjectOutput).mtimeMs;
|
||||
});
|
||||
after(() => {
|
||||
newFs = undefined!;
|
||||
@@ -359,6 +394,12 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
|
||||
host.assertDiagnosticMessages(...(incrementalExpectedDiagnostics || emptyArray));
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Build should pass without errors if not verifying diagnostics
|
||||
it(`verify no errors`, () => {
|
||||
host.assertErrors(/*empty*/);
|
||||
});
|
||||
}
|
||||
it(`Generates files matching the baseline`, () => {
|
||||
generateBaseline(newFs, proj, scenario, subScenario, fs);
|
||||
});
|
||||
@@ -373,7 +414,6 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
|
||||
fs: newFs.shadow(),
|
||||
tick,
|
||||
rootNames,
|
||||
expectedMapFileNames: emptyArray,
|
||||
modifyFs: fs => {
|
||||
// Delete output files
|
||||
for (const outputFile of expectedOutputFiles) {
|
||||
|
||||
@@ -16,8 +16,7 @@ namespace ts {
|
||||
tick,
|
||||
proj: "inferredTypeFromTransitiveModule",
|
||||
rootNames: ["/src"],
|
||||
expectedMapFileNames: emptyArray,
|
||||
lastProjectOutputJs: `/src/obj/index.js`,
|
||||
lastProjectOutput: `/src/obj/index.js`,
|
||||
outputFiles: [
|
||||
"/src/obj/bar.js", "/src/obj/bar.d.ts",
|
||||
"/src/obj/bundling.js", "/src/obj/bundling.d.ts",
|
||||
|
||||
@@ -16,8 +16,7 @@ namespace ts {
|
||||
tick,
|
||||
proj: "lateBoundSymbol",
|
||||
rootNames: ["/src/tsconfig.json"],
|
||||
expectedMapFileNames: emptyArray,
|
||||
lastProjectOutputJs: "/src/src/main.js",
|
||||
lastProjectOutput: "/src/src/main.js",
|
||||
outputFiles: [
|
||||
"/src/src/hkt.js",
|
||||
"/src/src/main.js",
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
namespace ts {
|
||||
// https://github.com/microsoft/TypeScript/issues/31696
|
||||
it("unittests:: tsbuild:: moduleSpecifiers:: synthesized module specifiers to referenced projects resolve correctly", () => {
|
||||
const baseFs = vfs.createFromFileSystem(Harness.IO, /*ignoreCase*/ false, {
|
||||
files: {
|
||||
describe("unittests:: tsbuild:: moduleSpecifiers:: synthesized module specifiers to referenced projects resolve correctly", () => {
|
||||
let projFs: vfs.FileSystem;
|
||||
const { time, tick } = getTime();
|
||||
before(() => {
|
||||
projFs = loadProjectFromFiles({
|
||||
"/src/common/nominal.ts": utils.dedent`
|
||||
export declare type Nominal<T, Name extends string> = T & {
|
||||
[Symbol.species]: Name;
|
||||
@@ -71,7 +73,6 @@ namespace ts {
|
||||
"skipLibCheck": true,
|
||||
"rootDir": "./",
|
||||
"outDir": "lib",
|
||||
"lib": ["dom", "es2015", "es2015.symbol.wellknown"]
|
||||
}
|
||||
}`,
|
||||
"/tsconfig.json": utils.dedent`{
|
||||
@@ -83,16 +84,23 @@ namespace ts {
|
||||
],
|
||||
"include": []
|
||||
}`
|
||||
}, time, symbolLibContent);
|
||||
});
|
||||
after(() => {
|
||||
projFs = undefined!;
|
||||
});
|
||||
verifyTsbuildOutput({
|
||||
scenario: `synthesized module specifiers resolve correctly`,
|
||||
projFs: () => projFs,
|
||||
time,
|
||||
tick,
|
||||
proj: "moduleSpecifiers",
|
||||
rootNames: ["/"],
|
||||
lastProjectOutput: `/src/lib/index.d.ts`,
|
||||
initialBuild: {
|
||||
modifyFs: noop,
|
||||
},
|
||||
cwd: "/"
|
||||
baselineOnly: true
|
||||
});
|
||||
const fs = baseFs.makeReadonly().shadow();
|
||||
const sys = new fakes.System(fs, { executingFilePath: "/", newLine: "\n" });
|
||||
const host = new fakes.SolutionBuilderHost(sys);
|
||||
const builder = createSolutionBuilder(host, ["/tsconfig.json"], { dry: false, force: false, verbose: false });
|
||||
builder.build();
|
||||
|
||||
// Prior to fixing GH31696 the import in `/lib/src/sub-project-2/index.d.ts` was `import("../../lib/src/common/nonterminal")`, which was invalid.
|
||||
Harness.Baseline.runBaseline("tsbuild/moduleSpecifiers/initial-build/resolves-correctly.js", vfs.formatPatch(fs.diff(baseFs)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,7 +288,7 @@ namespace ts {
|
||||
rootNames: ["/src/third"],
|
||||
expectedMapFileNames,
|
||||
expectedBuildInfoFilesForSectionBaselines: expectedBuildInfoFilesForSectionBaselines || expectedTsbuildInfoFileNames,
|
||||
lastProjectOutputJs: outputFiles[project.third][ext.js],
|
||||
lastProjectOutput: outputFiles[project.third][ext.js],
|
||||
initialBuild: {
|
||||
modifyFs,
|
||||
expectedDiagnostics: initialExpectedDiagnostics,
|
||||
|
||||
@@ -617,7 +617,7 @@ export class cNew {}`);
|
||||
"/src/core/index.d.ts.map",
|
||||
"/src/logic/index.js.map"
|
||||
],
|
||||
lastProjectOutputJs: "/src/tests/index.js",
|
||||
lastProjectOutput: "/src/tests/index.js",
|
||||
initialBuild,
|
||||
incrementalDtsChangedBuild: {
|
||||
modifyFs: fs => appendText(fs, "/src/core/index.ts", `
|
||||
@@ -727,7 +727,7 @@ class someClass { }`),
|
||||
"/src/core/index.d.ts.map",
|
||||
"/src/logic/index.js.map"
|
||||
],
|
||||
lastProjectOutputJs: "/src/tests/index.js",
|
||||
lastProjectOutput: "/src/tests/index.js",
|
||||
initialBuild,
|
||||
incrementalDtsChangedBuild: {
|
||||
modifyFs: fs => replaceText(fs, "/src/logic/tsconfig.json", `"declaration": true,`, `"declaration": true,
|
||||
@@ -795,7 +795,7 @@ class someClass { }`),
|
||||
"/src/core/index.d.ts.map",
|
||||
"/src/logic/index.js.map"
|
||||
],
|
||||
lastProjectOutputJs: "/src/tests/index.js",
|
||||
lastProjectOutput: "/src/tests/index.js",
|
||||
initialBuild: {
|
||||
modifyFs: fs => replaceText(fs, "/src/logic/tsconfig.json", `"composite": true,`, `"composite": true,
|
||||
"tsBuildInfoFile": "ownFile.tsbuildinfo",`),
|
||||
@@ -851,8 +851,7 @@ class someClass { }`),
|
||||
tick,
|
||||
proj: "sample1",
|
||||
rootNames: ["/src/core"],
|
||||
expectedMapFileNames: emptyArray,
|
||||
lastProjectOutputJs: "/src/core/index.js",
|
||||
lastProjectOutput: "/src/core/index.js",
|
||||
initialBuild: {
|
||||
modifyFs: fs => fs.writeFileSync("/src/core/tsconfig.json", `{
|
||||
"compilerOptions": {
|
||||
@@ -892,8 +891,7 @@ class someClass { }`),
|
||||
tick,
|
||||
proj: "sample1",
|
||||
rootNames: ["/src/core"],
|
||||
expectedMapFileNames: emptyArray,
|
||||
lastProjectOutputJs: "/src/core/index.js",
|
||||
lastProjectOutput: "/src/core/index.js",
|
||||
initialBuild: {
|
||||
modifyFs: fs => {
|
||||
fs.writeFileSync("/lib/lib.esnext.full.d.ts", `/// <reference no-default-lib="true"/>
|
||||
@@ -942,8 +940,7 @@ class someClass { }`),
|
||||
tick,
|
||||
proj: "sample1",
|
||||
rootNames: ["/src/core"],
|
||||
expectedMapFileNames: emptyArray,
|
||||
lastProjectOutputJs: "/src/core/index.js",
|
||||
lastProjectOutput: "/src/core/index.js",
|
||||
initialBuild: {
|
||||
modifyFs: fs => fs.writeFileSync("/src/core/tsconfig.json", `{
|
||||
"compilerOptions": {
|
||||
@@ -981,8 +978,7 @@ class someClass { }`),
|
||||
tick,
|
||||
proj: "sample1",
|
||||
rootNames: ["/src/tests"],
|
||||
expectedMapFileNames: emptyArray,
|
||||
lastProjectOutputJs: "/src/tests/index.js",
|
||||
lastProjectOutput: "/src/tests/index.js",
|
||||
initialBuild: {
|
||||
modifyFs: fs => fs.writeFileSync("/src/tests/tsconfig.json", `{
|
||||
"references": [
|
||||
|
||||
@@ -16,17 +16,17 @@ namespace ts.tscWatch {
|
||||
expectedIncrementalEmit?: ReadonlyArray<File>;
|
||||
expectedIncrementalErrors?: ReadonlyArray<string>;
|
||||
}
|
||||
function verifyIncrementalWatchEmit(input: VerifyIncrementalWatchEmitInput) {
|
||||
function verifyIncrementalWatchEmit(input: () => VerifyIncrementalWatchEmitInput) {
|
||||
it("with tsc --w", () => {
|
||||
verifyIncrementalWatchEmitWorker({
|
||||
input,
|
||||
input: input(),
|
||||
emitAndReportErrors: createWatchOfConfigFile,
|
||||
verifyErrors: checkOutputErrorsInitial
|
||||
});
|
||||
});
|
||||
it("with tsc", () => {
|
||||
verifyIncrementalWatchEmitWorker({
|
||||
input,
|
||||
input: input(),
|
||||
emitAndReportErrors: incrementalBuild,
|
||||
verifyErrors: checkNormalBuildErrors
|
||||
});
|
||||
@@ -122,7 +122,7 @@ namespace ts.tscWatch {
|
||||
|
||||
function checkFileEmit(actual: Map<string>, expected: ReadonlyArray<File>) {
|
||||
assert.equal(actual.size, expected.length, `Actual: ${JSON.stringify(arrayFrom(actual.entries()), /*replacer*/ undefined, " ")}\nExpected: ${JSON.stringify(expected, /*replacer*/ undefined, " ")}`);
|
||||
expected.forEach(file => {
|
||||
for (const file of expected) {
|
||||
let expectedContent = file.content;
|
||||
let actualContent = actual.get(file.path);
|
||||
if (isBuildInfoFile(file.path)) {
|
||||
@@ -130,7 +130,7 @@ namespace ts.tscWatch {
|
||||
expectedContent = sanitizeBuildInfo(expectedContent);
|
||||
}
|
||||
assert.equal(actualContent, expectedContent, `Emit for ${file.path}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const libFileInfo: BuilderState.FileInfo = {
|
||||
@@ -170,7 +170,7 @@ namespace ts.tscWatch {
|
||||
describe("own file emit without errors", () => {
|
||||
function verify(optionsToExtend?: CompilerOptions, expectedBuildinfoOptions?: CompilerOptions) {
|
||||
const modifiedFile2Content = file2.content.replace("y", "z").replace("20", "10");
|
||||
verifyIncrementalWatchEmit({
|
||||
verifyIncrementalWatchEmit(() => ({
|
||||
files: [libFile, file1, file2, configFile],
|
||||
optionsToExtend,
|
||||
expectedInitialEmit: [
|
||||
@@ -226,7 +226,7 @@ namespace ts.tscWatch {
|
||||
}
|
||||
],
|
||||
expectedIncrementalErrors: emptyArray,
|
||||
});
|
||||
}));
|
||||
}
|
||||
verify();
|
||||
describe("with commandline parameters that are not relative", () => {
|
||||
@@ -259,7 +259,7 @@ namespace ts.tscWatch {
|
||||
"file2.ts(1,7): error TS2322: Type '20' is not assignable to type 'string'.\n"
|
||||
];
|
||||
const modifiedFile1Content = file1.content.replace("x", "z");
|
||||
verifyIncrementalWatchEmit({
|
||||
verifyIncrementalWatchEmit(() => ({
|
||||
files: [libFile, file1, fileModified, configFile],
|
||||
expectedInitialEmit: [
|
||||
file1Js,
|
||||
@@ -320,7 +320,7 @@ namespace ts.tscWatch {
|
||||
}
|
||||
],
|
||||
expectedIncrementalErrors: file2Errors,
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe("with --out", () => {
|
||||
@@ -332,7 +332,7 @@ namespace ts.tscWatch {
|
||||
path: `${project}/out.js`,
|
||||
content: "var x = 10;\nvar y = 20;\n"
|
||||
};
|
||||
verifyIncrementalWatchEmit({
|
||||
verifyIncrementalWatchEmit(() => ({
|
||||
files: [libFile, file1, file2, config],
|
||||
expectedInitialEmit: [
|
||||
outFile,
|
||||
@@ -353,7 +353,7 @@ namespace ts.tscWatch {
|
||||
}
|
||||
],
|
||||
expectedInitialErrors: emptyArray
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
});
|
||||
@@ -397,7 +397,7 @@ namespace ts.tscWatch {
|
||||
|
||||
describe("own file emit without errors", () => {
|
||||
const modifiedFile2Content = file2.content.replace("y", "z").replace("20", "10");
|
||||
verifyIncrementalWatchEmit({
|
||||
verifyIncrementalWatchEmit(() => ({
|
||||
files: [libFile, file1, file2, config],
|
||||
expectedInitialEmit: [
|
||||
file1Js,
|
||||
@@ -451,7 +451,7 @@ namespace ts.tscWatch {
|
||||
}
|
||||
],
|
||||
expectedIncrementalErrors: emptyArray,
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe("own file emit with errors", () => {
|
||||
@@ -479,7 +479,7 @@ namespace ts.tscWatch {
|
||||
"file2.ts(1,14): error TS2322: Type '20' is not assignable to type 'string'.\n"
|
||||
];
|
||||
const modifiedFile1Content = file1.content.replace("x = 10", "z = 10");
|
||||
verifyIncrementalWatchEmit({
|
||||
verifyIncrementalWatchEmit(() => ({
|
||||
files: [libFile, file1, fileModified, config],
|
||||
expectedInitialEmit: [
|
||||
file1Js,
|
||||
@@ -541,6 +541,49 @@ namespace ts.tscWatch {
|
||||
}
|
||||
],
|
||||
expectedIncrementalErrors: file2Errors,
|
||||
}));
|
||||
|
||||
it("verify that state is read correctly", () => {
|
||||
const system = createWatchedSystem([libFile, file1, fileModified, config], { currentDirectory: project });
|
||||
incrementalBuild("tsconfig.json", system);
|
||||
|
||||
const command = parseConfigFileWithSystem("tsconfig.json", {}, system, noop)!;
|
||||
const builderProgram = createIncrementalProgram({
|
||||
rootNames: command.fileNames,
|
||||
options: command.options,
|
||||
projectReferences: command.projectReferences,
|
||||
configFileParsingDiagnostics: getConfigFileParsingDiagnostics(command),
|
||||
host: createIncrementalCompilerHost(command.options, system)
|
||||
});
|
||||
|
||||
const state = builderProgram.getState();
|
||||
assert.equal(state.changedFilesSet!.size, 0, "changes");
|
||||
|
||||
assert.equal(state.fileInfos.size, 3, "FileInfo size");
|
||||
assert.deepEqual(state.fileInfos.get(libFile.path), libFileInfo);
|
||||
assert.deepEqual(state.fileInfos.get(file1.path), getFileInfo(file1.content));
|
||||
assert.deepEqual(state.fileInfos.get(file2.path), file2FileInfo);
|
||||
|
||||
assert.deepEqual(state.compilerOptions, {
|
||||
incremental: true,
|
||||
module: ModuleKind.AMD,
|
||||
configFilePath: config.path
|
||||
});
|
||||
|
||||
assert.equal(state.referencedMap!.size, 0);
|
||||
assert.equal(state.exportedModulesMap!.size, 0);
|
||||
|
||||
assert.equal(state.semanticDiagnosticsPerFile!.size, 3);
|
||||
assert.deepEqual(state.semanticDiagnosticsPerFile!.get(libFile.path), emptyArray);
|
||||
assert.deepEqual(state.semanticDiagnosticsPerFile!.get(file1.path), emptyArray);
|
||||
const { file: _, relatedInformation: __, ...rest } = file2ReuasableError[1][0];
|
||||
assert.deepEqual(state.semanticDiagnosticsPerFile!.get(file2.path), [{
|
||||
...rest,
|
||||
file: state.program!.getSourceFileByPath(file2.path as Path)!,
|
||||
relatedInformation: undefined,
|
||||
reportsUnnecessary: undefined,
|
||||
source: undefined
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -561,7 +604,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
`;
|
||||
}
|
||||
verifyIncrementalWatchEmit({
|
||||
verifyIncrementalWatchEmit(() => ({
|
||||
files: [libFile, file1, file2, config],
|
||||
expectedInitialEmit: [
|
||||
outFile,
|
||||
@@ -582,7 +625,140 @@ namespace ts.tscWatch {
|
||||
}
|
||||
],
|
||||
expectedInitialErrors: emptyArray
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("incremental with circular references", () => {
|
||||
function getFileInfo(content: string): BuilderState.FileInfo {
|
||||
const signature = Harness.mockHash(content);
|
||||
return { version: signature, signature };
|
||||
}
|
||||
const config: File = {
|
||||
path: configFile.path,
|
||||
content: JSON.stringify({
|
||||
compilerOptions: {
|
||||
incremental: true,
|
||||
target: "es5",
|
||||
module: "commonjs",
|
||||
declaration: true,
|
||||
emitDeclarationOnly: true
|
||||
}
|
||||
})
|
||||
};
|
||||
const aTs: File = {
|
||||
path: `${project}/a.ts`,
|
||||
content: `import { B } from "./b";
|
||||
export interface A {
|
||||
b: B;
|
||||
}
|
||||
`
|
||||
};
|
||||
const bTs: File = {
|
||||
path: `${project}/b.ts`,
|
||||
content: `import { C } from "./c";
|
||||
export interface B {
|
||||
b: C;
|
||||
}
|
||||
`
|
||||
};
|
||||
const cTs: File = {
|
||||
path: `${project}/c.ts`,
|
||||
content: `import { A } from "./a";
|
||||
export interface C {
|
||||
a: A;
|
||||
}
|
||||
`
|
||||
};
|
||||
const indexTs: File = {
|
||||
path: `${project}/index.ts`,
|
||||
content: `export { A } from "./a";
|
||||
export { B } from "./b";
|
||||
export { C } from "./c";
|
||||
`
|
||||
};
|
||||
|
||||
verifyIncrementalWatchEmit(() => {
|
||||
const referencedMap: MapLike<string[]> = {
|
||||
"./a.ts": ["./b.ts"],
|
||||
"./b.ts": ["./c.ts"],
|
||||
"./c.ts": ["./a.ts"],
|
||||
"./index.ts": ["./a.ts", "./b.ts", "./c.ts"],
|
||||
};
|
||||
const initialProgram: ProgramBuildInfo = {
|
||||
fileInfos: {
|
||||
[libFilePath]: libFileInfo,
|
||||
"./c.ts": getFileInfo(cTs.content),
|
||||
"./b.ts": getFileInfo(bTs.content),
|
||||
"./a.ts": getFileInfo(aTs.content),
|
||||
"./index.ts": getFileInfo(indexTs.content)
|
||||
},
|
||||
options: {
|
||||
incremental: true,
|
||||
target: ScriptTarget.ES5,
|
||||
module: ModuleKind.CommonJS,
|
||||
declaration: true,
|
||||
emitDeclarationOnly: true,
|
||||
configFilePath: "./tsconfig.json"
|
||||
},
|
||||
referencedMap,
|
||||
exportedModulesMap: referencedMap,
|
||||
semanticDiagnosticsPerFile: [
|
||||
libFilePath,
|
||||
"./a.ts",
|
||||
"./b.ts",
|
||||
"./c.ts",
|
||||
"./index.ts",
|
||||
]
|
||||
};
|
||||
const { fileInfos, ...rest } = initialProgram;
|
||||
const expectedADts: File = { path: `${project}/a.d.ts`, content: aTs.content };
|
||||
const expectedBDts: File = { path: `${project}/b.d.ts`, content: bTs.content };
|
||||
const expectedCDts: File = { path: `${project}/c.d.ts`, content: cTs.content };
|
||||
const expectedIndexDts: File = { path: `${project}/index.d.ts`, content: indexTs.content };
|
||||
const modifiedATsContent = aTs.content.replace("b: B;", `b: B;
|
||||
foo: any;`);
|
||||
return {
|
||||
files: [libFile, aTs, bTs, cTs, indexTs, config],
|
||||
expectedInitialEmit: [
|
||||
expectedADts,
|
||||
expectedBDts,
|
||||
expectedCDts,
|
||||
expectedIndexDts,
|
||||
{
|
||||
path: `${project}/tsconfig.tsbuildinfo`,
|
||||
content: getBuildInfoText({
|
||||
program: initialProgram,
|
||||
version
|
||||
})
|
||||
}
|
||||
],
|
||||
expectedInitialErrors: emptyArray,
|
||||
modifyFs: host => host.writeFile(aTs.path, modifiedATsContent),
|
||||
expectedIncrementalEmit: [
|
||||
{ path: expectedADts.path, content: modifiedATsContent },
|
||||
expectedBDts,
|
||||
expectedCDts,
|
||||
expectedIndexDts,
|
||||
{
|
||||
path: `${project}/tsconfig.tsbuildinfo`,
|
||||
content: getBuildInfoText({
|
||||
program: {
|
||||
fileInfos: {
|
||||
[libFilePath]: libFileInfo,
|
||||
"./c.ts": getFileInfo(cTs.content),
|
||||
"./b.ts": getFileInfo(bTs.content),
|
||||
"./a.ts": getFileInfo(modifiedATsContent),
|
||||
"./index.ts": getFileInfo(indexTs.content)
|
||||
},
|
||||
...rest
|
||||
},
|
||||
version
|
||||
})
|
||||
}
|
||||
],
|
||||
expectedIncrementalErrors: emptyArray
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user