mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge master
This commit is contained in:
+40
-16
@@ -564,7 +564,7 @@ namespace ts {
|
||||
if (!isIIFE) {
|
||||
currentFlow = { flags: FlowFlags.Start };
|
||||
if (containerFlags & (ContainerFlags.IsFunctionExpression | ContainerFlags.IsObjectLiteralOrClassExpressionMethod)) {
|
||||
currentFlow.container = <FunctionExpression | ArrowFunction | MethodDeclaration>node;
|
||||
currentFlow.node = <FunctionExpression | ArrowFunction | MethodDeclaration>node;
|
||||
}
|
||||
}
|
||||
// We create a return control flow graph for IIFEs and constructors. For constructors
|
||||
@@ -581,6 +581,7 @@ namespace ts {
|
||||
if (!(currentFlow.flags & FlowFlags.Unreachable) && containerFlags & ContainerFlags.IsFunctionLike && nodeIsPresent((<FunctionLikeDeclaration>node).body)) {
|
||||
node.flags |= NodeFlags.HasImplicitReturn;
|
||||
if (hasExplicitReturn) node.flags |= NodeFlags.HasExplicitReturn;
|
||||
(<FunctionLikeDeclaration>node).endFlowNode = currentFlow;
|
||||
}
|
||||
if (node.kind === SyntaxKind.SourceFile) {
|
||||
node.flags |= emitFlags;
|
||||
@@ -671,6 +672,9 @@ namespace ts {
|
||||
bindJSDoc(node);
|
||||
return;
|
||||
}
|
||||
if (node.kind >= SyntaxKind.FirstStatement && node.kind <= SyntaxKind.LastStatement && !options.allowUnreachableCode) {
|
||||
node.flowNode = currentFlow;
|
||||
}
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.WhileStatement:
|
||||
bindWhileStatement(<WhileStatement>node);
|
||||
@@ -708,6 +712,9 @@ namespace ts {
|
||||
case SyntaxKind.CaseClause:
|
||||
bindCaseClause(<CaseClause>node);
|
||||
break;
|
||||
case SyntaxKind.ExpressionStatement:
|
||||
bindExpressionStatement(<ExpressionStatement>node);
|
||||
break;
|
||||
case SyntaxKind.LabeledStatement:
|
||||
bindLabeledStatement(<LabeledStatement>node);
|
||||
break;
|
||||
@@ -845,17 +852,11 @@ namespace ts {
|
||||
}
|
||||
|
||||
function createBranchLabel(): FlowLabel {
|
||||
return {
|
||||
flags: FlowFlags.BranchLabel,
|
||||
antecedents: undefined
|
||||
};
|
||||
return { flags: FlowFlags.BranchLabel, antecedents: undefined };
|
||||
}
|
||||
|
||||
function createLoopLabel(): FlowLabel {
|
||||
return {
|
||||
flags: FlowFlags.LoopLabel,
|
||||
antecedents: undefined
|
||||
};
|
||||
return { flags: FlowFlags.LoopLabel, antecedents: undefined };
|
||||
}
|
||||
|
||||
function setFlowNodeReferenced(flow: FlowNode) {
|
||||
@@ -885,7 +886,7 @@ namespace ts {
|
||||
return antecedent;
|
||||
}
|
||||
setFlowNodeReferenced(antecedent);
|
||||
return flowNodeCreated({ flags, expression, antecedent });
|
||||
return flowNodeCreated({ flags, antecedent, node: expression });
|
||||
}
|
||||
|
||||
function createFlowSwitchClause(antecedent: FlowNode, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): FlowNode {
|
||||
@@ -893,7 +894,7 @@ namespace ts {
|
||||
return antecedent;
|
||||
}
|
||||
setFlowNodeReferenced(antecedent);
|
||||
return flowNodeCreated({ flags: FlowFlags.SwitchClause, switchStatement, clauseStart, clauseEnd, antecedent });
|
||||
return flowNodeCreated({ flags: FlowFlags.SwitchClause, antecedent, switchStatement, clauseStart, clauseEnd });
|
||||
}
|
||||
|
||||
function createFlowAssignment(antecedent: FlowNode, node: Expression | VariableDeclaration | BindingElement): FlowNode {
|
||||
@@ -901,10 +902,14 @@ namespace ts {
|
||||
return flowNodeCreated({ flags: FlowFlags.Assignment, antecedent, node });
|
||||
}
|
||||
|
||||
function createFlowCall(antecedent: FlowNode, node: CallExpression): FlowNode {
|
||||
setFlowNodeReferenced(antecedent);
|
||||
return flowNodeCreated({ flags: FlowFlags.Call, antecedent, node });
|
||||
}
|
||||
|
||||
function createFlowArrayMutation(antecedent: FlowNode, node: CallExpression | BinaryExpression): FlowNode {
|
||||
setFlowNodeReferenced(antecedent);
|
||||
const res: FlowArrayMutation = flowNodeCreated({ flags: FlowFlags.ArrayMutation, antecedent, node });
|
||||
return res;
|
||||
return flowNodeCreated({ flags: FlowFlags.ArrayMutation, antecedent, node });
|
||||
}
|
||||
|
||||
function finishFlowLabel(flow: FlowLabel): FlowNode {
|
||||
@@ -1030,12 +1035,12 @@ namespace ts {
|
||||
function bindForInOrForOfStatement(node: ForInOrOfStatement): void {
|
||||
const preLoopLabel = createLoopLabel();
|
||||
const postLoopLabel = createBranchLabel();
|
||||
bind(node.expression);
|
||||
addAntecedent(preLoopLabel, currentFlow);
|
||||
currentFlow = preLoopLabel;
|
||||
if (node.kind === SyntaxKind.ForOfStatement) {
|
||||
bind(node.awaitModifier);
|
||||
}
|
||||
bind(node.expression);
|
||||
addAntecedent(postLoopLabel, currentFlow);
|
||||
bind(node.initializer);
|
||||
if (node.initializer.kind !== SyntaxKind.VariableDeclarationList) {
|
||||
@@ -1222,7 +1227,8 @@ namespace ts {
|
||||
addAntecedent(postSwitchLabel, currentFlow);
|
||||
const hasDefault = forEach(node.caseBlock.clauses, c => c.kind === SyntaxKind.DefaultClause);
|
||||
// We mark a switch statement as possibly exhaustive if it has no default clause and if all
|
||||
// case clauses have unreachable end points (e.g. they all return).
|
||||
// case clauses have unreachable end points (e.g. they all return). Note, we no longer need
|
||||
// this property in control flow analysis, it's there only for backwards compatibility.
|
||||
node.possiblyExhaustive = !hasDefault && !postSwitchLabel.antecedents;
|
||||
if (!hasDefault) {
|
||||
addAntecedent(postSwitchLabel, createFlowSwitchClause(preSwitchCaseFlow, node, 0, 0));
|
||||
@@ -1281,6 +1287,24 @@ namespace ts {
|
||||
activeLabels!.pop();
|
||||
}
|
||||
|
||||
function isDottedName(node: Expression): boolean {
|
||||
return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword ||
|
||||
node.kind === SyntaxKind.PropertyAccessExpression && isDottedName((<PropertyAccessExpression>node).expression) ||
|
||||
node.kind === SyntaxKind.ParenthesizedExpression && isDottedName((<ParenthesizedExpression>node).expression);
|
||||
}
|
||||
|
||||
function bindExpressionStatement(node: ExpressionStatement): void {
|
||||
bind(node.expression);
|
||||
// A top level call expression with a dotted function name and at least one argument
|
||||
// is potentially an assertion and is therefore included in the control flow.
|
||||
if (node.expression.kind === SyntaxKind.CallExpression) {
|
||||
const call = <CallExpression>node.expression;
|
||||
if (isDottedName(call.expression)) {
|
||||
currentFlow = createFlowCall(currentFlow, call);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function bindLabeledStatement(node: LabeledStatement): void {
|
||||
const preStatementLabel = createLoopLabel();
|
||||
const postStatementLabel = createBranchLabel();
|
||||
@@ -2859,7 +2883,7 @@ namespace ts {
|
||||
|
||||
// If this is a property-parameter, then also declare the property symbol into the
|
||||
// containing class.
|
||||
if (isParameterPropertyDeclaration(node)) {
|
||||
if (isParameterPropertyDeclaration(node, node.parent)) {
|
||||
const classDeclaration = <ClassLikeDeclaration>node.parent.parent;
|
||||
declareSymbol(classDeclaration.symbol.members!, classDeclaration.symbol, node, SymbolFlags.Property | (node.questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes);
|
||||
}
|
||||
|
||||
+106
-31
@@ -60,6 +60,10 @@ namespace ts {
|
||||
* Files pending to be emitted
|
||||
*/
|
||||
affectedFilesPendingEmit?: readonly Path[] | undefined;
|
||||
/**
|
||||
* Files pending to be emitted kind.
|
||||
*/
|
||||
affectedFilesPendingEmitKind?: ReadonlyMap<BuilderFileEmit> | undefined;
|
||||
/**
|
||||
* Current index to retrieve pending affected file
|
||||
*/
|
||||
@@ -70,6 +74,11 @@ namespace ts {
|
||||
hasReusableDiagnostic?: true;
|
||||
}
|
||||
|
||||
export const enum BuilderFileEmit {
|
||||
DtsOnly,
|
||||
Full
|
||||
}
|
||||
|
||||
/**
|
||||
* State to store the changed files, affected files and cache semantic diagnostics
|
||||
*/
|
||||
@@ -127,7 +136,11 @@ namespace ts {
|
||||
/**
|
||||
* Files pending to be emitted
|
||||
*/
|
||||
affectedFilesPendingEmit: readonly Path[] | undefined;
|
||||
affectedFilesPendingEmit: Path[] | undefined;
|
||||
/**
|
||||
* Files pending to be emitted kind.
|
||||
*/
|
||||
affectedFilesPendingEmitKind: Map<BuilderFileEmit> | undefined;
|
||||
/**
|
||||
* Current index to retrieve pending affected file
|
||||
*/
|
||||
@@ -139,7 +152,7 @@ namespace ts {
|
||||
/**
|
||||
* Already seen emitted files
|
||||
*/
|
||||
seenEmittedFiles: Map<true> | undefined;
|
||||
seenEmittedFiles: Map<BuilderFileEmit> | undefined;
|
||||
/**
|
||||
* true if program has been emitted
|
||||
*/
|
||||
@@ -160,8 +173,7 @@ namespace ts {
|
||||
const compilerOptions = newProgram.getCompilerOptions();
|
||||
state.compilerOptions = compilerOptions;
|
||||
// With --out or --outFile, any change affects all semantic diagnostics so no need to cache them
|
||||
// With --isolatedModules, emitting changed file doesnt emit dependent files so we cant know of dependent files to retrieve errors so dont cache the errors
|
||||
if (!compilerOptions.outFile && !compilerOptions.out && !compilerOptions.isolatedModules) {
|
||||
if (!compilerOptions.outFile && !compilerOptions.out) {
|
||||
state.semanticDiagnosticsPerFile = createMap<readonly Diagnostic[]>();
|
||||
}
|
||||
state.changedFilesSet = createMap<true>();
|
||||
@@ -186,7 +198,8 @@ namespace ts {
|
||||
copyEntries(changedFilesSet, state.changedFilesSet);
|
||||
}
|
||||
if (!compilerOptions.outFile && !compilerOptions.out && oldState!.affectedFilesPendingEmit) {
|
||||
state.affectedFilesPendingEmit = oldState!.affectedFilesPendingEmit;
|
||||
state.affectedFilesPendingEmit = oldState!.affectedFilesPendingEmit.slice();
|
||||
state.affectedFilesPendingEmitKind = cloneMapOrUndefined(oldState!.affectedFilesPendingEmitKind);
|
||||
state.affectedFilesPendingEmitIndex = oldState!.affectedFilesPendingEmitIndex;
|
||||
}
|
||||
}
|
||||
@@ -233,7 +246,7 @@ namespace ts {
|
||||
|
||||
if (oldCompilerOptions && compilerOptionsAffectEmit(compilerOptions, oldCompilerOptions)) {
|
||||
// Add all files to affectedFilesPendingEmit since emit changed
|
||||
addToAffectedFilesPendingEmit(state, newProgram.getSourceFiles().map(f => f.path));
|
||||
newProgram.getSourceFiles().forEach(f => addToAffectedFilesPendingEmit(state, f.path, BuilderFileEmit.Full));
|
||||
Debug.assert(state.seenAffectedFiles === undefined);
|
||||
state.seenAffectedFiles = createMap<true>();
|
||||
}
|
||||
@@ -243,7 +256,7 @@ namespace ts {
|
||||
|
||||
function convertToDiagnostics(diagnostics: readonly ReusableDiagnostic[], newProgram: Program, getCanonicalFileName: GetCanonicalFileName): readonly Diagnostic[] {
|
||||
if (!diagnostics.length) return emptyArray;
|
||||
const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(getOutputPathForBuildInfo(newProgram.getCompilerOptions())!, newProgram.getCurrentDirectory()));
|
||||
const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(getTsBuildInfoEmitOutputFilePath(newProgram.getCompilerOptions())!, newProgram.getCurrentDirectory()));
|
||||
return diagnostics.map(diagnostic => {
|
||||
const result: Diagnostic = convertToDiagnosticRelatedInformation(diagnostic, newProgram, toPath);
|
||||
result.reportsUnnecessary = diagnostic.reportsUnnecessary;
|
||||
@@ -295,7 +308,8 @@ namespace ts {
|
||||
newState.semanticDiagnosticsFromOldState = cloneMapOrUndefined(state.semanticDiagnosticsFromOldState);
|
||||
newState.program = state.program;
|
||||
newState.compilerOptions = state.compilerOptions;
|
||||
newState.affectedFilesPendingEmit = state.affectedFilesPendingEmit;
|
||||
newState.affectedFilesPendingEmit = state.affectedFilesPendingEmit && state.affectedFilesPendingEmit.slice();
|
||||
newState.affectedFilesPendingEmitKind = cloneMapOrUndefined(state.affectedFilesPendingEmitKind);
|
||||
newState.affectedFilesPendingEmitIndex = state.affectedFilesPendingEmitIndex;
|
||||
newState.seenEmittedFiles = cloneMapOrUndefined(state.seenEmittedFiles);
|
||||
newState.programEmitComplete = state.programEmitComplete;
|
||||
@@ -373,19 +387,24 @@ namespace ts {
|
||||
/**
|
||||
* Returns next file to be emitted from files that retrieved semantic diagnostics but did not emit yet
|
||||
*/
|
||||
function getNextAffectedFilePendingEmit(state: BuilderProgramState): SourceFile | undefined {
|
||||
function getNextAffectedFilePendingEmit(state: BuilderProgramState) {
|
||||
const { affectedFilesPendingEmit } = state;
|
||||
if (affectedFilesPendingEmit) {
|
||||
const seenEmittedFiles = state.seenEmittedFiles || (state.seenEmittedFiles = createMap());
|
||||
for (let i = state.affectedFilesPendingEmitIndex!; i < affectedFilesPendingEmit.length; i++) {
|
||||
const affectedFile = Debug.assertDefined(state.program).getSourceFileByPath(affectedFilesPendingEmit[i]);
|
||||
if (affectedFile && !seenEmittedFiles.has(affectedFile.path)) {
|
||||
// emit this file
|
||||
state.affectedFilesPendingEmitIndex = i;
|
||||
return affectedFile;
|
||||
if (affectedFile) {
|
||||
const seenKind = seenEmittedFiles.get(affectedFile.path);
|
||||
const emitKind = Debug.assertDefined(Debug.assertDefined(state.affectedFilesPendingEmitKind).get(affectedFile.path));
|
||||
if (seenKind === undefined || seenKind < emitKind) {
|
||||
// emit this file
|
||||
state.affectedFilesPendingEmitIndex = i;
|
||||
return { affectedFile, emitKind };
|
||||
}
|
||||
}
|
||||
}
|
||||
state.affectedFilesPendingEmit = undefined;
|
||||
state.affectedFilesPendingEmitKind = undefined;
|
||||
state.affectedFilesPendingEmitIndex = undefined;
|
||||
}
|
||||
return undefined;
|
||||
@@ -406,7 +425,7 @@ namespace ts {
|
||||
const options = program.getCompilerOptions();
|
||||
forEach(program.getSourceFiles(), f =>
|
||||
program.isSourceFileDefaultLibrary(f) &&
|
||||
!skipTypeChecking(f, options) &&
|
||||
!skipTypeChecking(f, options, program) &&
|
||||
removeSemanticDiagnosticsOf(state, f.path)
|
||||
);
|
||||
}
|
||||
@@ -442,7 +461,7 @@ namespace ts {
|
||||
);
|
||||
// If not dts emit, nothing more to do
|
||||
if (getEmitDeclarations(state.compilerOptions)) {
|
||||
addToAffectedFilesPendingEmit(state, [path]);
|
||||
addToAffectedFilesPendingEmit(state, path, BuilderFileEmit.DtsOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -463,16 +482,43 @@ namespace ts {
|
||||
return !state.semanticDiagnosticsFromOldState.size;
|
||||
}
|
||||
|
||||
function isChangedSignagure(state: BuilderProgramState, path: Path) {
|
||||
const newSignature = Debug.assertDefined(state.currentAffectedFilesSignatures).get(path);
|
||||
const oldSignagure = Debug.assertDefined(state.fileInfos.get(path)).signature;
|
||||
return newSignature !== oldSignagure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate on referencing modules that export entities from affected file
|
||||
*/
|
||||
function forEachReferencingModulesOfExportOfAffectedFile(state: BuilderProgramState, affectedFile: SourceFile, fn: (state: BuilderProgramState, filePath: Path) => boolean) {
|
||||
// If there was change in signature (dts output) for the changed file,
|
||||
// then only we need to handle pending file emit
|
||||
if (!state.exportedModulesMap || state.affectedFiles!.length === 1 || !state.changedFilesSet.has(affectedFile.path)) {
|
||||
if (!state.exportedModulesMap || !state.changedFilesSet.has(affectedFile.path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isChangedSignagure(state, affectedFile.path)) return;
|
||||
|
||||
// Since isolated modules dont change js files, files affected by change in signature is itself
|
||||
// But we need to cleanup semantic diagnostics and queue dts emit for affected files
|
||||
if (state.compilerOptions.isolatedModules) {
|
||||
const seenFileNamesMap = createMap<true>();
|
||||
seenFileNamesMap.set(affectedFile.path, true);
|
||||
const queue = BuilderState.getReferencedByPaths(state, affectedFile.resolvedPath);
|
||||
while (queue.length > 0) {
|
||||
const currentPath = queue.pop()!;
|
||||
if (!seenFileNamesMap.has(currentPath)) {
|
||||
seenFileNamesMap.set(currentPath, true);
|
||||
const result = fn(state, currentPath);
|
||||
if (result && isChangedSignagure(state, currentPath)) {
|
||||
const currentSourceFile = Debug.assertDefined(state.program).getSourceFileByPath(currentPath)!;
|
||||
queue.push(...BuilderState.getReferencedByPaths(state, currentSourceFile.resolvedPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Debug.assert(!!state.currentAffectedFilesExportedModulesMap);
|
||||
const seenFileAndExportsOfFile = createMap<true>();
|
||||
// Go through exported modules from cache first
|
||||
@@ -548,7 +594,13 @@ 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, isEmitResult?: boolean) {
|
||||
function doneWithAffectedFile(
|
||||
state: BuilderProgramState,
|
||||
affected: SourceFile | Program,
|
||||
emitKind?: BuilderFileEmit,
|
||||
isPendingEmit?: boolean,
|
||||
isBuildInfoEmit?: boolean
|
||||
) {
|
||||
if (isBuildInfoEmit) {
|
||||
state.emittedBuildInfo = true;
|
||||
}
|
||||
@@ -558,8 +610,8 @@ 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 (emitKind !== undefined) {
|
||||
(state.seenEmittedFiles || (state.seenEmittedFiles = createMap())).set((affected as SourceFile).path, emitKind);
|
||||
}
|
||||
if (isPendingEmit) {
|
||||
state.affectedFilesPendingEmitIndex!++;
|
||||
@@ -573,16 +625,23 @@ namespace ts {
|
||||
/**
|
||||
* Returns the result with affected file
|
||||
*/
|
||||
function toAffectedFileResult<T>(state: BuilderProgramState, result: T, affected: SourceFile | Program, isPendingEmit?: boolean, isBuildInfoEmit?: boolean): AffectedFileResult<T> {
|
||||
doneWithAffectedFile(state, affected, isPendingEmit, isBuildInfoEmit);
|
||||
function toAffectedFileResult<T>(state: BuilderProgramState, result: T, affected: SourceFile | Program): AffectedFileResult<T> {
|
||||
doneWithAffectedFile(state, affected);
|
||||
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);
|
||||
function toAffectedFileEmitResult(
|
||||
state: BuilderProgramState,
|
||||
result: EmitResult,
|
||||
affected: SourceFile | Program,
|
||||
emitKind: BuilderFileEmit,
|
||||
isPendingEmit?: boolean,
|
||||
isBuildInfoEmit?: boolean
|
||||
): AffectedFileResult<EmitResult> {
|
||||
doneWithAffectedFile(state, affected, emitKind, isPendingEmit, isBuildInfoEmit);
|
||||
return { result, affected };
|
||||
}
|
||||
|
||||
@@ -623,7 +682,7 @@ namespace ts {
|
||||
function getProgramBuildInfo(state: Readonly<ReusableBuilderProgramState>, getCanonicalFileName: GetCanonicalFileName): ProgramBuildInfo | undefined {
|
||||
if (state.compilerOptions.outFile || state.compilerOptions.out) return undefined;
|
||||
const currentDirectory = Debug.assertDefined(state.program).getCurrentDirectory();
|
||||
const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(getOutputPathForBuildInfo(state.compilerOptions)!, currentDirectory));
|
||||
const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(getTsBuildInfoEmitOutputFilePath(state.compilerOptions)!, currentDirectory));
|
||||
const fileInfos: MapLike<BuilderState.FileInfo> = {};
|
||||
state.fileInfos.forEach((value, key) => {
|
||||
const signature = state.currentAffectedFilesSignatures && state.currentAffectedFilesSignatures.get(key);
|
||||
@@ -849,11 +908,12 @@ namespace ts {
|
||||
*/
|
||||
function emitNextAffectedFile(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): AffectedFileResult<EmitResult> {
|
||||
let affected = getNextAffectedFile(state, cancellationToken, computeHash);
|
||||
let emitKind = BuilderFileEmit.Full;
|
||||
let isPendingEmitFile = false;
|
||||
if (!affected) {
|
||||
if (!state.compilerOptions.out && !state.compilerOptions.outFile) {
|
||||
affected = getNextAffectedFilePendingEmit(state);
|
||||
if (!affected) {
|
||||
const pendingAffectedFile = getNextAffectedFilePendingEmit(state);
|
||||
if (!pendingAffectedFile) {
|
||||
if (state.emittedBuildInfo) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -865,10 +925,12 @@ namespace ts {
|
||||
// Otherwise just affected file
|
||||
affected.emitBuildInfo(writeFile || maybeBind(host, host.writeFile), cancellationToken),
|
||||
affected,
|
||||
/*emitKind*/ BuilderFileEmit.Full,
|
||||
/*isPendingEmitFile*/ false,
|
||||
/*isBuildInfoEmit*/ true
|
||||
);
|
||||
}
|
||||
({ affectedFile: affected, emitKind } = pendingAffectedFile);
|
||||
isPendingEmitFile = true;
|
||||
}
|
||||
else {
|
||||
@@ -886,10 +948,17 @@ namespace ts {
|
||||
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),
|
||||
Debug.assertDefined(state.program).emit(
|
||||
affected === state.program ? undefined : affected as SourceFile,
|
||||
writeFile || maybeBind(host, host.writeFile),
|
||||
cancellationToken,
|
||||
emitOnlyDtsFiles || emitKind === BuilderFileEmit.DtsOnly,
|
||||
customTransformers
|
||||
),
|
||||
affected,
|
||||
emitKind,
|
||||
isPendingEmitFile,
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -953,7 +1022,7 @@ namespace ts {
|
||||
|
||||
// Add file to affected file pending emit to handle for later emit time
|
||||
if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
|
||||
addToAffectedFilesPendingEmit(state, [(affected as SourceFile).path]);
|
||||
addToAffectedFilesPendingEmit(state, (affected as SourceFile).path, BuilderFileEmit.Full);
|
||||
}
|
||||
|
||||
// Get diagnostics for the affected file if its not ignored
|
||||
@@ -1006,8 +1075,14 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function addToAffectedFilesPendingEmit(state: BuilderProgramState, affectedFilesPendingEmit: readonly Path[]) {
|
||||
state.affectedFilesPendingEmit = concatenate(state.affectedFilesPendingEmit, affectedFilesPendingEmit);
|
||||
function addToAffectedFilesPendingEmit(state: BuilderProgramState, affectedFilePendingEmit: Path, kind: BuilderFileEmit) {
|
||||
if (!state.affectedFilesPendingEmit) state.affectedFilesPendingEmit = [];
|
||||
if (!state.affectedFilesPendingEmitKind) state.affectedFilesPendingEmitKind = createMap();
|
||||
|
||||
const existingKind = state.affectedFilesPendingEmitKind.get(affectedFilePendingEmit);
|
||||
state.affectedFilesPendingEmit.push(affectedFilePendingEmit);
|
||||
state.affectedFilesPendingEmitKind.set(affectedFilePendingEmit, existingKind || kind);
|
||||
|
||||
// affectedFilesPendingEmitIndex === undefined
|
||||
// - means the emit state.affectedFilesPendingEmit was undefined before adding current affected files
|
||||
// so start from 0 as array would be affectedFilesPendingEmit
|
||||
|
||||
@@ -15,9 +15,9 @@ namespace ts {
|
||||
/*@internal*/
|
||||
namespace ts {
|
||||
export function getFileEmitOutput(program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean,
|
||||
cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): EmitOutput {
|
||||
cancellationToken?: CancellationToken, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitOutput {
|
||||
const outputFiles: OutputFile[] = [];
|
||||
const emitResult = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
|
||||
const emitResult = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers, forceDtsEmit);
|
||||
return { outputFiles, emitSkipped: emitResult.emitSkipped, exportedModulesFromDeclarationEmit: emitResult.exportedModulesFromDeclarationEmit };
|
||||
|
||||
function writeFile(fileName: string, text: string, writeByteOrderMark: boolean) {
|
||||
@@ -344,7 +344,14 @@ namespace ts.BuilderState {
|
||||
}
|
||||
}
|
||||
else {
|
||||
const emitOutput = getFileEmitOutput(programOfThisState, sourceFile, /*emitOnlyDtsFiles*/ true, cancellationToken);
|
||||
const emitOutput = getFileEmitOutput(
|
||||
programOfThisState,
|
||||
sourceFile,
|
||||
/*emitOnlyDtsFiles*/ true,
|
||||
cancellationToken,
|
||||
/*customTransformers*/ undefined,
|
||||
/*forceDtsEmit*/ true
|
||||
);
|
||||
const firstDts = emitOutput.outputFiles &&
|
||||
programOfThisState.getCompilerOptions().declarationMap ?
|
||||
emitOutput.outputFiles.length > 1 ? emitOutput.outputFiles[1] : undefined :
|
||||
@@ -459,7 +466,7 @@ namespace ts.BuilderState {
|
||||
/**
|
||||
* Gets the files referenced by the the file path
|
||||
*/
|
||||
function getReferencedByPaths(state: Readonly<BuilderState>, referencedFilePath: Path) {
|
||||
export function getReferencedByPaths(state: Readonly<BuilderState>, referencedFilePath: Path) {
|
||||
return arrayFrom(mapDefinedIterator(state.referencedMap!.entries(), ([filePath, referencesInFile]) =>
|
||||
referencesInFile.has(referencedFilePath) ? filePath as Path : undefined
|
||||
));
|
||||
|
||||
+673
-253
File diff suppressed because it is too large
Load Diff
@@ -772,6 +772,12 @@ namespace ts {
|
||||
category: Diagnostics.Advanced_Options,
|
||||
description: Diagnostics.Disable_size_limitations_on_JavaScript_projects
|
||||
},
|
||||
{
|
||||
name: "disableSourceOfProjectReferenceRedirect",
|
||||
type: "boolean",
|
||||
category: Diagnostics.Advanced_Options,
|
||||
description: Diagnostics.Disable_use_of_source_files_instead_of_declaration_files_from_referenced_projects
|
||||
},
|
||||
{
|
||||
name: "noImplicitUseStrict",
|
||||
type: "boolean",
|
||||
|
||||
@@ -1654,7 +1654,7 @@ namespace ts {
|
||||
*/
|
||||
export function compose<T>(...args: ((t: T) => T)[]): (t: T) => T;
|
||||
export function compose<T>(a: (t: T) => T, b: (t: T) => T, c: (t: T) => T, d: (t: T) => T, e: (t: T) => T): (t: T) => T {
|
||||
if (e) {
|
||||
if (!!e) {
|
||||
const args: ((t: T) => T)[] = [];
|
||||
for (let i = 0; i < arguments.length; i++) {
|
||||
args[i] = arguments[i];
|
||||
|
||||
@@ -1035,6 +1035,39 @@
|
||||
"category": "Error",
|
||||
"code": 1356
|
||||
},
|
||||
"An enum member name must be followed by a ',', '=', or '}'.": {
|
||||
"category": "Error",
|
||||
"code": 1357
|
||||
},
|
||||
|
||||
"The types of '{0}' are incompatible between these types.": {
|
||||
"category": "Error",
|
||||
"code": 2200
|
||||
},
|
||||
"The types returned by '{0}' are incompatible between these types.": {
|
||||
"category": "Error",
|
||||
"code": 2201
|
||||
},
|
||||
"Call signature return types '{0}' and '{1}' are incompatible.": {
|
||||
"category": "Error",
|
||||
"code": 2202,
|
||||
"elidedInCompatabilityPyramid": true
|
||||
},
|
||||
"Construct signature return types '{0}' and '{1}' are incompatible.": {
|
||||
"category": "Error",
|
||||
"code": 2203,
|
||||
"elidedInCompatabilityPyramid": true
|
||||
},
|
||||
"Call signatures with no arguments have incompatible return types '{0}' and '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 2204,
|
||||
"elidedInCompatabilityPyramid": true
|
||||
},
|
||||
"Construct signatures with no arguments have incompatible return types '{0}' and '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 2205,
|
||||
"elidedInCompatabilityPyramid": true
|
||||
},
|
||||
|
||||
"Duplicate identifier '{0}'.": {
|
||||
"category": "Error",
|
||||
@@ -2689,7 +2722,10 @@
|
||||
"category": "Error",
|
||||
"code": 2773
|
||||
},
|
||||
|
||||
"This condition will always return true since the function is always defined. Did you mean to call it instead?": {
|
||||
"category": "Error",
|
||||
"code": 2774
|
||||
},
|
||||
"Import declaration '{0}' is using private name '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 4000
|
||||
@@ -4003,6 +4039,10 @@
|
||||
"category": "Message",
|
||||
"code": 6220
|
||||
},
|
||||
"Disable use of source files instead of declaration files from referenced projects.": {
|
||||
"category": "Message",
|
||||
"code": 6221
|
||||
},
|
||||
|
||||
"Projects to reference": {
|
||||
"category": "Message",
|
||||
@@ -4643,6 +4683,10 @@
|
||||
"category": "Suggestion",
|
||||
"code": 80007
|
||||
},
|
||||
"Numeric literals with absolute values equal to 2^53 or greater are too large to be represented accurately as integers.": {
|
||||
"category": "Suggestion",
|
||||
"code": 80008
|
||||
},
|
||||
|
||||
"Add missing 'super()' call": {
|
||||
"category": "Message",
|
||||
@@ -5124,6 +5168,18 @@
|
||||
"category": "Message",
|
||||
"code": 95090
|
||||
},
|
||||
"Convert to a bigint numeric literal": {
|
||||
"category": "Message",
|
||||
"code": 95091
|
||||
},
|
||||
"Convert all to bigint numeric literals": {
|
||||
"category": "Message",
|
||||
"code": 95092
|
||||
},
|
||||
"Convert 'const' to 'let'": {
|
||||
"category": "Message",
|
||||
"code": 95093
|
||||
},
|
||||
|
||||
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
|
||||
"category": "Error",
|
||||
|
||||
+85
-40
@@ -20,7 +20,7 @@ namespace ts {
|
||||
export function forEachEmittedFile<T>(
|
||||
host: EmitHost, action: (emitFileNames: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle | undefined) => T,
|
||||
sourceFilesOrTargetSourceFile?: readonly SourceFile[] | SourceFile,
|
||||
emitOnlyDtsFiles = false,
|
||||
forceDtsEmit = false,
|
||||
onlyBuildInfo?: boolean,
|
||||
includeBuildInfo?: boolean) {
|
||||
const sourceFiles = isArray(sourceFilesOrTargetSourceFile) ? sourceFilesOrTargetSourceFile : getSourceFilesToEmit(host, sourceFilesOrTargetSourceFile);
|
||||
@@ -29,7 +29,7 @@ namespace ts {
|
||||
const prepends = host.getPrependNodes();
|
||||
if (sourceFiles.length || prepends.length) {
|
||||
const bundle = createBundle(sourceFiles, prepends);
|
||||
const result = action(getOutputPathsFor(bundle, host, emitOnlyDtsFiles), bundle);
|
||||
const result = action(getOutputPathsFor(bundle, host, forceDtsEmit), bundle);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
@@ -38,21 +38,20 @@ namespace ts {
|
||||
else {
|
||||
if (!onlyBuildInfo) {
|
||||
for (const sourceFile of sourceFiles) {
|
||||
const result = action(getOutputPathsFor(sourceFile, host, emitOnlyDtsFiles), sourceFile);
|
||||
const result = action(getOutputPathsFor(sourceFile, host, forceDtsEmit), sourceFile);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (includeBuildInfo) {
|
||||
const buildInfoPath = getOutputPathForBuildInfo(host.getCompilerOptions());
|
||||
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(host.getCompilerOptions());
|
||||
if (buildInfoPath) return action({ buildInfoPath }, /*sourceFileOrBundle*/ undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
export function getOutputPathForBuildInfo(options: CompilerOptions) {
|
||||
export function getTsBuildInfoEmitOutputFilePath(options: CompilerOptions) {
|
||||
const configFile = options.configFilePath;
|
||||
if (!isIncrementalCompilation(options)) return undefined;
|
||||
if (options.tsBuildInfoFile) return options.tsBuildInfoFile;
|
||||
@@ -80,7 +79,7 @@ namespace ts {
|
||||
const sourceMapFilePath = jsFilePath && getSourceMapFilePath(jsFilePath, options);
|
||||
const declarationFilePath = (forceDtsPaths || getEmitDeclarations(options)) ? removeFileExtension(outPath) + Extension.Dts : undefined;
|
||||
const declarationMapPath = declarationFilePath && getAreDeclarationMapsEnabled(options) ? declarationFilePath + ".map" : undefined;
|
||||
const buildInfoPath = getOutputPathForBuildInfo(options);
|
||||
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(options);
|
||||
return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath };
|
||||
}
|
||||
|
||||
@@ -170,38 +169,71 @@ namespace ts {
|
||||
undefined;
|
||||
}
|
||||
|
||||
function createAddOutput() {
|
||||
let outputs: string[] | undefined;
|
||||
return { addOutput, getOutputs };
|
||||
function addOutput(path: string | undefined) {
|
||||
if (path) {
|
||||
(outputs || (outputs = [])).push(path);
|
||||
}
|
||||
}
|
||||
function getOutputs(): readonly string[] {
|
||||
return outputs || emptyArray;
|
||||
}
|
||||
}
|
||||
|
||||
function getSingleOutputFileNames(configFile: ParsedCommandLine, addOutput: ReturnType<typeof createAddOutput>["addOutput"]) {
|
||||
const { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath } = getOutputPathsForBundle(configFile.options, /*forceDtsPaths*/ false);
|
||||
addOutput(jsFilePath);
|
||||
addOutput(sourceMapFilePath);
|
||||
addOutput(declarationFilePath);
|
||||
addOutput(declarationMapPath);
|
||||
addOutput(buildInfoPath);
|
||||
}
|
||||
|
||||
function getOwnOutputFileNames(configFile: ParsedCommandLine, inputFileName: string, ignoreCase: boolean, addOutput: ReturnType<typeof createAddOutput>["addOutput"]) {
|
||||
if (fileExtensionIs(inputFileName, Extension.Dts)) return;
|
||||
const js = getOutputJSFileName(inputFileName, configFile, ignoreCase);
|
||||
addOutput(js);
|
||||
if (fileExtensionIs(inputFileName, Extension.Json)) return;
|
||||
if (js && configFile.options.sourceMap) {
|
||||
addOutput(`${js}.map`);
|
||||
}
|
||||
if (getEmitDeclarations(configFile.options) && hasTSFileExtension(inputFileName)) {
|
||||
const dts = getOutputDeclarationFileName(inputFileName, configFile, ignoreCase);
|
||||
addOutput(dts);
|
||||
if (configFile.options.declarationMap) {
|
||||
addOutput(`${dts}.map`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
export function getAllProjectOutputs(configFile: ParsedCommandLine, ignoreCase: boolean): readonly string[] {
|
||||
let outputs: string[] | undefined;
|
||||
const addOutput = (path: string | undefined) => path && (outputs || (outputs = [])).push(path);
|
||||
const { addOutput, getOutputs } = createAddOutput();
|
||||
if (configFile.options.outFile || configFile.options.out) {
|
||||
const { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath } = getOutputPathsForBundle(configFile.options, /*forceDtsPaths*/ false);
|
||||
addOutput(jsFilePath);
|
||||
addOutput(sourceMapFilePath);
|
||||
addOutput(declarationFilePath);
|
||||
addOutput(declarationMapPath);
|
||||
addOutput(buildInfoPath);
|
||||
getSingleOutputFileNames(configFile, addOutput);
|
||||
}
|
||||
else {
|
||||
for (const inputFileName of configFile.fileNames) {
|
||||
if (fileExtensionIs(inputFileName, Extension.Dts)) continue;
|
||||
const js = getOutputJSFileName(inputFileName, configFile, ignoreCase);
|
||||
addOutput(js);
|
||||
if (fileExtensionIs(inputFileName, Extension.Json)) continue;
|
||||
if (js && configFile.options.sourceMap) {
|
||||
addOutput(`${js}.map`);
|
||||
}
|
||||
if (getEmitDeclarations(configFile.options) && hasTSFileExtension(inputFileName)) {
|
||||
const dts = getOutputDeclarationFileName(inputFileName, configFile, ignoreCase);
|
||||
addOutput(dts);
|
||||
if (configFile.options.declarationMap) {
|
||||
addOutput(`${dts}.map`);
|
||||
}
|
||||
}
|
||||
getOwnOutputFileNames(configFile, inputFileName, ignoreCase, addOutput);
|
||||
}
|
||||
addOutput(getOutputPathForBuildInfo(configFile.options));
|
||||
addOutput(getTsBuildInfoEmitOutputFilePath(configFile.options));
|
||||
}
|
||||
return outputs || emptyArray;
|
||||
return getOutputs();
|
||||
}
|
||||
|
||||
export function getOutputFileNames(commandLine: ParsedCommandLine, inputFileName: string, ignoreCase: boolean): readonly string[] {
|
||||
inputFileName = normalizePath(inputFileName);
|
||||
Debug.assert(contains(commandLine.fileNames, inputFileName), `Expected fileName to be present in command line`);
|
||||
const { addOutput, getOutputs } = createAddOutput();
|
||||
if (commandLine.options.outFile || commandLine.options.out) {
|
||||
getSingleOutputFileNames(commandLine, addOutput);
|
||||
}
|
||||
else {
|
||||
getOwnOutputFileNames(commandLine, inputFileName, ignoreCase, addOutput);
|
||||
}
|
||||
return getOutputs();
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
@@ -220,14 +252,14 @@ namespace ts {
|
||||
return getOutputDeclarationFileName(inputFileName, configFile, ignoreCase);
|
||||
}
|
||||
}
|
||||
const buildInfoPath = getOutputPathForBuildInfo(configFile.options);
|
||||
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(configFile.options);
|
||||
if (buildInfoPath) return buildInfoPath;
|
||||
return Debug.fail(`project ${configFile.options.configFilePath} expected to have at least one output`);
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
// targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature
|
||||
export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile | undefined, { scriptTransformers, declarationTransformers }: EmitTransformers, emitOnlyDtsFiles?: boolean, onlyBuildInfo?: boolean): EmitResult {
|
||||
export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile | undefined, { scriptTransformers, declarationTransformers }: EmitTransformers, emitOnlyDtsFiles?: boolean, onlyBuildInfo?: boolean, forceDtsEmit?: boolean): EmitResult {
|
||||
const compilerOptions = host.getCompilerOptions();
|
||||
const sourceMapDataList: SourceMapEmitResult[] | undefined = (compilerOptions.sourceMap || compilerOptions.inlineSourceMap || getAreDeclarationMapsEnabled(compilerOptions)) ? [] : undefined;
|
||||
const emittedFilesList: string[] | undefined = compilerOptions.listEmittedFiles ? [] : undefined;
|
||||
@@ -241,7 +273,14 @@ namespace ts {
|
||||
|
||||
// Emit each output file
|
||||
enter();
|
||||
forEachEmittedFile(host, emitSourceFileOrBundle, getSourceFilesToEmit(host, targetSourceFile), emitOnlyDtsFiles, onlyBuildInfo, !targetSourceFile);
|
||||
forEachEmittedFile(
|
||||
host,
|
||||
emitSourceFileOrBundle,
|
||||
getSourceFilesToEmit(host, targetSourceFile),
|
||||
forceDtsEmit,
|
||||
onlyBuildInfo,
|
||||
!targetSourceFile
|
||||
);
|
||||
exit();
|
||||
|
||||
|
||||
@@ -400,7 +439,7 @@ namespace ts {
|
||||
});
|
||||
const declBlocked = (!!declarationTransform.diagnostics && !!declarationTransform.diagnostics.length) || !!host.isEmitBlocked(declarationFilePath) || !!compilerOptions.noEmit;
|
||||
emitSkipped = emitSkipped || declBlocked;
|
||||
if (!declBlocked || emitOnlyDtsFiles) {
|
||||
if (!declBlocked || forceDtsEmit) {
|
||||
Debug.assert(declarationTransform.transformed.length === 1, "Should only see one output from the decl transform");
|
||||
printSourceFileOrBundle(
|
||||
declarationFilePath,
|
||||
@@ -415,7 +454,7 @@ namespace ts {
|
||||
// Explicitly do not passthru either `inline` option
|
||||
}
|
||||
);
|
||||
if (emitOnlyDtsFiles && declarationTransform.transformed[0].kind === SyntaxKind.SourceFile) {
|
||||
if (forceDtsEmit && declarationTransform.transformed[0].kind === SyntaxKind.SourceFile) {
|
||||
const sourceFile = declarationTransform.transformed[0];
|
||||
exportedModulesFromDeclarationEmit = sourceFile.exportedModulesFromDeclarationEmit;
|
||||
}
|
||||
@@ -1907,11 +1946,17 @@ namespace ts {
|
||||
//
|
||||
|
||||
function emitTypePredicate(node: TypePredicateNode) {
|
||||
if (node.assertsModifier) {
|
||||
emit(node.assertsModifier);
|
||||
writeSpace();
|
||||
}
|
||||
emit(node.parameterName);
|
||||
writeSpace();
|
||||
writeKeyword("is");
|
||||
writeSpace();
|
||||
emit(node.type);
|
||||
if (node.type) {
|
||||
writeSpace();
|
||||
writeKeyword("is");
|
||||
writeSpace();
|
||||
emit(node.type);
|
||||
}
|
||||
}
|
||||
|
||||
function emitTypeReference(node: TypeReferenceNode) {
|
||||
|
||||
+12
-2
@@ -669,16 +669,26 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function createTypePredicateNode(parameterName: Identifier | ThisTypeNode | string, type: TypeNode) {
|
||||
return createTypePredicateNodeWithModifier(/*assertsModifier*/ undefined, parameterName, type);
|
||||
}
|
||||
|
||||
export function createTypePredicateNodeWithModifier(assertsModifier: AssertsToken | undefined, parameterName: Identifier | ThisTypeNode | string, type: TypeNode | undefined) {
|
||||
const node = createSynthesizedNode(SyntaxKind.TypePredicate) as TypePredicateNode;
|
||||
node.assertsModifier = assertsModifier;
|
||||
node.parameterName = asName(parameterName);
|
||||
node.type = type;
|
||||
return node;
|
||||
}
|
||||
|
||||
export function updateTypePredicateNode(node: TypePredicateNode, parameterName: Identifier | ThisTypeNode, type: TypeNode) {
|
||||
return node.parameterName !== parameterName
|
||||
return updateTypePredicateNodeWithModifier(node, node.assertsModifier, parameterName, type);
|
||||
}
|
||||
|
||||
export function updateTypePredicateNodeWithModifier(node: TypePredicateNode, assertsModifier: AssertsToken | undefined, parameterName: Identifier | ThisTypeNode, type: TypeNode | undefined) {
|
||||
return node.assertsModifier !== assertsModifier
|
||||
|| node.parameterName !== parameterName
|
||||
|| node.type !== type
|
||||
? updateNode(createTypePredicateNode(parameterName, type), node)
|
||||
? updateNode(createTypePredicateNodeWithModifier(assertsModifier, parameterName, type), node)
|
||||
: node;
|
||||
}
|
||||
|
||||
|
||||
+23
-2
@@ -165,7 +165,8 @@ namespace ts {
|
||||
return visitNode(cbNode, (<TypeReferenceNode>node).typeName) ||
|
||||
visitNodes(cbNode, cbNodes, (<TypeReferenceNode>node).typeArguments);
|
||||
case SyntaxKind.TypePredicate:
|
||||
return visitNode(cbNode, (<TypePredicateNode>node).parameterName) ||
|
||||
return visitNode(cbNode, (<TypePredicateNode>node).assertsModifier) ||
|
||||
visitNode(cbNode, (<TypePredicateNode>node).parameterName) ||
|
||||
visitNode(cbNode, (<TypePredicateNode>node).type);
|
||||
case SyntaxKind.TypeQuery:
|
||||
return visitNode(cbNode, (<TypeQueryNode>node).exprName);
|
||||
@@ -770,7 +771,11 @@ namespace ts {
|
||||
fixupParentReferences(sourceFile);
|
||||
}
|
||||
|
||||
sourceFile.nodeCount = nodeCount;
|
||||
sourceFile.identifierCount = identifierCount;
|
||||
sourceFile.identifiers = identifiers;
|
||||
sourceFile.parseDiagnostics = parseDiagnostics;
|
||||
|
||||
const result = sourceFile as JsonSourceFile;
|
||||
clearState();
|
||||
return result;
|
||||
@@ -2121,7 +2126,7 @@ namespace ts {
|
||||
|
||||
// We didn't get a comma, and the list wasn't terminated, explicitly parse
|
||||
// out a comma so we give a good error message.
|
||||
parseExpected(SyntaxKind.CommaToken);
|
||||
parseExpected(SyntaxKind.CommaToken, getExpectedCommaDiagnostic(kind));
|
||||
|
||||
// If the token was a semicolon, and the caller allows that, then skip it and
|
||||
// continue. This ensures we get back on track and don't result in tons of
|
||||
@@ -2164,6 +2169,10 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
function getExpectedCommaDiagnostic(kind: ParsingContext) {
|
||||
return kind === ParsingContext.EnumMembers ? Diagnostics.An_enum_member_name_must_be_followed_by_a_or : undefined;
|
||||
}
|
||||
|
||||
interface MissingList<T extends Node> extends NodeArray<T> {
|
||||
isMissingList: true;
|
||||
}
|
||||
@@ -3033,6 +3042,8 @@ namespace ts {
|
||||
return parseParenthesizedType();
|
||||
case SyntaxKind.ImportKeyword:
|
||||
return parseImportType();
|
||||
case SyntaxKind.AssertsKeyword:
|
||||
return lookAhead(nextTokenIsIdentifierOrKeywordOnSameLine) ? parseAssertsTypePredicate() : parseTypeReference();
|
||||
default:
|
||||
return parseTypeReference();
|
||||
}
|
||||
@@ -3073,6 +3084,7 @@ namespace ts {
|
||||
case SyntaxKind.DotDotDotToken:
|
||||
case SyntaxKind.InferKeyword:
|
||||
case SyntaxKind.ImportKeyword:
|
||||
case SyntaxKind.AssertsKeyword:
|
||||
return true;
|
||||
case SyntaxKind.FunctionKeyword:
|
||||
return !inStartOfParameter;
|
||||
@@ -3249,6 +3261,7 @@ namespace ts {
|
||||
const type = parseType();
|
||||
if (typePredicateVariable) {
|
||||
const node = <TypePredicateNode>createNode(SyntaxKind.TypePredicate, typePredicateVariable.pos);
|
||||
node.assertsModifier = undefined;
|
||||
node.parameterName = typePredicateVariable;
|
||||
node.type = type;
|
||||
return finishNode(node);
|
||||
@@ -3266,6 +3279,14 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function parseAssertsTypePredicate(): TypeNode {
|
||||
const node = <TypePredicateNode>createNode(SyntaxKind.TypePredicate);
|
||||
node.assertsModifier = parseExpectedToken(SyntaxKind.AssertsKeyword);
|
||||
node.parameterName = token() === SyntaxKind.ThisKeyword ? parseThisTypeNode() : parseIdentifier();
|
||||
node.type = parseOptional(SyntaxKind.IsKeyword) ? parseType() : undefined;
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
function parseType(): TypeNode {
|
||||
// The rules about 'yield' only apply to actual code/expression contexts. They don't
|
||||
// apply to 'type' contexts. So we disable these parameters here before moving on.
|
||||
|
||||
+113
-42
@@ -817,6 +817,8 @@ namespace ts {
|
||||
let resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined;
|
||||
let projectReferenceRedirects: Map<ResolvedProjectReference | false> | undefined;
|
||||
let mapFromFileToProjectReferenceRedirects: Map<Path> | undefined;
|
||||
let mapFromToProjectReferenceRedirectSource: Map<SourceOfProjectReferenceRedirect> | undefined;
|
||||
const useSourceOfProjectReferenceRedirect = !!host.useSourceOfProjectReferenceRedirect && host.useSourceOfProjectReferenceRedirect();
|
||||
|
||||
const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options);
|
||||
// We set `structuralIsReused` to `undefined` because `tryReuseStructureFromOldProgram` calls `tryReuseStructureFromOldProgram` which checks
|
||||
@@ -831,17 +833,32 @@ namespace ts {
|
||||
if (!resolvedProjectReferences) {
|
||||
resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile);
|
||||
}
|
||||
if (host.setResolvedProjectReferenceCallbacks) {
|
||||
host.setResolvedProjectReferenceCallbacks({
|
||||
getSourceOfProjectReferenceRedirect,
|
||||
forEachResolvedProjectReference
|
||||
});
|
||||
}
|
||||
if (rootNames.length) {
|
||||
for (const parsedRef of resolvedProjectReferences) {
|
||||
if (!parsedRef) continue;
|
||||
const out = parsedRef.commandLine.options.outFile || parsedRef.commandLine.options.out;
|
||||
if (out) {
|
||||
processSourceFile(changeExtension(out, ".d.ts"), /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
|
||||
if (useSourceOfProjectReferenceRedirect) {
|
||||
if (out || getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) {
|
||||
for (const fileName of parsedRef.commandLine.fileNames) {
|
||||
processSourceFile(fileName, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) {
|
||||
for (const fileName of parsedRef.commandLine.fileNames) {
|
||||
if (!fileExtensionIs(fileName, Extension.Dts) && hasTSFileExtension(fileName)) {
|
||||
processSourceFile(getOutputDeclarationFileName(fileName, parsedRef.commandLine, !host.useCaseSensitiveFileNames()), /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
|
||||
else {
|
||||
if (out) {
|
||||
processSourceFile(changeExtension(out, ".d.ts"), /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
|
||||
}
|
||||
else if (getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) {
|
||||
for (const fileName of parsedRef.commandLine.fileNames) {
|
||||
if (!fileExtensionIs(fileName, Extension.Dts) && hasTSFileExtension(fileName)) {
|
||||
processSourceFile(getOutputDeclarationFileName(fileName, parsedRef.commandLine, !host.useCaseSensitiveFileNames()), /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -955,6 +972,7 @@ namespace ts {
|
||||
getResolvedProjectReferenceToRedirect,
|
||||
getResolvedProjectReferenceByPath,
|
||||
forEachResolvedProjectReference,
|
||||
isSourceOfProjectReferenceRedirect,
|
||||
emitBuildInfo
|
||||
};
|
||||
|
||||
@@ -987,9 +1005,15 @@ namespace ts {
|
||||
return ts.toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
}
|
||||
|
||||
function isValidSourceFileForEmit(file: SourceFile) {
|
||||
// source file is allowed to be emitted and its not source of project reference redirect
|
||||
return sourceFileMayBeEmitted(file, options, isSourceFileFromExternalLibrary, getResolvedProjectReferenceToRedirect) &&
|
||||
!isSourceOfProjectReferenceRedirect(file.fileName);
|
||||
}
|
||||
|
||||
function getCommonSourceDirectory() {
|
||||
if (commonSourceDirectory === undefined) {
|
||||
const emittedFiles = filter(files, file => sourceFileMayBeEmitted(file, options, isSourceFileFromExternalLibrary, getResolvedProjectReferenceToRedirect));
|
||||
const emittedFiles = filter(files, file => isValidSourceFileForEmit(file));
|
||||
if (options.rootDir && checkSourceFilesBelongToPath(emittedFiles, options.rootDir)) {
|
||||
// If a rootDir is specified use it as the commonSourceDirectory
|
||||
commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir, currentDirectory);
|
||||
@@ -1220,6 +1244,12 @@ namespace ts {
|
||||
}
|
||||
if (projectReferences) {
|
||||
resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile);
|
||||
if (host.setResolvedProjectReferenceCallbacks) {
|
||||
host.setResolvedProjectReferenceCallbacks({
|
||||
getSourceOfProjectReferenceRedirect,
|
||||
forEachResolvedProjectReference
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// check if program source files has changed in the way that can affect structure of the program
|
||||
@@ -1359,18 +1389,16 @@ namespace ts {
|
||||
// try to verify results of module resolution
|
||||
for (const { oldFile: oldSourceFile, newFile: newSourceFile } of modifiedSourceFiles) {
|
||||
const newSourceFilePath = getNormalizedAbsolutePath(newSourceFile.originalFileName, currentDirectory);
|
||||
if (resolveModuleNamesWorker) {
|
||||
const moduleNames = getModuleNames(newSourceFile);
|
||||
const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFilePath, newSourceFile);
|
||||
// ensure that module resolution results are still correct
|
||||
const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo);
|
||||
if (resolutionsChanged) {
|
||||
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||
newSourceFile.resolvedModules = zipToMap(moduleNames, resolutions);
|
||||
}
|
||||
else {
|
||||
newSourceFile.resolvedModules = oldSourceFile.resolvedModules;
|
||||
}
|
||||
const moduleNames = getModuleNames(newSourceFile);
|
||||
const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFilePath, newSourceFile);
|
||||
// ensure that module resolution results are still correct
|
||||
const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo);
|
||||
if (resolutionsChanged) {
|
||||
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||
newSourceFile.resolvedModules = zipToMap(moduleNames, resolutions);
|
||||
}
|
||||
else {
|
||||
newSourceFile.resolvedModules = oldSourceFile.resolvedModules;
|
||||
}
|
||||
if (resolveTypeReferenceDirectiveNamesWorker) {
|
||||
// We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
|
||||
@@ -1403,6 +1431,13 @@ namespace ts {
|
||||
for (const newSourceFile of newSourceFiles) {
|
||||
const filePath = newSourceFile.path;
|
||||
addFileToFilesByName(newSourceFile, filePath, newSourceFile.resolvedPath);
|
||||
if (useSourceOfProjectReferenceRedirect) {
|
||||
const redirectProject = getProjectReferenceRedirectProject(newSourceFile.fileName);
|
||||
if (redirectProject && !(redirectProject.commandLine.options.outFile || redirectProject.commandLine.options.out)) {
|
||||
const redirect = getProjectReferenceOutputName(redirectProject, newSourceFile.fileName);
|
||||
addFileToFilesByName(newSourceFile, toPath(redirect), /*redirectedPath*/ undefined);
|
||||
}
|
||||
}
|
||||
// Set the file as found during node modules search if it was found that way in old progra,
|
||||
if (oldProgram.isSourceFileFromExternalLibrary(oldProgram.getSourceFileByPath(newSourceFile.resolvedPath)!)) {
|
||||
sourceFilesFoundSearchingNodeModules.set(filePath, true);
|
||||
@@ -1530,18 +1565,18 @@ namespace ts {
|
||||
return noDiagnosticsTypeChecker || (noDiagnosticsTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ false));
|
||||
}
|
||||
|
||||
function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, transformers?: CustomTransformers): EmitResult {
|
||||
return runWithCancellationToken(() => emitWorker(program, sourceFile, writeFileCallback, cancellationToken, emitOnlyDtsFiles, transformers));
|
||||
function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, transformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult {
|
||||
return runWithCancellationToken(() => emitWorker(program, sourceFile, writeFileCallback, cancellationToken, emitOnlyDtsFiles, transformers, forceDtsEmit));
|
||||
}
|
||||
|
||||
function isEmitBlocked(emitFileName: string): boolean {
|
||||
return hasEmitBlockingDiagnostics.has(toPath(emitFileName));
|
||||
}
|
||||
|
||||
function emitWorker(program: Program, sourceFile: SourceFile | undefined, writeFileCallback: WriteFileCallback | undefined, cancellationToken: CancellationToken | undefined, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult {
|
||||
function emitWorker(program: Program, sourceFile: SourceFile | undefined, writeFileCallback: WriteFileCallback | undefined, cancellationToken: CancellationToken | undefined, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult {
|
||||
let declarationDiagnostics: readonly Diagnostic[] = [];
|
||||
|
||||
if (!emitOnlyDtsFiles) {
|
||||
if (!forceDtsEmit) {
|
||||
if (options.noEmit) {
|
||||
return { diagnostics: declarationDiagnostics, sourceMaps: undefined, emittedFiles: undefined, emitSkipped: true };
|
||||
}
|
||||
@@ -1590,6 +1625,8 @@ namespace ts {
|
||||
sourceFile,
|
||||
getTransformers(options, customTransformers, emitOnlyDtsFiles),
|
||||
emitOnlyDtsFiles,
|
||||
/*onlyBuildInfo*/ false,
|
||||
forceDtsEmit
|
||||
);
|
||||
|
||||
performance.mark("afterEmit");
|
||||
@@ -1680,7 +1717,7 @@ namespace ts {
|
||||
|
||||
function getSemanticDiagnosticsForFileNoCache(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] | undefined {
|
||||
return runWithCancellationToken(() => {
|
||||
if (skipTypeChecking(sourceFile, options)) {
|
||||
if (skipTypeChecking(sourceFile, options, program)) {
|
||||
return emptyArray;
|
||||
}
|
||||
|
||||
@@ -1757,13 +1794,12 @@ namespace ts {
|
||||
switch (parent.kind) {
|
||||
case SyntaxKind.Parameter:
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
if ((<ParameterDeclaration | PropertyDeclaration>parent).questionToken === node) {
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
if ((<ParameterDeclaration | PropertyDeclaration | MethodDeclaration>parent).questionToken === node) {
|
||||
diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_can_only_be_used_in_a_ts_file, "?"));
|
||||
return;
|
||||
}
|
||||
// falls through
|
||||
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
// falls through
|
||||
case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.GetAccessor:
|
||||
@@ -1833,7 +1869,6 @@ namespace ts {
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.ClassExpression:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
@@ -1841,7 +1876,7 @@ namespace ts {
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
// Check type parameters
|
||||
if (nodes === (<ClassLikeDeclaration | FunctionLikeDeclaration>parent).typeParameters) {
|
||||
if (nodes === (<DeclarationWithTypeParameterChildren>parent).typeParameters) {
|
||||
diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.type_parameter_declarations_can_only_be_used_in_a_ts_file));
|
||||
return;
|
||||
}
|
||||
@@ -1849,8 +1884,8 @@ namespace ts {
|
||||
|
||||
case SyntaxKind.VariableStatement:
|
||||
// Check modifiers
|
||||
if (nodes === (<ClassDeclaration | FunctionLikeDeclaration | VariableStatement>parent).modifiers) {
|
||||
return checkModifiers(<NodeArray<Modifier>>nodes, parent.kind === SyntaxKind.VariableStatement);
|
||||
if (nodes === parent.modifiers) {
|
||||
return checkModifiers(parent.modifiers, parent.kind === SyntaxKind.VariableStatement);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
@@ -1876,8 +1911,9 @@ namespace ts {
|
||||
case SyntaxKind.ExpressionWithTypeArguments:
|
||||
case SyntaxKind.JsxSelfClosingElement:
|
||||
case SyntaxKind.JsxOpeningElement:
|
||||
case SyntaxKind.TaggedTemplateExpression:
|
||||
// Check type arguments
|
||||
if (nodes === (<CallExpression | NewExpression | ExpressionWithTypeArguments | JsxOpeningLikeElement>parent).typeArguments) {
|
||||
if (nodes === (<NodeWithTypeArguments>parent).typeArguments) {
|
||||
diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.type_arguments_can_only_be_used_in_a_ts_file));
|
||||
return;
|
||||
}
|
||||
@@ -2233,6 +2269,16 @@ namespace ts {
|
||||
|
||||
// Get source file from normalized fileName
|
||||
function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, refFile: RefFile | undefined, packageId: PackageId | undefined): SourceFile | undefined {
|
||||
if (useSourceOfProjectReferenceRedirect) {
|
||||
const source = getSourceOfProjectReferenceRedirect(fileName);
|
||||
if (source) {
|
||||
const file = isString(source) ?
|
||||
findSourceFile(source, toPath(source), isDefaultLib, ignoreNoDefaultLib, refFile, packageId) :
|
||||
undefined;
|
||||
if (file) addFileToFilesByName(file, path, /*redirectedPath*/ undefined);
|
||||
return file;
|
||||
}
|
||||
}
|
||||
const originalFileName = fileName;
|
||||
if (filesByName.has(path)) {
|
||||
const file = filesByName.get(path);
|
||||
@@ -2281,7 +2327,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
let redirectedPath: Path | undefined;
|
||||
if (refFile) {
|
||||
if (refFile && !useSourceOfProjectReferenceRedirect) {
|
||||
const redirectProject = getProjectReferenceRedirectProject(fileName);
|
||||
if (redirectProject) {
|
||||
if (redirectProject.commandLine.options.outFile || redirectProject.commandLine.options.out) {
|
||||
@@ -2450,6 +2496,36 @@ namespace ts {
|
||||
});
|
||||
}
|
||||
|
||||
function getSourceOfProjectReferenceRedirect(file: string) {
|
||||
if (!isDeclarationFileName(file)) return undefined;
|
||||
if (mapFromToProjectReferenceRedirectSource === undefined) {
|
||||
mapFromToProjectReferenceRedirectSource = createMap();
|
||||
forEachResolvedProjectReference(resolvedRef => {
|
||||
if (resolvedRef) {
|
||||
const out = resolvedRef.commandLine.options.outFile || resolvedRef.commandLine.options.out;
|
||||
if (out) {
|
||||
// Dont know which source file it means so return true?
|
||||
const outputDts = changeExtension(out, Extension.Dts);
|
||||
mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), true);
|
||||
}
|
||||
else {
|
||||
forEach(resolvedRef.commandLine.fileNames, fileName => {
|
||||
if (!fileExtensionIs(fileName, Extension.Dts) && hasTSFileExtension(fileName)) {
|
||||
const outputDts = getOutputDeclarationFileName(fileName, resolvedRef.commandLine, host.useCaseSensitiveFileNames());
|
||||
mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), fileName);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return mapFromToProjectReferenceRedirectSource.get(toPath(file));
|
||||
}
|
||||
|
||||
function isSourceOfProjectReferenceRedirect(fileName: string) {
|
||||
return useSourceOfProjectReferenceRedirect && !!getResolvedProjectReferenceToRedirect(fileName);
|
||||
}
|
||||
|
||||
function forEachProjectReference<T>(
|
||||
projectReferences: readonly ProjectReference[] | undefined,
|
||||
resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined,
|
||||
@@ -2806,10 +2882,6 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (options.isolatedModules) {
|
||||
if (getEmitDeclarations(options)) {
|
||||
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, getEmitDeclarationOptionName(options), "isolatedModules");
|
||||
}
|
||||
|
||||
if (options.out) {
|
||||
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "out", "isolatedModules");
|
||||
}
|
||||
@@ -2861,8 +2933,7 @@ namespace ts {
|
||||
const rootPaths = arrayToSet(rootNames, toPath);
|
||||
for (const file of files) {
|
||||
// Ignore file that is not emitted
|
||||
if (!sourceFileMayBeEmitted(file, options, isSourceFileFromExternalLibrary, getResolvedProjectReferenceToRedirect)) continue;
|
||||
if (!rootPaths.has(file.path)) {
|
||||
if (isValidSourceFileForEmit(file) && !rootPaths.has(file.path)) {
|
||||
addProgramDiagnosticAtRefPath(
|
||||
file,
|
||||
rootPaths,
|
||||
@@ -3107,7 +3178,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function verifyProjectReferences() {
|
||||
const buildInfoPath = !options.noEmit && !options.suppressOutputPathCheck ? getOutputPathForBuildInfo(options) : undefined;
|
||||
const buildInfoPath = !options.noEmit && !options.suppressOutputPathCheck ? getTsBuildInfoEmitOutputFilePath(options) : undefined;
|
||||
forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, index, parent) => {
|
||||
const ref = (parent ? parent.commandLine.projectReferences : projectReferences)![index];
|
||||
const parentFile = parent && parent.sourceFile as JsonSourceFile;
|
||||
@@ -3134,7 +3205,7 @@ namespace ts {
|
||||
createDiagnosticForReference(parentFile, index, Diagnostics.Cannot_prepend_project_0_because_it_does_not_have_outFile_set, ref.path);
|
||||
}
|
||||
}
|
||||
if (!parent && buildInfoPath && buildInfoPath === getOutputPathForBuildInfo(options)) {
|
||||
if (!parent && buildInfoPath && buildInfoPath === getTsBuildInfoEmitOutputFilePath(options)) {
|
||||
createDiagnosticForReference(parentFile, index, Diagnostics.Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1, buildInfoPath, ref.path);
|
||||
hasEmitBlockingDiagnostics.set(toPath(buildInfoPath), true);
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ namespace ts {
|
||||
abstract: SyntaxKind.AbstractKeyword,
|
||||
any: SyntaxKind.AnyKeyword,
|
||||
as: SyntaxKind.AsKeyword,
|
||||
asserts: SyntaxKind.AssertsKeyword,
|
||||
bigint: SyntaxKind.BigIntKeyword,
|
||||
boolean: SyntaxKind.BooleanKeyword,
|
||||
break: SyntaxKind.BreakKeyword,
|
||||
|
||||
+30
-19
@@ -522,6 +522,33 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function recursiveCreateDirectory(directoryPath: string, sys: System) {
|
||||
const basePath = getDirectoryPath(directoryPath);
|
||||
const shouldCreateParent = basePath !== "" && directoryPath !== basePath && !sys.directoryExists(basePath);
|
||||
if (shouldCreateParent) {
|
||||
recursiveCreateDirectory(basePath, sys);
|
||||
}
|
||||
if (shouldCreateParent || !sys.directoryExists(directoryPath)) {
|
||||
sys.createDirectory(directoryPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* patch writefile to create folder before writing the file
|
||||
*/
|
||||
/*@internal*/
|
||||
export function patchWriteFileEnsuringDirectory(sys: System) {
|
||||
// patch writefile to create folder before writing the file
|
||||
const originalWriteFile = sys.writeFile;
|
||||
sys.writeFile = (path, data, writeBom) => {
|
||||
const directoryPath = getDirectoryPath(normalizeSlashes(path));
|
||||
if (directoryPath && !sys.directoryExists(directoryPath)) {
|
||||
recursiveCreateDirectory(directoryPath, sys);
|
||||
}
|
||||
originalWriteFile.call(sys, path, data, writeBom);
|
||||
};
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
export type BufferEncoding = "ascii" | "utf8" | "utf-8" | "utf16le" | "ucs2" | "ucs-2" | "base64" | "latin1" | "binary" | "hex";
|
||||
|
||||
@@ -651,6 +678,8 @@ namespace ts {
|
||||
base64decode?(input: string): string;
|
||||
base64encode?(input: string): string;
|
||||
/*@internal*/ bufferFrom?(input: string, encoding?: string): Buffer;
|
||||
// For testing
|
||||
/*@internal*/ now?(): Date;
|
||||
}
|
||||
|
||||
export interface FileWatcher {
|
||||
@@ -1365,17 +1394,6 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
function recursiveCreateDirectory(directoryPath: string, sys: System) {
|
||||
const basePath = getDirectoryPath(directoryPath);
|
||||
const shouldCreateParent = basePath !== "" && directoryPath !== basePath && !sys.directoryExists(basePath);
|
||||
if (shouldCreateParent) {
|
||||
recursiveCreateDirectory(basePath, sys);
|
||||
}
|
||||
if (shouldCreateParent || !sys.directoryExists(directoryPath)) {
|
||||
sys.createDirectory(directoryPath);
|
||||
}
|
||||
}
|
||||
|
||||
let sys: System | undefined;
|
||||
if (typeof ChakraHost !== "undefined") {
|
||||
sys = getChakraSystem();
|
||||
@@ -1387,14 +1405,7 @@ namespace ts {
|
||||
}
|
||||
if (sys) {
|
||||
// patch writefile to create folder before writing the file
|
||||
const originalWriteFile = sys.writeFile;
|
||||
sys.writeFile = (path, data, writeBom) => {
|
||||
const directoryPath = getDirectoryPath(normalizeSlashes(path));
|
||||
if (directoryPath && !sys!.directoryExists(directoryPath)) {
|
||||
recursiveCreateDirectory(directoryPath, sys!);
|
||||
}
|
||||
originalWriteFile.call(sys, path, data, writeBom);
|
||||
};
|
||||
patchWriteFileEnsuringDirectory(sys);
|
||||
}
|
||||
return sys!;
|
||||
})();
|
||||
|
||||
@@ -337,7 +337,7 @@ namespace ts {
|
||||
if (constructor && constructor.body) {
|
||||
let parameterPropertyDeclarationCount = 0;
|
||||
for (let i = indexOfFirstStatement; i < constructor.body.statements.length; i++) {
|
||||
if (isParameterPropertyDeclaration(getOriginalNode(constructor.body.statements[i]))) {
|
||||
if (isParameterPropertyDeclaration(getOriginalNode(constructor.body.statements[i]), constructor)) {
|
||||
parameterPropertyDeclarationCount++;
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -135,7 +135,7 @@ namespace ts {
|
||||
return getReturnTypeVisibilityError;
|
||||
}
|
||||
else if (isParameter(node)) {
|
||||
if (isParameterPropertyDeclaration(node) && hasModifier(node.parent, ModifierFlags.Private)) {
|
||||
if (isParameterPropertyDeclaration(node, node.parent) && hasModifier(node.parent, ModifierFlags.Private)) {
|
||||
return getVariableDeclarationTypeVisibilityError;
|
||||
}
|
||||
return getParameterDeclarationTypeVisibilityError;
|
||||
|
||||
@@ -2871,7 +2871,7 @@ namespace ts {
|
||||
function tryEnterOrLeaveBlock(operationIndex: number): void {
|
||||
if (blocks) {
|
||||
for (; blockIndex < blockActions!.length && blockOffsets![blockIndex] <= operationIndex; blockIndex++) {
|
||||
const block = blocks[blockIndex];
|
||||
const block: CodeBlock = blocks[blockIndex];
|
||||
const blockAction = blockActions![blockIndex];
|
||||
switch (block.kind) {
|
||||
case CodeBlockKind.Exception:
|
||||
|
||||
@@ -895,7 +895,7 @@ namespace ts {
|
||||
const members: ClassElement[] = [];
|
||||
const constructor = getFirstConstructorWithBody(node);
|
||||
const parametersWithPropertyAssignments = constructor &&
|
||||
filter(constructor.parameters, isParameterPropertyDeclaration);
|
||||
filter(constructor.parameters, p => isParameterPropertyDeclaration(p, constructor));
|
||||
|
||||
if (parametersWithPropertyAssignments) {
|
||||
for (const parameter of parametersWithPropertyAssignments) {
|
||||
@@ -1907,7 +1907,7 @@ namespace ts {
|
||||
|
||||
function transformConstructorBody(body: Block, constructor: ConstructorDeclaration) {
|
||||
const parametersWithPropertyAssignments = constructor &&
|
||||
filter(constructor.parameters, isParameterPropertyDeclaration);
|
||||
filter(constructor.parameters, p => isParameterPropertyDeclaration(p, constructor));
|
||||
if (!some(parametersWithPropertyAssignments)) {
|
||||
return visitFunctionBody(body, visitor, context);
|
||||
}
|
||||
|
||||
@@ -308,6 +308,7 @@ namespace ts {
|
||||
/*@internal*/ getUpToDateStatusOfProject(project: string): UpToDateStatus;
|
||||
/*@internal*/ invalidateProject(configFilePath: ResolvedConfigFilePath, reloadLevel?: ConfigFileProgramReloadLevel): void;
|
||||
/*@internal*/ buildNextInvalidatedProject(): void;
|
||||
/*@internal*/ getAllParsedConfigs(): readonly ParsedCommandLine[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -315,7 +316,7 @@ namespace ts {
|
||||
*/
|
||||
export function createBuilderStatusReporter(system: System, pretty?: boolean): DiagnosticReporter {
|
||||
return diagnostic => {
|
||||
let output = pretty ? `[${formatColorAndReset(new Date().toLocaleTimeString(), ForegroundColorEscapeSequences.Grey)}] ` : `${new Date().toLocaleTimeString()} - `;
|
||||
let output = pretty ? `[${formatColorAndReset(getLocaleTimeString(system), ForegroundColorEscapeSequences.Grey)}] ` : `${getLocaleTimeString(system)} - `;
|
||||
output += `${flattenDiagnosticMessageText(diagnostic.messageText, system.newLine)}${system.newLine + system.newLine}`;
|
||||
system.write(output);
|
||||
};
|
||||
@@ -1620,7 +1621,7 @@ namespace ts {
|
||||
|
||||
if (!state.buildInfoChecked.has(resolvedPath)) {
|
||||
state.buildInfoChecked.set(resolvedPath, true);
|
||||
const buildInfoPath = getOutputPathForBuildInfo(project.options);
|
||||
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(project.options);
|
||||
if (buildInfoPath) {
|
||||
const value = state.readFileWithCache(buildInfoPath);
|
||||
const buildInfo = value && getBuildInfo(value);
|
||||
@@ -2047,6 +2048,10 @@ namespace ts {
|
||||
},
|
||||
invalidateProject: (configFilePath, reloadLevel) => invalidateProject(state, configFilePath, reloadLevel || ConfigFileProgramReloadLevel.None),
|
||||
buildNextInvalidatedProject: () => buildNextInvalidatedProject(state),
|
||||
getAllParsedConfigs: () => arrayFrom(mapDefinedIterator(
|
||||
state.configFileCache.values(),
|
||||
config => isParsedCommandLine(config) ? config : undefined
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+97
-30
@@ -32,6 +32,7 @@ namespace ts {
|
||||
| SyntaxKind.AbstractKeyword
|
||||
| SyntaxKind.AnyKeyword
|
||||
| SyntaxKind.AsKeyword
|
||||
| SyntaxKind.AssertsKeyword
|
||||
| SyntaxKind.BigIntKeyword
|
||||
| SyntaxKind.BooleanKeyword
|
||||
| SyntaxKind.BreakKeyword
|
||||
@@ -250,6 +251,7 @@ namespace ts {
|
||||
// Contextual keywords
|
||||
AbstractKeyword,
|
||||
AsKeyword,
|
||||
AssertsKeyword,
|
||||
AnyKeyword,
|
||||
AsyncKeyword,
|
||||
AwaitKeyword,
|
||||
@@ -361,8 +363,8 @@ namespace ts {
|
||||
SemicolonClassElement,
|
||||
// Element
|
||||
Block,
|
||||
VariableStatement,
|
||||
EmptyStatement,
|
||||
VariableStatement,
|
||||
ExpressionStatement,
|
||||
IfStatement,
|
||||
DoStatement,
|
||||
@@ -512,6 +514,8 @@ namespace ts {
|
||||
LastTemplateToken = TemplateTail,
|
||||
FirstBinaryOperator = LessThanToken,
|
||||
LastBinaryOperator = CaretEqualsToken,
|
||||
FirstStatement = VariableStatement,
|
||||
LastStatement = DebuggerStatement,
|
||||
FirstNode = QualifiedName,
|
||||
FirstJSDocNode = JSDocTypeExpression,
|
||||
LastJSDocNode = JSDocPropertyTag,
|
||||
@@ -614,9 +618,13 @@ namespace ts {
|
||||
|
||||
/* @internal */
|
||||
export const enum RelationComparisonResult {
|
||||
Succeeded = 1, // Should be truthy
|
||||
Failed = 2,
|
||||
FailedAndReported = 3
|
||||
Succeeded = 1 << 0, // Should be truthy
|
||||
Failed = 1 << 1,
|
||||
Reported = 1 << 2,
|
||||
|
||||
ReportsUnmeasurable = 1 << 3,
|
||||
ReportsUnreliable = 1 << 4,
|
||||
ReportsMask = ReportsUnmeasurable | ReportsUnreliable
|
||||
}
|
||||
|
||||
export interface Node extends TextRange {
|
||||
@@ -736,6 +744,7 @@ namespace ts {
|
||||
export type AwaitKeywordToken = Token<SyntaxKind.AwaitKeyword>;
|
||||
export type PlusToken = Token<SyntaxKind.PlusToken>;
|
||||
export type MinusToken = Token<SyntaxKind.MinusToken>;
|
||||
export type AssertsToken = Token<SyntaxKind.AssertsKeyword>;
|
||||
|
||||
export type Modifier
|
||||
= Token<SyntaxKind.AbstractKeyword>
|
||||
@@ -1037,6 +1046,7 @@ namespace ts {
|
||||
questionToken?: QuestionToken;
|
||||
exclamationToken?: ExclamationToken;
|
||||
body?: Block | Expression;
|
||||
/* @internal */ endFlowNode?: FlowNode;
|
||||
}
|
||||
|
||||
export type FunctionLikeDeclaration =
|
||||
@@ -1180,8 +1190,9 @@ namespace ts {
|
||||
export interface TypePredicateNode extends TypeNode {
|
||||
kind: SyntaxKind.TypePredicate;
|
||||
parent: SignatureDeclaration | JSDocTypeExpression;
|
||||
assertsModifier?: AssertsToken;
|
||||
parameterName: Identifier | ThisTypeNode;
|
||||
type: TypeNode;
|
||||
type?: TypeNode;
|
||||
}
|
||||
|
||||
export interface TypeQueryNode extends TypeNode {
|
||||
@@ -2570,16 +2581,33 @@ namespace ts {
|
||||
FalseCondition = 1 << 6, // Condition known to be false
|
||||
SwitchClause = 1 << 7, // Switch statement clause
|
||||
ArrayMutation = 1 << 8, // Potential array mutation
|
||||
Referenced = 1 << 9, // Referenced as antecedent once
|
||||
Shared = 1 << 10, // Referenced as antecedent more than once
|
||||
PreFinally = 1 << 11, // Injected edge that links pre-finally label and pre-try flow
|
||||
AfterFinally = 1 << 12, // Injected edge that links post-finally flow with the rest of the graph
|
||||
Call = 1 << 9, // Potential assertion call
|
||||
Referenced = 1 << 10, // Referenced as antecedent once
|
||||
Shared = 1 << 11, // Referenced as antecedent more than once
|
||||
PreFinally = 1 << 12, // Injected edge that links pre-finally label and pre-try flow
|
||||
AfterFinally = 1 << 13, // Injected edge that links post-finally flow with the rest of the graph
|
||||
/** @internal */
|
||||
Cached = 1 << 13, // Indicates that at least one cross-call cache entry exists for this node, even if not a loop participant
|
||||
Cached = 1 << 14, // Indicates that at least one cross-call cache entry exists for this node, even if not a loop participant
|
||||
Label = BranchLabel | LoopLabel,
|
||||
Condition = TrueCondition | FalseCondition
|
||||
}
|
||||
|
||||
export type FlowNode =
|
||||
| AfterFinallyFlow
|
||||
| PreFinallyFlow
|
||||
| FlowStart
|
||||
| FlowLabel
|
||||
| FlowAssignment
|
||||
| FlowCall
|
||||
| FlowCondition
|
||||
| FlowSwitchClause
|
||||
| FlowArrayMutation;
|
||||
|
||||
export interface FlowNodeBase {
|
||||
flags: FlowFlags;
|
||||
id?: number; // Node id used by flow type cache in checker
|
||||
}
|
||||
|
||||
export interface FlowLock {
|
||||
locked?: boolean;
|
||||
}
|
||||
@@ -2593,18 +2621,11 @@ namespace ts {
|
||||
lock: FlowLock;
|
||||
}
|
||||
|
||||
export type FlowNode =
|
||||
| AfterFinallyFlow | PreFinallyFlow | FlowStart | FlowLabel | FlowAssignment | FlowCondition | FlowSwitchClause | FlowArrayMutation;
|
||||
export interface FlowNodeBase {
|
||||
flags: FlowFlags;
|
||||
id?: number; // Node id used by flow type cache in checker
|
||||
}
|
||||
|
||||
// FlowStart represents the start of a control flow. For a function expression or arrow
|
||||
// function, the container property references the function (which in turn has a flowNode
|
||||
// function, the node property references the function (which in turn has a flowNode
|
||||
// property for the containing control flow).
|
||||
export interface FlowStart extends FlowNodeBase {
|
||||
container?: FunctionExpression | ArrowFunction | MethodDeclaration;
|
||||
node?: FunctionExpression | ArrowFunction | MethodDeclaration;
|
||||
}
|
||||
|
||||
// FlowLabel represents a junction with multiple possible preceding control flows.
|
||||
@@ -2619,10 +2640,15 @@ namespace ts {
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
|
||||
export interface FlowCall extends FlowNodeBase {
|
||||
node: CallExpression;
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
|
||||
// FlowCondition represents a condition that is known to be true or false at the
|
||||
// node's location in the control flow.
|
||||
export interface FlowCondition extends FlowNodeBase {
|
||||
expression: Expression;
|
||||
node: Expression;
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
|
||||
@@ -2981,6 +3007,8 @@ namespace ts {
|
||||
* will be invoked when writing the JavaScript and declaration files.
|
||||
*/
|
||||
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult;
|
||||
/*@internal*/
|
||||
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult; // eslint-disable-line @typescript-eslint/unified-signatures
|
||||
|
||||
getOptionsDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[];
|
||||
getGlobalDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[];
|
||||
@@ -3005,11 +3033,11 @@ namespace ts {
|
||||
|
||||
/* @internal */ getClassifiableNames(): UnderscoreEscapedMap<true>;
|
||||
|
||||
/* @internal */ getNodeCount(): number;
|
||||
/* @internal */ getIdentifierCount(): number;
|
||||
/* @internal */ getSymbolCount(): number;
|
||||
/* @internal */ getTypeCount(): number;
|
||||
/* @internal */ getRelationCacheSizes(): { assignable: number, identity: number, subtype: number };
|
||||
getNodeCount(): number;
|
||||
getIdentifierCount(): number;
|
||||
getSymbolCount(): number;
|
||||
getTypeCount(): number;
|
||||
getRelationCacheSizes(): { assignable: number, identity: number, subtype: number };
|
||||
|
||||
/* @internal */ getFileProcessingDiagnostics(): DiagnosticCollection;
|
||||
/* @internal */ getResolvedTypeReferenceDirectives(): Map<ResolvedTypeReferenceDirective | undefined>;
|
||||
@@ -3037,6 +3065,7 @@ namespace ts {
|
||||
/*@internal*/ getResolvedProjectReferenceToRedirect(fileName: string): ResolvedProjectReference | undefined;
|
||||
/*@internal*/ forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference | undefined, resolvedProjectReferencePath: Path) => T | undefined): T | undefined;
|
||||
/*@internal*/ getResolvedProjectReferenceByPath(projectReferencePath: Path): ResolvedProjectReference | undefined;
|
||||
/*@internal*/ isSourceOfProjectReferenceRedirect(fileName: string): boolean;
|
||||
/*@internal*/ getProgramBuildInfo?(): ProgramBuildInfo | undefined;
|
||||
/*@internal*/ emitBuildInfo(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult;
|
||||
}
|
||||
@@ -3137,6 +3166,7 @@ namespace ts {
|
||||
getSourceFile(fileName: string): SourceFile | undefined;
|
||||
getResolvedTypeReferenceDirectives(): ReadonlyMap<ResolvedTypeReferenceDirective | undefined>;
|
||||
getProjectReferenceRedirect(fileName: string): string | undefined;
|
||||
isSourceOfProjectReferenceRedirect(fileName: string): boolean;
|
||||
|
||||
readonly redirectTargetsMap: RedirectTargetsMap;
|
||||
}
|
||||
@@ -3287,6 +3317,7 @@ namespace ts {
|
||||
/* @internal */ getElementTypeOfArrayType(arrayType: Type): Type | undefined;
|
||||
/* @internal */ createPromiseType(type: Type): Type;
|
||||
|
||||
/* @internal */ isTypeAssignableTo(source: Type, target: Type): boolean;
|
||||
/* @internal */ createAnonymousType(symbol: Symbol, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexInfo: IndexInfo | undefined, numberIndexInfo: IndexInfo | undefined): Type;
|
||||
/* @internal */ createSignature(
|
||||
declaration: SignatureDeclaration,
|
||||
@@ -3525,25 +3556,45 @@ namespace ts {
|
||||
|
||||
export const enum TypePredicateKind {
|
||||
This,
|
||||
Identifier
|
||||
Identifier,
|
||||
AssertsThis,
|
||||
AssertsIdentifier
|
||||
}
|
||||
|
||||
export interface TypePredicateBase {
|
||||
kind: TypePredicateKind;
|
||||
type: Type;
|
||||
type: Type | undefined;
|
||||
}
|
||||
|
||||
export interface ThisTypePredicate extends TypePredicateBase {
|
||||
kind: TypePredicateKind.This;
|
||||
parameterName: undefined;
|
||||
parameterIndex: undefined;
|
||||
type: Type;
|
||||
}
|
||||
|
||||
export interface IdentifierTypePredicate extends TypePredicateBase {
|
||||
kind: TypePredicateKind.Identifier;
|
||||
parameterName: string;
|
||||
parameterIndex: number;
|
||||
type: Type;
|
||||
}
|
||||
|
||||
export type TypePredicate = IdentifierTypePredicate | ThisTypePredicate;
|
||||
export interface AssertsThisTypePredicate extends TypePredicateBase {
|
||||
kind: TypePredicateKind.AssertsThis;
|
||||
parameterName: undefined;
|
||||
parameterIndex: undefined;
|
||||
type: Type | undefined;
|
||||
}
|
||||
|
||||
export interface AssertsIdentifierTypePredicate extends TypePredicateBase {
|
||||
kind: TypePredicateKind.AssertsIdentifier;
|
||||
parameterName: string;
|
||||
parameterIndex: number;
|
||||
type: Type | undefined;
|
||||
}
|
||||
|
||||
export type TypePredicate = ThisTypePredicate | IdentifierTypePredicate | AssertsThisTypePredicate | AssertsIdentifierTypePredicate;
|
||||
|
||||
/* @internal */
|
||||
export type AnyImportSyntax = ImportDeclaration | ImportEqualsDeclaration;
|
||||
@@ -3961,7 +4012,7 @@ namespace ts {
|
||||
resolvedSignature?: Signature; // Cached signature of signature node or call expression
|
||||
resolvedSymbol?: Symbol; // Cached name resolution result
|
||||
resolvedIndexInfo?: IndexInfo; // Cached indexing info resolution result
|
||||
maybeTypePredicate?: boolean; // Cached check whether call expression might reference a type predicate
|
||||
effectsSignature?: Signature; // Signature with possible control flow effects
|
||||
enumMemberValue?: string | number; // Constant value of enum member
|
||||
isVisible?: boolean; // Is this node visible
|
||||
containsArgumentsReference?: boolean; // Whether a function-like declaration contains an 'arguments' reference
|
||||
@@ -3976,6 +4027,7 @@ namespace ts {
|
||||
contextFreeType?: Type; // Cached context-free type used by the first pass of inference; used when a function's return is partially contextually sensitive
|
||||
deferredNodes?: Map<Node>; // Set of nodes whose checking has been deferred
|
||||
capturedBlockScopeBindings?: Symbol[]; // Block-scoped bindings captured beneath this part of an IterationStatement
|
||||
isExhaustive?: boolean; // Is node an exhaustive switch statement
|
||||
}
|
||||
|
||||
export const enum TypeFlags {
|
||||
@@ -4200,7 +4252,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
// Object type or intersection of object types
|
||||
export type BaseType = ObjectType | IntersectionType;
|
||||
export type BaseType = ObjectType | IntersectionType | TypeVariable; // Also `any` and `object`
|
||||
|
||||
export interface InterfaceTypeWithDeclaredMembers extends InterfaceType {
|
||||
declaredProperties: Symbol[]; // Declared members
|
||||
@@ -4629,6 +4681,8 @@ namespace ts {
|
||||
code: number;
|
||||
message: string;
|
||||
reportsUnnecessary?: {};
|
||||
/* @internal */
|
||||
elidedInCompatabilityPyramid?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4723,6 +4777,7 @@ namespace ts {
|
||||
/* @internal */ diagnostics?: boolean;
|
||||
/* @internal */ extendedDiagnostics?: boolean;
|
||||
disableSizeLimit?: boolean;
|
||||
disableSourceOfProjectReferenceRedirect?: boolean;
|
||||
downlevelIteration?: boolean;
|
||||
emitBOM?: boolean;
|
||||
emitDecoratorMetadata?: boolean;
|
||||
@@ -5251,11 +5306,23 @@ namespace ts {
|
||||
/* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean;
|
||||
createHash?(data: string): string;
|
||||
getParsedCommandLine?(fileName: string): ParsedCommandLine | undefined;
|
||||
/* @internal */ setResolvedProjectReferenceCallbacks?(callbacks: ResolvedProjectReferenceCallbacks): void;
|
||||
/* @internal */ useSourceOfProjectReferenceRedirect?(): boolean;
|
||||
|
||||
// TODO: later handle this in better way in builder host instead once the api for tsbuild finalizes and doesn't use compilerHost as base
|
||||
/*@internal*/createDirectory?(directory: string): void;
|
||||
}
|
||||
|
||||
/** true if --out otherwise source file name */
|
||||
/*@internal*/
|
||||
export type SourceOfProjectReferenceRedirect = string | true;
|
||||
|
||||
/*@internal*/
|
||||
export interface ResolvedProjectReferenceCallbacks {
|
||||
getSourceOfProjectReferenceRedirect(fileName: string): SourceOfProjectReferenceRedirect | undefined;
|
||||
forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference | undefined, resolvedProjectReferencePath: Path) => T | undefined): T | undefined;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export const enum TransformFlags {
|
||||
None = 0,
|
||||
|
||||
@@ -975,6 +975,8 @@ namespace ts {
|
||||
return getSpanOfTokenAtPosition(sourceFile, node.pos);
|
||||
}
|
||||
|
||||
Debug.assert(!isJSDoc(errorNode));
|
||||
|
||||
const isMissing = nodeIsMissing(errorNode);
|
||||
const pos = isMissing || isJsxText(node)
|
||||
? errorNode.pos
|
||||
@@ -1007,7 +1009,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function isDeclarationReadonly(declaration: Declaration): boolean {
|
||||
return !!(getCombinedModifierFlags(declaration) & ModifierFlags.Readonly && !isParameterPropertyDeclaration(declaration));
|
||||
return !!(getCombinedModifierFlags(declaration) & ModifierFlags.Readonly && !isParameterPropertyDeclaration(declaration, declaration.parent));
|
||||
}
|
||||
|
||||
export function isVarConst(node: VariableDeclaration | VariableDeclarationList): boolean {
|
||||
@@ -4981,8 +4983,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
export type ParameterPropertyDeclaration = ParameterDeclaration & { parent: ConstructorDeclaration, name: Identifier };
|
||||
export function isParameterPropertyDeclaration(node: Node): node is ParameterPropertyDeclaration {
|
||||
return hasModifier(node, ModifierFlags.ParameterPropertyModifier) && node.parent.kind === SyntaxKind.Constructor;
|
||||
export function isParameterPropertyDeclaration(node: Node, parent: Node): node is ParameterPropertyDeclaration {
|
||||
return hasModifier(node, ModifierFlags.ParameterPropertyModifier) && parent.kind === SyntaxKind.Constructor;
|
||||
}
|
||||
|
||||
export function isEmptyBindingPattern(node: BindingName): node is BindingPattern {
|
||||
@@ -8711,11 +8713,16 @@ namespace ts {
|
||||
return { pos: typeParameters.pos - 1, end: typeParameters.end + 1 };
|
||||
}
|
||||
|
||||
export function skipTypeChecking(sourceFile: SourceFile, options: CompilerOptions) {
|
||||
export interface HostWithIsSourceOfProjectReferenceRedirect {
|
||||
isSourceOfProjectReferenceRedirect(fileName: string): boolean;
|
||||
}
|
||||
export function skipTypeChecking(sourceFile: SourceFile, options: CompilerOptions, host: HostWithIsSourceOfProjectReferenceRedirect) {
|
||||
// If skipLibCheck is enabled, skip reporting errors if file is a declaration file.
|
||||
// If skipDefaultLibCheck is enabled, skip reporting errors if file contains a
|
||||
// '/// <reference no-default-lib="true"/>' directive.
|
||||
return options.skipLibCheck && sourceFile.isDeclarationFile || options.skipDefaultLibCheck && sourceFile.hasNoDefaultLib;
|
||||
return (options.skipLibCheck && sourceFile.isDeclarationFile ||
|
||||
options.skipDefaultLibCheck && sourceFile.hasNoDefaultLib) ||
|
||||
host.isSourceOfProjectReferenceRedirect(sourceFile.fileName);
|
||||
}
|
||||
|
||||
export function isJsonEqual(a: unknown, b: unknown): boolean {
|
||||
|
||||
@@ -333,7 +333,8 @@ namespace ts {
|
||||
|
||||
// Types
|
||||
case SyntaxKind.TypePredicate:
|
||||
return updateTypePredicateNode(<TypePredicateNode>node,
|
||||
return updateTypePredicateNodeWithModifier(<TypePredicateNode>node,
|
||||
visitNode((<TypePredicateNode>node).assertsModifier, visitor),
|
||||
visitNode((<TypePredicateNode>node).parameterName, visitor),
|
||||
visitNode((<TypePredicateNode>node).type, visitor, isTypeNode));
|
||||
|
||||
|
||||
+12
-3
@@ -54,6 +54,15 @@ namespace ts {
|
||||
: newLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get locale specific time based on whether we are in test mode
|
||||
*/
|
||||
export function getLocaleTimeString(system: System) {
|
||||
return !system.now ?
|
||||
new Date().toLocaleTimeString() :
|
||||
system.now().toLocaleTimeString("en-US", { timeZone: "UTC" });
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a function that reports watch status by writing to the system and handles the formating of the diagnostic
|
||||
*/
|
||||
@@ -61,7 +70,7 @@ namespace ts {
|
||||
return pretty ?
|
||||
(diagnostic, newLine, options) => {
|
||||
clearScreenIfNotWatchingForFileChanges(system, diagnostic, options);
|
||||
let output = `[${formatColorAndReset(new Date().toLocaleTimeString(), ForegroundColorEscapeSequences.Grey)}] `;
|
||||
let output = `[${formatColorAndReset(getLocaleTimeString(system), ForegroundColorEscapeSequences.Grey)}] `;
|
||||
output += `${flattenDiagnosticMessageText(diagnostic.messageText, system.newLine)}${newLine + newLine}`;
|
||||
system.write(output);
|
||||
} :
|
||||
@@ -72,7 +81,7 @@ namespace ts {
|
||||
output += newLine;
|
||||
}
|
||||
|
||||
output += `${new Date().toLocaleTimeString()} - `;
|
||||
output += `${getLocaleTimeString(system)} - `;
|
||||
output += `${flattenDiagnosticMessageText(diagnostic.messageText, system.newLine)}${getPlainDiagnosticFollowingNewLines(diagnostic, newLine)}`;
|
||||
|
||||
system.write(output);
|
||||
@@ -447,7 +456,7 @@ namespace ts {
|
||||
}
|
||||
export function readBuilderProgram(compilerOptions: CompilerOptions, host: ReadBuildProgramHost) {
|
||||
if (compilerOptions.out || compilerOptions.outFile) return undefined;
|
||||
const buildInfoPath = getOutputPathForBuildInfo(compilerOptions);
|
||||
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(compilerOptions);
|
||||
if (!buildInfoPath) return undefined;
|
||||
const content = host.readFile(buildInfoPath);
|
||||
if (!content) return undefined;
|
||||
|
||||
+37
-21
@@ -140,7 +140,7 @@ namespace fakes {
|
||||
}
|
||||
|
||||
public createHash(data: string): string {
|
||||
return data;
|
||||
return `${ts.generateDjb2Hash(data)}-${data}`;
|
||||
}
|
||||
|
||||
public realpath(path: string) {
|
||||
@@ -164,6 +164,10 @@ namespace fakes {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
now() {
|
||||
return new Date(this.vfs.time());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -520,39 +524,51 @@ ${indentText}${text}`;
|
||||
|
||||
export const version = "FakeTSVersion";
|
||||
|
||||
export class SolutionBuilderHost extends CompilerHost implements ts.SolutionBuilderHost<ts.BuilderProgram> {
|
||||
createProgram: ts.CreateProgram<ts.BuilderProgram>;
|
||||
|
||||
constructor(sys: System | vfs.FileSystem, options?: ts.CompilerOptions, setParentNodes?: boolean, createProgram?: ts.CreateProgram<ts.BuilderProgram>) {
|
||||
super(sys, options, setParentNodes);
|
||||
this.createProgram = createProgram || ts.createEmitAndSemanticDiagnosticsBuilderProgram;
|
||||
}
|
||||
|
||||
readFile(path: string) {
|
||||
const value = super.readFile(path);
|
||||
export function patchSolutionBuilderHost(host: ts.SolutionBuilderHost<ts.BuilderProgram>, sys: System) {
|
||||
const originalReadFile = host.readFile;
|
||||
host.readFile = (path, encoding) => {
|
||||
const value = originalReadFile.call(host, path, encoding);
|
||||
if (!value || !ts.isBuildInfoFile(path)) return value;
|
||||
const buildInfo = ts.getBuildInfo(value);
|
||||
ts.Debug.assert(buildInfo.version === version);
|
||||
buildInfo.version = ts.version;
|
||||
return ts.getBuildInfoText(buildInfo);
|
||||
};
|
||||
|
||||
if (host.writeFile) {
|
||||
const originalWriteFile = host.writeFile;
|
||||
host.writeFile = (fileName, content, writeByteOrderMark) => {
|
||||
if (!ts.isBuildInfoFile(fileName)) return originalWriteFile.call(host, fileName, content, writeByteOrderMark);
|
||||
const buildInfo = ts.getBuildInfo(content);
|
||||
sanitizeBuildInfoProgram(buildInfo);
|
||||
buildInfo.version = version;
|
||||
originalWriteFile.call(host, fileName, ts.getBuildInfoText(buildInfo), writeByteOrderMark);
|
||||
};
|
||||
}
|
||||
|
||||
public writeFile(fileName: string, content: string, writeByteOrderMark: boolean) {
|
||||
if (!ts.isBuildInfoFile(fileName)) return super.writeFile(fileName, content, writeByteOrderMark);
|
||||
const buildInfo = ts.getBuildInfo(content);
|
||||
sanitizeBuildInfoProgram(buildInfo);
|
||||
buildInfo.version = version;
|
||||
super.writeFile(fileName, ts.getBuildInfoText(buildInfo), writeByteOrderMark);
|
||||
ts.Debug.assert(host.now === undefined);
|
||||
host.now = () => new Date(sys.vfs.time());
|
||||
ts.Debug.assertDefined(host.createHash);
|
||||
}
|
||||
|
||||
export class SolutionBuilderHost extends CompilerHost implements ts.SolutionBuilderHost<ts.BuilderProgram> {
|
||||
createProgram: ts.CreateProgram<ts.BuilderProgram>;
|
||||
|
||||
private constructor(sys: System | vfs.FileSystem, options?: ts.CompilerOptions, setParentNodes?: boolean, createProgram?: ts.CreateProgram<ts.BuilderProgram>) {
|
||||
super(sys, options, setParentNodes);
|
||||
this.createProgram = createProgram || ts.createEmitAndSemanticDiagnosticsBuilderProgram;
|
||||
}
|
||||
|
||||
static create(sys: System | vfs.FileSystem, options?: ts.CompilerOptions, setParentNodes?: boolean, createProgram?: ts.CreateProgram<ts.BuilderProgram>) {
|
||||
const host = new SolutionBuilderHost(sys, options, setParentNodes, createProgram);
|
||||
patchSolutionBuilderHost(host, host.sys);
|
||||
return host;
|
||||
}
|
||||
|
||||
createHash(data: string) {
|
||||
return `${ts.generateDjb2Hash(data)}-${data}`;
|
||||
}
|
||||
|
||||
now() {
|
||||
return new Date(this.sys.vfs.time());
|
||||
}
|
||||
|
||||
diagnostics: SolutionBuilderDiagnostic[] = [];
|
||||
|
||||
reportDiagnostic(diagnostic: ts.Diagnostic) {
|
||||
|
||||
+35
-14
@@ -32,6 +32,10 @@ namespace vfs {
|
||||
let devCount = 0; // A monotonically increasing count of device ids
|
||||
let inoCount = 0; // A monotonically increasing count of inodes
|
||||
|
||||
export interface DiffOptions {
|
||||
includeChangedFileWithSameContent?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a virtual POSIX-like file system.
|
||||
*/
|
||||
@@ -693,21 +697,25 @@ namespace vfs {
|
||||
* Generates a `FileSet` patch containing all the entries in this `FileSystem` that are not in `base`.
|
||||
* @param base The base file system. If not provided, this file system's `shadowRoot` is used (if present).
|
||||
*/
|
||||
public diff(base = this.shadowRoot) {
|
||||
public diff(base = this.shadowRoot, options: DiffOptions = {}) {
|
||||
const differences: FileSet = {};
|
||||
const hasDifferences = base ? FileSystem.rootDiff(differences, this, base) : FileSystem.trackCreatedInodes(differences, this, this._getRootLinks());
|
||||
const hasDifferences = base ?
|
||||
FileSystem.rootDiff(differences, this, base, options) :
|
||||
FileSystem.trackCreatedInodes(differences, this, this._getRootLinks());
|
||||
return hasDifferences ? differences : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a `FileSet` patch containing all the entries in `chagned` that are not in `base`.
|
||||
* Generates a `FileSet` patch containing all the entries in `changed` that are not in `base`.
|
||||
*/
|
||||
public static diff(changed: FileSystem, base: FileSystem) {
|
||||
public static diff(changed: FileSystem, base: FileSystem, options: DiffOptions = {}) {
|
||||
const differences: FileSet = {};
|
||||
return FileSystem.rootDiff(differences, changed, base) ? differences : undefined;
|
||||
return FileSystem.rootDiff(differences, changed, base, options) ?
|
||||
differences :
|
||||
undefined;
|
||||
}
|
||||
|
||||
private static diffWorker(container: FileSet, changed: FileSystem, changedLinks: ReadonlyMap<string, Inode> | undefined, base: FileSystem, baseLinks: ReadonlyMap<string, Inode> | undefined) {
|
||||
private static diffWorker(container: FileSet, changed: FileSystem, changedLinks: ReadonlyMap<string, Inode> | undefined, base: FileSystem, baseLinks: ReadonlyMap<string, Inode> | undefined, options: DiffOptions) {
|
||||
if (changedLinks && !baseLinks) return FileSystem.trackCreatedInodes(container, changed, changedLinks);
|
||||
if (baseLinks && !changedLinks) return FileSystem.trackDeletedInodes(container, baseLinks);
|
||||
if (changedLinks && baseLinks) {
|
||||
@@ -724,10 +732,10 @@ namespace vfs {
|
||||
const baseNode = baseLinks.get(basename);
|
||||
if (baseNode) {
|
||||
if (isDirectory(changedNode) && isDirectory(baseNode)) {
|
||||
return hasChanges = FileSystem.directoryDiff(container, basename, changed, changedNode, base, baseNode) || hasChanges;
|
||||
return hasChanges = FileSystem.directoryDiff(container, basename, changed, changedNode, base, baseNode, options) || hasChanges;
|
||||
}
|
||||
if (isFile(changedNode) && isFile(baseNode)) {
|
||||
return hasChanges = FileSystem.fileDiff(container, basename, changed, changedNode, base, baseNode) || hasChanges;
|
||||
return hasChanges = FileSystem.fileDiff(container, basename, changed, changedNode, base, baseNode, options) || hasChanges;
|
||||
}
|
||||
if (isSymlink(changedNode) && isSymlink(baseNode)) {
|
||||
return hasChanges = FileSystem.symlinkDiff(container, basename, changedNode, baseNode) || hasChanges;
|
||||
@@ -740,7 +748,7 @@ namespace vfs {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static rootDiff(container: FileSet, changed: FileSystem, base: FileSystem) {
|
||||
private static rootDiff(container: FileSet, changed: FileSystem, base: FileSystem, options: DiffOptions) {
|
||||
while (!changed._lazy.links && changed._shadowRoot) changed = changed._shadowRoot;
|
||||
while (!base._lazy.links && base._shadowRoot) base = base._shadowRoot;
|
||||
|
||||
@@ -750,10 +758,10 @@ namespace vfs {
|
||||
// no difference if the root links are empty and unshadowed
|
||||
if (!changed._lazy.links && !changed._shadowRoot && !base._lazy.links && !base._shadowRoot) return false;
|
||||
|
||||
return FileSystem.diffWorker(container, changed, changed._getRootLinks(), base, base._getRootLinks());
|
||||
return FileSystem.diffWorker(container, changed, changed._getRootLinks(), base, base._getRootLinks(), options);
|
||||
}
|
||||
|
||||
private static directoryDiff(container: FileSet, basename: string, changed: FileSystem, changedNode: DirectoryInode, base: FileSystem, baseNode: DirectoryInode) {
|
||||
private static directoryDiff(container: FileSet, basename: string, changed: FileSystem, changedNode: DirectoryInode, base: FileSystem, baseNode: DirectoryInode, options: DiffOptions) {
|
||||
while (!changedNode.links && changedNode.shadowRoot) changedNode = changedNode.shadowRoot;
|
||||
while (!baseNode.links && baseNode.shadowRoot) baseNode = baseNode.shadowRoot;
|
||||
|
||||
@@ -770,7 +778,7 @@ namespace vfs {
|
||||
|
||||
// no difference if both nodes have identical children
|
||||
const children: FileSet = {};
|
||||
if (!FileSystem.diffWorker(children, changed, changed._getLinks(changedNode), base, base._getLinks(baseNode))) {
|
||||
if (!FileSystem.diffWorker(children, changed, changed._getLinks(changedNode), base, base._getLinks(baseNode), options)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -778,7 +786,7 @@ namespace vfs {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static fileDiff(container: FileSet, basename: string, changed: FileSystem, changedNode: FileInode, base: FileSystem, baseNode: FileInode) {
|
||||
private static fileDiff(container: FileSet, basename: string, changed: FileSystem, changedNode: FileInode, base: FileSystem, baseNode: FileInode, options: DiffOptions) {
|
||||
while (!changedNode.buffer && changedNode.shadowRoot) changedNode = changedNode.shadowRoot;
|
||||
while (!baseNode.buffer && baseNode.shadowRoot) baseNode = baseNode.shadowRoot;
|
||||
|
||||
@@ -800,7 +808,11 @@ namespace vfs {
|
||||
if (changedBuffer === baseBuffer) return false;
|
||||
|
||||
// no difference if both buffers are identical
|
||||
if (Buffer.compare(changedBuffer, baseBuffer) === 0) return false;
|
||||
if (Buffer.compare(changedBuffer, baseBuffer) === 0) {
|
||||
if (!options.includeChangedFileWithSameContent) return false;
|
||||
container[basename] = new SameFileContentFile(changedBuffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
container[basename] = new File(changedBuffer);
|
||||
return true;
|
||||
@@ -1361,6 +1373,12 @@ namespace vfs {
|
||||
}
|
||||
}
|
||||
|
||||
export class SameFileContentFile extends File {
|
||||
constructor(data: Buffer | string, metaAndEncoding?: { encoding?: string, meta?: Record<string, any> }) {
|
||||
super(data, metaAndEncoding);
|
||||
}
|
||||
}
|
||||
|
||||
/** Extended options for a hard link in a `FileSet` */
|
||||
export class Link {
|
||||
public readonly path: string;
|
||||
@@ -1549,6 +1567,9 @@ namespace vfs {
|
||||
else if (entry instanceof Directory) {
|
||||
text += formatPatchWorker(file, entry.files);
|
||||
}
|
||||
else if (entry instanceof SameFileContentFile) {
|
||||
text += `//// [${file}] file written with same contents\r\n`;
|
||||
}
|
||||
else if (entry instanceof File) {
|
||||
const content = typeof entry.data === "string" ? entry.data : entry.data.toString("utf8");
|
||||
text += `//// [${file}]\r\n${content}\r\n\r\n`;
|
||||
|
||||
@@ -44,7 +44,10 @@ interface Array<T> { length: number; [n: number]: T; }`
|
||||
}
|
||||
|
||||
export function createServerHost(fileOrFolderList: readonly FileOrFolderOrSymLink[], params?: TestServerHostCreationParameters): TestServerHost {
|
||||
return new TestServerHost(/*withSafelist*/ true, fileOrFolderList, params);
|
||||
const host = new TestServerHost(/*withSafelist*/ true, fileOrFolderList, params);
|
||||
// Just like sys, patch the host to use writeFile
|
||||
patchWriteFileEnsuringDirectory(host);
|
||||
return host;
|
||||
}
|
||||
|
||||
export interface File {
|
||||
@@ -174,8 +177,8 @@ interface Array<T> { length: number; [n: number]: T; }`
|
||||
}
|
||||
}
|
||||
|
||||
export function checkWatchedFiles(host: TestServerHost, expectedFiles: string[]) {
|
||||
checkMapKeys("watchedFiles", host.watchedFiles, expectedFiles);
|
||||
export function checkWatchedFiles(host: TestServerHost, expectedFiles: string[], additionalInfo?: string) {
|
||||
checkMapKeys(`watchedFiles:: ${additionalInfo || ""}::`, host.watchedFiles, expectedFiles);
|
||||
}
|
||||
|
||||
export function checkWatchedFilesDetailed(host: TestServerHost, expectedFiles: ReadonlyMap<number>): void;
|
||||
@@ -1016,6 +1019,19 @@ interface Array<T> { length: number; [n: number]: T; }`
|
||||
}
|
||||
}
|
||||
|
||||
export type TestServerHostTrackingWrittenFiles = TestServerHost & { writtenFiles: Map<true>; };
|
||||
|
||||
export function changeToHostTrackingWrittenFiles(inputHost: TestServerHost) {
|
||||
const host = inputHost as TestServerHostTrackingWrittenFiles;
|
||||
const originalWriteFile = host.writeFile;
|
||||
host.writtenFiles = createMap<true>();
|
||||
host.writeFile = (fileName, content) => {
|
||||
originalWriteFile.call(host, fileName, content);
|
||||
const path = host.toFullPath(fileName);
|
||||
host.writtenFiles.set(path, true);
|
||||
};
|
||||
return host;
|
||||
}
|
||||
export const tsbuildProjectsLocation = "/user/username/projects";
|
||||
export function getTsBuildProjectFilePath(project: string, file: string) {
|
||||
return `${tsbuildProjectsLocation}/${project}/${file}`;
|
||||
|
||||
Vendored
+11
-11
@@ -18,7 +18,7 @@ interface PromiseConstructor {
|
||||
* @param values An array of Promises.
|
||||
* @returns A new Promise.
|
||||
*/
|
||||
all<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>, T10 | PromiseLike<T10>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>;
|
||||
all<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>, T10 | PromiseLike<T10>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>;
|
||||
|
||||
/**
|
||||
* Creates a Promise that is resolved with an array of results when all of the provided Promises
|
||||
@@ -26,7 +26,7 @@ interface PromiseConstructor {
|
||||
* @param values An array of Promises.
|
||||
* @returns A new Promise.
|
||||
*/
|
||||
all<T1, T2, T3, T4, T5, T6, T7, T8, T9>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>;
|
||||
all<T1, T2, T3, T4, T5, T6, T7, T8, T9>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>;
|
||||
|
||||
/**
|
||||
* Creates a Promise that is resolved with an array of results when all of the provided Promises
|
||||
@@ -34,7 +34,7 @@ interface PromiseConstructor {
|
||||
* @param values An array of Promises.
|
||||
* @returns A new Promise.
|
||||
*/
|
||||
all<T1, T2, T3, T4, T5, T6, T7, T8>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>;
|
||||
all<T1, T2, T3, T4, T5, T6, T7, T8>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>;
|
||||
|
||||
/**
|
||||
* Creates a Promise that is resolved with an array of results when all of the provided Promises
|
||||
@@ -42,7 +42,7 @@ interface PromiseConstructor {
|
||||
* @param values An array of Promises.
|
||||
* @returns A new Promise.
|
||||
*/
|
||||
all<T1, T2, T3, T4, T5, T6, T7>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>]): Promise<[T1, T2, T3, T4, T5, T6, T7]>;
|
||||
all<T1, T2, T3, T4, T5, T6, T7>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>]): Promise<[T1, T2, T3, T4, T5, T6, T7]>;
|
||||
|
||||
/**
|
||||
* Creates a Promise that is resolved with an array of results when all of the provided Promises
|
||||
@@ -50,7 +50,7 @@ interface PromiseConstructor {
|
||||
* @param values An array of Promises.
|
||||
* @returns A new Promise.
|
||||
*/
|
||||
all<T1, T2, T3, T4, T5, T6>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>]): Promise<[T1, T2, T3, T4, T5, T6]>;
|
||||
all<T1, T2, T3, T4, T5, T6>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>]): Promise<[T1, T2, T3, T4, T5, T6]>;
|
||||
|
||||
/**
|
||||
* Creates a Promise that is resolved with an array of results when all of the provided Promises
|
||||
@@ -58,7 +58,7 @@ interface PromiseConstructor {
|
||||
* @param values An array of Promises.
|
||||
* @returns A new Promise.
|
||||
*/
|
||||
all<T1, T2, T3, T4, T5>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>]): Promise<[T1, T2, T3, T4, T5]>;
|
||||
all<T1, T2, T3, T4, T5>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>]): Promise<[T1, T2, T3, T4, T5]>;
|
||||
|
||||
/**
|
||||
* Creates a Promise that is resolved with an array of results when all of the provided Promises
|
||||
@@ -66,7 +66,7 @@ interface PromiseConstructor {
|
||||
* @param values An array of Promises.
|
||||
* @returns A new Promise.
|
||||
*/
|
||||
all<T1, T2, T3, T4>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>]): Promise<[T1, T2, T3, T4]>;
|
||||
all<T1, T2, T3, T4>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>]): Promise<[T1, T2, T3, T4]>;
|
||||
|
||||
/**
|
||||
* Creates a Promise that is resolved with an array of results when all of the provided Promises
|
||||
@@ -74,7 +74,7 @@ interface PromiseConstructor {
|
||||
* @param values An array of Promises.
|
||||
* @returns A new Promise.
|
||||
*/
|
||||
all<T1, T2, T3>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>]): Promise<[T1, T2, T3]>;
|
||||
all<T1, T2, T3>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>]): Promise<[T1, T2, T3]>;
|
||||
|
||||
/**
|
||||
* Creates a Promise that is resolved with an array of results when all of the provided Promises
|
||||
@@ -82,7 +82,7 @@ interface PromiseConstructor {
|
||||
* @param values An array of Promises.
|
||||
* @returns A new Promise.
|
||||
*/
|
||||
all<T1, T2>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>]): Promise<[T1, T2]>;
|
||||
all<T1, T2>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>]): Promise<[T1, T2]>;
|
||||
|
||||
/**
|
||||
* Creates a Promise that is resolved with an array of results when all of the provided Promises
|
||||
@@ -90,7 +90,7 @@ interface PromiseConstructor {
|
||||
* @param values An array of Promises.
|
||||
* @returns A new Promise.
|
||||
*/
|
||||
all<T>(values: (T | PromiseLike<T>)[]): Promise<T[]>;
|
||||
all<T>(values: readonly (T | PromiseLike<T>)[]): Promise<T[]>;
|
||||
|
||||
/**
|
||||
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
|
||||
@@ -98,7 +98,7 @@ interface PromiseConstructor {
|
||||
* @param values An array of Promises.
|
||||
* @returns A new Promise.
|
||||
*/
|
||||
race<T>(values: T[]): Promise<T extends PromiseLike<infer U> ? U : T>;
|
||||
race<T>(values: readonly T[]): Promise<T extends PromiseLike<infer U> ? U : T>;
|
||||
|
||||
/**
|
||||
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
|
||||
|
||||
Vendored
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
interface AsyncGenerator<T = unknown, TReturn = any, TNext = unknown> extends AsyncIterator<T, TReturn, TNext> {
|
||||
// NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
|
||||
next(...args: [] | [TNext | PromiseLike<TNext>]): Promise<IteratorResult<T, TReturn>>;
|
||||
next(...args: [] | [TNext]): Promise<IteratorResult<T, TReturn>>;
|
||||
return(value: TReturn | PromiseLike<TReturn>): Promise<IteratorResult<T, TReturn>>;
|
||||
throw(e: any): Promise<IteratorResult<T, TReturn>>;
|
||||
[Symbol.asyncIterator](): AsyncGenerator<T, TReturn, TNext>;
|
||||
|
||||
Vendored
+1
-1
@@ -11,7 +11,7 @@ interface SymbolConstructor {
|
||||
|
||||
interface AsyncIterator<T, TReturn = any, TNext = undefined> {
|
||||
// NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
|
||||
next(...args: [] | [TNext | PromiseLike<TNext>]): Promise<IteratorResult<T, TReturn>>;
|
||||
next(...args: [] | [TNext]): Promise<IteratorResult<T, TReturn>>;
|
||||
return?(value?: TReturn | PromiseLike<TReturn>): Promise<IteratorResult<T, TReturn>>;
|
||||
throw?(e?: any): Promise<IteratorResult<T, TReturn>>;
|
||||
}
|
||||
|
||||
@@ -1777,6 +1777,12 @@ namespace ts.server {
|
||||
configFileErrors.push(...parsedCommandLine.errors);
|
||||
}
|
||||
|
||||
this.logger.info(`Config: ${configFilename} : ${JSON.stringify({
|
||||
rootNames: parsedCommandLine.fileNames,
|
||||
options: parsedCommandLine.options,
|
||||
projectReferences: parsedCommandLine.projectReferences
|
||||
}, /*replacer*/ undefined, " ")}`);
|
||||
|
||||
Debug.assert(!!parsedCommandLine.fileNames);
|
||||
const compilerOptions = parsedCommandLine.options;
|
||||
|
||||
@@ -1818,7 +1824,7 @@ namespace ts.server {
|
||||
let scriptInfo: ScriptInfo | NormalizedPath;
|
||||
let path: Path;
|
||||
// Use the project's fileExists so that it can use caching instead of reaching to disk for the query
|
||||
if (!isDynamic && !project.fileExists(newRootFile)) {
|
||||
if (!isDynamic && !project.fileExistsWithCache(newRootFile)) {
|
||||
path = normalizedPathToPath(normalizedPath, this.currentDirectory, this.toCanonicalFileName);
|
||||
const existingValue = projectRootFilesMap.get(path)!;
|
||||
if (isScriptInfo(existingValue)) {
|
||||
@@ -1851,7 +1857,7 @@ namespace ts.server {
|
||||
projectRootFilesMap.forEach((value, path) => {
|
||||
if (!newRootScriptInfoMap.has(path)) {
|
||||
if (isScriptInfo(value)) {
|
||||
project.removeFile(value, project.fileExists(path), /*detachFromProject*/ true);
|
||||
project.removeFile(value, project.fileExistsWithCache(path), /*detachFromProject*/ true);
|
||||
}
|
||||
else {
|
||||
projectRootFilesMap.delete(path);
|
||||
@@ -2584,7 +2590,9 @@ namespace ts.server {
|
||||
|
||||
/*@internal*/
|
||||
getOriginalLocationEnsuringConfiguredProject(project: Project, location: DocumentPosition): DocumentPosition | undefined {
|
||||
const originalLocation = project.getSourceMapper().tryGetSourcePosition(location);
|
||||
const originalLocation = project.isSourceOfProjectReferenceRedirect(location.fileName) ?
|
||||
location :
|
||||
project.getSourceMapper().tryGetSourcePosition(location);
|
||||
if (!originalLocation) return undefined;
|
||||
|
||||
const { fileName } = originalLocation;
|
||||
@@ -2595,7 +2603,8 @@ namespace ts.server {
|
||||
if (!configFileName) return undefined;
|
||||
|
||||
const configuredProject = this.findConfiguredProjectByProjectName(configFileName) ||
|
||||
this.createAndLoadConfiguredProject(configFileName, `Creating project for original file: ${originalFileInfo.fileName} for location: ${location.fileName}`);
|
||||
this.createAndLoadConfiguredProject(configFileName, `Creating project for original file: ${originalFileInfo.fileName}${location !== originalLocation ? " for location: " + location.fileName : ""}`);
|
||||
if (configuredProject === project) return originalLocation;
|
||||
updateProjectIfDirty(configuredProject);
|
||||
// Keep this configured project as referenced from project
|
||||
addOriginalConfiguredProject(configuredProject);
|
||||
|
||||
+90
-3
@@ -196,6 +196,11 @@ namespace ts.server {
|
||||
/*@internal*/
|
||||
originalConfiguredProjects: Map<true> | undefined;
|
||||
|
||||
/*@internal*/
|
||||
getResolvedProjectReferenceToRedirect(_fileName: string): ResolvedProjectReference | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private readonly cancellationToken: ThrottledCancellationToken;
|
||||
|
||||
public isNonTsProject() {
|
||||
@@ -391,6 +396,11 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
fileExists(file: string): boolean {
|
||||
return this.fileExistsWithCache(file);
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
fileExistsWithCache(file: string): boolean {
|
||||
// As an optimization, don't hit the disks for files we already know don't exist
|
||||
// (because we're watching for their creation).
|
||||
const path = this.toPath(file);
|
||||
@@ -527,8 +537,11 @@ namespace ts.server {
|
||||
return this.projectService.getSourceFileLike(fileName, this);
|
||||
}
|
||||
|
||||
private shouldEmitFile(scriptInfo: ScriptInfo) {
|
||||
return scriptInfo && !scriptInfo.isDynamicOrHasMixedContent();
|
||||
/*@internal*/
|
||||
shouldEmitFile(scriptInfo: ScriptInfo | undefined) {
|
||||
return scriptInfo &&
|
||||
!scriptInfo.isDynamicOrHasMixedContent() &&
|
||||
!this.program!.isSourceOfProjectReferenceRedirect(scriptInfo.path);
|
||||
}
|
||||
|
||||
getCompileOnSaveAffectedFileList(scriptInfo: ScriptInfo): string[] {
|
||||
@@ -538,7 +551,7 @@ namespace ts.server {
|
||||
updateProjectIfDirty(this);
|
||||
this.builderState = BuilderState.create(this.program!, this.projectService.toCanonicalFileName, this.builderState);
|
||||
return mapDefined(BuilderState.getFilesAffectedBy(this.builderState, this.program!, scriptInfo.path, this.cancellationToken, data => this.projectService.host.createHash!(data)), // TODO: GH#18217
|
||||
sourceFile => this.shouldEmitFile(this.projectService.getScriptInfoForPath(sourceFile.path)!) ? sourceFile.fileName : undefined);
|
||||
sourceFile => this.shouldEmitFile(this.projectService.getScriptInfoForPath(sourceFile.path)) ? sourceFile.fileName : undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1223,6 +1236,11 @@ namespace ts.server {
|
||||
this.rootFilesMap.delete(info.path);
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
isSourceOfProjectReferenceRedirect(fileName: string) {
|
||||
return !!this.program && this.program.isSourceOfProjectReferenceRedirect(fileName);
|
||||
}
|
||||
|
||||
protected enableGlobalPlugins(options: CompilerOptions, pluginConfigOverrides: Map<any> | undefined) {
|
||||
const host = this.projectService.host;
|
||||
|
||||
@@ -1475,6 +1493,8 @@ namespace ts.server {
|
||||
configFileWatcher: FileWatcher | undefined;
|
||||
private directoriesWatchedForWildcards: Map<WildcardDirectoryWatcher> | undefined;
|
||||
readonly canonicalConfigFilePath: NormalizedPath;
|
||||
private projectReferenceCallbacks: ResolvedProjectReferenceCallbacks | undefined;
|
||||
private mapOfDeclarationDirectories: Map<true> | undefined;
|
||||
|
||||
/* @internal */
|
||||
pendingReload: ConfigFileProgramReloadLevel | undefined;
|
||||
@@ -1520,6 +1540,63 @@ namespace ts.server {
|
||||
this.canonicalConfigFilePath = asNormalizedPath(projectService.toCanonicalFileName(configFileName));
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
setResolvedProjectReferenceCallbacks(projectReferenceCallbacks: ResolvedProjectReferenceCallbacks) {
|
||||
this.projectReferenceCallbacks = projectReferenceCallbacks;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
useSourceOfProjectReferenceRedirect = () => !!this.languageServiceEnabled &&
|
||||
!this.getCompilerOptions().disableSourceOfProjectReferenceRedirect;
|
||||
|
||||
/**
|
||||
* This implementation of fileExists checks if the file being requested is
|
||||
* .d.ts file for the referenced Project.
|
||||
* If it is it returns true irrespective of whether that file exists on host
|
||||
*/
|
||||
fileExists(file: string): boolean {
|
||||
// Project references go to source file instead of .d.ts file
|
||||
if (this.useSourceOfProjectReferenceRedirect() && this.projectReferenceCallbacks) {
|
||||
const source = this.projectReferenceCallbacks.getSourceOfProjectReferenceRedirect(file);
|
||||
if (source) return isString(source) ? super.fileExists(source) : true;
|
||||
}
|
||||
return super.fileExists(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation of directoryExists checks if the directory being requested is
|
||||
* directory of .d.ts file for the referenced Project.
|
||||
* If it is it returns true irrespective of whether that directory exists on host
|
||||
*/
|
||||
directoryExists(path: string): boolean {
|
||||
if (super.directoryExists(path)) return true;
|
||||
if (!this.useSourceOfProjectReferenceRedirect() || !this.projectReferenceCallbacks) return false;
|
||||
|
||||
if (!this.mapOfDeclarationDirectories) {
|
||||
this.mapOfDeclarationDirectories = createMap();
|
||||
this.projectReferenceCallbacks.forEachResolvedProjectReference(ref => {
|
||||
if (!ref) return;
|
||||
const out = ref.commandLine.options.outFile || ref.commandLine.options.outDir;
|
||||
if (out) {
|
||||
this.mapOfDeclarationDirectories!.set(getDirectoryPath(this.toPath(out)), true);
|
||||
}
|
||||
else {
|
||||
// Set declaration's in different locations only, if they are next to source the directory present doesnt change
|
||||
const declarationDir = ref.commandLine.options.declarationDir || ref.commandLine.options.outDir;
|
||||
if (declarationDir) {
|
||||
this.mapOfDeclarationDirectories!.set(this.toPath(declarationDir), true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
const dirPath = this.toPath(path);
|
||||
const dirPathWithTrailingDirectorySeparator = `${dirPath}${directorySeparator}`;
|
||||
return !!forEachKey(
|
||||
this.mapOfDeclarationDirectories,
|
||||
declDirPath => dirPath === declDirPath || startsWith(declDirPath, dirPathWithTrailingDirectorySeparator)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the project has reload from disk pending, it reloads (and then updates graph as part of that) instead of just updating the graph
|
||||
* @returns: true if set of files in the project stays the same and false - otherwise.
|
||||
@@ -1528,6 +1605,8 @@ namespace ts.server {
|
||||
this.isInitialLoadPending = returnFalse;
|
||||
const reloadLevel = this.pendingReload;
|
||||
this.pendingReload = ConfigFileProgramReloadLevel.None;
|
||||
this.projectReferenceCallbacks = undefined;
|
||||
this.mapOfDeclarationDirectories = undefined;
|
||||
let result: boolean;
|
||||
switch (reloadLevel) {
|
||||
case ConfigFileProgramReloadLevel.Partial:
|
||||
@@ -1570,6 +1649,12 @@ namespace ts.server {
|
||||
return program && program.forEachResolvedProjectReference(cb);
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
getResolvedProjectReferenceToRedirect(fileName: string): ResolvedProjectReference | undefined {
|
||||
const program = this.getCurrentProgram();
|
||||
return program && program.getResolvedProjectReferenceToRedirect(fileName);
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
enablePluginsWithOptions(options: CompilerOptions, pluginConfigOverrides: Map<any> | undefined) {
|
||||
const host = this.projectService.host;
|
||||
@@ -1652,6 +1737,8 @@ namespace ts.server {
|
||||
this.stopWatchingWildCards();
|
||||
this.projectErrors = undefined;
|
||||
this.configFileSpecs = undefined;
|
||||
this.projectReferenceCallbacks = undefined;
|
||||
this.mapOfDeclarationDirectories = undefined;
|
||||
super.close();
|
||||
}
|
||||
|
||||
|
||||
@@ -495,15 +495,17 @@ namespace ts.server {
|
||||
// the default project; if no configured projects, the first external project should
|
||||
// be the default project; otherwise the first inferred project should be the default.
|
||||
let firstExternalProject;
|
||||
let firstConfiguredProject;
|
||||
for (const project of this.containingProjects) {
|
||||
if (project.projectKind === ProjectKind.Configured) {
|
||||
return project;
|
||||
if (!project.isSourceOfProjectReferenceRedirect(this.fileName)) return project;
|
||||
if (!firstConfiguredProject) firstConfiguredProject = project;
|
||||
}
|
||||
else if (project.projectKind === ProjectKind.External && !firstExternalProject) {
|
||||
firstExternalProject = project;
|
||||
}
|
||||
}
|
||||
return firstExternalProject || this.containingProjects[0];
|
||||
return firstConfiguredProject || firstExternalProject || this.containingProjects[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -448,7 +448,9 @@ namespace ts.server {
|
||||
|
||||
function getDefinitionInProject(definition: DocumentPosition | undefined, definingProject: Project, project: Project): DocumentPosition | undefined {
|
||||
if (!definition || project.containsFile(toNormalizedPath(definition.fileName))) return definition;
|
||||
const mappedDefinition = definingProject.getLanguageService().getSourceMapper().tryGetGeneratedPosition(definition);
|
||||
const mappedDefinition = definingProject.isSourceOfProjectReferenceRedirect(definition.fileName) ?
|
||||
definition :
|
||||
definingProject.getLanguageService().getSourceMapper().tryGetGeneratedPosition(definition);
|
||||
return mappedDefinition && project.containsFile(toNormalizedPath(mappedDefinition.fileName)) ? mappedDefinition : undefined;
|
||||
}
|
||||
|
||||
@@ -477,7 +479,7 @@ namespace ts.server {
|
||||
for (const symlinkedProject of symlinkedProjects) addToTodo({ project: symlinkedProject, location: originalLocation as TLocation }, toDo!, seenProjects);
|
||||
});
|
||||
}
|
||||
return originalLocation;
|
||||
return originalLocation === location ? undefined : originalLocation;
|
||||
});
|
||||
return toDo;
|
||||
}
|
||||
@@ -1037,7 +1039,9 @@ namespace ts.server {
|
||||
|
||||
private getEmitOutput(args: protocol.FileRequestArgs): EmitOutput {
|
||||
const { file, project } = this.getFileAndProject(args);
|
||||
return project.getLanguageService().getEmitOutput(file);
|
||||
return project.shouldEmitFile(project.getScriptInfo(file)) ?
|
||||
project.getLanguageService().getEmitOutput(file) :
|
||||
{ emitSkipped: true, outputFiles: [] };
|
||||
}
|
||||
|
||||
private mapDefinitionInfo(definitions: readonly DefinitionInfo[], project: Project): readonly protocol.FileSpanWithContext[] {
|
||||
@@ -1672,10 +1676,10 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
private createCheckList(fileNames: string[], defaultProject?: Project): PendingErrorCheck[] {
|
||||
private createCheckList(fileNames: string[]): PendingErrorCheck[] {
|
||||
return mapDefined<string, PendingErrorCheck>(fileNames, uncheckedFileName => {
|
||||
const fileName = toNormalizedPath(uncheckedFileName);
|
||||
const project = defaultProject || this.projectService.tryGetDefaultProjectForFile(fileName);
|
||||
const project = this.projectService.tryGetDefaultProjectForFile(fileName);
|
||||
return project && { fileName, project };
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/* @internal */
|
||||
namespace ts.codefix {
|
||||
const fixId = "fixConvertConstToLet";
|
||||
const errorCodes = [Diagnostics.Cannot_assign_to_0_because_it_is_a_constant.code];
|
||||
|
||||
registerCodeFix({
|
||||
errorCodes,
|
||||
getCodeActions: context => {
|
||||
const { sourceFile, span, program } = context;
|
||||
const variableStatement = getVariableStatement(sourceFile, span.start, program);
|
||||
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, variableStatement));
|
||||
return [createCodeFixAction(fixId, changes, Diagnostics.Convert_const_to_let, fixId, Diagnostics.Convert_const_to_let)];
|
||||
},
|
||||
fixIds: [fixId]
|
||||
});
|
||||
|
||||
function getVariableStatement(sourceFile: SourceFile, pos: number, program: Program) {
|
||||
const token = getTokenAtPosition(sourceFile, pos);
|
||||
const checker = program.getTypeChecker();
|
||||
const symbol = checker.getSymbolAtLocation(token);
|
||||
if (symbol) {
|
||||
return symbol.valueDeclaration.parent.parent as VariableStatement;
|
||||
}
|
||||
}
|
||||
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, variableStatement?: VariableStatement) {
|
||||
if (!variableStatement) {
|
||||
return;
|
||||
}
|
||||
const start = variableStatement.getStart();
|
||||
changes.replaceRangeWithText(sourceFile, { pos: start, end: start + 5 }, "let");
|
||||
}
|
||||
}
|
||||
@@ -15,11 +15,14 @@ namespace ts.codefix {
|
||||
return [createCodeFixAction(fixId, changes, Diagnostics.Add_async_modifier_to_containing_function, fixId, Diagnostics.Add_all_missing_async_modifiers)];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
const nodes = getNodes(diag.file, diag.start);
|
||||
if (!nodes) return;
|
||||
doChange(changes, context.sourceFile, nodes);
|
||||
}),
|
||||
getAllCodeActions: context => {
|
||||
const seen = createMap<true>();
|
||||
return codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
const nodes = getNodes(diag.file, diag.start);
|
||||
if (!nodes || !addToSeen(seen, getNodeId(nodes.insertBefore))) return;
|
||||
doChange(changes, context.sourceFile, nodes);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
function getReturnType(expr: FunctionDeclaration | MethodDeclaration | FunctionExpression | ArrowFunction) {
|
||||
|
||||
@@ -393,6 +393,19 @@ namespace ts.codefix {
|
||||
|
||||
function inferTypeFromReferences(program: Program, references: readonly Identifier[], cancellationToken: CancellationToken) {
|
||||
const checker = program.getTypeChecker();
|
||||
const builtinConstructors: { [s: string]: (t: Type) => Type } = {
|
||||
string: () => checker.getStringType(),
|
||||
number: () => checker.getNumberType(),
|
||||
Array: t => checker.createArrayType(t),
|
||||
Promise: t => checker.createPromiseType(t),
|
||||
};
|
||||
const builtins = [
|
||||
checker.getStringType(),
|
||||
checker.getNumberType(),
|
||||
checker.createArrayType(checker.getAnyType()),
|
||||
checker.createPromiseType(checker.getAnyType()),
|
||||
];
|
||||
|
||||
return {
|
||||
single,
|
||||
parameters,
|
||||
@@ -401,26 +414,74 @@ namespace ts.codefix {
|
||||
|
||||
interface CallUsage {
|
||||
argumentTypes: Type[];
|
||||
returnType: Usage;
|
||||
return_: Usage;
|
||||
}
|
||||
|
||||
interface Usage {
|
||||
isNumber?: boolean;
|
||||
isString?: boolean;
|
||||
isNumber: boolean | undefined;
|
||||
isString: boolean | undefined;
|
||||
/** Used ambiguously, eg x + ___ or object[___]; results in string | number if no other evidence exists */
|
||||
isNumberOrString?: boolean;
|
||||
isNumberOrString: boolean | undefined;
|
||||
|
||||
candidateTypes?: Type[];
|
||||
properties?: UnderscoreEscapedMap<Usage>;
|
||||
calls?: CallUsage[];
|
||||
constructs?: CallUsage[];
|
||||
numberIndex?: Usage;
|
||||
stringIndex?: Usage;
|
||||
candidateThisTypes?: Type[];
|
||||
candidateTypes: Type[] | undefined;
|
||||
properties: UnderscoreEscapedMap<Usage> | undefined;
|
||||
calls: CallUsage[] | undefined;
|
||||
constructs: CallUsage[] | undefined;
|
||||
numberIndex: Usage | undefined;
|
||||
stringIndex: Usage | undefined;
|
||||
candidateThisTypes: Type[] | undefined;
|
||||
inferredTypes: Type[] | undefined;
|
||||
}
|
||||
|
||||
function createEmptyUsage(): Usage {
|
||||
return {
|
||||
isNumber: undefined,
|
||||
isString: undefined,
|
||||
isNumberOrString: undefined,
|
||||
candidateTypes: undefined,
|
||||
properties: undefined,
|
||||
calls: undefined,
|
||||
constructs: undefined,
|
||||
numberIndex: undefined,
|
||||
stringIndex: undefined,
|
||||
candidateThisTypes: undefined,
|
||||
inferredTypes: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
function combineUsages(usages: Usage[]): Usage {
|
||||
const combinedProperties = createUnderscoreEscapedMap<Usage[]>();
|
||||
for (const u of usages) {
|
||||
if (u.properties) {
|
||||
u.properties.forEach((p, name) => {
|
||||
if (!combinedProperties.has(name)) {
|
||||
combinedProperties.set(name, []);
|
||||
}
|
||||
combinedProperties.get(name)!.push(p);
|
||||
});
|
||||
}
|
||||
}
|
||||
const properties = createUnderscoreEscapedMap<Usage>();
|
||||
combinedProperties.forEach((ps, name) => {
|
||||
properties.set(name, combineUsages(ps));
|
||||
});
|
||||
return {
|
||||
isNumber: usages.some(u => u.isNumber),
|
||||
isString: usages.some(u => u.isString),
|
||||
isNumberOrString: usages.some(u => u.isNumberOrString),
|
||||
candidateTypes: flatMap(usages, u => u.candidateTypes) as Type[],
|
||||
properties,
|
||||
calls: flatMap(usages, u => u.calls) as CallUsage[],
|
||||
constructs: flatMap(usages, u => u.constructs) as CallUsage[],
|
||||
numberIndex: forEach(usages, u => u.numberIndex),
|
||||
stringIndex: forEach(usages, u => u.stringIndex),
|
||||
candidateThisTypes: flatMap(usages, u => u.candidateThisTypes) as Type[],
|
||||
inferredTypes: undefined, // clear type cache
|
||||
};
|
||||
}
|
||||
|
||||
function single(): Type {
|
||||
return unifyFromUsage(inferTypesFromReferencesSingle(references));
|
||||
return combineTypes(inferTypesFromReferencesSingle(references));
|
||||
}
|
||||
|
||||
function parameters(declaration: FunctionLike): ParameterInference[] | undefined {
|
||||
@@ -428,7 +489,7 @@ namespace ts.codefix {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const usage: Usage = {};
|
||||
const usage = createEmptyUsage();
|
||||
for (const reference of references) {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
calculateUsageOfNode(reference, usage);
|
||||
@@ -456,7 +517,7 @@ namespace ts.codefix {
|
||||
const inferred = inferTypesFromReferencesSingle(getReferences(parameter.name, program, cancellationToken));
|
||||
types.push(...(isRest ? mapDefined(inferred, checker.getElementTypeOfArrayType) : inferred));
|
||||
}
|
||||
const type = unifyFromUsage(types);
|
||||
const type = combineTypes(types);
|
||||
return {
|
||||
type: isRest ? checker.createArrayType(type) : type,
|
||||
isOptional: isOptional && !isRest,
|
||||
@@ -466,22 +527,22 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
function thisParameter() {
|
||||
const usage: Usage = {};
|
||||
const usage = createEmptyUsage();
|
||||
for (const reference of references) {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
calculateUsageOfNode(reference, usage);
|
||||
}
|
||||
|
||||
return unifyFromUsage(usage.candidateThisTypes || emptyArray);
|
||||
return combineTypes(usage.candidateThisTypes || emptyArray);
|
||||
}
|
||||
|
||||
function inferTypesFromReferencesSingle(references: readonly Identifier[]): Type[] {
|
||||
const usage: Usage = {};
|
||||
const usage: Usage = createEmptyUsage();
|
||||
for (const reference of references) {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
calculateUsageOfNode(reference, usage);
|
||||
}
|
||||
return inferFromUsage(usage);
|
||||
return inferTypes(usage);
|
||||
}
|
||||
|
||||
function calculateUsageOfNode(node: Expression, usage: Usage): void {
|
||||
@@ -490,6 +551,9 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
switch (node.parent.kind) {
|
||||
case SyntaxKind.ExpressionStatement:
|
||||
addCandidateType(usage, checker.getVoidType());
|
||||
break;
|
||||
case SyntaxKind.PostfixUnaryExpression:
|
||||
usage.isNumber = true;
|
||||
break;
|
||||
@@ -632,6 +696,9 @@ namespace ts.codefix {
|
||||
else if (otherOperandType.flags & TypeFlags.StringLike) {
|
||||
usage.isString = true;
|
||||
}
|
||||
else if (otherOperandType.flags & TypeFlags.Any) {
|
||||
// do nothing, maybe we'll learn something elsewhere
|
||||
}
|
||||
else {
|
||||
usage.isNumberOrString = true;
|
||||
}
|
||||
@@ -677,7 +744,7 @@ namespace ts.codefix {
|
||||
function inferTypeFromCallExpression(parent: CallExpression | NewExpression, usage: Usage): void {
|
||||
const call: CallUsage = {
|
||||
argumentTypes: [],
|
||||
returnType: {}
|
||||
return_: createEmptyUsage()
|
||||
};
|
||||
|
||||
if (parent.arguments) {
|
||||
@@ -686,7 +753,7 @@ namespace ts.codefix {
|
||||
}
|
||||
}
|
||||
|
||||
calculateUsageOfNode(parent, call.returnType);
|
||||
calculateUsageOfNode(parent, call.return_);
|
||||
if (parent.kind === SyntaxKind.CallExpression) {
|
||||
(usage.calls || (usage.calls = [])).push(call);
|
||||
}
|
||||
@@ -700,7 +767,7 @@ namespace ts.codefix {
|
||||
if (!usage.properties) {
|
||||
usage.properties = createUnderscoreEscapedMap<Usage>();
|
||||
}
|
||||
const propertyUsage = usage.properties.get(name) || { };
|
||||
const propertyUsage = usage.properties.get(name) || createEmptyUsage();
|
||||
calculateUsageOfNode(parent, propertyUsage);
|
||||
usage.properties.set(name, propertyUsage);
|
||||
}
|
||||
@@ -712,7 +779,7 @@ namespace ts.codefix {
|
||||
}
|
||||
else {
|
||||
const indexType = checker.getTypeAtLocation(parent.argumentExpression);
|
||||
const indexUsage = {};
|
||||
const indexUsage = createEmptyUsage();
|
||||
calculateUsageOfNode(parent, indexUsage);
|
||||
if (indexType.flags & TypeFlags.NumberLike) {
|
||||
usage.numberIndex = indexUsage;
|
||||
@@ -752,8 +819,12 @@ namespace ts.codefix {
|
||||
return inferences.filter(i => toRemove.every(f => !f(i)));
|
||||
}
|
||||
|
||||
function unifyFromUsage(inferences: readonly Type[], fallback = checker.getAnyType()): Type {
|
||||
if (!inferences.length) return fallback;
|
||||
function combineFromUsage(usage: Usage) {
|
||||
return combineTypes(inferTypes(usage));
|
||||
}
|
||||
|
||||
function combineTypes(inferences: readonly Type[]): Type {
|
||||
if (!inferences.length) return checker.getAnyType();
|
||||
|
||||
// 1. string or number individually override string | number
|
||||
// 2. non-any, non-void overrides any or void
|
||||
@@ -776,12 +847,12 @@ namespace ts.codefix {
|
||||
const anons = good.filter(i => checker.getObjectFlags(i) & ObjectFlags.Anonymous) as AnonymousType[];
|
||||
if (anons.length) {
|
||||
good = good.filter(i => !(checker.getObjectFlags(i) & ObjectFlags.Anonymous));
|
||||
good.push(unifyAnonymousTypes(anons));
|
||||
good.push(combineAnonymousTypes(anons));
|
||||
}
|
||||
return checker.getWidenedType(checker.getUnionType(good));
|
||||
return checker.getWidenedType(checker.getUnionType(good.map(checker.getBaseTypeOfLiteralType), UnionReduction.Subtype));
|
||||
}
|
||||
|
||||
function unifyAnonymousTypes(anons: AnonymousType[]) {
|
||||
function combineAnonymousTypes(anons: AnonymousType[]) {
|
||||
if (anons.length === 1) {
|
||||
return anons[0];
|
||||
}
|
||||
@@ -822,7 +893,7 @@ namespace ts.codefix {
|
||||
numberIndices.length ? checker.createIndexInfo(checker.getUnionType(numberIndices), numberIndexReadonly) : undefined);
|
||||
}
|
||||
|
||||
function inferFromUsage(usage: Usage) {
|
||||
function inferTypes(usage: Usage): Type[] {
|
||||
const types = [];
|
||||
|
||||
if (usage.isNumber) {
|
||||
@@ -834,92 +905,155 @@ namespace ts.codefix {
|
||||
if (usage.isNumberOrString) {
|
||||
types.push(checker.getUnionType([checker.getStringType(), checker.getNumberType()]));
|
||||
}
|
||||
if (usage.numberIndex) {
|
||||
types.push(checker.createArrayType(combineFromUsage(usage.numberIndex)));
|
||||
}
|
||||
if (usage.properties && usage.properties.size
|
||||
|| usage.calls && usage.calls.length
|
||||
|| usage.constructs && usage.constructs.length
|
||||
|| usage.stringIndex) {
|
||||
types.push(inferStructuralType(usage));
|
||||
}
|
||||
|
||||
types.push(...(usage.candidateTypes || []).map(t => checker.getBaseTypeOfLiteralType(t)));
|
||||
types.push(...inferNamedTypesFromProperties(usage));
|
||||
|
||||
if (usage.properties && hasCalls(usage.properties.get("then" as __String))) {
|
||||
const paramType = getParameterTypeFromCalls(0, usage.properties.get("then" as __String)!.calls!, /*isRestParameter*/ false)!; // TODO: GH#18217
|
||||
const types = paramType.getCallSignatures().map(sig => sig.getReturnType());
|
||||
types.push(checker.createPromiseType(types.length ? checker.getUnionType(types, UnionReduction.Subtype) : checker.getAnyType()));
|
||||
}
|
||||
else if (usage.properties && hasCalls(usage.properties.get("push" as __String))) {
|
||||
types.push(checker.createArrayType(getParameterTypeFromCalls(0, usage.properties.get("push" as __String)!.calls!, /*isRestParameter*/ false)!));
|
||||
}
|
||||
|
||||
if (usage.numberIndex) {
|
||||
types.push(checker.createArrayType(recur(usage.numberIndex)));
|
||||
}
|
||||
else if (usage.properties || usage.calls || usage.constructs || usage.stringIndex) {
|
||||
const members = createUnderscoreEscapedMap<Symbol>();
|
||||
const callSignatures: Signature[] = [];
|
||||
const constructSignatures: Signature[] = [];
|
||||
let stringIndexInfo: IndexInfo | undefined;
|
||||
|
||||
if (usage.properties) {
|
||||
usage.properties.forEach((u, name) => {
|
||||
const symbol = checker.createSymbol(SymbolFlags.Property, name);
|
||||
symbol.type = recur(u);
|
||||
members.set(name, symbol);
|
||||
});
|
||||
}
|
||||
|
||||
if (usage.calls) {
|
||||
for (const call of usage.calls) {
|
||||
callSignatures.push(getSignatureFromCall(call));
|
||||
}
|
||||
}
|
||||
|
||||
if (usage.constructs) {
|
||||
for (const construct of usage.constructs) {
|
||||
constructSignatures.push(getSignatureFromCall(construct));
|
||||
}
|
||||
}
|
||||
|
||||
if (usage.stringIndex) {
|
||||
stringIndexInfo = checker.createIndexInfo(recur(usage.stringIndex), /*isReadonly*/ false);
|
||||
}
|
||||
|
||||
types.push(checker.createAnonymousType(/*symbol*/ undefined!, members, callSignatures, constructSignatures, stringIndexInfo, /*numberIndexInfo*/ undefined)); // TODO: GH#18217
|
||||
}
|
||||
return types;
|
||||
|
||||
function recur(innerUsage: Usage): Type {
|
||||
return unifyFromUsage(inferFromUsage(innerUsage));
|
||||
}
|
||||
}
|
||||
|
||||
function getParameterTypeFromCalls(parameterIndex: number, calls: CallUsage[], isRestParameter: boolean) {
|
||||
let types: Type[] = [];
|
||||
if (calls) {
|
||||
for (const call of calls) {
|
||||
if (call.argumentTypes.length > parameterIndex) {
|
||||
if (isRestParameter) {
|
||||
types = concatenate(types, map(call.argumentTypes.slice(parameterIndex), a => checker.getBaseTypeOfLiteralType(a)));
|
||||
}
|
||||
else {
|
||||
types.push(checker.getBaseTypeOfLiteralType(call.argumentTypes[parameterIndex]));
|
||||
function inferStructuralType(usage: Usage) {
|
||||
const members = createUnderscoreEscapedMap<Symbol>();
|
||||
if (usage.properties) {
|
||||
usage.properties.forEach((u, name) => {
|
||||
const symbol = checker.createSymbol(SymbolFlags.Property, name);
|
||||
symbol.type = combineFromUsage(u);
|
||||
members.set(name, symbol);
|
||||
});
|
||||
}
|
||||
const callSignatures: Signature[] = usage.calls ? [getSignatureFromCalls(usage.calls)] : [];
|
||||
const constructSignatures: Signature[] = usage.constructs ? [getSignatureFromCalls(usage.constructs)] : [];
|
||||
const stringIndexInfo = usage.stringIndex && checker.createIndexInfo(combineFromUsage(usage.stringIndex), /*isReadonly*/ false);
|
||||
return checker.createAnonymousType(/*symbol*/ undefined!, members, callSignatures, constructSignatures, stringIndexInfo, /*numberIndexInfo*/ undefined); // TODO: GH#18217
|
||||
}
|
||||
|
||||
function inferNamedTypesFromProperties(usage: Usage): Type[] {
|
||||
if (!usage.properties || !usage.properties.size) return [];
|
||||
const types = builtins.filter(t => allPropertiesAreAssignableToUsage(t, usage));
|
||||
if (0 < types.length && types.length < 3) {
|
||||
return types.map(t => inferInstantiationFromUsage(t, usage));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function allPropertiesAreAssignableToUsage(type: Type, usage: Usage) {
|
||||
if (!usage.properties) return false;
|
||||
return !forEachEntry(usage.properties, (propUsage, name) => {
|
||||
const source = checker.getTypeOfPropertyOfType(type, name as string);
|
||||
if (!source) {
|
||||
return true;
|
||||
}
|
||||
if (propUsage.calls) {
|
||||
const sigs = checker.getSignaturesOfType(source, SignatureKind.Call);
|
||||
return !sigs.length || !checker.isTypeAssignableTo(source, getFunctionFromCalls(propUsage.calls));
|
||||
}
|
||||
else {
|
||||
return !checker.isTypeAssignableTo(source, combineFromUsage(propUsage));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* inference is limited to
|
||||
* 1. generic types with a single parameter
|
||||
* 2. inference to/from calls with a single signature
|
||||
*/
|
||||
function inferInstantiationFromUsage(type: Type, usage: Usage) {
|
||||
if (!(getObjectFlags(type) & ObjectFlags.Reference) || !usage.properties) {
|
||||
return type;
|
||||
}
|
||||
const generic = (type as TypeReference).target;
|
||||
const singleTypeParameter = singleOrUndefined(generic.typeParameters);
|
||||
if (!singleTypeParameter) return type;
|
||||
|
||||
const types: Type[] = [];
|
||||
usage.properties.forEach((propUsage, name) => {
|
||||
const genericPropertyType = checker.getTypeOfPropertyOfType(generic, name as string);
|
||||
Debug.assert(!!genericPropertyType, "generic should have all the properties of its reference.");
|
||||
types.push(...inferTypeParameters(genericPropertyType!, combineFromUsage(propUsage), singleTypeParameter));
|
||||
});
|
||||
return builtinConstructors[type.symbol.escapedName as string](combineTypes(types));
|
||||
}
|
||||
|
||||
function inferTypeParameters(genericType: Type, usageType: Type, typeParameter: Type): readonly Type[] {
|
||||
if (genericType === typeParameter) {
|
||||
return [usageType];
|
||||
}
|
||||
else if (genericType.flags & TypeFlags.UnionOrIntersection) {
|
||||
return flatMap((genericType as UnionOrIntersectionType).types, t => inferTypeParameters(t, usageType, typeParameter));
|
||||
}
|
||||
else if (getObjectFlags(genericType) & ObjectFlags.Reference && getObjectFlags(usageType) & ObjectFlags.Reference) {
|
||||
// this is wrong because we need a reference to the targetType to, so we can check that it's also a reference
|
||||
const genericArgs = (genericType as TypeReference).typeArguments;
|
||||
const usageArgs = (usageType as TypeReference).typeArguments;
|
||||
const types = [];
|
||||
if (genericArgs && usageArgs) {
|
||||
for (let i = 0; i < genericArgs.length; i++) {
|
||||
if (usageArgs[i]) {
|
||||
types.push(...inferTypeParameters(genericArgs[i], usageArgs[i], typeParameter));
|
||||
}
|
||||
}
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
if (types.length) {
|
||||
const type = checker.getWidenedType(checker.getUnionType(types, UnionReduction.Subtype));
|
||||
return isRestParameter ? checker.createArrayType(type) : type;
|
||||
const genericSigs = checker.getSignaturesOfType(genericType, SignatureKind.Call);
|
||||
const usageSigs = checker.getSignaturesOfType(usageType, SignatureKind.Call);
|
||||
if (genericSigs.length === 1 && usageSigs.length === 1) {
|
||||
return inferFromSignatures(genericSigs[0], usageSigs[0], typeParameter);
|
||||
}
|
||||
return undefined;
|
||||
return [];
|
||||
}
|
||||
|
||||
function getSignatureFromCall(call: CallUsage): Signature {
|
||||
function inferFromSignatures(genericSig: Signature, usageSig: Signature, typeParameter: Type) {
|
||||
const types = [];
|
||||
for (let i = 0; i < genericSig.parameters.length; i++) {
|
||||
const genericParam = genericSig.parameters[i];
|
||||
const usageParam = usageSig.parameters[i];
|
||||
const isRest = genericSig.declaration && isRestParameter(genericSig.declaration.parameters[i]);
|
||||
if (!usageParam) {
|
||||
break;
|
||||
}
|
||||
let genericParamType = checker.getTypeOfSymbolAtLocation(genericParam, genericParam.valueDeclaration);
|
||||
const elementType = isRest && checker.getElementTypeOfArrayType(genericParamType);
|
||||
if (elementType) {
|
||||
genericParamType = elementType;
|
||||
}
|
||||
const targetType = (usageParam as SymbolLinks).type || checker.getTypeOfSymbolAtLocation(usageParam, usageParam.valueDeclaration);
|
||||
types.push(...inferTypeParameters(genericParamType, targetType, typeParameter));
|
||||
}
|
||||
const genericReturn = checker.getReturnTypeOfSignature(genericSig);
|
||||
const usageReturn = checker.getReturnTypeOfSignature(usageSig);
|
||||
types.push(...inferTypeParameters(genericReturn, usageReturn, typeParameter));
|
||||
return types;
|
||||
}
|
||||
|
||||
function getFunctionFromCalls(calls: CallUsage[]) {
|
||||
return checker.createAnonymousType(undefined!, createSymbolTable(), [getSignatureFromCalls(calls)], emptyArray, /*stringIndexInfo*/ undefined, /*numberIndexInfo*/ undefined);
|
||||
}
|
||||
|
||||
function getSignatureFromCalls(calls: CallUsage[]): Signature {
|
||||
const parameters: Symbol[] = [];
|
||||
for (let i = 0; i < call.argumentTypes.length; i++) {
|
||||
const length = Math.max(...calls.map(c => c.argumentTypes.length));
|
||||
for (let i = 0; i < length; i++) {
|
||||
const symbol = checker.createSymbol(SymbolFlags.FunctionScopedVariable, escapeLeadingUnderscores(`arg${i}`));
|
||||
symbol.type = checker.getWidenedType(checker.getBaseTypeOfLiteralType(call.argumentTypes[i]));
|
||||
symbol.type = combineTypes(calls.map(call => call.argumentTypes[i] || checker.getUndefinedType()));
|
||||
if (calls.some(call => call.argumentTypes[i] === undefined)) {
|
||||
symbol.flags |= SymbolFlags.Optional;
|
||||
}
|
||||
parameters.push(symbol);
|
||||
}
|
||||
const returnType = unifyFromUsage(inferFromUsage(call.returnType), checker.getVoidType());
|
||||
const returnType = combineFromUsage(combineUsages(calls.map(call => call.return_)));
|
||||
// TODO: GH#18217
|
||||
return checker.createSignature(/*declaration*/ undefined!, /*typeParameters*/ undefined, /*thisParameter*/ undefined, parameters, returnType, /*typePredicate*/ undefined, call.argumentTypes.length, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
|
||||
return checker.createSignature(/*declaration*/ undefined!, /*typeParameters*/ undefined, /*thisParameter*/ undefined, parameters, returnType, /*typePredicate*/ undefined, length, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
|
||||
}
|
||||
|
||||
function addCandidateType(usage: Usage, type: Type | undefined) {
|
||||
@@ -933,9 +1067,5 @@ namespace ts.codefix {
|
||||
(usage.candidateThisTypes || (usage.candidateThisTypes = [])).push(type);
|
||||
}
|
||||
}
|
||||
|
||||
function hasCalls(usage: Usage | undefined): boolean {
|
||||
return !!usage && !!usage.calls;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/* @internal */
|
||||
namespace ts.codefix {
|
||||
const fixId = "useBigintLiteral";
|
||||
const errorCodes = [
|
||||
Diagnostics.Numeric_literals_with_absolute_values_equal_to_2_53_or_greater_are_too_large_to_be_represented_accurately_as_integers.code,
|
||||
];
|
||||
|
||||
registerCodeFix({
|
||||
errorCodes,
|
||||
getCodeActions: context => {
|
||||
const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span));
|
||||
if (changes.length > 0) {
|
||||
return [createCodeFixAction(fixId, changes, Diagnostics.Convert_to_a_bigint_numeric_literal, fixId, Diagnostics.Convert_all_to_bigint_numeric_literals)];
|
||||
}
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: context => {
|
||||
return codeFixAll(context, errorCodes, (changes, diag) => makeChange(changes, diag.file, diag));
|
||||
},
|
||||
});
|
||||
|
||||
function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, span: TextSpan) {
|
||||
const numericLiteral = tryCast(getTokenAtPosition(sourceFile, span.start), isNumericLiteral);
|
||||
if (!numericLiteral) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We use .getText to overcome parser inaccuracies: https://github.com/microsoft/TypeScript/issues/33298
|
||||
const newText = numericLiteral.getText(sourceFile) + "n";
|
||||
|
||||
changeTracker.replaceNode(sourceFile, numericLiteral, createBigIntLiteral(newText));
|
||||
}
|
||||
}
|
||||
@@ -1145,7 +1145,7 @@ namespace ts.FindAllReferences.Core {
|
||||
}
|
||||
|
||||
export function eachSymbolReferenceInFile<T>(definition: Identifier, checker: TypeChecker, sourceFile: SourceFile, cb: (token: Identifier) => T): T | undefined {
|
||||
const symbol = isParameterPropertyDeclaration(definition.parent)
|
||||
const symbol = isParameterPropertyDeclaration(definition.parent, definition.parent.parent)
|
||||
? first(checker.getSymbolsOfParameterPropertyDeclaration(definition.parent, definition.text))
|
||||
: checker.getSymbolAtLocation(definition);
|
||||
if (!symbol) return undefined;
|
||||
@@ -1886,7 +1886,7 @@ namespace ts.FindAllReferences.Core {
|
||||
const res = fromRoot(symbol);
|
||||
if (res) return res;
|
||||
|
||||
if (symbol.valueDeclaration && isParameterPropertyDeclaration(symbol.valueDeclaration)) {
|
||||
if (symbol.valueDeclaration && isParameterPropertyDeclaration(symbol.valueDeclaration, symbol.valueDeclaration.parent)) {
|
||||
// For a parameter property, now try on the other symbol (property if this was a parameter, parameter if this was a property).
|
||||
const paramProps = checker.getSymbolsOfParameterPropertyDeclaration(cast(symbol.valueDeclaration, isParameter), symbol.name);
|
||||
Debug.assert(paramProps.length === 2 && !!(paramProps[0].flags & SymbolFlags.FunctionScopedVariable) && !!(paramProps[1].flags & SymbolFlags.Property)); // is [parameter, property]
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace ts.NavigationBar {
|
||||
curCancellationToken = cancellationToken;
|
||||
curSourceFile = sourceFile;
|
||||
try {
|
||||
return map(topLevelItems(rootNavigationBarNode(sourceFile)), convertToTopLevelItem);
|
||||
return map(primaryNavBarMenuItems(rootNavigationBarNode(sourceFile)), convertToPrimaryNavBarMenuItem);
|
||||
}
|
||||
finally {
|
||||
reset();
|
||||
@@ -111,8 +111,8 @@ namespace ts.NavigationBar {
|
||||
return root;
|
||||
}
|
||||
|
||||
function addLeafNode(node: Node): void {
|
||||
pushChild(parent, emptyNavigationBarNode(node));
|
||||
function addLeafNode(node: Node, name?: DeclarationName): void {
|
||||
pushChild(parent, emptyNavigationBarNode(node, name));
|
||||
}
|
||||
|
||||
function emptyNavigationBarNode(node: Node, name?: DeclarationName): NavigationBarNode {
|
||||
@@ -197,7 +197,7 @@ namespace ts.NavigationBar {
|
||||
|
||||
// Parameter properties are children of the class, not the constructor.
|
||||
for (const param of ctr.parameters) {
|
||||
if (isParameterPropertyDeclaration(param)) {
|
||||
if (isParameterPropertyDeclaration(param, ctr)) {
|
||||
addLeafNode(param);
|
||||
}
|
||||
}
|
||||
@@ -243,23 +243,26 @@ namespace ts.NavigationBar {
|
||||
}
|
||||
break;
|
||||
|
||||
case SyntaxKind.ShorthandPropertyAssignment:
|
||||
addNodeWithRecursiveChild(node, (<ShorthandPropertyAssignment>node).name);
|
||||
break;
|
||||
case SyntaxKind.SpreadAssignment:
|
||||
const { expression } = <SpreadAssignment>node;
|
||||
// Use the expression as the name of the SpreadAssignment, otherwise show as <unknown>.
|
||||
isIdentifier(expression) ? addLeafNode(node, expression) : addLeafNode(node);
|
||||
break;
|
||||
case SyntaxKind.BindingElement:
|
||||
case SyntaxKind.PropertyAssignment:
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
const { name, initializer } = <VariableDeclaration | BindingElement>node;
|
||||
const { name, initializer } = <VariableDeclaration | PropertyAssignment | BindingElement>node;
|
||||
if (isBindingPattern(name)) {
|
||||
addChildrenRecursively(name);
|
||||
}
|
||||
else if (initializer && isFunctionOrClassExpression(initializer)) {
|
||||
if (initializer.name) {
|
||||
// Don't add a node for the VariableDeclaration, just for the initializer.
|
||||
addChildrenRecursively(initializer);
|
||||
}
|
||||
else {
|
||||
// Add a node for the VariableDeclaration, but not for the initializer.
|
||||
startNode(node);
|
||||
forEachChild(initializer, addChildrenRecursively);
|
||||
endNode();
|
||||
}
|
||||
// Add a node for the VariableDeclaration, but not for the initializer.
|
||||
startNode(node);
|
||||
forEachChild(initializer, addChildrenRecursively);
|
||||
endNode();
|
||||
}
|
||||
else {
|
||||
addNodeWithRecursiveChild(node, initializer);
|
||||
@@ -699,12 +702,15 @@ namespace ts.NavigationBar {
|
||||
}
|
||||
}
|
||||
|
||||
/** Flattens the NavNode tree to a list, keeping only the top-level items. */
|
||||
function topLevelItems(root: NavigationBarNode): NavigationBarNode[] {
|
||||
const topLevel: NavigationBarNode[] = [];
|
||||
/** Flattens the NavNode tree to a list of items to appear in the primary navbar menu. */
|
||||
function primaryNavBarMenuItems(root: NavigationBarNode): NavigationBarNode[] {
|
||||
// The primary (middle) navbar menu displays the general code navigation hierarchy, similar to the navtree.
|
||||
// The secondary (right) navbar menu displays the child items of whichever primary item is selected.
|
||||
// Some less interesting items without their own child navigation items (e.g. a local variable declaration) only show up in the secondary menu.
|
||||
const primaryNavBarMenuItems: NavigationBarNode[] = [];
|
||||
function recur(item: NavigationBarNode) {
|
||||
if (isTopLevel(item)) {
|
||||
topLevel.push(item);
|
||||
if (shouldAppearInPrimaryNavBarMenu(item)) {
|
||||
primaryNavBarMenuItems.push(item);
|
||||
if (item.children) {
|
||||
for (const child of item.children) {
|
||||
recur(child);
|
||||
@@ -713,9 +719,16 @@ namespace ts.NavigationBar {
|
||||
}
|
||||
}
|
||||
recur(root);
|
||||
return topLevel;
|
||||
return primaryNavBarMenuItems;
|
||||
|
||||
function isTopLevel(item: NavigationBarNode): boolean {
|
||||
/** Determines if a node should appear in the primary navbar menu. */
|
||||
function shouldAppearInPrimaryNavBarMenu(item: NavigationBarNode): boolean {
|
||||
// Items with children should always appear in the primary navbar menu.
|
||||
if (item.children) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Some nodes are otherwise important enough to always include in the primary navigation menu.
|
||||
switch (navigationBarNodeKind(item)) {
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.ClassExpression:
|
||||
@@ -728,13 +741,6 @@ namespace ts.NavigationBar {
|
||||
case SyntaxKind.JSDocCallbackTag:
|
||||
return true;
|
||||
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
return hasSomeImportantChild(item);
|
||||
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
@@ -755,15 +761,9 @@ namespace ts.NavigationBar {
|
||||
case SyntaxKind.Constructor:
|
||||
return true;
|
||||
default:
|
||||
return hasSomeImportantChild(item);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function hasSomeImportantChild(item: NavigationBarNode): boolean {
|
||||
return some(item.children, child => {
|
||||
const childKind = navigationBarNodeKind(child);
|
||||
return childKind !== SyntaxKind.VariableDeclaration && childKind !== SyntaxKind.BindingElement;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -778,19 +778,19 @@ namespace ts.NavigationBar {
|
||||
};
|
||||
}
|
||||
|
||||
function convertToTopLevelItem(n: NavigationBarNode): NavigationBarItem {
|
||||
function convertToPrimaryNavBarMenuItem(n: NavigationBarNode): NavigationBarItem {
|
||||
return {
|
||||
text: getItemName(n.node, n.name),
|
||||
kind: getNodeKind(n.node),
|
||||
kindModifiers: getModifiers(n.node),
|
||||
spans: getSpans(n),
|
||||
childItems: map(n.children, convertToChildItem) || emptyChildItemArray,
|
||||
childItems: map(n.children, convertToSecondaryNavBarMenuItem) || emptyChildItemArray,
|
||||
indent: n.indent,
|
||||
bolded: false,
|
||||
grayed: false
|
||||
};
|
||||
|
||||
function convertToChildItem(n: NavigationBarNode): NavigationBarItem {
|
||||
function convertToSecondaryNavBarMenuItem(n: NavigationBarNode): NavigationBarItem {
|
||||
return {
|
||||
text: getItemName(n.node, n.name),
|
||||
kind: getNodeKind(n.node),
|
||||
|
||||
@@ -116,6 +116,7 @@ namespace ts.refactor.extractSymbol {
|
||||
export const cannotExtractRange: DiagnosticMessage = createMessage("Cannot extract range.");
|
||||
export const cannotExtractImport: DiagnosticMessage = createMessage("Cannot extract import statement.");
|
||||
export const cannotExtractSuper: DiagnosticMessage = createMessage("Cannot extract super call.");
|
||||
export const cannotExtractJSDoc: DiagnosticMessage = createMessage("Cannot extract JSDoc.");
|
||||
export const cannotExtractEmpty: DiagnosticMessage = createMessage("Cannot extract empty range.");
|
||||
export const expressionExpected: DiagnosticMessage = createMessage("expression expected.");
|
||||
export const uselessConstantType: DiagnosticMessage = createMessage("No reason to extract constant of type.");
|
||||
@@ -246,6 +247,10 @@ namespace ts.refactor.extractSymbol {
|
||||
return { targetRange: { range: statements, facts: rangeFacts, declarations } };
|
||||
}
|
||||
|
||||
if (isJSDoc(start)) {
|
||||
return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.cannotExtractJSDoc)] };
|
||||
}
|
||||
|
||||
if (isReturnStatement(start) && !start.expression) {
|
||||
// Makes no sense to extract an expression-less return statement.
|
||||
return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.cannotExtractRange)] };
|
||||
@@ -1006,11 +1011,14 @@ namespace ts.refactor.extractSymbol {
|
||||
const localNameText = getUniqueName(isClassLike(scope) ? "newProperty" : "newLocal", file);
|
||||
const isJS = isInJSFile(scope);
|
||||
|
||||
const variableType = isJS || !checker.isContextSensitive(node)
|
||||
let variableType = isJS || !checker.isContextSensitive(node)
|
||||
? undefined
|
||||
: checker.typeToTypeNode(checker.getContextualType(node)!, scope, NodeBuilderFlags.NoTruncation); // TODO: GH#18217
|
||||
|
||||
const initializer = transformConstantInitializer(node, substitutions);
|
||||
let initializer = transformConstantInitializer(node, substitutions);
|
||||
|
||||
({ variableType, initializer } = transformFunctionInitializerAndType(variableType, initializer));
|
||||
|
||||
suppressLeadingAndTrailingTrivia(initializer);
|
||||
|
||||
const changeTracker = textChanges.ChangeTracker.fromContext(context);
|
||||
@@ -1102,6 +1110,73 @@ namespace ts.refactor.extractSymbol {
|
||||
const renameFilename = node.getSourceFile().fileName;
|
||||
const renameLocation = getRenameLocation(edits, renameFilename, localNameText, /*isDeclaredBeforeUse*/ true);
|
||||
return { renameFilename, renameLocation, edits };
|
||||
|
||||
function transformFunctionInitializerAndType(variableType: TypeNode | undefined, initializer: Expression): { variableType: TypeNode | undefined, initializer: Expression } {
|
||||
// If no contextual type exists there is nothing to transfer to the function signature
|
||||
if (variableType === undefined) return { variableType, initializer };
|
||||
// Only do this for function expressions and arrow functions that are not generic
|
||||
if (!isFunctionExpression(initializer) && !isArrowFunction(initializer) || !!initializer.typeParameters) return { variableType, initializer };
|
||||
const functionType = checker.getTypeAtLocation(node);
|
||||
const functionSignature = singleOrUndefined(checker.getSignaturesOfType(functionType, SignatureKind.Call));
|
||||
|
||||
// If no function signature, maybe there was an error, do nothing
|
||||
if (!functionSignature) return { variableType, initializer };
|
||||
// If the function signature has generic type parameters we don't attempt to move the parameters
|
||||
if (!!functionSignature.getTypeParameters()) return { variableType, initializer };
|
||||
|
||||
// We add parameter types if needed
|
||||
const parameters: ParameterDeclaration[] = [];
|
||||
let hasAny = false;
|
||||
for (const p of initializer.parameters) {
|
||||
if (p.type) {
|
||||
parameters.push(p);
|
||||
}
|
||||
else {
|
||||
const paramType = checker.getTypeAtLocation(p);
|
||||
if (paramType === checker.getAnyType()) hasAny = true;
|
||||
|
||||
parameters.push(updateParameter(p,
|
||||
p.decorators, p.modifiers, p.dotDotDotToken,
|
||||
p.name, p.questionToken, p.type || checker.typeToTypeNode(paramType, scope, NodeBuilderFlags.NoTruncation), p.initializer));
|
||||
}
|
||||
}
|
||||
// If a parameter was inferred as any we skip adding function parameters at all.
|
||||
// Turning an implicit any (which under common settings is a error) to an explicit
|
||||
// is probably actually a worse refactor outcome.
|
||||
if (hasAny) return { variableType, initializer };
|
||||
variableType = undefined;
|
||||
if (isArrowFunction(initializer)) {
|
||||
initializer = updateArrowFunction(initializer, node.modifiers, initializer.typeParameters,
|
||||
parameters,
|
||||
initializer.type || checker.typeToTypeNode(functionSignature.getReturnType(), scope, NodeBuilderFlags.NoTruncation),
|
||||
initializer.equalsGreaterThanToken,
|
||||
initializer.body);
|
||||
}
|
||||
else {
|
||||
if (functionSignature && !!functionSignature.thisParameter) {
|
||||
const firstParameter = firstOrUndefined(parameters);
|
||||
// If the function signature has a this parameter and if the first defined parameter is not the this parameter, we must add it
|
||||
// Note: If this parameter was already there, it would have been previously updated with the type if not type was present
|
||||
if ((!firstParameter || (isIdentifier(firstParameter.name) && firstParameter.name.escapedText !== "this"))) {
|
||||
const thisType = checker.getTypeOfSymbolAtLocation(functionSignature.thisParameter, node);
|
||||
parameters.splice(0, 0, createParameter(
|
||||
/* decorators */ undefined,
|
||||
/* modifiers */ undefined,
|
||||
/* dotDotDotToken */ undefined,
|
||||
"this",
|
||||
/* questionToken */ undefined,
|
||||
checker.typeToTypeNode(thisType, scope, NodeBuilderFlags.NoTruncation)
|
||||
));
|
||||
}
|
||||
}
|
||||
initializer = updateFunctionExpression(initializer, node.modifiers, initializer.asteriskToken,
|
||||
initializer.name, initializer.typeParameters,
|
||||
parameters,
|
||||
initializer.type || checker.typeToTypeNode(functionSignature.getReturnType(), scope, NodeBuilderFlags.NoTruncation),
|
||||
initializer.body);
|
||||
}
|
||||
return { variableType, initializer };
|
||||
}
|
||||
}
|
||||
|
||||
function getContainingVariableDeclarationIfInList(node: Node, scope: Scope) {
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor {
|
||||
}
|
||||
|
||||
function isAcceptedDeclaration(node: Node): node is AcceptedDeclaration {
|
||||
return isParameterPropertyDeclaration(node) || isPropertyDeclaration(node) || isPropertyAssignment(node);
|
||||
return isParameterPropertyDeclaration(node, node.parent) || isPropertyDeclaration(node) || isPropertyAssignment(node);
|
||||
}
|
||||
|
||||
function createPropertyName (name: string, originalName: AcceptedNameType) {
|
||||
@@ -214,7 +214,7 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor {
|
||||
}
|
||||
|
||||
function insertAccessor(changeTracker: textChanges.ChangeTracker, file: SourceFile, accessor: AccessorDeclaration, declaration: AcceptedDeclaration, container: ContainerDeclaration) {
|
||||
isParameterPropertyDeclaration(declaration) ? changeTracker.insertNodeAtClassStart(file, <ClassLikeDeclaration>container, accessor) :
|
||||
isParameterPropertyDeclaration(declaration, declaration.parent) ? changeTracker.insertNodeAtClassStart(file, <ClassLikeDeclaration>container, accessor) :
|
||||
isPropertyAssignment(declaration) ? changeTracker.insertNodeAfterComma(file, declaration, accessor) :
|
||||
changeTracker.insertNodeAfter(file, declaration, accessor);
|
||||
}
|
||||
|
||||
@@ -1149,10 +1149,10 @@ namespace ts {
|
||||
useCaseSensitiveFileNames: () => useCaseSensitiveFileNames,
|
||||
getCurrentDirectory: () => currentDirectory,
|
||||
getProgram,
|
||||
fileExists: host.fileExists && (f => host.fileExists!(f)),
|
||||
readFile: host.readFile && ((f, encoding) => host.readFile!(f, encoding)),
|
||||
getDocumentPositionMapper: host.getDocumentPositionMapper && ((generatedFileName, sourceFileName) => host.getDocumentPositionMapper!(generatedFileName, sourceFileName)),
|
||||
getSourceFileLike: host.getSourceFileLike && (f => host.getSourceFileLike!(f)),
|
||||
fileExists: maybeBind(host, host.fileExists),
|
||||
readFile: maybeBind(host, host.readFile),
|
||||
getDocumentPositionMapper: maybeBind(host, host.getDocumentPositionMapper),
|
||||
getSourceFileLike: maybeBind(host, host.getSourceFileLike),
|
||||
log
|
||||
});
|
||||
|
||||
@@ -1250,6 +1250,12 @@ namespace ts {
|
||||
if (host.resolveTypeReferenceDirectives) {
|
||||
compilerHost.resolveTypeReferenceDirectives = (...args) => host.resolveTypeReferenceDirectives!(...args);
|
||||
}
|
||||
if (host.setResolvedProjectReferenceCallbacks) {
|
||||
compilerHost.setResolvedProjectReferenceCallbacks = callbacks => host.setResolvedProjectReferenceCallbacks!(callbacks);
|
||||
}
|
||||
if (host.useSourceOfProjectReferenceRedirect) {
|
||||
compilerHost.useSourceOfProjectReferenceRedirect = () => host.useSourceOfProjectReferenceRedirect!();
|
||||
}
|
||||
|
||||
const documentRegistryBucketKey = documentRegistry.getKeyForCompilationSettings(newSettings);
|
||||
const options: CreateProgramOptions = {
|
||||
|
||||
@@ -70,6 +70,11 @@ namespace ts {
|
||||
if (!sourceFile) return undefined;
|
||||
|
||||
const program = host.getProgram()!;
|
||||
// If this is source file of project reference source (instead of redirect) there is no generated position
|
||||
if (program.isSourceOfProjectReferenceRedirect(sourceFile.fileName)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const options = program.getCompilerOptions();
|
||||
const outPath = options.outFile || options.out;
|
||||
|
||||
|
||||
@@ -79,9 +79,11 @@
|
||||
"codefixes/fixStrictClassInitialization.ts",
|
||||
"codefixes/requireInTs.ts",
|
||||
"codefixes/useDefaultImport.ts",
|
||||
"codefixes/useBigintLiteral.ts",
|
||||
"codefixes/fixAddModuleReferTypeMissingTypeof.ts",
|
||||
"codefixes/convertToMappedObjectType.ts",
|
||||
"codefixes/removeUnnecessaryAwait.ts",
|
||||
"codefixes/convertConstToLet.ts",
|
||||
"refactors/convertExport.ts",
|
||||
"refactors/convertImport.ts",
|
||||
"refactors/extractSymbol.ts",
|
||||
|
||||
@@ -234,6 +234,10 @@ namespace ts {
|
||||
getDocumentPositionMapper?(generatedFileName: string, sourceFileName?: string): DocumentPositionMapper | undefined;
|
||||
/* @internal */
|
||||
getSourceFileLike?(fileName: string): SourceFileLike | undefined;
|
||||
/* @internal */
|
||||
setResolvedProjectReferenceCallbacks?(callbacks: ResolvedProjectReferenceCallbacks): void;
|
||||
/* @internal */
|
||||
useSourceOfProjectReferenceRedirect?(): boolean;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
|
||||
@@ -353,8 +353,13 @@ namespace ts {
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
return ScriptElementKind.memberFunctionElement;
|
||||
case SyntaxKind.PropertyAssignment:
|
||||
const {initializer} = node as PropertyAssignment;
|
||||
return isFunctionLike(initializer) ? ScriptElementKind.memberFunctionElement : ScriptElementKind.memberVariableElement;
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.PropertySignature:
|
||||
case SyntaxKind.ShorthandPropertyAssignment:
|
||||
case SyntaxKind.SpreadAssignment:
|
||||
return ScriptElementKind.memberVariableElement;
|
||||
case SyntaxKind.IndexSignature: return ScriptElementKind.indexSignatureElement;
|
||||
case SyntaxKind.ConstructSignature: return ScriptElementKind.constructSignatureElement;
|
||||
|
||||
@@ -10,6 +10,7 @@ interface ExecResult {
|
||||
|
||||
interface UserConfig {
|
||||
types: string[];
|
||||
cloneUrl: string;
|
||||
path?: string;
|
||||
}
|
||||
|
||||
@@ -49,13 +50,17 @@ abstract class ExternalCompileRunnerBase extends RunnerBase {
|
||||
const stdio = isWorker ? "pipe" : "inherit";
|
||||
let types: string[] | undefined;
|
||||
if (fs.existsSync(path.join(cwd, "test.json"))) {
|
||||
const submoduleDir = path.join(cwd, directoryName);
|
||||
exec("git", ["reset", "HEAD", "--hard"], { cwd: submoduleDir });
|
||||
exec("git", ["clean", "-f"], { cwd: submoduleDir });
|
||||
exec("git", ["submodule", "update", "--init", "--remote", "."], { cwd: originalCwd });
|
||||
|
||||
const config = JSON.parse(fs.readFileSync(path.join(cwd, "test.json"), { encoding: "utf8" })) as UserConfig;
|
||||
ts.Debug.assert(!!config.types, "Bad format from test.json: Types field must be present.");
|
||||
ts.Debug.assert(!!config.cloneUrl, "Bad format from test.json: cloneUrl field must be present.");
|
||||
const submoduleDir = path.join(cwd, directoryName);
|
||||
if (!fs.existsSync(submoduleDir)) {
|
||||
exec("git", ["clone", config.cloneUrl, directoryName], { cwd });
|
||||
}
|
||||
exec("git", ["reset", "HEAD", "--hard"], { cwd: submoduleDir });
|
||||
exec("git", ["clean", "-f"], { cwd: submoduleDir });
|
||||
exec("git", ["pull", "-f"], { cwd: submoduleDir });
|
||||
|
||||
types = config.types;
|
||||
|
||||
cwd = config.path ? path.join(cwd, config.path) : submoduleDir;
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
|
||||
"unittests/services/extract/helpers.ts",
|
||||
"unittests/tsbuild/helpers.ts",
|
||||
"unittests/tsc/helpers.ts",
|
||||
"unittests/tscWatch/helpers.ts",
|
||||
"unittests/tsserver/helpers.ts",
|
||||
|
||||
@@ -145,6 +146,8 @@
|
||||
"unittests/tsserver/occurences.ts",
|
||||
"unittests/tsserver/openFile.ts",
|
||||
"unittests/tsserver/projectErrors.ts",
|
||||
"unittests/tsserver/projectReferenceCompileOnSave.ts",
|
||||
"unittests/tsserver/projectReferenceErrors.ts",
|
||||
"unittests/tsserver/projectReferences.ts",
|
||||
"unittests/tsserver/projects.ts",
|
||||
"unittests/tsserver/refactors.ts",
|
||||
|
||||
@@ -160,4 +160,22 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
describe("unittests:: Program.getNodeCount / Program.getIdentifierCount", () => {
|
||||
it("works on projects that have .json files", () => {
|
||||
const main = new documents.TextDocument("/main.ts", 'export { version } from "./package.json";');
|
||||
const pkg = new documents.TextDocument("/package.json", '{"version": "1.0.0"}');
|
||||
|
||||
const fs = vfs.createFromFileSystem(Harness.IO, /*ignoreCase*/ false, { documents: [main, pkg], cwd: "/" });
|
||||
const program = createProgram(["/main.ts"], { resolveJsonModule: true }, new fakes.CompilerHost(fs, { newLine: NewLineKind.LineFeed }));
|
||||
|
||||
const json = program.getSourceFile("/package.json")!;
|
||||
assert.equal(json.scriptKind, ScriptKind.JSON);
|
||||
assert.isNumber(json.nodeCount);
|
||||
assert.isNumber(json.identifierCount);
|
||||
|
||||
assert.isNotNaN(program.getNodeCount());
|
||||
assert.isNotNaN(program.getIdentifierCount());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -380,6 +380,10 @@ switch (x) {
|
||||
`[#|{ 1;|] }`,
|
||||
[refactor.extractSymbol.Messages.cannotExtractRange.message]);
|
||||
|
||||
testExtractRangeFailed("extractRangeFailed19",
|
||||
`[#|/** @type {number} */|] const foo = 1;`,
|
||||
[refactor.extractSymbol.Messages.cannotExtractJSDoc.message]);
|
||||
|
||||
testExtractRangeFailed("extract-method-not-for-token-expression-statement", `[#|a|]`, [refactor.extractSymbol.Messages.cannotExtractIdentifier.message]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -300,6 +300,34 @@ namespace ts {
|
||||
});
|
||||
});
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/33295
|
||||
testBaseline("transformParameterProperty", () => {
|
||||
return transpileModule("", {
|
||||
transformers: {
|
||||
before: [transformAddParameterProperty],
|
||||
},
|
||||
compilerOptions: {
|
||||
target: ScriptTarget.ES5,
|
||||
newLine: NewLineKind.CarriageReturnLineFeed,
|
||||
}
|
||||
}).outputText;
|
||||
|
||||
function transformAddParameterProperty(_context: TransformationContext) {
|
||||
return (sourceFile: SourceFile): SourceFile => {
|
||||
return visitNode(sourceFile);
|
||||
};
|
||||
function visitNode(sf: SourceFile) {
|
||||
// produce `class Foo { constructor(@Dec private x) {} }`;
|
||||
// The decorator is required to trigger ts.ts transformations.
|
||||
const classDecl = createClassDeclaration([], [], "Foo", /*typeParameters*/ undefined, /*heritageClauses*/ undefined, [
|
||||
createConstructor(/*decorators*/ undefined, /*modifiers*/ undefined, [
|
||||
createParameter(/*decorators*/ [createDecorator(createIdentifier("Dec"))], /*modifiers*/ [createModifier(SyntaxKind.PrivateKeyword)], /*dotDotDotToken*/ undefined, "x")], createBlock([]))
|
||||
]);
|
||||
return updateSourceFileNode(sf, [classDecl]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function baselineDeclarationTransform(text: string, opts: TranspileOptions) {
|
||||
const fs = vfs.createFromFileSystem(Harness.IO, /*caseSensitive*/ true, { documents: [new documents.TextDocument("/.src/index.ts", text)] });
|
||||
const host = new fakes.CompilerHost(fs, opts.compilerOptions);
|
||||
@@ -389,7 +417,7 @@ class Clazz {
|
||||
}
|
||||
`, {
|
||||
transformers: {
|
||||
before: [addSyntheticComment(n => isPropertyDeclaration(n) || isParameterPropertyDeclaration(n) || isClassDeclaration(n) || isConstructorDeclaration(n))],
|
||||
before: [addSyntheticComment(n => isPropertyDeclaration(n) || isParameterPropertyDeclaration(n, n.parent) || isClassDeclaration(n) || isConstructorDeclaration(n))],
|
||||
},
|
||||
compilerOptions: {
|
||||
target: ScriptTarget.ES2015,
|
||||
|
||||
@@ -1,27 +1,8 @@
|
||||
namespace ts {
|
||||
describe("unittests:: tsbuild:: outFile:: on amd modules with --out", () => {
|
||||
let outFileFs: vfs.FileSystem;
|
||||
const { time, tick } = getTime();
|
||||
const enum ext { js, jsmap, dts, dtsmap, buildinfo }
|
||||
const enum project { lib, app }
|
||||
type OutputFile = [string, string, string, string, string];
|
||||
function relName(path: string) { return path.slice(1); }
|
||||
const outputFiles: [OutputFile, OutputFile] = [
|
||||
[
|
||||
"/src/lib/module.js",
|
||||
"/src/lib/module.js.map",
|
||||
"/src/lib/module.d.ts",
|
||||
"/src/lib/module.d.ts.map",
|
||||
"/src/lib/module.tsbuildinfo"
|
||||
],
|
||||
[
|
||||
"/src/app/module.js",
|
||||
"/src/app/module.js.map",
|
||||
"/src/app/module.d.ts",
|
||||
"/src/app/module.d.ts.map",
|
||||
"/src/app/module.tsbuildinfo"
|
||||
]
|
||||
];
|
||||
type Sources = [string, readonly string[]];
|
||||
const enum source { config, ts }
|
||||
const sources: [Sources, Sources] = [
|
||||
@@ -43,68 +24,52 @@ namespace ts {
|
||||
]
|
||||
];
|
||||
before(() => {
|
||||
outFileFs = loadProjectFromDisk("tests/projects/amdModulesWithOut", time);
|
||||
outFileFs = loadProjectFromDisk("tests/projects/amdModulesWithOut");
|
||||
});
|
||||
after(() => {
|
||||
outFileFs = undefined!;
|
||||
});
|
||||
|
||||
interface VerifyOutFileScenarioInput {
|
||||
scenario: string;
|
||||
modifyFs: (fs: vfs.FileSystem) => void;
|
||||
subScenario: string;
|
||||
modifyFs?: (fs: vfs.FileSystem) => void;
|
||||
modifyAgainFs?: (fs: vfs.FileSystem) => void;
|
||||
}
|
||||
|
||||
function verifyOutFileScenario({
|
||||
scenario,
|
||||
subScenario,
|
||||
modifyFs,
|
||||
modifyAgainFs
|
||||
}: VerifyOutFileScenarioInput) {
|
||||
verifyTsbuildOutput({
|
||||
scenario,
|
||||
projFs: () => outFileFs,
|
||||
time,
|
||||
tick,
|
||||
proj: "amdModulesWithOut",
|
||||
rootNames: ["/src/app"],
|
||||
expectedMapFileNames: [
|
||||
outputFiles[project.lib][ext.jsmap],
|
||||
outputFiles[project.lib][ext.dtsmap],
|
||||
outputFiles[project.app][ext.jsmap],
|
||||
outputFiles[project.app][ext.dtsmap],
|
||||
],
|
||||
expectedBuildInfoFilesForSectionBaselines: [
|
||||
[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]]
|
||||
],
|
||||
lastProjectOutput: outputFiles[project.app][ext.js],
|
||||
initialBuild: {
|
||||
modifyFs
|
||||
},
|
||||
incrementalDtsUnchangedBuild: {
|
||||
modifyFs: fs => appendText(fs, relName(sources[project.lib][source.ts][1]), "console.log(x);")
|
||||
},
|
||||
incrementalHeaderChangedBuild: modifyAgainFs ? {
|
||||
modifyFs: modifyAgainFs
|
||||
} : undefined,
|
||||
outputFiles: [
|
||||
...outputFiles[project.lib],
|
||||
...outputFiles[project.app]
|
||||
],
|
||||
baselineOnly: true
|
||||
verifyTscIncrementalEdits({
|
||||
scenario: "amdModulesWithOut",
|
||||
subScenario,
|
||||
fs: () => outFileFs,
|
||||
commandLineArgs: ["--b", "/src/app", "--verbose"],
|
||||
baselineSourceMap: true,
|
||||
modifyFs,
|
||||
incrementalScenarios: [
|
||||
{
|
||||
buildKind: BuildKind.IncrementalDtsUnchanged,
|
||||
modifyFs: fs => appendText(fs, relName(sources[project.lib][source.ts][1]), "console.log(x);")
|
||||
},
|
||||
...(modifyAgainFs ? [{
|
||||
buildKind: BuildKind.IncrementalHeadersChange,
|
||||
modifyFs: modifyAgainFs
|
||||
}] : emptyArray),
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
describe("Prepend output with .tsbuildinfo", () => {
|
||||
verifyOutFileScenario({
|
||||
scenario: "modules and globals mixed in amd",
|
||||
modifyFs: noop
|
||||
subScenario: "modules and globals mixed in amd",
|
||||
});
|
||||
|
||||
// Prologues
|
||||
describe("Prologues", () => {
|
||||
verifyOutFileScenario({
|
||||
scenario: "multiple prologues in all projects",
|
||||
subScenario: "multiple prologues in all projects",
|
||||
modifyFs: fs => {
|
||||
enableStrict(fs, sources[project.lib][source.config]);
|
||||
addTestPrologue(fs, sources[project.lib][source.ts][0], `"myPrologue"`);
|
||||
@@ -122,7 +87,7 @@ namespace ts {
|
||||
describe("Shebang", () => {
|
||||
// changes declaration because its emitted in .d.ts file
|
||||
verifyOutFileScenario({
|
||||
scenario: "shebang in all projects",
|
||||
subScenario: "shebang in all projects",
|
||||
modifyFs: fs => {
|
||||
addShebang(fs, "lib", "file0");
|
||||
addShebang(fs, "lib", "file1");
|
||||
@@ -134,7 +99,7 @@ namespace ts {
|
||||
// emitHelpers
|
||||
describe("emitHelpers", () => {
|
||||
verifyOutFileScenario({
|
||||
scenario: "multiple emitHelpers in all projects",
|
||||
subScenario: "multiple emitHelpers in all projects",
|
||||
modifyFs: fs => {
|
||||
addSpread(fs, "lib", "file0");
|
||||
addRest(fs, "lib", "file1");
|
||||
@@ -149,7 +114,7 @@ namespace ts {
|
||||
describe("triple slash refs", () => {
|
||||
// changes declaration because its emitted in .d.ts file
|
||||
verifyOutFileScenario({
|
||||
scenario: "triple slash refs in all projects",
|
||||
subScenario: "triple slash refs in all projects",
|
||||
modifyFs: fs => {
|
||||
addTripleSlashRef(fs, "lib", "file0");
|
||||
addTripleSlashRef(fs, "app", "file4");
|
||||
@@ -193,7 +158,7 @@ ${internal} export enum internalEnum { a, b, c }`);
|
||||
|
||||
// Verify initial + incremental edits
|
||||
verifyOutFileScenario({
|
||||
scenario: "stripInternal",
|
||||
subScenario: "stripInternal",
|
||||
modifyFs: stripInternalScenario,
|
||||
modifyAgainFs: fs => replaceText(fs, sources[project.lib][source.ts][1], `export const`, `/*@internal*/ export const`),
|
||||
});
|
||||
@@ -207,47 +172,13 @@ ${internal} export enum internalEnum { a, b, c }`);
|
||||
replaceText(fs, sources[project.app][source.ts][0], "file1", "lib/file1");
|
||||
}
|
||||
|
||||
const libOutputFile: OutputFile = [
|
||||
"/src/lib/module.js",
|
||||
"/src/lib/module.js.map",
|
||||
"/src/lib/module.d.ts",
|
||||
"/src/lib/module.d.ts.map",
|
||||
"/src/lib/module.tsbuildinfo"
|
||||
];
|
||||
verifyTsbuildOutput({
|
||||
scenario: "when the module resolution finds original source file",
|
||||
projFs: () => outFileFs,
|
||||
time,
|
||||
tick,
|
||||
proj: "amdModulesWithOut",
|
||||
rootNames: ["/src/app"],
|
||||
expectedMapFileNames: [
|
||||
libOutputFile[ext.jsmap],
|
||||
libOutputFile[ext.dtsmap],
|
||||
outputFiles[project.app][ext.jsmap],
|
||||
outputFiles[project.app][ext.dtsmap],
|
||||
],
|
||||
expectedBuildInfoFilesForSectionBaselines: [
|
||||
[libOutputFile[ext.buildinfo], libOutputFile[ext.js], libOutputFile[ext.dts]],
|
||||
[outputFiles[project.app][ext.buildinfo], outputFiles[project.app][ext.js], outputFiles[project.app][ext.dts]]
|
||||
],
|
||||
lastProjectOutput: outputFiles[project.app][ext.js],
|
||||
initialBuild: {
|
||||
modifyFs,
|
||||
expectedDiagnostics: [
|
||||
getExpectedDiagnosticForProjectsInBuild("src/lib/tsconfig.json", "src/app/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/lib/tsconfig.json", "src/module.js"],
|
||||
[Diagnostics.Building_project_0, sources[project.lib][source.config]],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/app/tsconfig.json", "src/app/module.js"],
|
||||
[Diagnostics.Building_project_0, sources[project.app][source.config]],
|
||||
]
|
||||
},
|
||||
outputFiles: [
|
||||
...libOutputFile,
|
||||
...outputFiles[project.app]
|
||||
],
|
||||
baselineOnly: true,
|
||||
verifyDiagnostics: true
|
||||
verifyTsc({
|
||||
scenario: "amdModulesWithOut",
|
||||
subScenario: "when the module resolution finds original source file",
|
||||
fs: () => outFileFs,
|
||||
commandLineArgs: ["-b", "/src/app", "--verbose"],
|
||||
modifyFs,
|
||||
baselineSourceMap: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace ts {
|
||||
|
||||
it("verify that subsequent builds after initial build doesnt build anything", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
createSolutionBuilder(host, ["/src"], { verbose: true }).build();
|
||||
host.assertDiagnosticMessages(
|
||||
getExpectedDiagnosticForProjectsInBuild("src/src/folder/tsconfig.json", "src/src/folder2/tsconfig.json", "src/src/tsconfig.json", "src/tests/tsconfig.json", "src/tsconfig.json"),
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
namespace ts {
|
||||
describe("unittests:: tsbuild:: on demo project", () => {
|
||||
let projFs: vfs.FileSystem;
|
||||
const { time } = getTime();
|
||||
|
||||
before(() => {
|
||||
projFs = loadProjectFromDisk("tests/projects/demo", time);
|
||||
projFs = loadProjectFromDisk("tests/projects/demo");
|
||||
});
|
||||
|
||||
after(() => {
|
||||
@@ -49,7 +47,7 @@ namespace ts {
|
||||
|
||||
function verifyBuild({ modifyDiskLayout, expectedExitStatus, expectedDiagnostics, expectedOutputs, notExpectedOutputs }: VerifyBuild) {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
modifyDiskLayout(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tsconfig.json"], { verbose: true });
|
||||
const exitStatus = builder.build();
|
||||
|
||||
@@ -1,109 +1,53 @@
|
||||
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);
|
||||
projFs = loadProjectFromDisk("tests/projects/emitDeclarationOnly");
|
||||
});
|
||||
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: {
|
||||
verifyTscIncrementalEdits({
|
||||
subScenario: `only dts output in circular import project with emitDeclarationOnly${disableMap ? "" : " and declarationMap"}`,
|
||||
fs: () => projFs,
|
||||
scenario: "emitDeclarationOnly",
|
||||
commandLineArgs: ["--b", "/src", "--verbose"],
|
||||
modifyFs: disableMap ?
|
||||
(fs => replaceText(fs, "/src/tsconfig.json", `"declarationMap": true,`, "")) :
|
||||
undefined,
|
||||
incrementalScenarios: [{
|
||||
buildKind: BuildKind.IncrementalDtsChange,
|
||||
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"; }`);
|
||||
verifyTscIncrementalEdits({
|
||||
subScenario: `only dts output in non circular imports project with emitDeclarationOnly`,
|
||||
fs: () => projFs,
|
||||
scenario: "emitDeclarationOnly",
|
||||
commandLineArgs: ["--b", "/src", "--verbose"],
|
||||
modifyFs: fs => {
|
||||
fs.rimrafSync("/src/src/index.ts");
|
||||
replaceText(fs, "/src/src/a.ts", `import { B } from "./b";`, `export class B { prop = "hello"; }`);
|
||||
},
|
||||
incrementalScenarios: [
|
||||
{
|
||||
buildKind: BuildKind.IncrementalDtsChange,
|
||||
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_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 { }
|
||||
{
|
||||
buildKind: BuildKind.IncrementalDtsUnchanged,
|
||||
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
|
||||
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace ts {
|
||||
describe("unittests:: tsbuild - empty files option in tsconfig", () => {
|
||||
it("has empty files diagnostic when files is empty and no references are provided", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/no-references"], { dry: false, force: false, verbose: false });
|
||||
|
||||
host.clearDiagnostics();
|
||||
@@ -26,7 +26,7 @@ namespace ts {
|
||||
|
||||
it("does not have empty files diagnostic when files is empty and references are provided", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/with-references"], { dry: false, force: false, verbose: false });
|
||||
|
||||
host.clearDiagnostics();
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace ts {
|
||||
|
||||
before(() => {
|
||||
const fs = new vfs.FileSystem(false);
|
||||
host = new fakes.SolutionBuilderHost(fs);
|
||||
host = fakes.SolutionBuilderHost.create(fs);
|
||||
writeProjects(fs, ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"], deps);
|
||||
});
|
||||
|
||||
|
||||
@@ -113,9 +113,11 @@ interface Symbol {
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* Load project from disk into /src folder
|
||||
*/
|
||||
export function loadProjectFromDisk(
|
||||
root: string,
|
||||
time?: vfs.FileSystemOptions["time"],
|
||||
libContentToAppend?: string
|
||||
): vfs.FileSystem {
|
||||
const resolver = vfs.createResolver(Harness.IO);
|
||||
@@ -125,22 +127,22 @@ interface Symbol {
|
||||
},
|
||||
cwd: "/",
|
||||
meta: { defaultLibLocation: "/lib" },
|
||||
time
|
||||
});
|
||||
addLibAndMakeReadonly(fs, libContentToAppend);
|
||||
return fs;
|
||||
}
|
||||
|
||||
/**
|
||||
* All the files must be in /src
|
||||
*/
|
||||
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;
|
||||
@@ -152,6 +154,26 @@ interface Symbol {
|
||||
fs.makeReadonly();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the FS mountuing existing fs's /src and /lib folder
|
||||
*/
|
||||
export function getFsWithTime(baseFs: vfs.FileSystem) {
|
||||
const { time, tick } = getTime();
|
||||
const host = new fakes.System(baseFs) as any as vfs.FileSystemResolverHost;
|
||||
host.getWorkspaceRoot = notImplemented;
|
||||
const resolver = vfs.createResolver(host);
|
||||
const fs = new vfs.FileSystem(/*ignoreCase*/ true, {
|
||||
files: {
|
||||
["/src"]: new vfs.Mount("/src", resolver),
|
||||
["/lib"]: new vfs.Mount("/lib", resolver)
|
||||
},
|
||||
cwd: "/",
|
||||
meta: { defaultLibLocation: "/lib" },
|
||||
time
|
||||
});
|
||||
return { fs, time, tick };
|
||||
}
|
||||
|
||||
export function verifyOutputsPresent(fs: vfs.FileSystem, outputs: readonly string[]) {
|
||||
for (const output of outputs) {
|
||||
assert(fs.existsSync(output), `Expect file ${output} to exist`);
|
||||
@@ -164,9 +186,10 @@ interface Symbol {
|
||||
}
|
||||
}
|
||||
|
||||
function generateSourceMapBaselineFiles(fs: vfs.FileSystem, mapFileNames: readonly string[]) {
|
||||
for (const mapFile of mapFileNames) {
|
||||
if (!fs.existsSync(mapFile)) continue;
|
||||
export function generateSourceMapBaselineFiles(fs: vfs.FileSystem, mapFileNames: Iterator<string>) {
|
||||
while (true) {
|
||||
const { value: mapFile, done } = mapFileNames.next();
|
||||
if (done) break;
|
||||
const text = Harness.SourceMapRecorder.getSourceMapRecordWithVFS(fs, mapFile);
|
||||
fs.writeFileSync(`${mapFile}.baseline.txt`, text);
|
||||
}
|
||||
@@ -229,234 +252,117 @@ interface Symbol {
|
||||
}
|
||||
}
|
||||
|
||||
interface BuildInput {
|
||||
fs: vfs.FileSystem;
|
||||
tick: () => void;
|
||||
rootNames: readonly string[];
|
||||
expectedMapFileNames?: readonly string[];
|
||||
expectedBuildInfoFilesForSectionBaselines?: readonly BuildInfoSectionBaselineFiles[];
|
||||
modifyFs: (fs: vfs.FileSystem) => void;
|
||||
}
|
||||
|
||||
function build({ fs, tick, rootNames, expectedMapFileNames, expectedBuildInfoFilesForSectionBaselines, modifyFs }: BuildInput) {
|
||||
const actualReadFileMap = createMap<number>();
|
||||
modifyFs(fs);
|
||||
tick();
|
||||
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const builder = createSolutionBuilder(host, rootNames, { dry: false, force: false, verbose: true });
|
||||
host.clearDiagnostics();
|
||||
const originalReadFile = host.readFile;
|
||||
host.readFile = path => {
|
||||
// Dont record libs
|
||||
if (path.startsWith("/src/")) {
|
||||
actualReadFileMap.set(path, (actualReadFileMap.get(path) || 0) + 1);
|
||||
export function baselineBuildInfo(
|
||||
configs: readonly ParsedCommandLine[],
|
||||
fs: vfs.FileSystem,
|
||||
writtenFiles: Map<true>
|
||||
) {
|
||||
let expectedBuildInfoFiles: BuildInfoSectionBaselineFiles[] | undefined;
|
||||
for (const { options } of configs) {
|
||||
const out = options.outFile || options.out;
|
||||
if (out) {
|
||||
const { jsFilePath, declarationFilePath, buildInfoPath } = getOutputPathsForBundle(options, /*forceDts*/ false);
|
||||
if (buildInfoPath && writtenFiles.has(buildInfoPath)) {
|
||||
(expectedBuildInfoFiles || (expectedBuildInfoFiles = [])).push(
|
||||
[buildInfoPath, jsFilePath, declarationFilePath]
|
||||
);
|
||||
}
|
||||
}
|
||||
return originalReadFile.call(host, path);
|
||||
};
|
||||
builder.build();
|
||||
if (expectedMapFileNames) generateSourceMapBaselineFiles(fs, expectedMapFileNames);
|
||||
generateBuildInfoSectionBaselineFiles(fs, expectedBuildInfoFilesForSectionBaselines || emptyArray);
|
||||
fs.makeReadonly();
|
||||
return { fs, actualReadFileMap, host, builder };
|
||||
}
|
||||
|
||||
function generateBaseline(fs: vfs.FileSystem, proj: string, scenario: string, subScenario: string, baseFs: vfs.FileSystem) {
|
||||
const patch = fs.diff(baseFs);
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
Harness.Baseline.runBaseline(`tsbuild/${proj}/${subScenario.split(" ").join("-")}/${scenario.split(" ").join("-")}.js`, patch ? vfs.formatPatch(patch) : null);
|
||||
}
|
||||
|
||||
function verifyReadFileCalls(actualReadFileMap: Map<number>, expectedReadFiles: ReadonlyMap<number>) {
|
||||
TestFSWithWatch.verifyMapSize("readFileCalls", actualReadFileMap, arrayFrom(expectedReadFiles.keys()));
|
||||
expectedReadFiles.forEach((expected, expectedFile) => {
|
||||
const actual = actualReadFileMap.get(expectedFile);
|
||||
assert.equal(actual, expected, `Mismatch in read file call number for: ${expectedFile}
|
||||
Not in Actual: ${JSON.stringify(arrayFrom(mapDefinedIterator(expectedReadFiles.keys(), f => actualReadFileMap.has(f) ? undefined : f)))}
|
||||
Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIterator(actualReadFileMap.entries(), ([p, v]) => expectedReadFiles.get(p) !== v ? [p, v, expectedReadFiles.get(p) || 0] : undefined)))}`);
|
||||
});
|
||||
}
|
||||
|
||||
export function getReadFilesMap(filesReadOnce: readonly string[], ...filesWithTwoReadCalls: string[]) {
|
||||
const map = arrayToMap(filesReadOnce, identity, () => 1);
|
||||
for (const fileWithTwoReadCalls of filesWithTwoReadCalls) {
|
||||
map.set(fileWithTwoReadCalls, 2);
|
||||
}
|
||||
return map;
|
||||
if (expectedBuildInfoFiles) generateBuildInfoSectionBaselineFiles(fs, expectedBuildInfoFiles);
|
||||
}
|
||||
|
||||
export interface ExpectedBuildOutput {
|
||||
expectedDiagnostics?: readonly fakes.ExpectedDiagnostic[];
|
||||
expectedReadFiles?: ReadonlyMap<number>;
|
||||
}
|
||||
|
||||
export interface BuildState extends ExpectedBuildOutput {
|
||||
export interface TscIncremental {
|
||||
buildKind: BuildKind;
|
||||
modifyFs: (fs: vfs.FileSystem) => void;
|
||||
subScenario?: string;
|
||||
}
|
||||
|
||||
export interface VerifyTsBuildInput {
|
||||
scenario: string;
|
||||
projFs: () => vfs.FileSystem;
|
||||
time: () => number;
|
||||
tick: () => void;
|
||||
proj: string;
|
||||
rootNames: readonly string[];
|
||||
/** map file names to generate baseline of */
|
||||
expectedMapFileNames?: readonly string[];
|
||||
expectedBuildInfoFilesForSectionBaselines?: readonly BuildInfoSectionBaselineFiles[];
|
||||
lastProjectOutput: string;
|
||||
initialBuild: BuildState;
|
||||
outputFiles?: readonly string[];
|
||||
incrementalDtsChangedBuild?: BuildState;
|
||||
incrementalDtsUnchangedBuild?: BuildState;
|
||||
incrementalHeaderChangedBuild?: BuildState;
|
||||
baselineOnly?: true;
|
||||
verifyDiagnostics?: true;
|
||||
export interface VerifyTsBuildInput extends TscCompile {
|
||||
incrementalScenarios: TscIncremental[];
|
||||
}
|
||||
|
||||
export function verifyTsbuildOutput({
|
||||
scenario, projFs, time, tick, proj, rootNames, outputFiles, baselineOnly, verifyDiagnostics,
|
||||
expectedMapFileNames, expectedBuildInfoFilesForSectionBaselines, lastProjectOutput,
|
||||
initialBuild, incrementalDtsChangedBuild, incrementalDtsUnchangedBuild, incrementalHeaderChangedBuild
|
||||
export function verifyTscIncrementalEdits({
|
||||
subScenario, fs, scenario, commandLineArgs,
|
||||
baselineSourceMap, modifyFs, baselineReadFileCalls,
|
||||
incrementalScenarios
|
||||
}: VerifyTsBuildInput) {
|
||||
describe(`tsc --b ${proj}:: ${scenario}`, () => {
|
||||
let fs: vfs.FileSystem;
|
||||
let actualReadFileMap: Map<number>;
|
||||
let firstBuildTime: number;
|
||||
let host: fakes.SolutionBuilderHost;
|
||||
describe(`tsc --b ${scenario}:: ${subScenario}`, () => {
|
||||
let tick: () => void;
|
||||
let sys: TscCompileSystem;
|
||||
before(() => {
|
||||
const result = build({
|
||||
fs: projFs().shadow(),
|
||||
tick,
|
||||
rootNames,
|
||||
expectedMapFileNames,
|
||||
expectedBuildInfoFilesForSectionBaselines,
|
||||
modifyFs: initialBuild.modifyFs,
|
||||
let baseFs: vfs.FileSystem;
|
||||
({ fs: baseFs, tick } = getFsWithTime(fs()));
|
||||
sys = tscCompile({
|
||||
scenario,
|
||||
subScenario,
|
||||
fs: () => baseFs.makeReadonly(),
|
||||
commandLineArgs,
|
||||
modifyFs: fs => {
|
||||
if (modifyFs) modifyFs(fs);
|
||||
tick();
|
||||
},
|
||||
baselineSourceMap,
|
||||
baselineReadFileCalls
|
||||
});
|
||||
({ fs, actualReadFileMap, host } = result);
|
||||
firstBuildTime = time();
|
||||
Debug.assert(!!incrementalScenarios.length, `${scenario}/${subScenario}:: No incremental scenarios, you probably want to use verifyTsc instead.`);
|
||||
});
|
||||
after(() => {
|
||||
fs = undefined!;
|
||||
actualReadFileMap = undefined!;
|
||||
host = undefined!;
|
||||
sys = undefined!;
|
||||
tick = undefined!;
|
||||
});
|
||||
describe("initialBuild", () => {
|
||||
if (!baselineOnly || verifyDiagnostics) {
|
||||
it(`verify diagnostics`, () => {
|
||||
host.assertDiagnosticMessages(...(initialBuild.expectedDiagnostics || emptyArray));
|
||||
});
|
||||
}
|
||||
it(`Generates files matching the baseline`, () => {
|
||||
generateBaseline(fs, proj, scenario, "initial Build", projFs());
|
||||
});
|
||||
if (!baselineOnly) {
|
||||
it("verify readFile calls", () => {
|
||||
verifyReadFileCalls(actualReadFileMap, Debug.assertDefined(initialBuild.expectedReadFiles));
|
||||
});
|
||||
}
|
||||
verifyTscBaseline(() => sys);
|
||||
});
|
||||
|
||||
function incrementalBuild(subScenario: string, incrementalModifyFs: (fs: vfs.FileSystem) => void, incrementalExpectedDiagnostics: readonly fakes.ExpectedDiagnostic[] | undefined, incrementalExpectedReadFiles: ReadonlyMap<number> | undefined) {
|
||||
describe(subScenario, () => {
|
||||
let newFs: vfs.FileSystem;
|
||||
let actualReadFileMap: Map<number>;
|
||||
let host: fakes.SolutionBuilderHost;
|
||||
let beforeBuildTime: number;
|
||||
let afterBuildTime: number;
|
||||
for (const { buildKind, modifyFs, subScenario: incrementalSubScenario } of incrementalScenarios) {
|
||||
describe(incrementalSubScenario || buildKind, () => {
|
||||
let newSys: TscCompileSystem;
|
||||
before(() => {
|
||||
beforeBuildTime = fs.statSync(lastProjectOutput).mtimeMs;
|
||||
Debug.assert(buildKind !== BuildKind.Initial, "Incremental edit cannot be initial compilation");
|
||||
tick();
|
||||
newFs = fs.shadow();
|
||||
tick();
|
||||
({ actualReadFileMap, host } = build({
|
||||
fs: newFs,
|
||||
tick,
|
||||
rootNames,
|
||||
expectedMapFileNames,
|
||||
expectedBuildInfoFilesForSectionBaselines,
|
||||
modifyFs: incrementalModifyFs,
|
||||
}));
|
||||
afterBuildTime = newFs.statSync(lastProjectOutput).mtimeMs;
|
||||
newSys = tscCompile({
|
||||
scenario,
|
||||
subScenario: incrementalSubScenario || subScenario,
|
||||
buildKind,
|
||||
fs: () => sys.vfs,
|
||||
commandLineArgs,
|
||||
modifyFs: fs => {
|
||||
tick();
|
||||
modifyFs(fs);
|
||||
tick();
|
||||
},
|
||||
baselineSourceMap,
|
||||
baselineReadFileCalls
|
||||
});
|
||||
});
|
||||
after(() => {
|
||||
newFs = undefined!;
|
||||
actualReadFileMap = undefined!;
|
||||
host = undefined!;
|
||||
newSys = undefined!;
|
||||
});
|
||||
it("verify build output times", () => {
|
||||
assert.equal(beforeBuildTime, firstBuildTime, "First build timestamp is correct");
|
||||
assert.equal(afterBuildTime, time(), "Second build timestamp is correct");
|
||||
});
|
||||
if (!baselineOnly || verifyDiagnostics) {
|
||||
it(`verify diagnostics`, () => {
|
||||
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);
|
||||
});
|
||||
if (!baselineOnly) {
|
||||
it("verify readFile calls", () => {
|
||||
verifyReadFileCalls(actualReadFileMap, Debug.assertDefined(incrementalExpectedReadFiles));
|
||||
});
|
||||
}
|
||||
verifyTscBaseline(() => newSys);
|
||||
it(`Verify emit output file text is same when built clean`, () => {
|
||||
const expectedOutputFiles = Debug.assertDefined(outputFiles);
|
||||
const { fs } = build({
|
||||
fs: newFs.shadow(),
|
||||
tick,
|
||||
rootNames,
|
||||
const sys = tscCompile({
|
||||
scenario,
|
||||
subScenario,
|
||||
fs: () => newSys.vfs,
|
||||
commandLineArgs,
|
||||
modifyFs: fs => {
|
||||
tick();
|
||||
// Delete output files
|
||||
for (const outputFile of expectedOutputFiles) {
|
||||
if (fs.existsSync(outputFile)) {
|
||||
fs.rimrafSync(outputFile);
|
||||
}
|
||||
}
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, commandLineArgs, { clean: true });
|
||||
builder.clean();
|
||||
},
|
||||
});
|
||||
|
||||
for (const outputFile of expectedOutputFiles) {
|
||||
const expectedText = fs.existsSync(outputFile) ? fs.readFileSync(outputFile, "utf8") : undefined;
|
||||
const actualText = newFs.existsSync(outputFile) ? newFs.readFileSync(outputFile, "utf8") : undefined;
|
||||
for (const outputFile of arrayFrom(sys.writtenFiles.keys())) {
|
||||
const expectedText = sys.readFile(outputFile);
|
||||
const actualText = newSys.readFile(outputFile);
|
||||
assert.equal(actualText, expectedText, `File: ${outputFile}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
if (incrementalDtsChangedBuild) {
|
||||
incrementalBuild(
|
||||
"incremental declaration changes",
|
||||
incrementalDtsChangedBuild.modifyFs,
|
||||
incrementalDtsChangedBuild.expectedDiagnostics,
|
||||
incrementalDtsChangedBuild.expectedReadFiles,
|
||||
);
|
||||
}
|
||||
|
||||
if (incrementalDtsUnchangedBuild) {
|
||||
incrementalBuild(
|
||||
"incremental declaration doesnt change",
|
||||
incrementalDtsUnchangedBuild.modifyFs,
|
||||
incrementalDtsUnchangedBuild.expectedDiagnostics,
|
||||
incrementalDtsUnchangedBuild.expectedReadFiles
|
||||
);
|
||||
}
|
||||
|
||||
if (incrementalHeaderChangedBuild) {
|
||||
incrementalBuild(
|
||||
"incremental headers change without dts changes",
|
||||
incrementalHeaderChangedBuild.modifyFs,
|
||||
incrementalHeaderChangedBuild.expectedDiagnostics,
|
||||
incrementalHeaderChangedBuild.expectedReadFiles
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,48 +1,59 @@
|
||||
namespace ts {
|
||||
describe("unittests:: tsbuild:: inferredTypeFromTransitiveModule::", () => {
|
||||
let projFs: vfs.FileSystem;
|
||||
const { time, tick } = getTime();
|
||||
before(() => {
|
||||
projFs = loadProjectFromDisk("tests/projects/inferredTypeFromTransitiveModule", time);
|
||||
projFs = loadProjectFromDisk("tests/projects/inferredTypeFromTransitiveModule");
|
||||
});
|
||||
after(() => {
|
||||
projFs = undefined!;
|
||||
});
|
||||
|
||||
verifyTsbuildOutput({
|
||||
scenario: "inferred type from transitive module",
|
||||
projFs: () => projFs,
|
||||
time,
|
||||
tick,
|
||||
proj: "inferredTypeFromTransitiveModule",
|
||||
rootNames: ["/src"],
|
||||
lastProjectOutput: `/src/obj/index.js`,
|
||||
outputFiles: [
|
||||
"/src/obj/bar.js", "/src/obj/bar.d.ts",
|
||||
"/src/obj/bundling.js", "/src/obj/bundling.d.ts",
|
||||
"/src/obj/lazyIndex.js", "/src/obj/lazyIndex.d.ts",
|
||||
"/src/obj/index.js", "/src/obj/index.d.ts",
|
||||
"/src/obj/tsconfig.tsbuildinfo"
|
||||
],
|
||||
initialBuild: {
|
||||
modifyFs: noop,
|
||||
expectedDiagnostics: [
|
||||
getExpectedDiagnosticForProjectsInBuild("src/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/tsconfig.json", "src/obj/bar.js"],
|
||||
[Diagnostics.Building_project_0, "/src/tsconfig.json"]
|
||||
]
|
||||
verifyTscIncrementalEdits({
|
||||
scenario: "inferredTypeFromTransitiveModule",
|
||||
subScenario: "inferred type from transitive module",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src", "--verbose"],
|
||||
incrementalScenarios: [{
|
||||
buildKind: BuildKind.IncrementalDtsChange,
|
||||
modifyFs: changeBarParam,
|
||||
}],
|
||||
});
|
||||
|
||||
verifyTscIncrementalEdits({
|
||||
subScenario: "inferred type from transitive module with isolatedModules",
|
||||
fs: () => projFs,
|
||||
scenario: "inferredTypeFromTransitiveModule",
|
||||
commandLineArgs: ["--b", "/src", "--verbose"],
|
||||
modifyFs: changeToIsolatedModules,
|
||||
incrementalScenarios: [{
|
||||
buildKind: BuildKind.IncrementalDtsChange,
|
||||
modifyFs: changeBarParam
|
||||
}]
|
||||
});
|
||||
|
||||
verifyTscIncrementalEdits({
|
||||
scenario: "inferredTypeFromTransitiveModule",
|
||||
subScenario: "reports errors in files affected by change in signature with isolatedModules",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src", "--verbose"],
|
||||
modifyFs: fs => {
|
||||
changeToIsolatedModules(fs);
|
||||
appendText(fs, "/src/lazyIndex.ts", `
|
||||
import { default as bar } from './bar';
|
||||
bar("hello");`);
|
||||
},
|
||||
incrementalDtsChangedBuild: {
|
||||
modifyFs: fs => replaceText(fs, "/src/bar.ts", "param: string", ""),
|
||||
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/obj/bar.js", "src/bar.ts"],
|
||||
[Diagnostics.Building_project_0, "/src/tsconfig.json"],
|
||||
[Diagnostics.Updating_unchanged_output_timestamps_of_project_0, "/src/tsconfig.json"]
|
||||
]
|
||||
},
|
||||
baselineOnly: true,
|
||||
verifyDiagnostics: true
|
||||
incrementalScenarios: [{
|
||||
buildKind: BuildKind.IncrementalDtsChange,
|
||||
modifyFs: changeBarParam
|
||||
}]
|
||||
});
|
||||
});
|
||||
|
||||
function changeToIsolatedModules(fs: vfs.FileSystem) {
|
||||
replaceText(fs, "/src/tsconfig.json", `"incremental": true`, `"incremental": true, "isolatedModules": true`);
|
||||
}
|
||||
|
||||
function changeBarParam(fs: vfs.FileSystem) {
|
||||
replaceText(fs, "/src/bar.ts", "param: string", "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +1,22 @@
|
||||
namespace ts {
|
||||
describe("unittests:: tsbuild:: lateBoundSymbol:: interface is merged and contains late bound member", () => {
|
||||
let projFs: vfs.FileSystem;
|
||||
const { time, tick } = getTime();
|
||||
before(() => {
|
||||
projFs = loadProjectFromDisk("tests/projects/lateBoundSymbol", time);
|
||||
projFs = loadProjectFromDisk("tests/projects/lateBoundSymbol");
|
||||
});
|
||||
after(() => {
|
||||
projFs = undefined!; // Release the contents
|
||||
});
|
||||
|
||||
verifyTsbuildOutput({
|
||||
scenario: "interface is merged and contains late bound member",
|
||||
projFs: () => projFs,
|
||||
time,
|
||||
tick,
|
||||
proj: "lateBoundSymbol",
|
||||
rootNames: ["/src/tsconfig.json"],
|
||||
lastProjectOutput: "/src/src/main.js",
|
||||
outputFiles: [
|
||||
"/src/src/hkt.js",
|
||||
"/src/src/main.js",
|
||||
"/src/tsconfig.tsbuildinfo",
|
||||
],
|
||||
initialBuild: {
|
||||
modifyFs: noop,
|
||||
expectedDiagnostics: [
|
||||
getExpectedDiagnosticForProjectsInBuild("src/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/tsconfig.json", "src/src/hkt.js"],
|
||||
[Diagnostics.Building_project_0, "/src/tsconfig.json"]
|
||||
]
|
||||
},
|
||||
incrementalDtsUnchangedBuild: {
|
||||
verifyTscIncrementalEdits({
|
||||
subScenario: "interface is merged and contains late bound member",
|
||||
fs: () => projFs,
|
||||
scenario: "lateBoundSymbol",
|
||||
commandLineArgs: ["--b", "/src/tsconfig.json", "--verbose"],
|
||||
incrementalScenarios: [{
|
||||
buildKind: BuildKind.IncrementalDtsUnchanged,
|
||||
modifyFs: fs => replaceText(fs, "/src/src/main.ts", "const x = 10;", ""),
|
||||
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/src/hkt.js", "src/src/main.ts"],
|
||||
[Diagnostics.Building_project_0, "/src/tsconfig.json"],
|
||||
[Diagnostics.Updating_unchanged_output_timestamps_of_project_0, "/src/tsconfig.json"]
|
||||
]
|
||||
},
|
||||
baselineOnly: true,
|
||||
verifyDiagnostics: true
|
||||
}]
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace ts {
|
||||
it("unittests:: tsbuild - when tsconfig extends the missing file", () => {
|
||||
const projFs = loadProjectFromDisk("tests/projects/missingExtendedConfig");
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tsconfig.json"], {});
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
namespace ts {
|
||||
// https://github.com/microsoft/TypeScript/issues/31696
|
||||
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`
|
||||
verifyTsc({
|
||||
scenario: "moduleSpecifiers",
|
||||
subScenario: `synthesized module specifiers resolve correctly`,
|
||||
fs: () => loadProjectFromFiles({
|
||||
"/src/solution/common/nominal.ts": utils.dedent`
|
||||
export declare type Nominal<T, Name extends string> = T & {
|
||||
[Symbol.species]: Name;
|
||||
};
|
||||
`,
|
||||
"/src/common/tsconfig.json": utils.dedent`
|
||||
"/src/solution/common/tsconfig.json": utils.dedent`
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
@@ -18,12 +18,12 @@ namespace ts {
|
||||
},
|
||||
"include": ["nominal.ts"]
|
||||
}`,
|
||||
"/src/sub-project/index.ts": utils.dedent`
|
||||
"/src/solution/sub-project/index.ts": utils.dedent`
|
||||
import { Nominal } from '../common/nominal';
|
||||
|
||||
export type MyNominal = Nominal<string, 'MyNominal'>;
|
||||
`,
|
||||
"/src/sub-project/tsconfig.json": utils.dedent`
|
||||
"/src/solution/sub-project/tsconfig.json": utils.dedent`
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
@@ -34,7 +34,7 @@ namespace ts {
|
||||
],
|
||||
"include": ["./index.ts"]
|
||||
}`,
|
||||
"/src/sub-project-2/index.ts": utils.dedent`
|
||||
"/src/solution/sub-project-2/index.ts": utils.dedent`
|
||||
import { MyNominal } from '../sub-project/index';
|
||||
|
||||
const variable = {
|
||||
@@ -45,7 +45,7 @@ namespace ts {
|
||||
return 'key';
|
||||
}
|
||||
`,
|
||||
"/src/sub-project-2/tsconfig.json": utils.dedent`
|
||||
"/src/solution/sub-project-2/tsconfig.json": utils.dedent`
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
@@ -56,7 +56,7 @@ namespace ts {
|
||||
],
|
||||
"include": ["./index.ts"]
|
||||
}`,
|
||||
"/src/tsconfig.json": utils.dedent`
|
||||
"/src/solution/tsconfig.json": utils.dedent`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true
|
||||
@@ -67,7 +67,7 @@ namespace ts {
|
||||
],
|
||||
"include": []
|
||||
}`,
|
||||
"/tsconfig.base.json": utils.dedent`
|
||||
"/src/tsconfig.base.json": utils.dedent`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"skipLibCheck": true,
|
||||
@@ -75,32 +75,17 @@ namespace ts {
|
||||
"outDir": "lib",
|
||||
}
|
||||
}`,
|
||||
"/tsconfig.json": utils.dedent`{
|
||||
"/src/tsconfig.json": utils.dedent`{
|
||||
"compilerOptions": {
|
||||
"composite": true
|
||||
},
|
||||
"references": [
|
||||
{ "path": "./src" }
|
||||
{ "path": "./solution" }
|
||||
],
|
||||
"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,
|
||||
},
|
||||
baselineOnly: true
|
||||
}, symbolLibContent),
|
||||
commandLineArgs: ["-b", "/src", "--verbose"]
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -55,21 +55,7 @@ namespace ts {
|
||||
]
|
||||
]
|
||||
];
|
||||
const expectedMapFileNames = [
|
||||
outputFiles[project.first][ext.jsmap],
|
||||
outputFiles[project.first][ext.dtsmap],
|
||||
outputFiles[project.second][ext.jsmap],
|
||||
outputFiles[project.second][ext.dtsmap],
|
||||
outputFiles[project.third][ext.jsmap],
|
||||
outputFiles[project.third][ext.dtsmap]
|
||||
];
|
||||
const expectedTsbuildInfoFileNames: readonly BuildInfoSectionBaselineFiles[] = [
|
||||
[outputFiles[project.first][ext.buildinfo], outputFiles[project.first][ext.js], outputFiles[project.first][ext.dts]],
|
||||
[outputFiles[project.second][ext.buildinfo], outputFiles[project.second][ext.js], outputFiles[project.second][ext.dts]],
|
||||
[outputFiles[project.third][ext.buildinfo], outputFiles[project.third][ext.js], outputFiles[project.third][ext.dts]]
|
||||
];
|
||||
const relSources = sources.map(([config, sources]) => [relName(config), sources.map(relName)]) as any as [Sources, Sources, Sources];
|
||||
const { time, tick } = getTime();
|
||||
let expectedOutputFiles = [
|
||||
...outputFiles[project.first],
|
||||
...outputFiles[project.second],
|
||||
@@ -84,247 +70,78 @@ namespace ts {
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, relSources[project.third][source.config], relOutputFiles[project.third][ext.js]],
|
||||
[Diagnostics.Building_project_0, sources[project.third][source.config]]
|
||||
];
|
||||
let initialExpectedReadFiles: ReadonlyMap<number> = getReadFilesMap(
|
||||
[
|
||||
// Configs
|
||||
sources[project.first][source.config],
|
||||
sources[project.second][source.config],
|
||||
sources[project.third][source.config],
|
||||
|
||||
// Source files
|
||||
...sources[project.first][source.ts],
|
||||
...sources[project.second][source.ts],
|
||||
...sources[project.third][source.ts],
|
||||
|
||||
// outputs
|
||||
...outputFiles[project.first],
|
||||
...outputFiles[project.second],
|
||||
]
|
||||
);
|
||||
|
||||
let dtsChangedExpectedDiagnostics: readonly fakes.ExpectedDiagnostic[] = [
|
||||
getExpectedDiagnosticForProjectsInBuild(relSources[project.first][source.config], relSources[project.second][source.config], relSources[project.third][source.config]),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, relSources[project.first][source.config], relOutputFiles[project.first][ext.js], relSources[project.first][source.ts][part.one]],
|
||||
[Diagnostics.Building_project_0, sources[project.first][source.config]],
|
||||
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, relSources[project.second][source.config], relSources[project.second][source.ts][part.one], relOutputFiles[project.second][ext.js]],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, relSources[project.third][source.config], relOutputFiles[project.third][ext.js], "src/first"],
|
||||
[Diagnostics.Building_project_0, sources[project.third][source.config]]
|
||||
];
|
||||
let dtsChangedExpectedReadFiles: ReadonlyMap<number> = getReadFilesMap(
|
||||
[
|
||||
// Configs
|
||||
sources[project.first][source.config],
|
||||
sources[project.second][source.config],
|
||||
sources[project.third][source.config],
|
||||
|
||||
// Source files
|
||||
...sources[project.first][source.ts],
|
||||
...sources[project.third][source.ts],
|
||||
|
||||
// outputs
|
||||
...outputFiles[project.first],
|
||||
...outputFiles[project.second],
|
||||
outputFiles[project.third][ext.dts],
|
||||
],
|
||||
outputFiles[project.first][ext.dts], // dts changes so once read old content, and once new (to emit third)
|
||||
);
|
||||
|
||||
let dtsChangedExpectedDiagnosticsDependOrdered: readonly fakes.ExpectedDiagnostic[] = [
|
||||
getExpectedDiagnosticForProjectsInBuild(relSources[project.first][source.config], relSources[project.second][source.config], relSources[project.third][source.config]),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, relSources[project.first][source.config], relOutputFiles[project.first][ext.js], relSources[project.first][source.ts][part.one]],
|
||||
[Diagnostics.Building_project_0, sources[project.first][source.config]],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, relSources[project.second][source.config], relOutputFiles[project.second][ext.js], "src/first"],
|
||||
[Diagnostics.Building_project_0, sources[project.second][source.config]],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, relSources[project.third][source.config], relOutputFiles[project.third][ext.js], "src/second"],
|
||||
[Diagnostics.Building_project_0, sources[project.third][source.config]]
|
||||
];
|
||||
let dtsChangedExpectedReadFilesDependOrdered: ReadonlyMap<number> = getDtsChangedReadFilesDependOrdered();
|
||||
|
||||
let dtsUnchangedExpectedDiagnostics: readonly fakes.ExpectedDiagnostic[] = [
|
||||
getExpectedDiagnosticForProjectsInBuild(relSources[project.first][source.config], relSources[project.second][source.config], relSources[project.third][source.config]),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, relSources[project.first][source.config], relOutputFiles[project.first][ext.js], relSources[project.first][source.ts][part.one]],
|
||||
[Diagnostics.Building_project_0, sources[project.first][source.config]],
|
||||
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, relSources[project.second][source.config], relSources[project.second][source.ts][part.one], relOutputFiles[project.second][ext.js]],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_of_its_dependency_1_has_changed, relSources[project.third][source.config], "src/first"],
|
||||
[Diagnostics.Updating_output_of_project_0, sources[project.third][source.config]],
|
||||
[Diagnostics.Updating_unchanged_output_timestamps_of_project_0, sources[project.third][source.config]],
|
||||
];
|
||||
let dtsUnchangedExpectedReadFiles: ReadonlyMap<number> = getReadFilesMap(
|
||||
[
|
||||
// Configs
|
||||
sources[project.first][source.config],
|
||||
sources[project.second][source.config],
|
||||
sources[project.third][source.config],
|
||||
|
||||
// Source files
|
||||
...sources[project.first][source.ts],
|
||||
|
||||
// outputs to prepend
|
||||
...outputFiles[project.first],
|
||||
...outputFiles[project.second],
|
||||
...outputFiles[project.third],
|
||||
]
|
||||
);
|
||||
|
||||
let dtsUnchangedExpectedDiagnosticsDependOrdered: readonly fakes.ExpectedDiagnostic[] = [
|
||||
getExpectedDiagnosticForProjectsInBuild(relSources[project.first][source.config], relSources[project.second][source.config], relSources[project.third][source.config]),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, relSources[project.first][source.config], relOutputFiles[project.first][ext.js], relSources[project.first][source.ts][part.one]],
|
||||
[Diagnostics.Building_project_0, sources[project.first][source.config]],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_of_its_dependency_1_has_changed, relSources[project.second][source.config], "src/first"],
|
||||
[Diagnostics.Updating_output_of_project_0, sources[project.second][source.config]],
|
||||
[Diagnostics.Updating_unchanged_output_timestamps_of_project_0, sources[project.second][source.config]],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_of_its_dependency_1_has_changed, relSources[project.third][source.config], "src/second"],
|
||||
[Diagnostics.Updating_output_of_project_0, sources[project.third][source.config]],
|
||||
[Diagnostics.Updating_unchanged_output_timestamps_of_project_0, sources[project.third][source.config]],
|
||||
];
|
||||
let dtsUnchangedExpectedReadFilesDependOrdered: ReadonlyMap<number> = getDtsUnchangedExpectedReadFilesDependOrdered();
|
||||
|
||||
before(() => {
|
||||
outFileFs = loadProjectFromDisk("tests/projects/outfile-concat", time);
|
||||
outFileFs = loadProjectFromDisk("tests/projects/outfile-concat");
|
||||
});
|
||||
after(() => {
|
||||
outFileFs = undefined!;
|
||||
expectedOutputFiles = undefined!;
|
||||
initialExpectedDiagnostics = undefined!;
|
||||
initialExpectedReadFiles = undefined!;
|
||||
dtsChangedExpectedDiagnostics = undefined!;
|
||||
dtsChangedExpectedReadFiles = undefined!;
|
||||
dtsChangedExpectedDiagnosticsDependOrdered = undefined!;
|
||||
dtsChangedExpectedReadFilesDependOrdered = undefined!;
|
||||
dtsUnchangedExpectedDiagnostics = undefined!;
|
||||
dtsUnchangedExpectedReadFiles = undefined!;
|
||||
dtsUnchangedExpectedDiagnosticsDependOrdered = undefined!;
|
||||
dtsUnchangedExpectedReadFilesDependOrdered = undefined!;
|
||||
});
|
||||
|
||||
function createSolutionBuilder(host: fakes.SolutionBuilderHost, baseOptions?: BuildOptions) {
|
||||
return ts.createSolutionBuilder(host, ["/src/third"], { dry: false, force: false, verbose: true, ...(baseOptions || {}) });
|
||||
}
|
||||
|
||||
function getInitialExpectedReadFiles(additionalSourceFiles?: readonly string[]) {
|
||||
if (!additionalSourceFiles) return initialExpectedReadFiles;
|
||||
const expectedReadFiles = cloneMap(initialExpectedReadFiles);
|
||||
for (const path of additionalSourceFiles) {
|
||||
expectedReadFiles.set(path, 1);
|
||||
}
|
||||
return expectedReadFiles;
|
||||
}
|
||||
|
||||
function getDtsChangedReadFilesDependOrdered() {
|
||||
const value = cloneMap(dtsChangedExpectedReadFiles);
|
||||
for (const path of sources[project.second][source.ts]) {
|
||||
value.set(path, 1);
|
||||
}
|
||||
value.set(outputFiles[project.second][ext.dts], 2); // dts changes so once read old content, and once new (to emit third)
|
||||
return value;
|
||||
}
|
||||
|
||||
function getDtsChangedReadFiles(dependOrdered?: boolean, additionalSourceFiles?: readonly string[]) {
|
||||
const value = dependOrdered ? dtsChangedExpectedReadFilesDependOrdered : dtsChangedExpectedReadFiles;
|
||||
if (!additionalSourceFiles) return value;
|
||||
const expectedReadFiles = cloneMap(value);
|
||||
for (const path of additionalSourceFiles) {
|
||||
expectedReadFiles.set(path, 1);
|
||||
}
|
||||
return expectedReadFiles;
|
||||
}
|
||||
|
||||
function getDtsUnchangedExpectedReadFilesDependOrdered() {
|
||||
const value = cloneMap(dtsUnchangedExpectedReadFiles);
|
||||
// Since this changes too
|
||||
for (const path of outputFiles[project.second]) {
|
||||
value.set(path, 2);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function getDtsUnchangedReadFiles(dependOrdered?: boolean, additionalSourceFiles?: readonly string[]) {
|
||||
const value = dependOrdered ? dtsUnchangedExpectedReadFilesDependOrdered : dtsUnchangedExpectedReadFiles;
|
||||
if (!additionalSourceFiles || additionalSourceFiles.length !== 3) return value;
|
||||
const expectedReadFiles = cloneMap(value);
|
||||
// Additional source Files
|
||||
expectedReadFiles.set(additionalSourceFiles[project.first], 1);
|
||||
return expectedReadFiles;
|
||||
}
|
||||
|
||||
interface VerifyOutFileScenarioInput {
|
||||
scenario: string;
|
||||
modifyFs: (fs: vfs.FileSystem) => void;
|
||||
subScenario: string;
|
||||
modifyFs?: (fs: vfs.FileSystem) => void;
|
||||
modifyAgainFs?: (fs: vfs.FileSystem) => void;
|
||||
additionalSourceFiles?: readonly string[];
|
||||
expectedBuildInfoFilesForSectionBaselines?: readonly BuildInfoSectionBaselineFiles[];
|
||||
dependOrdered?: true;
|
||||
ignoreDtsChanged?: true;
|
||||
ignoreDtsUnchanged?: true;
|
||||
baselineOnly?: true;
|
||||
}
|
||||
|
||||
function verifyOutFileScenario({
|
||||
scenario,
|
||||
subScenario,
|
||||
modifyFs,
|
||||
modifyAgainFs,
|
||||
additionalSourceFiles,
|
||||
expectedBuildInfoFilesForSectionBaselines,
|
||||
dependOrdered,
|
||||
ignoreDtsChanged,
|
||||
ignoreDtsUnchanged,
|
||||
baselineOnly
|
||||
}: VerifyOutFileScenarioInput) {
|
||||
const initialExpectedReadFiles = !baselineOnly ? getInitialExpectedReadFiles(additionalSourceFiles) : undefined;
|
||||
const dtsChangedReadFiles = !baselineOnly && !ignoreDtsChanged ? getDtsChangedReadFiles(dependOrdered, additionalSourceFiles) : undefined;
|
||||
const dtsUnchanged: ExpectedBuildOutput | undefined = !baselineOnly && (!ignoreDtsUnchanged || !modifyAgainFs) ? {
|
||||
expectedDiagnostics: dependOrdered ?
|
||||
dtsUnchangedExpectedDiagnosticsDependOrdered :
|
||||
dtsUnchangedExpectedDiagnostics,
|
||||
expectedReadFiles: getDtsUnchangedReadFiles(dependOrdered, additionalSourceFiles)
|
||||
} : undefined;
|
||||
|
||||
verifyTsbuildOutput({
|
||||
scenario,
|
||||
projFs: () => outFileFs,
|
||||
time,
|
||||
tick,
|
||||
proj: "outfile-concat",
|
||||
rootNames: ["/src/third"],
|
||||
expectedMapFileNames,
|
||||
expectedBuildInfoFilesForSectionBaselines: expectedBuildInfoFilesForSectionBaselines || expectedTsbuildInfoFileNames,
|
||||
lastProjectOutput: outputFiles[project.third][ext.js],
|
||||
initialBuild: {
|
||||
modifyFs,
|
||||
expectedDiagnostics: initialExpectedDiagnostics,
|
||||
expectedReadFiles: initialExpectedReadFiles
|
||||
},
|
||||
incrementalDtsChangedBuild: !ignoreDtsChanged ? {
|
||||
const incrementalScenarios: TscIncremental[] = [];
|
||||
if (!ignoreDtsChanged) {
|
||||
incrementalScenarios.push({
|
||||
buildKind: BuildKind.IncrementalDtsChange,
|
||||
modifyFs: fs => replaceText(fs, relSources[project.first][source.ts][part.one], "Hello", "Hola"),
|
||||
expectedDiagnostics: dependOrdered ?
|
||||
dtsChangedExpectedDiagnosticsDependOrdered :
|
||||
dtsChangedExpectedDiagnostics,
|
||||
expectedReadFiles: dtsChangedReadFiles
|
||||
} : undefined,
|
||||
incrementalDtsUnchangedBuild: !ignoreDtsUnchanged ? {
|
||||
});
|
||||
}
|
||||
if (!ignoreDtsUnchanged) {
|
||||
incrementalScenarios.push({
|
||||
buildKind: BuildKind.IncrementalDtsUnchanged,
|
||||
modifyFs: fs => appendText(fs, relSources[project.first][source.ts][part.one], "console.log(s);"),
|
||||
expectedDiagnostics: dtsUnchanged && dtsUnchanged.expectedDiagnostics,
|
||||
expectedReadFiles: dtsUnchanged && dtsUnchanged.expectedReadFiles
|
||||
} : undefined,
|
||||
incrementalHeaderChangedBuild: modifyAgainFs ? {
|
||||
modifyFs: modifyAgainFs,
|
||||
expectedDiagnostics: dtsUnchanged && dtsUnchanged.expectedDiagnostics,
|
||||
expectedReadFiles: dtsUnchanged && dtsUnchanged.expectedReadFiles
|
||||
} : undefined,
|
||||
outputFiles: expectedOutputFiles,
|
||||
baselineOnly
|
||||
});
|
||||
});
|
||||
}
|
||||
if (modifyAgainFs) {
|
||||
incrementalScenarios.push({
|
||||
buildKind: BuildKind.IncrementalHeadersChange,
|
||||
modifyFs: modifyAgainFs
|
||||
});
|
||||
}
|
||||
const input: VerifyTsBuildInput = {
|
||||
subScenario,
|
||||
fs: () => outFileFs,
|
||||
scenario: "outfile-concat",
|
||||
commandLineArgs: ["--b", "/src/third", "--verbose"],
|
||||
baselineSourceMap: true,
|
||||
modifyFs,
|
||||
baselineReadFileCalls: !baselineOnly,
|
||||
incrementalScenarios,
|
||||
};
|
||||
return incrementalScenarios.length ?
|
||||
verifyTscIncrementalEdits(input) :
|
||||
verifyTsc(input);
|
||||
}
|
||||
|
||||
// Verify initial + incremental edits
|
||||
verifyOutFileScenario({
|
||||
scenario: "baseline sectioned sourcemaps",
|
||||
modifyFs: noop
|
||||
subScenario: "baseline sectioned sourcemaps",
|
||||
});
|
||||
|
||||
// Verify baseline with build info + dts unChanged
|
||||
verifyOutFileScenario({
|
||||
scenario: "when final project is not composite but uses project references",
|
||||
subScenario: "when final project is not composite but uses project references",
|
||||
modifyFs: fs => replaceText(fs, sources[project.third][source.config], `"composite": true,`, ""),
|
||||
ignoreDtsChanged: true,
|
||||
baselineOnly: true
|
||||
@@ -332,7 +149,7 @@ namespace ts {
|
||||
|
||||
// Verify baseline with build info
|
||||
verifyOutFileScenario({
|
||||
scenario: "when final project is not composite but incremental",
|
||||
subScenario: "when final project is not composite but incremental",
|
||||
modifyFs: fs => replaceText(fs, sources[project.third][source.config], `"composite": true,`, `"incremental": true,`),
|
||||
ignoreDtsChanged: true,
|
||||
ignoreDtsUnchanged: true,
|
||||
@@ -341,14 +158,9 @@ namespace ts {
|
||||
|
||||
// Verify baseline with build info
|
||||
verifyOutFileScenario({
|
||||
scenario: "when final project specifies tsBuildInfoFile",
|
||||
subScenario: "when final project specifies tsBuildInfoFile",
|
||||
modifyFs: fs => replaceText(fs, sources[project.third][source.config], `"composite": true,`, `"composite": true,
|
||||
"tsBuildInfoFile": "./thirdjs/output/third.tsbuildinfo",`),
|
||||
expectedBuildInfoFilesForSectionBaselines: [
|
||||
expectedTsbuildInfoFileNames[0],
|
||||
expectedTsbuildInfoFileNames[1],
|
||||
["/src/third/thirdjs/output/third.tsbuildinfo", expectedTsbuildInfoFileNames[2][1], expectedTsbuildInfoFileNames[2][2]]
|
||||
],
|
||||
ignoreDtsChanged: true,
|
||||
ignoreDtsUnchanged: true,
|
||||
baselineOnly: true
|
||||
@@ -361,7 +173,7 @@ namespace ts {
|
||||
...outputFiles[project.second],
|
||||
...outputFiles[project.third]
|
||||
];
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host);
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(...initialExpectedDiagnostics);
|
||||
@@ -377,13 +189,13 @@ namespace ts {
|
||||
});
|
||||
|
||||
it("verify buildInfo absence results in new build", () => {
|
||||
const fs = outFileFs.shadow();
|
||||
const { fs, tick } = getFsWithTime(outFileFs);
|
||||
const expectedOutputs = [
|
||||
...outputFiles[project.first],
|
||||
...outputFiles[project.second],
|
||||
...outputFiles[project.third]
|
||||
];
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
let builder = createSolutionBuilder(host);
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(...initialExpectedDiagnostics);
|
||||
@@ -391,7 +203,11 @@ namespace ts {
|
||||
verifyOutputsPresent(fs, expectedOutputs);
|
||||
// Delete bundle info
|
||||
host.clearDiagnostics();
|
||||
|
||||
tick();
|
||||
host.deleteFile(outputFiles[project.first][ext.buildinfo]);
|
||||
tick();
|
||||
|
||||
builder = createSolutionBuilder(host);
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
@@ -407,7 +223,7 @@ namespace ts {
|
||||
|
||||
it("verify that if incremental is set to false, tsbuildinfo is not generated", () => {
|
||||
const fs = outFileFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
replaceText(fs, sources[project.third][source.config], `"composite": true,`, "");
|
||||
const builder = createSolutionBuilder(host);
|
||||
builder.build();
|
||||
@@ -418,14 +234,16 @@ namespace ts {
|
||||
});
|
||||
|
||||
it("rebuilds completely when version in tsbuildinfo doesnt match ts version", () => {
|
||||
const fs = outFileFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const { fs, tick } = getFsWithTime(outFileFs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
let builder = createSolutionBuilder(host);
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(...initialExpectedDiagnostics);
|
||||
host.clearDiagnostics();
|
||||
tick();
|
||||
builder = createSolutionBuilder(host);
|
||||
changeCompilerVersion(host);
|
||||
tick();
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
getExpectedDiagnosticForProjectsInBuild(relSources[project.first][source.config], relSources[project.second][source.config], relSources[project.third][source.config]),
|
||||
@@ -439,12 +257,12 @@ namespace ts {
|
||||
});
|
||||
|
||||
it("rebuilds completely when command line incremental flag changes between non dts changes", () => {
|
||||
const fs = outFileFs.shadow();
|
||||
const { fs, tick } = getFsWithTime(outFileFs);
|
||||
// Make non composite third project
|
||||
replaceText(fs, sources[project.third][source.config], `"composite": true,`, "");
|
||||
|
||||
// Build with command line incremental
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
let builder = createSolutionBuilder(host, { incremental: true });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(...initialExpectedDiagnostics);
|
||||
@@ -483,7 +301,7 @@ namespace ts {
|
||||
|
||||
it("builds till project specified", () => {
|
||||
const fs = outFileFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, { verbose: false });
|
||||
const result = builder.build(sources[project.second][source.config]);
|
||||
host.assertDiagnosticMessages(/*empty*/);
|
||||
@@ -496,7 +314,7 @@ namespace ts {
|
||||
|
||||
it("cleans till project specified", () => {
|
||||
const fs = outFileFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, { verbose: false });
|
||||
builder.build();
|
||||
const result = builder.clean(sources[project.second][source.config]);
|
||||
@@ -513,7 +331,7 @@ namespace ts {
|
||||
describe("Prologues", () => {
|
||||
// Verify initial + incremental edits
|
||||
verifyOutFileScenario({
|
||||
scenario: "strict in all projects",
|
||||
subScenario: "strict in all projects",
|
||||
modifyFs: fs => {
|
||||
enableStrict(fs, sources[project.first][source.config]);
|
||||
enableStrict(fs, sources[project.second][source.config]);
|
||||
@@ -524,7 +342,7 @@ namespace ts {
|
||||
|
||||
// Verify ignore dtsChanged
|
||||
verifyOutFileScenario({
|
||||
scenario: "strict in one dependency",
|
||||
subScenario: "strict in one dependency",
|
||||
modifyFs: fs => enableStrict(fs, sources[project.second][source.config]),
|
||||
modifyAgainFs: fs => addTestPrologue(fs, "src/first/first_PART1.ts", `"myPrologue"`),
|
||||
ignoreDtsChanged: true,
|
||||
@@ -533,7 +351,7 @@ namespace ts {
|
||||
|
||||
// Verify initial + incremental edits - sourcemap verification
|
||||
verifyOutFileScenario({
|
||||
scenario: "multiple prologues in all projects",
|
||||
subScenario: "multiple prologues in all projects",
|
||||
modifyFs: fs => {
|
||||
enableStrict(fs, sources[project.first][source.config]);
|
||||
addTestPrologue(fs, sources[project.first][source.ts][part.one], `"myPrologue"`);
|
||||
@@ -549,7 +367,7 @@ namespace ts {
|
||||
|
||||
// Verify ignore dtsChanged
|
||||
verifyOutFileScenario({
|
||||
scenario: "multiple prologues in different projects",
|
||||
subScenario: "multiple prologues in different projects",
|
||||
modifyFs: fs => {
|
||||
enableStrict(fs, sources[project.first][source.config]);
|
||||
addTestPrologue(fs, sources[project.second][source.ts][part.one], `"myPrologue"`);
|
||||
@@ -567,7 +385,7 @@ namespace ts {
|
||||
// changes declaration because its emitted in .d.ts file
|
||||
// Verify initial + incremental edits
|
||||
verifyOutFileScenario({
|
||||
scenario: "shebang in all projects",
|
||||
subScenario: "shebang in all projects",
|
||||
modifyFs: fs => {
|
||||
addShebang(fs, "first", "first_PART1");
|
||||
addShebang(fs, "first", "first_part2");
|
||||
@@ -578,7 +396,7 @@ namespace ts {
|
||||
|
||||
// Verify ignore dtsChanged
|
||||
verifyOutFileScenario({
|
||||
scenario: "shebang in only one dependency project",
|
||||
subScenario: "shebang in only one dependency project",
|
||||
modifyFs: fs => addShebang(fs, "second", "second_part1"),
|
||||
ignoreDtsChanged: true,
|
||||
baselineOnly: true
|
||||
@@ -589,7 +407,7 @@ namespace ts {
|
||||
describe("emitHelpers", () => {
|
||||
// Verify initial + incremental edits
|
||||
verifyOutFileScenario({
|
||||
scenario: "emitHelpers in all projects",
|
||||
subScenario: "emitHelpers in all projects",
|
||||
modifyFs: fs => {
|
||||
addRest(fs, "first", "first_PART1");
|
||||
addRest(fs, "second", "second_part1");
|
||||
@@ -600,7 +418,7 @@ namespace ts {
|
||||
|
||||
// Verify ignore dtsChanged
|
||||
verifyOutFileScenario({
|
||||
scenario: "emitHelpers in only one dependency project",
|
||||
subScenario: "emitHelpers in only one dependency project",
|
||||
modifyFs: fs => {
|
||||
addStubFoo(fs, "first", "first_PART1");
|
||||
addRest(fs, "second", "second_part1");
|
||||
@@ -612,7 +430,7 @@ namespace ts {
|
||||
|
||||
// Verify ignore dtsChanged
|
||||
verifyOutFileScenario({
|
||||
scenario: "multiple emitHelpers in all projects",
|
||||
subScenario: "multiple emitHelpers in all projects",
|
||||
modifyFs: fs => {
|
||||
addRest(fs, "first", "first_PART1");
|
||||
addSpread(fs, "first", "first_part3");
|
||||
@@ -628,7 +446,7 @@ namespace ts {
|
||||
|
||||
// Verify ignore dtsChanged
|
||||
verifyOutFileScenario({
|
||||
scenario: "multiple emitHelpers in different projects",
|
||||
subScenario: "multiple emitHelpers in different projects",
|
||||
modifyFs: fs => {
|
||||
addRest(fs, "first", "first_PART1");
|
||||
addSpread(fs, "second", "second_part1");
|
||||
@@ -645,24 +463,18 @@ namespace ts {
|
||||
// changes declaration because its emitted in .d.ts file
|
||||
// Verify initial + incremental edits
|
||||
verifyOutFileScenario({
|
||||
scenario: "triple slash refs in all projects",
|
||||
subScenario: "triple slash refs in all projects",
|
||||
modifyFs: fs => {
|
||||
addTripleSlashRef(fs, "first", "first_part2");
|
||||
addTripleSlashRef(fs, "second", "second_part1");
|
||||
addTripleSlashRef(fs, "third", "third_part1");
|
||||
},
|
||||
additionalSourceFiles: [
|
||||
getTripleSlashRef("first"), getTripleSlashRef("second"), getTripleSlashRef("third")
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
// Verify ignore dtsChanged
|
||||
verifyOutFileScenario({
|
||||
scenario: "triple slash refs in one project",
|
||||
subScenario: "triple slash refs in one project",
|
||||
modifyFs: fs => addTripleSlashRef(fs, "second", "second_part1"),
|
||||
additionalSourceFiles: [
|
||||
getTripleSlashRef("second")
|
||||
],
|
||||
ignoreDtsChanged: true,
|
||||
baselineOnly: true
|
||||
});
|
||||
@@ -721,14 +533,14 @@ ${internal} enum internalEnum { a, b, c }`);
|
||||
|
||||
// Verify initial + incremental edits
|
||||
verifyOutFileScenario({
|
||||
scenario: "stripInternal",
|
||||
subScenario: "stripInternal",
|
||||
modifyFs: stripInternalScenario,
|
||||
modifyAgainFs: fs => replaceText(fs, sources[project.first][source.ts][part.one], `/*@internal*/ interface`, "interface"),
|
||||
});
|
||||
|
||||
// Verify ignore dtsChanged
|
||||
verifyOutFileScenario({
|
||||
scenario: "stripInternal with comments emit enabled",
|
||||
subScenario: "stripInternal with comments emit enabled",
|
||||
modifyFs: fs => stripInternalScenario(fs, /*removeCommentsDisabled*/ true),
|
||||
modifyAgainFs: fs => replaceText(fs, sources[project.first][source.ts][part.one], `/*@internal*/ interface`, "interface"),
|
||||
ignoreDtsChanged: true,
|
||||
@@ -737,7 +549,7 @@ ${internal} enum internalEnum { a, b, c }`);
|
||||
|
||||
// Verify ignore dtsChanged
|
||||
verifyOutFileScenario({
|
||||
scenario: "stripInternal jsdoc style comment",
|
||||
subScenario: "stripInternal jsdoc style comment",
|
||||
modifyFs: fs => stripInternalScenario(fs, /*removeCommentsDisabled*/ false, /*jsDocStyle*/ true),
|
||||
modifyAgainFs: fs => replaceText(fs, sources[project.first][source.ts][part.one], `/**@internal*/ interface`, "interface"),
|
||||
ignoreDtsChanged: true,
|
||||
@@ -746,7 +558,7 @@ ${internal} enum internalEnum { a, b, c }`);
|
||||
|
||||
// Verify ignore dtsChanged
|
||||
verifyOutFileScenario({
|
||||
scenario: "stripInternal jsdoc style with comments emit enabled",
|
||||
subScenario: "stripInternal jsdoc style with comments emit enabled",
|
||||
modifyFs: fs => stripInternalScenario(fs, /*removeCommentsDisabled*/ true, /*jsDocStyle*/ true),
|
||||
ignoreDtsChanged: true,
|
||||
baselineOnly: true
|
||||
@@ -766,37 +578,33 @@ ${internal} enum internalEnum { a, b, c }`);
|
||||
|
||||
// Verify initial + incremental edits
|
||||
verifyOutFileScenario({
|
||||
scenario: "stripInternal when one-two-three are prepended in order",
|
||||
subScenario: "stripInternal when one-two-three are prepended in order",
|
||||
modifyFs: stripInternalWithDependentOrder,
|
||||
modifyAgainFs: fs => replaceText(fs, sources[project.first][source.ts][part.one], `/*@internal*/ interface`, "interface"),
|
||||
dependOrdered: true,
|
||||
});
|
||||
|
||||
// Verify ignore dtsChanged
|
||||
verifyOutFileScenario({
|
||||
scenario: "stripInternal with comments emit enabled when one-two-three are prepended in order",
|
||||
subScenario: "stripInternal with comments emit enabled when one-two-three are prepended in order",
|
||||
modifyFs: fs => stripInternalWithDependentOrder(fs, /*removeCommentsDisabled*/ true),
|
||||
modifyAgainFs: fs => replaceText(fs, sources[project.first][source.ts][part.one], `/*@internal*/ interface`, "interface"),
|
||||
dependOrdered: true,
|
||||
ignoreDtsChanged: true,
|
||||
baselineOnly: true
|
||||
});
|
||||
|
||||
// Verify ignore dtsChanged
|
||||
verifyOutFileScenario({
|
||||
scenario: "stripInternal jsdoc style comment when one-two-three are prepended in order",
|
||||
subScenario: "stripInternal jsdoc style comment when one-two-three are prepended in order",
|
||||
modifyFs: fs => stripInternalWithDependentOrder(fs, /*removeCommentsDisabled*/ false, /*jsDocStyle*/ true),
|
||||
modifyAgainFs: fs => replaceText(fs, sources[project.first][source.ts][part.one], `/**@internal*/ interface`, "interface"),
|
||||
dependOrdered: true,
|
||||
ignoreDtsChanged: true,
|
||||
baselineOnly: true
|
||||
});
|
||||
|
||||
// Verify ignore dtsChanged
|
||||
verifyOutFileScenario({
|
||||
scenario: "stripInternal jsdoc style with comments emit enabled when one-two-three are prepended in order",
|
||||
subScenario: "stripInternal jsdoc style with comments emit enabled when one-two-three are prepended in order",
|
||||
modifyFs: fs => stripInternalWithDependentOrder(fs, /*removeCommentsDisabled*/ true, /*jsDocStyle*/ true),
|
||||
dependOrdered: true,
|
||||
ignoreDtsChanged: true,
|
||||
baselineOnly: true
|
||||
});
|
||||
@@ -804,7 +612,7 @@ ${internal} enum internalEnum { a, b, c }`);
|
||||
|
||||
// only baseline
|
||||
verifyOutFileScenario({
|
||||
scenario: "stripInternal baseline when internal is inside another internal",
|
||||
subScenario: "stripInternal baseline when internal is inside another internal",
|
||||
modifyFs: fs => {
|
||||
stripInternalOfThird(fs);
|
||||
prependText(fs, sources[project.first][source.ts][part.one], `namespace ts {
|
||||
@@ -843,7 +651,7 @@ ${internal} enum internalEnum { a, b, c }`);
|
||||
|
||||
// only baseline
|
||||
verifyOutFileScenario({
|
||||
scenario: "stripInternal when few members of enum are internal",
|
||||
subScenario: "stripInternal when few members of enum are internal",
|
||||
modifyFs: fs => {
|
||||
stripInternalOfThird(fs);
|
||||
prependText(fs, sources[project.first][source.ts][part.one], `enum TokenFlags {
|
||||
@@ -883,7 +691,7 @@ ${internal} enum internalEnum { a, b, c }`);
|
||||
|
||||
// Verify ignore dtsChanged
|
||||
verifyOutFileScenario({
|
||||
scenario: "when source files are empty in the own file",
|
||||
subScenario: "when source files are empty in the own file",
|
||||
modifyFs: makeThirdEmptySourceFile,
|
||||
ignoreDtsChanged: true,
|
||||
baselineOnly: true
|
||||
@@ -891,7 +699,7 @@ ${internal} enum internalEnum { a, b, c }`);
|
||||
|
||||
// only baseline
|
||||
verifyOutFileScenario({
|
||||
scenario: "declarationMap and sourceMap disabled",
|
||||
subScenario: "declarationMap and sourceMap disabled",
|
||||
modifyFs: fs => {
|
||||
makeThirdEmptySourceFile(fs);
|
||||
replaceText(fs, sources[project.third][source.config], `"composite": true,`, "");
|
||||
@@ -921,7 +729,7 @@ ${internal} enum internalEnum { a, b, c }`);
|
||||
replaceText(fs, sources[project.second][source.config], `"outFile": "../2/second-output.js",`, "");
|
||||
replaceText(fs, sources[project.third][source.config], `"outFile": "./thirdjs/output/third-output.js",`, "");
|
||||
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host);
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace ts {
|
||||
"/src/dist/main/b.js", "/src/dist/main/b.d.ts"
|
||||
];
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/src/main", "/src/src/other"], {});
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(/*empty*/);
|
||||
@@ -34,7 +34,7 @@ namespace ts {
|
||||
];
|
||||
const fs = projFs.shadow();
|
||||
replaceText(fs, "/src/tsconfig.base.json", `"rootDir": "./src/",`, "");
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/src/main"], { verbose: true });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
@@ -69,7 +69,7 @@ namespace ts {
|
||||
fs.writeFileSync("/src/src/other/tsconfig.json", JSON.stringify({
|
||||
compilerOptions: { composite: true, outDir: "../../dist/" },
|
||||
}));
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/src/main"], { verbose: true });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
@@ -105,7 +105,7 @@ namespace ts {
|
||||
fs.writeFileSync("/src/src/other/tsconfig.other.json", JSON.stringify({
|
||||
compilerOptions: { composite: true, outDir: "../../dist/" },
|
||||
}));
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/src/main/tsconfig.main.json"], { verbose: true });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
namespace ts {
|
||||
describe("unittests:: tsbuild:: with resolveJsonModule option on project resolveJsonModuleAndComposite", () => {
|
||||
let projFs: vfs.FileSystem;
|
||||
const { time, tick } = getTime();
|
||||
const allExpectedOutputs = ["/src/dist/src/index.js", "/src/dist/src/index.d.ts", "/src/dist/src/hello.json"];
|
||||
before(() => {
|
||||
projFs = loadProjectFromDisk("tests/projects/resolveJsonModuleAndComposite", time);
|
||||
projFs = loadProjectFromDisk("tests/projects/resolveJsonModuleAndComposite");
|
||||
});
|
||||
|
||||
after(() => {
|
||||
@@ -17,7 +16,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function verifyProjectWithResolveJsonModuleWithFs(fs: vfs.FileSystem, configFile: string, allExpectedOutputs: readonly string[], ...expectedDiagnosticMessages: fakes.ExpectedDiagnostic[]) {
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, [configFile], { dry: false, force: false, verbose: false });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(...expectedDiagnosticMessages);
|
||||
@@ -65,10 +64,10 @@ export default hello.hello`);
|
||||
});
|
||||
|
||||
it("with resolveJsonModule and sourceMap", () => {
|
||||
const fs = projFs.shadow();
|
||||
const { fs, tick } = getFsWithTime(projFs);
|
||||
const configFile = "src/tsconfig_withFiles.json";
|
||||
replaceText(fs, configFile, `"composite": true,`, `"composite": true, "sourceMap": true,`);
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
let builder = createSolutionBuilder(host, [configFile], { verbose: true });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
@@ -88,10 +87,10 @@ export default hello.hello`);
|
||||
});
|
||||
|
||||
it("with resolveJsonModule and without outDir", () => {
|
||||
const fs = projFs.shadow();
|
||||
const { fs, tick } = getFsWithTime(projFs);
|
||||
const configFile = "src/tsconfig_withFiles.json";
|
||||
replaceText(fs, configFile, `"outDir": "dist",`, "");
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
let builder = createSolutionBuilder(host, [configFile], { verbose: true });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
@@ -112,10 +111,9 @@ export default hello.hello`);
|
||||
});
|
||||
|
||||
describe("unittests:: tsbuild:: with resolveJsonModule option on project importJsonFromProjectReference", () => {
|
||||
const { time, tick } = getTime();
|
||||
let projFs: vfs.FileSystem;
|
||||
before(() => {
|
||||
projFs = loadProjectFromDisk("tests/projects/importJsonFromProjectReference", time);
|
||||
projFs = loadProjectFromDisk("tests/projects/importJsonFromProjectReference");
|
||||
});
|
||||
|
||||
after(() => {
|
||||
@@ -124,11 +122,11 @@ export default hello.hello`);
|
||||
|
||||
it("when importing json module from project reference", () => {
|
||||
const expectedOutput = "/src/main/index.js";
|
||||
const fs = projFs.shadow();
|
||||
const { fs, tick } = getFsWithTime(projFs);
|
||||
const configFile = "src/tsconfig.json";
|
||||
const stringsConfigFile = "src/strings/tsconfig.json";
|
||||
const mainConfigFile = "src/main/tsconfig.json";
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
let builder = createSolutionBuilder(host, [configFile], { verbose: true });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
namespace ts {
|
||||
describe("unittests:: tsbuild:: on 'sample1' project", () => {
|
||||
let projFs: vfs.FileSystem;
|
||||
const { time, tick } = getTime();
|
||||
const testsOutputs = ["/src/tests/index.js", "/src/tests/index.d.ts", "/src/tests/tsconfig.tsbuildinfo"];
|
||||
const logicOutputs = ["/src/logic/index.js", "/src/logic/index.js.map", "/src/logic/index.d.ts", "/src/logic/tsconfig.tsbuildinfo"];
|
||||
const coreOutputs = ["/src/core/index.js", "/src/core/index.d.ts", "/src/core/index.d.ts.map", "/src/core/tsconfig.tsbuildinfo"];
|
||||
const allExpectedOutputs = [...testsOutputs, ...logicOutputs, ...coreOutputs];
|
||||
|
||||
before(() => {
|
||||
projFs = loadProjectFromDisk("tests/projects/sample1", time);
|
||||
projFs = loadProjectFromDisk("tests/projects/sample1");
|
||||
});
|
||||
|
||||
after(() => {
|
||||
@@ -18,7 +17,7 @@ namespace ts {
|
||||
describe("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.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
|
||||
|
||||
host.clearDiagnostics();
|
||||
@@ -36,7 +35,7 @@ namespace ts {
|
||||
references: [{ path: "../core" }]
|
||||
}));
|
||||
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], {});
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(/*empty*/);
|
||||
@@ -52,7 +51,7 @@ namespace ts {
|
||||
references: [{ path: "../core" }]
|
||||
}));
|
||||
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], {});
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(/*empty*/);
|
||||
@@ -64,7 +63,7 @@ namespace ts {
|
||||
it("builds correctly when project is not composite or doesnt have any references", () => {
|
||||
const fs = projFs.shadow();
|
||||
replaceText(fs, "/src/core/tsconfig.json", `"composite": true,`, "");
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/core"], { verbose: true });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
@@ -79,7 +78,7 @@ namespace ts {
|
||||
describe("dry builds", () => {
|
||||
it("doesn't write any files in a dry build", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: true, force: false, verbose: false });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
@@ -93,8 +92,8 @@ namespace ts {
|
||||
});
|
||||
|
||||
it("indicates that it would skip builds during a dry build", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const { fs, tick } = getFsWithTime(projFs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
|
||||
let builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
|
||||
builder.build();
|
||||
@@ -114,7 +113,7 @@ namespace ts {
|
||||
describe("clean builds", () => {
|
||||
it("removes all files it built", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
|
||||
builder.build();
|
||||
@@ -136,7 +135,7 @@ namespace ts {
|
||||
|
||||
it("cleans till project specified", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], {});
|
||||
builder.build();
|
||||
const result = builder.clean("/src/logic");
|
||||
@@ -148,7 +147,7 @@ namespace ts {
|
||||
|
||||
it("cleaning project in not build order doesnt throw error", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], {});
|
||||
builder.build();
|
||||
const result = builder.clean("/src/logic2");
|
||||
@@ -160,8 +159,8 @@ namespace ts {
|
||||
|
||||
describe("force builds", () => {
|
||||
it("always builds under --force", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const { fs, time, tick } = getFsWithTime(projFs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
|
||||
let builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: true, verbose: false });
|
||||
builder.build();
|
||||
@@ -187,8 +186,8 @@ namespace ts {
|
||||
|
||||
describe("can detect when and what to rebuild", () => {
|
||||
function initializeWithBuild(opts?: BuildOptions) {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const { fs, tick } = getFsWithTime(projFs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
let builder = createSolutionBuilder(host, ["/src/tests"], { verbose: true });
|
||||
builder.build();
|
||||
host.clearDiagnostics();
|
||||
@@ -199,7 +198,7 @@ namespace ts {
|
||||
|
||||
it("Builds the project", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], { verbose: true });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
@@ -273,8 +272,8 @@ namespace ts {
|
||||
});
|
||||
|
||||
it("does not rebuild if there is no program and bundle in the ts build info event if version doesnt match ts version", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs, /*options*/ undefined, /*setParentNodes*/ undefined, createAbstractBuilder);
|
||||
const { fs, tick } = getFsWithTime(projFs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs, /*options*/ undefined, /*setParentNodes*/ undefined, createAbstractBuilder);
|
||||
let builder = createSolutionBuilder(host, ["/src/tests"], { verbose: true });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
@@ -329,10 +328,10 @@ namespace ts {
|
||||
});
|
||||
|
||||
it("rebuilds when extended config file changes", () => {
|
||||
const fs = projFs.shadow();
|
||||
const { fs, tick } = getFsWithTime(projFs);
|
||||
fs.writeFileSync("/src/tests/tsconfig.base.json", JSON.stringify({ compilerOptions: { target: "es3" } }));
|
||||
replaceText(fs, "/src/tests/tsconfig.json", `"references": [`, `"extends": "./tsconfig.base.json", "references": [`);
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
let builder = createSolutionBuilder(host, ["/src/tests"], { verbose: true });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
@@ -360,7 +359,7 @@ namespace ts {
|
||||
|
||||
it("builds till project specified", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], {});
|
||||
const result = builder.build("/src/logic");
|
||||
host.assertDiagnosticMessages(/*empty*/);
|
||||
@@ -371,7 +370,7 @@ namespace ts {
|
||||
|
||||
it("building project in not build order doesnt throw error", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], {});
|
||||
const result = builder.build("/src/logic2");
|
||||
host.assertDiagnosticMessages(/*empty*/);
|
||||
@@ -386,7 +385,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], {});
|
||||
verifyBuildNextResult({
|
||||
project: "/src/core/tsconfig.json" as ResolvedConfigFileName,
|
||||
@@ -420,7 +419,7 @@ namespace ts {
|
||||
|
||||
it("building using buildReferencedProject", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], { verbose: true });
|
||||
builder.buildReferences("/src/tests");
|
||||
host.assertDiagnosticMessages(
|
||||
@@ -438,7 +437,7 @@ namespace ts {
|
||||
describe("downstream-blocked compilations", () => {
|
||||
it("won't build downstream projects if upstream projects have errors", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: true });
|
||||
|
||||
// Induce an error in the middle project
|
||||
@@ -462,8 +461,8 @@ namespace ts {
|
||||
|
||||
describe("project invalidation", () => {
|
||||
it("invalidates projects correctly", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const { fs, time, tick } = getFsWithTime(projFs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
|
||||
|
||||
builder.build();
|
||||
@@ -518,7 +517,7 @@ export class cNew {}`);
|
||||
describe("lists files", () => {
|
||||
it("listFiles", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], { listFiles: true });
|
||||
builder.build();
|
||||
assert.deepEqual(host.traces, [
|
||||
@@ -545,7 +544,7 @@ export class cNew {}`);
|
||||
|
||||
it("listEmittedFiles", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], { listEmittedFiles: true });
|
||||
builder.build();
|
||||
assert.deepEqual(host.traces, [
|
||||
@@ -568,338 +567,73 @@ export class cNew {}`);
|
||||
});
|
||||
|
||||
describe("emit output", () => {
|
||||
const initialBuild: BuildState = {
|
||||
modifyFs: noop,
|
||||
expectedDiagnostics: [
|
||||
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/core/tsconfig.json", "src/core/anotherModule.js"],
|
||||
[Diagnostics.Building_project_0, "/src/core/tsconfig.json"],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/logic/tsconfig.json", "src/logic/index.js"],
|
||||
[Diagnostics.Building_project_0, "/src/logic/tsconfig.json"],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/tests/tsconfig.json", "src/tests/index.js"],
|
||||
[Diagnostics.Building_project_0, "/src/tests/tsconfig.json"]
|
||||
],
|
||||
expectedReadFiles: getReadFilesMap(
|
||||
[
|
||||
// Configs
|
||||
"/src/core/tsconfig.json",
|
||||
"/src/logic/tsconfig.json",
|
||||
"/src/tests/tsconfig.json",
|
||||
|
||||
// Source files
|
||||
"/src/core/anotherModule.ts",
|
||||
"/src/core/index.ts",
|
||||
"/src/core/some_decl.d.ts",
|
||||
"/src/logic/index.ts",
|
||||
"/src/tests/index.ts",
|
||||
|
||||
// Modules of generated files
|
||||
"/src/core/anotherModule.d.ts",
|
||||
"/src/core/index.d.ts",
|
||||
"/src/logic/index.d.ts",
|
||||
|
||||
// build info
|
||||
"/src/core/tsconfig.tsbuildinfo",
|
||||
"/src/logic/tsconfig.tsbuildinfo",
|
||||
"/src/tests/tsconfig.tsbuildinfo"
|
||||
]
|
||||
)
|
||||
};
|
||||
verifyTsbuildOutput({
|
||||
scenario: "sample",
|
||||
projFs: () => projFs,
|
||||
time,
|
||||
tick,
|
||||
proj: "sample1",
|
||||
rootNames: ["/src/tests"],
|
||||
expectedMapFileNames: [
|
||||
"/src/core/anotherModule.d.ts.map",
|
||||
"/src/core/index.d.ts.map",
|
||||
"/src/logic/index.js.map"
|
||||
],
|
||||
lastProjectOutput: "/src/tests/index.js",
|
||||
initialBuild,
|
||||
incrementalDtsChangedBuild: {
|
||||
modifyFs: fs => appendText(fs, "/src/core/index.ts", `
|
||||
verifyTscIncrementalEdits({
|
||||
subScenario: "sample",
|
||||
fs: () => projFs,
|
||||
scenario: "sample1",
|
||||
commandLineArgs: ["--b", "/src/tests", "--verbose"],
|
||||
baselineSourceMap: true,
|
||||
baselineReadFileCalls: true,
|
||||
incrementalScenarios: [
|
||||
{
|
||||
buildKind: BuildKind.IncrementalDtsChange,
|
||||
modifyFs: fs => appendText(fs, "/src/core/index.ts", `
|
||||
export class someClass { }`),
|
||||
expectedDiagnostics: [
|
||||
// Emits only partial core instead of all outputs
|
||||
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/core/tsconfig.json", "src/core/anotherModule.js", "src/core/index.ts"],
|
||||
[Diagnostics.Building_project_0, "/src/core/tsconfig.json"],
|
||||
[Diagnostics.Updating_unchanged_output_timestamps_of_project_0, "/src/core/tsconfig.json"],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/logic/tsconfig.json", "src/logic/index.js", "src/core"],
|
||||
[Diagnostics.Building_project_0, "/src/logic/tsconfig.json"],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/tests/tsconfig.json", "src/tests/index.js", "src/core"],
|
||||
[Diagnostics.Building_project_0, "/src/tests/tsconfig.json"],
|
||||
],
|
||||
expectedReadFiles: getReadFilesMap(
|
||||
[
|
||||
// Configs
|
||||
"/src/core/tsconfig.json",
|
||||
"/src/logic/tsconfig.json",
|
||||
"/src/tests/tsconfig.json",
|
||||
|
||||
// Source files
|
||||
"/src/core/anotherModule.ts",
|
||||
"/src/core/index.ts",
|
||||
"/src/core/some_decl.d.ts",
|
||||
"/src/logic/index.ts",
|
||||
"/src/tests/index.ts",
|
||||
|
||||
// Modules of generated files
|
||||
"/src/core/anotherModule.d.ts",
|
||||
"/src/core/index.d.ts",
|
||||
"/src/logic/index.d.ts",
|
||||
|
||||
// build info
|
||||
"/src/core/tsconfig.tsbuildinfo",
|
||||
"/src/logic/tsconfig.tsbuildinfo",
|
||||
"/src/tests/tsconfig.tsbuildinfo",
|
||||
|
||||
"/src/tests/index.d.ts", // to check if d.ts has changed
|
||||
],
|
||||
"/src/core/index.d.ts", // to check if changed, and to build other projects after change
|
||||
),
|
||||
},
|
||||
incrementalDtsUnchangedBuild: {
|
||||
modifyFs: fs => appendText(fs, "/src/core/index.ts", `
|
||||
},
|
||||
{
|
||||
buildKind: BuildKind.IncrementalDtsUnchanged,
|
||||
modifyFs: fs => appendText(fs, "/src/core/index.ts", `
|
||||
class someClass { }`),
|
||||
expectedDiagnostics: [
|
||||
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/core/tsconfig.json", "src/core/anotherModule.js", "src/core/index.ts"],
|
||||
[Diagnostics.Building_project_0, "/src/core/tsconfig.json"],
|
||||
[Diagnostics.Updating_unchanged_output_timestamps_of_project_0, "/src/core/tsconfig.json"],
|
||||
[Diagnostics.Project_0_is_up_to_date_with_d_ts_files_from_its_dependencies, "src/logic/tsconfig.json"],
|
||||
[Diagnostics.Updating_output_timestamps_of_project_0, "/src/logic/tsconfig.json"],
|
||||
[Diagnostics.Project_0_is_up_to_date_with_d_ts_files_from_its_dependencies, "src/tests/tsconfig.json"],
|
||||
[Diagnostics.Updating_output_timestamps_of_project_0, "/src/tests/tsconfig.json"]
|
||||
],
|
||||
expectedReadFiles: getReadFilesMap(
|
||||
[
|
||||
// Configs
|
||||
"/src/core/tsconfig.json",
|
||||
"/src/logic/tsconfig.json",
|
||||
"/src/tests/tsconfig.json",
|
||||
|
||||
// Source files
|
||||
"/src/core/anotherModule.ts",
|
||||
"/src/core/index.ts",
|
||||
"/src/core/some_decl.d.ts",
|
||||
|
||||
// to check if changed
|
||||
"/src/core/index.d.ts",
|
||||
|
||||
// build info
|
||||
"/src/core/tsconfig.tsbuildinfo",
|
||||
"/src/logic/tsconfig.tsbuildinfo",
|
||||
"/src/tests/tsconfig.tsbuildinfo",
|
||||
],
|
||||
)
|
||||
},
|
||||
outputFiles: [
|
||||
"/src/core/anotherModule.js",
|
||||
"/src/core/anotherModule.d.ts",
|
||||
"/src/core/anotherModule.d.ts.map",
|
||||
"/src/core/index.js",
|
||||
"/src/core/index.d.ts",
|
||||
"/src/core/index.d.ts.map",
|
||||
"/src/core/tsconfig.tsbuildinfo",
|
||||
"/src/logic/index.js",
|
||||
"/src/logic/index.js.map",
|
||||
"/src/logic/index.d.ts",
|
||||
"/src/logic/tsconfig.tsbuildinfo",
|
||||
"/src/tests/index.js",
|
||||
"/src/tests/index.d.ts",
|
||||
"/src/tests/tsconfig.tsbuildinfo",
|
||||
]
|
||||
});
|
||||
|
||||
verifyTsbuildOutput({
|
||||
scenario: "when logic config changes declaration dir",
|
||||
projFs: () => projFs,
|
||||
time,
|
||||
tick,
|
||||
proj: "sample1",
|
||||
rootNames: ["/src/tests"],
|
||||
expectedMapFileNames: [
|
||||
"/src/core/anotherModule.d.ts.map",
|
||||
"/src/core/index.d.ts.map",
|
||||
"/src/logic/index.js.map"
|
||||
],
|
||||
lastProjectOutput: "/src/tests/index.js",
|
||||
initialBuild,
|
||||
incrementalDtsChangedBuild: {
|
||||
modifyFs: fs => replaceText(fs, "/src/logic/tsconfig.json", `"declaration": true,`, `"declaration": true,
|
||||
},
|
||||
{
|
||||
subScenario: "when logic config changes declaration dir",
|
||||
buildKind: BuildKind.IncrementalDtsChange,
|
||||
modifyFs: fs => replaceText(fs, "/src/logic/tsconfig.json", `"declaration": true,`, `"declaration": true,
|
||||
"declarationDir": "decls",`),
|
||||
expectedDiagnostics: [
|
||||
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/core/tsconfig.json", "src/core/anotherModule.ts", "src/core/anotherModule.js"],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/logic/tsconfig.json", "src/logic/decls/index.d.ts"],
|
||||
[Diagnostics.Building_project_0, "/src/logic/tsconfig.json"],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/tests/tsconfig.json", "src/tests/index.js", "src/logic"],
|
||||
[Diagnostics.Building_project_0, "/src/tests/tsconfig.json"],
|
||||
],
|
||||
expectedReadFiles: getReadFilesMap(
|
||||
[
|
||||
// Configs
|
||||
"/src/core/tsconfig.json",
|
||||
"/src/logic/tsconfig.json",
|
||||
"/src/tests/tsconfig.json",
|
||||
|
||||
// Source files
|
||||
"/src/logic/index.ts",
|
||||
"/src/tests/index.ts",
|
||||
|
||||
// Modules of generated files
|
||||
"/src/core/anotherModule.d.ts",
|
||||
"/src/core/index.d.ts",
|
||||
"/src/logic/decls/index.d.ts",
|
||||
|
||||
// build info
|
||||
"/src/core/tsconfig.tsbuildinfo",
|
||||
"/src/logic/tsconfig.tsbuildinfo",
|
||||
"/src/tests/tsconfig.tsbuildinfo",
|
||||
|
||||
"/src/tests/index.d.ts", // to check if d.ts has changed
|
||||
]
|
||||
)
|
||||
},
|
||||
outputFiles: [
|
||||
"/src/core/anotherModule.js",
|
||||
"/src/core/anotherModule.d.ts",
|
||||
"/src/core/anotherModule.d.ts.map",
|
||||
"/src/core/index.js",
|
||||
"/src/core/index.d.ts",
|
||||
"/src/core/index.d.ts.map",
|
||||
"/src/core/tsconfig.tsbuildinfo",
|
||||
"/src/logic/index.js",
|
||||
"/src/logic/index.js.map",
|
||||
"/src/logic/decls/index.d.ts",
|
||||
"/src/logic/tsconfig.tsbuildinfo",
|
||||
"/src/tests/index.js",
|
||||
"/src/tests/index.d.ts",
|
||||
"/src/tests/tsconfig.tsbuildinfo",
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
verifyTsbuildOutput({
|
||||
scenario: "when logic specifies tsBuildInfoFile",
|
||||
projFs: () => projFs,
|
||||
time,
|
||||
tick,
|
||||
proj: "sample1",
|
||||
rootNames: ["/src/tests"],
|
||||
expectedMapFileNames: [
|
||||
"/src/core/anotherModule.d.ts.map",
|
||||
"/src/core/index.d.ts.map",
|
||||
"/src/logic/index.js.map"
|
||||
],
|
||||
lastProjectOutput: "/src/tests/index.js",
|
||||
initialBuild: {
|
||||
modifyFs: fs => replaceText(fs, "/src/logic/tsconfig.json", `"composite": true,`, `"composite": true,
|
||||
verifyTsc({
|
||||
scenario: "sample1",
|
||||
subScenario: "when logic specifies tsBuildInfoFile",
|
||||
fs: () => projFs,
|
||||
modifyFs: fs => replaceText(fs, "/src/logic/tsconfig.json", `"composite": true,`, `"composite": true,
|
||||
"tsBuildInfoFile": "ownFile.tsbuildinfo",`),
|
||||
expectedDiagnostics: initialBuild.expectedDiagnostics,
|
||||
expectedReadFiles: getReadFilesMap(
|
||||
[
|
||||
// Configs
|
||||
"/src/core/tsconfig.json",
|
||||
"/src/logic/tsconfig.json",
|
||||
"/src/tests/tsconfig.json",
|
||||
|
||||
// Source files
|
||||
"/src/core/anotherModule.ts",
|
||||
"/src/core/index.ts",
|
||||
"/src/core/some_decl.d.ts",
|
||||
"/src/logic/index.ts",
|
||||
"/src/tests/index.ts",
|
||||
|
||||
// Modules of generated files
|
||||
"/src/core/anotherModule.d.ts",
|
||||
"/src/core/index.d.ts",
|
||||
"/src/logic/index.d.ts",
|
||||
|
||||
// build info
|
||||
"/src/core/tsconfig.tsbuildinfo",
|
||||
"/src/logic/ownFile.tsbuildinfo",
|
||||
"/src/tests/tsconfig.tsbuildinfo"
|
||||
]
|
||||
)
|
||||
},
|
||||
outputFiles: [
|
||||
"/src/core/anotherModule.js",
|
||||
"/src/core/anotherModule.d.ts",
|
||||
"/src/core/anotherModule.d.ts.map",
|
||||
"/src/core/index.js",
|
||||
"/src/core/index.d.ts",
|
||||
"/src/core/index.d.ts.map",
|
||||
"/src/core/tsconfig.tsbuildinfo",
|
||||
"/src/logic/index.js",
|
||||
"/src/logic/index.js.map",
|
||||
"/src/logic/index.d.ts",
|
||||
"/src/logic/ownFile.tsbuildinfo",
|
||||
"/src/tests/index.js",
|
||||
"/src/tests/index.d.ts",
|
||||
"/src/tests/tsconfig.tsbuildinfo",
|
||||
]
|
||||
commandLineArgs: ["--b", "/src/tests", "--verbose"],
|
||||
baselineSourceMap: true,
|
||||
baselineReadFileCalls: true
|
||||
});
|
||||
|
||||
verifyTsbuildOutput({
|
||||
scenario: "when declaration option changes",
|
||||
projFs: () => projFs,
|
||||
time,
|
||||
tick,
|
||||
proj: "sample1",
|
||||
rootNames: ["/src/core"],
|
||||
lastProjectOutput: "/src/core/index.js",
|
||||
initialBuild: {
|
||||
modifyFs: fs => fs.writeFileSync("/src/core/tsconfig.json", `{
|
||||
verifyTscIncrementalEdits({
|
||||
subScenario: "when declaration option changes",
|
||||
fs: () => projFs,
|
||||
scenario: "sample1",
|
||||
commandLineArgs: ["--b", "/src/core", "--verbose"],
|
||||
modifyFs: fs => fs.writeFileSync("/src/core/tsconfig.json", `{
|
||||
"compilerOptions": {
|
||||
"incremental": true,
|
||||
"skipDefaultLibCheck": true
|
||||
}
|
||||
}`),
|
||||
expectedDiagnostics: [
|
||||
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/core/tsconfig.json", "src/core/anotherModule.js"],
|
||||
[Diagnostics.Building_project_0, "/src/core/tsconfig.json"],
|
||||
]
|
||||
},
|
||||
incrementalDtsChangedBuild: {
|
||||
incrementalScenarios: [{
|
||||
buildKind: BuildKind.IncrementalDtsChange,
|
||||
modifyFs: fs => replaceText(fs, "/src/core/tsconfig.json", `"incremental": true,`, `"incremental": true, "declaration": true,`),
|
||||
expectedDiagnostics: [
|
||||
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/core/tsconfig.json", "src/core/anotherModule.d.ts"],
|
||||
[Diagnostics.Building_project_0, "/src/core/tsconfig.json"]
|
||||
]
|
||||
},
|
||||
outputFiles: [
|
||||
"/src/core/anotherModule.js",
|
||||
"/src/core/anotherModule.d.ts",
|
||||
"/src/core/index.js",
|
||||
"/src/core/index.d.ts",
|
||||
"/src/core/tsconfig.tsbuildinfo",
|
||||
],
|
||||
baselineOnly: true,
|
||||
verifyDiagnostics: true
|
||||
}],
|
||||
});
|
||||
|
||||
verifyTsbuildOutput({
|
||||
scenario: "when target option changes",
|
||||
projFs: () => projFs,
|
||||
time,
|
||||
tick,
|
||||
proj: "sample1",
|
||||
rootNames: ["/src/core"],
|
||||
lastProjectOutput: "/src/core/index.js",
|
||||
initialBuild: {
|
||||
modifyFs: fs => {
|
||||
fs.writeFileSync("/lib/lib.esnext.full.d.ts", `/// <reference no-default-lib="true"/>
|
||||
verifyTscIncrementalEdits({
|
||||
subScenario: "when target option changes",
|
||||
fs: () => projFs,
|
||||
scenario: "sample1",
|
||||
commandLineArgs: ["--b", "/src/core", "--verbose"],
|
||||
modifyFs: fs => {
|
||||
fs.writeFileSync("/lib/lib.esnext.full.d.ts", `/// <reference no-default-lib="true"/>
|
||||
/// <reference lib="esnext" />`);
|
||||
fs.writeFileSync("/lib/lib.esnext.d.ts", libContent);
|
||||
fs.writeFileSync("/lib/lib.d.ts", `/// <reference no-default-lib="true"/>
|
||||
fs.writeFileSync("/lib/lib.esnext.d.ts", libContent);
|
||||
fs.writeFileSync("/lib/lib.d.ts", `/// <reference no-default-lib="true"/>
|
||||
/// <reference lib="esnext" />`);
|
||||
fs.writeFileSync("/src/core/tsconfig.json", `{
|
||||
fs.writeFileSync("/src/core/tsconfig.json", `{
|
||||
"compilerOptions": {
|
||||
"incremental": true,
|
||||
"listFiles": true,
|
||||
@@ -907,80 +641,36 @@ class someClass { }`),
|
||||
"target": "esnext",
|
||||
}
|
||||
}`);
|
||||
},
|
||||
expectedDiagnostics: [
|
||||
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/core/tsconfig.json", "src/core/anotherModule.js"],
|
||||
[Diagnostics.Building_project_0, "/src/core/tsconfig.json"],
|
||||
]
|
||||
},
|
||||
incrementalDtsChangedBuild: {
|
||||
incrementalScenarios: [{
|
||||
buildKind: BuildKind.IncrementalDtsChange,
|
||||
modifyFs: fs => replaceText(fs, "/src/core/tsconfig.json", "esnext", "es5"),
|
||||
expectedDiagnostics: [
|
||||
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/core/tsconfig.json", "src/core/anotherModule.js", "src/core/tsconfig.json"],
|
||||
[Diagnostics.Building_project_0, "/src/core/tsconfig.json"]
|
||||
]
|
||||
},
|
||||
outputFiles: [
|
||||
"/src/core/anotherModule.js",
|
||||
"/src/core/anotherModule.d.ts",
|
||||
"/src/core/index.js",
|
||||
"/src/core/index.d.ts",
|
||||
"/src/core/tsconfig.tsbuildinfo",
|
||||
],
|
||||
baselineOnly: true,
|
||||
verifyDiagnostics: true
|
||||
}],
|
||||
});
|
||||
|
||||
verifyTsbuildOutput({
|
||||
scenario: "when module option changes",
|
||||
projFs: () => projFs,
|
||||
time,
|
||||
tick,
|
||||
proj: "sample1",
|
||||
rootNames: ["/src/core"],
|
||||
lastProjectOutput: "/src/core/index.js",
|
||||
initialBuild: {
|
||||
modifyFs: fs => fs.writeFileSync("/src/core/tsconfig.json", `{
|
||||
verifyTscIncrementalEdits({
|
||||
subScenario: "when module option changes",
|
||||
fs: () => projFs,
|
||||
scenario: "sample1",
|
||||
commandLineArgs: ["--b", "/src/core", "--verbose"],
|
||||
modifyFs: fs => fs.writeFileSync("/src/core/tsconfig.json", `{
|
||||
"compilerOptions": {
|
||||
"incremental": true,
|
||||
"module": "commonjs"
|
||||
}
|
||||
}`),
|
||||
expectedDiagnostics: [
|
||||
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/core/tsconfig.json", "src/core/anotherModule.js"],
|
||||
[Diagnostics.Building_project_0, "/src/core/tsconfig.json"],
|
||||
]
|
||||
},
|
||||
incrementalDtsChangedBuild: {
|
||||
incrementalScenarios: [{
|
||||
buildKind: BuildKind.IncrementalDtsChange,
|
||||
modifyFs: fs => replaceText(fs, "/src/core/tsconfig.json", `"module": "commonjs"`, `"module": "amd"`),
|
||||
expectedDiagnostics: [
|
||||
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/core/tsconfig.json", "src/core/anotherModule.js", "src/core/tsconfig.json"],
|
||||
[Diagnostics.Building_project_0, "/src/core/tsconfig.json"]
|
||||
]
|
||||
},
|
||||
outputFiles: [
|
||||
"/src/core/anotherModule.js",
|
||||
"/src/core/index.js",
|
||||
"/src/core/tsconfig.tsbuildinfo",
|
||||
],
|
||||
baselineOnly: true,
|
||||
verifyDiagnostics: true
|
||||
}],
|
||||
});
|
||||
|
||||
verifyTsbuildOutput({
|
||||
scenario: "when esModuleInterop option changes",
|
||||
projFs: () => projFs,
|
||||
time,
|
||||
tick,
|
||||
proj: "sample1",
|
||||
rootNames: ["/src/tests"],
|
||||
lastProjectOutput: "/src/tests/index.js",
|
||||
initialBuild: {
|
||||
modifyFs: fs => fs.writeFileSync("/src/tests/tsconfig.json", `{
|
||||
verifyTscIncrementalEdits({
|
||||
subScenario: "when esModuleInterop option changes",
|
||||
fs: () => projFs,
|
||||
scenario: "sample1",
|
||||
commandLineArgs: ["--b", "/src/tests", "--verbose"],
|
||||
modifyFs: fs => fs.writeFileSync("/src/tests/tsconfig.json", `{
|
||||
"references": [
|
||||
{ "path": "../core" },
|
||||
{ "path": "../logic" }
|
||||
@@ -994,29 +684,10 @@ class someClass { }`),
|
||||
"esModuleInterop": false
|
||||
}
|
||||
}`),
|
||||
expectedDiagnostics: [
|
||||
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/core/tsconfig.json", "src/core/anotherModule.js"],
|
||||
[Diagnostics.Building_project_0, "/src/core/tsconfig.json"],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/logic/tsconfig.json", "src/logic/index.js"],
|
||||
[Diagnostics.Building_project_0, "/src/logic/tsconfig.json"],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/tests/tsconfig.json", "src/tests/index.js"],
|
||||
[Diagnostics.Building_project_0, "/src/tests/tsconfig.json"]
|
||||
]
|
||||
},
|
||||
incrementalDtsChangedBuild: {
|
||||
incrementalScenarios: [{
|
||||
buildKind: BuildKind.IncrementalDtsChange,
|
||||
modifyFs: fs => replaceText(fs, "/src/tests/tsconfig.json", `"esModuleInterop": false`, `"esModuleInterop": true`),
|
||||
expectedDiagnostics: [
|
||||
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/core/tsconfig.json", "src/core/anotherModule.ts", "src/core/anotherModule.js"],
|
||||
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/logic/tsconfig.json", "src/logic/index.ts", "src/logic/index.js"],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/tests/tsconfig.json", "src/tests/index.js", "src/tests/tsconfig.json"],
|
||||
[Diagnostics.Building_project_0, "/src/tests/tsconfig.json"]
|
||||
]
|
||||
},
|
||||
outputFiles: [],
|
||||
baselineOnly: true,
|
||||
verifyDiagnostics: true
|
||||
}],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace ts {
|
||||
|
||||
function verifyBuild(modifyDiskLayout: (fs: vfs.FileSystem) => void, allExpectedOutputs: readonly string[], expectedFileTraces: readonly string[], ...expectedDiagnostics: fakes.ExpectedDiagnostic[]) {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
modifyDiskLayout(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tsconfig.c.json"], { listFiles: true });
|
||||
builder.build();
|
||||
|
||||
@@ -2,18 +2,12 @@ namespace ts.tscWatch {
|
||||
import projectsLocation = TestFSWithWatch.tsbuildProjectsLocation;
|
||||
import getFilePathInProject = TestFSWithWatch.getTsBuildProjectFilePath;
|
||||
import getFileFromProject = TestFSWithWatch.getTsBuildProjectFile;
|
||||
type TsBuildWatchSystem = WatchedSystem & { writtenFiles: Map<true>; };
|
||||
type TsBuildWatchSystem = TestFSWithWatch.TestServerHostTrackingWrittenFiles;
|
||||
|
||||
function createTsBuildWatchSystem(fileOrFolderList: readonly TestFSWithWatch.FileOrFolderOrSymLink[], params?: TestFSWithWatch.TestServerHostCreationParameters) {
|
||||
const host = createWatchedSystem(fileOrFolderList, params) as TsBuildWatchSystem;
|
||||
const originalWriteFile = host.writeFile;
|
||||
host.writtenFiles = createMap<true>();
|
||||
host.writeFile = (fileName, content) => {
|
||||
originalWriteFile.call(host, fileName, content);
|
||||
const path = host.toFullPath(fileName);
|
||||
host.writtenFiles.set(path, true);
|
||||
};
|
||||
return host;
|
||||
return TestFSWithWatch.changeToHostTrackingWrittenFiles(
|
||||
createWatchedSystem(fileOrFolderList, params)
|
||||
);
|
||||
}
|
||||
|
||||
export function createSolutionBuilder(system: WatchedSystem, rootNames: readonly string[], defaultOptions?: BuildOptions) {
|
||||
@@ -710,8 +704,8 @@ let x: string = 10;`);
|
||||
const coreIndexDts = projectFileName(SubProject.core, "index.d.ts");
|
||||
const coreAnotherModuleDts = projectFileName(SubProject.core, "anotherModule.d.ts");
|
||||
const logicIndexDts = projectFileName(SubProject.logic, "index.d.ts");
|
||||
const expectedWatchedFiles = () => [core[0], logic[0], ...tests, libFile].map(f => f.path).concat([coreIndexDts, coreAnotherModuleDts, logicIndexDts].map(f => f.toLowerCase()));
|
||||
const expectedWatchedDirectoriesRecursive = projectSystem.getTypeRootsFromLocation(projectPath(SubProject.tests));
|
||||
const expectedProjectFiles = () => [libFile, ...tests, ...logic.slice(1), ...core.slice(1, core.length - 1)].map(f => f.path);
|
||||
const expectedProgramFiles = () => [tests[1].path, libFile.path, coreIndexDts, coreAnotherModuleDts, logicIndexDts];
|
||||
|
||||
function createSolutionAndWatchMode() {
|
||||
@@ -723,12 +717,19 @@ let x: string = 10;`);
|
||||
}
|
||||
|
||||
function verifyWatches(host: TsBuildWatchSystem, withTsserver?: boolean) {
|
||||
verifyWatchesOfProject(host, withTsserver ? expectedWatchedFiles().filter(f => f !== tests[1].path.toLowerCase()) : expectedWatchedFiles(), expectedWatchedDirectoriesRecursive);
|
||||
verifyWatchesOfProject(
|
||||
host,
|
||||
withTsserver ?
|
||||
[...core.slice(0, core.length - 1), ...logic, tests[0], libFile].map(f => f.path.toLowerCase()) :
|
||||
[core[0], logic[0], ...tests, libFile].map(f => f.path).concat([coreIndexDts, coreAnotherModuleDts, logicIndexDts].map(f => f.toLowerCase())),
|
||||
expectedWatchedDirectoriesRecursive
|
||||
);
|
||||
}
|
||||
|
||||
function verifyScenario(
|
||||
edit: (host: TsBuildWatchSystem, solutionBuilder: SolutionBuilder<EmitAndSemanticDiagnosticsBuilderProgram>) => void,
|
||||
expectedFilesAfterEdit: () => readonly string[]
|
||||
expectedProgramFilesAfterEdit: () => readonly string[],
|
||||
expectedProjectFilesAfterEdit: () => readonly string[]
|
||||
) {
|
||||
it("with tsc-watch", () => {
|
||||
const { host, solutionBuilder, watch } = createSolutionAndWatchMode();
|
||||
@@ -737,7 +738,7 @@ let x: string = 10;`);
|
||||
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
checkProgramActualFiles(watch(), expectedFilesAfterEdit());
|
||||
checkProgramActualFiles(watch(), expectedProgramFilesAfterEdit());
|
||||
|
||||
});
|
||||
|
||||
@@ -747,7 +748,7 @@ let x: string = 10;`);
|
||||
edit(host, solutionBuilder);
|
||||
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
checkProjectActualFiles(service, tests[0].path, [tests[0].path, ...expectedFilesAfterEdit()]);
|
||||
checkProjectActualFiles(service, tests[0].path, expectedProjectFilesAfterEdit());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -777,7 +778,7 @@ function foo() {
|
||||
|
||||
// not ideal, but currently because of d.ts but no new file is written
|
||||
// There will be timeout queued even though file contents are same
|
||||
}, expectedProgramFiles);
|
||||
}, expectedProgramFiles, expectedProjectFiles);
|
||||
});
|
||||
|
||||
describe("non local edit in ts file, rebuilds in watch compilation", () => {
|
||||
@@ -787,7 +788,7 @@ export function gfoo() {
|
||||
}`);
|
||||
solutionBuilder.invalidateProject(logic[0].path.toLowerCase() as ResolvedConfigFilePath);
|
||||
solutionBuilder.buildNextInvalidatedProject();
|
||||
}, expectedProgramFiles);
|
||||
}, expectedProgramFiles, expectedProjectFiles);
|
||||
});
|
||||
|
||||
describe("change in project reference config file builds correctly", () => {
|
||||
@@ -798,7 +799,7 @@ export function gfoo() {
|
||||
}));
|
||||
solutionBuilder.invalidateProject(logic[0].path.toLowerCase() as ResolvedConfigFilePath, ConfigFileProgramReloadLevel.Full);
|
||||
solutionBuilder.buildNextInvalidatedProject();
|
||||
}, () => [tests[1].path, libFile.path, coreIndexDts, coreAnotherModuleDts, projectFilePath(SubProject.logic, "decls/index.d.ts")]);
|
||||
}, () => [tests[1].path, libFile.path, coreIndexDts, coreAnotherModuleDts, projectFilePath(SubProject.logic, "decls/index.d.ts")], expectedProjectFiles);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -888,7 +889,9 @@ export function gfoo() {
|
||||
const aDts = dtsFile(multiFolder ? "a/index" : "a"), bDts = dtsFile(multiFolder ? "b/index" : "b");
|
||||
const expectedFiles = [jsFile(multiFolder ? "a/index" : "a"), aDts, jsFile(multiFolder ? "b/index" : "b"), bDts, jsFile(multiFolder ? "c/index" : "c")];
|
||||
const expectedProgramFiles = [cTs.path, libFile.path, aDts, refs.path, bDts];
|
||||
const expectedProjectFiles = [cTs.path, libFile.path, aTs.path, refs.path, bTs.path];
|
||||
const expectedWatchedFiles = expectedProgramFiles.concat(cTsconfig.path, bTsconfig.path, aTsconfig.path).map(s => s.toLowerCase());
|
||||
const expectedProjectWatchedFiles = expectedProjectFiles.concat(cTsconfig.path, bTsconfig.path, aTsconfig.path).map(s => s.toLowerCase());
|
||||
const expectedWatchedDirectories = multiFolder ? [
|
||||
getProjectPath(project).toLowerCase() // watches for directories created for resolution of b
|
||||
] : emptyArray;
|
||||
@@ -926,22 +929,29 @@ export function gfoo() {
|
||||
}
|
||||
|
||||
function verifyProject(host: TsBuildWatchSystem, service: projectSystem.TestProjectService, orphanInfos?: readonly string[]) {
|
||||
verifyServerState(host, service, expectedProgramFiles, expectedWatchedFiles, expectedWatchedDirectoriesRecursive, orphanInfos);
|
||||
verifyServerState({ host, service, expectedProjectFiles, expectedProjectWatchedFiles, expectedWatchedDirectoriesRecursive, orphanInfos });
|
||||
}
|
||||
|
||||
function verifyServerState(
|
||||
host: TsBuildWatchSystem,
|
||||
service: projectSystem.TestProjectService,
|
||||
expectedProgramFiles: readonly string[],
|
||||
expectedWatchedFiles: readonly string[],
|
||||
expectedWatchedDirectoriesRecursive: readonly string[],
|
||||
orphanInfos?: readonly string[]) {
|
||||
checkProjectActualFiles(service, cTsconfig.path, expectedProgramFiles.concat(cTsconfig.path));
|
||||
const watchedFiles = expectedWatchedFiles.filter(f => f !== cTs.path.toLowerCase());
|
||||
if (orphanInfos) {
|
||||
interface VerifyServerState {
|
||||
host: TsBuildWatchSystem;
|
||||
service: projectSystem.TestProjectService;
|
||||
expectedProjectFiles: readonly string[];
|
||||
expectedProjectWatchedFiles: readonly string[];
|
||||
expectedWatchedDirectoriesRecursive: readonly string[];
|
||||
orphanInfos?: readonly string[];
|
||||
}
|
||||
function verifyServerState({ host, service, expectedProjectFiles, expectedProjectWatchedFiles, expectedWatchedDirectoriesRecursive, orphanInfos }: VerifyServerState) {
|
||||
checkProjectActualFiles(service, cTsconfig.path, expectedProjectFiles.concat(cTsconfig.path));
|
||||
const watchedFiles = expectedProjectWatchedFiles.filter(f => f !== cTs.path.toLowerCase());
|
||||
const actualOrphan = arrayFrom(mapDefinedIterator(
|
||||
service.filenameToScriptInfo.values(),
|
||||
v => v.containingProjects.length === 0 ? v.fileName : undefined
|
||||
));
|
||||
assert.equal(actualOrphan.length, orphanInfos ? orphanInfos.length : 0, `Orphans found: ${JSON.stringify(actualOrphan, /*replacer*/ undefined, " ")}`);
|
||||
if (orphanInfos && orphanInfos.length) {
|
||||
for (const orphan of orphanInfos) {
|
||||
const info = service.getScriptInfoForPath(orphan as Path);
|
||||
assert.isDefined(info);
|
||||
assert.isDefined(info, `${orphan} expected to be present. Actual: ${JSON.stringify(actualOrphan, /*replacer*/ undefined, " ")}`);
|
||||
assert.equal(info!.containingProjects.length, 0);
|
||||
watchedFiles.push(orphan);
|
||||
}
|
||||
@@ -949,16 +959,20 @@ export function gfoo() {
|
||||
verifyWatchesOfProject(host, watchedFiles, expectedWatchedDirectoriesRecursive, expectedWatchedDirectories);
|
||||
}
|
||||
|
||||
function verifyScenario(
|
||||
edit: (host: TsBuildWatchSystem, solutionBuilder: SolutionBuilder<EmitAndSemanticDiagnosticsBuilderProgram>) => void,
|
||||
expectedEditErrors: readonly string[],
|
||||
expectedProgramFiles: readonly string[],
|
||||
expectedWatchedFiles: readonly string[],
|
||||
expectedWatchedDirectoriesRecursive: readonly string[],
|
||||
dependencies: readonly [string, readonly string[]][],
|
||||
revert?: (host: TsBuildWatchSystem) => void,
|
||||
orphanInfosAfterEdit?: readonly string[],
|
||||
orphanInfosAfterRevert?: readonly string[]) {
|
||||
interface VerifyScenario {
|
||||
edit: (host: TsBuildWatchSystem, solutionBuilder: SolutionBuilder<EmitAndSemanticDiagnosticsBuilderProgram>) => void;
|
||||
expectedEditErrors: readonly string[];
|
||||
expectedProgramFiles: readonly string[];
|
||||
expectedProjectFiles: readonly string[];
|
||||
expectedWatchedFiles: readonly string[];
|
||||
expectedProjectWatchedFiles: readonly string[];
|
||||
expectedWatchedDirectoriesRecursive: readonly string[];
|
||||
dependencies: readonly [string, readonly string[]][];
|
||||
revert?: (host: TsBuildWatchSystem) => void;
|
||||
orphanInfosAfterEdit?: readonly string[];
|
||||
orphanInfosAfterRevert?: readonly string[];
|
||||
}
|
||||
function verifyScenario({ edit, expectedEditErrors, expectedProgramFiles, expectedProjectFiles, expectedWatchedFiles, expectedProjectWatchedFiles, expectedWatchedDirectoriesRecursive, dependencies, revert, orphanInfosAfterEdit, orphanInfosAfterRevert }: VerifyScenario) {
|
||||
it("with tsc-watch", () => {
|
||||
const { host, solutionBuilder, watch } = createSolutionAndWatchMode();
|
||||
|
||||
@@ -985,7 +999,7 @@ export function gfoo() {
|
||||
edit(host, solutionBuilder);
|
||||
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
verifyServerState(host, service, expectedProgramFiles, expectedWatchedFiles, expectedWatchedDirectoriesRecursive, orphanInfosAfterEdit);
|
||||
verifyServerState({ host, service, expectedProjectFiles, expectedProjectWatchedFiles, expectedWatchedDirectoriesRecursive, orphanInfos: orphanInfosAfterEdit });
|
||||
|
||||
if (revert) {
|
||||
revert(host);
|
||||
@@ -1010,20 +1024,21 @@ export function gfoo() {
|
||||
});
|
||||
|
||||
describe("non local edit updates the program and watch correctly", () => {
|
||||
verifyScenario(
|
||||
(host, solutionBuilder) => {
|
||||
verifyScenario({
|
||||
edit: (host, solutionBuilder) => {
|
||||
// edit
|
||||
host.writeFile(bTs.path, `${bTs.content}
|
||||
export function gfoo() {
|
||||
}`);
|
||||
solutionBuilder.invalidateProject(bTsconfig.path.toLowerCase() as ResolvedConfigFilePath);
|
||||
host.writeFile(bTs.path, `${bTs.content}\nexport function gfoo() {\n}`);
|
||||
solutionBuilder.invalidateProject((bTsconfig.path.toLowerCase() as ResolvedConfigFilePath));
|
||||
solutionBuilder.buildNextInvalidatedProject();
|
||||
},
|
||||
emptyArray,
|
||||
expectedEditErrors: emptyArray,
|
||||
expectedProgramFiles,
|
||||
expectedProjectFiles,
|
||||
expectedWatchedFiles,
|
||||
expectedProjectWatchedFiles,
|
||||
expectedWatchedDirectoriesRecursive,
|
||||
defaultDependencies);
|
||||
dependencies: defaultDependencies
|
||||
});
|
||||
});
|
||||
|
||||
describe("edit on config file", () => {
|
||||
@@ -1032,30 +1047,32 @@ export function gfoo() {
|
||||
path: getFilePathInProject(project, "nrefs/a.d.ts"),
|
||||
content: refs.content
|
||||
};
|
||||
verifyScenario(
|
||||
host => {
|
||||
verifyScenario({
|
||||
edit: host => {
|
||||
const cTsConfigJson = JSON.parse(cTsconfig.content);
|
||||
host.ensureFileOrFolder(nrefs);
|
||||
cTsConfigJson.compilerOptions.paths = { "@ref/*": nrefsPath };
|
||||
host.writeFile(cTsconfig.path, JSON.stringify(cTsConfigJson));
|
||||
},
|
||||
emptyArray,
|
||||
expectedProgramFiles.map(nrefReplacer),
|
||||
expectedWatchedFiles.map(nrefReplacer),
|
||||
expectedWatchedDirectoriesRecursive.map(nrefReplacer),
|
||||
[
|
||||
expectedEditErrors: emptyArray,
|
||||
expectedProgramFiles: expectedProgramFiles.map(nrefReplacer),
|
||||
expectedProjectFiles: expectedProjectFiles.map(nrefReplacer),
|
||||
expectedWatchedFiles: expectedWatchedFiles.map(nrefReplacer),
|
||||
expectedProjectWatchedFiles: expectedProjectWatchedFiles.map(nrefReplacer),
|
||||
expectedWatchedDirectoriesRecursive: expectedWatchedDirectoriesRecursive.map(nrefReplacer),
|
||||
dependencies: [
|
||||
[aDts, [aDts]],
|
||||
[bDts, [bDts, aDts]],
|
||||
[nrefs.path, [nrefs.path]],
|
||||
[cTs.path, [cTs.path, nrefs.path, bDts]]
|
||||
],
|
||||
// revert the update
|
||||
host => host.writeFile(cTsconfig.path, cTsconfig.content),
|
||||
revert: host => host.writeFile(cTsconfig.path, cTsconfig.content),
|
||||
// AfterEdit:: Extra watched files on server since the script infos arent deleted till next file open
|
||||
[refs.path.toLowerCase()],
|
||||
orphanInfosAfterEdit: [refs.path.toLowerCase()],
|
||||
// AfterRevert:: Extra watched files on server since the script infos arent deleted till next file open
|
||||
[nrefs.path.toLowerCase()]
|
||||
);
|
||||
orphanInfosAfterRevert: [nrefs.path.toLowerCase()]
|
||||
});
|
||||
});
|
||||
|
||||
describe("edit in referenced config file", () => {
|
||||
@@ -1064,82 +1081,84 @@ export function gfoo() {
|
||||
content: "export declare class A {}"
|
||||
};
|
||||
const expectedProgramFiles = [cTs.path, bDts, nrefs.path, refs.path, libFile.path];
|
||||
const expectedProjectFiles = [cTs.path, bTs.path, nrefs.path, refs.path, libFile.path];
|
||||
const [, ...expectedWatchedDirectoriesRecursiveWithoutA] = expectedWatchedDirectoriesRecursive; // Not looking in a folder for resolution in multi folder scenario
|
||||
verifyScenario(
|
||||
host => {
|
||||
verifyScenario({
|
||||
edit: host => {
|
||||
const bTsConfigJson = JSON.parse(bTsconfig.content);
|
||||
host.ensureFileOrFolder(nrefs);
|
||||
bTsConfigJson.compilerOptions.paths = { "@ref/*": nrefsPath };
|
||||
host.writeFile(bTsconfig.path, JSON.stringify(bTsConfigJson));
|
||||
},
|
||||
emptyArray,
|
||||
expectedEditErrors: emptyArray,
|
||||
expectedProgramFiles,
|
||||
expectedProgramFiles.concat(cTsconfig.path, bTsconfig.path, aTsconfig.path).map(s => s.toLowerCase()),
|
||||
(multiFolder ? expectedWatchedDirectoriesRecursiveWithoutA : expectedWatchedDirectoriesRecursive).concat(getFilePathInProject(project, "nrefs").toLowerCase()),
|
||||
[
|
||||
expectedProjectFiles,
|
||||
expectedWatchedFiles: expectedProgramFiles.concat(cTsconfig.path, bTsconfig.path, aTsconfig.path).map(s => s.toLowerCase()),
|
||||
expectedProjectWatchedFiles: expectedProjectFiles.concat(cTsconfig.path, bTsconfig.path, aTsconfig.path).map(s => s.toLowerCase()),
|
||||
expectedWatchedDirectoriesRecursive: (multiFolder ? expectedWatchedDirectoriesRecursiveWithoutA : expectedWatchedDirectoriesRecursive).concat(getFilePathInProject(project, "nrefs").toLowerCase()),
|
||||
dependencies: [
|
||||
[nrefs.path, [nrefs.path]],
|
||||
[bDts, [bDts, nrefs.path]],
|
||||
[refs.path, [refs.path]],
|
||||
[cTs.path, [cTs.path, refs.path, bDts]],
|
||||
],
|
||||
// revert the update
|
||||
host => host.writeFile(bTsconfig.path, bTsconfig.content),
|
||||
revert: host => host.writeFile(bTsconfig.path, bTsconfig.content),
|
||||
// AfterEdit:: Extra watched files on server since the script infos arent deleted till next file open
|
||||
[aDts.toLowerCase()],
|
||||
orphanInfosAfterEdit: [aTs.path.toLowerCase()],
|
||||
// AfterRevert:: Extra watched files on server since the script infos arent deleted till next file open
|
||||
[nrefs.path.toLowerCase()]
|
||||
);
|
||||
orphanInfosAfterRevert: [nrefs.path.toLowerCase()]
|
||||
});
|
||||
});
|
||||
|
||||
describe("deleting referenced config file", () => {
|
||||
const expectedProgramFiles = [cTs.path, bTs.path, refs.path, libFile.path];
|
||||
const expectedWatchedFiles = expectedProgramFiles.concat(cTsconfig.path, bTsconfig.path).map(s => s.toLowerCase());
|
||||
const [, ...expectedWatchedDirectoriesRecursiveWithoutA] = expectedWatchedDirectoriesRecursive; // Not looking in a folder for resolution in multi folder scenario
|
||||
// Resolutions should change now
|
||||
// Should map to b.ts instead with options from our own config
|
||||
verifyScenario(
|
||||
host => host.deleteFile(bTsconfig.path),
|
||||
[
|
||||
verifyScenario({
|
||||
edit: host => host.deleteFile(bTsconfig.path),
|
||||
expectedEditErrors: [
|
||||
`${multiFolder ? "c/tsconfig.json" : "tsconfig.c.json"}(9,21): error TS6053: File '/user/username/projects/transitiveReferences/${multiFolder ? "b" : "tsconfig.b.json"}' not found.\n`
|
||||
],
|
||||
expectedProgramFiles,
|
||||
expectedProgramFiles.concat(cTsconfig.path, bTsconfig.path).map(s => s.toLowerCase()),
|
||||
multiFolder ? expectedWatchedDirectoriesRecursiveWithoutA : expectedWatchedDirectoriesRecursive,
|
||||
[
|
||||
expectedProjectFiles: expectedProgramFiles,
|
||||
expectedWatchedFiles,
|
||||
expectedProjectWatchedFiles: expectedWatchedFiles,
|
||||
expectedWatchedDirectoriesRecursive: multiFolder ? expectedWatchedDirectoriesRecursiveWithoutA : expectedWatchedDirectoriesRecursive,
|
||||
dependencies: [
|
||||
[bTs.path, [bTs.path, refs.path]],
|
||||
[refs.path, [refs.path]],
|
||||
[cTs.path, [cTs.path, refs.path, bTs.path]],
|
||||
],
|
||||
// revert the update
|
||||
host => host.writeFile(bTsconfig.path, bTsconfig.content),
|
||||
revert: host => host.writeFile(bTsconfig.path, bTsconfig.content),
|
||||
// AfterEdit:: Extra watched files on server since the script infos arent deleted till next file open
|
||||
[bDts.toLowerCase(), aDts.toLowerCase(), aTsconfig.path.toLowerCase()],
|
||||
// AfterRevert:: Extra watched files on server since the script infos arent deleted till next file open
|
||||
[bTs.path.toLowerCase()]
|
||||
);
|
||||
orphanInfosAfterEdit: [aTs.path.toLowerCase(), aTsconfig.path.toLowerCase()],
|
||||
});
|
||||
});
|
||||
|
||||
describe("deleting transitively referenced config file", () => {
|
||||
verifyScenario(
|
||||
host => host.deleteFile(aTsconfig.path),
|
||||
[
|
||||
verifyScenario({
|
||||
edit: host => host.deleteFile(aTsconfig.path),
|
||||
expectedEditErrors: [
|
||||
`${multiFolder ? "b/tsconfig.json" : "tsconfig.b.json"}(10,21): error TS6053: File '/user/username/projects/transitiveReferences/${multiFolder ? "a" : "tsconfig.a.json"}' not found.\n`
|
||||
],
|
||||
expectedProgramFiles.map(s => s.replace(aDts, aTs.path)),
|
||||
expectedWatchedFiles.map(s => s.replace(aDts.toLowerCase(), aTs.path.toLocaleLowerCase())),
|
||||
expectedProgramFiles: expectedProgramFiles.map(s => s.replace(aDts, aTs.path)),
|
||||
expectedProjectFiles,
|
||||
expectedWatchedFiles: expectedWatchedFiles.map(s => s.replace(aDts.toLowerCase(), aTs.path.toLocaleLowerCase())),
|
||||
expectedProjectWatchedFiles,
|
||||
expectedWatchedDirectoriesRecursive,
|
||||
[
|
||||
dependencies: [
|
||||
[aTs.path, [aTs.path]],
|
||||
[bDts, [bDts, aTs.path]],
|
||||
[refs.path, [refs.path]],
|
||||
[cTs.path, [cTs.path, refs.path, bDts]],
|
||||
],
|
||||
// revert the update
|
||||
host => host.writeFile(aTsconfig.path, aTsconfig.content),
|
||||
// AfterEdit:: Extra watched files on server since the script infos arent deleted till next file open
|
||||
[aDts.toLowerCase()],
|
||||
// AfterRevert:: Extra watched files on server since the script infos arent deleted till next file open
|
||||
[aTs.path.toLowerCase()]
|
||||
);
|
||||
revert: host => host.writeFile(aTsconfig.path, aTsconfig.content),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
namespace ts {
|
||||
export type TscCompileSystem = fakes.System & {
|
||||
writtenFiles: Map<true>;
|
||||
baseLine(): void;
|
||||
};
|
||||
function executeCommandLine(sys: TscCompileSystem, commandLineArgs: readonly string[]) {
|
||||
if (isBuild(commandLineArgs)) {
|
||||
return performBuild(sys, commandLineArgs.slice(1));
|
||||
}
|
||||
|
||||
const reportDiagnostic = createDiagnosticReporter(sys);
|
||||
const commandLine = parseCommandLine(commandLineArgs, path => sys.readFile(path));
|
||||
if (commandLine.options.build) {
|
||||
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Option_build_must_be_the_first_command_line_argument));
|
||||
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
|
||||
}
|
||||
|
||||
if (commandLine.errors.length > 0) {
|
||||
commandLine.errors.forEach(reportDiagnostic);
|
||||
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
|
||||
}
|
||||
|
||||
let configFileName: string | undefined;
|
||||
if (commandLine.options.project) {
|
||||
if (commandLine.fileNames.length !== 0) {
|
||||
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Option_project_cannot_be_mixed_with_source_files_on_a_command_line));
|
||||
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
|
||||
}
|
||||
|
||||
const fileOrDirectory = normalizePath(commandLine.options.project);
|
||||
if (!fileOrDirectory /* current directory "." */ || sys.directoryExists(fileOrDirectory)) {
|
||||
configFileName = combinePaths(fileOrDirectory, "tsconfig.json");
|
||||
if (!sys.fileExists(configFileName)) {
|
||||
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Cannot_find_a_tsconfig_json_file_at_the_specified_directory_Colon_0, commandLine.options.project));
|
||||
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
|
||||
}
|
||||
}
|
||||
else {
|
||||
configFileName = fileOrDirectory;
|
||||
if (!sys.fileExists(configFileName)) {
|
||||
reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_specified_path_does_not_exist_Colon_0, commandLine.options.project));
|
||||
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (commandLine.fileNames.length === 0) {
|
||||
const searchPath = normalizePath(sys.getCurrentDirectory());
|
||||
configFileName = findConfigFile(searchPath, sys.fileExists);
|
||||
}
|
||||
|
||||
Debug.assert(commandLine.fileNames.length !== 0 || !!configFileName);
|
||||
|
||||
if (configFileName) {
|
||||
const configParseResult = Debug.assertDefined(parseConfigFileWithSystem(configFileName, commandLine.options, sys, reportDiagnostic));
|
||||
if (isIncrementalCompilation(configParseResult.options)) {
|
||||
performIncrementalCompilation(sys, configParseResult);
|
||||
}
|
||||
else {
|
||||
performCompilation(sys, configParseResult);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (isIncrementalCompilation(commandLine.options)) {
|
||||
performIncrementalCompilation(sys, commandLine);
|
||||
}
|
||||
else {
|
||||
performCompilation(sys, commandLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createReportErrorSummary(sys: TscCompileSystem, options: CompilerOptions): ReportEmitErrorSummary | undefined {
|
||||
return options.pretty ?
|
||||
errorCount => sys.write(getErrorSummaryText(errorCount, sys.newLine)) :
|
||||
undefined;
|
||||
}
|
||||
|
||||
function performCompilation(sys: TscCompileSystem, config: ParsedCommandLine) {
|
||||
const { fileNames, options, projectReferences } = config;
|
||||
const reportDiagnostic = createDiagnosticReporter(sys, options.pretty);
|
||||
const host = createCompilerHostWorker(options, /*setParentPos*/ undefined, sys);
|
||||
const currentDirectory = host.getCurrentDirectory();
|
||||
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames());
|
||||
changeCompilerHostLikeToUseCache(host, fileName => toPath(fileName, currentDirectory, getCanonicalFileName));
|
||||
const program = createProgram({
|
||||
rootNames: fileNames,
|
||||
options,
|
||||
projectReferences,
|
||||
host,
|
||||
configFileParsingDiagnostics: getConfigFileParsingDiagnostics(config)
|
||||
});
|
||||
const exitStatus = emitFilesAndReportErrorsAndGetExitStatus(
|
||||
program,
|
||||
reportDiagnostic,
|
||||
s => sys.write(s + sys.newLine),
|
||||
createReportErrorSummary(sys, options)
|
||||
);
|
||||
baselineBuildInfo([config], sys.vfs, sys.writtenFiles);
|
||||
return sys.exit(exitStatus);
|
||||
}
|
||||
|
||||
function performIncrementalCompilation(sys: TscCompileSystem, config: ParsedCommandLine) {
|
||||
const reportDiagnostic = createDiagnosticReporter(sys, config.options.pretty);
|
||||
const { options, fileNames, projectReferences } = config;
|
||||
const exitCode = ts.performIncrementalCompilation({
|
||||
system: sys,
|
||||
rootNames: fileNames,
|
||||
options,
|
||||
configFileParsingDiagnostics: getConfigFileParsingDiagnostics(config),
|
||||
projectReferences,
|
||||
reportDiagnostic,
|
||||
reportErrorSummary: createReportErrorSummary(sys, options),
|
||||
});
|
||||
baselineBuildInfo([config], sys.vfs, sys.writtenFiles);
|
||||
return sys.exit(exitCode);
|
||||
}
|
||||
|
||||
function performBuild(sys: TscCompileSystem, args: string[]) {
|
||||
const { buildOptions, projects, errors } = parseBuildCommand(args);
|
||||
const reportDiagnostic = createDiagnosticReporter(sys, buildOptions.pretty);
|
||||
|
||||
if (errors.length > 0) {
|
||||
errors.forEach(reportDiagnostic);
|
||||
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
|
||||
}
|
||||
|
||||
Debug.assert(projects.length !== 0);
|
||||
|
||||
const buildHost = createSolutionBuilderHost(
|
||||
sys,
|
||||
/*createProgram*/ undefined,
|
||||
reportDiagnostic,
|
||||
createBuilderStatusReporter(sys, buildOptions.pretty),
|
||||
createReportErrorSummary(sys, buildOptions)
|
||||
);
|
||||
fakes.patchSolutionBuilderHost(buildHost, sys);
|
||||
const builder = createSolutionBuilder(buildHost, projects, buildOptions);
|
||||
const exitCode = buildOptions.clean ? builder.clean() : builder.build();
|
||||
baselineBuildInfo(builder.getAllParsedConfigs(), sys.vfs, sys.writtenFiles);
|
||||
return sys.exit(exitCode);
|
||||
}
|
||||
|
||||
function isBuild(commandLineArgs: readonly string[]) {
|
||||
if (commandLineArgs.length > 0 && commandLineArgs[0].charCodeAt(0) === CharacterCodes.minus) {
|
||||
const firstOption = commandLineArgs[0].slice(commandLineArgs[0].charCodeAt(1) === CharacterCodes.minus ? 2 : 1).toLowerCase();
|
||||
return firstOption === "build" || firstOption === "b";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export enum BuildKind {
|
||||
Initial = "initial-build",
|
||||
IncrementalDtsChange = "incremental-declaration-changes",
|
||||
IncrementalDtsUnchanged = "incremental-declaration-doesnt-change",
|
||||
IncrementalHeadersChange = "incremental-headers-change-without-dts-changes"
|
||||
}
|
||||
|
||||
export interface TscCompile {
|
||||
scenario: string;
|
||||
subScenario: string;
|
||||
buildKind?: BuildKind; // Should be defined for tsc --b
|
||||
fs: () => vfs.FileSystem;
|
||||
commandLineArgs: readonly string[];
|
||||
|
||||
modifyFs?: (fs: vfs.FileSystem) => void;
|
||||
baselineSourceMap?: boolean;
|
||||
baselineReadFileCalls?: boolean;
|
||||
}
|
||||
|
||||
export function tscCompile(input: TscCompile) {
|
||||
const baseFs = input.fs();
|
||||
const fs = baseFs.shadow();
|
||||
const {
|
||||
scenario, subScenario, buildKind,
|
||||
commandLineArgs, modifyFs,
|
||||
baselineSourceMap, baselineReadFileCalls
|
||||
} = input;
|
||||
if (modifyFs) modifyFs(fs);
|
||||
|
||||
// Create system
|
||||
const sys = new fakes.System(fs, { executingFilePath: "/lib/tsc" }) as TscCompileSystem;
|
||||
const writtenFiles = sys.writtenFiles = createMap<true>();
|
||||
const originalWriteFile = sys.writeFile;
|
||||
sys.writeFile = (fileName, content, writeByteOrderMark) => {
|
||||
assert.isFalse(writtenFiles.has(fileName));
|
||||
writtenFiles.set(fileName, true);
|
||||
return originalWriteFile.call(sys, fileName, content, writeByteOrderMark);
|
||||
};
|
||||
const actualReadFileMap: MapLike<number> = {};
|
||||
const originalReadFile = sys.readFile;
|
||||
sys.readFile = path => {
|
||||
// Dont record libs
|
||||
if (path.startsWith("/src/")) {
|
||||
actualReadFileMap[path] = (getProperty(actualReadFileMap, path) || 0) + 1;
|
||||
}
|
||||
return originalReadFile.call(sys, path);
|
||||
};
|
||||
|
||||
sys.write(`${sys.getExecutingFilePath()} ${commandLineArgs.join(" ")}\n`);
|
||||
sys.exit = exitCode => sys.exitCode = exitCode;
|
||||
executeCommandLine(sys, commandLineArgs);
|
||||
sys.write(`exitCode:: ${sys.exitCode}\n`);
|
||||
if (baselineReadFileCalls) {
|
||||
sys.write(`readFiles:: ${JSON.stringify(actualReadFileMap, /*replacer*/ undefined, " ")} `);
|
||||
}
|
||||
if (baselineSourceMap) generateSourceMapBaselineFiles(fs, mapDefinedIterator(writtenFiles.keys(), f => f.endsWith(".map") ? f : undefined));
|
||||
|
||||
// Baseline the errors
|
||||
fs.writeFileSync(`/lib/${buildKind || BuildKind.Initial}Output.txt`, sys.output.join(""));
|
||||
fs.makeReadonly();
|
||||
|
||||
sys.baseLine = () => {
|
||||
const patch = fs.diff(baseFs, { includeChangedFileWithSameContent: true });
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
Harness.Baseline.runBaseline(`${isBuild(commandLineArgs) ? "tsbuild" : "tsc"}/${scenario}/${buildKind || BuildKind.Initial}/${subScenario.split(" ").join("-")}.js`, patch ? vfs.formatPatch(patch) : null);
|
||||
};
|
||||
return sys;
|
||||
}
|
||||
|
||||
export function verifyTscBaseline(sys: () => TscCompileSystem) {
|
||||
it(`Generates files matching the baseline`, () => {
|
||||
sys().baseLine();
|
||||
});
|
||||
}
|
||||
|
||||
export function verifyTsc(input: TscCompile) {
|
||||
describe(input.scenario, () => {
|
||||
describe(input.subScenario, () => {
|
||||
let sys: TscCompileSystem;
|
||||
before(() => {
|
||||
sys = tscCompile({
|
||||
...input,
|
||||
fs: () => getFsWithTime(input.fs()).fs.makeReadonly()
|
||||
});
|
||||
});
|
||||
after(() => {
|
||||
sys = undefined!;
|
||||
});
|
||||
verifyTscBaseline(() => sys);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,8 @@ namespace ts.tscWatch {
|
||||
file,
|
||||
fileStamp: host.getModifiedTime(file.path.replace(".ts", ".js")),
|
||||
errors: builderProgram.getSemanticDiagnostics(watch().getSourceFileByPath(file.path as Path)),
|
||||
errorsFromOldState: !!state.semanticDiagnosticsFromOldState && state.semanticDiagnosticsFromOldState.has(file.path)
|
||||
errorsFromOldState: !!state.semanticDiagnosticsFromOldState && state.semanticDiagnosticsFromOldState.has(file.path),
|
||||
dtsStamp: host.getModifiedTime(file.path.replace(".ts", ".d.ts"))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -24,21 +25,36 @@ namespace ts.tscWatch {
|
||||
return find(stampsAndErrors, info => info.file === file)!;
|
||||
}
|
||||
|
||||
function verifyOutputFileStampsAndErrors(
|
||||
file: File,
|
||||
emitExpected: boolean,
|
||||
errorRefershExpected: boolean,
|
||||
beforeChangeFileStampsAndErrors: readonly ReturnType<typeof getOutputFileStampAndError>[],
|
||||
afterChangeFileStampsAndErrors: readonly ReturnType<typeof getOutputFileStampAndError>[]
|
||||
) {
|
||||
interface VerifyOutputFileStampAndErrors {
|
||||
file: File;
|
||||
jsEmitExpected: boolean;
|
||||
dtsEmitExpected: boolean;
|
||||
errorRefershExpected: boolean;
|
||||
beforeChangeFileStampsAndErrors: readonly ReturnType<typeof getOutputFileStampAndError>[];
|
||||
afterChangeFileStampsAndErrors: readonly ReturnType<typeof getOutputFileStampAndError>[];
|
||||
}
|
||||
function verifyOutputFileStampsAndErrors({
|
||||
file,
|
||||
jsEmitExpected,
|
||||
dtsEmitExpected,
|
||||
errorRefershExpected,
|
||||
beforeChangeFileStampsAndErrors,
|
||||
afterChangeFileStampsAndErrors
|
||||
}: VerifyOutputFileStampAndErrors) {
|
||||
const beforeChange = findStampAndErrors(beforeChangeFileStampsAndErrors, file);
|
||||
const afterChange = findStampAndErrors(afterChangeFileStampsAndErrors, file);
|
||||
if (emitExpected) {
|
||||
if (jsEmitExpected) {
|
||||
assert.notStrictEqual(afterChange.fileStamp, beforeChange.fileStamp, `Expected emit for file ${file.path}`);
|
||||
}
|
||||
else {
|
||||
assert.strictEqual(afterChange.fileStamp, beforeChange.fileStamp, `Did not expect new emit for file ${file.path}`);
|
||||
}
|
||||
if (dtsEmitExpected) {
|
||||
assert.notStrictEqual(afterChange.dtsStamp, beforeChange.dtsStamp, `Expected emit for file ${file.path}`);
|
||||
}
|
||||
else {
|
||||
assert.strictEqual(afterChange.dtsStamp, beforeChange.dtsStamp, `Did not expect new emit for file ${file.path}`);
|
||||
}
|
||||
if (errorRefershExpected) {
|
||||
if (afterChange.errors !== emptyArray || beforeChange.errors !== emptyArray) {
|
||||
assert.notStrictEqual(afterChange.errors, beforeChange.errors, `Expected new errors for file ${file.path}`);
|
||||
@@ -51,19 +67,22 @@ namespace ts.tscWatch {
|
||||
}
|
||||
}
|
||||
|
||||
interface VerifyEmitAndErrorUpdates {
|
||||
change: (host: WatchedSystem) => void;
|
||||
getInitialErrors: (watch: Watch) => readonly Diagnostic[] | readonly string[];
|
||||
getIncrementalErrors: (watch: Watch) => readonly Diagnostic[] | readonly string[];
|
||||
filesWithNewEmit: readonly File[];
|
||||
filesWithOnlyErrorRefresh: readonly File[];
|
||||
filesNotTouched: readonly File[];
|
||||
configFile?: File;
|
||||
interface VerifyEmitAndErrorUpdatesWorker extends VerifyEmitAndErrorUpdates {
|
||||
configFile: File;
|
||||
}
|
||||
|
||||
function verifyEmitAndErrorUpdates({ filesWithNewEmit, filesWithOnlyErrorRefresh, filesNotTouched, configFile = config, change, getInitialErrors, getIncrementalErrors }: VerifyEmitAndErrorUpdates) {
|
||||
function verifyEmitAndErrorUpdatesWorker({
|
||||
fileWithChange,
|
||||
filesWithNewEmit,
|
||||
filesWithOnlyErrorRefresh,
|
||||
filesNotTouched,
|
||||
configFile,
|
||||
change,
|
||||
getInitialErrors,
|
||||
getIncrementalErrors
|
||||
}: VerifyEmitAndErrorUpdatesWorker) {
|
||||
const nonLibFiles = [...filesWithNewEmit, ...filesWithOnlyErrorRefresh, ...filesNotTouched];
|
||||
const files = [...nonLibFiles, configFile, libFile];
|
||||
const compilerOptions = (JSON.parse(configFile.content).compilerOptions || {}) as CompilerOptions;
|
||||
const host = createWatchedSystem(files, { currentDirectory });
|
||||
const watch = createWatchOfConfigFile("tsconfig.json", host);
|
||||
checkProgramActualFiles(watch(), [...nonLibFiles.map(f => f.path), libFile.path]);
|
||||
@@ -73,9 +92,77 @@ namespace ts.tscWatch {
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkOutputErrorsIncremental(host, getIncrementalErrors(watch));
|
||||
const afterChange = getOutputFileStampsAndErrors(host, watch, nonLibFiles);
|
||||
filesWithNewEmit.forEach(file => verifyOutputFileStampsAndErrors(file, /*emitExpected*/ true, /*errorRefershExpected*/ true, beforeChange, afterChange));
|
||||
filesWithOnlyErrorRefresh.forEach(file => verifyOutputFileStampsAndErrors(file, /*emitExpected*/ false, /*errorRefershExpected*/ true, beforeChange, afterChange));
|
||||
filesNotTouched.forEach(file => verifyOutputFileStampsAndErrors(file, /*emitExpected*/ false, /*errorRefershExpected*/ false, beforeChange, afterChange));
|
||||
filesWithNewEmit.forEach(file => verifyOutputFileStampsAndErrors({
|
||||
file,
|
||||
jsEmitExpected: !compilerOptions.isolatedModules || fileWithChange === file,
|
||||
dtsEmitExpected: getEmitDeclarations(compilerOptions),
|
||||
errorRefershExpected: true,
|
||||
beforeChangeFileStampsAndErrors: beforeChange,
|
||||
afterChangeFileStampsAndErrors: afterChange
|
||||
}));
|
||||
filesWithOnlyErrorRefresh.forEach(file => verifyOutputFileStampsAndErrors({
|
||||
file,
|
||||
jsEmitExpected: false,
|
||||
dtsEmitExpected: getEmitDeclarations(compilerOptions) && !file.path.endsWith(".d.ts"),
|
||||
errorRefershExpected: true,
|
||||
beforeChangeFileStampsAndErrors: beforeChange,
|
||||
afterChangeFileStampsAndErrors: afterChange
|
||||
}));
|
||||
filesNotTouched.forEach(file => verifyOutputFileStampsAndErrors({
|
||||
file,
|
||||
jsEmitExpected: false,
|
||||
dtsEmitExpected: false,
|
||||
errorRefershExpected: false,
|
||||
beforeChangeFileStampsAndErrors: beforeChange,
|
||||
afterChangeFileStampsAndErrors: afterChange
|
||||
}));
|
||||
}
|
||||
|
||||
function changeCompilerOptions(input: VerifyEmitAndErrorUpdates, additionalOptions: CompilerOptions): File {
|
||||
const configFile = input.configFile || config;
|
||||
const content = JSON.parse(configFile.content);
|
||||
content.compilerOptions = { ...content.compilerOptions, ...additionalOptions };
|
||||
return { path: configFile.path, content: JSON.stringify(content) };
|
||||
}
|
||||
|
||||
interface VerifyEmitAndErrorUpdates {
|
||||
change: (host: WatchedSystem) => void;
|
||||
getInitialErrors: (watch: Watch) => readonly Diagnostic[] | readonly string[];
|
||||
getIncrementalErrors: (watch: Watch) => readonly Diagnostic[] | readonly string[];
|
||||
fileWithChange: File;
|
||||
filesWithNewEmit: readonly File[];
|
||||
filesWithOnlyErrorRefresh: readonly File[];
|
||||
filesNotTouched: readonly File[];
|
||||
configFile?: File;
|
||||
}
|
||||
function verifyEmitAndErrorUpdates(input: VerifyEmitAndErrorUpdates) {
|
||||
it("with default config", () => {
|
||||
verifyEmitAndErrorUpdatesWorker({
|
||||
...input,
|
||||
configFile: input.configFile || config
|
||||
});
|
||||
});
|
||||
|
||||
it("with default config and --declaration", () => {
|
||||
verifyEmitAndErrorUpdatesWorker({
|
||||
...input,
|
||||
configFile: changeCompilerOptions(input, { declaration: true })
|
||||
});
|
||||
});
|
||||
|
||||
it("config with --isolatedModules", () => {
|
||||
verifyEmitAndErrorUpdatesWorker({
|
||||
...input,
|
||||
configFile: changeCompilerOptions(input, { isolatedModules: true })
|
||||
});
|
||||
});
|
||||
|
||||
it("config with --isolatedModules and --declaration", () => {
|
||||
verifyEmitAndErrorUpdatesWorker({
|
||||
...input,
|
||||
configFile: changeCompilerOptions(input, { isolatedModules: true, declaration: true })
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe("deep import changes", () => {
|
||||
@@ -93,6 +180,7 @@ console.log(b.c.d);`
|
||||
addImportedModule(bFile);
|
||||
addImportedModule(cFile);
|
||||
verifyEmitAndErrorUpdates({
|
||||
fileWithChange: cFile,
|
||||
filesWithNewEmit,
|
||||
filesWithOnlyErrorRefresh,
|
||||
filesNotTouched: emptyArray,
|
||||
@@ -113,7 +201,7 @@ console.log(b.c.d);`
|
||||
}
|
||||
}
|
||||
|
||||
it("updates errors when deep import file changes", () => {
|
||||
describe("updates errors when deep import file changes", () => {
|
||||
const bFile: File = {
|
||||
path: `${currentDirectory}/b.ts`,
|
||||
content: `import {C} from './c';
|
||||
@@ -132,7 +220,7 @@ export class B
|
||||
verifyDeepImportChange(bFile, cFile);
|
||||
});
|
||||
|
||||
it("updates errors when deep import through declaration file changes", () => {
|
||||
describe("updates errors when deep import through declaration file changes", () => {
|
||||
const bFile: File = {
|
||||
path: `${currentDirectory}/b.d.ts`,
|
||||
content: `import {C} from './c';
|
||||
@@ -152,7 +240,7 @@ export class B
|
||||
});
|
||||
});
|
||||
|
||||
it("updates errors in file not exporting a deep multilevel import that changes", () => {
|
||||
describe("updates errors in file not exporting a deep multilevel import that changes", () => {
|
||||
const aFile: File = {
|
||||
path: `${currentDirectory}/a.ts`,
|
||||
content: `export interface Point {
|
||||
@@ -193,6 +281,7 @@ getPoint().c.x;`
|
||||
content: `import "./d";`
|
||||
};
|
||||
verifyEmitAndErrorUpdates({
|
||||
fileWithChange: aFile,
|
||||
filesWithNewEmit: [aFile, bFile],
|
||||
filesWithOnlyErrorRefresh: [cFile, dFile],
|
||||
filesNotTouched: [eFile],
|
||||
@@ -265,6 +354,7 @@ export class Data {
|
||||
filesWithOnlyErrorRefresh.push(lib2Data2);
|
||||
}
|
||||
verifyEmitAndErrorUpdates({
|
||||
fileWithChange: lib1ToolsInterface,
|
||||
filesWithNewEmit,
|
||||
filesWithOnlyErrorRefresh,
|
||||
filesNotTouched: emptyArray,
|
||||
@@ -276,11 +366,11 @@ export class Data {
|
||||
]
|
||||
});
|
||||
}
|
||||
it("when there are no circular import and exports", () => {
|
||||
describe("when there are no circular import and exports", () => {
|
||||
verifyTransitiveExports(lib2Data);
|
||||
});
|
||||
|
||||
it("when there are circular import and exports", () => {
|
||||
describe("when there are circular import and exports", () => {
|
||||
const lib2Data: File = {
|
||||
path: `${currentDirectory}/lib2/data.ts`,
|
||||
content: `import { ITest } from "lib1/public"; import { Data2 } from "./data2";
|
||||
|
||||
@@ -179,7 +179,7 @@ namespace ts.projectSystem {
|
||||
}
|
||||
|
||||
function verifyUserTsConfigProject(session: TestSession) {
|
||||
checkProjectActualFiles(session.getProjectService().configuredProjects.get(userTsconfig.path)!, [userTs.path, aDts.path, userTsconfig.path]);
|
||||
checkProjectActualFiles(session.getProjectService().configuredProjects.get(userTsconfig.path)!, [userTs.path, aTs.path, userTsconfig.path]);
|
||||
}
|
||||
|
||||
it("goToDefinition", () => {
|
||||
@@ -450,6 +450,13 @@ namespace ts.projectSystem {
|
||||
name: "function f(): void",
|
||||
},
|
||||
references: [
|
||||
makeReferenceEntry({
|
||||
file: aTs,
|
||||
text: "f",
|
||||
options: { index: 1 },
|
||||
contextText: "function f() {}",
|
||||
isDefinition: true
|
||||
}),
|
||||
{
|
||||
fileName: bTs.path,
|
||||
isDefinition: false,
|
||||
@@ -457,13 +464,6 @@ namespace ts.projectSystem {
|
||||
isWriteAccess: false,
|
||||
textSpan: { start: 0, length: 1 },
|
||||
},
|
||||
makeReferenceEntry({
|
||||
file: aTs,
|
||||
text: "f",
|
||||
options: { index: 1 },
|
||||
contextText: "function f() {}",
|
||||
isDefinition: true
|
||||
})
|
||||
],
|
||||
}
|
||||
]);
|
||||
|
||||
@@ -73,44 +73,64 @@ namespace ts.projectSystem {
|
||||
verifyEvent(project, `Change in config file detected`);
|
||||
});
|
||||
|
||||
it("when opening original location project", () => {
|
||||
const aDTs: File = {
|
||||
path: `${projectRoot}/a/a.d.ts`,
|
||||
content: `export declare class A {
|
||||
describe("when opening original location project", () => {
|
||||
it("with project references", () => {
|
||||
verify();
|
||||
});
|
||||
|
||||
it("when disableSourceOfProjectReferenceRedirect is true", () => {
|
||||
verify(/*disableSourceOfProjectReferenceRedirect*/ true);
|
||||
});
|
||||
|
||||
function verify(disableSourceOfProjectReferenceRedirect?: true) {
|
||||
const aDTs: File = {
|
||||
path: `${projectRoot}/a/a.d.ts`,
|
||||
content: `export declare class A {
|
||||
}
|
||||
//# sourceMappingURL=a.d.ts.map
|
||||
`
|
||||
};
|
||||
const aDTsMap: File = {
|
||||
path: `${projectRoot}/a/a.d.ts.map`,
|
||||
content: `{"version":3,"file":"a.d.ts","sourceRoot":"","sources":["./a.ts"],"names":[],"mappings":"AAAA,qBAAa,CAAC;CAAI"}`
|
||||
};
|
||||
const bTs: File = {
|
||||
path: bTsPath,
|
||||
content: `import {A} from "../a/a"; new A();`
|
||||
};
|
||||
const configB: File = {
|
||||
path: configBPath,
|
||||
content: JSON.stringify({
|
||||
references: [{ path: "../a" }]
|
||||
})
|
||||
};
|
||||
};
|
||||
const aDTsMap: File = {
|
||||
path: `${projectRoot}/a/a.d.ts.map`,
|
||||
content: `{"version":3,"file":"a.d.ts","sourceRoot":"","sources":["./a.ts"],"names":[],"mappings":"AAAA,qBAAa,CAAC;CAAI"}`
|
||||
};
|
||||
const bTs: File = {
|
||||
path: bTsPath,
|
||||
content: `import {A} from "../a/a"; new A();`
|
||||
};
|
||||
const configB: File = {
|
||||
path: configBPath,
|
||||
content: JSON.stringify({
|
||||
...(disableSourceOfProjectReferenceRedirect && {
|
||||
compilerOptions: {
|
||||
disableSourceOfProjectReferenceRedirect
|
||||
}
|
||||
}),
|
||||
references: [{ path: "../a" }]
|
||||
})
|
||||
};
|
||||
|
||||
const { service, session, verifyEventWithOpenTs, verifyEvent } = createSessionToVerifyEvent(files.concat(aDTs, aDTsMap, bTs, configB));
|
||||
verifyEventWithOpenTs(bTs, configB.path, 1);
|
||||
const { service, session, verifyEventWithOpenTs, verifyEvent } = createSessionToVerifyEvent(files.concat(aDTs, aDTsMap, bTs, configB));
|
||||
verifyEventWithOpenTs(bTs, configB.path, 1);
|
||||
|
||||
session.executeCommandSeq<protocol.ReferencesRequest>({
|
||||
command: protocol.CommandTypes.References,
|
||||
arguments: {
|
||||
file: bTs.path,
|
||||
...protocolLocationFromSubstring(bTs.content, "A()")
|
||||
}
|
||||
});
|
||||
session.executeCommandSeq<protocol.ReferencesRequest>({
|
||||
command: protocol.CommandTypes.References,
|
||||
arguments: {
|
||||
file: bTs.path,
|
||||
...protocolLocationFromSubstring(bTs.content, "A()")
|
||||
}
|
||||
});
|
||||
|
||||
checkNumberOfProjects(service, { configuredProjects: 2 });
|
||||
const project = service.configuredProjects.get(configA.path)!;
|
||||
assert.isDefined(project);
|
||||
verifyEvent(project, `Creating project for original file: ${aTs.path} for location: ${aDTs.path}`);
|
||||
checkNumberOfProjects(service, { configuredProjects: 2 });
|
||||
const project = service.configuredProjects.get(configA.path)!;
|
||||
assert.isDefined(project);
|
||||
verifyEvent(
|
||||
project,
|
||||
disableSourceOfProjectReferenceRedirect ?
|
||||
`Creating project for original file: ${aTs.path} for location: ${aDTs.path}` :
|
||||
`Creating project for original file: ${aTs.path}`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
describe("with external projects and config files ", () => {
|
||||
|
||||
@@ -491,8 +491,8 @@ namespace ts.projectSystem {
|
||||
checkArray("Open files", arrayFrom(projectService.openFiles.keys(), path => projectService.getScriptInfoForPath(path as Path)!.fileName), expectedFiles.map(file => file.path));
|
||||
}
|
||||
|
||||
export function checkScriptInfos(projectService: server.ProjectService, expectedFiles: readonly string[]) {
|
||||
checkArray("ScriptInfos files", arrayFrom(projectService.filenameToScriptInfo.values(), info => info.fileName), expectedFiles);
|
||||
export function checkScriptInfos(projectService: server.ProjectService, expectedFiles: readonly string[], additionInfo?: string) {
|
||||
checkArray(`ScriptInfos files: ${additionInfo || ""}`, arrayFrom(projectService.filenameToScriptInfo.values(), info => info.fileName), expectedFiles);
|
||||
}
|
||||
|
||||
export function protocolLocationFromSubstring(str: string, substring: string): protocol.Location {
|
||||
@@ -501,7 +501,7 @@ namespace ts.projectSystem {
|
||||
return protocolToLocation(str)(start);
|
||||
}
|
||||
|
||||
function protocolToLocation(text: string): (pos: number) => protocol.Location {
|
||||
export function protocolToLocation(text: string): (pos: number) => protocol.Location {
|
||||
const lineStarts = computeLineStarts(text);
|
||||
return pos => {
|
||||
const x = computeLineAndCharacterOfPosition(lineStarts, pos);
|
||||
|
||||
@@ -0,0 +1,410 @@
|
||||
namespace ts.projectSystem {
|
||||
describe("unittests:: tsserver:: with project references and compile on save", () => {
|
||||
const projectLocation = "/user/username/projects/myproject";
|
||||
const dependecyLocation = `${projectLocation}/dependency`;
|
||||
const usageLocation = `${projectLocation}/usage`;
|
||||
const dependencyTs: File = {
|
||||
path: `${dependecyLocation}/fns.ts`,
|
||||
content: `export function fn1() { }
|
||||
export function fn2() { }
|
||||
`
|
||||
};
|
||||
const dependencyConfig: File = {
|
||||
path: `${dependecyLocation}/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
compilerOptions: { composite: true, declarationDir: "../decls" },
|
||||
compileOnSave: true
|
||||
})
|
||||
};
|
||||
const usageTs: File = {
|
||||
path: `${usageLocation}/usage.ts`,
|
||||
content: `import {
|
||||
fn1,
|
||||
fn2,
|
||||
} from '../decls/fns'
|
||||
fn1();
|
||||
fn2();
|
||||
`
|
||||
};
|
||||
const usageConfig: File = {
|
||||
path: `${usageLocation}/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
compileOnSave: true,
|
||||
references: [{ path: "../dependency" }]
|
||||
})
|
||||
};
|
||||
|
||||
interface VerifySingleScenarioWorker extends VerifySingleScenario {
|
||||
withProject: boolean;
|
||||
}
|
||||
function verifySingleScenarioWorker({
|
||||
withProject, scenario, openFiles, requestArgs, change, expectedResult
|
||||
}: VerifySingleScenarioWorker) {
|
||||
it(scenario, () => {
|
||||
const host = TestFSWithWatch.changeToHostTrackingWrittenFiles(
|
||||
createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile])
|
||||
);
|
||||
const session = createSession(host);
|
||||
openFilesForSession(openFiles(), session);
|
||||
const reqArgs = requestArgs();
|
||||
const {
|
||||
expectedAffected,
|
||||
expectedEmit: { expectedEmitSuccess, expectedFiles },
|
||||
expectedEmitOutput
|
||||
} = expectedResult(withProject);
|
||||
|
||||
if (change) {
|
||||
session.executeCommandSeq<protocol.CompileOnSaveAffectedFileListRequest>({
|
||||
command: protocol.CommandTypes.CompileOnSaveAffectedFileList,
|
||||
arguments: { file: dependencyTs.path }
|
||||
});
|
||||
const { file, insertString } = change();
|
||||
if (session.getProjectService().openFiles.has(file.path)) {
|
||||
const toLocation = protocolToLocation(file.content);
|
||||
const location = toLocation(file.content.length);
|
||||
session.executeCommandSeq<protocol.ChangeRequest>({
|
||||
command: protocol.CommandTypes.Change,
|
||||
arguments: {
|
||||
file: file.path,
|
||||
...location,
|
||||
endLine: location.line,
|
||||
endOffset: location.offset,
|
||||
insertString
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
host.writeFile(file.path, `${file.content}${insertString}`);
|
||||
}
|
||||
host.writtenFiles.clear();
|
||||
}
|
||||
|
||||
const args = withProject ? reqArgs : { file: reqArgs.file };
|
||||
// Verify CompileOnSaveAffectedFileList
|
||||
const actualAffectedFiles = session.executeCommandSeq<protocol.CompileOnSaveAffectedFileListRequest>({
|
||||
command: protocol.CommandTypes.CompileOnSaveAffectedFileList,
|
||||
arguments: args
|
||||
}).response as protocol.CompileOnSaveAffectedFileListSingleProject[];
|
||||
assert.deepEqual(actualAffectedFiles, expectedAffected, "Affected files");
|
||||
|
||||
// Verify CompileOnSaveEmit
|
||||
const actualEmit = session.executeCommandSeq<protocol.CompileOnSaveEmitFileRequest>({
|
||||
command: protocol.CommandTypes.CompileOnSaveEmitFile,
|
||||
arguments: args
|
||||
}).response;
|
||||
assert.deepEqual(actualEmit, expectedEmitSuccess, "Emit files");
|
||||
assert.equal(host.writtenFiles.size, expectedFiles.length);
|
||||
for (const file of expectedFiles) {
|
||||
assert.equal(host.readFile(file.path), file.content, `Expected to write ${file.path}`);
|
||||
assert.isTrue(host.writtenFiles.has(file.path), `${file.path} is newly written`);
|
||||
}
|
||||
|
||||
// Verify EmitOutput
|
||||
const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq<protocol.EmitOutputRequest>({
|
||||
command: protocol.CommandTypes.EmitOutput,
|
||||
arguments: args
|
||||
}).response as EmitOutput;
|
||||
assert.deepEqual(actualEmitOutput, expectedEmitOutput, "Emit output");
|
||||
});
|
||||
}
|
||||
|
||||
interface VerifySingleScenario {
|
||||
scenario: string;
|
||||
openFiles: () => readonly File[];
|
||||
requestArgs: () => protocol.FileRequestArgs;
|
||||
skipWithoutProject?: boolean;
|
||||
change?: () => SingleScenarioChange;
|
||||
expectedResult: GetSingleScenarioResult;
|
||||
}
|
||||
function verifySingleScenario(scenario: VerifySingleScenario) {
|
||||
if (!scenario.skipWithoutProject) {
|
||||
describe("without specifying project file", () => {
|
||||
verifySingleScenarioWorker({
|
||||
withProject: false,
|
||||
...scenario
|
||||
});
|
||||
});
|
||||
}
|
||||
describe("with specifying project file", () => {
|
||||
verifySingleScenarioWorker({
|
||||
withProject: true,
|
||||
...scenario
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
interface SingleScenarioExpectedEmit {
|
||||
expectedEmitSuccess: boolean;
|
||||
expectedFiles: readonly File[];
|
||||
}
|
||||
interface SingleScenarioResult {
|
||||
expectedAffected: protocol.CompileOnSaveAffectedFileListSingleProject[];
|
||||
expectedEmit: SingleScenarioExpectedEmit;
|
||||
expectedEmitOutput: EmitOutput;
|
||||
}
|
||||
type GetSingleScenarioResult = (withProject: boolean) => SingleScenarioResult;
|
||||
interface SingleScenarioChange {
|
||||
file: File;
|
||||
insertString: string;
|
||||
}
|
||||
interface ScenarioDetails {
|
||||
scenarioName: string;
|
||||
requestArgs: () => protocol.FileRequestArgs;
|
||||
skipWithoutProject?: boolean;
|
||||
initial: GetSingleScenarioResult;
|
||||
localChangeToDependency: GetSingleScenarioResult;
|
||||
localChangeToUsage: GetSingleScenarioResult;
|
||||
changeToDependency: GetSingleScenarioResult;
|
||||
changeToUsage: GetSingleScenarioResult;
|
||||
}
|
||||
interface VerifyScenario {
|
||||
openFiles: () => readonly File[];
|
||||
scenarios: readonly ScenarioDetails[];
|
||||
}
|
||||
|
||||
const localChange = "function fn3() { }";
|
||||
const change = `export ${localChange}`;
|
||||
const changeJs = `function fn3() { }
|
||||
exports.fn3 = fn3;`;
|
||||
const changeDts = "export declare function fn3(): void;";
|
||||
function verifyScenario({ openFiles, scenarios }: VerifyScenario) {
|
||||
for (const {
|
||||
scenarioName, requestArgs, skipWithoutProject, initial,
|
||||
localChangeToDependency, localChangeToUsage,
|
||||
changeToDependency, changeToUsage
|
||||
} of scenarios) {
|
||||
describe(scenarioName, () => {
|
||||
verifySingleScenario({
|
||||
scenario: "with initial file open",
|
||||
openFiles,
|
||||
requestArgs,
|
||||
skipWithoutProject,
|
||||
expectedResult: initial
|
||||
});
|
||||
|
||||
verifySingleScenario({
|
||||
scenario: "with local change to dependency",
|
||||
openFiles,
|
||||
requestArgs,
|
||||
skipWithoutProject,
|
||||
change: () => ({ file: dependencyTs, insertString: localChange }),
|
||||
expectedResult: localChangeToDependency
|
||||
});
|
||||
|
||||
verifySingleScenario({
|
||||
scenario: "with local change to usage",
|
||||
openFiles,
|
||||
requestArgs,
|
||||
skipWithoutProject,
|
||||
change: () => ({ file: usageTs, insertString: localChange }),
|
||||
expectedResult: localChangeToUsage
|
||||
});
|
||||
|
||||
verifySingleScenario({
|
||||
scenario: "with change to dependency",
|
||||
openFiles,
|
||||
requestArgs,
|
||||
skipWithoutProject,
|
||||
change: () => ({ file: dependencyTs, insertString: change }),
|
||||
expectedResult: changeToDependency
|
||||
});
|
||||
|
||||
verifySingleScenario({
|
||||
scenario: "with change to usage",
|
||||
openFiles,
|
||||
requestArgs,
|
||||
skipWithoutProject,
|
||||
change: () => ({ file: usageTs, insertString: change }),
|
||||
expectedResult: changeToUsage
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function expectedAffectedFiles(config: File, fileNames: File[]): protocol.CompileOnSaveAffectedFileListSingleProject {
|
||||
return {
|
||||
projectFileName: config.path,
|
||||
fileNames: fileNames.map(f => f.path),
|
||||
projectUsesOutFile: false
|
||||
};
|
||||
}
|
||||
|
||||
function expectedUsageEmit(appendJsText?: string): SingleScenarioExpectedEmit {
|
||||
const appendJs = appendJsText ? `${appendJsText}
|
||||
` : "";
|
||||
return {
|
||||
expectedEmitSuccess: true,
|
||||
expectedFiles: [{
|
||||
path: `${usageLocation}/usage.js`,
|
||||
content: `"use strict";
|
||||
exports.__esModule = true;
|
||||
var fns_1 = require("../decls/fns");
|
||||
fns_1.fn1();
|
||||
fns_1.fn2();
|
||||
${appendJs}`
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
function expectedEmitOutput({ expectedFiles }: SingleScenarioExpectedEmit): EmitOutput {
|
||||
return {
|
||||
outputFiles: expectedFiles.map(({ path, content }) => ({
|
||||
name: path,
|
||||
text: content,
|
||||
writeByteOrderMark: false
|
||||
})),
|
||||
emitSkipped: false
|
||||
};
|
||||
}
|
||||
|
||||
function expectedUsageEmitOutput(appendJsText?: string): EmitOutput {
|
||||
return expectedEmitOutput(expectedUsageEmit(appendJsText));
|
||||
}
|
||||
|
||||
function noEmit(): SingleScenarioExpectedEmit {
|
||||
return {
|
||||
expectedEmitSuccess: false,
|
||||
expectedFiles: emptyArray
|
||||
};
|
||||
}
|
||||
|
||||
function noEmitOutput(): EmitOutput {
|
||||
return {
|
||||
emitSkipped: true,
|
||||
outputFiles: []
|
||||
};
|
||||
}
|
||||
|
||||
function expectedDependencyEmit(appendJsText?: string, appendDtsText?: string): SingleScenarioExpectedEmit {
|
||||
const appendJs = appendJsText ? `${appendJsText}
|
||||
` : "";
|
||||
const appendDts = appendDtsText ? `${appendDtsText}
|
||||
` : "";
|
||||
return {
|
||||
expectedEmitSuccess: true,
|
||||
expectedFiles: [
|
||||
{
|
||||
path: `${dependecyLocation}/fns.js`,
|
||||
content: `"use strict";
|
||||
exports.__esModule = true;
|
||||
function fn1() { }
|
||||
exports.fn1 = fn1;
|
||||
function fn2() { }
|
||||
exports.fn2 = fn2;
|
||||
${appendJs}`
|
||||
},
|
||||
{
|
||||
path: `${projectLocation}/decls/fns.d.ts`,
|
||||
content: `export declare function fn1(): void;
|
||||
export declare function fn2(): void;
|
||||
${appendDts}`
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
function expectedDependencyEmitOutput(appendJsText?: string, appendDtsText?: string): EmitOutput {
|
||||
return expectedEmitOutput(expectedDependencyEmit(appendJsText, appendDtsText));
|
||||
}
|
||||
|
||||
function scenarioDetailsOfUsage(isDependencyOpen?: boolean): ScenarioDetails[] {
|
||||
return [
|
||||
{
|
||||
scenarioName: "Of usageTs",
|
||||
requestArgs: () => ({ file: usageTs.path, projectFileName: usageConfig.path }),
|
||||
initial: () => initialUsageTs(),
|
||||
// no change to usage so same as initial only usage file
|
||||
localChangeToDependency: () => initialUsageTs(),
|
||||
localChangeToUsage: () => initialUsageTs(localChange),
|
||||
changeToDependency: () => initialUsageTs(),
|
||||
changeToUsage: () => initialUsageTs(changeJs)
|
||||
},
|
||||
{
|
||||
scenarioName: "Of dependencyTs in usage project",
|
||||
requestArgs: () => ({ file: dependencyTs.path, projectFileName: usageConfig.path }),
|
||||
skipWithoutProject: !!isDependencyOpen,
|
||||
initial: () => initialDependencyTs(),
|
||||
localChangeToDependency: () => initialDependencyTs(/*noUsageFiles*/ true),
|
||||
localChangeToUsage: () => initialDependencyTs(/*noUsageFiles*/ true),
|
||||
changeToDependency: () => initialDependencyTs(),
|
||||
changeToUsage: () => initialDependencyTs(/*noUsageFiles*/ true)
|
||||
}
|
||||
];
|
||||
|
||||
function initialUsageTs(jsText?: string) {
|
||||
return {
|
||||
expectedAffected: [
|
||||
expectedAffectedFiles(usageConfig, [usageTs])
|
||||
],
|
||||
expectedEmit: expectedUsageEmit(jsText),
|
||||
expectedEmitOutput: expectedUsageEmitOutput(jsText)
|
||||
};
|
||||
}
|
||||
|
||||
function initialDependencyTs(noUsageFiles?: true) {
|
||||
return {
|
||||
expectedAffected: [
|
||||
expectedAffectedFiles(usageConfig, noUsageFiles ? [] : [usageTs])
|
||||
],
|
||||
expectedEmit: noEmit(),
|
||||
expectedEmitOutput: noEmitOutput()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function scenarioDetailsOfDependencyWhenOpen(): ScenarioDetails {
|
||||
return {
|
||||
scenarioName: "Of dependencyTs",
|
||||
requestArgs: () => ({ file: dependencyTs.path, projectFileName: dependencyConfig.path }),
|
||||
initial,
|
||||
localChangeToDependency: withProject => ({
|
||||
expectedAffected: withProject ?
|
||||
[
|
||||
expectedAffectedFiles(dependencyConfig, [dependencyTs])
|
||||
] :
|
||||
[
|
||||
expectedAffectedFiles(usageConfig, []),
|
||||
expectedAffectedFiles(dependencyConfig, [dependencyTs])
|
||||
],
|
||||
expectedEmit: expectedDependencyEmit(localChange),
|
||||
expectedEmitOutput: expectedDependencyEmitOutput(localChange)
|
||||
}),
|
||||
localChangeToUsage: withProject => initial(withProject, /*noUsageFiles*/ true),
|
||||
changeToDependency: withProject => initial(withProject, /*noUsageFiles*/ undefined, changeJs, changeDts),
|
||||
changeToUsage: withProject => initial(withProject, /*noUsageFiles*/ true)
|
||||
};
|
||||
|
||||
function initial(withProject: boolean, noUsageFiles?: true, appendJs?: string, appendDts?: string): SingleScenarioResult {
|
||||
return {
|
||||
expectedAffected: withProject ?
|
||||
[
|
||||
expectedAffectedFiles(dependencyConfig, [dependencyTs])
|
||||
] :
|
||||
[
|
||||
expectedAffectedFiles(usageConfig, noUsageFiles ? [] : [usageTs]),
|
||||
expectedAffectedFiles(dependencyConfig, [dependencyTs])
|
||||
],
|
||||
expectedEmit: expectedDependencyEmit(appendJs, appendDts),
|
||||
expectedEmitOutput: expectedDependencyEmitOutput(appendJs, appendDts)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
describe("when dependency project is not open", () => {
|
||||
verifyScenario({
|
||||
openFiles: () => [usageTs],
|
||||
scenarios: scenarioDetailsOfUsage()
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the depedency file is open", () => {
|
||||
verifyScenario({
|
||||
openFiles: () => [usageTs, dependencyTs],
|
||||
scenarios: [
|
||||
...scenarioDetailsOfUsage(/*isDependencyOpen*/ true),
|
||||
scenarioDetailsOfDependencyWhenOpen(),
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,430 @@
|
||||
namespace ts.projectSystem {
|
||||
describe("unittests:: tsserver:: with project references and error reporting", () => {
|
||||
const projectLocation = "/user/username/projects/myproject";
|
||||
const dependecyLocation = `${projectLocation}/dependency`;
|
||||
const usageLocation = `${projectLocation}/usage`;
|
||||
|
||||
interface CheckErrorsInFile {
|
||||
session: TestSession;
|
||||
host: TestServerHost;
|
||||
expected: GetErrDiagnostics;
|
||||
expectedSequenceId?: number;
|
||||
}
|
||||
function checkErrorsInFile({ session, host, expected: { file, syntax, semantic, suggestion }, expectedSequenceId }: CheckErrorsInFile) {
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
checkErrorMessage(session, "syntaxDiag", { file: file.path, diagnostics: syntax });
|
||||
session.clearMessages();
|
||||
|
||||
host.runQueuedImmediateCallbacks(1);
|
||||
checkErrorMessage(session, "semanticDiag", { file: file.path, diagnostics: semantic });
|
||||
session.clearMessages();
|
||||
|
||||
host.runQueuedImmediateCallbacks(1);
|
||||
checkErrorMessage(session, "suggestionDiag", { file: file.path, diagnostics: suggestion });
|
||||
if (expectedSequenceId !== undefined) {
|
||||
checkCompleteEvent(session, 2, expectedSequenceId);
|
||||
}
|
||||
session.clearMessages();
|
||||
}
|
||||
|
||||
interface CheckAllErrors {
|
||||
session: TestSession;
|
||||
host: TestServerHost;
|
||||
expected: readonly GetErrDiagnostics[];
|
||||
expectedSequenceId: number;
|
||||
}
|
||||
function checkAllErrors({ session, host, expected, expectedSequenceId }: CheckAllErrors) {
|
||||
for (let i = 0; i < expected.length; i++) {
|
||||
checkErrorsInFile({
|
||||
session,
|
||||
host,
|
||||
expected: expected[i],
|
||||
expectedSequenceId: i === expected.length - 1 ? expectedSequenceId : undefined
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function verifyErrorsUsingGeterr({ allFiles, openFiles, expectedGetErr }: VerifyScenario) {
|
||||
it("verifies the errors in open file", () => {
|
||||
const host = createServerHost([...allFiles(), libFile]);
|
||||
const session = createSession(host, { canUseEvents: true, });
|
||||
openFilesForSession(openFiles(), session);
|
||||
|
||||
session.clearMessages();
|
||||
const expectedSequenceId = session.getNextSeq();
|
||||
const expected = expectedGetErr();
|
||||
session.executeCommandSeq<protocol.GeterrRequest>({
|
||||
command: protocol.CommandTypes.Geterr,
|
||||
arguments: {
|
||||
delay: 0,
|
||||
files: expected.map(f => f.file.path)
|
||||
}
|
||||
});
|
||||
|
||||
checkAllErrors({ session, host, expected, expectedSequenceId });
|
||||
});
|
||||
}
|
||||
|
||||
function verifyErrorsUsingGeterrForProject({ allFiles, openFiles, expectedGetErrForProject }: VerifyScenario) {
|
||||
it("verifies the errors in projects", () => {
|
||||
const host = createServerHost([...allFiles(), libFile]);
|
||||
const session = createSession(host, { canUseEvents: true, });
|
||||
openFilesForSession(openFiles(), session);
|
||||
|
||||
session.clearMessages();
|
||||
for (const expected of expectedGetErrForProject()) {
|
||||
const expectedSequenceId = session.getNextSeq();
|
||||
session.executeCommandSeq<protocol.GeterrForProjectRequest>({
|
||||
command: protocol.CommandTypes.GeterrForProject,
|
||||
arguments: {
|
||||
delay: 0,
|
||||
file: expected.project
|
||||
}
|
||||
});
|
||||
|
||||
checkAllErrors({ session, host, expected: expected.errors, expectedSequenceId });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function verifyErrorsUsingSyncMethods({ allFiles, openFiles, expectedSyncDiagnostics }: VerifyScenario) {
|
||||
it("verifies the errors using sync commands", () => {
|
||||
const host = createServerHost([...allFiles(), libFile]);
|
||||
const session = createSession(host);
|
||||
openFilesForSession(openFiles(), session);
|
||||
for (const { file, project, syntax, semantic, suggestion } of expectedSyncDiagnostics()) {
|
||||
const actualSyntax = session.executeCommandSeq<protocol.SyntacticDiagnosticsSyncRequest>({
|
||||
command: protocol.CommandTypes.SyntacticDiagnosticsSync,
|
||||
arguments: {
|
||||
file: file.path,
|
||||
projectFileName: project
|
||||
}
|
||||
}).response as protocol.Diagnostic[];
|
||||
assert.deepEqual(actualSyntax, syntax, `Syntax diagnostics for file: ${file.path}, project: ${project}`);
|
||||
const actualSemantic = session.executeCommandSeq<protocol.SemanticDiagnosticsSyncRequest>({
|
||||
command: protocol.CommandTypes.SemanticDiagnosticsSync,
|
||||
arguments: {
|
||||
file: file.path,
|
||||
projectFileName: project
|
||||
}
|
||||
}).response as protocol.Diagnostic[];
|
||||
assert.deepEqual(actualSemantic, semantic, `Semantic diagnostics for file: ${file.path}, project: ${project}`);
|
||||
const actualSuggestion = session.executeCommandSeq<protocol.SuggestionDiagnosticsSyncRequest>({
|
||||
command: protocol.CommandTypes.SuggestionDiagnosticsSync,
|
||||
arguments: {
|
||||
file: file.path,
|
||||
projectFileName: project
|
||||
}
|
||||
}).response as protocol.Diagnostic[];
|
||||
assert.deepEqual(actualSuggestion, suggestion, `Suggestion diagnostics for file: ${file.path}, project: ${project}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function verifyConfigFileErrors({ allFiles, openFiles, expectedConfigFileDiagEvents }: VerifyScenario) {
|
||||
it("verify config file errors", () => {
|
||||
const host = createServerHost([...allFiles(), libFile]);
|
||||
const { session, events } = createSessionWithEventTracking<server.ConfigFileDiagEvent>(host, server.ConfigFileDiagEvent);
|
||||
|
||||
for (const file of openFiles()) {
|
||||
session.executeCommandSeq<protocol.OpenRequest>({
|
||||
command: protocol.CommandTypes.Open,
|
||||
arguments: { file: file.path }
|
||||
});
|
||||
}
|
||||
|
||||
assert.deepEqual(events, expectedConfigFileDiagEvents().map(data => ({
|
||||
eventName: server.ConfigFileDiagEvent,
|
||||
data
|
||||
})));
|
||||
});
|
||||
}
|
||||
|
||||
interface GetErrDiagnostics {
|
||||
file: File;
|
||||
syntax: protocol.Diagnostic[];
|
||||
semantic: protocol.Diagnostic[];
|
||||
suggestion: protocol.Diagnostic[];
|
||||
}
|
||||
interface GetErrForProjectDiagnostics {
|
||||
project: string;
|
||||
errors: readonly GetErrDiagnostics[];
|
||||
}
|
||||
interface SyncDiagnostics extends GetErrDiagnostics {
|
||||
project?: string;
|
||||
}
|
||||
interface VerifyScenario {
|
||||
allFiles: () => readonly File[];
|
||||
openFiles: () => readonly File[];
|
||||
expectedGetErr: () => readonly GetErrDiagnostics[];
|
||||
expectedGetErrForProject: () => readonly GetErrForProjectDiagnostics[];
|
||||
expectedSyncDiagnostics: () => readonly SyncDiagnostics[];
|
||||
expectedConfigFileDiagEvents: () => readonly server.ConfigFileDiagEvent["data"][];
|
||||
}
|
||||
function verifyScenario(scenario: VerifyScenario) {
|
||||
verifyErrorsUsingGeterr(scenario);
|
||||
verifyErrorsUsingGeterrForProject(scenario);
|
||||
verifyErrorsUsingSyncMethods(scenario);
|
||||
verifyConfigFileErrors(scenario);
|
||||
}
|
||||
|
||||
function emptyDiagnostics(file: File): GetErrDiagnostics {
|
||||
return {
|
||||
file,
|
||||
syntax: emptyArray,
|
||||
semantic: emptyArray,
|
||||
suggestion: emptyArray
|
||||
};
|
||||
}
|
||||
|
||||
function syncDiagnostics(diagnostics: GetErrDiagnostics, project: string): SyncDiagnostics {
|
||||
return { project, ...diagnostics };
|
||||
}
|
||||
|
||||
interface VerifyUsageAndDependency {
|
||||
allFiles: readonly [File, File, File, File]; // dependencyTs, dependencyConfig, usageTs, usageConfig
|
||||
usageDiagnostics(): GetErrDiagnostics;
|
||||
dependencyDiagnostics(): GetErrDiagnostics;
|
||||
|
||||
}
|
||||
function verifyUsageAndDependency({ allFiles, usageDiagnostics, dependencyDiagnostics }: VerifyUsageAndDependency) {
|
||||
const [dependencyTs, dependencyConfig, usageTs, usageConfig] = allFiles;
|
||||
function usageProjectDiagnostics(): GetErrForProjectDiagnostics {
|
||||
return {
|
||||
project: usageTs.path,
|
||||
errors: [
|
||||
usageDiagnostics(),
|
||||
emptyDiagnostics(dependencyTs)
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
function dependencyProjectDiagnostics(): GetErrForProjectDiagnostics {
|
||||
return {
|
||||
project: dependencyTs.path,
|
||||
errors: [
|
||||
dependencyDiagnostics()
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
function usageConfigDiag(): server.ConfigFileDiagEvent["data"] {
|
||||
return {
|
||||
triggerFile: usageTs.path,
|
||||
configFileName: usageConfig.path,
|
||||
diagnostics: emptyArray
|
||||
};
|
||||
}
|
||||
|
||||
function dependencyConfigDiag(): server.ConfigFileDiagEvent["data"] {
|
||||
return {
|
||||
triggerFile: dependencyTs.path,
|
||||
configFileName: dependencyConfig.path,
|
||||
diagnostics: emptyArray
|
||||
};
|
||||
}
|
||||
|
||||
describe("when dependency project is not open", () => {
|
||||
verifyScenario({
|
||||
allFiles: () => allFiles,
|
||||
openFiles: () => [usageTs],
|
||||
expectedGetErr: () => [
|
||||
usageDiagnostics()
|
||||
],
|
||||
expectedGetErrForProject: () => [
|
||||
usageProjectDiagnostics(),
|
||||
{
|
||||
project: dependencyTs.path,
|
||||
errors: [
|
||||
emptyDiagnostics(dependencyTs),
|
||||
usageDiagnostics()
|
||||
]
|
||||
}
|
||||
],
|
||||
expectedSyncDiagnostics: () => [
|
||||
// Without project
|
||||
usageDiagnostics(),
|
||||
emptyDiagnostics(dependencyTs),
|
||||
// With project
|
||||
syncDiagnostics(usageDiagnostics(), usageConfig.path),
|
||||
syncDiagnostics(emptyDiagnostics(dependencyTs), usageConfig.path),
|
||||
],
|
||||
expectedConfigFileDiagEvents: () => [
|
||||
usageConfigDiag()
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the depedency file is open", () => {
|
||||
verifyScenario({
|
||||
allFiles: () => allFiles,
|
||||
openFiles: () => [usageTs, dependencyTs],
|
||||
expectedGetErr: () => [
|
||||
usageDiagnostics(),
|
||||
dependencyDiagnostics(),
|
||||
],
|
||||
expectedGetErrForProject: () => [
|
||||
usageProjectDiagnostics(),
|
||||
dependencyProjectDiagnostics()
|
||||
],
|
||||
expectedSyncDiagnostics: () => [
|
||||
// Without project
|
||||
usageDiagnostics(),
|
||||
dependencyDiagnostics(),
|
||||
// With project
|
||||
syncDiagnostics(usageDiagnostics(), usageConfig.path),
|
||||
syncDiagnostics(emptyDiagnostics(dependencyTs), usageConfig.path),
|
||||
syncDiagnostics(dependencyDiagnostics(), dependencyConfig.path),
|
||||
],
|
||||
expectedConfigFileDiagEvents: () => [
|
||||
usageConfigDiag(),
|
||||
dependencyConfigDiag()
|
||||
],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe("with module scenario", () => {
|
||||
const dependencyTs: File = {
|
||||
path: `${dependecyLocation}/fns.ts`,
|
||||
content: `export function fn1() { }
|
||||
export function fn2() { }
|
||||
// Introduce error for fnErr import in main
|
||||
// export function fnErr() { }
|
||||
// Error in dependency ts file
|
||||
export let x: string = 10;`
|
||||
};
|
||||
const dependencyConfig: File = {
|
||||
path: `${dependecyLocation}/tsconfig.json`,
|
||||
content: JSON.stringify({ compilerOptions: { composite: true, declarationDir: "../decls" } })
|
||||
};
|
||||
const usageTs: File = {
|
||||
path: `${usageLocation}/usage.ts`,
|
||||
content: `import {
|
||||
fn1,
|
||||
fn2,
|
||||
fnErr
|
||||
} from '../decls/fns'
|
||||
fn1();
|
||||
fn2();
|
||||
fnErr();
|
||||
`
|
||||
};
|
||||
const usageConfig: File = {
|
||||
path: `${usageLocation}/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
compilerOptions: { composite: true },
|
||||
references: [{ path: "../dependency" }]
|
||||
})
|
||||
};
|
||||
function usageDiagnostics(): GetErrDiagnostics {
|
||||
return {
|
||||
file: usageTs,
|
||||
syntax: emptyArray,
|
||||
semantic: [
|
||||
createDiagnostic(
|
||||
{ line: 4, offset: 5 },
|
||||
{ line: 4, offset: 10 },
|
||||
Diagnostics.Module_0_has_no_exported_member_1,
|
||||
[`"../dependency/fns"`, "fnErr"],
|
||||
"error",
|
||||
)
|
||||
],
|
||||
suggestion: emptyArray
|
||||
};
|
||||
}
|
||||
|
||||
function dependencyDiagnostics(): GetErrDiagnostics {
|
||||
return {
|
||||
file: dependencyTs,
|
||||
syntax: emptyArray,
|
||||
semantic: [
|
||||
createDiagnostic(
|
||||
{ line: 6, offset: 12 },
|
||||
{ line: 6, offset: 13 },
|
||||
Diagnostics.Type_0_is_not_assignable_to_type_1,
|
||||
["10", "string"],
|
||||
"error",
|
||||
)
|
||||
],
|
||||
suggestion: emptyArray
|
||||
};
|
||||
}
|
||||
|
||||
verifyUsageAndDependency({
|
||||
allFiles: [dependencyTs, dependencyConfig, usageTs, usageConfig],
|
||||
usageDiagnostics,
|
||||
dependencyDiagnostics
|
||||
});
|
||||
});
|
||||
|
||||
describe("with non module --out", () => {
|
||||
const dependencyTs: File = {
|
||||
path: `${dependecyLocation}/fns.ts`,
|
||||
content: `function fn1() { }
|
||||
function fn2() { }
|
||||
// Introduce error for fnErr import in main
|
||||
// function fnErr() { }
|
||||
// Error in dependency ts file
|
||||
let x: string = 10;`
|
||||
};
|
||||
const dependencyConfig: File = {
|
||||
path: `${dependecyLocation}/tsconfig.json`,
|
||||
content: JSON.stringify({ compilerOptions: { composite: true, outFile: "../dependency.js" } })
|
||||
};
|
||||
const usageTs: File = {
|
||||
path: `${usageLocation}/usage.ts`,
|
||||
content: `fn1();
|
||||
fn2();
|
||||
fnErr();
|
||||
`
|
||||
};
|
||||
const usageConfig: File = {
|
||||
path: `${usageLocation}/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
compilerOptions: { composite: true, outFile: "../usage.js" },
|
||||
references: [{ path: "../dependency" }]
|
||||
})
|
||||
};
|
||||
function usageDiagnostics(): GetErrDiagnostics {
|
||||
return {
|
||||
file: usageTs,
|
||||
syntax: emptyArray,
|
||||
semantic: [
|
||||
createDiagnostic(
|
||||
{ line: 3, offset: 1 },
|
||||
{ line: 3, offset: 6 },
|
||||
Diagnostics.Cannot_find_name_0,
|
||||
["fnErr"],
|
||||
"error",
|
||||
)
|
||||
],
|
||||
suggestion: emptyArray
|
||||
};
|
||||
}
|
||||
|
||||
function dependencyDiagnostics(): GetErrDiagnostics {
|
||||
return {
|
||||
file: dependencyTs,
|
||||
syntax: emptyArray,
|
||||
semantic: [
|
||||
createDiagnostic(
|
||||
{ line: 6, offset: 5 },
|
||||
{ line: 6, offset: 6 },
|
||||
Diagnostics.Type_0_is_not_assignable_to_type_1,
|
||||
["10", "string"],
|
||||
"error",
|
||||
)
|
||||
],
|
||||
suggestion: emptyArray
|
||||
};
|
||||
}
|
||||
|
||||
verifyUsageAndDependency({
|
||||
allFiles: [dependencyTs, dependencyConfig, usageTs, usageConfig],
|
||||
usageDiagnostics,
|
||||
dependencyDiagnostics
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user