mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge remote-tracking branch 'origin/master' into release-3.7
This commit is contained in:
+30
-18
@@ -161,7 +161,12 @@ namespace ts {
|
||||
IsObjectLiteralOrClassExpressionMethod = 1 << 7,
|
||||
}
|
||||
|
||||
let flowNodeCreated: <T extends FlowNode>(node: T) => T = identity;
|
||||
function initFlowNode<T extends FlowNode>(node: T) {
|
||||
Debug.attachFlowNodeDebugInfo(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
let flowNodeCreated: <T extends FlowNode>(node: T) => T = initFlowNode;
|
||||
|
||||
const binder = createBinder();
|
||||
|
||||
@@ -238,6 +243,10 @@ namespace ts {
|
||||
|
||||
Symbol = objectAllocator.getSymbolConstructor();
|
||||
|
||||
// Attach debugging information if necessary
|
||||
Debug.attachFlowNodeDebugInfo(unreachableFlow);
|
||||
Debug.attachFlowNodeDebugInfo(reportedUnreachableFlow);
|
||||
|
||||
if (!file.locals) {
|
||||
bind(file);
|
||||
file.symbolCount = symbolCount;
|
||||
@@ -626,7 +635,7 @@ namespace ts {
|
||||
// A non-async, non-generator IIFE is considered part of the containing control flow. Return statements behave
|
||||
// similarly to break statements that exit to a label just past the statement body.
|
||||
if (!isIIFE) {
|
||||
currentFlow = { flags: FlowFlags.Start };
|
||||
currentFlow = initFlowNode({ flags: FlowFlags.Start });
|
||||
if (containerFlags & (ContainerFlags.IsFunctionExpression | ContainerFlags.IsObjectLiteralOrClassExpressionMethod)) {
|
||||
currentFlow.node = <FunctionExpression | ArrowFunction | MethodDeclaration>node;
|
||||
}
|
||||
@@ -638,7 +647,7 @@ namespace ts {
|
||||
currentContinueTarget = undefined;
|
||||
activeLabels = undefined;
|
||||
hasExplicitReturn = false;
|
||||
flowNodeCreated = identity;
|
||||
flowNodeCreated = initFlowNode;
|
||||
bindChildren(node);
|
||||
// Reset all reachability check related flags on node (for incremental scenarios)
|
||||
node.flags &= ~NodeFlags.ReachabilityAndEmitFlags;
|
||||
@@ -812,9 +821,6 @@ namespace ts {
|
||||
case SyntaxKind.JSDocEnumTag:
|
||||
bindJSDocTypeAlias(node as JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag);
|
||||
break;
|
||||
case SyntaxKind.JSDocClassTag:
|
||||
bindJSDocClassTag(node as JSDocClassTag);
|
||||
break;
|
||||
// In source files and blocks, bind functions first to match hoisting that occurs at runtime
|
||||
case SyntaxKind.SourceFile: {
|
||||
bindEachFunctionsFirst((node as SourceFile).statements);
|
||||
@@ -856,9 +862,8 @@ namespace ts {
|
||||
function isNarrowableReference(expr: Expression): boolean {
|
||||
return expr.kind === SyntaxKind.Identifier || expr.kind === SyntaxKind.ThisKeyword || expr.kind === SyntaxKind.SuperKeyword ||
|
||||
(isPropertyAccessExpression(expr) || isNonNullExpression(expr) || isParenthesizedExpression(expr)) && isNarrowableReference(expr.expression) ||
|
||||
isElementAccessExpression(expr) &&
|
||||
isStringOrNumericLiteralLike(expr.argumentExpression) &&
|
||||
isNarrowableReference(expr.expression);
|
||||
isElementAccessExpression(expr) && isStringOrNumericLiteralLike(expr.argumentExpression) && isNarrowableReference(expr.expression) ||
|
||||
isOptionalChain(expr);
|
||||
}
|
||||
|
||||
function hasNarrowableArgument(expr: CallExpression) {
|
||||
@@ -920,11 +925,11 @@ namespace ts {
|
||||
}
|
||||
|
||||
function createBranchLabel(): FlowLabel {
|
||||
return { flags: FlowFlags.BranchLabel, antecedents: undefined };
|
||||
return initFlowNode({ flags: FlowFlags.BranchLabel, antecedents: undefined });
|
||||
}
|
||||
|
||||
function createLoopLabel(): FlowLabel {
|
||||
return { flags: FlowFlags.LoopLabel, antecedents: undefined };
|
||||
return initFlowNode({ flags: FlowFlags.LoopLabel, antecedents: undefined });
|
||||
}
|
||||
|
||||
function setFlowNodeReferenced(flow: FlowNode) {
|
||||
@@ -948,7 +953,7 @@ namespace ts {
|
||||
}
|
||||
if (expression.kind === SyntaxKind.TrueKeyword && flags & FlowFlags.FalseCondition ||
|
||||
expression.kind === SyntaxKind.FalseKeyword && flags & FlowFlags.TrueCondition) {
|
||||
if (!isOptionalChainRoot(expression.parent)) {
|
||||
if (!isExpressionOfOptionalChainRoot(expression)) {
|
||||
return unreachableFlow;
|
||||
}
|
||||
}
|
||||
@@ -1194,7 +1199,7 @@ namespace ts {
|
||||
// as possible antecedents of the start of the `catch` or `finally` blocks.
|
||||
// Don't bother intercepting the call if there's no finally or catch block that needs the information
|
||||
if (node.catchClause || node.finallyBlock) {
|
||||
flowNodeCreated = node => (tryPriors.push(node), node);
|
||||
flowNodeCreated = node => (tryPriors.push(node), initFlowNode(node));
|
||||
}
|
||||
bind(node.tryBlock);
|
||||
flowNodeCreated = oldFlowNodeCreated;
|
||||
@@ -1263,7 +1268,7 @@ namespace ts {
|
||||
//
|
||||
// extra edges that we inject allows to control this behavior
|
||||
// if when walking the flow we step on post-finally edge - we can mark matching pre-finally edge as locked so it will be skipped.
|
||||
const preFinallyFlow: PreFinallyFlow = { flags: FlowFlags.PreFinally, antecedent: preFinallyPrior, lock: {} };
|
||||
const preFinallyFlow: PreFinallyFlow = initFlowNode({ flags: FlowFlags.PreFinally, antecedent: preFinallyPrior, lock: {} });
|
||||
addAntecedent(preFinallyLabel, preFinallyFlow);
|
||||
|
||||
currentFlow = finishFlowLabel(preFinallyLabel);
|
||||
@@ -1333,7 +1338,7 @@ namespace ts {
|
||||
bind(clause);
|
||||
fallthroughFlow = currentFlow;
|
||||
if (!(currentFlow.flags & FlowFlags.Unreachable) && i !== clauses.length - 1 && options.noFallthroughCasesInSwitch) {
|
||||
errorOnFirstToken(clause, Diagnostics.Fallthrough_case_in_switch);
|
||||
clause.fallthroughFlowNode = currentFlow;
|
||||
}
|
||||
}
|
||||
clauses.transformFlags = subtreeTransformFlags | TransformFlags.HasComputedFlags;
|
||||
@@ -1984,7 +1989,7 @@ namespace ts {
|
||||
const host = getJSDocHost(typeAlias);
|
||||
container = findAncestor(host.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer)) || file;
|
||||
blockScopeContainer = getEnclosingBlockScopeContainer(host) || file;
|
||||
currentFlow = { flags: FlowFlags.Start };
|
||||
currentFlow = initFlowNode({ flags: FlowFlags.Start });
|
||||
parent = typeAlias;
|
||||
bind(typeAlias.typeExpression);
|
||||
const declName = getNameOfDeclaration(typeAlias);
|
||||
@@ -2446,6 +2451,8 @@ namespace ts {
|
||||
case SyntaxKind.JSDocTypeLiteral:
|
||||
case SyntaxKind.MappedType:
|
||||
return bindAnonymousTypeWorker(node as TypeLiteralNode | MappedTypeNode | JSDocTypeLiteral);
|
||||
case SyntaxKind.JSDocClassTag:
|
||||
return bindJSDocClassTag(node as JSDocClassTag);
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
return bindObjectLiteralExpression(<ObjectLiteralExpression>node);
|
||||
case SyntaxKind.FunctionExpression:
|
||||
@@ -2683,7 +2690,8 @@ namespace ts {
|
||||
const flags = exportAssignmentIsAlias(node)
|
||||
? SymbolFlags.Alias // An export= with an EntityNameExpression or a ClassExpression exports all meanings of that identifier or class
|
||||
: SymbolFlags.Property | SymbolFlags.ExportValue | SymbolFlags.ValueModule;
|
||||
declareSymbol(file.symbol.exports!, file.symbol, node, flags | SymbolFlags.Assignment, SymbolFlags.None);
|
||||
const symbol = declareSymbol(file.symbol.exports!, file.symbol, node, flags | SymbolFlags.Assignment, SymbolFlags.None);
|
||||
setValueDeclaration(symbol, node);
|
||||
}
|
||||
|
||||
function bindThisPropertyAssignment(node: BindablePropertyAssignmentExpression | PropertyAccessExpression | LiteralLikeElementAccessExpression) {
|
||||
@@ -2781,6 +2789,10 @@ namespace ts {
|
||||
|
||||
function bindObjectDefinePrototypeProperty(node: BindableObjectDefinePropertyCall) {
|
||||
const namespaceSymbol = lookupSymbolForPropertyAccess((node.arguments[0] as PropertyAccessExpression).expression as EntityNameExpression);
|
||||
if (namespaceSymbol) {
|
||||
// Ensure the namespace symbol becomes class-like
|
||||
addDeclarationToSymbol(namespaceSymbol, namespaceSymbol.valueDeclaration, SymbolFlags.Class);
|
||||
}
|
||||
bindPotentiallyNewExpandoMemberToNamespace(node, namespaceSymbol, /*isPrototypeProperty*/ true);
|
||||
}
|
||||
|
||||
@@ -2862,7 +2874,7 @@ namespace ts {
|
||||
}
|
||||
});
|
||||
}
|
||||
if (containerIsClass && namespaceSymbol) {
|
||||
if (containerIsClass && namespaceSymbol && namespaceSymbol.valueDeclaration) {
|
||||
addDeclarationToSymbol(namespaceSymbol, namespaceSymbol.valueDeclaration, SymbolFlags.Class);
|
||||
}
|
||||
return namespaceSymbol;
|
||||
|
||||
+2
-35
@@ -251,6 +251,7 @@ namespace ts {
|
||||
state.seenAffectedFiles = createMap<true>();
|
||||
}
|
||||
|
||||
state.emittedBuildInfo = !state.changedFilesSet.size && !state.affectedFilesPendingEmit;
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -1118,7 +1119,7 @@ namespace ts {
|
||||
|
||||
const state: ReusableBuilderProgramState = {
|
||||
fileInfos,
|
||||
compilerOptions: convertFromReusableCompilerOptions(program.options, toAbsolutePath),
|
||||
compilerOptions: convertToOptionsWithAbsolutePaths(program.options, toAbsolutePath),
|
||||
referencedMap: getMapOfReferencedSet(program.referencedMap, toPath),
|
||||
exportedModulesMap: getMapOfReferencedSet(program.exportedModulesMap, toPath),
|
||||
semanticDiagnosticsPerFile: program.semanticDiagnosticsPerFile && arrayToMap(program.semanticDiagnosticsPerFile, value => toPath(isString(value) ? value : value[0]), value => isString(value) ? emptyArray : value[1]),
|
||||
@@ -1156,40 +1157,6 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function convertFromReusableCompilerOptions(options: CompilerOptions, toAbsolutePath: (path: string) => string) {
|
||||
const result: CompilerOptions = {};
|
||||
const optionsNameMap = getOptionNameMap().optionNameMap;
|
||||
|
||||
for (const name in options) {
|
||||
if (hasProperty(options, name)) {
|
||||
result[name] = convertFromReusableCompilerOptionValue(
|
||||
optionsNameMap.get(name.toLowerCase()),
|
||||
options[name] as CompilerOptionsValue,
|
||||
toAbsolutePath
|
||||
);
|
||||
}
|
||||
}
|
||||
if (result.configFilePath) {
|
||||
result.configFilePath = toAbsolutePath(result.configFilePath);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function convertFromReusableCompilerOptionValue(option: CommandLineOption | undefined, value: CompilerOptionsValue, toAbsolutePath: (path: string) => string) {
|
||||
if (option) {
|
||||
if (option.type === "list") {
|
||||
const values = value as readonly (string | number)[];
|
||||
if (option.element.isFilePath && values.length) {
|
||||
return values.map(toAbsolutePath);
|
||||
}
|
||||
}
|
||||
else if (option.isFilePath) {
|
||||
return toAbsolutePath(value as string);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export function createRedirectedBuilderProgram(state: { program: Program | undefined; compilerOptions: CompilerOptions; }, configFileParsingDiagnostics: readonly Diagnostic[]): BuilderProgram {
|
||||
return {
|
||||
getState: notImplemented,
|
||||
|
||||
+396
-253
File diff suppressed because it is too large
Load Diff
@@ -216,6 +216,15 @@ namespace ts {
|
||||
isCommandLineOnly: true,
|
||||
description: Diagnostics.Print_the_final_configuration_instead_of_building
|
||||
},
|
||||
{
|
||||
name: "listFilesOnly",
|
||||
type: "boolean",
|
||||
category: Diagnostics.Command_line_Options,
|
||||
affectsSemanticDiagnostics: true,
|
||||
affectsEmit: true,
|
||||
isCommandLineOnly: true,
|
||||
description: Diagnostics.Print_names_of_files_that_are_part_of_the_compilation_and_then_stop_processing
|
||||
},
|
||||
|
||||
// Basic
|
||||
{
|
||||
@@ -1227,7 +1236,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
export function parseBuildCommand(args: string[]): ParsedBuildCommand {
|
||||
export function parseBuildCommand(args: readonly string[]): ParsedBuildCommand {
|
||||
let buildOptionNameMap: OptionNameMap | undefined;
|
||||
const returnBuildOptionNameMap = () => (buildOptionNameMap || (buildOptionNameMap = createOptionNameMap(buildOpts)));
|
||||
const { options, fileNames: projects, errors } = parseCommandLineWorker(returnBuildOptionNameMap, [
|
||||
@@ -1258,125 +1267,12 @@ namespace ts {
|
||||
return { buildOptions, projects, errors };
|
||||
}
|
||||
|
||||
function getDiagnosticText(_message: DiagnosticMessage, ..._args: any[]): string {
|
||||
/* @internal */
|
||||
export function getDiagnosticText(_message: DiagnosticMessage, ..._args: any[]): string {
|
||||
const diagnostic = createCompilerDiagnostic.apply(undefined, arguments);
|
||||
return <string>diagnostic.messageText;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function printVersion() {
|
||||
sys.write(getDiagnosticText(Diagnostics.Version_0, version) + sys.newLine);
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function printHelp(optionsList: readonly CommandLineOption[], syntaxPrefix = "") {
|
||||
const output: string[] = [];
|
||||
|
||||
// We want to align our "syntax" and "examples" commands to a certain margin.
|
||||
const syntaxLength = getDiagnosticText(Diagnostics.Syntax_Colon_0, "").length;
|
||||
const examplesLength = getDiagnosticText(Diagnostics.Examples_Colon_0, "").length;
|
||||
let marginLength = Math.max(syntaxLength, examplesLength);
|
||||
|
||||
// Build up the syntactic skeleton.
|
||||
let syntax = makePadding(marginLength - syntaxLength);
|
||||
syntax += `tsc ${syntaxPrefix}[${getDiagnosticText(Diagnostics.options)}] [${getDiagnosticText(Diagnostics.file)}...]`;
|
||||
|
||||
output.push(getDiagnosticText(Diagnostics.Syntax_Colon_0, syntax));
|
||||
output.push(sys.newLine + sys.newLine);
|
||||
|
||||
// Build up the list of examples.
|
||||
const padding = makePadding(marginLength);
|
||||
output.push(getDiagnosticText(Diagnostics.Examples_Colon_0, makePadding(marginLength - examplesLength) + "tsc hello.ts") + sys.newLine);
|
||||
output.push(padding + "tsc --outFile file.js file.ts" + sys.newLine);
|
||||
output.push(padding + "tsc @args.txt" + sys.newLine);
|
||||
output.push(padding + "tsc --build tsconfig.json" + sys.newLine);
|
||||
output.push(sys.newLine);
|
||||
|
||||
output.push(getDiagnosticText(Diagnostics.Options_Colon) + sys.newLine);
|
||||
|
||||
// We want our descriptions to align at the same column in our output,
|
||||
// so we keep track of the longest option usage string.
|
||||
marginLength = 0;
|
||||
const usageColumn: string[] = []; // Things like "-d, --declaration" go in here.
|
||||
const descriptionColumn: string[] = [];
|
||||
|
||||
const optionsDescriptionMap = createMap<string[]>(); // Map between option.description and list of option.type if it is a kind
|
||||
|
||||
for (const option of optionsList) {
|
||||
// If an option lacks a description,
|
||||
// it is not officially supported.
|
||||
if (!option.description) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let usageText = " ";
|
||||
if (option.shortName) {
|
||||
usageText += "-" + option.shortName;
|
||||
usageText += getParamType(option);
|
||||
usageText += ", ";
|
||||
}
|
||||
|
||||
usageText += "--" + option.name;
|
||||
usageText += getParamType(option);
|
||||
|
||||
usageColumn.push(usageText);
|
||||
let description: string;
|
||||
|
||||
if (option.name === "lib") {
|
||||
description = getDiagnosticText(option.description);
|
||||
const element = (<CommandLineOptionOfListType>option).element;
|
||||
const typeMap = <Map<number | string>>element.type;
|
||||
optionsDescriptionMap.set(description, arrayFrom(typeMap.keys()).map(key => `'${key}'`));
|
||||
}
|
||||
else {
|
||||
description = getDiagnosticText(option.description);
|
||||
}
|
||||
|
||||
descriptionColumn.push(description);
|
||||
|
||||
// Set the new margin for the description column if necessary.
|
||||
marginLength = Math.max(usageText.length, marginLength);
|
||||
}
|
||||
|
||||
// Special case that can't fit in the loop.
|
||||
const usageText = " @<" + getDiagnosticText(Diagnostics.file) + ">";
|
||||
usageColumn.push(usageText);
|
||||
descriptionColumn.push(getDiagnosticText(Diagnostics.Insert_command_line_options_and_files_from_a_file));
|
||||
marginLength = Math.max(usageText.length, marginLength);
|
||||
|
||||
// Print out each row, aligning all the descriptions on the same column.
|
||||
for (let i = 0; i < usageColumn.length; i++) {
|
||||
const usage = usageColumn[i];
|
||||
const description = descriptionColumn[i];
|
||||
const kindsList = optionsDescriptionMap.get(description);
|
||||
output.push(usage + makePadding(marginLength - usage.length + 2) + description + sys.newLine);
|
||||
|
||||
if (kindsList) {
|
||||
output.push(makePadding(marginLength + 4));
|
||||
for (const kind of kindsList) {
|
||||
output.push(kind + " ");
|
||||
}
|
||||
output.push(sys.newLine);
|
||||
}
|
||||
}
|
||||
|
||||
for (const line of output) {
|
||||
sys.write(line);
|
||||
}
|
||||
return;
|
||||
|
||||
function getParamType(option: CommandLineOption) {
|
||||
if (option.paramType !== undefined) {
|
||||
return " " + getDiagnosticText(option.paramType);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function makePadding(paddingLength: number): string {
|
||||
return Array(paddingLength + 1).join(" ");
|
||||
}
|
||||
}
|
||||
|
||||
export type DiagnosticReporter = (diagnostic: Diagnostic) => void;
|
||||
/**
|
||||
* Reports config file diagnostics
|
||||
@@ -1801,6 +1697,12 @@ namespace ts {
|
||||
references: readonly ProjectReference[] | undefined;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export interface ConvertToTSConfigHost {
|
||||
getCurrentDirectory(): string;
|
||||
useCaseSensitiveFileNames: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an uncommented, complete tsconfig for use with "--showConfig"
|
||||
* @param configParseResult options to be generated into tsconfig.json
|
||||
@@ -1808,7 +1710,7 @@ namespace ts {
|
||||
* @param host provides current directory and case sensitivity services
|
||||
*/
|
||||
/** @internal */
|
||||
export function convertToTSConfig(configParseResult: ParsedCommandLine, configFileName: string, host: { getCurrentDirectory(): string, useCaseSensitiveFileNames: boolean }): TSConfig {
|
||||
export function convertToTSConfig(configParseResult: ParsedCommandLine, configFileName: string, host: ConvertToTSConfigHost): TSConfig {
|
||||
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames);
|
||||
const files = map(
|
||||
filter(
|
||||
@@ -1816,7 +1718,8 @@ namespace ts {
|
||||
(!configParseResult.configFileSpecs || !configParseResult.configFileSpecs.validatedIncludeSpecs) ? _ => true : matchesSpecs(
|
||||
configFileName,
|
||||
configParseResult.configFileSpecs.validatedIncludeSpecs,
|
||||
configParseResult.configFileSpecs.validatedExcludeSpecs
|
||||
configParseResult.configFileSpecs.validatedExcludeSpecs,
|
||||
host,
|
||||
)
|
||||
),
|
||||
f => getRelativePathFromFile(getNormalizedAbsolutePath(configFileName, host.getCurrentDirectory()), getNormalizedAbsolutePath(f, host.getCurrentDirectory()), getCanonicalFileName)
|
||||
@@ -1854,11 +1757,11 @@ namespace ts {
|
||||
return specs;
|
||||
}
|
||||
|
||||
function matchesSpecs(path: string, includeSpecs: readonly string[] | undefined, excludeSpecs: readonly string[] | undefined): (path: string) => boolean {
|
||||
function matchesSpecs(path: string, includeSpecs: readonly string[] | undefined, excludeSpecs: readonly string[] | undefined, host: ConvertToTSConfigHost): (path: string) => boolean {
|
||||
if (!includeSpecs) return _ => true;
|
||||
const patterns = getFileMatcherPatterns(path, excludeSpecs, includeSpecs, sys.useCaseSensitiveFileNames, sys.getCurrentDirectory());
|
||||
const excludeRe = patterns.excludePattern && getRegexFromPattern(patterns.excludePattern, sys.useCaseSensitiveFileNames);
|
||||
const includeRe = patterns.includeFilePattern && getRegexFromPattern(patterns.includeFilePattern, sys.useCaseSensitiveFileNames);
|
||||
const patterns = getFileMatcherPatterns(path, excludeSpecs, includeSpecs, host.useCaseSensitiveFileNames, host.getCurrentDirectory());
|
||||
const excludeRe = patterns.excludePattern && getRegexFromPattern(patterns.excludePattern, host.useCaseSensitiveFileNames);
|
||||
const includeRe = patterns.includeFilePattern && getRegexFromPattern(patterns.includeFilePattern, host.useCaseSensitiveFileNames);
|
||||
if (includeRe) {
|
||||
if (excludeRe) {
|
||||
return path => !(includeRe.test(path) && !excludeRe.test(path));
|
||||
@@ -2041,6 +1944,41 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function convertToOptionsWithAbsolutePaths(options: CompilerOptions, toAbsolutePath: (path: string) => string) {
|
||||
const result: CompilerOptions = {};
|
||||
const optionsNameMap = getOptionNameMap().optionNameMap;
|
||||
|
||||
for (const name in options) {
|
||||
if (hasProperty(options, name)) {
|
||||
result[name] = convertToOptionValueWithAbsolutePaths(
|
||||
optionsNameMap.get(name.toLowerCase()),
|
||||
options[name] as CompilerOptionsValue,
|
||||
toAbsolutePath
|
||||
);
|
||||
}
|
||||
}
|
||||
if (result.configFilePath) {
|
||||
result.configFilePath = toAbsolutePath(result.configFilePath);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function convertToOptionValueWithAbsolutePaths(option: CommandLineOption | undefined, value: CompilerOptionsValue, toAbsolutePath: (path: string) => string) {
|
||||
if (option) {
|
||||
if (option.type === "list") {
|
||||
const values = value as readonly (string | number)[];
|
||||
if (option.element.isFilePath && values.length) {
|
||||
return values.map(toAbsolutePath);
|
||||
}
|
||||
}
|
||||
else if (option.isFilePath) {
|
||||
return toAbsolutePath(value as string);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the contents of a config file (tsconfig.json).
|
||||
* @param json The contents of the config file to parse
|
||||
@@ -2604,7 +2542,7 @@ namespace ts {
|
||||
|
||||
function normalizeNonListOptionValue(option: CommandLineOption, basePath: string, value: any): CompilerOptionsValue {
|
||||
if (option.isFilePath) {
|
||||
value = normalizePath(combinePaths(basePath, value));
|
||||
value = getNormalizedAbsolutePath(value, basePath);
|
||||
if (value === "") {
|
||||
value = ".";
|
||||
}
|
||||
|
||||
+34
-227
@@ -42,6 +42,12 @@ namespace ts {
|
||||
clear(): void;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export interface MapConstructor {
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-function-type
|
||||
new <T>(): Map<T>;
|
||||
}
|
||||
|
||||
/** ES6 Iterator type. */
|
||||
export interface Iterator<T> {
|
||||
next(): { value: T, done?: false } | { value: never, done: true };
|
||||
@@ -66,28 +72,40 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
// Natives
|
||||
// NOTE: This must be declared in a separate block from the one below so that we don't collide with the exported definition of `Map`.
|
||||
declare const Map: (new <T>() => Map<T>) | undefined;
|
||||
|
||||
/**
|
||||
* Returns the native Map implementation if it is available and compatible (i.e. supports iteration).
|
||||
*/
|
||||
export function tryGetNativeMap(): MapConstructor | undefined {
|
||||
// Internet Explorer's Map doesn't support iteration, so don't use it.
|
||||
// eslint-disable-next-line no-in-operator
|
||||
return typeof Map !== "undefined" && "entries" in Map.prototype ? Map : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
export const emptyArray: never[] = [] as never[];
|
||||
|
||||
/** Create a MapLike with good performance. */
|
||||
function createDictionaryObject<T>(): MapLike<T> {
|
||||
const map = Object.create(/*prototype*/ null); // eslint-disable-line no-null/no-null
|
||||
export const Map: MapConstructor = tryGetNativeMap() || (() => {
|
||||
// NOTE: createMapShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it.
|
||||
if (typeof createMapShim === "function") {
|
||||
return createMapShim();
|
||||
}
|
||||
throw new Error("TypeScript requires an environment that provides a compatible native Map implementation.");
|
||||
})();
|
||||
|
||||
// Using 'delete' on an object causes V8 to put the object in dictionary mode.
|
||||
// This disables creation of hidden classes, which are expensive when an object is
|
||||
// constantly changing shape.
|
||||
map.__ = undefined;
|
||||
delete map.__;
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/** Create a new map. If a template object is provided, the map will copy entries from it. */
|
||||
/** Create a new map. */
|
||||
export function createMap<T>(): Map<T> {
|
||||
return new MapCtr<T>();
|
||||
return new Map<T>();
|
||||
}
|
||||
|
||||
/** Create a new map from an array of entries. */
|
||||
export function createMapFromEntries<T>(entries: [string, T][]): Map<T> {
|
||||
const map = createMap<T>();
|
||||
for (const [key, value] of entries) {
|
||||
@@ -96,8 +114,9 @@ namespace ts {
|
||||
return map;
|
||||
}
|
||||
|
||||
/** Create a new map from a template object is provided, the map will copy entries from it. */
|
||||
export function createMapFromTemplate<T>(template: MapLike<T>): Map<T> {
|
||||
const map: Map<T> = new MapCtr<T>();
|
||||
const map: Map<T> = new Map<T>();
|
||||
|
||||
// Copies keys/values from template. Note that for..in will not throw if
|
||||
// template is undefined, and instead will just exit the loop.
|
||||
@@ -110,204 +129,6 @@ namespace ts {
|
||||
return map;
|
||||
}
|
||||
|
||||
// The global Map object. This may not be available, so we must test for it.
|
||||
declare const Map: (new <T>() => Map<T>) | undefined;
|
||||
// Internet Explorer's Map doesn't support iteration, so don't use it.
|
||||
// eslint-disable-next-line no-in-operator
|
||||
export const MapCtr = typeof Map !== "undefined" && "entries" in Map.prototype ? Map : shimMap();
|
||||
|
||||
// Keep the class inside a function so it doesn't get compiled if it's not used.
|
||||
export function shimMap(): new <T>() => Map<T> {
|
||||
|
||||
interface MapEntry<T> {
|
||||
readonly key?: string;
|
||||
value?: T;
|
||||
|
||||
// Linked list references for iterators.
|
||||
nextEntry?: MapEntry<T>;
|
||||
previousEntry?: MapEntry<T>;
|
||||
|
||||
/**
|
||||
* Specifies if iterators should skip the next entry.
|
||||
* This will be set when an entry is deleted.
|
||||
* See https://github.com/Microsoft/TypeScript/pull/27292 for more information.
|
||||
*/
|
||||
skipNext?: boolean;
|
||||
}
|
||||
|
||||
class MapIterator<T, U extends (string | T | [string, T])> {
|
||||
private currentEntry?: MapEntry<T>;
|
||||
private selector: (key: string, value: T) => U;
|
||||
|
||||
constructor(currentEntry: MapEntry<T>, selector: (key: string, value: T) => U) {
|
||||
this.currentEntry = currentEntry;
|
||||
this.selector = selector;
|
||||
}
|
||||
|
||||
public next(): { value: U, done: false } | { value: never, done: true } {
|
||||
// Navigate to the next entry.
|
||||
while (this.currentEntry) {
|
||||
const skipNext = !!this.currentEntry.skipNext;
|
||||
this.currentEntry = this.currentEntry.nextEntry;
|
||||
|
||||
if (!skipNext) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.currentEntry) {
|
||||
return { value: this.selector(this.currentEntry.key!, this.currentEntry.value!), done: false };
|
||||
}
|
||||
else {
|
||||
return { value: undefined as never, done: true };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return class <T> implements Map<T> {
|
||||
private data = createDictionaryObject<MapEntry<T>>();
|
||||
public size = 0;
|
||||
|
||||
// Linked list references for iterators.
|
||||
// See https://github.com/Microsoft/TypeScript/pull/27292
|
||||
// for more information.
|
||||
|
||||
/**
|
||||
* The first entry in the linked list.
|
||||
* Note that this is only a stub that serves as starting point
|
||||
* for iterators and doesn't contain a key and a value.
|
||||
*/
|
||||
private readonly firstEntry: MapEntry<T>;
|
||||
private lastEntry: MapEntry<T>;
|
||||
|
||||
constructor() {
|
||||
// Create a first (stub) map entry that will not contain a key
|
||||
// and value but serves as starting point for iterators.
|
||||
this.firstEntry = {};
|
||||
// When the map is empty, the last entry is the same as the
|
||||
// first one.
|
||||
this.lastEntry = this.firstEntry;
|
||||
}
|
||||
|
||||
get(key: string): T | undefined {
|
||||
const entry = this.data[key] as MapEntry<T> | undefined;
|
||||
return entry && entry.value!;
|
||||
}
|
||||
|
||||
set(key: string, value: T): this {
|
||||
if (!this.has(key)) {
|
||||
this.size++;
|
||||
|
||||
// Create a new entry that will be appended at the
|
||||
// end of the linked list.
|
||||
const newEntry: MapEntry<T> = {
|
||||
key,
|
||||
value
|
||||
};
|
||||
this.data[key] = newEntry;
|
||||
|
||||
// Adjust the references.
|
||||
const previousLastEntry = this.lastEntry;
|
||||
previousLastEntry.nextEntry = newEntry;
|
||||
newEntry.previousEntry = previousLastEntry;
|
||||
this.lastEntry = newEntry;
|
||||
}
|
||||
else {
|
||||
this.data[key].value = value;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
has(key: string): boolean {
|
||||
// eslint-disable-next-line no-in-operator
|
||||
return key in this.data;
|
||||
}
|
||||
|
||||
delete(key: string): boolean {
|
||||
if (this.has(key)) {
|
||||
this.size--;
|
||||
const entry = this.data[key];
|
||||
delete this.data[key];
|
||||
|
||||
// Adjust the linked list references of the neighbor entries.
|
||||
const previousEntry = entry.previousEntry!;
|
||||
previousEntry.nextEntry = entry.nextEntry;
|
||||
if (entry.nextEntry) {
|
||||
entry.nextEntry.previousEntry = previousEntry;
|
||||
}
|
||||
|
||||
// When the deleted entry was the last one, we need to
|
||||
// adjust the lastEntry reference.
|
||||
if (this.lastEntry === entry) {
|
||||
this.lastEntry = previousEntry;
|
||||
}
|
||||
|
||||
// Adjust the forward reference of the deleted entry
|
||||
// in case an iterator still references it. This allows us
|
||||
// to throw away the entry, but when an active iterator
|
||||
// (which points to the current entry) continues, it will
|
||||
// navigate to the entry that originally came before the
|
||||
// current one and skip it.
|
||||
entry.previousEntry = undefined;
|
||||
entry.nextEntry = previousEntry;
|
||||
entry.skipNext = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.data = createDictionaryObject<MapEntry<T>>();
|
||||
this.size = 0;
|
||||
|
||||
// Reset the linked list. Note that we must adjust the forward
|
||||
// references of the deleted entries to ensure iterators stuck
|
||||
// in the middle of the list don't continue with deleted entries,
|
||||
// but can continue with new entries added after the clear()
|
||||
// operation.
|
||||
const firstEntry = this.firstEntry;
|
||||
let currentEntry = firstEntry.nextEntry;
|
||||
while (currentEntry) {
|
||||
const nextEntry = currentEntry.nextEntry;
|
||||
currentEntry.previousEntry = undefined;
|
||||
currentEntry.nextEntry = firstEntry;
|
||||
currentEntry.skipNext = true;
|
||||
|
||||
currentEntry = nextEntry;
|
||||
}
|
||||
firstEntry.nextEntry = undefined;
|
||||
this.lastEntry = firstEntry;
|
||||
}
|
||||
|
||||
keys(): Iterator<string> {
|
||||
return new MapIterator(this.firstEntry, key => key);
|
||||
}
|
||||
|
||||
values(): Iterator<T> {
|
||||
return new MapIterator(this.firstEntry, (_key, value) => value);
|
||||
}
|
||||
|
||||
entries(): Iterator<[string, T]> {
|
||||
return new MapIterator(this.firstEntry, (key, value) => [key, value] as [string, T]);
|
||||
}
|
||||
|
||||
forEach(action: (value: T, key: string) => void): void {
|
||||
const iterator = this.entries();
|
||||
while (true) {
|
||||
const iterResult = iterator.next();
|
||||
if (iterResult.done) {
|
||||
break;
|
||||
}
|
||||
|
||||
const [key, value] = iterResult.value;
|
||||
action(value, key);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function length(array: readonly any[] | undefined): number {
|
||||
return array ? array.length : 0;
|
||||
}
|
||||
@@ -2052,20 +1873,6 @@ namespace ts {
|
||||
return str.indexOf(substring) !== -1;
|
||||
}
|
||||
|
||||
export function fileExtensionIs(path: string, extension: string): boolean {
|
||||
return path.length > extension.length && endsWith(path, extension);
|
||||
}
|
||||
|
||||
export function fileExtensionIsOneOf(path: string, extensions: readonly string[]): boolean {
|
||||
for (const extension of extensions) {
|
||||
if (fileExtensionIs(path, extension)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a string like "jquery-min.4.2.3" and returns "jquery"
|
||||
*/
|
||||
|
||||
@@ -221,6 +221,40 @@ namespace ts {
|
||||
|
||||
let isDebugInfoEnabled = false;
|
||||
|
||||
interface ExtendedDebugModule {
|
||||
init(_ts: typeof ts): void;
|
||||
formatControlFlowGraph(flowNode: FlowNode): string;
|
||||
}
|
||||
|
||||
let extendedDebugModule: ExtendedDebugModule | undefined;
|
||||
|
||||
function extendedDebug() {
|
||||
enableDebugInfo();
|
||||
if (!extendedDebugModule) {
|
||||
throw new Error("Debugging helpers could not be loaded.");
|
||||
}
|
||||
return extendedDebugModule;
|
||||
}
|
||||
|
||||
export function printControlFlowGraph(flowNode: FlowNode) {
|
||||
return console.log(formatControlFlowGraph(flowNode));
|
||||
}
|
||||
|
||||
export function formatControlFlowGraph(flowNode: FlowNode) {
|
||||
return extendedDebug().formatControlFlowGraph(flowNode);
|
||||
}
|
||||
|
||||
export function attachFlowNodeDebugInfo(flowNode: FlowNode) {
|
||||
if (isDebugInfoEnabled) {
|
||||
if (!("__debugFlowFlags" in flowNode)) { // eslint-disable-line no-in-operator
|
||||
Object.defineProperties(flowNode, {
|
||||
__debugFlowFlags: { get(this: FlowNode) { return formatEnum(this.flags, (ts as any).FlowFlags, /*isFlags*/ true); } },
|
||||
__debugToString: { value(this: FlowNode) { return formatControlFlowGraph(this); } }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects debug information into frequently used types.
|
||||
*/
|
||||
@@ -266,6 +300,21 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
// attempt to load extended debugging information
|
||||
try {
|
||||
if (sys && sys.require) {
|
||||
const basePath = getDirectoryPath(resolvePath(sys.getExecutingFilePath()));
|
||||
const result = sys.require(basePath, "./compiler-debug") as RequireResult<ExtendedDebugModule>;
|
||||
if (!result.error) {
|
||||
result.module.init(ts);
|
||||
extendedDebugModule = result.module;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
isDebugInfoEnabled = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"category": "Error",
|
||||
"code": 1022
|
||||
},
|
||||
"An index signature parameter type must be 'string' or 'number'.": {
|
||||
"An index signature parameter type must be either 'string' or 'number'.": {
|
||||
"category": "Error",
|
||||
"code": 1023
|
||||
},
|
||||
@@ -903,7 +903,7 @@
|
||||
"category": "Error",
|
||||
"code": 1322
|
||||
},
|
||||
"Dynamic import is only supported when '--module' flag is 'commonjs' or 'esNext'.": {
|
||||
"Dynamic imports are only supported when the '--module' flag is set to 'esnext', 'commonjs', 'amd', 'system', or 'umd'.": {
|
||||
"category": "Error",
|
||||
"code": 1323
|
||||
},
|
||||
@@ -1043,6 +1043,10 @@
|
||||
"category": "Error",
|
||||
"code": 1358
|
||||
},
|
||||
"Identifier expected. '{0}' is a reserved word that cannot be used here.": {
|
||||
"category": "Error",
|
||||
"code": 1359
|
||||
},
|
||||
|
||||
"The types of '{0}' are incompatible between these types.": {
|
||||
"category": "Error",
|
||||
@@ -1557,10 +1561,6 @@
|
||||
"category": "Error",
|
||||
"code": 2423
|
||||
},
|
||||
"Class '{0}' defines instance member function '{1}', but extended class '{2}' defines it as instance member property.": {
|
||||
"category": "Error",
|
||||
"code": 2424
|
||||
},
|
||||
"Class '{0}' defines instance member property '{1}', but extended class '{2}' defines it as instance member function.": {
|
||||
"category": "Error",
|
||||
"code": 2425
|
||||
@@ -2249,6 +2249,14 @@
|
||||
"category": "Error",
|
||||
"code": 2612
|
||||
},
|
||||
"Module '{0}' has no default export. Did you mean to use 'import { {1} } from {0}' instead?": {
|
||||
"category": "Error",
|
||||
"code": 2613
|
||||
},
|
||||
"Module '{0}' has no exported member '{1}'. Did you mean to use 'import {1} from {0}' instead?": {
|
||||
"category": "Error",
|
||||
"code": 2614
|
||||
},
|
||||
|
||||
"Cannot augment module '{0}' with value exports because it resolves to a non-module entity.": {
|
||||
"category": "Error",
|
||||
@@ -4303,6 +4311,10 @@
|
||||
"category": "Message",
|
||||
"code": 6502
|
||||
},
|
||||
"Print names of files that are part of the compilation and then stop processing.": {
|
||||
"category": "Message",
|
||||
"code": 6503
|
||||
},
|
||||
|
||||
"Variable '{0}' implicitly has an '{1}' type.": {
|
||||
"category": "Error",
|
||||
|
||||
@@ -393,7 +393,9 @@ namespace ts {
|
||||
declarationFilePath: string | undefined,
|
||||
declarationMapPath: string | undefined,
|
||||
relativeToBuildInfo: (path: string) => string) {
|
||||
if (!sourceFileOrBundle || !declarationFilePath) {
|
||||
if (!sourceFileOrBundle) return;
|
||||
if (!declarationFilePath) {
|
||||
if (emitOnlyDtsFiles || compilerOptions.emitDeclarationOnly) emitSkipped = true;
|
||||
return;
|
||||
}
|
||||
const sourceFiles = isSourceFile(sourceFileOrBundle) ? [sourceFileOrBundle] : sourceFileOrBundle.sourceFiles;
|
||||
|
||||
+15
-6
@@ -1065,7 +1065,9 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function updatePropertyAccess(node: PropertyAccessExpression, expression: Expression, name: Identifier) {
|
||||
Debug.assert(!(node.flags & NodeFlags.OptionalChain), "Cannot update a PropertyAccessChain using updatePropertyAccess. Use updatePropertyAccessChain instead.");
|
||||
if (isOptionalChain(node)) {
|
||||
return updatePropertyAccessChain(node, expression, node.questionDotToken, name);
|
||||
}
|
||||
// Because we are updating existed propertyAccess we want to inherit its emitFlags
|
||||
// instead of using the default from createPropertyAccess
|
||||
return node.expression !== expression
|
||||
@@ -1103,7 +1105,9 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function updateElementAccess(node: ElementAccessExpression, expression: Expression, argumentExpression: Expression) {
|
||||
Debug.assert(!(node.flags & NodeFlags.OptionalChain), "Cannot update an ElementAccessChain using updateElementAccess. Use updateElementAccessChain instead.");
|
||||
if (isOptionalChain(node)) {
|
||||
return updateElementAccessChain(node, expression, node.questionDotToken, argumentExpression);
|
||||
}
|
||||
return node.expression !== expression
|
||||
|| node.argumentExpression !== argumentExpression
|
||||
? updateNode(createElementAccess(expression, argumentExpression), node)
|
||||
@@ -1137,7 +1141,9 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function updateCall(node: CallExpression, expression: Expression, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[]) {
|
||||
Debug.assert(!(node.flags & NodeFlags.OptionalChain), "Cannot update a CallChain using updateCall. Use updateCallChain instead.");
|
||||
if (isOptionalChain(node)) {
|
||||
return updateCallChain(node, expression, node.questionDotToken, typeArguments, argumentsArray);
|
||||
}
|
||||
return node.expression !== expression
|
||||
|| node.typeArguments !== typeArguments
|
||||
|| node.arguments !== argumentsArray
|
||||
@@ -1805,7 +1811,7 @@ namespace ts {
|
||||
const node = <ForOfStatement>createSynthesizedNode(SyntaxKind.ForOfStatement);
|
||||
node.awaitModifier = awaitModifier;
|
||||
node.initializer = initializer;
|
||||
node.expression = expression;
|
||||
node.expression = isCommaSequence(expression) ? createParen(expression) : expression;
|
||||
node.statement = asEmbeddedStatement(statement);
|
||||
return node;
|
||||
}
|
||||
@@ -2974,7 +2980,10 @@ namespace ts {
|
||||
(texts || (texts = [])).push(prependNode);
|
||||
break;
|
||||
case BundleFileSectionKind.Internal:
|
||||
if (stripInternal) break;
|
||||
if (stripInternal) {
|
||||
if (!texts) texts = [];
|
||||
break;
|
||||
}
|
||||
// falls through
|
||||
|
||||
case BundleFileSectionKind.Text:
|
||||
@@ -4730,7 +4739,7 @@ namespace ts {
|
||||
const conditionalPrecedence = getOperatorPrecedence(SyntaxKind.ConditionalExpression, SyntaxKind.QuestionToken);
|
||||
const emittedCondition = skipPartiallyEmittedExpressions(condition);
|
||||
const conditionPrecedence = getExpressionPrecedence(emittedCondition);
|
||||
if (compareValues(conditionPrecedence, conditionalPrecedence) === Comparison.LessThan) {
|
||||
if (compareValues(conditionPrecedence, conditionalPrecedence) !== Comparison.GreaterThan) {
|
||||
return createParen(condition);
|
||||
}
|
||||
return condition;
|
||||
|
||||
@@ -317,9 +317,13 @@ namespace ts.moduleSpecifiers {
|
||||
|
||||
// If the module could be imported by a directory name, use that directory's name
|
||||
const moduleSpecifier = packageNameOnly ? moduleFileName : getDirectoryOrExtensionlessFileName(moduleFileName);
|
||||
const globalTypingsCacheLocation = host.getGlobalTypingsCacheLocation && host.getGlobalTypingsCacheLocation();
|
||||
// Get a path that's relative to node_modules or the importing file's path
|
||||
// if node_modules folder is in this folder or any of its parent folders, no need to keep it.
|
||||
if (!startsWith(sourceDirectory, getCanonicalFileName(moduleSpecifier.substring(0, parts.topLevelNodeModulesIndex)))) return undefined;
|
||||
const pathToTopLevelNodeModules = getCanonicalFileName(moduleSpecifier.substring(0, parts.topLevelNodeModulesIndex));
|
||||
if (!(startsWith(sourceDirectory, pathToTopLevelNodeModules) || globalTypingsCacheLocation && startsWith(getCanonicalFileName(globalTypingsCacheLocation), pathToTopLevelNodeModules))) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// If the module was found in @types, get the actual Node package name
|
||||
const nodeModulesDirectoryName = moduleSpecifier.substring(parts.topLevelPackageNameIndex + 1);
|
||||
|
||||
@@ -1401,7 +1401,14 @@ namespace ts {
|
||||
// Only for end of file because the error gets reported incorrectly on embedded script tags.
|
||||
const reportAtCurrentPosition = token() === SyntaxKind.EndOfFileToken;
|
||||
|
||||
return createMissingNode<Identifier>(SyntaxKind.Identifier, reportAtCurrentPosition, diagnosticMessage || Diagnostics.Identifier_expected);
|
||||
const isReservedWord = scanner.isReservedWord();
|
||||
const msgArg = scanner.getTokenText();
|
||||
|
||||
const defaultMessage = isReservedWord ?
|
||||
Diagnostics.Identifier_expected_0_is_a_reserved_word_that_cannot_be_used_here :
|
||||
Diagnostics.Identifier_expected;
|
||||
|
||||
return createMissingNode<Identifier>(SyntaxKind.Identifier, reportAtCurrentPosition, diagnosticMessage || defaultMessage, msgArg);
|
||||
}
|
||||
|
||||
function parseIdentifier(diagnosticMessage?: DiagnosticMessage): Identifier {
|
||||
|
||||
@@ -0,0 +1,855 @@
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
/**
|
||||
* Internally, we represent paths as strings with '/' as the directory separator.
|
||||
* When we make system calls (eg: LanguageServiceHost.getDirectory()),
|
||||
* we expect the host to correctly handle paths in our specified format.
|
||||
*/
|
||||
export const directorySeparator = "/";
|
||||
const altDirectorySeparator = "\\";
|
||||
const urlSchemeSeparator = "://";
|
||||
const backslashRegExp = /\\/g;
|
||||
|
||||
//// Path Tests
|
||||
|
||||
/**
|
||||
* Determines whether a charCode corresponds to `/` or `\`.
|
||||
*/
|
||||
export function isAnyDirectorySeparator(charCode: number): boolean {
|
||||
return charCode === CharacterCodes.slash || charCode === CharacterCodes.backslash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a path starts with a URL scheme (e.g. starts with `http://`, `ftp://`, `file://`, etc.).
|
||||
*/
|
||||
export function isUrl(path: string) {
|
||||
return getEncodedRootLength(path) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a path is an absolute disk path (e.g. starts with `/`, or a dos path
|
||||
* like `c:`, `c:\` or `c:/`).
|
||||
*/
|
||||
export function isRootedDiskPath(path: string) {
|
||||
return getEncodedRootLength(path) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a path consists only of a path root.
|
||||
*/
|
||||
export function isDiskPathRoot(path: string) {
|
||||
const rootLength = getEncodedRootLength(path);
|
||||
return rootLength > 0 && rootLength === path.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a path starts with an absolute path component (i.e. `/`, `c:/`, `file://`, etc.).
|
||||
*
|
||||
* ```ts
|
||||
* // POSIX
|
||||
* pathIsAbsolute("/path/to/file.ext") === true
|
||||
* // DOS
|
||||
* pathIsAbsolute("c:/path/to/file.ext") === true
|
||||
* // URL
|
||||
* pathIsAbsolute("file:///path/to/file.ext") === true
|
||||
* // Non-absolute
|
||||
* pathIsAbsolute("path/to/file.ext") === false
|
||||
* pathIsAbsolute("./path/to/file.ext") === false
|
||||
* ```
|
||||
*/
|
||||
export function pathIsAbsolute(path: string): boolean {
|
||||
return getEncodedRootLength(path) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a path starts with a relative path component (i.e. `.` or `..`).
|
||||
*/
|
||||
export function pathIsRelative(path: string): boolean {
|
||||
return /^\.\.?($|[\\/])/.test(path);
|
||||
}
|
||||
|
||||
export function hasExtension(fileName: string): boolean {
|
||||
return stringContains(getBaseFileName(fileName), ".");
|
||||
}
|
||||
|
||||
export function fileExtensionIs(path: string, extension: string): boolean {
|
||||
return path.length > extension.length && endsWith(path, extension);
|
||||
}
|
||||
|
||||
export function fileExtensionIsOneOf(path: string, extensions: readonly string[]): boolean {
|
||||
for (const extension of extensions) {
|
||||
if (fileExtensionIs(path, extension)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a path has a trailing separator (`/` or `\\`).
|
||||
*/
|
||||
export function hasTrailingDirectorySeparator(path: string) {
|
||||
return path.length > 0 && isAnyDirectorySeparator(path.charCodeAt(path.length - 1));
|
||||
}
|
||||
|
||||
//// Path Parsing
|
||||
|
||||
function isVolumeCharacter(charCode: number) {
|
||||
return (charCode >= CharacterCodes.a && charCode <= CharacterCodes.z) ||
|
||||
(charCode >= CharacterCodes.A && charCode <= CharacterCodes.Z);
|
||||
}
|
||||
|
||||
function getFileUrlVolumeSeparatorEnd(url: string, start: number) {
|
||||
const ch0 = url.charCodeAt(start);
|
||||
if (ch0 === CharacterCodes.colon) return start + 1;
|
||||
if (ch0 === CharacterCodes.percent && url.charCodeAt(start + 1) === CharacterCodes._3) {
|
||||
const ch2 = url.charCodeAt(start + 2);
|
||||
if (ch2 === CharacterCodes.a || ch2 === CharacterCodes.A) return start + 3;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns length of the root part of a path or URL (i.e. length of "/", "x:/", "//server/share/, file:///user/files").
|
||||
* If the root is part of a URL, the twos-complement of the root length is returned.
|
||||
*/
|
||||
function getEncodedRootLength(path: string): number {
|
||||
if (!path) return 0;
|
||||
const ch0 = path.charCodeAt(0);
|
||||
|
||||
// POSIX or UNC
|
||||
if (ch0 === CharacterCodes.slash || ch0 === CharacterCodes.backslash) {
|
||||
if (path.charCodeAt(1) !== ch0) return 1; // POSIX: "/" (or non-normalized "\")
|
||||
|
||||
const p1 = path.indexOf(ch0 === CharacterCodes.slash ? directorySeparator : altDirectorySeparator, 2);
|
||||
if (p1 < 0) return path.length; // UNC: "//server" or "\\server"
|
||||
|
||||
return p1 + 1; // UNC: "//server/" or "\\server\"
|
||||
}
|
||||
|
||||
// DOS
|
||||
if (isVolumeCharacter(ch0) && path.charCodeAt(1) === CharacterCodes.colon) {
|
||||
const ch2 = path.charCodeAt(2);
|
||||
if (ch2 === CharacterCodes.slash || ch2 === CharacterCodes.backslash) return 3; // DOS: "c:/" or "c:\"
|
||||
if (path.length === 2) return 2; // DOS: "c:" (but not "c:d")
|
||||
}
|
||||
|
||||
// URL
|
||||
const schemeEnd = path.indexOf(urlSchemeSeparator);
|
||||
if (schemeEnd !== -1) {
|
||||
const authorityStart = schemeEnd + urlSchemeSeparator.length;
|
||||
const authorityEnd = path.indexOf(directorySeparator, authorityStart);
|
||||
if (authorityEnd !== -1) { // URL: "file:///", "file://server/", "file://server/path"
|
||||
// For local "file" URLs, include the leading DOS volume (if present).
|
||||
// Per https://www.ietf.org/rfc/rfc1738.txt, a host of "" or "localhost" is a
|
||||
// special case interpreted as "the machine from which the URL is being interpreted".
|
||||
const scheme = path.slice(0, schemeEnd);
|
||||
const authority = path.slice(authorityStart, authorityEnd);
|
||||
if (scheme === "file" && (authority === "" || authority === "localhost") &&
|
||||
isVolumeCharacter(path.charCodeAt(authorityEnd + 1))) {
|
||||
const volumeSeparatorEnd = getFileUrlVolumeSeparatorEnd(path, authorityEnd + 2);
|
||||
if (volumeSeparatorEnd !== -1) {
|
||||
if (path.charCodeAt(volumeSeparatorEnd) === CharacterCodes.slash) {
|
||||
// URL: "file:///c:/", "file://localhost/c:/", "file:///c%3a/", "file://localhost/c%3a/"
|
||||
return ~(volumeSeparatorEnd + 1);
|
||||
}
|
||||
if (volumeSeparatorEnd === path.length) {
|
||||
// URL: "file:///c:", "file://localhost/c:", "file:///c$3a", "file://localhost/c%3a"
|
||||
// but not "file:///c:d" or "file:///c%3ad"
|
||||
return ~volumeSeparatorEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ~(authorityEnd + 1); // URL: "file://server/", "http://server/"
|
||||
}
|
||||
return ~path.length; // URL: "file://server", "http://server"
|
||||
}
|
||||
|
||||
// relative
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns length of the root part of a path or URL (i.e. length of "/", "x:/", "//server/share/, file:///user/files").
|
||||
*
|
||||
* For example:
|
||||
* ```ts
|
||||
* getRootLength("a") === 0 // ""
|
||||
* getRootLength("/") === 1 // "/"
|
||||
* getRootLength("c:") === 2 // "c:"
|
||||
* getRootLength("c:d") === 0 // ""
|
||||
* getRootLength("c:/") === 3 // "c:/"
|
||||
* getRootLength("c:\\") === 3 // "c:\\"
|
||||
* getRootLength("//server") === 7 // "//server"
|
||||
* getRootLength("//server/share") === 8 // "//server/"
|
||||
* getRootLength("\\\\server") === 7 // "\\\\server"
|
||||
* getRootLength("\\\\server\\share") === 8 // "\\\\server\\"
|
||||
* getRootLength("file:///path") === 8 // "file:///"
|
||||
* getRootLength("file:///c:") === 10 // "file:///c:"
|
||||
* getRootLength("file:///c:d") === 8 // "file:///"
|
||||
* getRootLength("file:///c:/path") === 11 // "file:///c:/"
|
||||
* getRootLength("file://server") === 13 // "file://server"
|
||||
* getRootLength("file://server/path") === 14 // "file://server/"
|
||||
* getRootLength("http://server") === 13 // "http://server"
|
||||
* getRootLength("http://server/path") === 14 // "http://server/"
|
||||
* ```
|
||||
*/
|
||||
export function getRootLength(path: string) {
|
||||
const rootLength = getEncodedRootLength(path);
|
||||
return rootLength < 0 ? ~rootLength : rootLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path except for its basename. Semantics align with NodeJS's `path.dirname`
|
||||
* except that we support URLs as well.
|
||||
*
|
||||
* ```ts
|
||||
* // POSIX
|
||||
* getDirectoryPath("/path/to/file.ext") === "/path/to"
|
||||
* getDirectoryPath("/path/to/") === "/path"
|
||||
* getDirectoryPath("/") === "/"
|
||||
* // DOS
|
||||
* getDirectoryPath("c:/path/to/file.ext") === "c:/path/to"
|
||||
* getDirectoryPath("c:/path/to/") === "c:/path"
|
||||
* getDirectoryPath("c:/") === "c:/"
|
||||
* getDirectoryPath("c:") === "c:"
|
||||
* // URL
|
||||
* getDirectoryPath("http://typescriptlang.org/path/to/file.ext") === "http://typescriptlang.org/path/to"
|
||||
* getDirectoryPath("http://typescriptlang.org/path/to") === "http://typescriptlang.org/path"
|
||||
* getDirectoryPath("http://typescriptlang.org/") === "http://typescriptlang.org/"
|
||||
* getDirectoryPath("http://typescriptlang.org") === "http://typescriptlang.org"
|
||||
* ```
|
||||
*/
|
||||
export function getDirectoryPath(path: Path): Path;
|
||||
/**
|
||||
* Returns the path except for its basename. Semantics align with NodeJS's `path.dirname`
|
||||
* except that we support URLs as well.
|
||||
*
|
||||
* ```ts
|
||||
* // POSIX
|
||||
* getDirectoryPath("/path/to/file.ext") === "/path/to"
|
||||
* getDirectoryPath("/path/to/") === "/path"
|
||||
* getDirectoryPath("/") === "/"
|
||||
* // DOS
|
||||
* getDirectoryPath("c:/path/to/file.ext") === "c:/path/to"
|
||||
* getDirectoryPath("c:/path/to/") === "c:/path"
|
||||
* getDirectoryPath("c:/") === "c:/"
|
||||
* getDirectoryPath("c:") === "c:"
|
||||
* // URL
|
||||
* getDirectoryPath("http://typescriptlang.org/path/to/file.ext") === "http://typescriptlang.org/path/to"
|
||||
* getDirectoryPath("http://typescriptlang.org/path/to") === "http://typescriptlang.org/path"
|
||||
* getDirectoryPath("http://typescriptlang.org/") === "http://typescriptlang.org/"
|
||||
* getDirectoryPath("http://typescriptlang.org") === "http://typescriptlang.org"
|
||||
* getDirectoryPath("file://server/path/to/file.ext") === "file://server/path/to"
|
||||
* getDirectoryPath("file://server/path/to") === "file://server/path"
|
||||
* getDirectoryPath("file://server/") === "file://server/"
|
||||
* getDirectoryPath("file://server") === "file://server"
|
||||
* getDirectoryPath("file:///path/to/file.ext") === "file:///path/to"
|
||||
* getDirectoryPath("file:///path/to") === "file:///path"
|
||||
* getDirectoryPath("file:///") === "file:///"
|
||||
* getDirectoryPath("file://") === "file://"
|
||||
* ```
|
||||
*/
|
||||
export function getDirectoryPath(path: string): string;
|
||||
export function getDirectoryPath(path: string): string {
|
||||
path = normalizeSlashes(path);
|
||||
|
||||
// If the path provided is itself the root, then return it.
|
||||
const rootLength = getRootLength(path);
|
||||
if (rootLength === path.length) return path;
|
||||
|
||||
// return the leading portion of the path up to the last (non-terminal) directory separator
|
||||
// but not including any trailing directory separator.
|
||||
path = removeTrailingDirectorySeparator(path);
|
||||
return path.slice(0, Math.max(rootLength, path.lastIndexOf(directorySeparator)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path except for its containing directory name.
|
||||
* Semantics align with NodeJS's `path.basename` except that we support URL's as well.
|
||||
*
|
||||
* ```ts
|
||||
* // POSIX
|
||||
* getBaseFileName("/path/to/file.ext") === "file.ext"
|
||||
* getBaseFileName("/path/to/") === "to"
|
||||
* getBaseFileName("/") === ""
|
||||
* // DOS
|
||||
* getBaseFileName("c:/path/to/file.ext") === "file.ext"
|
||||
* getBaseFileName("c:/path/to/") === "to"
|
||||
* getBaseFileName("c:/") === ""
|
||||
* getBaseFileName("c:") === ""
|
||||
* // URL
|
||||
* getBaseFileName("http://typescriptlang.org/path/to/file.ext") === "file.ext"
|
||||
* getBaseFileName("http://typescriptlang.org/path/to/") === "to"
|
||||
* getBaseFileName("http://typescriptlang.org/") === ""
|
||||
* getBaseFileName("http://typescriptlang.org") === ""
|
||||
* getBaseFileName("file://server/path/to/file.ext") === "file.ext"
|
||||
* getBaseFileName("file://server/path/to/") === "to"
|
||||
* getBaseFileName("file://server/") === ""
|
||||
* getBaseFileName("file://server") === ""
|
||||
* getBaseFileName("file:///path/to/file.ext") === "file.ext"
|
||||
* getBaseFileName("file:///path/to/") === "to"
|
||||
* getBaseFileName("file:///") === ""
|
||||
* getBaseFileName("file://") === ""
|
||||
* ```
|
||||
*/
|
||||
export function getBaseFileName(path: string): string;
|
||||
/**
|
||||
* Gets the portion of a path following the last (non-terminal) separator (`/`).
|
||||
* Semantics align with NodeJS's `path.basename` except that we support URL's as well.
|
||||
* If the base name has any one of the provided extensions, it is removed.
|
||||
*
|
||||
* ```ts
|
||||
* getBaseFileName("/path/to/file.ext", ".ext", true) === "file"
|
||||
* getBaseFileName("/path/to/file.js", ".ext", true) === "file.js"
|
||||
* getBaseFileName("/path/to/file.js", [".ext", ".js"], true) === "file"
|
||||
* getBaseFileName("/path/to/file.ext", ".EXT", false) === "file.ext"
|
||||
* ```
|
||||
*/
|
||||
export function getBaseFileName(path: string, extensions: string | readonly string[], ignoreCase: boolean): string;
|
||||
export function getBaseFileName(path: string, extensions?: string | readonly string[], ignoreCase?: boolean) {
|
||||
path = normalizeSlashes(path);
|
||||
|
||||
// if the path provided is itself the root, then it has not file name.
|
||||
const rootLength = getRootLength(path);
|
||||
if (rootLength === path.length) return "";
|
||||
|
||||
// return the trailing portion of the path starting after the last (non-terminal) directory
|
||||
// separator but not including any trailing directory separator.
|
||||
path = removeTrailingDirectorySeparator(path);
|
||||
const name = path.slice(Math.max(getRootLength(path), path.lastIndexOf(directorySeparator) + 1));
|
||||
const extension = extensions !== undefined && ignoreCase !== undefined ? getAnyExtensionFromPath(name, extensions, ignoreCase) : undefined;
|
||||
return extension ? name.slice(0, name.length - extension.length) : name;
|
||||
}
|
||||
|
||||
function tryGetExtensionFromPath(path: string, extension: string, stringEqualityComparer: (a: string, b: string) => boolean) {
|
||||
if (!startsWith(extension, ".")) extension = "." + extension;
|
||||
if (path.length >= extension.length && path.charCodeAt(path.length - extension.length) === CharacterCodes.dot) {
|
||||
const pathExtension = path.slice(path.length - extension.length);
|
||||
if (stringEqualityComparer(pathExtension, extension)) {
|
||||
return pathExtension;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getAnyExtensionFromPathWorker(path: string, extensions: string | readonly string[], stringEqualityComparer: (a: string, b: string) => boolean) {
|
||||
if (typeof extensions === "string") {
|
||||
return tryGetExtensionFromPath(path, extensions, stringEqualityComparer) || "";
|
||||
}
|
||||
for (const extension of extensions) {
|
||||
const result = tryGetExtensionFromPath(path, extension, stringEqualityComparer);
|
||||
if (result) return result;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file extension for a path.
|
||||
*
|
||||
* ```ts
|
||||
* getAnyExtensionFromPath("/path/to/file.ext") === ".ext"
|
||||
* getAnyExtensionFromPath("/path/to/file.ext/") === ".ext"
|
||||
* getAnyExtensionFromPath("/path/to/file") === ""
|
||||
* getAnyExtensionFromPath("/path/to.ext/file") === ""
|
||||
* ```
|
||||
*/
|
||||
export function getAnyExtensionFromPath(path: string): string;
|
||||
/**
|
||||
* Gets the file extension for a path, provided it is one of the provided extensions.
|
||||
*
|
||||
* ```ts
|
||||
* getAnyExtensionFromPath("/path/to/file.ext", ".ext", true) === ".ext"
|
||||
* getAnyExtensionFromPath("/path/to/file.js", ".ext", true) === ""
|
||||
* getAnyExtensionFromPath("/path/to/file.js", [".ext", ".js"], true) === ".js"
|
||||
* getAnyExtensionFromPath("/path/to/file.ext", ".EXT", false) === ""
|
||||
*/
|
||||
export function getAnyExtensionFromPath(path: string, extensions: string | readonly string[], ignoreCase: boolean): string;
|
||||
export function getAnyExtensionFromPath(path: string, extensions?: string | readonly string[], ignoreCase?: boolean): string {
|
||||
// Retrieves any string from the final "." onwards from a base file name.
|
||||
// Unlike extensionFromPath, which throws an exception on unrecognized extensions.
|
||||
if (extensions) {
|
||||
return getAnyExtensionFromPathWorker(removeTrailingDirectorySeparator(path), extensions, ignoreCase ? equateStringsCaseInsensitive : equateStringsCaseSensitive);
|
||||
}
|
||||
const baseFileName = getBaseFileName(path);
|
||||
const extensionIndex = baseFileName.lastIndexOf(".");
|
||||
if (extensionIndex >= 0) {
|
||||
return baseFileName.substring(extensionIndex);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function pathComponents(path: string, rootLength: number) {
|
||||
const root = path.substring(0, rootLength);
|
||||
const rest = path.substring(rootLength).split(directorySeparator);
|
||||
if (rest.length && !lastOrUndefined(rest)) rest.pop();
|
||||
return [root, ...rest];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a path into an array containing a root component (at index 0) and zero or more path
|
||||
* components (at indices > 0). The result is not normalized.
|
||||
* If the path is relative, the root component is `""`.
|
||||
* If the path is absolute, the root component includes the first path separator (`/`).
|
||||
*
|
||||
* ```ts
|
||||
* // POSIX
|
||||
* getPathComponents("/path/to/file.ext") === ["/", "path", "to", "file.ext"]
|
||||
* getPathComponents("/path/to/") === ["/", "path", "to"]
|
||||
* getPathComponents("/") === ["/"]
|
||||
* // DOS
|
||||
* getPathComponents("c:/path/to/file.ext") === ["c:/", "path", "to", "file.ext"]
|
||||
* getPathComponents("c:/path/to/") === ["c:/", "path", "to"]
|
||||
* getPathComponents("c:/") === ["c:/"]
|
||||
* getPathComponents("c:") === ["c:"]
|
||||
* // URL
|
||||
* getPathComponents("http://typescriptlang.org/path/to/file.ext") === ["http://typescriptlang.org/", "path", "to", "file.ext"]
|
||||
* getPathComponents("http://typescriptlang.org/path/to/") === ["http://typescriptlang.org/", "path", "to"]
|
||||
* getPathComponents("http://typescriptlang.org/") === ["http://typescriptlang.org/"]
|
||||
* getPathComponents("http://typescriptlang.org") === ["http://typescriptlang.org"]
|
||||
* getPathComponents("file://server/path/to/file.ext") === ["file://server/", "path", "to", "file.ext"]
|
||||
* getPathComponents("file://server/path/to/") === ["file://server/", "path", "to"]
|
||||
* getPathComponents("file://server/") === ["file://server/"]
|
||||
* getPathComponents("file://server") === ["file://server"]
|
||||
* getPathComponents("file:///path/to/file.ext") === ["file:///", "path", "to", "file.ext"]
|
||||
* getPathComponents("file:///path/to/") === ["file:///", "path", "to"]
|
||||
* getPathComponents("file:///") === ["file:///"]
|
||||
* getPathComponents("file://") === ["file://"]
|
||||
*/
|
||||
export function getPathComponents(path: string, currentDirectory = "") {
|
||||
path = combinePaths(currentDirectory, path);
|
||||
return pathComponents(path, getRootLength(path));
|
||||
}
|
||||
|
||||
//// Path Formatting
|
||||
|
||||
/**
|
||||
* Formats a parsed path consisting of a root component (at index 0) and zero or more path
|
||||
* segments (at indices > 0).
|
||||
*
|
||||
* ```ts
|
||||
* getPathFromPathComponents(["/", "path", "to", "file.ext"]) === "/path/to/file.ext"
|
||||
* ```
|
||||
*/
|
||||
export function getPathFromPathComponents(pathComponents: readonly string[]) {
|
||||
if (pathComponents.length === 0) return "";
|
||||
|
||||
const root = pathComponents[0] && ensureTrailingDirectorySeparator(pathComponents[0]);
|
||||
return root + pathComponents.slice(1).join(directorySeparator);
|
||||
}
|
||||
|
||||
//// Path Normalization
|
||||
|
||||
/**
|
||||
* Normalize path separators, converting `\` into `/`.
|
||||
*/
|
||||
export function normalizeSlashes(path: string): string {
|
||||
return path.replace(backslashRegExp, directorySeparator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce an array of path components to a more simplified path by navigating any
|
||||
* `"."` or `".."` entries in the path.
|
||||
*/
|
||||
export function reducePathComponents(components: readonly string[]) {
|
||||
if (!some(components)) return [];
|
||||
const reduced = [components[0]];
|
||||
for (let i = 1; i < components.length; i++) {
|
||||
const component = components[i];
|
||||
if (!component) continue;
|
||||
if (component === ".") continue;
|
||||
if (component === "..") {
|
||||
if (reduced.length > 1) {
|
||||
if (reduced[reduced.length - 1] !== "..") {
|
||||
reduced.pop();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (reduced[0]) continue;
|
||||
}
|
||||
reduced.push(component);
|
||||
}
|
||||
return reduced;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines paths. If a path is absolute, it replaces any previous path. Relative paths are not simplified.
|
||||
*
|
||||
* ```ts
|
||||
* // Non-rooted
|
||||
* combinePaths("path", "to", "file.ext") === "path/to/file.ext"
|
||||
* combinePaths("path", "dir", "..", "to", "file.ext") === "path/dir/../to/file.ext"
|
||||
* // POSIX
|
||||
* combinePaths("/path", "to", "file.ext") === "/path/to/file.ext"
|
||||
* combinePaths("/path", "/to", "file.ext") === "/to/file.ext"
|
||||
* // DOS
|
||||
* combinePaths("c:/path", "to", "file.ext") === "c:/path/to/file.ext"
|
||||
* combinePaths("c:/path", "c:/to", "file.ext") === "c:/to/file.ext"
|
||||
* // URL
|
||||
* combinePaths("file:///path", "to", "file.ext") === "file:///path/to/file.ext"
|
||||
* combinePaths("file:///path", "file:///to", "file.ext") === "file:///to/file.ext"
|
||||
* ```
|
||||
*/
|
||||
export function combinePaths(path: string, ...paths: (string | undefined)[]): string {
|
||||
if (path) path = normalizeSlashes(path);
|
||||
for (let relativePath of paths) {
|
||||
if (!relativePath) continue;
|
||||
relativePath = normalizeSlashes(relativePath);
|
||||
if (!path || getRootLength(relativePath) !== 0) {
|
||||
path = relativePath;
|
||||
}
|
||||
else {
|
||||
path = ensureTrailingDirectorySeparator(path) + relativePath;
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines and resolves paths. If a path is absolute, it replaces any previous path. Any
|
||||
* `.` and `..` path components are resolved. Trailing directory separators are preserved.
|
||||
*
|
||||
* ```ts
|
||||
* resolvePath("/path", "to", "file.ext") === "path/to/file.ext"
|
||||
* resolvePath("/path", "to", "file.ext/") === "path/to/file.ext/"
|
||||
* resolvePath("/path", "dir", "..", "to", "file.ext") === "path/to/file.ext"
|
||||
* ```
|
||||
*/
|
||||
export function resolvePath(path: string, ...paths: (string | undefined)[]): string {
|
||||
return normalizePath(some(paths) ? combinePaths(path, ...paths) : normalizeSlashes(path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a path into an array containing a root component (at index 0) and zero or more path
|
||||
* components (at indices > 0). The result is normalized.
|
||||
* If the path is relative, the root component is `""`.
|
||||
* If the path is absolute, the root component includes the first path separator (`/`).
|
||||
*
|
||||
* ```ts
|
||||
* getNormalizedPathComponents("to/dir/../file.ext", "/path/") === ["/", "path", "to", "file.ext"]
|
||||
*/
|
||||
export function getNormalizedPathComponents(path: string, currentDirectory: string | undefined) {
|
||||
return reducePathComponents(getPathComponents(path, currentDirectory));
|
||||
}
|
||||
|
||||
export function getNormalizedAbsolutePath(fileName: string, currentDirectory: string | undefined) {
|
||||
return getPathFromPathComponents(getNormalizedPathComponents(fileName, currentDirectory));
|
||||
}
|
||||
|
||||
export function normalizePath(path: string): string {
|
||||
path = normalizeSlashes(path);
|
||||
const normalized = getPathFromPathComponents(reducePathComponents(getPathComponents(path)));
|
||||
return normalized && hasTrailingDirectorySeparator(path) ? ensureTrailingDirectorySeparator(normalized) : normalized;
|
||||
}
|
||||
|
||||
function getPathWithoutRoot(pathComponents: readonly string[]) {
|
||||
if (pathComponents.length === 0) return "";
|
||||
return pathComponents.slice(1).join(directorySeparator);
|
||||
}
|
||||
|
||||
export function getNormalizedAbsolutePathWithoutRoot(fileName: string, currentDirectory: string | undefined) {
|
||||
return getPathWithoutRoot(getNormalizedPathComponents(fileName, currentDirectory));
|
||||
}
|
||||
|
||||
export function toPath(fileName: string, basePath: string | undefined, getCanonicalFileName: (path: string) => string): Path {
|
||||
const nonCanonicalizedPath = isRootedDiskPath(fileName)
|
||||
? normalizePath(fileName)
|
||||
: getNormalizedAbsolutePath(fileName, basePath);
|
||||
return <Path>getCanonicalFileName(nonCanonicalizedPath);
|
||||
}
|
||||
|
||||
export function normalizePathAndParts(path: string): { path: string, parts: string[] } {
|
||||
path = normalizeSlashes(path);
|
||||
const [root, ...parts] = reducePathComponents(getPathComponents(path));
|
||||
if (parts.length) {
|
||||
const joinedParts = root + parts.join(directorySeparator);
|
||||
return { path: hasTrailingDirectorySeparator(path) ? ensureTrailingDirectorySeparator(joinedParts) : joinedParts, parts };
|
||||
}
|
||||
else {
|
||||
return { path: root, parts };
|
||||
}
|
||||
}
|
||||
|
||||
//// Path Mutation
|
||||
|
||||
/**
|
||||
* Removes a trailing directory separator from a path, if it does not already have one.
|
||||
*
|
||||
* ```ts
|
||||
* removeTrailingDirectorySeparator("/path/to/file.ext") === "/path/to/file.ext"
|
||||
* removeTrailingDirectorySeparator("/path/to/file.ext/") === "/path/to/file.ext"
|
||||
* ```
|
||||
*/
|
||||
export function removeTrailingDirectorySeparator(path: Path): Path;
|
||||
export function removeTrailingDirectorySeparator(path: string): string;
|
||||
export function removeTrailingDirectorySeparator(path: string) {
|
||||
if (hasTrailingDirectorySeparator(path)) {
|
||||
return path.substr(0, path.length - 1);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a trailing directory separator to a path, if it does not already have one.
|
||||
*
|
||||
* ```ts
|
||||
* ensureTrailingDirectorySeparator("/path/to/file.ext") === "/path/to/file.ext/"
|
||||
* ensureTrailingDirectorySeparator("/path/to/file.ext/") === "/path/to/file.ext/"
|
||||
* ```
|
||||
*/
|
||||
export function ensureTrailingDirectorySeparator(path: Path): Path;
|
||||
export function ensureTrailingDirectorySeparator(path: string): string;
|
||||
export function ensureTrailingDirectorySeparator(path: string) {
|
||||
if (!hasTrailingDirectorySeparator(path)) {
|
||||
return path + directorySeparator;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures a path is either absolute (prefixed with `/` or `c:`) or dot-relative (prefixed
|
||||
* with `./` or `../`) so as not to be confused with an unprefixed module name.
|
||||
*
|
||||
* ```ts
|
||||
* ensurePathIsNonModuleName("/path/to/file.ext") === "/path/to/file.ext"
|
||||
* ensurePathIsNonModuleName("./path/to/file.ext") === "./path/to/file.ext"
|
||||
* ensurePathIsNonModuleName("../path/to/file.ext") === "../path/to/file.ext"
|
||||
* ensurePathIsNonModuleName("path/to/file.ext") === "./path/to/file.ext"
|
||||
* ```
|
||||
*/
|
||||
export function ensurePathIsNonModuleName(path: string): string {
|
||||
return !pathIsAbsolute(path) && !pathIsRelative(path) ? "./" + path : path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the extension of a path to the provided extension.
|
||||
*
|
||||
* ```ts
|
||||
* changeAnyExtension("/path/to/file.ext", ".js") === "/path/to/file.js"
|
||||
* ```
|
||||
*/
|
||||
export function changeAnyExtension(path: string, ext: string): string;
|
||||
/**
|
||||
* Changes the extension of a path to the provided extension if it has one of the provided extensions.
|
||||
*
|
||||
* ```ts
|
||||
* changeAnyExtension("/path/to/file.ext", ".js", ".ext") === "/path/to/file.js"
|
||||
* changeAnyExtension("/path/to/file.ext", ".js", ".ts") === "/path/to/file.ext"
|
||||
* changeAnyExtension("/path/to/file.ext", ".js", [".ext", ".ts"]) === "/path/to/file.js"
|
||||
* ```
|
||||
*/
|
||||
export function changeAnyExtension(path: string, ext: string, extensions: string | readonly string[], ignoreCase: boolean): string;
|
||||
export function changeAnyExtension(path: string, ext: string, extensions?: string | readonly string[], ignoreCase?: boolean) {
|
||||
const pathext = extensions !== undefined && ignoreCase !== undefined ? getAnyExtensionFromPath(path, extensions, ignoreCase) : getAnyExtensionFromPath(path);
|
||||
return pathext ? path.slice(0, path.length - pathext.length) + (startsWith(ext, ".") ? ext : "." + ext) : path;
|
||||
}
|
||||
|
||||
//// Path Comparisons
|
||||
|
||||
// check path for these segments: '', '.'. '..'
|
||||
const relativePathSegmentRegExp = /(^|\/)\.{0,2}($|\/)/;
|
||||
|
||||
function comparePathsWorker(a: string, b: string, componentComparer: (a: string, b: string) => Comparison) {
|
||||
if (a === b) return Comparison.EqualTo;
|
||||
if (a === undefined) return Comparison.LessThan;
|
||||
if (b === undefined) return Comparison.GreaterThan;
|
||||
|
||||
// NOTE: Performance optimization - shortcut if the root segments differ as there would be no
|
||||
// need to perform path reduction.
|
||||
const aRoot = a.substring(0, getRootLength(a));
|
||||
const bRoot = b.substring(0, getRootLength(b));
|
||||
const result = compareStringsCaseInsensitive(aRoot, bRoot);
|
||||
if (result !== Comparison.EqualTo) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Performance optimization - shortcut if there are no relative path segments in
|
||||
// the non-root portion of the path
|
||||
const aRest = a.substring(aRoot.length);
|
||||
const bRest = b.substring(bRoot.length);
|
||||
if (!relativePathSegmentRegExp.test(aRest) && !relativePathSegmentRegExp.test(bRest)) {
|
||||
return componentComparer(aRest, bRest);
|
||||
}
|
||||
|
||||
// The path contains a relative path segment. Normalize the paths and perform a slower component
|
||||
// by component comparison.
|
||||
const aComponents = reducePathComponents(getPathComponents(a));
|
||||
const bComponents = reducePathComponents(getPathComponents(b));
|
||||
const sharedLength = Math.min(aComponents.length, bComponents.length);
|
||||
for (let i = 1; i < sharedLength; i++) {
|
||||
const result = componentComparer(aComponents[i], bComponents[i]);
|
||||
if (result !== Comparison.EqualTo) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return compareValues(aComponents.length, bComponents.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a case-sensitive comparison of two paths. Path roots are always compared case-insensitively.
|
||||
*/
|
||||
export function comparePathsCaseSensitive(a: string, b: string) {
|
||||
return comparePathsWorker(a, b, compareStringsCaseSensitive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a case-insensitive comparison of two paths.
|
||||
*/
|
||||
export function comparePathsCaseInsensitive(a: string, b: string) {
|
||||
return comparePathsWorker(a, b, compareStringsCaseInsensitive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two paths using the provided case sensitivity.
|
||||
*/
|
||||
export function comparePaths(a: string, b: string, ignoreCase?: boolean): Comparison;
|
||||
export function comparePaths(a: string, b: string, currentDirectory: string, ignoreCase?: boolean): Comparison;
|
||||
export function comparePaths(a: string, b: string, currentDirectory?: string | boolean, ignoreCase?: boolean) {
|
||||
if (typeof currentDirectory === "string") {
|
||||
a = combinePaths(currentDirectory, a);
|
||||
b = combinePaths(currentDirectory, b);
|
||||
}
|
||||
else if (typeof currentDirectory === "boolean") {
|
||||
ignoreCase = currentDirectory;
|
||||
}
|
||||
return comparePathsWorker(a, b, getStringComparer(ignoreCase));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a `parent` path contains a `child` path using the provide case sensitivity.
|
||||
*/
|
||||
export function containsPath(parent: string, child: string, ignoreCase?: boolean): boolean;
|
||||
export function containsPath(parent: string, child: string, currentDirectory: string, ignoreCase?: boolean): boolean;
|
||||
export function containsPath(parent: string, child: string, currentDirectory?: string | boolean, ignoreCase?: boolean) {
|
||||
if (typeof currentDirectory === "string") {
|
||||
parent = combinePaths(currentDirectory, parent);
|
||||
child = combinePaths(currentDirectory, child);
|
||||
}
|
||||
else if (typeof currentDirectory === "boolean") {
|
||||
ignoreCase = currentDirectory;
|
||||
}
|
||||
if (parent === undefined || child === undefined) return false;
|
||||
if (parent === child) return true;
|
||||
const parentComponents = reducePathComponents(getPathComponents(parent));
|
||||
const childComponents = reducePathComponents(getPathComponents(child));
|
||||
if (childComponents.length < parentComponents.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const componentEqualityComparer = ignoreCase ? equateStringsCaseInsensitive : equateStringsCaseSensitive;
|
||||
for (let i = 0; i < parentComponents.length; i++) {
|
||||
const equalityComparer = i === 0 ? equateStringsCaseInsensitive : componentEqualityComparer;
|
||||
if (!equalityComparer(parentComponents[i], childComponents[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether `fileName` starts with the specified `directoryName` using the provided path canonicalization callback.
|
||||
* Comparison is case-sensitive between the canonical paths.
|
||||
*
|
||||
* @deprecated Use `containsPath` if possible.
|
||||
*/
|
||||
export function startsWithDirectory(fileName: string, directoryName: string, getCanonicalFileName: GetCanonicalFileName): boolean {
|
||||
const canonicalFileName = getCanonicalFileName(fileName);
|
||||
const canonicalDirectoryName = getCanonicalFileName(directoryName);
|
||||
return startsWith(canonicalFileName, canonicalDirectoryName + "/") || startsWith(canonicalFileName, canonicalDirectoryName + "\\");
|
||||
}
|
||||
|
||||
//// Relative Paths
|
||||
|
||||
export function getPathComponentsRelativeTo(from: string, to: string, stringEqualityComparer: (a: string, b: string) => boolean, getCanonicalFileName: GetCanonicalFileName) {
|
||||
const fromComponents = reducePathComponents(getPathComponents(from));
|
||||
const toComponents = reducePathComponents(getPathComponents(to));
|
||||
|
||||
let start: number;
|
||||
for (start = 0; start < fromComponents.length && start < toComponents.length; start++) {
|
||||
const fromComponent = getCanonicalFileName(fromComponents[start]);
|
||||
const toComponent = getCanonicalFileName(toComponents[start]);
|
||||
const comparer = start === 0 ? equateStringsCaseInsensitive : stringEqualityComparer;
|
||||
if (!comparer(fromComponent, toComponent)) break;
|
||||
}
|
||||
|
||||
if (start === 0) {
|
||||
return toComponents;
|
||||
}
|
||||
|
||||
const components = toComponents.slice(start);
|
||||
const relative: string[] = [];
|
||||
for (; start < fromComponents.length; start++) {
|
||||
relative.push("..");
|
||||
}
|
||||
return ["", ...relative, ...components];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a relative path that can be used to traverse between `from` and `to`.
|
||||
*/
|
||||
export function getRelativePathFromDirectory(from: string, to: string, ignoreCase: boolean): string;
|
||||
/**
|
||||
* Gets a relative path that can be used to traverse between `from` and `to`.
|
||||
*/
|
||||
export function getRelativePathFromDirectory(fromDirectory: string, to: string, getCanonicalFileName: GetCanonicalFileName): string; // eslint-disable-line @typescript-eslint/unified-signatures
|
||||
export function getRelativePathFromDirectory(fromDirectory: string, to: string, getCanonicalFileNameOrIgnoreCase: GetCanonicalFileName | boolean) {
|
||||
Debug.assert((getRootLength(fromDirectory) > 0) === (getRootLength(to) > 0), "Paths must either both be absolute or both be relative");
|
||||
const getCanonicalFileName = typeof getCanonicalFileNameOrIgnoreCase === "function" ? getCanonicalFileNameOrIgnoreCase : identity;
|
||||
const ignoreCase = typeof getCanonicalFileNameOrIgnoreCase === "boolean" ? getCanonicalFileNameOrIgnoreCase : false;
|
||||
const pathComponents = getPathComponentsRelativeTo(fromDirectory, to, ignoreCase ? equateStringsCaseInsensitive : equateStringsCaseSensitive, getCanonicalFileName);
|
||||
return getPathFromPathComponents(pathComponents);
|
||||
}
|
||||
|
||||
export function convertToRelativePath(absoluteOrRelativePath: string, basePath: string, getCanonicalFileName: (path: string) => string): string {
|
||||
return !isRootedDiskPath(absoluteOrRelativePath)
|
||||
? absoluteOrRelativePath
|
||||
: getRelativePathToDirectoryOrUrl(basePath, absoluteOrRelativePath, basePath, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
|
||||
}
|
||||
|
||||
export function getRelativePathFromFile(from: string, to: string, getCanonicalFileName: GetCanonicalFileName) {
|
||||
return ensurePathIsNonModuleName(getRelativePathFromDirectory(getDirectoryPath(from), to, getCanonicalFileName));
|
||||
}
|
||||
|
||||
export function getRelativePathToDirectoryOrUrl(directoryPathOrUrl: string, relativeOrAbsolutePath: string, currentDirectory: string, getCanonicalFileName: GetCanonicalFileName, isAbsolutePathAnUrl: boolean) {
|
||||
const pathComponents = getPathComponentsRelativeTo(
|
||||
resolvePath(currentDirectory, directoryPathOrUrl),
|
||||
resolvePath(currentDirectory, relativeOrAbsolutePath),
|
||||
equateStringsCaseSensitive,
|
||||
getCanonicalFileName
|
||||
);
|
||||
|
||||
const firstComponent = pathComponents[0];
|
||||
if (isAbsolutePathAnUrl && isRootedDiskPath(firstComponent)) {
|
||||
const prefix = firstComponent.charAt(0) === directorySeparator ? "file://" : "file:///";
|
||||
pathComponents[0] = prefix + firstComponent;
|
||||
}
|
||||
|
||||
return getPathFromPathComponents(pathComponents);
|
||||
}
|
||||
|
||||
//// Path Traversal
|
||||
|
||||
/**
|
||||
* Calls `callback` on `directory` and every ancestor directory it has, returning the first defined result.
|
||||
*/
|
||||
export function forEachAncestorDirectory<T>(directory: Path, callback: (directory: Path) => T | undefined): T | undefined;
|
||||
export function forEachAncestorDirectory<T>(directory: string, callback: (directory: string) => T | undefined): T | undefined;
|
||||
export function forEachAncestorDirectory<T>(directory: Path, callback: (directory: Path) => T | undefined): T | undefined {
|
||||
while (true) {
|
||||
const result = callback(directory);
|
||||
if (result !== undefined) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const parentPath = getDirectoryPath(directory);
|
||||
if (parentPath === directory) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
directory = parentPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,4 @@ namespace ts {
|
||||
|
||||
/** Performance logger that will generate ETW events if possible - check for `logEvent` member, as `etwModule` will be `{}` when browserified */
|
||||
export const perfLogger: PerfLogger = etwModule && etwModule.logEvent ? etwModule : nullLogger;
|
||||
const args = typeof process === "undefined" ? [] : process.argv;
|
||||
perfLogger.logInfoEvent(`Starting TypeScript v${versionMajorMinor} with command line: ${JSON.stringify(args)}`);
|
||||
}
|
||||
|
||||
@@ -545,6 +545,9 @@ namespace ts {
|
||||
return resolutions;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export const inferredTypesContainingFile = "__inferred type names__.ts";
|
||||
|
||||
interface DiagnosticCache<T extends Diagnostic> {
|
||||
perFile?: Map<T[]>;
|
||||
allDiagnostics?: Diagnostic[];
|
||||
@@ -875,7 +878,7 @@ namespace ts {
|
||||
if (typeReferences.length) {
|
||||
// This containingFilename needs to match with the one used in managed-side
|
||||
const containingDirectory = options.configFilePath ? getDirectoryPath(options.configFilePath) : host.getCurrentDirectory();
|
||||
const containingFilename = combinePaths(containingDirectory, "__inferred type names__.ts");
|
||||
const containingFilename = combinePaths(containingDirectory, inferredTypesContainingFile);
|
||||
const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeReferences, containingFilename);
|
||||
for (let i = 0; i < typeReferences.length; i++) {
|
||||
processTypeReferenceDirective(typeReferences[i], resolutions[i]);
|
||||
@@ -2926,10 +2929,6 @@ namespace ts {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_incremental_can_only_be_specified_using_tsconfig_emitting_to_single_file_or_when_option_tsBuildInfoFile_is_specified));
|
||||
}
|
||||
|
||||
if (options.noEmit && isIncrementalCompilation(options)) {
|
||||
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "noEmit", options.incremental ? "incremental" : "composite");
|
||||
}
|
||||
|
||||
verifyProjectReferences();
|
||||
|
||||
// List of collected files is complete; validate exhautiveness if this is a project with a file list
|
||||
|
||||
@@ -669,6 +669,11 @@ namespace ts {
|
||||
// Mark the file as needing re-evaluation of module resolution instead of using it blindly.
|
||||
resolution.isInvalidated = true;
|
||||
(filesWithInvalidatedResolutions || (filesWithInvalidatedResolutions = createMap<true>())).set(containingFilePath, true);
|
||||
|
||||
// When its a file with inferred types resolution, invalidate type reference directive resolution
|
||||
if (containingFilePath.endsWith(inferredTypesContainingFile)) {
|
||||
resolutionHost.onChangedAutomaticTypeDirectiveNames();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -682,6 +682,7 @@ namespace ts {
|
||||
/*@internal*/ bufferFrom?(input: string, encoding?: string): Buffer;
|
||||
// For testing
|
||||
/*@internal*/ now?(): Date;
|
||||
/*@internal*/ require?(baseDir: string, moduleName: string): RequireResult;
|
||||
}
|
||||
|
||||
export interface FileWatcher {
|
||||
@@ -876,6 +877,15 @@ namespace ts {
|
||||
bufferFrom,
|
||||
base64decode: input => bufferFrom(input, "base64").toString("utf8"),
|
||||
base64encode: input => bufferFrom(input).toString("base64"),
|
||||
require: (baseDir, moduleName) => {
|
||||
try {
|
||||
const modulePath = resolveJSModule(moduleName, baseDir, nodeSystem);
|
||||
return { module: require(modulePath), modulePath, error: undefined };
|
||||
}
|
||||
catch (error) {
|
||||
return { module: undefined, modulePath: undefined, error };
|
||||
}
|
||||
}
|
||||
};
|
||||
return nodeSystem;
|
||||
|
||||
@@ -1022,6 +1032,7 @@ namespace ts {
|
||||
return watchDirectoryUsingFsWatch;
|
||||
}
|
||||
|
||||
// defer watchDirectoryRecursively as it depends on `ts.createMap()` which may not be usable yet.
|
||||
const watchDirectory = tscWatchDirectory === "RecursiveDirectoryUsingFsWatchFile" ?
|
||||
createWatchDirectoryUsing(fsWatchFile) :
|
||||
tscWatchDirectory === "RecursiveDirectoryUsingDynamicPriorityPolling" ?
|
||||
|
||||
@@ -132,7 +132,7 @@ namespace ts {
|
||||
// Create a temporary variable to store a computed property name (if necessary).
|
||||
// If it's not inlineable, then we emit an expression after the class which assigns
|
||||
// the property name to the temporary variable.
|
||||
const expr = getPropertyNameExpressionIfNeeded(node.name, !!node.initializer);
|
||||
const expr = getPropertyNameExpressionIfNeeded(node.name, !!node.initializer || !!context.getCompilerOptions().useDefineForClassFields);
|
||||
if (expr && !isSimpleInlineableExpression(expr)) {
|
||||
(pendingExpressions || (pendingExpressions = [])).push(expr);
|
||||
}
|
||||
@@ -145,7 +145,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
const savedPendingExpressions = pendingExpressions;
|
||||
pendingExpressions = undefined!;
|
||||
pendingExpressions = undefined;
|
||||
|
||||
const extendsClauseElement = getEffectiveBaseTypeNode(node);
|
||||
const isDerivedClass = !!(extendsClauseElement && skipOuterExpressions(extendsClauseElement.expression).kind !== SyntaxKind.NullKeyword);
|
||||
|
||||
@@ -285,6 +285,7 @@ namespace ts {
|
||||
let combinedStatements: NodeArray<Statement>;
|
||||
if (isSourceFileJS(currentSourceFile)) {
|
||||
combinedStatements = createNodeArray(transformDeclarationsForJS(node));
|
||||
refs.forEach(referenceVisitor);
|
||||
emittedImports = filter(combinedStatements, isAnyImportSyntax);
|
||||
}
|
||||
else {
|
||||
|
||||
+9
-15
@@ -1788,31 +1788,25 @@ namespace ts {
|
||||
|
||||
let reportQueue = true;
|
||||
let successfulProjects = 0;
|
||||
let errorProjects = 0;
|
||||
while (true) {
|
||||
const invalidatedProject = getNextInvalidatedProject(state, buildOrder, reportQueue);
|
||||
if (!invalidatedProject) break;
|
||||
reportQueue = false;
|
||||
invalidatedProject.done(cancellationToken);
|
||||
if (state.diagnostics.has(invalidatedProject.projectPath)) {
|
||||
errorProjects++;
|
||||
}
|
||||
else {
|
||||
successfulProjects++;
|
||||
}
|
||||
if (!state.diagnostics.has(invalidatedProject.projectPath)) successfulProjects++;
|
||||
}
|
||||
|
||||
disableCache(state);
|
||||
reportErrorSummary(state, buildOrder);
|
||||
startWatching(state, buildOrder);
|
||||
|
||||
return isCircularBuildOrder(buildOrder) ?
|
||||
ExitStatus.ProjectReferenceCycle_OutputsSkupped :
|
||||
errorProjects ?
|
||||
successfulProjects ?
|
||||
ExitStatus.DiagnosticsPresent_OutputsGenerated :
|
||||
ExitStatus.DiagnosticsPresent_OutputsSkipped :
|
||||
ExitStatus.Success;
|
||||
return isCircularBuildOrder(buildOrder)
|
||||
? ExitStatus.ProjectReferenceCycle_OutputsSkipped
|
||||
: !buildOrder.some(p => state.diagnostics.has(toResolvedConfigFilePath(state, p)))
|
||||
? ExitStatus.Success
|
||||
: successfulProjects
|
||||
? ExitStatus.DiagnosticsPresent_OutputsGenerated
|
||||
: ExitStatus.DiagnosticsPresent_OutputsSkipped;
|
||||
}
|
||||
|
||||
function clean(state: SolutionBuilderState, project?: string, onlyReferences?: boolean) {
|
||||
@@ -1821,7 +1815,7 @@ namespace ts {
|
||||
|
||||
if (isCircularBuildOrder(buildOrder)) {
|
||||
reportErrors(state, buildOrder.circularDiagnostics);
|
||||
return ExitStatus.ProjectReferenceCycle_OutputsSkupped;
|
||||
return ExitStatus.ProjectReferenceCycle_OutputsSkipped;
|
||||
}
|
||||
|
||||
const { options, host } = state;
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
"outFile": "../../built/local/compiler.js"
|
||||
},
|
||||
|
||||
"references": [],
|
||||
"references": [
|
||||
{ "path": "../shims" },
|
||||
],
|
||||
|
||||
"files": [
|
||||
"core.ts",
|
||||
@@ -15,6 +17,7 @@
|
||||
|
||||
"types.ts",
|
||||
"sys.ts",
|
||||
"path.ts",
|
||||
"diagnosticInformationMap.generated.ts",
|
||||
"scanner.ts",
|
||||
"utilities.ts",
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"outFile": "../../built/local/compiler.release.js",
|
||||
"removeComments": true,
|
||||
"preserveConstEnums": false
|
||||
}
|
||||
},
|
||||
"references": [
|
||||
{ "path": "../shims" }
|
||||
]
|
||||
}
|
||||
|
||||
+43
-8
@@ -2250,12 +2250,14 @@ namespace ts {
|
||||
parent: CaseBlock;
|
||||
expression: Expression;
|
||||
statements: NodeArray<Statement>;
|
||||
/* @internal */ fallthroughFlowNode?: FlowNode;
|
||||
}
|
||||
|
||||
export interface DefaultClause extends Node {
|
||||
kind: SyntaxKind.DefaultClause;
|
||||
parent: CaseBlock;
|
||||
statements: NodeArray<Statement>;
|
||||
/* @internal */ fallthroughFlowNode?: FlowNode;
|
||||
}
|
||||
|
||||
export type CaseOrDefaultClause = CaseClause | DefaultClause;
|
||||
@@ -2681,6 +2683,7 @@ namespace ts {
|
||||
isArrayType?: boolean;
|
||||
}
|
||||
|
||||
// NOTE: Ensure this is up-to-date with src/debug/debug.ts
|
||||
export const enum FlowFlags {
|
||||
Unreachable = 1 << 0, // Unreachable code
|
||||
Start = 1 << 1, // Start of flow graph
|
||||
@@ -3258,6 +3261,9 @@ namespace ts {
|
||||
InvalidProject_OutputsSkipped = 3,
|
||||
|
||||
// When build is skipped because project references form cycle
|
||||
ProjectReferenceCycle_OutputsSkipped = 4,
|
||||
|
||||
/** @deprecated Use ProjectReferenceCycle_OutputsSkipped instead. */
|
||||
ProjectReferenceCycle_OutputsSkupped = 4,
|
||||
}
|
||||
|
||||
@@ -3365,8 +3371,10 @@ namespace ts {
|
||||
|
||||
getFullyQualifiedName(symbol: Symbol): string;
|
||||
getAugmentedPropertiesOfType(type: Type): Symbol[];
|
||||
|
||||
getRootSymbols(symbol: Symbol): readonly Symbol[];
|
||||
getContextualType(node: Expression): Type | undefined;
|
||||
/* @internal */ getContextualType(node: Expression, contextFlags?: ContextFlags): Type | undefined; // eslint-disable-line @typescript-eslint/unified-signatures
|
||||
/* @internal */ getContextualTypeForObjectLiteralElement(element: ObjectLiteralElementLike): Type | undefined;
|
||||
/* @internal */ getContextualTypeForArgumentAtIndex(call: CallLikeExpression, argIndex: number): Type | undefined;
|
||||
/* @internal */ getContextualTypeForJsxAttribute(attribute: JsxAttribute | JsxSpreadAttribute): Type | undefined;
|
||||
@@ -3443,8 +3451,7 @@ namespace ts {
|
||||
resolvedReturnType: Type,
|
||||
typePredicate: TypePredicate | undefined,
|
||||
minArgumentCount: number,
|
||||
hasRestParameter: boolean,
|
||||
hasLiteralTypes: boolean,
|
||||
flags: SignatureFlags
|
||||
): Signature;
|
||||
/* @internal */ createSymbol(flags: SymbolFlags, name: __String): TransientSymbol;
|
||||
/* @internal */ createIndexInfo(type: Type, isReadonly: boolean, declaration?: SignatureDeclaration): IndexInfo;
|
||||
@@ -3526,6 +3533,14 @@ namespace ts {
|
||||
Subtype
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export const enum ContextFlags {
|
||||
None = 0,
|
||||
Signature = 1 << 0, // Obtaining contextual signature
|
||||
NoConstraints = 1 << 1, // Don't obtain type variable constraints
|
||||
Completion = 1 << 2, // Obtaining constraint type for completion
|
||||
}
|
||||
|
||||
// NOTE: If modifying this enum, must modify `TypeFormatFlags` too!
|
||||
export const enum NodeBuilderFlags {
|
||||
None = 0,
|
||||
@@ -4655,7 +4670,22 @@ namespace ts {
|
||||
Construct,
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export const enum SignatureFlags {
|
||||
None = 0,
|
||||
HasRestParameter = 1 << 0, // Indicates last parameter is rest parameter
|
||||
HasLiteralTypes = 1 << 1, // Indicates signature is specialized
|
||||
IsOptionalCall = 1 << 2, // Indicates signature comes from a CallChain
|
||||
|
||||
// We do not propagate `IsOptionalCall` to instantiated signatures, as that would result in us
|
||||
// attempting to add `| undefined` on each recursive call to `getReturnTypeOfSignature` when
|
||||
// instantiating the return type.
|
||||
PropagatingFlags = HasRestParameter | HasLiteralTypes,
|
||||
}
|
||||
|
||||
export interface Signature {
|
||||
/* @internal */ flags: SignatureFlags;
|
||||
/* @internal */ checker?: TypeChecker;
|
||||
declaration?: SignatureDeclaration | JSDocSignature; // Originating declaration
|
||||
typeParameters?: readonly TypeParameter[]; // Type parameters (undefined if non-generic)
|
||||
parameters: readonly Symbol[]; // Parameters
|
||||
@@ -4672,10 +4702,6 @@ namespace ts {
|
||||
/* @internal */
|
||||
minArgumentCount: number; // Number of non-optional parameters
|
||||
/* @internal */
|
||||
hasRestParameter: boolean; // True if last parameter is rest parameter
|
||||
/* @internal */
|
||||
hasLiteralTypes: boolean; // True if specialized
|
||||
/* @internal */
|
||||
target?: Signature; // Instantiation target
|
||||
/* @internal */
|
||||
mapper?: TypeMapper; // Instantiation mapper
|
||||
@@ -4686,11 +4712,11 @@ namespace ts {
|
||||
/* @internal */
|
||||
canonicalSignatureCache?: Signature; // Canonical version of signature (deferred)
|
||||
/* @internal */
|
||||
optionalCallSignatureCache?: Signature; // Optional chained call version of signature (deferred)
|
||||
/* @internal */
|
||||
isolatedSignatureType?: ObjectType; // A manufactured type that just contains the signature for purposes of signature comparison
|
||||
/* @internal */
|
||||
instantiations?: Map<Signature>; // Generic signature instantiation cache
|
||||
/* @internal */
|
||||
isOptionalCall?: boolean;
|
||||
}
|
||||
|
||||
export const enum IndexKind {
|
||||
@@ -4935,6 +4961,7 @@ namespace ts {
|
||||
lib?: string[];
|
||||
/*@internal*/listEmittedFiles?: boolean;
|
||||
/*@internal*/listFiles?: boolean;
|
||||
/*@internal*/listFilesOnly?: boolean;
|
||||
locale?: string;
|
||||
mapRoot?: string;
|
||||
maxNodeModuleJsDepth?: number;
|
||||
@@ -5123,6 +5150,11 @@ namespace ts {
|
||||
/* @internal */ spec: ConfigFileSpecs;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export type RequireResult<T = {}> =
|
||||
| { module: T, modulePath?: string, error: undefined }
|
||||
| { module: undefined, modulePath?: undefined, error: { stack?: string, message?: string } };
|
||||
|
||||
export interface CreateProgramOptions {
|
||||
rootNames: readonly string[];
|
||||
options: CompilerOptions;
|
||||
@@ -6149,6 +6181,8 @@ namespace ts {
|
||||
readFile?(path: string): string | undefined;
|
||||
/* @internal */
|
||||
getProbableSymlinks?(files: readonly SourceFile[]): ReadonlyMap<string>;
|
||||
/* @internal */
|
||||
getGlobalTypingsCacheLocation?(): string | undefined;
|
||||
}
|
||||
|
||||
// Note: this used to be deprecated in our public API, but is still used internally
|
||||
@@ -6418,6 +6452,7 @@ namespace ts {
|
||||
readonly disableSuggestions?: boolean;
|
||||
readonly quotePreference?: "auto" | "double" | "single";
|
||||
readonly includeCompletionsForModuleExports?: boolean;
|
||||
readonly includeAutomaticOptionalChainCompletions?: boolean;
|
||||
readonly includeCompletionsWithInsertText?: boolean;
|
||||
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
|
||||
/** Determines whether we import `foo/index.ts` as "foo", "foo/index", or "foo/index.js" */
|
||||
|
||||
+54
-646
@@ -36,7 +36,7 @@ namespace ts {
|
||||
|
||||
/** Create a new escaped identifier map. */
|
||||
export function createUnderscoreEscapedMap<T>(): UnderscoreEscapedMap<T> {
|
||||
return new MapCtr<T>() as UnderscoreEscapedMap<T>;
|
||||
return new Map<T>() as UnderscoreEscapedMap<T>;
|
||||
}
|
||||
|
||||
export function hasEntries(map: ReadonlyUnderscoreEscapedMap<any> | undefined): map is ReadonlyUnderscoreEscapedMap<any> {
|
||||
@@ -94,13 +94,6 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
export function toPath(fileName: string, basePath: string | undefined, getCanonicalFileName: (path: string) => string): Path {
|
||||
const nonCanonicalizedPath = isRootedDiskPath(fileName)
|
||||
? normalizePath(fileName)
|
||||
: getNormalizedAbsolutePath(fileName, basePath);
|
||||
return <Path>getCanonicalFileName(nonCanonicalizedPath);
|
||||
}
|
||||
|
||||
export function changesAffectModuleResolution(oldOptions: CompilerOptions, newOptions: CompilerOptions): boolean {
|
||||
return oldOptions.configFilePath !== newOptions.configFilePath ||
|
||||
optionsHaveModuleResolutionChanges(oldOptions, newOptions);
|
||||
@@ -968,6 +961,11 @@ namespace ts {
|
||||
break;
|
||||
case SyntaxKind.ArrowFunction:
|
||||
return getErrorSpanForArrowFunction(sourceFile, <ArrowFunction>node);
|
||||
case SyntaxKind.CaseClause:
|
||||
case SyntaxKind.DefaultClause:
|
||||
const start = skipTrivia(sourceFile.text, (<CaseOrDefaultClause>node).pos);
|
||||
const end = (<CaseOrDefaultClause>node).statements.length > 0 ? (<CaseOrDefaultClause>node).statements[0].pos : (<CaseOrDefaultClause>node).end;
|
||||
return createTextSpanFromBounds(start, end);
|
||||
}
|
||||
|
||||
if (errorNode === undefined) {
|
||||
@@ -1821,9 +1819,9 @@ namespace ts {
|
||||
* exactly one argument (of the form 'require("name")').
|
||||
* This function does not test if the node is in a JavaScript file or not.
|
||||
*/
|
||||
export function isRequireCall(callExpression: Node, checkArgumentIsStringLiteralLike: true): callExpression is RequireOrImportCall & { expression: Identifier, arguments: [StringLiteralLike] };
|
||||
export function isRequireCall(callExpression: Node, checkArgumentIsStringLiteralLike: boolean): callExpression is CallExpression;
|
||||
export function isRequireCall(callExpression: Node, checkArgumentIsStringLiteralLike: boolean): callExpression is CallExpression {
|
||||
export function isRequireCall(callExpression: Node, requireStringLiteralLikeArgument: true): callExpression is RequireOrImportCall & { expression: Identifier, arguments: [StringLiteralLike] };
|
||||
export function isRequireCall(callExpression: Node, requireStringLiteralLikeArgument: boolean): callExpression is CallExpression;
|
||||
export function isRequireCall(callExpression: Node, requireStringLiteralLikeArgument: boolean): callExpression is CallExpression {
|
||||
if (callExpression.kind !== SyntaxKind.CallExpression) {
|
||||
return false;
|
||||
}
|
||||
@@ -1837,7 +1835,7 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
const arg = args[0];
|
||||
return !checkArgumentIsStringLiteralLike || isStringLiteralLike(arg);
|
||||
return !requireStringLiteralLikeArgument || isStringLiteralLike(arg);
|
||||
}
|
||||
|
||||
export function isSingleOrDoubleQuote(charCode: number) {
|
||||
@@ -2060,7 +2058,7 @@ namespace ts {
|
||||
idText(expr.expression.expression) === "Object" &&
|
||||
idText(expr.expression.name) === "defineProperty" &&
|
||||
isStringOrNumericLiteralLike(expr.arguments[1]) &&
|
||||
isBindableStaticNameExpression(expr.arguments[0]);
|
||||
isBindableStaticNameExpression(expr.arguments[0], /*excludeThisKeyword*/ true);
|
||||
}
|
||||
|
||||
export function isBindableStaticElementAccessExpression(node: Node, excludeThisKeyword?: boolean): node is BindableStaticElementAccessExpression {
|
||||
@@ -2174,9 +2172,11 @@ namespace ts {
|
||||
nextToLast = nextToLast.expression as Exclude<BindableStaticNameExpression, Identifier>;
|
||||
}
|
||||
const id = nextToLast.expression;
|
||||
if (id.escapedText === "exports" ||
|
||||
id.escapedText === "module" && getElementOrPropertyAccessName(nextToLast) === "exports") {
|
||||
// exports.name = expr OR module.exports.name = expr
|
||||
if ((id.escapedText === "exports" ||
|
||||
id.escapedText === "module" && getElementOrPropertyAccessName(nextToLast) === "exports") &&
|
||||
// ExportsProperty does not support binding with computed names
|
||||
isBindableStaticAccessExpression(lhs)) {
|
||||
// exports.name = expr OR module.exports.name = expr OR exports["name"] = expr ...
|
||||
return AssignmentDeclarationKind.ExportsProperty;
|
||||
}
|
||||
// F.G...x = expr
|
||||
@@ -4179,6 +4179,23 @@ namespace ts {
|
||||
return node.kind === SyntaxKind.Identifier || isPropertyAccessEntityNameExpression(node);
|
||||
}
|
||||
|
||||
export function getFirstIdentifier(node: EntityNameOrEntityNameExpression): Identifier {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
return node;
|
||||
case SyntaxKind.QualifiedName:
|
||||
do {
|
||||
node = node.left;
|
||||
} while (node.kind !== SyntaxKind.Identifier);
|
||||
return node;
|
||||
case SyntaxKind.PropertyAccessExpression:
|
||||
do {
|
||||
node = node.expression;
|
||||
} while (node.kind !== SyntaxKind.Identifier);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
export function isDottedName(node: Expression): boolean {
|
||||
return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword ||
|
||||
node.kind === SyntaxKind.PropertyAccessExpression && isDottedName((<PropertyAccessExpression>node).expression) ||
|
||||
@@ -4733,25 +4750,6 @@ namespace ts {
|
||||
});
|
||||
}
|
||||
|
||||
/** Calls `callback` on `directory` and every ancestor directory it has, returning the first defined result. */
|
||||
export function forEachAncestorDirectory<T>(directory: Path, callback: (directory: Path) => T | undefined): T | undefined;
|
||||
export function forEachAncestorDirectory<T>(directory: string, callback: (directory: string) => T | undefined): T | undefined;
|
||||
export function forEachAncestorDirectory<T>(directory: Path, callback: (directory: Path) => T | undefined): T | undefined {
|
||||
while (true) {
|
||||
const result = callback(directory);
|
||||
if (result !== undefined) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const parentPath = getDirectoryPath(directory);
|
||||
if (parentPath === directory) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
directory = parentPath;
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if the given type is the constructor type for an abstract class
|
||||
export function isAbstractConstructorType(type: Type): boolean {
|
||||
return !!(getObjectFlags(type) & ObjectFlags.Anonymous) && !!type.symbol && isAbstractConstructorSymbol(type.symbol);
|
||||
@@ -5906,9 +5904,19 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function isOptionalChain(node: Node): node is PropertyAccessChain | ElementAccessChain | CallChain {
|
||||
return isPropertyAccessChain(node)
|
||||
|| isElementAccessChain(node)
|
||||
|| isCallChain(node);
|
||||
const kind = node.kind;
|
||||
return !!(node.flags & NodeFlags.OptionalChain) &&
|
||||
(kind === SyntaxKind.PropertyAccessExpression
|
||||
|| kind === SyntaxKind.ElementAccessExpression
|
||||
|| kind === SyntaxKind.CallExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a node is the expression preceding an optional chain (i.e. `a` in `a?.b`).
|
||||
*/
|
||||
/* @internal */
|
||||
export function isExpressionOfOptionalChainRoot(node: Node): node is Expression & { parent: OptionalChainRoot } {
|
||||
return isOptionalChainRoot(node.parent) && node.parent.expression === node;
|
||||
}
|
||||
|
||||
export function isNewExpression(node: Node): node is NewExpression {
|
||||
@@ -7310,7 +7318,7 @@ namespace ts {
|
||||
getSourceFileConstructor(): new (kind: SyntaxKind.SourceFile, pos?: number, end?: number) => SourceFile;
|
||||
getSymbolConstructor(): new (flags: SymbolFlags, name: __String) => Symbol;
|
||||
getTypeConstructor(): new (checker: TypeChecker, flags: TypeFlags) => Type;
|
||||
getSignatureConstructor(): new (checker: TypeChecker) => Signature;
|
||||
getSignatureConstructor(): new (checker: TypeChecker, flags: SignatureFlags) => Signature;
|
||||
getSourceMapSourceConstructor(): new (fileName: string, text: string, skipTrivia?: (pos: number) => number) => SourceMapSource;
|
||||
}
|
||||
|
||||
@@ -7331,7 +7339,12 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function Signature() {}
|
||||
function Signature(this: Signature, checker: TypeChecker, flags: SignatureFlags) {
|
||||
this.flags = flags;
|
||||
if (Debug.isDebugging) {
|
||||
this.checker = checker;
|
||||
}
|
||||
}
|
||||
|
||||
function Node(this: Node, kind: SyntaxKind, pos: number, end: number) {
|
||||
this.pos = pos;
|
||||
@@ -7635,294 +7648,6 @@ namespace ts {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internally, we represent paths as strings with '/' as the directory separator.
|
||||
* When we make system calls (eg: LanguageServiceHost.getDirectory()),
|
||||
* we expect the host to correctly handle paths in our specified format.
|
||||
*/
|
||||
export const directorySeparator = "/";
|
||||
const altDirectorySeparator = "\\";
|
||||
const urlSchemeSeparator = "://";
|
||||
const backslashRegExp = /\\/g;
|
||||
|
||||
/**
|
||||
* Normalize path separators.
|
||||
*/
|
||||
export function normalizeSlashes(path: string): string {
|
||||
return path.replace(backslashRegExp, directorySeparator);
|
||||
}
|
||||
|
||||
function isVolumeCharacter(charCode: number) {
|
||||
return (charCode >= CharacterCodes.a && charCode <= CharacterCodes.z) ||
|
||||
(charCode >= CharacterCodes.A && charCode <= CharacterCodes.Z);
|
||||
}
|
||||
|
||||
function getFileUrlVolumeSeparatorEnd(url: string, start: number) {
|
||||
const ch0 = url.charCodeAt(start);
|
||||
if (ch0 === CharacterCodes.colon) return start + 1;
|
||||
if (ch0 === CharacterCodes.percent && url.charCodeAt(start + 1) === CharacterCodes._3) {
|
||||
const ch2 = url.charCodeAt(start + 2);
|
||||
if (ch2 === CharacterCodes.a || ch2 === CharacterCodes.A) return start + 3;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns length of the root part of a path or URL (i.e. length of "/", "x:/", "//server/share/, file:///user/files").
|
||||
* If the root is part of a URL, the twos-complement of the root length is returned.
|
||||
*/
|
||||
function getEncodedRootLength(path: string): number {
|
||||
if (!path) return 0;
|
||||
const ch0 = path.charCodeAt(0);
|
||||
|
||||
// POSIX or UNC
|
||||
if (ch0 === CharacterCodes.slash || ch0 === CharacterCodes.backslash) {
|
||||
if (path.charCodeAt(1) !== ch0) return 1; // POSIX: "/" (or non-normalized "\")
|
||||
|
||||
const p1 = path.indexOf(ch0 === CharacterCodes.slash ? directorySeparator : altDirectorySeparator, 2);
|
||||
if (p1 < 0) return path.length; // UNC: "//server" or "\\server"
|
||||
|
||||
return p1 + 1; // UNC: "//server/" or "\\server\"
|
||||
}
|
||||
|
||||
// DOS
|
||||
if (isVolumeCharacter(ch0) && path.charCodeAt(1) === CharacterCodes.colon) {
|
||||
const ch2 = path.charCodeAt(2);
|
||||
if (ch2 === CharacterCodes.slash || ch2 === CharacterCodes.backslash) return 3; // DOS: "c:/" or "c:\"
|
||||
if (path.length === 2) return 2; // DOS: "c:" (but not "c:d")
|
||||
}
|
||||
|
||||
// URL
|
||||
const schemeEnd = path.indexOf(urlSchemeSeparator);
|
||||
if (schemeEnd !== -1) {
|
||||
const authorityStart = schemeEnd + urlSchemeSeparator.length;
|
||||
const authorityEnd = path.indexOf(directorySeparator, authorityStart);
|
||||
if (authorityEnd !== -1) { // URL: "file:///", "file://server/", "file://server/path"
|
||||
// For local "file" URLs, include the leading DOS volume (if present).
|
||||
// Per https://www.ietf.org/rfc/rfc1738.txt, a host of "" or "localhost" is a
|
||||
// special case interpreted as "the machine from which the URL is being interpreted".
|
||||
const scheme = path.slice(0, schemeEnd);
|
||||
const authority = path.slice(authorityStart, authorityEnd);
|
||||
if (scheme === "file" && (authority === "" || authority === "localhost") &&
|
||||
isVolumeCharacter(path.charCodeAt(authorityEnd + 1))) {
|
||||
const volumeSeparatorEnd = getFileUrlVolumeSeparatorEnd(path, authorityEnd + 2);
|
||||
if (volumeSeparatorEnd !== -1) {
|
||||
if (path.charCodeAt(volumeSeparatorEnd) === CharacterCodes.slash) {
|
||||
// URL: "file:///c:/", "file://localhost/c:/", "file:///c%3a/", "file://localhost/c%3a/"
|
||||
return ~(volumeSeparatorEnd + 1);
|
||||
}
|
||||
if (volumeSeparatorEnd === path.length) {
|
||||
// URL: "file:///c:", "file://localhost/c:", "file:///c$3a", "file://localhost/c%3a"
|
||||
// but not "file:///c:d" or "file:///c%3ad"
|
||||
return ~volumeSeparatorEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ~(authorityEnd + 1); // URL: "file://server/", "http://server/"
|
||||
}
|
||||
return ~path.length; // URL: "file://server", "http://server"
|
||||
}
|
||||
|
||||
// relative
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns length of the root part of a path or URL (i.e. length of "/", "x:/", "//server/share/, file:///user/files").
|
||||
*
|
||||
* For example:
|
||||
* ```ts
|
||||
* getRootLength("a") === 0 // ""
|
||||
* getRootLength("/") === 1 // "/"
|
||||
* getRootLength("c:") === 2 // "c:"
|
||||
* getRootLength("c:d") === 0 // ""
|
||||
* getRootLength("c:/") === 3 // "c:/"
|
||||
* getRootLength("c:\\") === 3 // "c:\\"
|
||||
* getRootLength("//server") === 7 // "//server"
|
||||
* getRootLength("//server/share") === 8 // "//server/"
|
||||
* getRootLength("\\\\server") === 7 // "\\\\server"
|
||||
* getRootLength("\\\\server\\share") === 8 // "\\\\server\\"
|
||||
* getRootLength("file:///path") === 8 // "file:///"
|
||||
* getRootLength("file:///c:") === 10 // "file:///c:"
|
||||
* getRootLength("file:///c:d") === 8 // "file:///"
|
||||
* getRootLength("file:///c:/path") === 11 // "file:///c:/"
|
||||
* getRootLength("file://server") === 13 // "file://server"
|
||||
* getRootLength("file://server/path") === 14 // "file://server/"
|
||||
* getRootLength("http://server") === 13 // "http://server"
|
||||
* getRootLength("http://server/path") === 14 // "http://server/"
|
||||
* ```
|
||||
*/
|
||||
export function getRootLength(path: string) {
|
||||
const rootLength = getEncodedRootLength(path);
|
||||
return rootLength < 0 ? ~rootLength : rootLength;
|
||||
}
|
||||
|
||||
// TODO(rbuckton): replace references with `resolvePath`
|
||||
export function normalizePath(path: string): string {
|
||||
return resolvePath(path);
|
||||
}
|
||||
|
||||
export function normalizePathAndParts(path: string): { path: string, parts: string[] } {
|
||||
path = normalizeSlashes(path);
|
||||
const [root, ...parts] = reducePathComponents(getPathComponents(path));
|
||||
if (parts.length) {
|
||||
const joinedParts = root + parts.join(directorySeparator);
|
||||
return { path: hasTrailingDirectorySeparator(path) ? ensureTrailingDirectorySeparator(joinedParts) : joinedParts, parts };
|
||||
}
|
||||
else {
|
||||
return { path: root, parts };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path except for its basename. Semantics align with NodeJS's `path.dirname`
|
||||
* except that we support URLs as well.
|
||||
*
|
||||
* ```ts
|
||||
* getDirectoryPath("/path/to/file.ext") === "/path/to"
|
||||
* getDirectoryPath("/path/to/") === "/path"
|
||||
* getDirectoryPath("/") === "/"
|
||||
* ```
|
||||
*/
|
||||
export function getDirectoryPath(path: Path): Path;
|
||||
/**
|
||||
* Returns the path except for its basename. Semantics align with NodeJS's `path.dirname`
|
||||
* except that we support URLs as well.
|
||||
*
|
||||
* ```ts
|
||||
* getDirectoryPath("/path/to/file.ext") === "/path/to"
|
||||
* getDirectoryPath("/path/to/") === "/path"
|
||||
* getDirectoryPath("/") === "/"
|
||||
* ```
|
||||
*/
|
||||
export function getDirectoryPath(path: string): string;
|
||||
export function getDirectoryPath(path: string): string {
|
||||
path = normalizeSlashes(path);
|
||||
|
||||
// If the path provided is itself the root, then return it.
|
||||
const rootLength = getRootLength(path);
|
||||
if (rootLength === path.length) return path;
|
||||
|
||||
// return the leading portion of the path up to the last (non-terminal) directory separator
|
||||
// but not including any trailing directory separator.
|
||||
path = removeTrailingDirectorySeparator(path);
|
||||
return path.slice(0, Math.max(rootLength, path.lastIndexOf(directorySeparator)));
|
||||
}
|
||||
|
||||
export function startsWithDirectory(fileName: string, directoryName: string, getCanonicalFileName: GetCanonicalFileName): boolean {
|
||||
const canonicalFileName = getCanonicalFileName(fileName);
|
||||
const canonicalDirectoryName = getCanonicalFileName(directoryName);
|
||||
return startsWith(canonicalFileName, canonicalDirectoryName + "/") || startsWith(canonicalFileName, canonicalDirectoryName + "\\");
|
||||
}
|
||||
|
||||
export function isUrl(path: string) {
|
||||
return getEncodedRootLength(path) < 0;
|
||||
}
|
||||
|
||||
export function pathIsRelative(path: string): boolean {
|
||||
return /^\.\.?($|[\\/])/.test(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a path is an absolute path (e.g. starts with `/`, or a dos path
|
||||
* like `c:`, `c:\` or `c:/`).
|
||||
*/
|
||||
export function isRootedDiskPath(path: string) {
|
||||
return getEncodedRootLength(path) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a path consists only of a path root.
|
||||
*/
|
||||
export function isDiskPathRoot(path: string) {
|
||||
const rootLength = getEncodedRootLength(path);
|
||||
return rootLength > 0 && rootLength === path.length;
|
||||
}
|
||||
|
||||
export function convertToRelativePath(absoluteOrRelativePath: string, basePath: string, getCanonicalFileName: (path: string) => string): string {
|
||||
return !isRootedDiskPath(absoluteOrRelativePath)
|
||||
? absoluteOrRelativePath
|
||||
: getRelativePathToDirectoryOrUrl(basePath, absoluteOrRelativePath, basePath, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
|
||||
}
|
||||
|
||||
function pathComponents(path: string, rootLength: number) {
|
||||
const root = path.substring(0, rootLength);
|
||||
const rest = path.substring(rootLength).split(directorySeparator);
|
||||
if (rest.length && !lastOrUndefined(rest)) rest.pop();
|
||||
return [root, ...rest];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a path into an array containing a root component (at index 0) and zero or more path
|
||||
* components (at indices > 0). The result is not normalized.
|
||||
* If the path is relative, the root component is `""`.
|
||||
* If the path is absolute, the root component includes the first path separator (`/`).
|
||||
*/
|
||||
export function getPathComponents(path: string, currentDirectory = "") {
|
||||
path = combinePaths(currentDirectory, path);
|
||||
const rootLength = getRootLength(path);
|
||||
return pathComponents(path, rootLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce an array of path components to a more simplified path by navigating any
|
||||
* `"."` or `".."` entries in the path.
|
||||
*/
|
||||
export function reducePathComponents(components: readonly string[]) {
|
||||
if (!some(components)) return [];
|
||||
const reduced = [components[0]];
|
||||
for (let i = 1; i < components.length; i++) {
|
||||
const component = components[i];
|
||||
if (!component) continue;
|
||||
if (component === ".") continue;
|
||||
if (component === "..") {
|
||||
if (reduced.length > 1) {
|
||||
if (reduced[reduced.length - 1] !== "..") {
|
||||
reduced.pop();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (reduced[0]) continue;
|
||||
}
|
||||
reduced.push(component);
|
||||
}
|
||||
return reduced;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a path into an array containing a root component (at index 0) and zero or more path
|
||||
* components (at indices > 0). The result is normalized.
|
||||
* If the path is relative, the root component is `""`.
|
||||
* If the path is absolute, the root component includes the first path separator (`/`).
|
||||
*/
|
||||
export function getNormalizedPathComponents(path: string, currentDirectory: string | undefined) {
|
||||
return reducePathComponents(getPathComponents(path, currentDirectory));
|
||||
}
|
||||
|
||||
export function getNormalizedAbsolutePath(fileName: string, currentDirectory: string | undefined) {
|
||||
return getPathFromPathComponents(getNormalizedPathComponents(fileName, currentDirectory));
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a parsed path consisting of a root component (at index 0) and zero or more path
|
||||
* segments (at indices > 0).
|
||||
*/
|
||||
export function getPathFromPathComponents(pathComponents: readonly string[]) {
|
||||
if (pathComponents.length === 0) return "";
|
||||
|
||||
const root = pathComponents[0] && ensureTrailingDirectorySeparator(pathComponents[0]);
|
||||
return root + pathComponents.slice(1).join(directorySeparator);
|
||||
}
|
||||
|
||||
export function getNormalizedAbsolutePathWithoutRoot(fileName: string, currentDirectory: string | undefined) {
|
||||
return getPathWithoutRoot(getNormalizedPathComponents(fileName, currentDirectory));
|
||||
}
|
||||
|
||||
function getPathWithoutRoot(pathComponents: readonly string[]) {
|
||||
if (pathComponents.length === 0) return "";
|
||||
return pathComponents.slice(1).join(directorySeparator);
|
||||
}
|
||||
|
||||
export function discoverProbableSymlinks(files: readonly SourceFile[], getCanonicalFileName: GetCanonicalFileName, cwd: string): ReadonlyMap<string> {
|
||||
const result = createMap<string>();
|
||||
const symlinks = flatten<readonly [string, string]>(mapDefined(files, sf =>
|
||||
@@ -7956,278 +7681,8 @@ namespace ts {
|
||||
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
export function getPathComponentsRelativeTo(from: string, to: string, stringEqualityComparer: (a: string, b: string) => boolean, getCanonicalFileName: GetCanonicalFileName) {
|
||||
const fromComponents = reducePathComponents(getPathComponents(from));
|
||||
const toComponents = reducePathComponents(getPathComponents(to));
|
||||
|
||||
let start: number;
|
||||
for (start = 0; start < fromComponents.length && start < toComponents.length; start++) {
|
||||
const fromComponent = getCanonicalFileName(fromComponents[start]);
|
||||
const toComponent = getCanonicalFileName(toComponents[start]);
|
||||
const comparer = start === 0 ? equateStringsCaseInsensitive : stringEqualityComparer;
|
||||
if (!comparer(fromComponent, toComponent)) break;
|
||||
}
|
||||
|
||||
if (start === 0) {
|
||||
return toComponents;
|
||||
}
|
||||
|
||||
const components = toComponents.slice(start);
|
||||
const relative: string[] = [];
|
||||
for (; start < fromComponents.length; start++) {
|
||||
relative.push("..");
|
||||
}
|
||||
return ["", ...relative, ...components];
|
||||
}
|
||||
|
||||
export function getRelativePathFromFile(from: string, to: string, getCanonicalFileName: GetCanonicalFileName) {
|
||||
return ensurePathIsNonModuleName(getRelativePathFromDirectory(getDirectoryPath(from), to, getCanonicalFileName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a relative path that can be used to traverse between `from` and `to`.
|
||||
*/
|
||||
export function getRelativePathFromDirectory(from: string, to: string, ignoreCase: boolean): string;
|
||||
/**
|
||||
* Gets a relative path that can be used to traverse between `from` and `to`.
|
||||
*/
|
||||
export function getRelativePathFromDirectory(fromDirectory: string, to: string, getCanonicalFileName: GetCanonicalFileName): string; // eslint-disable-line @typescript-eslint/unified-signatures
|
||||
export function getRelativePathFromDirectory(fromDirectory: string, to: string, getCanonicalFileNameOrIgnoreCase: GetCanonicalFileName | boolean) {
|
||||
Debug.assert((getRootLength(fromDirectory) > 0) === (getRootLength(to) > 0), "Paths must either both be absolute or both be relative");
|
||||
const getCanonicalFileName = typeof getCanonicalFileNameOrIgnoreCase === "function" ? getCanonicalFileNameOrIgnoreCase : identity;
|
||||
const ignoreCase = typeof getCanonicalFileNameOrIgnoreCase === "boolean" ? getCanonicalFileNameOrIgnoreCase : false;
|
||||
const pathComponents = getPathComponentsRelativeTo(fromDirectory, to, ignoreCase ? equateStringsCaseInsensitive : equateStringsCaseSensitive, getCanonicalFileName);
|
||||
return getPathFromPathComponents(pathComponents);
|
||||
}
|
||||
|
||||
export function getRelativePathToDirectoryOrUrl(directoryPathOrUrl: string, relativeOrAbsolutePath: string, currentDirectory: string, getCanonicalFileName: GetCanonicalFileName, isAbsolutePathAnUrl: boolean) {
|
||||
const pathComponents = getPathComponentsRelativeTo(
|
||||
resolvePath(currentDirectory, directoryPathOrUrl),
|
||||
resolvePath(currentDirectory, relativeOrAbsolutePath),
|
||||
equateStringsCaseSensitive,
|
||||
getCanonicalFileName
|
||||
);
|
||||
|
||||
const firstComponent = pathComponents[0];
|
||||
if (isAbsolutePathAnUrl && isRootedDiskPath(firstComponent)) {
|
||||
const prefix = firstComponent.charAt(0) === directorySeparator ? "file://" : "file:///";
|
||||
pathComponents[0] = prefix + firstComponent;
|
||||
}
|
||||
|
||||
return getPathFromPathComponents(pathComponents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures a path is either absolute (prefixed with `/` or `c:`) or dot-relative (prefixed
|
||||
* with `./` or `../`) so as not to be confused with an unprefixed module name.
|
||||
*/
|
||||
export function ensurePathIsNonModuleName(path: string): string {
|
||||
return getRootLength(path) === 0 && !pathIsRelative(path) ? "./" + path : path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path except for its containing directory name.
|
||||
* Semantics align with NodeJS's `path.basename` except that we support URL's as well.
|
||||
*
|
||||
* ```ts
|
||||
* getBaseFileName("/path/to/file.ext") === "file.ext"
|
||||
* getBaseFileName("/path/to/") === "to"
|
||||
* getBaseFileName("/") === ""
|
||||
* ```
|
||||
*/
|
||||
export function getBaseFileName(path: string): string;
|
||||
/**
|
||||
* Gets the portion of a path following the last (non-terminal) separator (`/`).
|
||||
* Semantics align with NodeJS's `path.basename` except that we support URL's as well.
|
||||
* If the base name has any one of the provided extensions, it is removed.
|
||||
*
|
||||
* ```ts
|
||||
* getBaseFileName("/path/to/file.ext", ".ext", true) === "file"
|
||||
* getBaseFileName("/path/to/file.js", ".ext", true) === "file.js"
|
||||
* ```
|
||||
*/
|
||||
export function getBaseFileName(path: string, extensions: string | readonly string[], ignoreCase: boolean): string;
|
||||
export function getBaseFileName(path: string, extensions?: string | readonly string[], ignoreCase?: boolean) {
|
||||
path = normalizeSlashes(path);
|
||||
|
||||
// if the path provided is itself the root, then it has not file name.
|
||||
const rootLength = getRootLength(path);
|
||||
if (rootLength === path.length) return "";
|
||||
|
||||
// return the trailing portion of the path starting after the last (non-terminal) directory
|
||||
// separator but not including any trailing directory separator.
|
||||
path = removeTrailingDirectorySeparator(path);
|
||||
const name = path.slice(Math.max(getRootLength(path), path.lastIndexOf(directorySeparator) + 1));
|
||||
const extension = extensions !== undefined && ignoreCase !== undefined ? getAnyExtensionFromPath(name, extensions, ignoreCase) : undefined;
|
||||
return extension ? name.slice(0, name.length - extension.length) : name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines paths. If a path is absolute, it replaces any previous path.
|
||||
*/
|
||||
export function combinePaths(path: string, ...paths: (string | undefined)[]): string {
|
||||
if (path) path = normalizeSlashes(path);
|
||||
for (let relativePath of paths) {
|
||||
if (!relativePath) continue;
|
||||
relativePath = normalizeSlashes(relativePath);
|
||||
if (!path || getRootLength(relativePath) !== 0) {
|
||||
path = relativePath;
|
||||
}
|
||||
else {
|
||||
path = ensureTrailingDirectorySeparator(path) + relativePath;
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines and resolves paths. If a path is absolute, it replaces any previous path. Any
|
||||
* `.` and `..` path components are resolved.
|
||||
*/
|
||||
export function resolvePath(path: string, ...paths: (string | undefined)[]): string {
|
||||
const combined = some(paths) ? combinePaths(path, ...paths) : normalizeSlashes(path);
|
||||
const normalized = getPathFromPathComponents(reducePathComponents(getPathComponents(combined)));
|
||||
return normalized && hasTrailingDirectorySeparator(combined) ? ensureTrailingDirectorySeparator(normalized) : normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a path has a trailing separator (`/` or `\\`).
|
||||
*/
|
||||
export function hasTrailingDirectorySeparator(path: string) {
|
||||
if (path.length === 0) return false;
|
||||
const ch = path.charCodeAt(path.length - 1);
|
||||
return ch === CharacterCodes.slash || ch === CharacterCodes.backslash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a trailing directory separator from a path.
|
||||
* @param path The path.
|
||||
*/
|
||||
export function removeTrailingDirectorySeparator(path: Path): Path;
|
||||
export function removeTrailingDirectorySeparator(path: string): string;
|
||||
export function removeTrailingDirectorySeparator(path: string) {
|
||||
if (hasTrailingDirectorySeparator(path)) {
|
||||
return path.substr(0, path.length - 1);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a trailing directory separator to a path, if it does not already have one.
|
||||
* @param path The path.
|
||||
*/
|
||||
export function ensureTrailingDirectorySeparator(path: Path): Path;
|
||||
export function ensureTrailingDirectorySeparator(path: string): string;
|
||||
export function ensureTrailingDirectorySeparator(path: string) {
|
||||
if (!hasTrailingDirectorySeparator(path)) {
|
||||
return path + directorySeparator;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
// check path for these segments: '', '.'. '..'
|
||||
const relativePathSegmentRegExp = /(^|\/)\.{0,2}($|\/)/;
|
||||
|
||||
function comparePathsWorker(a: string, b: string, componentComparer: (a: string, b: string) => Comparison) {
|
||||
if (a === b) return Comparison.EqualTo;
|
||||
if (a === undefined) return Comparison.LessThan;
|
||||
if (b === undefined) return Comparison.GreaterThan;
|
||||
|
||||
// NOTE: Performance optimization - shortcut if the root segments differ as there would be no
|
||||
// need to perform path reduction.
|
||||
const aRoot = a.substring(0, getRootLength(a));
|
||||
const bRoot = b.substring(0, getRootLength(b));
|
||||
const result = compareStringsCaseInsensitive(aRoot, bRoot);
|
||||
if (result !== Comparison.EqualTo) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Performance optimization - shortcut if there are no relative path segments in
|
||||
// the non-root portion of the path
|
||||
const aRest = a.substring(aRoot.length);
|
||||
const bRest = b.substring(bRoot.length);
|
||||
if (!relativePathSegmentRegExp.test(aRest) && !relativePathSegmentRegExp.test(bRest)) {
|
||||
return componentComparer(aRest, bRest);
|
||||
}
|
||||
|
||||
// The path contains a relative path segment. Normalize the paths and perform a slower component
|
||||
// by component comparison.
|
||||
const aComponents = reducePathComponents(getPathComponents(a));
|
||||
const bComponents = reducePathComponents(getPathComponents(b));
|
||||
const sharedLength = Math.min(aComponents.length, bComponents.length);
|
||||
for (let i = 1; i < sharedLength; i++) {
|
||||
const result = componentComparer(aComponents[i], bComponents[i]);
|
||||
if (result !== Comparison.EqualTo) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return compareValues(aComponents.length, bComponents.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a case-sensitive comparison of two paths.
|
||||
*/
|
||||
export function comparePathsCaseSensitive(a: string, b: string) {
|
||||
return comparePathsWorker(a, b, compareStringsCaseSensitive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a case-insensitive comparison of two paths.
|
||||
*/
|
||||
export function comparePathsCaseInsensitive(a: string, b: string) {
|
||||
return comparePathsWorker(a, b, compareStringsCaseInsensitive);
|
||||
}
|
||||
|
||||
export function comparePaths(a: string, b: string, ignoreCase?: boolean): Comparison;
|
||||
export function comparePaths(a: string, b: string, currentDirectory: string, ignoreCase?: boolean): Comparison;
|
||||
export function comparePaths(a: string, b: string, currentDirectory?: string | boolean, ignoreCase?: boolean) {
|
||||
if (typeof currentDirectory === "string") {
|
||||
a = combinePaths(currentDirectory, a);
|
||||
b = combinePaths(currentDirectory, b);
|
||||
}
|
||||
else if (typeof currentDirectory === "boolean") {
|
||||
ignoreCase = currentDirectory;
|
||||
}
|
||||
return comparePathsWorker(a, b, getStringComparer(ignoreCase));
|
||||
}
|
||||
|
||||
export function containsPath(parent: string, child: string, ignoreCase?: boolean): boolean;
|
||||
export function containsPath(parent: string, child: string, currentDirectory: string, ignoreCase?: boolean): boolean;
|
||||
export function containsPath(parent: string, child: string, currentDirectory?: string | boolean, ignoreCase?: boolean) {
|
||||
if (typeof currentDirectory === "string") {
|
||||
parent = combinePaths(currentDirectory, parent);
|
||||
child = combinePaths(currentDirectory, child);
|
||||
}
|
||||
else if (typeof currentDirectory === "boolean") {
|
||||
ignoreCase = currentDirectory;
|
||||
}
|
||||
if (parent === undefined || child === undefined) return false;
|
||||
if (parent === child) return true;
|
||||
const parentComponents = reducePathComponents(getPathComponents(parent));
|
||||
const childComponents = reducePathComponents(getPathComponents(child));
|
||||
if (childComponents.length < parentComponents.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const componentEqualityComparer = ignoreCase ? equateStringsCaseInsensitive : equateStringsCaseSensitive;
|
||||
for (let i = 0; i < parentComponents.length; i++) {
|
||||
const equalityComparer = i === 0 ? equateStringsCaseInsensitive : componentEqualityComparer;
|
||||
if (!equalityComparer(parentComponents[i], childComponents[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function isDirectorySeparator(charCode: number): boolean {
|
||||
return charCode === CharacterCodes.slash || charCode === CharacterCodes.backslash;
|
||||
}
|
||||
|
||||
function stripLeadingDirectorySeparator(s: string): string | undefined {
|
||||
return isDirectorySeparator(s.charCodeAt(0)) ? s.slice(1) : undefined;
|
||||
return isAnyDirectorySeparator(s.charCodeAt(0)) ? s.slice(1) : undefined;
|
||||
}
|
||||
|
||||
export function tryRemoveDirectoryPrefix(path: string, dirPath: string, getCanonicalFileName: GetCanonicalFileName): string | undefined {
|
||||
@@ -8250,10 +7705,6 @@ namespace ts {
|
||||
|
||||
const wildcardCharCodes = [CharacterCodes.asterisk, CharacterCodes.question];
|
||||
|
||||
export function hasExtension(fileName: string): boolean {
|
||||
return stringContains(getBaseFileName(fileName), ".");
|
||||
}
|
||||
|
||||
export const commonPackageFolders: readonly string[] = ["node_modules", "bower_components", "jspm_packages"];
|
||||
|
||||
const implicitExcludePathRegexPattern = `(?!(${commonPackageFolders.join("|")})(/|$))`;
|
||||
@@ -8718,13 +8169,6 @@ namespace ts {
|
||||
return <T>changeAnyExtension(path, newExtension, extensionsToRemove, /*ignoreCase*/ false);
|
||||
}
|
||||
|
||||
export function changeAnyExtension(path: string, ext: string): string;
|
||||
export function changeAnyExtension(path: string, ext: string, extensions: string | readonly string[], ignoreCase: boolean): string;
|
||||
export function changeAnyExtension(path: string, ext: string, extensions?: string | readonly string[], ignoreCase?: boolean) {
|
||||
const pathext = extensions !== undefined && ignoreCase !== undefined ? getAnyExtensionFromPath(path, extensions, ignoreCase) : getAnyExtensionFromPath(path);
|
||||
return pathext ? path.slice(0, path.length - pathext.length) + (startsWith(ext, ".") ? ext : "." + ext) : path;
|
||||
}
|
||||
|
||||
export function tryParsePattern(pattern: string): Pattern | undefined {
|
||||
// This should be verified outside of here and a proper error thrown.
|
||||
Debug.assert(hasZeroOrOneAsteriskCharacter(pattern));
|
||||
@@ -8767,42 +8211,6 @@ namespace ts {
|
||||
return find<Extension>(extensionsToRemove, e => fileExtensionIs(path, e));
|
||||
}
|
||||
|
||||
function getAnyExtensionFromPathWorker(path: string, extensions: string | readonly string[], stringEqualityComparer: (a: string, b: string) => boolean) {
|
||||
if (typeof extensions === "string") extensions = [extensions];
|
||||
for (let extension of extensions) {
|
||||
if (!startsWith(extension, ".")) extension = "." + extension;
|
||||
if (path.length >= extension.length && path.charAt(path.length - extension.length) === ".") {
|
||||
const pathExtension = path.slice(path.length - extension.length);
|
||||
if (stringEqualityComparer(pathExtension, extension)) {
|
||||
return pathExtension;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file extension for a path.
|
||||
*/
|
||||
export function getAnyExtensionFromPath(path: string): string;
|
||||
/**
|
||||
* Gets the file extension for a path, provided it is one of the provided extensions.
|
||||
*/
|
||||
export function getAnyExtensionFromPath(path: string, extensions: string | readonly string[], ignoreCase: boolean): string;
|
||||
export function getAnyExtensionFromPath(path: string, extensions?: string | readonly string[], ignoreCase?: boolean): string {
|
||||
// Retrieves any string from the final "." onwards from a base file name.
|
||||
// Unlike extensionFromPath, which throws an exception on unrecognized extensions.
|
||||
if (extensions) {
|
||||
return getAnyExtensionFromPathWorker(path, extensions, ignoreCase ? equateStringsCaseInsensitive : equateStringsCaseSensitive);
|
||||
}
|
||||
const baseFileName = getBaseFileName(path);
|
||||
const extensionIndex = baseFileName.lastIndexOf(".");
|
||||
if (extensionIndex >= 0) {
|
||||
return baseFileName.substring(extensionIndex);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
export function isCheckJsEnabledForFile(sourceFile: SourceFile, compilerOptions: CompilerOptions) {
|
||||
return sourceFile.checkJsDirective ? sourceFile.checkJsDirective.enabled : compilerOptions.checkJs;
|
||||
}
|
||||
|
||||
+13
-6
@@ -91,7 +91,7 @@ namespace ts {
|
||||
/** Parses config file using System interface */
|
||||
export function parseConfigFileWithSystem(configFileName: string, optionsToExtend: CompilerOptions, system: System, reportDiagnostic: DiagnosticReporter) {
|
||||
const host: ParseConfigFileHost = <any>system;
|
||||
host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(sys, reportDiagnostic, diagnostic);
|
||||
host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(system, reportDiagnostic, diagnostic);
|
||||
const result = getParsedCommandLineOfConfigFile(configFileName, optionsToExtend, host);
|
||||
host.onUnRecoverableConfigFileDiagnostic = undefined!; // TODO: GH#18217
|
||||
return result;
|
||||
@@ -129,7 +129,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function listFiles(program: ProgramToEmitFilesAndReportErrors, writeFileName: (s: string) => void) {
|
||||
if (program.getCompilerOptions().listFiles) {
|
||||
if (program.getCompilerOptions().listFiles || program.getCompilerOptions().listFilesOnly) {
|
||||
forEach(program.getSourceFiles(), file => {
|
||||
writeFileName(file.fileName);
|
||||
});
|
||||
@@ -149,6 +149,8 @@ namespace ts {
|
||||
emitOnlyDtsFiles?: boolean,
|
||||
customTransformers?: CustomTransformers
|
||||
) {
|
||||
const isListFilesOnly = !!program.getCompilerOptions().listFilesOnly;
|
||||
|
||||
// First get and report any syntactic errors.
|
||||
const diagnostics = program.getConfigFileParsingDiagnostics().slice();
|
||||
const configFileParsingDiagnosticsLength = diagnostics.length;
|
||||
@@ -158,15 +160,20 @@ namespace ts {
|
||||
// semantic errors.
|
||||
if (diagnostics.length === configFileParsingDiagnosticsLength) {
|
||||
addRange(diagnostics, program.getOptionsDiagnostics(cancellationToken));
|
||||
addRange(diagnostics, program.getGlobalDiagnostics(cancellationToken));
|
||||
|
||||
if (diagnostics.length === configFileParsingDiagnosticsLength) {
|
||||
addRange(diagnostics, program.getSemanticDiagnostics(/*sourceFile*/ undefined, cancellationToken));
|
||||
if (!isListFilesOnly) {
|
||||
addRange(diagnostics, program.getGlobalDiagnostics(cancellationToken));
|
||||
|
||||
if (diagnostics.length === configFileParsingDiagnosticsLength) {
|
||||
addRange(diagnostics, program.getSemanticDiagnostics(/*sourceFile*/ undefined, cancellationToken));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Emit and report any errors we ran into.
|
||||
const emitResult = program.emit(/*targetSourceFile*/ undefined, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
|
||||
const emitResult = isListFilesOnly
|
||||
? { emitSkipped: true, diagnostics: emptyArray }
|
||||
: program.emit(/*targetSourceFile*/ undefined, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
|
||||
const { emittedFiles, diagnostics: emitDiagnostics } = emitResult;
|
||||
addRange(diagnostics, emitDiagnostics);
|
||||
|
||||
|
||||
@@ -0,0 +1,499 @@
|
||||
/// <reference lib="es2019" />
|
||||
|
||||
/* @internal */
|
||||
namespace Debug {
|
||||
interface Node {
|
||||
kind: number;
|
||||
}
|
||||
|
||||
type FunctionExpression = Node;
|
||||
type ArrowFunction = Node;
|
||||
type MethodDeclaration = Node;
|
||||
type Expression = Node;
|
||||
type SourceFile = Node;
|
||||
|
||||
interface SwitchStatement extends Node {
|
||||
caseBlock: CaseBlock;
|
||||
}
|
||||
|
||||
interface CaseBlock extends Node {
|
||||
clauses: (CaseClause | DefaultClause)[];
|
||||
}
|
||||
|
||||
interface CaseClause extends Node {
|
||||
_caseclauseBrand: any;
|
||||
expression: Expression;
|
||||
}
|
||||
|
||||
interface DefaultClause extends Node {
|
||||
_defaultClauseBrand: any;
|
||||
}
|
||||
|
||||
interface TypeScriptModule {
|
||||
readonly SyntaxKind: {
|
||||
readonly CaseClause: number;
|
||||
readonly DefaultClause: number;
|
||||
};
|
||||
|
||||
readonly FlowFlags: {
|
||||
readonly Unreachable: number,
|
||||
readonly Start: number,
|
||||
readonly BranchLabel: number,
|
||||
readonly LoopLabel: number,
|
||||
readonly Assignment: number,
|
||||
readonly TrueCondition: number,
|
||||
readonly FalseCondition: number,
|
||||
readonly SwitchClause: number,
|
||||
readonly ArrayMutation: number,
|
||||
readonly Call: number,
|
||||
readonly Referenced: number,
|
||||
readonly Shared: number,
|
||||
readonly PreFinally: number,
|
||||
readonly AfterFinally: number,
|
||||
readonly Label: number,
|
||||
readonly Condition: number,
|
||||
};
|
||||
|
||||
getSourceFileOfNode(node: Node): SourceFile;
|
||||
getSourceTextOfNodeFromSourceFile(sourceFile: SourceFile, node: Node, includeTrivia?: boolean): string;
|
||||
isDefaultClause(node: Node): node is DefaultClause;
|
||||
}
|
||||
|
||||
type FlowNode =
|
||||
| AfterFinallyFlow
|
||||
| PreFinallyFlow
|
||||
| FlowStart
|
||||
| FlowLabel
|
||||
| FlowAssignment
|
||||
| FlowCall
|
||||
| FlowCondition
|
||||
| FlowSwitchClause
|
||||
| FlowArrayMutation
|
||||
;
|
||||
|
||||
interface FlowNodeBase {
|
||||
flags: FlowFlags;
|
||||
id?: number;
|
||||
}
|
||||
|
||||
interface AfterFinallyFlow extends FlowNodeBase {
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
|
||||
interface PreFinallyFlow extends FlowNodeBase {
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
|
||||
interface FlowStart extends FlowNodeBase {
|
||||
node?: FunctionExpression | ArrowFunction | MethodDeclaration;
|
||||
}
|
||||
|
||||
interface FlowLabel extends FlowNodeBase {
|
||||
antecedents: FlowNode[] | undefined;
|
||||
}
|
||||
|
||||
interface FlowAssignment extends FlowNodeBase {
|
||||
node: Expression;
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
|
||||
interface FlowCall extends FlowNodeBase {
|
||||
node: Expression;
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
|
||||
interface FlowCondition extends FlowNodeBase {
|
||||
node: Expression;
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
|
||||
interface FlowSwitchClause extends FlowNodeBase {
|
||||
switchStatement: SwitchStatement;
|
||||
clauseStart: number;
|
||||
clauseEnd: number;
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
|
||||
interface FlowArrayMutation extends FlowNodeBase {
|
||||
node: Expression;
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
|
||||
type FlowFlags = number;
|
||||
let FlowFlags: TypeScriptModule["FlowFlags"];
|
||||
let getSourceFileOfNode: TypeScriptModule["getSourceFileOfNode"];
|
||||
let getSourceTextOfNodeFromSourceFile: TypeScriptModule["getSourceTextOfNodeFromSourceFile"];
|
||||
let isDefaultClause: TypeScriptModule["isDefaultClause"];
|
||||
|
||||
export function init(ts: TypeScriptModule) {
|
||||
FlowFlags = ts.FlowFlags;
|
||||
getSourceFileOfNode = ts.getSourceFileOfNode;
|
||||
getSourceTextOfNodeFromSourceFile = ts.getSourceTextOfNodeFromSourceFile;
|
||||
isDefaultClause = ts.isDefaultClause;
|
||||
}
|
||||
|
||||
let nextDebugFlowId = -1;
|
||||
|
||||
function getDebugFlowNodeId(f: FlowNode) {
|
||||
if (!f.id) {
|
||||
f.id = nextDebugFlowId;
|
||||
nextDebugFlowId--;
|
||||
}
|
||||
return f.id;
|
||||
}
|
||||
|
||||
export function formatControlFlowGraph(flowNode: FlowNode) {
|
||||
const enum BoxCharacter {
|
||||
lr = "─",
|
||||
ud = "│",
|
||||
dr = "╭",
|
||||
dl = "╮",
|
||||
ul = "╯",
|
||||
ur = "╰",
|
||||
udr = "├",
|
||||
udl = "┤",
|
||||
dlr = "┬",
|
||||
ulr = "┴",
|
||||
udlr = "╫",
|
||||
}
|
||||
|
||||
const enum Connection {
|
||||
Up = 1 << 0,
|
||||
Down = 1 << 1,
|
||||
Left = 1 << 2,
|
||||
Right = 1 << 3,
|
||||
|
||||
UpDown = Up | Down,
|
||||
LeftRight = Left | Right,
|
||||
UpLeft = Up | Left,
|
||||
UpRight = Up | Right,
|
||||
DownLeft = Down | Left,
|
||||
DownRight = Down | Right,
|
||||
UpDownLeft = UpDown | Left,
|
||||
UpDownRight = UpDown | Right,
|
||||
UpLeftRight = Up | LeftRight,
|
||||
DownLeftRight = Down | LeftRight,
|
||||
UpDownLeftRight = UpDown | LeftRight,
|
||||
|
||||
NoChildren = 1 << 4,
|
||||
}
|
||||
|
||||
interface FlowGraphNode {
|
||||
id: number;
|
||||
flowNode: FlowNode;
|
||||
edges: FlowGraphEdge[];
|
||||
text: string;
|
||||
lane: number;
|
||||
endLane: number;
|
||||
level: number;
|
||||
}
|
||||
|
||||
interface FlowGraphEdge {
|
||||
source: FlowGraphNode;
|
||||
target: FlowGraphNode;
|
||||
}
|
||||
|
||||
const hasAntecedentFlags =
|
||||
FlowFlags.Assignment |
|
||||
FlowFlags.Condition |
|
||||
FlowFlags.SwitchClause |
|
||||
FlowFlags.ArrayMutation |
|
||||
FlowFlags.Call |
|
||||
FlowFlags.PreFinally |
|
||||
FlowFlags.AfterFinally;
|
||||
|
||||
const hasNodeFlags =
|
||||
FlowFlags.Start |
|
||||
FlowFlags.Assignment |
|
||||
FlowFlags.Call |
|
||||
FlowFlags.Condition |
|
||||
FlowFlags.ArrayMutation;
|
||||
|
||||
const links: Record<number, FlowGraphNode> = Object.create(/*o*/ null); // eslint-disable-line no-null/no-null
|
||||
const nodes: FlowGraphNode[] = [];
|
||||
const edges: FlowGraphEdge[] = [];
|
||||
const root = buildGraphNode(flowNode);
|
||||
for (const node of nodes) {
|
||||
computeLevel(node);
|
||||
}
|
||||
|
||||
const height = computeHeight(root);
|
||||
const columnWidths = computeColumnWidths(height);
|
||||
computeLanes(root, 0);
|
||||
return renderGraph();
|
||||
|
||||
function isFlowSwitchClause(f: FlowNode): f is FlowSwitchClause {
|
||||
return !!(f.flags & FlowFlags.SwitchClause);
|
||||
}
|
||||
|
||||
function hasAntecedents(f: FlowNode): f is FlowLabel & { antecedents: FlowNode[] } {
|
||||
return !!(f.flags & FlowFlags.Label) && !!(f as FlowLabel).antecedents;
|
||||
}
|
||||
|
||||
function hasAntecedent(f: FlowNode): f is Extract<FlowNode, { antecedent: FlowNode }> {
|
||||
return !!(f.flags & hasAntecedentFlags);
|
||||
}
|
||||
|
||||
function hasNode(f: FlowNode): f is Extract<FlowNode, { node?: Node }> {
|
||||
return !!(f.flags & hasNodeFlags);
|
||||
}
|
||||
|
||||
function getChildren(node: FlowGraphNode) {
|
||||
const children: FlowGraphNode[] = [];
|
||||
for (const edge of node.edges) {
|
||||
if (edge.source === node) {
|
||||
children.push(edge.target);
|
||||
}
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
function getParents(node: FlowGraphNode) {
|
||||
const parents: FlowGraphNode[] = [];
|
||||
for (const edge of node.edges) {
|
||||
if (edge.target === node) {
|
||||
parents.push(edge.source);
|
||||
}
|
||||
}
|
||||
return parents;
|
||||
}
|
||||
|
||||
function buildGraphNode(flowNode: FlowNode) {
|
||||
const id = getDebugFlowNodeId(flowNode);
|
||||
let graphNode = links[id];
|
||||
if (!graphNode) {
|
||||
links[id] = graphNode = { id, flowNode, edges: [], text: renderFlowNode(flowNode), lane: -1, endLane: -1, level: -1 };
|
||||
nodes.push(graphNode);
|
||||
if (!(flowNode.flags & FlowFlags.PreFinally)) {
|
||||
if (hasAntecedents(flowNode)) {
|
||||
|
||||
for (const antecedent of flowNode.antecedents) {
|
||||
buildGraphEdge(graphNode, antecedent);
|
||||
}
|
||||
}
|
||||
else if (hasAntecedent(flowNode)) {
|
||||
buildGraphEdge(graphNode, flowNode.antecedent);
|
||||
}
|
||||
}
|
||||
}
|
||||
return graphNode;
|
||||
}
|
||||
|
||||
function buildGraphEdge(source: FlowGraphNode, antecedent: FlowNode) {
|
||||
const target = buildGraphNode(antecedent);
|
||||
const edge: FlowGraphEdge = { source, target };
|
||||
edges.push(edge);
|
||||
source.edges.push(edge);
|
||||
target.edges.push(edge);
|
||||
}
|
||||
|
||||
function computeLevel(node: FlowGraphNode): number {
|
||||
if (node.level !== -1) {
|
||||
return node.level;
|
||||
}
|
||||
let level = 0;
|
||||
for (const parent of getParents(node)) {
|
||||
level = Math.max(level, computeLevel(parent) + 1);
|
||||
}
|
||||
return node.level = level;
|
||||
}
|
||||
|
||||
function computeHeight(node: FlowGraphNode): number {
|
||||
let height = 0;
|
||||
for (const child of getChildren(node)) {
|
||||
height = Math.max(height, computeHeight(child));
|
||||
}
|
||||
return height + 1;
|
||||
}
|
||||
|
||||
function computeColumnWidths(height: number) {
|
||||
const columns: number[] = fill(Array(height), 0);
|
||||
for (const node of nodes) {
|
||||
columns[node.level] = Math.max(columns[node.level], node.text.length);
|
||||
}
|
||||
return columns;
|
||||
}
|
||||
|
||||
function computeLanes(node: FlowGraphNode, lane: number) {
|
||||
if (node.lane === -1) {
|
||||
node.lane = lane;
|
||||
node.endLane = lane;
|
||||
const children = getChildren(node);
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
if (i > 0) lane++;
|
||||
const child = children[i];
|
||||
computeLanes(child, lane);
|
||||
if (child.endLane > node.endLane) {
|
||||
lane = child.endLane;
|
||||
}
|
||||
}
|
||||
node.endLane = lane;
|
||||
}
|
||||
}
|
||||
|
||||
function getHeader(flags: FlowFlags) {
|
||||
if (flags & FlowFlags.Start) return "Start";
|
||||
if (flags & FlowFlags.BranchLabel) return "Branch";
|
||||
if (flags & FlowFlags.LoopLabel) return "Loop";
|
||||
if (flags & FlowFlags.Assignment) return "Assignment";
|
||||
if (flags & FlowFlags.TrueCondition) return "True";
|
||||
if (flags & FlowFlags.FalseCondition) return "False";
|
||||
if (flags & FlowFlags.SwitchClause) return "SwitchClause";
|
||||
if (flags & FlowFlags.ArrayMutation) return "ArrayMutation";
|
||||
if (flags & FlowFlags.Call) return "Call";
|
||||
if (flags & FlowFlags.PreFinally) return "PreFinally";
|
||||
if (flags & FlowFlags.AfterFinally) return "AfterFinally";
|
||||
if (flags & FlowFlags.Unreachable) return "Unreachable";
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
function getNodeText(node: Node) {
|
||||
const sourceFile = getSourceFileOfNode(node);
|
||||
return getSourceTextOfNodeFromSourceFile(sourceFile, node, /*includeTrivia*/ false);
|
||||
}
|
||||
|
||||
function renderFlowNode(flowNode: FlowNode) {
|
||||
let text = getHeader(flowNode.flags);
|
||||
if (hasNode(flowNode)) {
|
||||
if (flowNode.node) {
|
||||
text += ` (${getNodeText(flowNode.node)})`;
|
||||
}
|
||||
}
|
||||
else if (isFlowSwitchClause(flowNode)) {
|
||||
const clauses: string[] = [];
|
||||
for (let i = flowNode.clauseStart; i < flowNode.clauseEnd; i++) {
|
||||
const clause = flowNode.switchStatement.caseBlock.clauses[i];
|
||||
if (isDefaultClause(clause)) {
|
||||
clauses.push("default");
|
||||
}
|
||||
else {
|
||||
clauses.push(getNodeText(clause.expression));
|
||||
}
|
||||
}
|
||||
text += ` (${clauses.join(", ")})`;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
function renderGraph() {
|
||||
const columnCount = columnWidths.length;
|
||||
const laneCount = nodes.reduce((x, n) => Math.max(x, n.lane), 0) + 1;
|
||||
const lanes: string[] = fill(Array(laneCount), "");
|
||||
const grid: (FlowGraphNode | undefined)[][] = columnWidths.map(() => Array(laneCount));
|
||||
const connectors: Connection[][] = columnWidths.map(() => fill(Array(laneCount), 0));
|
||||
|
||||
// build connectors
|
||||
for (const node of nodes) {
|
||||
grid[node.level][node.lane] = node;
|
||||
const children = getChildren(node);
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
let connector: Connection = Connection.Right;
|
||||
if (child.lane === node.lane) connector |= Connection.Left;
|
||||
if (i > 0) connector |= Connection.Up;
|
||||
if (i < children.length - 1) connector |= Connection.Down;
|
||||
connectors[node.level][child.lane] |= connector;
|
||||
}
|
||||
if (children.length === 0) {
|
||||
connectors[node.level][node.lane] |= Connection.NoChildren;
|
||||
}
|
||||
const parents = getParents(node);
|
||||
for (let i = 0; i < parents.length; i++) {
|
||||
const parent = parents[i];
|
||||
let connector: Connection = Connection.Left;
|
||||
if (i > 0) connector |= Connection.Up;
|
||||
if (i < parents.length - 1) connector |= Connection.Down;
|
||||
connectors[node.level - 1][parent.lane] |= connector;
|
||||
}
|
||||
}
|
||||
|
||||
// fill in missing connectors
|
||||
for (let column = 0; column < columnCount; column++) {
|
||||
for (let lane = 0; lane < laneCount; lane++) {
|
||||
const left = column > 0 ? connectors[column - 1][lane] : 0;
|
||||
const above = lane > 0 ? connectors[column][lane - 1] : 0;
|
||||
let connector = connectors[column][lane];
|
||||
if (!connector) {
|
||||
if (left & Connection.Right) connector |= Connection.LeftRight;
|
||||
if (above & Connection.Down) connector |= Connection.UpDown;
|
||||
connectors[column][lane] = connector;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let column = 0; column < columnCount; column++) {
|
||||
for (let lane = 0; lane < lanes.length; lane++) {
|
||||
const connector = connectors[column][lane];
|
||||
const fill = connector & Connection.Left ? BoxCharacter.lr : " ";
|
||||
const node = grid[column][lane];
|
||||
if (!node) {
|
||||
if (column < columnCount - 1) {
|
||||
writeLane(lane, repeat(fill, columnWidths[column] + 1));
|
||||
}
|
||||
}
|
||||
else {
|
||||
writeLane(lane, node.text);
|
||||
if (column < columnCount - 1) {
|
||||
writeLane(lane, " ");
|
||||
writeLane(lane, repeat(fill, columnWidths[column] - node.text.length));
|
||||
}
|
||||
}
|
||||
writeLane(lane, getBoxCharacter(connector));
|
||||
writeLane(lane, connector & Connection.Right && column < columnCount - 1 && !grid[column + 1][lane] ? BoxCharacter.lr : " ");
|
||||
}
|
||||
}
|
||||
|
||||
return `\n${lanes.join("\n")}\n`;
|
||||
|
||||
function writeLane(lane: number, text: string) {
|
||||
lanes[lane] += text;
|
||||
}
|
||||
}
|
||||
|
||||
function getBoxCharacter(connector: Connection) {
|
||||
switch (connector) {
|
||||
case Connection.UpDown: return BoxCharacter.ud;
|
||||
case Connection.LeftRight: return BoxCharacter.lr;
|
||||
case Connection.UpLeft: return BoxCharacter.ul;
|
||||
case Connection.UpRight: return BoxCharacter.ur;
|
||||
case Connection.DownLeft: return BoxCharacter.dl;
|
||||
case Connection.DownRight: return BoxCharacter.dr;
|
||||
case Connection.UpDownLeft: return BoxCharacter.udl;
|
||||
case Connection.UpDownRight: return BoxCharacter.udr;
|
||||
case Connection.UpLeftRight: return BoxCharacter.ulr;
|
||||
case Connection.DownLeftRight: return BoxCharacter.dlr;
|
||||
case Connection.UpDownLeftRight: return BoxCharacter.udlr;
|
||||
}
|
||||
return " ";
|
||||
}
|
||||
|
||||
function fill<T>(array: T[], value: T) {
|
||||
if (array.fill) {
|
||||
array.fill(value);
|
||||
}
|
||||
else {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
array[i] = value;
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
function repeat(ch: string, length: number) {
|
||||
if (ch.repeat) {
|
||||
return length > 0 ? ch.repeat(length) : "";
|
||||
}
|
||||
let s = "";
|
||||
while (s.length < length) {
|
||||
s += ch;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
// Export as a module. NOTE: Can't use module exports as this is built using --outFile
|
||||
declare const module: { exports: {} };
|
||||
if (typeof module !== "undefined" && module.exports) {
|
||||
module.exports = Debug;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "../tsconfig-library-base",
|
||||
"compilerOptions": {
|
||||
"target": "es2019",
|
||||
"lib": ["es2019"],
|
||||
"outFile": "../../built/local/compiler-debug.js",
|
||||
"declaration": false,
|
||||
"sourceMap": true
|
||||
},
|
||||
"files": [
|
||||
"debug.ts"
|
||||
]
|
||||
}
|
||||
@@ -290,7 +290,7 @@ namespace ts.server {
|
||||
const args: protocol.FileLocationRequestArgs = this.createFileLocationRequestArgs(fileName, position);
|
||||
|
||||
const request = this.processRequest<protocol.DefinitionRequest>(CommandNames.DefinitionAndBoundSpan, args);
|
||||
const response = this.processResponse<protocol.DefinitionInfoAndBoundSpanReponse>(request);
|
||||
const response = this.processResponse<protocol.DefinitionInfoAndBoundSpanResponse>(request);
|
||||
const body = Debug.assertDefined(response.body); // TODO: GH#18217
|
||||
|
||||
return {
|
||||
|
||||
@@ -524,7 +524,7 @@ ${indentText}${text}`;
|
||||
|
||||
export const version = "FakeTSVersion";
|
||||
|
||||
export function patchSolutionBuilderHost(host: ts.SolutionBuilderHost<ts.BuilderProgram>, sys: System) {
|
||||
export function patchHostForBuildInfoReadWrite(host: ts.CompilerHost | ts.SolutionBuilderHost<ts.BuilderProgram>) {
|
||||
const originalReadFile = host.readFile;
|
||||
host.readFile = (path, encoding) => {
|
||||
const value = originalReadFile.call(host, path, encoding);
|
||||
@@ -537,7 +537,7 @@ ${indentText}${text}`;
|
||||
|
||||
if (host.writeFile) {
|
||||
const originalWriteFile = host.writeFile;
|
||||
host.writeFile = (fileName, content, writeByteOrderMark) => {
|
||||
host.writeFile = (fileName: string, content: string, writeByteOrderMark: boolean) => {
|
||||
if (!ts.isBuildInfoFile(fileName)) return originalWriteFile.call(host, fileName, content, writeByteOrderMark);
|
||||
const buildInfo = ts.getBuildInfo(content);
|
||||
sanitizeBuildInfoProgram(buildInfo);
|
||||
@@ -545,6 +545,10 @@ ${indentText}${text}`;
|
||||
originalWriteFile.call(host, fileName, ts.getBuildInfoText(buildInfo), writeByteOrderMark);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function patchSolutionBuilderHost(host: ts.SolutionBuilderHost<ts.BuilderProgram>, sys: System) {
|
||||
patchHostForBuildInfoReadWrite(host);
|
||||
|
||||
ts.Debug.assert(host.now === undefined);
|
||||
host.now = () => new Date(sys.vfs.time());
|
||||
|
||||
@@ -775,7 +775,9 @@ namespace FourSlash {
|
||||
private verifyCompletionsWorker(options: FourSlashInterface.VerifyCompletionsOptions): void {
|
||||
const actualCompletions = this.getCompletionListAtCaret({ ...options.preferences, triggerCharacter: options.triggerCharacter })!;
|
||||
if (!actualCompletions) {
|
||||
if (ts.hasProperty(options, "exact") && options.exact === undefined) return;
|
||||
if (ts.hasProperty(options, "exact") && (options.exact === undefined || ts.isArray(options.exact) && !options.exact.length)) {
|
||||
return;
|
||||
}
|
||||
this.raiseError(`No completions at position '${this.currentCaretPosition}'.`);
|
||||
}
|
||||
|
||||
@@ -4894,12 +4896,14 @@ namespace FourSlashInterface {
|
||||
"declare",
|
||||
"keyof",
|
||||
"module",
|
||||
"namespace",
|
||||
"never",
|
||||
"readonly",
|
||||
"number",
|
||||
"object",
|
||||
"string",
|
||||
"symbol",
|
||||
"type",
|
||||
"unique",
|
||||
"unknown",
|
||||
"bigint",
|
||||
@@ -5091,12 +5095,14 @@ namespace FourSlashInterface {
|
||||
"declare",
|
||||
"keyof",
|
||||
"module",
|
||||
"namespace",
|
||||
"never",
|
||||
"readonly",
|
||||
"number",
|
||||
"object",
|
||||
"string",
|
||||
"symbol",
|
||||
"type",
|
||||
"unique",
|
||||
"unknown",
|
||||
"bigint",
|
||||
|
||||
@@ -220,6 +220,10 @@ namespace Harness.LanguageService {
|
||||
return !!this.typesRegistry && this.typesRegistry.has(name);
|
||||
}
|
||||
|
||||
getGlobalTypingsCacheLocation() {
|
||||
return "/Library/Caches/typescript";
|
||||
}
|
||||
|
||||
installPackage = ts.notImplemented;
|
||||
|
||||
getCompilationSettings() { return this.settings; }
|
||||
@@ -796,7 +800,7 @@ namespace Harness.LanguageService {
|
||||
return mockHash(s);
|
||||
}
|
||||
|
||||
require(_initialDir: string, _moduleName: string): ts.server.RequireResult {
|
||||
require(_initialDir: string, _moduleName: string): ts.RequireResult {
|
||||
switch (_moduleName) {
|
||||
// Adds to the Quick Info a fixed string and a string from the config file
|
||||
// and replaces the first display part
|
||||
|
||||
@@ -341,7 +341,7 @@ interface Array<T> { length: number; [n: number]: T; }`
|
||||
private readonly currentDirectory: string;
|
||||
private readonly customWatchFile: HostWatchFile | undefined;
|
||||
private readonly customRecursiveWatchDirectory: HostWatchDirectory | undefined;
|
||||
public require: ((initialPath: string, moduleName: string) => server.RequireResult) | undefined;
|
||||
public require: ((initialPath: string, moduleName: string) => RequireResult) | undefined;
|
||||
|
||||
constructor(
|
||||
public withSafeList: boolean,
|
||||
|
||||
Vendored
+2
-2
@@ -9762,7 +9762,7 @@ interface ImageData {
|
||||
declare var ImageData: {
|
||||
prototype: ImageData;
|
||||
new(width: number, height: number): ImageData;
|
||||
new(array: Uint8ClampedArray, width: number, height: number): ImageData;
|
||||
new(array: Uint8ClampedArray, width: number, height?: number): ImageData;
|
||||
};
|
||||
|
||||
interface InnerHTML {
|
||||
@@ -19083,7 +19083,7 @@ declare namespace WebAssembly {
|
||||
|
||||
var Instance: {
|
||||
prototype: Instance;
|
||||
new(module: Module, importObject?: any): Instance;
|
||||
new(module: Module, importObject?: Imports): Instance;
|
||||
};
|
||||
|
||||
interface LinkError {
|
||||
|
||||
Vendored
+8
-8
@@ -682,8 +682,8 @@ interface Math {
|
||||
/** Returns a pseudorandom number between 0 and 1. */
|
||||
random(): number;
|
||||
/**
|
||||
* Returns a supplied numeric expression rounded to the nearest number.
|
||||
* @param x The value to be rounded to the nearest number.
|
||||
* Returns a supplied numeric expression rounded to the nearest integer.
|
||||
* @param x The value to be rounded to the nearest integer.
|
||||
*/
|
||||
round(x: number): number;
|
||||
/**
|
||||
@@ -873,12 +873,12 @@ interface DateConstructor {
|
||||
/**
|
||||
* Returns the number of milliseconds between midnight, January 1, 1970 Universal Coordinated Time (UTC) (or GMT) and the specified date.
|
||||
* @param year The full year designation is required for cross-century date accuracy. If year is between 0 and 99 is used, then year is assumed to be 1900 + year.
|
||||
* @param month The month as an number between 0 and 11 (January to December).
|
||||
* @param date The date as an number between 1 and 31.
|
||||
* @param hours Must be supplied if minutes is supplied. An number from 0 to 23 (midnight to 11pm) that specifies the hour.
|
||||
* @param minutes Must be supplied if seconds is supplied. An number from 0 to 59 that specifies the minutes.
|
||||
* @param seconds Must be supplied if milliseconds is supplied. An number from 0 to 59 that specifies the seconds.
|
||||
* @param ms An number from 0 to 999 that specifies the milliseconds.
|
||||
* @param month The month as a number between 0 and 11 (January to December).
|
||||
* @param date The date as a number between 1 and 31.
|
||||
* @param hours Must be supplied if minutes is supplied. A number from 0 to 23 (midnight to 11pm) that specifies the hour.
|
||||
* @param minutes Must be supplied if seconds is supplied. A number from 0 to 59 that specifies the minutes.
|
||||
* @param seconds Must be supplied if milliseconds is supplied. A number from 0 to 59 that specifies the seconds.
|
||||
* @param ms A number from 0 to 999 that specifies the milliseconds.
|
||||
*/
|
||||
UTC(year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number): number;
|
||||
now(): number;
|
||||
|
||||
Vendored
+2
-2
@@ -2202,7 +2202,7 @@ interface ImageData {
|
||||
declare var ImageData: {
|
||||
prototype: ImageData;
|
||||
new(width: number, height: number): ImageData;
|
||||
new(array: Uint8ClampedArray, width: number, height: number): ImageData;
|
||||
new(array: Uint8ClampedArray, width: number, height?: number): ImageData;
|
||||
};
|
||||
|
||||
/** This Channel Messaging API interface allows us to create a new message channel and send data through it via its two MessagePort properties. */
|
||||
@@ -5609,7 +5609,7 @@ declare namespace WebAssembly {
|
||||
|
||||
var Instance: {
|
||||
prototype: Instance;
|
||||
new(module: Module, importObject?: any): Instance;
|
||||
new(module: Module, importObject?: Imports): Instance;
|
||||
};
|
||||
|
||||
interface Memory {
|
||||
|
||||
@@ -4087,7 +4087,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Function lacks ending return statement and return type does not include 'undefined'.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[函数缺少结束返回语句,返回类型不包括 "undefined"。]]></Val>
|
||||
<Val><![CDATA[函数缺少结束 return 语句,返回类型不包括 "undefined"。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -4987,7 +4987,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[JSX fragment is not supported when using an inline JSX factory pragma]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[使用内联 JSX 工厂杂注时,不支持 JSX 片段]]></Val>
|
||||
<Val><![CDATA[使用内联 JSX 工厂 pragma 时,不支持 JSX 片段]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[A '{0}' modifier cannot be used with an import declaration.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA['{0}' 修飾詞無法與匯入宣告並用。]]></Val>
|
||||
<Val><![CDATA['{0}' 修飾元無法與匯入宣告並用。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -34,7 +34,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[A '{0}' modifier cannot be used with an interface declaration.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA['{0}' 修飾詞無法與介面宣告並用。]]></Val>
|
||||
<Val><![CDATA['{0}' 修飾元無法與介面宣告並用。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -88,7 +88,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[A class declaration without the 'default' modifier must have a name.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[不具 'default' 修飾詞的類別宣告必須要有名稱。]]></Val>
|
||||
<Val><![CDATA[不具 'default' 修飾元的類別宣告必須要有名稱。]]></Val>
|
||||
</Tgt>
|
||||
<Prev Cat="Text">
|
||||
<Val><![CDATA[A class declaration without the 'default' modifier must have a name]]></Val>
|
||||
@@ -265,7 +265,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[A 'declare' modifier cannot be used in an already ambient context.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[不得在現有環境內容中使用 'declare' 修飾詞。]]></Val>
|
||||
<Val><![CDATA[不得在現有環境內容中使用 'declare' 修飾元。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -274,7 +274,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[A 'declare' modifier is required for a top level declaration in a .d.ts file.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[.d.ts 檔案中的最上層宣告需要 'declare' 修飾詞。]]></Val>
|
||||
<Val><![CDATA[.d.ts 檔案中的最上層宣告需要 'declare' 修飾元。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -886,7 +886,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Accessibility modifier already seen.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[已有存取範圍修飾詞。]]></Val>
|
||||
<Val><![CDATA[已有存取範圍修飾元。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -934,7 +934,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Add all missing 'async' modifiers]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[新增缺少的所有 'async' 修飾詞]]></Val>
|
||||
<Val><![CDATA[新增缺少的所有 'async' 修飾元]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -961,7 +961,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Add async modifier to containing function]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[將 async 修飾詞新增至包含的函式]]></Val>
|
||||
<Val><![CDATA[將 async 修飾元新增至包含的函式]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -1129,7 +1129,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[All declarations of '{0}' must have identical modifiers.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA['{0}' 的所有宣告都必須有相同修飾詞。]]></Val>
|
||||
<Val><![CDATA['{0}' 的所有宣告都必須有相同修飾元。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -1363,7 +1363,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[An export assignment cannot have modifiers.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[匯出指派不得有修飾詞。]]></Val>
|
||||
<Val><![CDATA[匯出指派不得有修飾元。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -1381,7 +1381,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[An export declaration cannot have modifiers.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[匯出宣告不得有修飾詞。]]></Val>
|
||||
<Val><![CDATA[匯出宣告不得有修飾元。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -1417,7 +1417,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[An import declaration cannot have modifiers.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[匯入宣告不得有修飾詞。]]></Val>
|
||||
<Val><![CDATA[匯入宣告不得有修飾元。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -1480,7 +1480,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[An index signature parameter cannot have an accessibility modifier.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[索引簽章參數不得有存取範圍修飾詞。]]></Val>
|
||||
<Val><![CDATA[索引簽章參數不得有存取範圍修飾元。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -1708,7 +1708,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Augmentations for the global scope should have 'declare' modifier unless they appear in already ambient context.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[除非全域範圍的增強指定已顯示在環境內容中,否則應含有 'declare' 修飾詞。]]></Val>
|
||||
<Val><![CDATA[除非全域範圍的增強指定已顯示在環境內容中,否則應含有 'declare' 修飾元。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -4087,7 +4087,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Function lacks ending return statement and return type does not include 'undefined'.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[函式缺少結束傳回陳述式,且傳回類型不包括 'undefined'。]]></Val>
|
||||
<Val><![CDATA[函式缺少結束 return 陳述式,且傳回類型不包括 'undefined'。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -5260,7 +5260,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Modifiers cannot appear here.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[此處不得出現修飾詞。]]></Val>
|
||||
<Val><![CDATA[此處不得出現修飾元。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -9325,7 +9325,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA['{0}' modifier already seen.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[已有 '{0}' 修飾詞。]]></Val>
|
||||
<Val><![CDATA[已有 '{0}' 修飾元。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -9334,7 +9334,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA['{0}' modifier cannot appear on a class element.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[類別項目不得有 '{0}' 修飾詞。]]></Val>
|
||||
<Val><![CDATA[類別項目不得有 '{0}' 修飾元。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -9343,7 +9343,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA['{0}' modifier cannot appear on a constructor declaration.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[建構函式宣告不得有 '{0}' 修飾詞。]]></Val>
|
||||
<Val><![CDATA[建構函式宣告不得有 '{0}' 修飾元。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -9352,7 +9352,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA['{0}' modifier cannot appear on a data property.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[資料屬性不得有 '{0}' 修飾詞。]]></Val>
|
||||
<Val><![CDATA[資料屬性不得有 '{0}' 修飾元。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -9361,7 +9361,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA['{0}' modifier cannot appear on a module or namespace element.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[模組或命名空間元素不能有 '{0}' 修飾詞。]]></Val>
|
||||
<Val><![CDATA[模組或命名空間元素不能有 '{0}' 修飾元。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -9370,7 +9370,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA['{0}' modifier cannot appear on a parameter.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[參數不得有 '{0}' 修飾詞。]]></Val>
|
||||
<Val><![CDATA[參數不得有 '{0}' 修飾元。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -9379,7 +9379,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA['{0}' modifier cannot appear on a type member.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[類型成員不能有 '{0}' 修飾詞。]]></Val>
|
||||
<Val><![CDATA[類型成員不能有 '{0}' 修飾元。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -9388,7 +9388,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA['{0}' modifier cannot appear on an index signature.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[索引簽章不能有 '{0}' 修飾詞。]]></Val>
|
||||
<Val><![CDATA[索引簽章不能有 '{0}' 修飾元。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -9397,7 +9397,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA['{0}' modifier cannot be used here.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[無法在此處使用 '{0}' 修飾詞。]]></Val>
|
||||
<Val><![CDATA[無法在此處使用 '{0}' 修飾元。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -9406,7 +9406,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA['{0}' modifier cannot be used in an ambient context.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[無法在環境內容中使用 '{0}' 修飾詞。]]></Val>
|
||||
<Val><![CDATA[無法在環境內容中使用 '{0}' 修飾元。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -9415,7 +9415,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA['{0}' modifier cannot be used with '{1}' modifier.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA['{0}' 修飾詞無法與 '{1}' 修飾詞並用。]]></Val>
|
||||
<Val><![CDATA['{0}' 修飾元無法與 '{1}' 修飾元並用。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -9424,7 +9424,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA['{0}' modifier cannot be used with a class declaration.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA['{0}' 修飾詞無法與類別宣告並用。]]></Val>
|
||||
<Val><![CDATA['{0}' 修飾元無法與類別宣告並用。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -9433,7 +9433,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA['{0}' modifier must precede '{1}' modifier.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA['{0}' 修飾詞必須在 '{1}' 修飾詞之前。]]></Val>
|
||||
<Val><![CDATA['{0}' 修飾元必須在 '{1}' 修飾元之前。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -9496,7 +9496,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA['abstract' modifier can only appear on a class, method, or property declaration.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA['abstract' 修飾詞只能出現在類別宣告、方法宣告或屬性宣告。]]></Val>
|
||||
<Val><![CDATA['abstract' 修飾元只能出現在類別宣告、方法宣告或屬性宣告。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -9649,7 +9649,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA['export' modifier cannot be applied to ambient modules and module augmentations since they are always visible.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA['export' 修飾詞無法套用至環境模組或模組增強指定,原因是這二者永遠會顯示。]]></Val>
|
||||
<Val><![CDATA['export' 修飾元無法套用至環境模組或模組增強指定,原因是這二者永遠會顯示。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -9874,7 +9874,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA['readonly' modifier can only appear on a property declaration or index signature.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA['readonly' 修飾詞只能出現在屬性宣告或索引簽章。]]></Val>
|
||||
<Val><![CDATA['readonly' 修飾元只能出現在屬性宣告或索引簽章。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LCX SchemaVersion="6.0" Name="f:\ddSetup\sources\typescript\localization\compiler2.resx" PsrId="306" FileType="1" SrcCul="en-US" TgtCul="cs-CZ" xmlns="http://schemas.microsoft.com/locstudio/2006/6/lcx">
|
||||
<Props>
|
||||
<Str Name="CustomName1" Val="Custom 1" />
|
||||
@@ -4096,7 +4096,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Function lacks ending return statement and return type does not include 'undefined'.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Ve funkci chybí koncový návratový příkaz a návratový typ neobsahuje undefined.]]></Val>
|
||||
<Val><![CDATA[Ve funkci chybí koncový příkaz return a návratový typ neobsahuje undefined.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LCX SchemaVersion="6.0" Name="f:\ddSetup\sources\typescript\localization\compiler2.resx" PsrId="306" FileType="1" SrcCul="en-US" TgtCul="fr-FR" xmlns="http://schemas.microsoft.com/locstudio/2006/6/lcx">
|
||||
<Props>
|
||||
<Str Name="CustomName1" Val="Custom 1" />
|
||||
@@ -1348,7 +1348,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[An export assignment can only be used in a module.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Une attribution d'exportation peut uniquement être utilisée dans un module.]]></Val>
|
||||
<Val><![CDATA[Une affectation d'exportation peut uniquement être utilisée dans un module.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -5539,7 +5539,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Not all code paths return a value.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Les chemins de code ne retournent pas tous une valeur.]]></Val>
|
||||
<Val><![CDATA[Les chemins du code ne retournent pas tous une valeur.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -6916,7 +6916,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Report error when not all code paths in function return a value.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Signalez une erreur quand les chemins de code de la fonction ne retournent pas tous une valeur.]]></Val>
|
||||
<Val><![CDATA[Signalez une erreur quand les chemins du code de la fonction ne retournent pas tous une valeur.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
|
||||
@@ -1228,7 +1228,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[An AMD module cannot have multiple name assignments.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[AMD モジュールに複数の名前を割り当てることはできません。]]></Val>
|
||||
<Val><![CDATA[AMD モジュールに複数の名前を代入することはできません。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -1336,7 +1336,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[An export assignment can only be used in a module.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[エクスポートの割り当てはモジュールでのみ使用可能です。]]></Val>
|
||||
<Val><![CDATA[エクスポートの代入はモジュールでのみ使用可能です。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -1345,7 +1345,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[An export assignment cannot be used in a module with other exported elements.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[エクスポートの割り当ては、エクスポートされた他の要素を含むモジュールでは使用できません。]]></Val>
|
||||
<Val><![CDATA[エクスポートの代入は、エクスポートされた他の要素を含むモジュールでは使用できません。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -1354,7 +1354,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[An export assignment cannot be used in a namespace.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[エクスポートの割り当ては、名前空間では使用できません。]]></Val>
|
||||
<Val><![CDATA[エクスポートの代入は、名前空間では使用できません。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -1363,7 +1363,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[An export assignment cannot have modifiers.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[エクスポートの割り当てに修飾子を指定することはできません。]]></Val>
|
||||
<Val><![CDATA[エクスポートの代入に修飾子を指定することはできません。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -3610,7 +3610,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Export assignment cannot be used when targeting ECMAScript modules. Consider using 'export default' or another module format instead.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[ECMAScript モジュールを対象にする場合は、エクスポート割り当てを使用できません。代わりに 'export default' または別のモジュール書式の使用をご検討ください。]]></Val>
|
||||
<Val><![CDATA[ECMAScript モジュールを対象にする場合は、エクスポート代入を使用できません。代わりに 'export default' または別のモジュール書式の使用をご検討ください。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -3619,7 +3619,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Export assignment is not supported when '--module' flag is 'system'.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[割り当てのエクスポートは、'--module' フラグが 'system' の場合にはサポートされません。]]></Val>
|
||||
<Val><![CDATA[代入のエクスポートは、'--module' フラグが 'system' の場合にはサポートされません。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -3703,7 +3703,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Exports and export assignments are not permitted in module augmentations.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[エクスポートとエクスポートの割り当てはモジュールの拡張では許可されていません。]]></Val>
|
||||
<Val><![CDATA[エクスポートとエクスポートの代入はモジュールの拡張では許可されていません。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -4390,7 +4390,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', or another module format instead.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[ECMAScript モジュールを対象にする場合は、インポート割り当てを使用できません。代わりに 'import * as ns from "mod"'、'import {a} from "mod"'、'import d from "mod"' などのモジュール書式の使用をご検討ください。]]></Val>
|
||||
<Val><![CDATA[ECMAScript モジュールを対象にする場合は、インポート代入を使用できません。代わりに 'import * as ns from "mod"'、'import {a} from "mod"'、'import d from "mod"' などのモジュール書式の使用をご検討ください。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -4987,7 +4987,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[JSX fragment is not supported when using an inline JSX factory pragma]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[JSX フラグメントはインライン JSX ファクトリ プラグマの使用時にサポートされていません]]></Val>
|
||||
<Val><![CDATA[JSX フラグメントはインライン JSX ファクトリ pragma の使用時にサポートされていません]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -6622,7 +6622,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Property assignment expected.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[プロパティの割り当てが必要です。]]></Val>
|
||||
<Val><![CDATA[プロパティの代入が必要です。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -7870,7 +7870,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[The expression of an export assignment must be an identifier or qualified name in an ambient context.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[エクスポートの割り当ての式は、環境コンテキストの識別子または修飾名にする必要があります。]]></Val>
|
||||
<Val><![CDATA[エクスポートの代入の式は、環境コンテキストの識別子または修飾名にする必要があります。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -8170,7 +8170,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[The target of an object rest assignment must be a variable or a property access.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[オブジェクトの残り部分の割り当ての対象は、変数またはプロパティ アクセスである必要があります。]]></Val>
|
||||
<Val><![CDATA[オブジェクトの残り部分の代入の対象は、変数またはプロパティ アクセスである必要があります。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -9535,7 +9535,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA['=' can only be used in an object literal property inside a destructuring assignment.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA['=' は、非構造化割り当て内のオブジェクト リテラル プロパティでのみ使用できます。]]></Val>
|
||||
<Val><![CDATA['=' は、非構造化代入内のオブジェクト リテラル プロパティでのみ使用できます。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -9601,7 +9601,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA['const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment or type query.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA['const' 列挙型は、プロパティまたはインデックスのアクセス式、インポート宣言またはエクスポートの割り当ての右辺、型のクエリにのみ使用できます。]]></Val>
|
||||
<Val><![CDATA['const' 列挙型は、プロパティまたはインデックスのアクセス式、インポート宣言またはエクスポートの代入の右辺、型のクエリにのみ使用できます。]]></Val>
|
||||
</Tgt>
|
||||
<Prev Cat="Text">
|
||||
<Val><![CDATA['const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment.]]></Val>
|
||||
|
||||
@@ -9856,7 +9856,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA['parameter modifiers' can only be used in a .ts file.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA['parameter modifiers'는 .ts 파일에서만 사용할 수 있습니다.]]></Val>
|
||||
<Val><![CDATA['매개 변수 한정자'는 .ts 파일에서만 사용할 수 있습니다.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
|
||||
@@ -303,7 +303,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[A definite assignment assertion '!' is not permitted in this context.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Asercja określonego przydziału „!” nie jest dozwolona w tym kontekście.]]></Val>
|
||||
<Val><![CDATA[Asercja określonego przypisania „!” nie jest dozwolona w tym kontekście.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -4077,7 +4077,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Function lacks ending return statement and return type does not include 'undefined'.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Funkcja nie zawiera końcowej instrukcji „return”, a zwracany typ nie obejmuje wartości „undefined”.]]></Val>
|
||||
<Val><![CDATA[Funkcja nie zawiera końcowej instrukcji return, a zwracany typ nie obejmuje wartości „undefined”.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -5517,7 +5517,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Not all code paths return a value.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Nie wszystkie ścieżki kodu zwracają wartość.]]></Val>
|
||||
<Val><![CDATA[Nie wszystkie ścieżki w kodzie zwracają wartość.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -6891,7 +6891,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Report error when not all code paths in function return a value.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Zgłoś błąd, gdy nie wszystkie ścieżki kodu zwracają wartość.]]></Val>
|
||||
<Val><![CDATA[Zgłoś błąd, gdy nie wszystkie ścieżki w kodzie zwracają wartość.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -309,7 +309,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[A definite assignment assertion '!' is not permitted in this context.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Утверждение определенного присваивания "!" запрещено в этом контексте.]]></Val>
|
||||
<Val><![CDATA[Утверждение определенного назначения "!" запрещено в этом контексте.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -978,7 +978,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Add definite assignment assertion to property '{0}']]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Добавить утверждение определенного присваивания к свойству "{0}"]]></Val>
|
||||
<Val><![CDATA[Добавить утверждение определенного назначения к свойству "{0}"]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -987,7 +987,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Add definite assignment assertions to all uninitialized properties]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Добавить утверждения определенного присваивания ко всем неинициализированным свойствам]]></Val>
|
||||
<Val><![CDATA[Добавить утверждения определенного назначения ко всем неинициализированным свойствам]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -1227,7 +1227,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[An AMD module cannot have multiple name assignments.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Модуль AMD не может иметь несколько присваиваний имен.]]></Val>
|
||||
<Val><![CDATA[Модуль AMD не может иметь несколько назначений имен.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -1335,7 +1335,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[An export assignment can only be used in a module.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Присваивание экспорта может быть использовано только в модуле.]]></Val>
|
||||
<Val><![CDATA[Назначение экспорта может быть использовано только в модуле.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -1344,7 +1344,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[An export assignment cannot be used in a module with other exported elements.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Присваивание экспорта нельзя использовать в модуле с другими экспортированными элементами.]]></Val>
|
||||
<Val><![CDATA[Назначение экспорта нельзя использовать в модуле с другими экспортированными элементами.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -1353,7 +1353,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[An export assignment cannot be used in a namespace.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Присваивание экспорта нельзя использовать в пространстве имен.]]></Val>
|
||||
<Val><![CDATA[Назначение экспорта нельзя использовать в пространстве имен.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -1362,7 +1362,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[An export assignment cannot have modifiers.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Присваивание экспорта не может иметь модификаторы.]]></Val>
|
||||
<Val><![CDATA[Назначение экспорта не может иметь модификаторы.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -3618,7 +3618,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Export assignment is not supported when '--module' flag is 'system'.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Присваивание экспорта не поддерживается, если флаг "--module" имеет значение "system".]]></Val>
|
||||
<Val><![CDATA[Назначение экспорта не поддерживается, если флаг "--module" имеет значение "system".]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -4086,7 +4086,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Function lacks ending return statement and return type does not include 'undefined'.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[В функции отсутствует завершающий оператор возвращаемого значения, а тип возвращаемого значения не включает undefined.]]></Val>
|
||||
<Val><![CDATA[В функции отсутствует завершающий оператор return, а тип возвращаемого значения не включает "undefined".]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -5526,7 +5526,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Not all code paths return a value.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Не все пути кода возвращают значение.]]></Val>
|
||||
<Val><![CDATA[Не все пути к коду возвращают значение.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -6621,7 +6621,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Property assignment expected.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Ожидалось присваивание свойства.]]></Val>
|
||||
<Val><![CDATA[Ожидалось назначение свойства.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -6903,7 +6903,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Report error when not all code paths in function return a value.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Сообщать об ошибке, если не все пути кода в функции возвращают значение.]]></Val>
|
||||
<Val><![CDATA[Сообщать об ошибке, если не все пути к коду в функции возвращают значение.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -7869,7 +7869,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[The expression of an export assignment must be an identifier or qualified name in an ambient context.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Выражение присваивания экспорта должно представлять собой идентификатор или полное имя в окружающем контексте.]]></Val>
|
||||
<Val><![CDATA[Выражение назначения экспорта должно представлять собой идентификатор или полное имя в окружающем контексте.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -9600,7 +9600,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA['const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment or type query.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Перечисления const можно использовать только в выражениях доступа к свойству или индексу, а также в правой части объявления импорта, присваивания экспорта или запроса типа.]]></Val>
|
||||
<Val><![CDATA[Перечисления const можно использовать только в выражениях доступа к свойству или индексу, а также в правой части объявления импорта, назначения экспорта или запроса типа.]]></Val>
|
||||
</Tgt>
|
||||
<Prev Cat="Text">
|
||||
<Val><![CDATA['const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment.]]></Val>
|
||||
|
||||
@@ -4188,7 +4188,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Generic type instantiation is excessively deep and possibly infinite.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Genel tür oluşturma işlemi, fazla ayrıntılı ve büyük olasılıkla sınırsız.]]></Val>
|
||||
<Val><![CDATA[Genel tür örneği oluşturma işlemi, fazla ayrıntılı ve büyük olasılıkla sınırsız.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
|
||||
@@ -1533,6 +1533,14 @@ namespace ts.server {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
findDefaultConfiguredProject(info: ScriptInfo) {
|
||||
if (!info.isScriptOpen()) return undefined;
|
||||
const configFileName = this.getConfigFileNameForFile(info);
|
||||
return configFileName &&
|
||||
this.findConfiguredProjectByProjectName(configFileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function tries to search for a tsconfig.json for the given file.
|
||||
* This is different from the method the compiler uses because
|
||||
|
||||
@@ -307,6 +307,11 @@ namespace ts.server {
|
||||
return this.typingsCache.installPackage({ ...options, projectName: this.projectName, projectRootPath: this.toPath(this.currentDirectory) });
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
getGlobalTypingsCacheLocation() {
|
||||
return this.getGlobalCache();
|
||||
}
|
||||
|
||||
private get typingsCache(): TypingsCache {
|
||||
return this.projectService.typingsCache;
|
||||
}
|
||||
|
||||
+10
-1
@@ -918,10 +918,13 @@ namespace ts.server.protocol {
|
||||
body?: FileSpanWithContext[];
|
||||
}
|
||||
|
||||
export interface DefinitionInfoAndBoundSpanReponse extends Response {
|
||||
export interface DefinitionInfoAndBoundSpanResponse extends Response {
|
||||
body?: DefinitionInfoAndBoundSpan;
|
||||
}
|
||||
|
||||
/** @deprecated Use `DefinitionInfoAndBoundSpanResponse` instead. */
|
||||
export type DefinitionInfoAndBoundSpanReponse = DefinitionInfoAndBoundSpanResponse;
|
||||
|
||||
/**
|
||||
* Definition response message. Gives text range for definition.
|
||||
*/
|
||||
@@ -3004,6 +3007,12 @@ namespace ts.server.protocol {
|
||||
* For those entries, The `insertText` and `replacementSpan` properties will be set to change from `.x` property access to `["x"]`.
|
||||
*/
|
||||
readonly includeCompletionsWithInsertText?: boolean;
|
||||
/**
|
||||
* Unless this option is `false`, or `includeCompletionsWithInsertText` is not enabled,
|
||||
* member completion lists triggered with `.` will include entries on potentially-null and potentially-undefined
|
||||
* values, with insertion text to replace preceding `.` tokens with `?.`.
|
||||
*/
|
||||
readonly includeAutomaticOptionalChainCompletions?: boolean;
|
||||
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
|
||||
readonly allowTextChangesInNewFiles?: boolean;
|
||||
readonly lazyConfiguredProjectsFromExternalProject?: boolean;
|
||||
|
||||
@@ -491,21 +491,40 @@ namespace ts.server {
|
||||
case 1:
|
||||
return this.containingProjects[0];
|
||||
default:
|
||||
// if this file belongs to multiple projects, the first configured project should be
|
||||
// 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.
|
||||
// If this file belongs to multiple projects, below is the order in which default project is used
|
||||
// - for open script info, its default configured project during opening is default if info is part of it
|
||||
// - first configured project of which script info is not a source of project reference redirect
|
||||
// - first configured project
|
||||
// - first external project
|
||||
// - first inferred project
|
||||
let firstExternalProject;
|
||||
let firstConfiguredProject;
|
||||
for (const project of this.containingProjects) {
|
||||
let firstNonSourceOfProjectReferenceRedirect;
|
||||
let defaultConfiguredProject: ConfiguredProject | false | undefined;
|
||||
for (let index = 0; index < this.containingProjects.length; index++) {
|
||||
const project = this.containingProjects[index];
|
||||
if (project.projectKind === ProjectKind.Configured) {
|
||||
if (!project.isSourceOfProjectReferenceRedirect(this.fileName)) return project;
|
||||
if (!project.isSourceOfProjectReferenceRedirect(this.fileName)) {
|
||||
// If we havent found default configuredProject and
|
||||
// its not the last one, find it and use that one if there
|
||||
if (defaultConfiguredProject === undefined &&
|
||||
index !== this.containingProjects.length - 1) {
|
||||
defaultConfiguredProject = project.projectService.findDefaultConfiguredProject(this) || false;
|
||||
}
|
||||
if (defaultConfiguredProject === project) return project;
|
||||
if (!firstNonSourceOfProjectReferenceRedirect) firstNonSourceOfProjectReferenceRedirect = project;
|
||||
}
|
||||
if (!firstConfiguredProject) firstConfiguredProject = project;
|
||||
}
|
||||
else if (project.projectKind === ProjectKind.External && !firstExternalProject) {
|
||||
firstExternalProject = project;
|
||||
}
|
||||
}
|
||||
return firstConfiguredProject || firstExternalProject || this.containingProjects[0];
|
||||
return defaultConfiguredProject ||
|
||||
firstNonSourceOfProjectReferenceRedirect ||
|
||||
firstConfiguredProject ||
|
||||
firstExternalProject ||
|
||||
this.containingProjects[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -499,8 +499,10 @@ namespace ts {
|
||||
return { spans, endOfLineState: EndOfLineState.None };
|
||||
|
||||
function pushClassification(start: number, end: number, type: ClassificationType): void {
|
||||
const length = end - start;
|
||||
Debug.assert(length > 0, `Classification had non-positive length of ${length}`);
|
||||
spans.push(start);
|
||||
spans.push(end - start);
|
||||
spans.push(length);
|
||||
spans.push(type);
|
||||
}
|
||||
}
|
||||
@@ -985,8 +987,7 @@ namespace ts {
|
||||
return ClassificationType.bigintLiteral;
|
||||
}
|
||||
else if (tokenKind === SyntaxKind.StringLiteral) {
|
||||
// TODO: GH#18217
|
||||
return token!.parent.kind === SyntaxKind.JsxAttribute ? ClassificationType.jsxAttributeStringLiteralValue : ClassificationType.stringLiteral;
|
||||
return token && token.parent.kind === SyntaxKind.JsxAttribute ? ClassificationType.jsxAttributeStringLiteralValue : ClassificationType.stringLiteral;
|
||||
}
|
||||
else if (tokenKind === SyntaxKind.RegularExpressionLiteral) {
|
||||
// TODO: we should get another classification type for these literals.
|
||||
|
||||
@@ -233,14 +233,14 @@ namespace ts.codefix {
|
||||
let someSigHasRestParameter = false;
|
||||
for (const sig of signatures) {
|
||||
minArgumentCount = Math.min(sig.minArgumentCount, minArgumentCount);
|
||||
if (sig.hasRestParameter) {
|
||||
if (signatureHasRestParameter(sig)) {
|
||||
someSigHasRestParameter = true;
|
||||
}
|
||||
if (sig.parameters.length >= maxArgsSignature.parameters.length && (!sig.hasRestParameter || maxArgsSignature.hasRestParameter)) {
|
||||
if (sig.parameters.length >= maxArgsSignature.parameters.length && (!signatureHasRestParameter(sig) || signatureHasRestParameter(maxArgsSignature))) {
|
||||
maxArgsSignature = sig;
|
||||
}
|
||||
}
|
||||
const maxNonRestArgs = maxArgsSignature.parameters.length - (maxArgsSignature.hasRestParameter ? 1 : 0);
|
||||
const maxNonRestArgs = maxArgsSignature.parameters.length - (signatureHasRestParameter(maxArgsSignature) ? 1 : 0);
|
||||
const maxArgsParameterSymbolNames = maxArgsSignature.parameters.map(symbol => symbol.name);
|
||||
|
||||
const parameters = createDummyParameters(maxNonRestArgs, maxArgsParameterSymbolNames, /* types */ undefined, minArgumentCount, /*inJs*/ false);
|
||||
|
||||
@@ -631,6 +631,7 @@ namespace ts.codefix {
|
||||
let filteredCount = 0;
|
||||
const packageJson = filterByPackageJson && createAutoImportFilter(from, program, host);
|
||||
const allSourceFiles = program.getSourceFiles();
|
||||
const globalTypingsCache = host.getGlobalTypingsCacheLocation && host.getGlobalTypingsCacheLocation();
|
||||
forEachExternalModule(program.getTypeChecker(), allSourceFiles, (module, sourceFile) => {
|
||||
if (sourceFile === undefined) {
|
||||
if (!packageJson || packageJson.allowsImportingAmbientModule(module, allSourceFiles)) {
|
||||
@@ -640,7 +641,10 @@ namespace ts.codefix {
|
||||
filteredCount++;
|
||||
}
|
||||
}
|
||||
else if (sourceFile && sourceFile !== from && isImportablePath(from.fileName, sourceFile.fileName)) {
|
||||
else if (sourceFile &&
|
||||
sourceFile !== from &&
|
||||
isImportablePath(from.fileName, sourceFile.fileName, hostGetCanonicalFileName(host), globalTypingsCache)
|
||||
) {
|
||||
if (!packageJson || packageJson.allowsImportingSourceFile(sourceFile, allSourceFiles)) {
|
||||
cb(module);
|
||||
}
|
||||
@@ -669,10 +673,13 @@ namespace ts.codefix {
|
||||
* Don't include something from a `node_modules` that isn't actually reachable by a global import.
|
||||
* A relative import to node_modules is usually a bad idea.
|
||||
*/
|
||||
function isImportablePath(fromPath: string, toPath: string): boolean {
|
||||
function isImportablePath(fromPath: string, toPath: string, getCanonicalFileName: GetCanonicalFileName, globalCachePath?: string): boolean {
|
||||
// If it's in a `node_modules` but is not reachable from here via a global import, don't bother.
|
||||
const toNodeModules = forEachAncestorDirectory(toPath, ancestor => getBaseFileName(ancestor) === "node_modules" ? ancestor : undefined);
|
||||
return toNodeModules === undefined || startsWith(fromPath, getDirectoryPath(toNodeModules));
|
||||
const toNodeModulesParent = toNodeModules && getDirectoryPath(getCanonicalFileName(toNodeModules));
|
||||
return toNodeModulesParent === undefined
|
||||
|| startsWith(getCanonicalFileName(fromPath), toNodeModulesParent)
|
||||
|| (!!globalCachePath && startsWith(getCanonicalFileName(globalCachePath), toNodeModulesParent));
|
||||
}
|
||||
|
||||
export function moduleSymbolToValidIdentifier(moduleSymbol: Symbol, target: ScriptTarget): string {
|
||||
@@ -718,6 +725,7 @@ namespace ts.codefix {
|
||||
readFile: maybeBind(host, host.readFile),
|
||||
useCaseSensitiveFileNames: maybeBind(host, host.useCaseSensitiveFileNames),
|
||||
getProbableSymlinks: maybeBind(host, host.getProbableSymlinks) || program.getProbableSymlinks,
|
||||
getGlobalTypingsCacheLocation: maybeBind(host, host.getGlobalTypingsCacheLocation),
|
||||
};
|
||||
|
||||
let usesNodeCoreModules: boolean | undefined;
|
||||
|
||||
@@ -49,21 +49,21 @@ namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes,
|
||||
getCodeActions(context) {
|
||||
const { sourceFile, program, span: { start }, errorCode, cancellationToken, host } = context;
|
||||
const { sourceFile, program, span: { start }, errorCode, cancellationToken, host, formatContext, preferences } = context;
|
||||
|
||||
const token = getTokenAtPosition(sourceFile, start);
|
||||
let declaration!: Declaration | undefined;
|
||||
const changes = textChanges.ChangeTracker.with(context, changes => { declaration = doChange(changes, sourceFile, token, errorCode, program, cancellationToken, /*markSeen*/ returnTrue, host); });
|
||||
const changes = textChanges.ChangeTracker.with(context, changes => { declaration = doChange(changes, sourceFile, token, errorCode, program, cancellationToken, /*markSeen*/ returnTrue, host, formatContext, preferences); });
|
||||
const name = declaration && getNameOfDeclaration(declaration);
|
||||
return !name || changes.length === 0 ? undefined
|
||||
: [createCodeFixAction(fixId, changes, [getDiagnostic(errorCode, token), name.getText(sourceFile)], fixId, Diagnostics.Infer_all_types_from_usage)];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions(context) {
|
||||
const { sourceFile, program, cancellationToken, host } = context;
|
||||
const { sourceFile, program, cancellationToken, host, formatContext, preferences } = context;
|
||||
const markSeen = nodeSeenTracker();
|
||||
return codeFixAll(context, errorCodes, (changes, err) => {
|
||||
doChange(changes, sourceFile, getTokenAtPosition(err.file, err.start), err.code, program, cancellationToken, markSeen, host);
|
||||
doChange(changes, sourceFile, getTokenAtPosition(err.file, err.start), err.code, program, cancellationToken, markSeen, host, formatContext, preferences);
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -106,7 +106,7 @@ namespace ts.codefix {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, token: Node, errorCode: number, program: Program, cancellationToken: CancellationToken, markSeen: NodeSeenTracker, host: LanguageServiceHost): Declaration | undefined {
|
||||
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, token: Node, errorCode: number, program: Program, cancellationToken: CancellationToken, markSeen: NodeSeenTracker, host: LanguageServiceHost, formatContext: formatting.FormatContext, preferences: UserPreferences): Declaration | undefined {
|
||||
if (!isParameterPropertyModifier(token.kind) && token.kind !== SyntaxKind.Identifier && token.kind !== SyntaxKind.DotDotDotToken && token.kind !== SyntaxKind.ThisKeyword) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -118,7 +118,7 @@ namespace ts.codefix {
|
||||
case Diagnostics.Member_0_implicitly_has_an_1_type.code:
|
||||
case Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_where_its_type_cannot_be_determined.code:
|
||||
if ((isVariableDeclaration(parent) && markSeen(parent)) || isPropertyDeclaration(parent) || isPropertySignature(parent)) { // handle bad location
|
||||
annotateVariableDeclaration(changes, sourceFile, parent, program, host, cancellationToken);
|
||||
annotateVariableDeclaration(changes, sourceFile, parent, program, host, cancellationToken, formatContext, preferences);
|
||||
return parent;
|
||||
}
|
||||
if (isPropertyAccessExpression(parent)) {
|
||||
@@ -136,7 +136,7 @@ namespace ts.codefix {
|
||||
case Diagnostics.Variable_0_implicitly_has_an_1_type.code: {
|
||||
const symbol = program.getTypeChecker().getSymbolAtLocation(token);
|
||||
if (symbol && symbol.valueDeclaration && isVariableDeclaration(symbol.valueDeclaration) && markSeen(symbol.valueDeclaration)) {
|
||||
annotateVariableDeclaration(changes, sourceFile, symbol.valueDeclaration, program, host, cancellationToken);
|
||||
annotateVariableDeclaration(changes, sourceFile, symbol.valueDeclaration, program, host, cancellationToken, formatContext, preferences);
|
||||
return symbol.valueDeclaration;
|
||||
}
|
||||
return undefined;
|
||||
@@ -152,14 +152,14 @@ namespace ts.codefix {
|
||||
// Parameter declarations
|
||||
case Diagnostics.Parameter_0_implicitly_has_an_1_type.code:
|
||||
if (isSetAccessorDeclaration(containingFunction)) {
|
||||
annotateSetAccessor(changes, sourceFile, containingFunction, program, host, cancellationToken);
|
||||
annotateSetAccessor(changes, sourceFile, containingFunction, program, host, cancellationToken, formatContext, preferences);
|
||||
return containingFunction;
|
||||
}
|
||||
// falls through
|
||||
case Diagnostics.Rest_parameter_0_implicitly_has_an_any_type.code:
|
||||
if (markSeen(containingFunction)) {
|
||||
const param = cast(parent, isParameter);
|
||||
annotateParameters(changes, sourceFile, param, containingFunction, program, host, cancellationToken);
|
||||
annotateParameters(changes, sourceFile, param, containingFunction, program, host, cancellationToken, formatContext, preferences);
|
||||
return param;
|
||||
}
|
||||
return undefined;
|
||||
@@ -168,7 +168,7 @@ namespace ts.codefix {
|
||||
case Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation.code:
|
||||
case Diagnostics._0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type.code:
|
||||
if (isGetAccessorDeclaration(containingFunction) && isIdentifier(containingFunction.name)) {
|
||||
annotate(changes, sourceFile, containingFunction, inferTypeForVariableFromUsage(containingFunction.name, program, cancellationToken), program, host);
|
||||
annotate(changes, sourceFile, containingFunction, inferTypeForVariableFromUsage(containingFunction.name, program, cancellationToken), program, host, formatContext, preferences);
|
||||
return containingFunction;
|
||||
}
|
||||
return undefined;
|
||||
@@ -176,7 +176,7 @@ namespace ts.codefix {
|
||||
// Set Accessor declarations
|
||||
case Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation.code:
|
||||
if (isSetAccessorDeclaration(containingFunction)) {
|
||||
annotateSetAccessor(changes, sourceFile, containingFunction, program, host, cancellationToken);
|
||||
annotateSetAccessor(changes, sourceFile, containingFunction, program, host, cancellationToken, formatContext, preferences);
|
||||
return containingFunction;
|
||||
}
|
||||
return undefined;
|
||||
@@ -194,13 +194,32 @@ namespace ts.codefix {
|
||||
}
|
||||
}
|
||||
|
||||
function annotateVariableDeclaration(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: VariableDeclaration | PropertyDeclaration | PropertySignature, program: Program, host: LanguageServiceHost, cancellationToken: CancellationToken): void {
|
||||
function annotateVariableDeclaration(
|
||||
changes: textChanges.ChangeTracker,
|
||||
sourceFile: SourceFile,
|
||||
declaration: VariableDeclaration | PropertyDeclaration | PropertySignature,
|
||||
program: Program,
|
||||
host: LanguageServiceHost,
|
||||
cancellationToken: CancellationToken,
|
||||
formatContext: formatting.FormatContext,
|
||||
preferences: UserPreferences,
|
||||
): void {
|
||||
if (isIdentifier(declaration.name)) {
|
||||
annotate(changes, sourceFile, declaration, inferTypeForVariableFromUsage(declaration.name, program, cancellationToken), program, host);
|
||||
annotate(changes, sourceFile, declaration, inferTypeForVariableFromUsage(declaration.name, program, cancellationToken), program, host, formatContext, preferences);
|
||||
}
|
||||
}
|
||||
|
||||
function annotateParameters(changes: textChanges.ChangeTracker, sourceFile: SourceFile, parameterDeclaration: ParameterDeclaration, containingFunction: FunctionLike, program: Program, host: LanguageServiceHost, cancellationToken: CancellationToken): void {
|
||||
function annotateParameters(
|
||||
changes: textChanges.ChangeTracker,
|
||||
sourceFile: SourceFile,
|
||||
parameterDeclaration: ParameterDeclaration,
|
||||
containingFunction: FunctionLike,
|
||||
program: Program,
|
||||
host: LanguageServiceHost,
|
||||
cancellationToken: CancellationToken,
|
||||
formatContext: formatting.FormatContext,
|
||||
preferences: UserPreferences,
|
||||
): void {
|
||||
if (!isIdentifier(parameterDeclaration.name)) {
|
||||
return;
|
||||
}
|
||||
@@ -216,7 +235,7 @@ namespace ts.codefix {
|
||||
if (needParens) changes.insertNodeBefore(sourceFile, first(containingFunction.parameters), createToken(SyntaxKind.OpenParenToken));
|
||||
for (const { declaration, type } of parameterInferences) {
|
||||
if (declaration && !declaration.type && !declaration.initializer) {
|
||||
annotate(changes, sourceFile, declaration, type, program, host);
|
||||
annotate(changes, sourceFile, declaration, type, program, host, formatContext, preferences);
|
||||
}
|
||||
}
|
||||
if (needParens) changes.insertNodeAfter(sourceFile, last(containingFunction.parameters), createToken(SyntaxKind.CloseParenToken));
|
||||
@@ -248,7 +267,16 @@ namespace ts.codefix {
|
||||
]);
|
||||
}
|
||||
|
||||
function annotateSetAccessor(changes: textChanges.ChangeTracker, sourceFile: SourceFile, setAccessorDeclaration: SetAccessorDeclaration, program: Program, host: LanguageServiceHost, cancellationToken: CancellationToken): void {
|
||||
function annotateSetAccessor(
|
||||
changes: textChanges.ChangeTracker,
|
||||
sourceFile: SourceFile,
|
||||
setAccessorDeclaration: SetAccessorDeclaration,
|
||||
program: Program,
|
||||
host: LanguageServiceHost,
|
||||
cancellationToken: CancellationToken,
|
||||
formatContext: formatting.FormatContext,
|
||||
preferences: UserPreferences,
|
||||
): void {
|
||||
const param = firstOrUndefined(setAccessorDeclaration.parameters);
|
||||
if (param && isIdentifier(setAccessorDeclaration.name) && isIdentifier(param.name)) {
|
||||
let type = inferTypeForVariableFromUsage(setAccessorDeclaration.name, program, cancellationToken);
|
||||
@@ -259,12 +287,12 @@ namespace ts.codefix {
|
||||
annotateJSDocParameters(changes, sourceFile, [{ declaration: param, type }], program, host);
|
||||
}
|
||||
else {
|
||||
annotate(changes, sourceFile, param, type, program, host);
|
||||
annotate(changes, sourceFile, param, type, program, host, formatContext, preferences);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function annotate(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: textChanges.TypeAnnotatable, type: Type, program: Program, host: LanguageServiceHost): void {
|
||||
function annotate(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: textChanges.TypeAnnotatable, type: Type, program: Program, host: LanguageServiceHost, formatContext: formatting.FormatContext, preferences: UserPreferences): void {
|
||||
const typeNode = getTypeNodeIfAccessible(type, declaration, program, host);
|
||||
if (typeNode) {
|
||||
if (isInJSFile(sourceFile) && declaration.kind !== SyntaxKind.PropertySignature) {
|
||||
@@ -276,12 +304,42 @@ namespace ts.codefix {
|
||||
const typeTag = isGetAccessorDeclaration(declaration) ? createJSDocReturnTag(typeExpression, "") : createJSDocTypeTag(typeExpression, "");
|
||||
addJSDocTags(changes, sourceFile, parent, [typeTag]);
|
||||
}
|
||||
else {
|
||||
else if (!tryReplaceImportTypeNodeWithAutoImport(typeNode, changes, sourceFile, declaration, type, program, host, formatContext, preferences)) {
|
||||
changes.tryInsertTypeAnnotation(sourceFile, declaration, typeNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function tryReplaceImportTypeNodeWithAutoImport(typeNode: TypeNode, changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: textChanges.TypeAnnotatable, type: Type, program: Program, host: LanguageServiceHost, formatContext: formatting.FormatContext, preferences: UserPreferences): boolean {
|
||||
if (isLiteralImportTypeNode(typeNode) && typeNode.qualifier && type.symbol) {
|
||||
// Replace 'import("./a").SomeType' with 'SomeType' and an actual import if possible
|
||||
const moduleSymbol = find(type.symbol.declarations, d => !!d.getSourceFile().externalModuleIndicator)?.getSourceFile().symbol;
|
||||
// Symbol for the left-most thing after the dot
|
||||
if (moduleSymbol) {
|
||||
const symbol = getFirstIdentifier(typeNode.qualifier).symbol;
|
||||
const action = getImportCompletionAction(
|
||||
symbol,
|
||||
moduleSymbol,
|
||||
sourceFile,
|
||||
symbol.name,
|
||||
host,
|
||||
program,
|
||||
formatContext,
|
||||
declaration.pos,
|
||||
preferences,
|
||||
);
|
||||
if (action.codeAction.changes.length && changes.tryInsertTypeAnnotation(sourceFile, declaration, createTypeReferenceNode(typeNode.qualifier, typeNode.typeArguments))) {
|
||||
for (const change of action.codeAction.changes) {
|
||||
const file = sourceFile.fileName === change.fileName ? sourceFile : Debug.assertDefined(program.getSourceFile(change.fileName));
|
||||
changes.pushRaw(file, change);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function annotateJSDocParameters(changes: textChanges.ChangeTracker, sourceFile: SourceFile, parameterInferences: readonly ParameterInference[], program: Program, host: LanguageServiceHost): void {
|
||||
const signature = parameterInferences.length && parameterInferences[0].declaration.parent;
|
||||
if (!signature) {
|
||||
@@ -1054,7 +1112,7 @@ namespace ts.codefix {
|
||||
}
|
||||
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, length, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
|
||||
return checker.createSignature(/*declaration*/ undefined!, /*typeParameters*/ undefined, /*thisParameter*/ undefined, parameters, returnType, /*typePredicate*/ undefined, length, SignatureFlags.None);
|
||||
}
|
||||
|
||||
function addCandidateType(usage: Usage, type: Type | undefined) {
|
||||
|
||||
@@ -338,6 +338,7 @@ namespace ts.Completions {
|
||||
): CompletionEntry | undefined {
|
||||
let insertText: string | undefined;
|
||||
let replacementSpan: TextSpan | undefined;
|
||||
|
||||
const insertQuestionDot = origin && originIsNullableMember(origin);
|
||||
const useBraces = origin && originIsSymbolMember(origin) || needsConvertPropertyAccess;
|
||||
if (origin && originIsThisType(origin)) {
|
||||
@@ -780,7 +781,7 @@ namespace ts.Completions {
|
||||
sourceFile: SourceFile,
|
||||
isUncheckedFile: boolean,
|
||||
position: number,
|
||||
preferences: Pick<UserPreferences, "includeCompletionsForModuleExports" | "includeCompletionsWithInsertText">,
|
||||
preferences: Pick<UserPreferences, "includeCompletionsForModuleExports" | "includeCompletionsWithInsertText" | "includeAutomaticOptionalChainCompletions">,
|
||||
detailsEntryId: CompletionEntryIdentifier | undefined,
|
||||
host: LanguageServiceHost,
|
||||
): CompletionData | Request | undefined {
|
||||
@@ -1000,6 +1001,7 @@ namespace ts.Completions {
|
||||
let completionKind = CompletionKind.None;
|
||||
let isNewIdentifierLocation = false;
|
||||
let keywordFilters = KeywordCompletionFilters.None;
|
||||
// This also gets mutated in nested-functions after the return
|
||||
let symbols: Symbol[] = [];
|
||||
const symbolToOriginInfoMap: SymbolOriginInfoMap = [];
|
||||
const symbolToSortTextMap: SymbolSortTextMap = [];
|
||||
@@ -1115,8 +1117,17 @@ namespace ts.Completions {
|
||||
let type = typeChecker.getTypeOfSymbolAtLocation(symbol, node).getNonOptionalType();
|
||||
let insertQuestionDot = false;
|
||||
if (type.isNullableType()) {
|
||||
insertQuestionDot = isRightOfDot && !isRightOfQuestionDot;
|
||||
type = type.getNonNullableType();
|
||||
const canCorrectToQuestionDot =
|
||||
isRightOfDot &&
|
||||
!isRightOfQuestionDot &&
|
||||
preferences.includeAutomaticOptionalChainCompletions !== false;
|
||||
|
||||
if (canCorrectToQuestionDot || isRightOfQuestionDot) {
|
||||
type = type.getNonNullableType();
|
||||
if (canCorrectToQuestionDot) {
|
||||
insertQuestionDot = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
addTypeProperties(type, !!(node.flags & NodeFlags.AwaitContext), insertQuestionDot);
|
||||
}
|
||||
@@ -1136,8 +1147,17 @@ namespace ts.Completions {
|
||||
let type = typeChecker.getTypeAtLocation(node).getNonOptionalType();
|
||||
let insertQuestionDot = false;
|
||||
if (type.isNullableType()) {
|
||||
insertQuestionDot = isRightOfDot && !isRightOfQuestionDot;
|
||||
type = type.getNonNullableType();
|
||||
const canCorrectToQuestionDot =
|
||||
isRightOfDot &&
|
||||
!isRightOfQuestionDot &&
|
||||
preferences.includeAutomaticOptionalChainCompletions !== false;
|
||||
|
||||
if (canCorrectToQuestionDot || isRightOfQuestionDot) {
|
||||
type = type.getNonNullableType();
|
||||
if (canCorrectToQuestionDot) {
|
||||
insertQuestionDot = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
addTypeProperties(type, !!(node.flags & NodeFlags.AwaitContext), insertQuestionDot);
|
||||
}
|
||||
@@ -1464,7 +1484,7 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gathers symbols that can be imported from other files, deduplicating along the way. Symbols can be “duplicates”
|
||||
* Gathers symbols that can be imported from other files, de-duplicating along the way. Symbols can be "duplicates"
|
||||
* if re-exported from another module, e.g. `export { foo } from "./a"`. That syntax creates a fresh symbol, but
|
||||
* it’s just an alias to the first, and both have the same name, so we generally want to filter those aliases out,
|
||||
* if and only if the the first can be imported (it may be excluded due to package.json filtering in
|
||||
@@ -1548,7 +1568,7 @@ namespace ts.Completions {
|
||||
// Don't add another completion for `export =` of a symbol that's already global.
|
||||
// So in `declare namespace foo {} declare module "foo" { export = foo; }`, there will just be the global completion for `foo`.
|
||||
if (resolvedModuleSymbol !== moduleSymbol &&
|
||||
every(resolvedModuleSymbol.declarations, d => !!d.getSourceFile().externalModuleIndicator)) {
|
||||
every(resolvedModuleSymbol.declarations, d => !!d.getSourceFile().externalModuleIndicator && !findAncestor(d, isGlobalScopeAugmentation))) {
|
||||
pushSymbol(resolvedModuleSymbol, moduleSymbol, /*skipFilter*/ true);
|
||||
}
|
||||
|
||||
@@ -1760,7 +1780,7 @@ namespace ts.Completions {
|
||||
let existingMembers: readonly Declaration[] | undefined;
|
||||
|
||||
if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) {
|
||||
const typeForObject = typeChecker.getContextualType(objectLikeContainer);
|
||||
const typeForObject = typeChecker.getContextualType(objectLikeContainer, ContextFlags.Completion);
|
||||
if (!typeForObject) return GlobalsSearch.Fail;
|
||||
isNewIdentifierLocation = hasIndexSignature(typeForObject);
|
||||
typeMembers = getPropertiesForObjectExpression(typeForObject, objectLikeContainer, typeChecker);
|
||||
@@ -2406,6 +2426,8 @@ namespace ts.Completions {
|
||||
return isFunctionLikeBodyKeyword(kind)
|
||||
|| kind === SyntaxKind.DeclareKeyword
|
||||
|| kind === SyntaxKind.ModuleKeyword
|
||||
|| kind === SyntaxKind.TypeKeyword
|
||||
|| kind === SyntaxKind.NamespaceKeyword
|
||||
|| isTypeKeyword(kind) && kind !== SyntaxKind.UndefinedKeyword;
|
||||
case KeywordCompletionFilters.FunctionLikeBodyKeywords:
|
||||
return isFunctionLikeBodyKeyword(kind);
|
||||
|
||||
@@ -1395,8 +1395,10 @@ namespace ts.FindAllReferences.Core {
|
||||
|| exportSpecifier.name.originalKeywordKind === SyntaxKind.DefaultKeyword;
|
||||
const exportKind = isDefaultExport ? ExportKind.Default : ExportKind.Named;
|
||||
const exportSymbol = Debug.assertDefined(exportSpecifier.symbol);
|
||||
const exportInfo = Debug.assertDefined(getExportInfo(exportSymbol, exportKind, state.checker));
|
||||
searchForImportsOfExport(referenceLocation, exportSymbol, exportInfo, state);
|
||||
const exportInfo = getExportInfo(exportSymbol, exportKind, state.checker);
|
||||
if (exportInfo) {
|
||||
searchForImportsOfExport(referenceLocation, exportSymbol, exportInfo, state);
|
||||
}
|
||||
}
|
||||
|
||||
// At `export { x } from "foo"`, also search for the imported symbol `"foo".x`.
|
||||
|
||||
@@ -59,8 +59,8 @@ namespace ts.formatting {
|
||||
// in other cases there should be no space between '?' and next token
|
||||
rule("NoSpaceAfterQuestionMark", SyntaxKind.QuestionToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
|
||||
rule("NoSpaceBeforeDot", anyToken, SyntaxKind.DotToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceAfterDot", SyntaxKind.DotToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceBeforeDot", anyToken, [SyntaxKind.DotToken, SyntaxKind.QuestionDotToken], [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceAfterDot", [SyntaxKind.DotToken, SyntaxKind.QuestionDotToken], anyToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
|
||||
rule("NoSpaceBetweenImportParenInImportType", SyntaxKind.ImportKeyword, SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext, isImportTypeContext], RuleAction.DeleteSpace),
|
||||
|
||||
|
||||
@@ -158,25 +158,6 @@ namespace ts.JsDoc {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through 'array' by index and performs the callback on each element of array until the callback
|
||||
* returns a truthy value, then returns that value.
|
||||
* If no such value is found, the callback is applied to each element of array and undefined is returned.
|
||||
*/
|
||||
function forEachUnique<T, U>(array: readonly T[] | undefined, callback: (element: T, index: number) => U): U | undefined {
|
||||
if (array) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
if (array.indexOf(array[i]) === i) {
|
||||
const result = callback(array[i], i);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function getJSDocTagNameCompletions(): CompletionEntry[] {
|
||||
return jsDocTagNameCompletionEntries || (jsDocTagNameCompletionEntries = map(jsDocTagNames, tagName => {
|
||||
return {
|
||||
|
||||
@@ -452,9 +452,19 @@ namespace ts {
|
||||
isClass(): this is InterfaceType {
|
||||
return !!(getObjectFlags(this) & ObjectFlags.Class);
|
||||
}
|
||||
/**
|
||||
* This polyfills `referenceType.typeArguments` for API consumers
|
||||
*/
|
||||
get typeArguments() {
|
||||
if (getObjectFlags(this) & ObjectFlags.Reference) {
|
||||
return this.checker.getTypeArguments(this as Type as TypeReference);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
class SignatureObject implements Signature {
|
||||
flags: SignatureFlags;
|
||||
checker: TypeChecker;
|
||||
declaration!: SignatureDeclaration;
|
||||
typeParameters?: TypeParameter[];
|
||||
@@ -464,8 +474,6 @@ namespace ts {
|
||||
resolvedTypePredicate: TypePredicate | undefined;
|
||||
minTypeArgumentCount!: number;
|
||||
minArgumentCount!: number;
|
||||
hasRestParameter!: boolean;
|
||||
hasLiteralTypes!: boolean;
|
||||
|
||||
// Undefined is used to indicate the value has not been computed. If, after computing, the
|
||||
// symbol has no doc comment, then the empty array will be returned.
|
||||
@@ -475,9 +483,11 @@ namespace ts {
|
||||
// symbol has no doc comment, then the empty array will be returned.
|
||||
jsDocTags?: JSDocTagInfo[];
|
||||
|
||||
constructor(checker: TypeChecker) {
|
||||
constructor(checker: TypeChecker, flags: SignatureFlags) {
|
||||
this.checker = checker;
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
getDeclaration(): SignatureDeclaration {
|
||||
return this.declaration;
|
||||
}
|
||||
@@ -518,11 +528,11 @@ namespace ts {
|
||||
|
||||
let doc = JsDoc.getJsDocCommentsFromDeclarations(declarations);
|
||||
if (doc.length === 0 || declarations.some(hasJSDocInheritDocTag)) {
|
||||
for (const declaration of declarations) {
|
||||
forEachUnique(declarations, declaration => {
|
||||
const inheritedDocs = findInheritedJSDocComments(declaration, declaration.symbol.name, checker!); // TODO: GH#18217
|
||||
// TODO: GH#16312 Return a ReadonlyArray, avoid copying inheritedDocs
|
||||
if (inheritedDocs) doc = doc.length === 0 ? inheritedDocs.slice() : inheritedDocs.concat(lineBreakPart(), doc);
|
||||
}
|
||||
});
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
@@ -1629,12 +1639,12 @@ namespace ts {
|
||||
return NavigateTo.getNavigateToItems(sourceFiles, program.getTypeChecker(), cancellationToken, searchValue, maxResultCount, excludeDtsFiles);
|
||||
}
|
||||
|
||||
function getEmitOutput(fileName: string, emitOnlyDtsFiles = false) {
|
||||
function getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean, forceDtsEmit?: boolean) {
|
||||
synchronizeHostData();
|
||||
|
||||
const sourceFile = getValidSourceFile(fileName);
|
||||
const customTransformers = host.getCustomTransformers && host.getCustomTransformers();
|
||||
return getFileEmitOutput(program, sourceFile, emitOnlyDtsFiles, cancellationToken, customTransformers);
|
||||
return getFileEmitOutput(program, sourceFile, !!emitOnlyDtsFiles, cancellationToken, customTransformers, forceDtsEmit);
|
||||
}
|
||||
|
||||
// Signature help
|
||||
|
||||
@@ -204,7 +204,7 @@ namespace ts.Completions.StringCompletions {
|
||||
const candidates: Signature[] = [];
|
||||
checker.getResolvedSignature(argumentInfo.invocation, candidates, argumentInfo.argumentCount);
|
||||
const types = flatMap(candidates, candidate => {
|
||||
if (!candidate.hasRestParameter && argumentInfo.argumentCount > candidate.parameters.length) return;
|
||||
if (!signatureHasRestParameter(candidate) && argumentInfo.argumentCount > candidate.parameters.length) return;
|
||||
const type = checker.getParameterType(candidate, argumentInfo.argumentIndex);
|
||||
isNewIdentifier = isNewIdentifier || !!(type.flags & TypeFlags.String);
|
||||
return getStringLiteralTypes(type, uniques);
|
||||
|
||||
@@ -237,7 +237,7 @@ namespace ts.SymbolDisplay {
|
||||
// get the signature from the declaration and write it
|
||||
const functionDeclaration = <FunctionLike>location.parent;
|
||||
// Use function declaration to write the signatures only if the symbol corresponding to this declaration
|
||||
const locationIsSymbolDeclaration = find(symbol.declarations, declaration =>
|
||||
const locationIsSymbolDeclaration = symbol.declarations && find(symbol.declarations, declaration =>
|
||||
declaration === (location.kind === SyntaxKind.ConstructorKeyword ? functionDeclaration.parent : functionDeclaration));
|
||||
|
||||
if (locationIsSymbolDeclaration) {
|
||||
|
||||
@@ -248,6 +248,18 @@ namespace ts.textChanges {
|
||||
/** Public for tests only. Other callers should use `ChangeTracker.with`. */
|
||||
constructor(private readonly newLineCharacter: string, private readonly formatContext: formatting.FormatContext) {}
|
||||
|
||||
public pushRaw(sourceFile: SourceFile, change: FileTextChanges) {
|
||||
Debug.assertEqual(sourceFile.fileName, change.fileName);
|
||||
for (const c of change.textChanges) {
|
||||
this.changes.push({
|
||||
kind: ChangeKind.Text,
|
||||
sourceFile,
|
||||
text: c.newText,
|
||||
range: createTextRangeFromSpan(c.span),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public deleteRange(sourceFile: SourceFile, range: TextRange): void {
|
||||
this.changes.push({ kind: ChangeKind.Remove, sourceFile, range });
|
||||
}
|
||||
@@ -383,12 +395,12 @@ namespace ts.textChanges {
|
||||
}
|
||||
|
||||
/** Prefer this over replacing a node with another that has a type annotation, as it avoids reformatting the other parts of the node. */
|
||||
public tryInsertTypeAnnotation(sourceFile: SourceFile, node: TypeAnnotatable, type: TypeNode): void {
|
||||
public tryInsertTypeAnnotation(sourceFile: SourceFile, node: TypeAnnotatable, type: TypeNode): boolean {
|
||||
let endNode: Node | undefined;
|
||||
if (isFunctionLike(node)) {
|
||||
endNode = findChildOfKind(node, SyntaxKind.CloseParenToken, sourceFile);
|
||||
if (!endNode) {
|
||||
if (!isArrowFunction(node)) return; // Function missing parentheses, give up
|
||||
if (!isArrowFunction(node)) return false; // Function missing parentheses, give up
|
||||
// If no `)`, is an arrow function `x => x`, so use the end of the first parameter
|
||||
endNode = first(node.parameters);
|
||||
}
|
||||
@@ -398,6 +410,7 @@ namespace ts.textChanges {
|
||||
}
|
||||
|
||||
this.insertNodeAt(sourceFile, endNode.end, type, { prefix: ": " });
|
||||
return true;
|
||||
}
|
||||
|
||||
public tryInsertThisTypeAnnotation(sourceFile: SourceFile, node: ThisTypeAnnotatable, type: TypeNode): void {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"outFile": "../../built/local/services.js"
|
||||
},
|
||||
"references": [
|
||||
{ "path": "../shims" },
|
||||
{ "path": "../compiler" },
|
||||
{ "path": "../jsTyping" }
|
||||
],
|
||||
|
||||
@@ -68,6 +68,10 @@ namespace ts {
|
||||
isClass(): this is InterfaceType;
|
||||
}
|
||||
|
||||
export interface TypeReference {
|
||||
typeArguments?: readonly Type[];
|
||||
}
|
||||
|
||||
export interface Signature {
|
||||
getDeclaration(): SignatureDeclaration;
|
||||
getTypeParameters(): TypeParameter[] | undefined;
|
||||
@@ -239,6 +243,8 @@ namespace ts {
|
||||
resolveTypeReferenceDirectives?(typeDirectiveNames: string[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions): (ResolvedTypeReferenceDirective | undefined)[];
|
||||
/* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution;
|
||||
/* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean;
|
||||
/* @internal */
|
||||
getGlobalTypingsCacheLocation?(): string | undefined;
|
||||
|
||||
/*
|
||||
* Required for full import and type reference completions.
|
||||
@@ -387,7 +393,7 @@ namespace ts {
|
||||
organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[];
|
||||
getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[];
|
||||
|
||||
getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput;
|
||||
getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean, forceDtsEmit?: boolean): EmitOutput;
|
||||
|
||||
getProgram(): Program | undefined;
|
||||
|
||||
@@ -534,7 +540,7 @@ namespace ts {
|
||||
|
||||
export interface FileTextChanges {
|
||||
fileName: string;
|
||||
textChanges: TextChange[];
|
||||
textChanges: readonly TextChange[];
|
||||
isNewFile?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -1458,6 +1458,25 @@ namespace ts {
|
||||
export function documentSpansEqual(a: DocumentSpan, b: DocumentSpan): boolean {
|
||||
return a.fileName === b.fileName && textSpansEqual(a.textSpan, b.textSpan);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through 'array' by index and performs the callback on each element of array until the callback
|
||||
* returns a truthy value, then returns that value.
|
||||
* If no such value is found, the callback is applied to each element of array and undefined is returned.
|
||||
*/
|
||||
export function forEachUnique<T, U>(array: readonly T[] | undefined, callback: (element: T, index: number) => U): U | undefined {
|
||||
if (array) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
if (array.indexOf(array[i]) === i) {
|
||||
const result = callback(array[i], i);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// Display-part writer helpers
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
// NOTE: Due to how the project-reference merging ends up working, `T` isn't considered referenced until `Map` merges with the definition
|
||||
// in src/compiler/core.ts
|
||||
// @ts-ignore
|
||||
export interface Map<T> {
|
||||
// full type defined in ~/src/compiler/core.ts
|
||||
}
|
||||
|
||||
export function createMapShim(): new <T>() => Map<T> {
|
||||
/** Create a MapLike with good performance. */
|
||||
function createDictionaryObject<T>(): Record<string, T> {
|
||||
const map = Object.create(/*prototype*/ null); // eslint-disable-line no-null/no-null
|
||||
|
||||
// Using 'delete' on an object causes V8 to put the object in dictionary mode.
|
||||
// This disables creation of hidden classes, which are expensive when an object is
|
||||
// constantly changing shape.
|
||||
map.__ = undefined;
|
||||
delete map.__;
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
interface MapEntry<T> {
|
||||
readonly key?: string;
|
||||
value?: T;
|
||||
|
||||
// Linked list references for iterators.
|
||||
nextEntry?: MapEntry<T>;
|
||||
previousEntry?: MapEntry<T>;
|
||||
|
||||
/**
|
||||
* Specifies if iterators should skip the next entry.
|
||||
* This will be set when an entry is deleted.
|
||||
* See https://github.com/Microsoft/TypeScript/pull/27292 for more information.
|
||||
*/
|
||||
skipNext?: boolean;
|
||||
}
|
||||
|
||||
class MapIterator<T, U extends (string | T | [string, T])> {
|
||||
private currentEntry?: MapEntry<T>;
|
||||
private selector: (key: string, value: T) => U;
|
||||
|
||||
constructor(currentEntry: MapEntry<T>, selector: (key: string, value: T) => U) {
|
||||
this.currentEntry = currentEntry;
|
||||
this.selector = selector;
|
||||
}
|
||||
|
||||
public next(): { value: U, done: false } | { value: never, done: true } {
|
||||
// Navigate to the next entry.
|
||||
while (this.currentEntry) {
|
||||
const skipNext = !!this.currentEntry.skipNext;
|
||||
this.currentEntry = this.currentEntry.nextEntry;
|
||||
|
||||
if (!skipNext) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.currentEntry) {
|
||||
return { value: this.selector(this.currentEntry.key!, this.currentEntry.value!), done: false };
|
||||
}
|
||||
else {
|
||||
return { value: undefined as never, done: true };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return class <T> implements Map<T> {
|
||||
private data = createDictionaryObject<MapEntry<T>>();
|
||||
public size = 0;
|
||||
|
||||
// Linked list references for iterators.
|
||||
// See https://github.com/Microsoft/TypeScript/pull/27292
|
||||
// for more information.
|
||||
|
||||
/**
|
||||
* The first entry in the linked list.
|
||||
* Note that this is only a stub that serves as starting point
|
||||
* for iterators and doesn't contain a key and a value.
|
||||
*/
|
||||
private readonly firstEntry: MapEntry<T>;
|
||||
private lastEntry: MapEntry<T>;
|
||||
|
||||
constructor() {
|
||||
// Create a first (stub) map entry that will not contain a key
|
||||
// and value but serves as starting point for iterators.
|
||||
this.firstEntry = {};
|
||||
// When the map is empty, the last entry is the same as the
|
||||
// first one.
|
||||
this.lastEntry = this.firstEntry;
|
||||
}
|
||||
|
||||
get(key: string): T | undefined {
|
||||
const entry = this.data[key] as MapEntry<T> | undefined;
|
||||
return entry && entry.value!;
|
||||
}
|
||||
|
||||
set(key: string, value: T): this {
|
||||
if (!this.has(key)) {
|
||||
this.size++;
|
||||
|
||||
// Create a new entry that will be appended at the
|
||||
// end of the linked list.
|
||||
const newEntry: MapEntry<T> = {
|
||||
key,
|
||||
value
|
||||
};
|
||||
this.data[key] = newEntry;
|
||||
|
||||
// Adjust the references.
|
||||
const previousLastEntry = this.lastEntry;
|
||||
previousLastEntry.nextEntry = newEntry;
|
||||
newEntry.previousEntry = previousLastEntry;
|
||||
this.lastEntry = newEntry;
|
||||
}
|
||||
else {
|
||||
this.data[key].value = value;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
has(key: string): boolean {
|
||||
// eslint-disable-next-line no-in-operator
|
||||
return key in this.data;
|
||||
}
|
||||
|
||||
delete(key: string): boolean {
|
||||
if (this.has(key)) {
|
||||
this.size--;
|
||||
const entry = this.data[key];
|
||||
delete this.data[key];
|
||||
|
||||
// Adjust the linked list references of the neighbor entries.
|
||||
const previousEntry = entry.previousEntry!;
|
||||
previousEntry.nextEntry = entry.nextEntry;
|
||||
if (entry.nextEntry) {
|
||||
entry.nextEntry.previousEntry = previousEntry;
|
||||
}
|
||||
|
||||
// When the deleted entry was the last one, we need to
|
||||
// adjust the lastEntry reference.
|
||||
if (this.lastEntry === entry) {
|
||||
this.lastEntry = previousEntry;
|
||||
}
|
||||
|
||||
// Adjust the forward reference of the deleted entry
|
||||
// in case an iterator still references it. This allows us
|
||||
// to throw away the entry, but when an active iterator
|
||||
// (which points to the current entry) continues, it will
|
||||
// navigate to the entry that originally came before the
|
||||
// current one and skip it.
|
||||
entry.previousEntry = undefined;
|
||||
entry.nextEntry = previousEntry;
|
||||
entry.skipNext = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.data = createDictionaryObject<MapEntry<T>>();
|
||||
this.size = 0;
|
||||
|
||||
// Reset the linked list. Note that we must adjust the forward
|
||||
// references of the deleted entries to ensure iterators stuck
|
||||
// in the middle of the list don't continue with deleted entries,
|
||||
// but can continue with new entries added after the clear()
|
||||
// operation.
|
||||
const firstEntry = this.firstEntry;
|
||||
let currentEntry = firstEntry.nextEntry;
|
||||
while (currentEntry) {
|
||||
const nextEntry = currentEntry.nextEntry;
|
||||
currentEntry.previousEntry = undefined;
|
||||
currentEntry.nextEntry = firstEntry;
|
||||
currentEntry.skipNext = true;
|
||||
|
||||
currentEntry = nextEntry;
|
||||
}
|
||||
firstEntry.nextEntry = undefined;
|
||||
this.lastEntry = firstEntry;
|
||||
}
|
||||
|
||||
keys(): Iterator<string> {
|
||||
return new MapIterator(this.firstEntry, key => key);
|
||||
}
|
||||
|
||||
values(): Iterator<T> {
|
||||
return new MapIterator(this.firstEntry, (_key, value) => value);
|
||||
}
|
||||
|
||||
entries(): Iterator<[string, T]> {
|
||||
return new MapIterator(this.firstEntry, (key, value) => [key, value] as [string, T]);
|
||||
}
|
||||
|
||||
forEach(action: (value: T, key: string) => void): void {
|
||||
const iterator = this.entries();
|
||||
while (true) {
|
||||
const iterResult = iterator.next();
|
||||
if (iterResult.done) {
|
||||
break;
|
||||
}
|
||||
|
||||
const [key, value] = iterResult.value;
|
||||
action(value, key);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../tsconfig-base",
|
||||
"compilerOptions": {
|
||||
"outFile": "../../built/local/shims.js"
|
||||
},
|
||||
"files": [
|
||||
"mapShim.ts"
|
||||
]
|
||||
}
|
||||
@@ -147,7 +147,7 @@ namespace Harness.Parallel.Host {
|
||||
}
|
||||
|
||||
cursor.hide();
|
||||
readline.moveCursor(process.stdout, -process.stdout.columns!, -this._lineCount);
|
||||
readline.moveCursor(process.stdout, -process.stdout.columns, -this._lineCount);
|
||||
let lineCount = 0;
|
||||
const numProgressBars = this._progressBars.length;
|
||||
for (let i = 0; i < numProgressBars; i++) {
|
||||
@@ -156,7 +156,7 @@ namespace Harness.Parallel.Host {
|
||||
process.stdout.write(this._progressBars[i].text + os.EOL);
|
||||
}
|
||||
else {
|
||||
readline.moveCursor(process.stdout, -process.stdout.columns!, +1);
|
||||
readline.moveCursor(process.stdout, -process.stdout.columns, +1);
|
||||
}
|
||||
|
||||
lineCount++;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
]
|
||||
},
|
||||
"references": [
|
||||
{ "path": "../shims", "prepend": true },
|
||||
{ "path": "../compiler", "prepend": true },
|
||||
{ "path": "../services", "prepend": true },
|
||||
{ "path": "../jsTyping", "prepend": true },
|
||||
@@ -38,6 +39,7 @@
|
||||
|
||||
"unittests/services/extract/helpers.ts",
|
||||
"unittests/tsbuild/helpers.ts",
|
||||
"../tsc/executeCommandLine.ts",
|
||||
"unittests/tsc/helpers.ts",
|
||||
"unittests/tscWatch/helpers.ts",
|
||||
"unittests/tsserver/helpers.ts",
|
||||
@@ -60,7 +62,7 @@
|
||||
"unittests/publicApi.ts",
|
||||
"unittests/reuseProgramStructure.ts",
|
||||
"unittests/semver.ts",
|
||||
"unittests/shimMap.ts",
|
||||
"unittests/createMapShim.ts",
|
||||
"unittests/transform.ts",
|
||||
"unittests/config/commandLineParsing.ts",
|
||||
"unittests/config/configurationExtension.ts",
|
||||
@@ -98,6 +100,7 @@
|
||||
"unittests/tsbuild/demo.ts",
|
||||
"unittests/tsbuild/emitDeclarationOnly.ts",
|
||||
"unittests/tsbuild/emptyFiles.ts",
|
||||
"unittests/tsbuild/exitCodeOnBogusFile.ts",
|
||||
"unittests/tsbuild/graphOrdering.ts",
|
||||
"unittests/tsbuild/inferredTypeFromTransitiveModule.ts",
|
||||
"unittests/tsbuild/javascriptProjectEmit.ts",
|
||||
@@ -112,6 +115,8 @@
|
||||
"unittests/tsbuild/watchEnvironment.ts",
|
||||
"unittests/tsbuild/watchMode.ts",
|
||||
"unittests/tsc/declarationEmit.ts",
|
||||
"unittests/tsc/incremental.ts",
|
||||
"unittests/tsc/listFilesOnly.ts",
|
||||
"unittests/tscWatch/consoleClearing.ts",
|
||||
"unittests/tscWatch/emit.ts",
|
||||
"unittests/tscWatch/emitAndErrorUpdates.ts",
|
||||
|
||||
@@ -446,6 +446,23 @@ namespace ts {
|
||||
});
|
||||
});
|
||||
|
||||
it("parse build with listFilesOnly ", () => {
|
||||
// --lib es6 0.ts
|
||||
assertParseResult(["--listFilesOnly"],
|
||||
{
|
||||
errors: [{
|
||||
messageText:"Unknown build option '--listFilesOnly'.",
|
||||
category: Diagnostics.Unknown_build_option_0.category,
|
||||
code: Diagnostics.Unknown_build_option_0.code,
|
||||
file: undefined,
|
||||
start: undefined,
|
||||
length: undefined,
|
||||
}],
|
||||
projects: ["."],
|
||||
buildOptions: {}
|
||||
});
|
||||
});
|
||||
|
||||
it("Parse multiple flags with input projects at the end", () => {
|
||||
// --lib es5,es2015.symbol.wellknown --target es5 0.ts
|
||||
assertParseResult(["--force", "--verbose", "src", "tests"],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace ts {
|
||||
describe("unittests:: shimMap", () => {
|
||||
describe("unittests:: createMapShim", () => {
|
||||
|
||||
function testMapIterationAddedValues(map: Map<string>, useForEach: boolean): string {
|
||||
let resultString = "";
|
||||
@@ -93,15 +93,15 @@ namespace ts {
|
||||
const nativeMapIteratorResult = testMapIterationAddedValues(nativeMap, /* useForEach */ false);
|
||||
assert.equal(nativeMapIteratorResult, expectedResult, "nativeMap-iterator");
|
||||
|
||||
// Then, test the shimMap.
|
||||
let localShimMap = new (shimMap())<string>();
|
||||
// Then, test the map shim.
|
||||
const MapShim = createMapShim(); // tslint:disable-line variable-name
|
||||
let localShimMap = new MapShim<string>();
|
||||
const shimMapForEachResult = testMapIterationAddedValues(localShimMap, /* useForEach */ true);
|
||||
assert.equal(shimMapForEachResult, expectedResult, "shimMap-forEach");
|
||||
|
||||
localShimMap = new (shimMap())<string>();
|
||||
localShimMap = new MapShim<string>();
|
||||
const shimMapIteratorResult = testMapIterationAddedValues(localShimMap, /* useForEach */ false);
|
||||
assert.equal(shimMapIteratorResult, expectedResult, "shimMap-iterator");
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -15,10 +15,9 @@ class Carousel<T> extends Vue {
|
||||
"vue-class-component.d.ts": `import Vue from "./vue";
|
||||
export function Component(x: Config): any;`
|
||||
};
|
||||
// Regression test for GH #18245 - bug in single line comment writer caused a debug assertion when attempting
|
||||
// to write an alias to a module's default export was referrenced across files and had no default export
|
||||
it("should be able to create a language service which can respond to deinition requests without throwing", () => {
|
||||
const languageService = createLanguageService({
|
||||
|
||||
function createLanguageService() {
|
||||
return ts.createLanguageService({
|
||||
getCompilationSettings() {
|
||||
return {};
|
||||
},
|
||||
@@ -39,8 +38,45 @@ export function Component(x: Config): any;`
|
||||
return getDefaultLibFilePath(options);
|
||||
},
|
||||
});
|
||||
}
|
||||
// Regression test for GH #18245 - bug in single line comment writer caused a debug assertion when attempting
|
||||
// to write an alias to a module's default export was referrenced across files and had no default export
|
||||
it("should be able to create a language service which can respond to deinition requests without throwing", () => {
|
||||
const languageService = createLanguageService();
|
||||
const definitions = languageService.getDefinitionAtPosition("foo.ts", 160); // 160 is the latter `vueTemplateHtml` position
|
||||
expect(definitions).to.exist; // eslint-disable-line no-unused-expressions
|
||||
});
|
||||
|
||||
it("getEmitOutput on language service has way to force dts emit", () => {
|
||||
const languageService = createLanguageService();
|
||||
assert.deepEqual(
|
||||
languageService.getEmitOutput(
|
||||
"foo.ts",
|
||||
/*emitOnlyDtsFiles*/ true
|
||||
),
|
||||
{
|
||||
emitSkipped: true,
|
||||
outputFiles: emptyArray,
|
||||
exportedModulesFromDeclarationEmit: undefined
|
||||
}
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
languageService.getEmitOutput(
|
||||
"foo.ts",
|
||||
/*emitOnlyDtsFiles*/ true,
|
||||
/*forceDtsEmit*/ true
|
||||
),
|
||||
{
|
||||
emitSkipped: false,
|
||||
outputFiles: [{
|
||||
name: "foo.d.ts",
|
||||
text: "export {};\r\n",
|
||||
writeByteOrderMark: false
|
||||
}],
|
||||
exportedModulesFromDeclarationEmit: undefined
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -9,40 +9,12 @@ namespace ts {
|
||||
projFs = undefined!; // Release the contents
|
||||
});
|
||||
|
||||
function outputs(folder: string) {
|
||||
return [
|
||||
`${folder}/index.js`,
|
||||
`${folder}/index.d.ts`,
|
||||
`${folder}/tsconfig.tsbuildinfo`
|
||||
];
|
||||
}
|
||||
|
||||
it("verify that subsequent builds after initial build doesnt build anything", () => {
|
||||
const fs = projFs.shadow();
|
||||
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"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/src/folder/tsconfig.json", "src/src/folder/index.js"],
|
||||
[Diagnostics.Building_project_0, "/src/src/folder/tsconfig.json"],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/src/folder2/tsconfig.json", "src/src/folder2/index.js"],
|
||||
[Diagnostics.Building_project_0, "/src/src/folder2/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"],
|
||||
);
|
||||
verifyOutputsPresent(fs, [
|
||||
...outputs("/src/src/folder"),
|
||||
...outputs("/src/src/folder2"),
|
||||
...outputs("/src/tests"),
|
||||
]);
|
||||
host.clearDiagnostics();
|
||||
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"),
|
||||
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/src/folder/tsconfig.json", "src/src/folder/index.ts", "src/src/folder/index.js"],
|
||||
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/src/folder2/tsconfig.json", "src/src/folder2/index.ts", "src/src/folder2/index.js"],
|
||||
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/tests/tsconfig.json", "src/tests/index.ts", "src/tests/index.js"],
|
||||
);
|
||||
verifyTscIncrementalEdits({
|
||||
scenario: "containerOnlyReferenced",
|
||||
subScenario: "verify that subsequent builds after initial build doesnt build anything",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src", "--verbose"],
|
||||
incrementalScenarios: [noChangeRun]
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -9,152 +9,41 @@ namespace ts {
|
||||
projFs = undefined!; // Release the contents
|
||||
});
|
||||
|
||||
function coreOutputs(): string[] {
|
||||
return [
|
||||
"/src/lib/core/utilities.js",
|
||||
"/src/lib/core/utilities.d.ts",
|
||||
"/src/lib/core/tsconfig.tsbuildinfo"
|
||||
];
|
||||
}
|
||||
|
||||
function animalOutputs(): string[] {
|
||||
return [
|
||||
"/src/lib/animals/animal.js",
|
||||
"/src/lib/animals/animal.d.ts",
|
||||
"/src/lib/animals/index.js",
|
||||
"/src/lib/animals/index.d.ts",
|
||||
"/src/lib/animals/dog.js",
|
||||
"/src/lib/animals/dog.d.ts",
|
||||
"/src/lib/animals/tsconfig.tsbuildinfo"
|
||||
];
|
||||
}
|
||||
|
||||
function zooOutputs(): string[] {
|
||||
return [
|
||||
"/src/lib/zoo/zoo.js",
|
||||
"/src/lib/zoo/zoo.d.ts",
|
||||
"/src/lib/zoo/tsconfig.tsbuildinfo"
|
||||
];
|
||||
}
|
||||
|
||||
interface VerifyBuild {
|
||||
modifyDiskLayout: (fs: vfs.FileSystem) => void;
|
||||
expectedExitStatus: ExitStatus;
|
||||
expectedDiagnostics: (fs: vfs.FileSystem) => fakes.ExpectedDiagnostic[];
|
||||
expectedOutputs: readonly string[];
|
||||
notExpectedOutputs: readonly string[];
|
||||
}
|
||||
|
||||
function verifyBuild({ modifyDiskLayout, expectedExitStatus, expectedDiagnostics, expectedOutputs, notExpectedOutputs }: VerifyBuild) {
|
||||
const fs = projFs.shadow();
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
modifyDiskLayout(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tsconfig.json"], { verbose: true });
|
||||
const exitStatus = builder.build();
|
||||
assert.equal(exitStatus, expectedExitStatus);
|
||||
host.assertDiagnosticMessages(...expectedDiagnostics(fs));
|
||||
verifyOutputsPresent(fs, expectedOutputs);
|
||||
verifyOutputsAbsent(fs, notExpectedOutputs);
|
||||
}
|
||||
|
||||
it("in master branch with everything setup correctly, reports no error", () => {
|
||||
verifyBuild({
|
||||
modifyDiskLayout: noop,
|
||||
expectedExitStatus: ExitStatus.Success,
|
||||
expectedDiagnostics: () => [
|
||||
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/animals/tsconfig.json", "src/zoo/tsconfig.json", "src/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/core/tsconfig.json", "src/lib/core/utilities.js"],
|
||||
[Diagnostics.Building_project_0, "/src/core/tsconfig.json"],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/animals/tsconfig.json", "src/lib/animals/animal.js"],
|
||||
[Diagnostics.Building_project_0, "/src/animals/tsconfig.json"],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/zoo/tsconfig.json", "src/lib/zoo/zoo.js"],
|
||||
[Diagnostics.Building_project_0, "/src/zoo/tsconfig.json"]
|
||||
],
|
||||
expectedOutputs: [...coreOutputs(), ...animalOutputs(), ...zooOutputs()],
|
||||
notExpectedOutputs: emptyArray
|
||||
});
|
||||
verifyTsc({
|
||||
scenario: "demo",
|
||||
subScenario: "in master branch with everything setup correctly and reports no error",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/tsconfig.json", "--verbose"]
|
||||
});
|
||||
|
||||
it("in circular branch reports the error about it by stopping build", () => {
|
||||
verifyBuild({
|
||||
modifyDiskLayout: fs => replaceText(
|
||||
fs,
|
||||
"/src/core/tsconfig.json",
|
||||
"}",
|
||||
`},
|
||||
verifyTsc({
|
||||
scenario: "demo",
|
||||
subScenario: "in circular branch reports the error about it by stopping build",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/tsconfig.json", "--verbose"],
|
||||
modifyFs: fs => replaceText(
|
||||
fs,
|
||||
"/src/core/tsconfig.json",
|
||||
"}",
|
||||
`},
|
||||
"references": [
|
||||
{
|
||||
"path": "../zoo"
|
||||
}
|
||||
]`
|
||||
),
|
||||
expectedExitStatus: ExitStatus.ProjectReferenceCycle_OutputsSkupped,
|
||||
expectedDiagnostics: () => [
|
||||
getExpectedDiagnosticForProjectsInBuild("src/animals/tsconfig.json", "src/zoo/tsconfig.json", "src/core/tsconfig.json", "src/tsconfig.json"),
|
||||
errorDiagnostic([
|
||||
Diagnostics.Project_references_may_not_form_a_circular_graph_Cycle_detected_Colon_0,
|
||||
[
|
||||
"/src/tsconfig.json",
|
||||
"/src/core/tsconfig.json",
|
||||
"/src/zoo/tsconfig.json",
|
||||
"/src/animals/tsconfig.json"
|
||||
].join("\r\n")
|
||||
])
|
||||
],
|
||||
expectedOutputs: emptyArray,
|
||||
notExpectedOutputs: [...coreOutputs(), ...animalOutputs(), ...zooOutputs()]
|
||||
});
|
||||
)
|
||||
});
|
||||
|
||||
it("in bad-ref branch reports the error about files not in rootDir at the import location", () => {
|
||||
verifyBuild({
|
||||
modifyDiskLayout: fs => prependText(
|
||||
fs,
|
||||
"/src/core/utilities.ts",
|
||||
`import * as A from '../animals';
|
||||
verifyTsc({
|
||||
scenario: "demo",
|
||||
subScenario: "in bad-ref branch reports the error about files not in rootDir at the import location",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/tsconfig.json", "--verbose"],
|
||||
modifyFs: fs => prependText(
|
||||
fs,
|
||||
"/src/core/utilities.ts",
|
||||
`import * as A from '../animals';
|
||||
`
|
||||
),
|
||||
expectedExitStatus: ExitStatus.DiagnosticsPresent_OutputsSkipped,
|
||||
expectedDiagnostics: fs => [
|
||||
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/animals/tsconfig.json", "src/zoo/tsconfig.json", "src/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/core/tsconfig.json", "src/lib/core/utilities.js"],
|
||||
[Diagnostics.Building_project_0, "/src/core/tsconfig.json"],
|
||||
{
|
||||
message: [Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, "/src/animals/animal.ts", "/src/core"],
|
||||
location: expectedLocationIndexOf(fs, "/src/animals/index.ts", `'./animal'`),
|
||||
},
|
||||
{
|
||||
message: [Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, "/src/animals/animal.ts", "/src/core/tsconfig.json"],
|
||||
location: expectedLocationIndexOf(fs, "/src/animals/index.ts", `'./animal'`),
|
||||
},
|
||||
{
|
||||
message: [Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, "/src/animals/dog.ts", "/src/core"],
|
||||
location: expectedLocationIndexOf(fs, "/src/animals/index.ts", `'./dog'`),
|
||||
},
|
||||
{
|
||||
message: [Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, "/src/animals/dog.ts", "/src/core/tsconfig.json"],
|
||||
location: expectedLocationIndexOf(fs, "/src/animals/index.ts", `'./dog'`),
|
||||
},
|
||||
{
|
||||
message: [Diagnostics._0_is_declared_but_its_value_is_never_read, "A"],
|
||||
location: expectedLocationIndexOf(fs, "/src/core/utilities.ts", `import * as A from '../animals';`),
|
||||
},
|
||||
{
|
||||
message: [Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, "/src/animals/index.ts", "/src/core"],
|
||||
location: expectedLocationIndexOf(fs, "/src/core/utilities.ts", `'../animals'`),
|
||||
},
|
||||
{
|
||||
message: [Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, "/src/animals/index.ts", "/src/core/tsconfig.json"],
|
||||
location: expectedLocationIndexOf(fs, "/src/core/utilities.ts", `'../animals'`),
|
||||
},
|
||||
[Diagnostics.Project_0_can_t_be_built_because_its_dependency_1_has_errors, "src/animals/tsconfig.json", "src/core"],
|
||||
[Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors, "/src/animals/tsconfig.json", "/src/core"],
|
||||
[Diagnostics.Project_0_can_t_be_built_because_its_dependency_1_was_not_built, "src/zoo/tsconfig.json", "src/animals"],
|
||||
[Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_was_not_built, "/src/zoo/tsconfig.json", "/src/animals"],
|
||||
],
|
||||
expectedOutputs: emptyArray,
|
||||
notExpectedOutputs: [...coreOutputs(), ...animalOutputs(), ...zooOutputs()]
|
||||
});
|
||||
)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,40 +1,25 @@
|
||||
namespace ts {
|
||||
const projFs = loadProjectFromDisk("tests/projects/empty-files");
|
||||
|
||||
const allExpectedOutputs = [
|
||||
"/src/core/index.js",
|
||||
"/src/core/index.d.ts",
|
||||
"/src/core/index.d.ts.map",
|
||||
];
|
||||
|
||||
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 = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/no-references"], { dry: false, force: false, verbose: false });
|
||||
|
||||
host.clearDiagnostics();
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages({
|
||||
message: [Diagnostics.The_files_list_in_config_file_0_is_empty, "/src/no-references/tsconfig.json"],
|
||||
location: expectedLocationLastIndexOf(fs, "/src/no-references/tsconfig.json", "[]"),
|
||||
});
|
||||
|
||||
// Check for outputs to not be written.
|
||||
verifyOutputsAbsent(fs, allExpectedOutputs);
|
||||
let projFs: vfs.FileSystem;
|
||||
before(() => {
|
||||
projFs = loadProjectFromDisk("tests/projects/empty-files");
|
||||
});
|
||||
after(() => {
|
||||
projFs = undefined!;
|
||||
});
|
||||
|
||||
it("does not have empty files diagnostic when files is empty and references are provided", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/with-references"], { dry: false, force: false, verbose: false });
|
||||
verifyTsc({
|
||||
scenario: "emptyFiles",
|
||||
subScenario: "has empty files diagnostic when files is empty and no references are provided",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/no-references"],
|
||||
});
|
||||
|
||||
host.clearDiagnostics();
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(/*empty*/);
|
||||
|
||||
// Check for outputs to be written.
|
||||
verifyOutputsPresent(fs, allExpectedOutputs);
|
||||
verifyTsc({
|
||||
scenario: "emptyFiles",
|
||||
subScenario: "does not have empty files diagnostic when files is empty and references are provided",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/with-references"],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace ts {
|
||||
// https://github.com/microsoft/TypeScript/issues/33849
|
||||
describe("unittests:: tsbuild:: exitCodeOnBogusFile:: test exit code", () => {
|
||||
verifyTsc({
|
||||
scenario: "exitCodeOnBogusFile",
|
||||
subScenario: `test exit code`,
|
||||
fs: () => loadProjectFromFiles({}, symbolLibContent),
|
||||
commandLineArgs: ["-b", "bogus.json"]
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -228,7 +228,7 @@ interface Symbol {
|
||||
if (section.kind !== BundleFileSectionKind.Prepend) {
|
||||
writeTextOfSection(section.pos, section.end);
|
||||
}
|
||||
else {
|
||||
else if (section.texts.length > 0) {
|
||||
Debug.assert(section.pos === first(section.texts).pos);
|
||||
Debug.assert(section.end === last(section.texts).end);
|
||||
for (const text of section.texts) {
|
||||
@@ -237,6 +237,9 @@ interface Symbol {
|
||||
writeTextOfSection(text.pos, text.end);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Debug.assert(section.pos === section.end);
|
||||
}
|
||||
}
|
||||
baselineRecorder.WriteLine("======================================================================");
|
||||
|
||||
@@ -276,6 +279,7 @@ interface Symbol {
|
||||
buildKind: BuildKind;
|
||||
modifyFs: (fs: vfs.FileSystem) => void;
|
||||
subScenario?: string;
|
||||
commandLineArgs?: readonly string[];
|
||||
}
|
||||
|
||||
export interface VerifyTsBuildInput extends TscCompile {
|
||||
@@ -287,7 +291,7 @@ interface Symbol {
|
||||
baselineSourceMap, modifyFs, baselineReadFileCalls,
|
||||
incrementalScenarios
|
||||
}: VerifyTsBuildInput) {
|
||||
describe(`tsc --b ${scenario}:: ${subScenario}`, () => {
|
||||
describe(`tsc ${commandLineArgs.join(" ")} ${scenario}:: ${subScenario}`, () => {
|
||||
let tick: () => void;
|
||||
let sys: TscCompileSystem;
|
||||
before(() => {
|
||||
@@ -315,7 +319,12 @@ interface Symbol {
|
||||
verifyTscBaseline(() => sys);
|
||||
});
|
||||
|
||||
for (const { buildKind, modifyFs, subScenario: incrementalSubScenario } of incrementalScenarios) {
|
||||
for (const {
|
||||
buildKind,
|
||||
modifyFs,
|
||||
subScenario: incrementalSubScenario,
|
||||
commandLineArgs: incrementalCommandLineArgs
|
||||
} of incrementalScenarios) {
|
||||
describe(incrementalSubScenario || buildKind, () => {
|
||||
let newSys: TscCompileSystem;
|
||||
before(() => {
|
||||
@@ -326,7 +335,7 @@ interface Symbol {
|
||||
subScenario: incrementalSubScenario || subScenario,
|
||||
buildKind,
|
||||
fs: () => sys.vfs,
|
||||
commandLineArgs,
|
||||
commandLineArgs: incrementalCommandLineArgs || commandLineArgs,
|
||||
modifyFs: fs => {
|
||||
tick();
|
||||
modifyFs(fs);
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
namespace ts {
|
||||
describe("unittests:: tsbuild:: when tsconfig extends the missing file", () => {
|
||||
it("unittests:: tsbuild - when tsconfig extends the missing file", () => {
|
||||
const projFs = loadProjectFromDisk("tests/projects/missingExtendedConfig");
|
||||
const fs = projFs.shadow();
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tsconfig.json"], {});
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
errorDiagnostic([Diagnostics.The_specified_path_does_not_exist_Colon_0, "/src/foobar.json"]),
|
||||
errorDiagnostic([Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, "/src/tsconfig.first.json", "[\"**/*\"]", "[]"]),
|
||||
errorDiagnostic([Diagnostics.The_specified_path_does_not_exist_Colon_0, "/src/foobar.json"]),
|
||||
errorDiagnostic([Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, "/src/tsconfig.second.json", "[\"**/*\"]", "[]"])
|
||||
);
|
||||
let projFs: vfs.FileSystem;
|
||||
before(() => {
|
||||
projFs = loadProjectFromDisk("tests/projects/missingExtendedConfig");
|
||||
});
|
||||
after(() => {
|
||||
projFs = undefined!;
|
||||
});
|
||||
verifyTsc({
|
||||
scenario: "missingExtendedConfig",
|
||||
subScenario: "when tsconfig extends the missing file",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/tsconfig.json"],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -56,11 +56,6 @@ namespace ts {
|
||||
]
|
||||
];
|
||||
const relSources = sources.map(([config, sources]) => [relName(config), sources.map(relName)]) as any as [Sources, Sources, Sources];
|
||||
let expectedOutputFiles = [
|
||||
...outputFiles[project.first],
|
||||
...outputFiles[project.second],
|
||||
...outputFiles[project.third]
|
||||
];
|
||||
let initialExpectedDiagnostics: 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_output_file_1_does_not_exist, relSources[project.first][source.config], relOutputFiles[project.first][ext.js]],
|
||||
@@ -75,7 +70,6 @@ namespace ts {
|
||||
});
|
||||
after(() => {
|
||||
outFileFs = undefined!;
|
||||
expectedOutputFiles = undefined!;
|
||||
initialExpectedDiagnostics = undefined!;
|
||||
});
|
||||
|
||||
@@ -166,71 +160,37 @@ namespace ts {
|
||||
baselineOnly: true
|
||||
});
|
||||
|
||||
it("clean projects", () => {
|
||||
function getOutFileFsAfterBuild() {
|
||||
const fs = outFileFs.shadow();
|
||||
const expectedOutputs = [
|
||||
...outputFiles[project.first],
|
||||
...outputFiles[project.second],
|
||||
...outputFiles[project.third]
|
||||
];
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host);
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(...initialExpectedDiagnostics);
|
||||
// Verify they exist
|
||||
verifyOutputsPresent(fs, expectedOutputs);
|
||||
host.clearDiagnostics();
|
||||
builder.clean();
|
||||
host.assertDiagnosticMessages(/*none*/);
|
||||
// Verify they are gone
|
||||
verifyOutputsAbsent(fs, expectedOutputs);
|
||||
// Subsequent clean shouldn't throw / etc
|
||||
builder.clean();
|
||||
fs.makeReadonly();
|
||||
return fs;
|
||||
}
|
||||
|
||||
verifyTscIncrementalEdits({
|
||||
scenario: "outFile",
|
||||
subScenario: "clean projects",
|
||||
fs: getOutFileFsAfterBuild,
|
||||
commandLineArgs: ["--b", "/src/third", "--clean"],
|
||||
incrementalScenarios: [noChangeRun]
|
||||
});
|
||||
|
||||
it("verify buildInfo absence results in new build", () => {
|
||||
const { fs, tick } = getFsWithTime(outFileFs);
|
||||
const expectedOutputs = [
|
||||
...outputFiles[project.first],
|
||||
...outputFiles[project.second],
|
||||
...outputFiles[project.third]
|
||||
];
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
let builder = createSolutionBuilder(host);
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(...initialExpectedDiagnostics);
|
||||
// Verify they exist
|
||||
verifyOutputsPresent(fs, expectedOutputs);
|
||||
// Delete bundle info
|
||||
host.clearDiagnostics();
|
||||
|
||||
tick();
|
||||
host.deleteFile(outputFiles[project.first][ext.buildinfo]);
|
||||
tick();
|
||||
|
||||
builder = createSolutionBuilder(host);
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
getExpectedDiagnosticForProjectsInBuild(relSources[project.first][source.config], relSources[project.second][source.config], relSources[project.third][source.config]),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, relSources[project.first][source.config], relOutputFiles[project.first][ext.buildinfo]],
|
||||
[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]],
|
||||
);
|
||||
verifyTsc({
|
||||
scenario: "outFile",
|
||||
subScenario: "verify buildInfo absence results in new build",
|
||||
fs: getOutFileFsAfterBuild,
|
||||
commandLineArgs: ["--b", "/src/third", "--verbose"],
|
||||
modifyFs: fs => fs.unlinkSync(outputFiles[project.first][ext.buildinfo]),
|
||||
});
|
||||
|
||||
it("verify that if incremental is set to false, tsbuildinfo is not generated", () => {
|
||||
const fs = outFileFs.shadow();
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
replaceText(fs, sources[project.third][source.config], `"composite": true,`, "");
|
||||
const builder = createSolutionBuilder(host);
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(...initialExpectedDiagnostics);
|
||||
// Verify they exist - without tsbuildinfo for third project
|
||||
verifyOutputsPresent(fs, expectedOutputFiles.slice(0, expectedOutputFiles.length - 2));
|
||||
verifyOutputsAbsent(fs, [outputFiles[project.third][ext.buildinfo]]);
|
||||
verifyTsc({
|
||||
scenario: "outFile",
|
||||
subScenario: "tsbuildinfo is not generated when incremental is set to false",
|
||||
fs: () => outFileFs,
|
||||
commandLineArgs: ["--b", "/src/third", "--verbose"],
|
||||
modifyFs: fs => replaceText(fs, sources[project.third][source.config], `"composite": true,`, ""),
|
||||
});
|
||||
|
||||
it("rebuilds completely when version in tsbuildinfo doesnt match ts version", () => {
|
||||
@@ -682,6 +642,40 @@ ${internal} enum internalEnum { a, b, c }`);
|
||||
ignoreDtsUnchanged: true,
|
||||
baselineOnly: true
|
||||
});
|
||||
|
||||
verifyOutFileScenario({
|
||||
subScenario: "stripInternal when prepend is completely internal",
|
||||
baselineOnly: true,
|
||||
ignoreDtsChanged: true,
|
||||
ignoreDtsUnchanged: true,
|
||||
modifyFs: fs => {
|
||||
fs.writeFileSync(sources[project.first][source.ts][part.one], "/* @internal */ const A = 1;");
|
||||
fs.writeFileSync(sources[project.third][source.ts][part.one], "const B = 2;");
|
||||
fs.writeFileSync(sources[project.first][source.config], JSON.stringify({
|
||||
compilerOptions: {
|
||||
composite: true,
|
||||
declaration: true,
|
||||
declarationMap: true,
|
||||
skipDefaultLibCheck: true,
|
||||
sourceMap: true,
|
||||
outFile: "./bin/first-output.js"
|
||||
},
|
||||
files: [sources[project.first][source.ts][part.one]]
|
||||
}));
|
||||
fs.writeFileSync(sources[project.third][source.config], JSON.stringify({
|
||||
compilerOptions: {
|
||||
composite: true,
|
||||
declaration: true,
|
||||
declarationMap: false,
|
||||
stripInternal: true,
|
||||
sourceMap: true,
|
||||
outFile: "./thirdjs/output/third-output.js"
|
||||
},
|
||||
references: [{ path: "../first", prepend: true }],
|
||||
files: [sources[project.third][source.ts][part.one]]
|
||||
}));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("empty source files", () => {
|
||||
@@ -713,44 +707,26 @@ ${internal} enum internalEnum { a, b, c }`);
|
||||
});
|
||||
});
|
||||
|
||||
it("non module projects without prepend", () => {
|
||||
const fs = outFileFs.shadow();
|
||||
// No prepend
|
||||
replaceText(fs, sources[project.third][source.config], `{ "path": "../first", "prepend": true }`, `{ "path": "../first" }`);
|
||||
replaceText(fs, sources[project.third][source.config], `{ "path": "../second", "prepend": true }`, `{ "path": "../second" }`);
|
||||
verifyTsc({
|
||||
scenario: "outFile",
|
||||
subScenario: "non module projects without prepend",
|
||||
fs: () => outFileFs,
|
||||
commandLineArgs: ["--b", "/src/third", "--verbose"],
|
||||
modifyFs: fs => {
|
||||
// No prepend
|
||||
replaceText(fs, sources[project.third][source.config], `{ "path": "../first", "prepend": true }`, `{ "path": "../first" }`);
|
||||
replaceText(fs, sources[project.third][source.config], `{ "path": "../second", "prepend": true }`, `{ "path": "../second" }`);
|
||||
|
||||
// Non Modules
|
||||
replaceText(fs, sources[project.first][source.config], `"composite": true,`, `"composite": true, "module": "none",`);
|
||||
replaceText(fs, sources[project.second][source.config], `"composite": true,`, `"composite": true, "module": "none",`);
|
||||
replaceText(fs, sources[project.third][source.config], `"composite": true,`, `"composite": true, "module": "none",`);
|
||||
// Non Modules
|
||||
replaceText(fs, sources[project.first][source.config], `"composite": true,`, `"composite": true, "module": "none",`);
|
||||
replaceText(fs, sources[project.second][source.config], `"composite": true,`, `"composite": true, "module": "none",`);
|
||||
replaceText(fs, sources[project.third][source.config], `"composite": true,`, `"composite": true, "module": "none",`);
|
||||
|
||||
// Own file emit
|
||||
replaceText(fs, sources[project.first][source.config], `"outFile": "./bin/first-output.js",`, "");
|
||||
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 = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host);
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
getExpectedDiagnosticForProjectsInBuild(relSources[project.first][source.config], relSources[project.second][source.config], relSources[project.third][source.config]),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, relSources[project.first][source.config], "src/first/first_PART1.js"],
|
||||
[Diagnostics.Building_project_0, sources[project.first][source.config]],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, relSources[project.second][source.config], "src/second/second_part1.js"],
|
||||
[Diagnostics.Building_project_0, sources[project.second][source.config]],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, relSources[project.third][source.config], "src/third/third_part1.js"],
|
||||
[Diagnostics.Building_project_0, sources[project.third][source.config]]
|
||||
);
|
||||
const expectedOutputFiles = flatMap(sources, ([config, ts]) => [
|
||||
removeFileExtension(config) + Extension.TsBuildInfo,
|
||||
...flatMap(ts, f => [
|
||||
removeFileExtension(f) + Extension.Js,
|
||||
removeFileExtension(f) + Extension.Js + ".map",
|
||||
removeFileExtension(f) + Extension.Dts,
|
||||
removeFileExtension(f) + Extension.Dts + ".map",
|
||||
])
|
||||
]);
|
||||
verifyOutputsPresent(fs, expectedOutputFiles);
|
||||
// Own file emit
|
||||
replaceText(fs, sources[project.first][source.config], `"outFile": "./bin/first-output.js",`, "");
|
||||
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",`, "");
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -9,113 +9,53 @@ namespace ts {
|
||||
projFs = undefined!; // Release the contents
|
||||
});
|
||||
|
||||
it("verify that it builds correctly", () => {
|
||||
const allExpectedOutputs = [
|
||||
"/src/dist/other/other.js", "/src/dist/other/other.d.ts",
|
||||
"/src/dist/main/a.js", "/src/dist/main/a.d.ts",
|
||||
"/src/dist/main/b.js", "/src/dist/main/b.d.ts"
|
||||
];
|
||||
const fs = projFs.shadow();
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/src/main", "/src/src/other"], {});
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(/*empty*/);
|
||||
verifyOutputsPresent(fs, allExpectedOutputs);
|
||||
verifyTsc({
|
||||
scenario: "projectReferenceWithRootDirInParent",
|
||||
subScenario: "builds correctly",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/src/main", "/src/src/other"],
|
||||
});
|
||||
|
||||
it("verify that it reports error for same .tsbuildinfo file because no rootDir in the base", () => {
|
||||
const allExpectedOutputs = [
|
||||
"/src/dist/other.js", "/src/dist/other.d.ts",
|
||||
"/src/dist/tsconfig.tsbuildinfo"
|
||||
];
|
||||
const missingOutputs = [
|
||||
"/src/dist/a.js", "/src/dist/a.d.ts",
|
||||
"/src/dist/b.js", "/src/dist/b.d.ts"
|
||||
];
|
||||
const fs = projFs.shadow();
|
||||
replaceText(fs, "/src/tsconfig.base.json", `"rootDir": "./src/",`, "");
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/src/main"], { verbose: true });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
getExpectedDiagnosticForProjectsInBuild("src/src/other/tsconfig.json", "src/src/main/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/src/other/tsconfig.json", "src/dist/other.js"],
|
||||
[Diagnostics.Building_project_0, "/src/src/other/tsconfig.json"],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/src/main/tsconfig.json", "src/dist/a.js"],
|
||||
[Diagnostics.Building_project_0, "/src/src/main/tsconfig.json"],
|
||||
{
|
||||
message: [Diagnostics.Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1, "/src/dist/tsconfig.tsbuildinfo", "/src/src/other"],
|
||||
location: expectedLocationIndexOf(fs, "/src/src/main/tsconfig.json", `{ "path": "../other" }`),
|
||||
}
|
||||
);
|
||||
verifyOutputsPresent(fs, allExpectedOutputs);
|
||||
verifyOutputsAbsent(fs, missingOutputs);
|
||||
verifyTsc({
|
||||
scenario: "projectReferenceWithRootDirInParent",
|
||||
subScenario: "reports error for same tsbuildinfo file because no rootDir in the base",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/src/main", "--verbose"],
|
||||
modifyFs: fs => replaceText(fs, "/src/tsconfig.base.json", `"rootDir": "./src/",`, ""),
|
||||
});
|
||||
|
||||
it("verify that it reports error for same .tsbuildinfo file", () => {
|
||||
const allExpectedOutputs = [
|
||||
"/src/dist/other.js", "/src/dist/other.d.ts",
|
||||
"/src/dist/tsconfig.tsbuildinfo"
|
||||
];
|
||||
const missingOutputs = [
|
||||
"/src/dist/a.js", "/src/dist/a.d.ts",
|
||||
"/src/dist/b.js", "/src/dist/b.d.ts"
|
||||
];
|
||||
const fs = projFs.shadow();
|
||||
fs.writeFileSync("/src/src/main/tsconfig.json", JSON.stringify({
|
||||
compilerOptions: { composite: true, outDir: "../../dist/" },
|
||||
references: [{ path: "../other" }]
|
||||
}));
|
||||
fs.writeFileSync("/src/src/other/tsconfig.json", JSON.stringify({
|
||||
compilerOptions: { composite: true, outDir: "../../dist/" },
|
||||
}));
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/src/main"], { verbose: true });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
getExpectedDiagnosticForProjectsInBuild("src/src/other/tsconfig.json", "src/src/main/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/src/other/tsconfig.json", "src/dist/other.js"],
|
||||
[Diagnostics.Building_project_0, "/src/src/other/tsconfig.json"],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/src/main/tsconfig.json", "src/dist/a.js"],
|
||||
[Diagnostics.Building_project_0, "/src/src/main/tsconfig.json"],
|
||||
{
|
||||
message: [Diagnostics.Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1, "/src/dist/tsconfig.tsbuildinfo", "/src/src/other"],
|
||||
location: expectedLocationIndexOf(fs, "/src/src/main/tsconfig.json", `{"path":"../other"}`),
|
||||
}
|
||||
);
|
||||
verifyOutputsPresent(fs, allExpectedOutputs);
|
||||
verifyOutputsAbsent(fs, missingOutputs);
|
||||
verifyTsc({
|
||||
scenario: "projectReferenceWithRootDirInParent",
|
||||
subScenario: "reports error for same tsbuildinfo file",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/src/main", "--verbose"],
|
||||
modifyFs: fs => {
|
||||
fs.writeFileSync("/src/src/main/tsconfig.json", JSON.stringify({
|
||||
compilerOptions: { composite: true, outDir: "../../dist/" },
|
||||
references: [{ path: "../other" }]
|
||||
}));
|
||||
fs.writeFileSync("/src/src/other/tsconfig.json", JSON.stringify({
|
||||
compilerOptions: { composite: true, outDir: "../../dist/" },
|
||||
}));
|
||||
},
|
||||
});
|
||||
|
||||
it("verify that it reports no error when .tsbuildinfo differ", () => {
|
||||
const allExpectedOutputs = [
|
||||
"/src/dist/other.js", "/src/dist/other.d.ts",
|
||||
"/src/dist/tsconfig.main.tsbuildinfo",
|
||||
"/src/dist/a.js", "/src/dist/a.d.ts",
|
||||
"/src/dist/b.js", "/src/dist/b.d.ts",
|
||||
"/src/dist/tsconfig.other.tsbuildinfo"
|
||||
];
|
||||
const fs = projFs.shadow();
|
||||
fs.renameSync("/src/src/main/tsconfig.json", "/src/src/main/tsconfig.main.json");
|
||||
fs.renameSync("/src/src/other/tsconfig.json", "/src/src/other/tsconfig.other.json");
|
||||
fs.writeFileSync("/src/src/main/tsconfig.main.json", JSON.stringify({
|
||||
compilerOptions: { composite: true, outDir: "../../dist/" },
|
||||
references: [{ path: "../other/tsconfig.other.json" }]
|
||||
}));
|
||||
fs.writeFileSync("/src/src/other/tsconfig.other.json", JSON.stringify({
|
||||
compilerOptions: { composite: true, outDir: "../../dist/" },
|
||||
}));
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/src/main/tsconfig.main.json"], { verbose: true });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
getExpectedDiagnosticForProjectsInBuild("src/src/other/tsconfig.other.json", "src/src/main/tsconfig.main.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/src/other/tsconfig.other.json", "src/dist/other.js"],
|
||||
[Diagnostics.Building_project_0, "/src/src/other/tsconfig.other.json"],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/src/main/tsconfig.main.json", "src/dist/a.js"],
|
||||
[Diagnostics.Building_project_0, "/src/src/main/tsconfig.main.json"]
|
||||
);
|
||||
verifyOutputsPresent(fs, allExpectedOutputs);
|
||||
verifyTsc({
|
||||
scenario: "projectReferenceWithRootDirInParent",
|
||||
subScenario: "reports no error when tsbuildinfo differ",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/src/main/tsconfig.main.json", "--verbose"],
|
||||
modifyFs: fs => {
|
||||
fs.renameSync("/src/src/main/tsconfig.json", "/src/src/main/tsconfig.main.json");
|
||||
fs.renameSync("/src/src/other/tsconfig.json", "/src/src/other/tsconfig.other.json");
|
||||
fs.writeFileSync("/src/src/main/tsconfig.main.json", JSON.stringify({
|
||||
compilerOptions: { composite: true, outDir: "../../dist/" },
|
||||
references: [{ path: "../other/tsconfig.other.json" }]
|
||||
}));
|
||||
fs.writeFileSync("/src/src/other/tsconfig.other.json", JSON.stringify({
|
||||
compilerOptions: { composite: true, outDir: "../../dist/" },
|
||||
}));
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
namespace ts {
|
||||
describe("unittests:: tsbuild:: with resolveJsonModule option on project resolveJsonModuleAndComposite", () => {
|
||||
let projFs: vfs.FileSystem;
|
||||
const allExpectedOutputs = ["/src/dist/src/index.js", "/src/dist/src/index.d.ts", "/src/dist/src/hello.json"];
|
||||
before(() => {
|
||||
projFs = loadProjectFromDisk("tests/projects/resolveJsonModuleAndComposite");
|
||||
});
|
||||
@@ -10,108 +9,64 @@ namespace ts {
|
||||
projFs = undefined!; // Release the contents
|
||||
});
|
||||
|
||||
function verifyProjectWithResolveJsonModule(configFile: string, ...expectedDiagnosticMessages: fakes.ExpectedDiagnostic[]) {
|
||||
const fs = projFs.shadow();
|
||||
verifyProjectWithResolveJsonModuleWithFs(fs, configFile, allExpectedOutputs, ...expectedDiagnosticMessages);
|
||||
}
|
||||
|
||||
function verifyProjectWithResolveJsonModuleWithFs(fs: vfs.FileSystem, configFile: string, allExpectedOutputs: readonly string[], ...expectedDiagnosticMessages: fakes.ExpectedDiagnostic[]) {
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, [configFile], { dry: false, force: false, verbose: false });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(...expectedDiagnosticMessages);
|
||||
if (!expectedDiagnosticMessages.length) {
|
||||
// Check for outputs. Not an exhaustive list
|
||||
verifyOutputsPresent(fs, allExpectedOutputs);
|
||||
}
|
||||
}
|
||||
|
||||
it("with resolveJsonModule and include only", () => {
|
||||
verifyProjectWithResolveJsonModule(
|
||||
"/src/tsconfig_withInclude.json",
|
||||
{
|
||||
message: [
|
||||
Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern,
|
||||
"/src/src/hello.json",
|
||||
"/src/tsconfig_withInclude.json"
|
||||
],
|
||||
location: expectedLocationIndexOf(projFs, "/src/src/index.ts", `"./hello.json"`)
|
||||
}
|
||||
);
|
||||
verifyTsc({
|
||||
scenario: "resolveJsonModule",
|
||||
subScenario: "include only",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/tsconfig_withInclude.json"],
|
||||
});
|
||||
|
||||
it("with resolveJsonModule and include of *.json along with other include", () => {
|
||||
verifyProjectWithResolveJsonModule("/src/tsconfig_withIncludeOfJson.json");
|
||||
verifyTsc({
|
||||
scenario: "resolveJsonModule",
|
||||
subScenario: "include of json along with other include",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/tsconfig_withIncludeOfJson.json"],
|
||||
});
|
||||
|
||||
it("with resolveJsonModule and include of *.json along with other include and file name matches ts file", () => {
|
||||
const fs = projFs.shadow();
|
||||
fs.rimrafSync("/src/src/hello.json");
|
||||
fs.writeFileSync("/src/src/index.json", JSON.stringify({ hello: "world" }));
|
||||
fs.writeFileSync("/src/src/index.ts", `import hello from "./index.json"
|
||||
verifyTsc({
|
||||
scenario: "resolveJsonModule",
|
||||
subScenario: "include of json along with other include and file name matches ts file",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/tsconfig_withIncludeOfJson.json"],
|
||||
modifyFs: fs => {
|
||||
fs.rimrafSync("/src/src/hello.json");
|
||||
fs.writeFileSync("/src/src/index.json", JSON.stringify({ hello: "world" }));
|
||||
fs.writeFileSync("/src/src/index.ts", `import hello from "./index.json"
|
||||
|
||||
export default hello.hello`);
|
||||
const allExpectedOutputs = ["/src/dist/src/index.js", "/src/dist/src/index.d.ts", "/src/dist/src/index.json"];
|
||||
verifyProjectWithResolveJsonModuleWithFs(
|
||||
fs,
|
||||
"/src/tsconfig_withIncludeOfJson.json",
|
||||
allExpectedOutputs,
|
||||
errorDiagnostic([Diagnostics.Cannot_write_file_0_because_it_would_be_overwritten_by_multiple_input_files, "/src/dist/src/index.d.ts"])
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
it("with resolveJsonModule and files containing json file", () => {
|
||||
verifyProjectWithResolveJsonModule("/src/tsconfig_withFiles.json");
|
||||
verifyTsc({
|
||||
scenario: "resolveJsonModule",
|
||||
subScenario: "files containing json file",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/tsconfig_withFiles.json"],
|
||||
});
|
||||
|
||||
it("with resolveJsonModule and include and files", () => {
|
||||
verifyProjectWithResolveJsonModule("/src/tsconfig_withIncludeAndFiles.json");
|
||||
verifyTsc({
|
||||
scenario: "resolveJsonModule",
|
||||
subScenario: "include and files",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/tsconfig_withIncludeAndFiles.json"],
|
||||
});
|
||||
|
||||
it("with resolveJsonModule and sourceMap", () => {
|
||||
const { fs, tick } = getFsWithTime(projFs);
|
||||
const configFile = "src/tsconfig_withFiles.json";
|
||||
replaceText(fs, configFile, `"composite": true,`, `"composite": true, "sourceMap": true,`);
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
let builder = createSolutionBuilder(host, [configFile], { verbose: true });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
getExpectedDiagnosticForProjectsInBuild(configFile),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, configFile, "src/dist/src/index.js"],
|
||||
[Diagnostics.Building_project_0, `/${configFile}`]
|
||||
);
|
||||
verifyOutputsPresent(fs, [...allExpectedOutputs, "/src/dist/src/index.js.map"]);
|
||||
host.clearDiagnostics();
|
||||
builder = createSolutionBuilder(host, [configFile], { verbose: true });
|
||||
tick();
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
getExpectedDiagnosticForProjectsInBuild(configFile),
|
||||
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, configFile, "src/src/index.ts", "src/dist/src/index.js"]
|
||||
);
|
||||
verifyTscIncrementalEdits({
|
||||
scenario: "resolveJsonModule",
|
||||
subScenario: "sourcemap",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "src/tsconfig_withFiles.json", "--verbose"],
|
||||
modifyFs: fs => replaceText(fs, "src/tsconfig_withFiles.json", `"composite": true,`, `"composite": true, "sourceMap": true,`),
|
||||
incrementalScenarios: [noChangeRun]
|
||||
});
|
||||
|
||||
it("with resolveJsonModule and without outDir", () => {
|
||||
const { fs, tick } = getFsWithTime(projFs);
|
||||
const configFile = "src/tsconfig_withFiles.json";
|
||||
replaceText(fs, configFile, `"outDir": "dist",`, "");
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
let builder = createSolutionBuilder(host, [configFile], { verbose: true });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
getExpectedDiagnosticForProjectsInBuild(configFile),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, configFile, "src/src/index.js"],
|
||||
[Diagnostics.Building_project_0, `/${configFile}`]
|
||||
);
|
||||
verifyOutputsPresent(fs, ["/src/src/index.js", "/src/src/index.d.ts"]);
|
||||
host.clearDiagnostics();
|
||||
builder = createSolutionBuilder(host, [configFile], { verbose: true });
|
||||
tick();
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
getExpectedDiagnosticForProjectsInBuild(configFile),
|
||||
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, configFile, "src/src/index.ts", "src/src/index.js"]
|
||||
);
|
||||
verifyTscIncrementalEdits({
|
||||
scenario: "resolveJsonModule",
|
||||
subScenario: "without outDir",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "src/tsconfig_withFiles.json", "--verbose"],
|
||||
modifyFs: fs => replaceText(fs, "src/tsconfig_withFiles.json", `"outDir": "dist",`, ""),
|
||||
incrementalScenarios: [noChangeRun]
|
||||
});
|
||||
});
|
||||
|
||||
@@ -125,32 +80,12 @@ export default hello.hello`);
|
||||
projFs = undefined!; // Release the contents
|
||||
});
|
||||
|
||||
it("when importing json module from project reference", () => {
|
||||
const expectedOutput = "/src/main/index.js";
|
||||
const { fs, tick } = getFsWithTime(projFs);
|
||||
const configFile = "src/tsconfig.json";
|
||||
const stringsConfigFile = "src/strings/tsconfig.json";
|
||||
const mainConfigFile = "src/main/tsconfig.json";
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
let builder = createSolutionBuilder(host, [configFile], { verbose: true });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
getExpectedDiagnosticForProjectsInBuild(stringsConfigFile, mainConfigFile, configFile),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, stringsConfigFile, "src/strings/tsconfig.tsbuildinfo"],
|
||||
[Diagnostics.Building_project_0, `/${stringsConfigFile}`],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, mainConfigFile, "src/main/index.js"],
|
||||
[Diagnostics.Building_project_0, `/${mainConfigFile}`],
|
||||
);
|
||||
verifyOutputsPresent(fs, [expectedOutput]);
|
||||
host.clearDiagnostics();
|
||||
builder = createSolutionBuilder(host, [configFile], { verbose: true });
|
||||
tick();
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
getExpectedDiagnosticForProjectsInBuild(stringsConfigFile, mainConfigFile, configFile),
|
||||
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, stringsConfigFile, "src/strings/foo.json", "src/strings/tsconfig.tsbuildinfo"],
|
||||
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, mainConfigFile, "src/main/index.ts", "src/main/index.js"],
|
||||
);
|
||||
verifyTscIncrementalEdits({
|
||||
scenario: "resolveJsonModule",
|
||||
subScenario: "importing json module from project reference",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "src/tsconfig.json", "--verbose"],
|
||||
incrementalScenarios: [noChangeRun]
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,123 +14,63 @@ namespace ts {
|
||||
projFs = undefined!; // Release the contents
|
||||
});
|
||||
|
||||
function getSampleFsAfterBuild() {
|
||||
const fs = projFs.shadow();
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], {});
|
||||
builder.build();
|
||||
fs.makeReadonly();
|
||||
return fs;
|
||||
}
|
||||
|
||||
describe("sanity check of clean build of 'sample1' project", () => {
|
||||
it("can build the sample project 'sample1' without error", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
|
||||
|
||||
host.clearDiagnostics();
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(/*empty*/);
|
||||
|
||||
// Check for outputs. Not an exhaustive list
|
||||
verifyOutputsPresent(fs, allExpectedOutputs);
|
||||
});
|
||||
|
||||
it("builds correctly when outDir is specified", () => {
|
||||
const fs = projFs.shadow();
|
||||
fs.writeFileSync("/src/logic/tsconfig.json", JSON.stringify({
|
||||
verifyTsc({
|
||||
scenario: "sample1",
|
||||
subScenario: "builds correctly when outDir is specified",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/tests"],
|
||||
modifyFs: fs => fs.writeFileSync("/src/logic/tsconfig.json", JSON.stringify({
|
||||
compilerOptions: { composite: true, declaration: true, sourceMap: true, outDir: "outDir" },
|
||||
references: [{ path: "../core" }]
|
||||
}));
|
||||
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], {});
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(/*empty*/);
|
||||
const expectedOutputs = allExpectedOutputs.map(f => f.replace("/logic/", "/logic/outDir/"));
|
||||
// Check for outputs. Not an exhaustive list
|
||||
verifyOutputsPresent(fs, expectedOutputs);
|
||||
})),
|
||||
});
|
||||
|
||||
it("builds correctly when declarationDir is specified", () => {
|
||||
const fs = projFs.shadow();
|
||||
fs.writeFileSync("/src/logic/tsconfig.json", JSON.stringify({
|
||||
verifyTsc({
|
||||
scenario: "sample1",
|
||||
subScenario: "builds correctly when declarationDir is specified",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/tests"],
|
||||
modifyFs: fs => fs.writeFileSync("/src/logic/tsconfig.json", JSON.stringify({
|
||||
compilerOptions: { composite: true, declaration: true, sourceMap: true, declarationDir: "out/decls" },
|
||||
references: [{ path: "../core" }]
|
||||
}));
|
||||
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], {});
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(/*empty*/);
|
||||
const expectedOutputs = allExpectedOutputs.map(f => f.replace("/logic/index.d.ts", "/logic/out/decls/index.d.ts"));
|
||||
// Check for outputs. Not an exhaustive list
|
||||
verifyOutputsPresent(fs, expectedOutputs);
|
||||
})),
|
||||
});
|
||||
|
||||
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 = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/core"], { verbose: true });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
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"]
|
||||
);
|
||||
verifyOutputsPresent(fs, ["/src/core/index.js", "/src/core/index.d.ts", "/src/core/index.d.ts.map"]);
|
||||
verifyTsc({
|
||||
scenario: "sample1",
|
||||
subScenario: "builds correctly when project is not composite or doesnt have any references",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/core", "--verbose"],
|
||||
modifyFs: fs => replaceText(fs, "/src/core/tsconfig.json", `"composite": true,`, ""),
|
||||
});
|
||||
});
|
||||
|
||||
describe("dry builds", () => {
|
||||
it("doesn't write any files in a dry build", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: true, force: false, verbose: false });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
[Diagnostics.A_non_dry_build_would_build_project_0, "/src/core/tsconfig.json"],
|
||||
[Diagnostics.A_non_dry_build_would_build_project_0, "/src/logic/tsconfig.json"],
|
||||
[Diagnostics.A_non_dry_build_would_build_project_0, "/src/tests/tsconfig.json"]
|
||||
);
|
||||
|
||||
// Check for outputs to not be written. Not an exhaustive list
|
||||
verifyOutputsAbsent(fs, allExpectedOutputs);
|
||||
});
|
||||
|
||||
it("indicates that it would skip builds during a dry build", () => {
|
||||
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();
|
||||
tick();
|
||||
|
||||
host.clearDiagnostics();
|
||||
builder = createSolutionBuilder(host, ["/src/tests"], { dry: true, force: false, verbose: false });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
[Diagnostics.Project_0_is_up_to_date, "/src/core/tsconfig.json"],
|
||||
[Diagnostics.Project_0_is_up_to_date, "/src/logic/tsconfig.json"],
|
||||
[Diagnostics.Project_0_is_up_to_date, "/src/tests/tsconfig.json"]
|
||||
);
|
||||
verifyTsc({
|
||||
scenario: "sample1",
|
||||
subScenario: "does not write any files in a dry build",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/tests", "--dry"],
|
||||
});
|
||||
});
|
||||
|
||||
describe("clean builds", () => {
|
||||
it("removes all files it built", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
|
||||
builder.build();
|
||||
// Verify they exist
|
||||
verifyOutputsPresent(fs, allExpectedOutputs);
|
||||
|
||||
builder.clean();
|
||||
// Verify they are gone
|
||||
verifyOutputsAbsent(fs, allExpectedOutputs);
|
||||
|
||||
// Subsequent clean shouldn't throw / etc
|
||||
builder.clean();
|
||||
verifyOutputsAbsent(fs, allExpectedOutputs);
|
||||
|
||||
builder.build();
|
||||
// Verify they exist
|
||||
verifyOutputsPresent(fs, allExpectedOutputs);
|
||||
verifyTscIncrementalEdits({
|
||||
scenario: "sample1",
|
||||
subScenario: "removes all files it built",
|
||||
fs: getSampleFsAfterBuild,
|
||||
commandLineArgs: ["--b", "/src/tests", "--clean"],
|
||||
incrementalScenarios: [noChangeRun]
|
||||
});
|
||||
|
||||
it("cleans till project specified", () => {
|
||||
@@ -158,29 +98,12 @@ namespace ts {
|
||||
});
|
||||
|
||||
describe("force builds", () => {
|
||||
it("always builds under --force", () => {
|
||||
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();
|
||||
let currentTime = time();
|
||||
checkOutputTimestamps(currentTime);
|
||||
|
||||
tick();
|
||||
Debug.assert(time() !== currentTime, "Time moves on");
|
||||
currentTime = time();
|
||||
builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: true, verbose: false });
|
||||
builder.build();
|
||||
checkOutputTimestamps(currentTime);
|
||||
|
||||
function checkOutputTimestamps(expected: number) {
|
||||
// Check timestamps
|
||||
for (const output of allExpectedOutputs) {
|
||||
const actual = fs.statSync(output).mtimeMs;
|
||||
assert(actual === expected, `File ${output} has timestamp ${actual}, expected ${expected}`);
|
||||
}
|
||||
}
|
||||
verifyTscIncrementalEdits({
|
||||
scenario: "sample1",
|
||||
subScenario: "always builds under with force option",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/tests", "--force"],
|
||||
incrementalScenarios: [noChangeRun]
|
||||
});
|
||||
});
|
||||
|
||||
@@ -196,64 +119,42 @@ namespace ts {
|
||||
return { fs, host, builder };
|
||||
}
|
||||
|
||||
it("Builds the project", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], { verbose: true });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
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"]
|
||||
);
|
||||
});
|
||||
|
||||
// All three projects are up to date
|
||||
it("Detects that all projects are up to date", () => {
|
||||
const { host, builder } = initializeWithBuild();
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
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_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/tests/tsconfig.json", "src/tests/index.ts", "src/tests/index.js"]
|
||||
);
|
||||
});
|
||||
|
||||
// Update a file in the leaf node (tests), only it should rebuild the last one
|
||||
it("Only builds the leaf node project", () => {
|
||||
const { fs, host, builder } = initializeWithBuild();
|
||||
fs.writeFileSync("/src/tests/index.ts", "const m = 10;");
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
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/index.ts"],
|
||||
[Diagnostics.Building_project_0, "/src/tests/tsconfig.json"]
|
||||
);
|
||||
});
|
||||
|
||||
// Update a file in the parent (without affecting types), should get fast downstream builds
|
||||
it("Detects type-only changes in upstream projects", () => {
|
||||
const { fs, host, builder } = initializeWithBuild();
|
||||
replaceText(fs, "/src/core/index.ts", "HELLO WORLD", "WELCOME PLANET");
|
||||
builder.build();
|
||||
|
||||
host.assertDiagnosticMessages(
|
||||
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"]
|
||||
);
|
||||
verifyTscIncrementalEdits({
|
||||
scenario: "sample1",
|
||||
subScenario: "can detect when and what to rebuild",
|
||||
fs: getSampleFsAfterBuild,
|
||||
commandLineArgs: ["--b", "/src/tests", "--verbose"],
|
||||
incrementalScenarios: [
|
||||
// Update a file in the leaf node (tests), only it should rebuild the last one
|
||||
{
|
||||
subScenario: "Only builds the leaf node project",
|
||||
buildKind: BuildKind.IncrementalDtsUnchanged,
|
||||
modifyFs: fs => fs.writeFileSync("/src/tests/index.ts", "const m = 10;"),
|
||||
},
|
||||
// Update a file in the parent (without affecting types), should get fast downstream builds
|
||||
{
|
||||
subScenario: "Detects type-only changes in upstream projects",
|
||||
buildKind: BuildKind.IncrementalDtsChange,
|
||||
modifyFs: fs => replaceText(fs, "/src/core/index.ts", "HELLO WORLD", "WELCOME PLANET"),
|
||||
},
|
||||
{
|
||||
subScenario: "indicates that it would skip builds during a dry build",
|
||||
buildKind: BuildKind.IncrementalDtsUnchanged,
|
||||
modifyFs: noop,
|
||||
commandLineArgs: ["--b", "/src/tests", "--dry"],
|
||||
},
|
||||
{
|
||||
subScenario: "rebuilds from start if force option is set",
|
||||
buildKind: BuildKind.IncrementalDtsChange,
|
||||
modifyFs: noop,
|
||||
commandLineArgs: ["--b", "/src/tests", "--verbose", "--force"],
|
||||
},
|
||||
{
|
||||
subScenario: "rebuilds when tsconfig changes",
|
||||
buildKind: BuildKind.IncrementalDtsChange,
|
||||
modifyFs: fs => replaceText(fs, "/src/tests/tsconfig.json", `"composite": true`, `"composite": true, "target": "es3"`),
|
||||
},
|
||||
]
|
||||
});
|
||||
|
||||
it("rebuilds completely when version in tsbuildinfo doesnt match ts version", () => {
|
||||
@@ -300,61 +201,19 @@ namespace ts {
|
||||
);
|
||||
});
|
||||
|
||||
it("rebuilds from start if --f is passed", () => {
|
||||
const { host, builder } = initializeWithBuild({ force: true });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
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.Building_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.Building_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.Building_project_0, "/src/tests/tsconfig.json"]
|
||||
);
|
||||
});
|
||||
|
||||
it("rebuilds when tsconfig changes", () => {
|
||||
const { fs, host, builder } = initializeWithBuild();
|
||||
replaceText(fs, "/src/tests/tsconfig.json", `"composite": true`, `"composite": true, "target": "es3"`);
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
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"],
|
||||
);
|
||||
});
|
||||
|
||||
it("rebuilds when extended config file changes", () => {
|
||||
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 = fakes.SolutionBuilderHost.create(fs);
|
||||
let builder = createSolutionBuilder(host, ["/src/tests"], { verbose: true });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
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"]
|
||||
);
|
||||
host.clearDiagnostics();
|
||||
tick();
|
||||
builder = createSolutionBuilder(host, ["/src/tests"], { verbose: true });
|
||||
fs.writeFileSync("/src/tests/tsconfig.base.json", JSON.stringify({ compilerOptions: {} }));
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
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.base.json"],
|
||||
[Diagnostics.Building_project_0, "/src/tests/tsconfig.json"]
|
||||
);
|
||||
verifyTscIncrementalEdits({
|
||||
scenario: "sample1",
|
||||
subScenario: "rebuilds when extended config file changes",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/tests", "--verbose"],
|
||||
modifyFs: fs => {
|
||||
fs.writeFileSync("/src/tests/tsconfig.base.json", JSON.stringify({ compilerOptions: { target: "es3" } }));
|
||||
replaceText(fs, "/src/tests/tsconfig.json", `"references": [`, `"extends": "./tsconfig.base.json", "references": [`);
|
||||
},
|
||||
incrementalScenarios: [{
|
||||
buildKind: BuildKind.IncrementalDtsChange,
|
||||
modifyFs: fs => fs.writeFileSync("/src/tests/tsconfig.base.json", JSON.stringify({ compilerOptions: {} }))
|
||||
}]
|
||||
});
|
||||
|
||||
it("builds till project specified", () => {
|
||||
@@ -435,27 +294,12 @@ namespace ts {
|
||||
});
|
||||
|
||||
describe("downstream-blocked compilations", () => {
|
||||
it("won't build downstream projects if upstream projects have errors", () => {
|
||||
const fs = projFs.shadow();
|
||||
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
|
||||
replaceText(fs, "/src/logic/index.ts", "c.multiply(10, 15)", `c.muitply()`);
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(
|
||||
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"],
|
||||
{
|
||||
message: [Diagnostics.Property_0_does_not_exist_on_type_1, "muitply", `typeof import("/src/core/index")`],
|
||||
location: expectedLocationIndexOf(fs, "/src/logic/index.ts", "muitply"),
|
||||
},
|
||||
[Diagnostics.Project_0_can_t_be_built_because_its_dependency_1_has_errors, "src/tests/tsconfig.json", "src/logic"],
|
||||
[Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors, "/src/tests/tsconfig.json", "/src/logic"]
|
||||
);
|
||||
verifyTsc({
|
||||
scenario: "sample1",
|
||||
subScenario: "does not build downstream projects if upstream projects have errors",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/tests", "--verbose"],
|
||||
modifyFs: fs => replaceText(fs, "/src/logic/index.ts", "c.multiply(10, 15)", `c.muitply()`)
|
||||
});
|
||||
});
|
||||
|
||||
@@ -515,54 +359,17 @@ export class cNew {}`);
|
||||
});
|
||||
|
||||
describe("lists files", () => {
|
||||
it("listFiles", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], { listFiles: true });
|
||||
builder.build();
|
||||
assert.deepEqual(host.traces, [
|
||||
"/lib/lib.d.ts",
|
||||
"/src/core/anotherModule.ts",
|
||||
"/src/core/index.ts",
|
||||
"/src/core/some_decl.d.ts",
|
||||
"/lib/lib.d.ts",
|
||||
...getCoreOutputs(),
|
||||
"/src/logic/index.ts",
|
||||
"/lib/lib.d.ts",
|
||||
...getCoreOutputs(),
|
||||
"/src/logic/index.d.ts",
|
||||
"/src/tests/index.ts"
|
||||
]);
|
||||
|
||||
function getCoreOutputs() {
|
||||
return [
|
||||
"/src/core/index.d.ts",
|
||||
"/src/core/anotherModule.d.ts"
|
||||
];
|
||||
}
|
||||
verifyTsc({
|
||||
scenario: "sample1",
|
||||
subScenario: "listFiles",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/tests", "--listFiles"],
|
||||
});
|
||||
|
||||
it("listEmittedFiles", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], { listEmittedFiles: true });
|
||||
builder.build();
|
||||
assert.deepEqual(host.traces, [
|
||||
"TSFILE: /src/core/anotherModule.js",
|
||||
"TSFILE: /src/core/anotherModule.d.ts.map",
|
||||
"TSFILE: /src/core/anotherModule.d.ts",
|
||||
"TSFILE: /src/core/index.js",
|
||||
"TSFILE: /src/core/index.d.ts.map",
|
||||
"TSFILE: /src/core/index.d.ts",
|
||||
"TSFILE: /src/core/tsconfig.tsbuildinfo",
|
||||
"TSFILE: /src/logic/index.js.map",
|
||||
"TSFILE: /src/logic/index.js",
|
||||
"TSFILE: /src/logic/index.d.ts",
|
||||
"TSFILE: /src/logic/tsconfig.tsbuildinfo",
|
||||
"TSFILE: /src/tests/index.js",
|
||||
"TSFILE: /src/tests/index.d.ts",
|
||||
"TSFILE: /src/tests/tsconfig.tsbuildinfo",
|
||||
]);
|
||||
verifyTsc({
|
||||
scenario: "sample1",
|
||||
subScenario: "listEmittedFiles",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/tests", "--listEmittedFiles"],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -590,7 +397,8 @@ class someClass { }`),
|
||||
buildKind: BuildKind.IncrementalDtsChange,
|
||||
modifyFs: fs => replaceText(fs, "/src/logic/tsconfig.json", `"declaration": true,`, `"declaration": true,
|
||||
"declarationDir": "decls",`),
|
||||
}
|
||||
},
|
||||
noChangeRun,
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
@@ -1,23 +1,6 @@
|
||||
namespace ts {
|
||||
describe("unittests:: tsbuild:: when project reference is referenced transitively", () => {
|
||||
let projFs: vfs.FileSystem;
|
||||
const allExpectedOutputs = [
|
||||
"/src/a.js", "/src/a.d.ts",
|
||||
"/src/b.js", "/src/b.d.ts",
|
||||
"/src/c.js"
|
||||
];
|
||||
const expectedFileTraces = [
|
||||
"/lib/lib.d.ts",
|
||||
"/src/a.ts",
|
||||
"/lib/lib.d.ts",
|
||||
"/src/a.d.ts",
|
||||
"/src/b.ts",
|
||||
"/lib/lib.d.ts",
|
||||
"/src/a.d.ts",
|
||||
"/src/b.d.ts",
|
||||
"/src/refs/a.d.ts",
|
||||
"/src/c.ts"
|
||||
];
|
||||
before(() => {
|
||||
projFs = loadProjectFromDisk("tests/projects/transitiveReferences");
|
||||
});
|
||||
@@ -25,17 +8,6 @@ namespace ts {
|
||||
projFs = undefined!; // Release the contents
|
||||
});
|
||||
|
||||
function verifyBuild(modifyDiskLayout: (fs: vfs.FileSystem) => void, allExpectedOutputs: readonly string[], expectedFileTraces: readonly string[], ...expectedDiagnostics: fakes.ExpectedDiagnostic[]) {
|
||||
const fs = projFs.shadow();
|
||||
const host = fakes.SolutionBuilderHost.create(fs);
|
||||
modifyDiskLayout(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tsconfig.c.json"], { listFiles: true });
|
||||
builder.build();
|
||||
host.assertDiagnosticMessages(...expectedDiagnostics);
|
||||
verifyOutputsPresent(fs, allExpectedOutputs);
|
||||
assert.deepEqual(host.traces, expectedFileTraces);
|
||||
}
|
||||
|
||||
function modifyFsBTsToNonRelativeImport(fs: vfs.FileSystem, moduleResolution: "node" | "classic") {
|
||||
fs.writeFileSync("/src/b.ts", `import {A} from 'a';
|
||||
export const b = new A();`);
|
||||
@@ -49,35 +21,27 @@ export const b = new A();`);
|
||||
}));
|
||||
}
|
||||
|
||||
it("verify that it builds correctly", () => {
|
||||
verifyBuild(noop, allExpectedOutputs, expectedFileTraces);
|
||||
verifyTsc({
|
||||
scenario: "transitiveReferences",
|
||||
subScenario: "builds correctly",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/tsconfig.c.json", "--listFiles"],
|
||||
});
|
||||
|
||||
it("verify that it builds correctly when the referenced project uses different module resolution", () => {
|
||||
verifyBuild(fs => modifyFsBTsToNonRelativeImport(fs, "classic"), allExpectedOutputs, expectedFileTraces);
|
||||
verifyTsc({
|
||||
scenario: "transitiveReferences",
|
||||
subScenario: "builds correctly when the referenced project uses different module resolution",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/tsconfig.c.json", "--listFiles"],
|
||||
modifyFs: fs => modifyFsBTsToNonRelativeImport(fs, "classic"),
|
||||
});
|
||||
|
||||
it("verify that it build reports error about module not found with node resolution with external module name", () => {
|
||||
// Error in b build only a
|
||||
const allExpectedOutputs = ["/src/a.js", "/src/a.d.ts"];
|
||||
const expectedFileTraces = [
|
||||
"/lib/lib.d.ts",
|
||||
"/src/a.ts",
|
||||
"/lib/lib.d.ts",
|
||||
"/src/b.ts"
|
||||
];
|
||||
verifyBuild(fs => modifyFsBTsToNonRelativeImport(fs, "node"),
|
||||
allExpectedOutputs,
|
||||
expectedFileTraces,
|
||||
{
|
||||
message: [Diagnostics.Cannot_find_module_0, "a"],
|
||||
location: {
|
||||
file: "/src/b.ts",
|
||||
start: `import {A} from 'a';`.indexOf(`'a'`),
|
||||
length: `'a'`.length
|
||||
}
|
||||
},
|
||||
);
|
||||
verifyTsc({
|
||||
scenario: "transitiveReferences",
|
||||
subScenario: "reports error about module not found with node resolution with external module name",
|
||||
fs: () => projFs,
|
||||
commandLineArgs: ["--b", "/src/tsconfig.c.json", "--listFiles"],
|
||||
modifyFs: fs => modifyFsBTsToNonRelativeImport(fs, "node"),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,158 +3,20 @@ namespace ts {
|
||||
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"
|
||||
IncrementalHeadersChange = "incremental-headers-change-without-dts-changes",
|
||||
NoChangeRun ="no-change-run"
|
||||
}
|
||||
|
||||
export const noChangeRun: TscIncremental = {
|
||||
buildKind: BuildKind.NoChangeRun,
|
||||
modifyFs: noop
|
||||
};
|
||||
|
||||
export interface TscCompile {
|
||||
scenario: string;
|
||||
subScenario: string;
|
||||
@@ -198,8 +60,17 @@ namespace ts {
|
||||
|
||||
sys.write(`${sys.getExecutingFilePath()} ${commandLineArgs.join(" ")}\n`);
|
||||
sys.exit = exitCode => sys.exitCode = exitCode;
|
||||
executeCommandLine(sys, commandLineArgs);
|
||||
sys.write(`exitCode:: ${sys.exitCode}\n`);
|
||||
executeCommandLine(
|
||||
sys,
|
||||
{
|
||||
onCompilerHostCreate: host => fakes.patchHostForBuildInfoReadWrite(host),
|
||||
onCompilationComplete: config => baselineBuildInfo([config], sys.vfs, sys.writtenFiles),
|
||||
onSolutionBuilderHostCreate: host => fakes.patchSolutionBuilderHost(host, sys),
|
||||
onSolutionBuildComplete: configs => baselineBuildInfo(configs, sys.vfs, sys.writtenFiles),
|
||||
},
|
||||
commandLineArgs,
|
||||
);
|
||||
sys.write(`exitCode:: ExitStatus.${ExitStatus[sys.exitCode as ExitStatus]}\n`);
|
||||
if (baselineReadFileCalls) {
|
||||
sys.write(`readFiles:: ${JSON.stringify(actualReadFileMap, /*replacer*/ undefined, " ")} `);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
namespace ts {
|
||||
describe("unittests:: tsc:: incremental::", () => {
|
||||
verifyTscIncrementalEdits({
|
||||
scenario: "incremental",
|
||||
subScenario: "when passing filename for buildinfo on commandline",
|
||||
fs: () => loadProjectFromFiles({
|
||||
"/src/project/src/main.ts": "export const x = 10;",
|
||||
"/src/project/tsconfig.json": utils.dedent`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
]
|
||||
}`,
|
||||
}),
|
||||
commandLineArgs: ["--incremental", "--p", "src/project", "--tsBuildInfoFile", "src/project/.tsbuildinfo"],
|
||||
incrementalScenarios: [noChangeRun]
|
||||
});
|
||||
|
||||
verifyTscIncrementalEdits({
|
||||
scenario: "incremental",
|
||||
subScenario: "when passing rootDir from commandline",
|
||||
fs: () => loadProjectFromFiles({
|
||||
"/src/project/src/main.ts": "export const x = 10;",
|
||||
"/src/project/tsconfig.json": utils.dedent`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"incremental": true,
|
||||
"outDir": "dist",
|
||||
},
|
||||
}`,
|
||||
}),
|
||||
commandLineArgs: ["--p", "src/project", "--rootDir", "src/project/src"],
|
||||
incrementalScenarios: [noChangeRun]
|
||||
});
|
||||
|
||||
verifyTscIncrementalEdits({
|
||||
scenario: "incremental",
|
||||
subScenario: "with only dts files",
|
||||
fs: () => loadProjectFromFiles({
|
||||
"/src/project/src/main.d.ts": "export const x = 10;",
|
||||
"/src/project/src/another.d.ts": "export const y = 10;",
|
||||
"/src/project/tsconfig.json": "{}",
|
||||
}),
|
||||
commandLineArgs: ["--incremental", "--p", "src/project"],
|
||||
incrementalScenarios: [
|
||||
noChangeRun,
|
||||
{
|
||||
buildKind: BuildKind.IncrementalDtsUnchanged,
|
||||
modifyFs: fs => appendText(fs, "/src/project/src/main.d.ts", "export const xy = 100;")
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
verifyTscIncrementalEdits({
|
||||
scenario: "incremental",
|
||||
subScenario: "when passing rootDir is in the tsconfig",
|
||||
fs: () => loadProjectFromFiles({
|
||||
"/src/project/src/main.ts": "export const x = 10;",
|
||||
"/src/project/tsconfig.json": utils.dedent`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"incremental": true,
|
||||
"outDir": "./built",
|
||||
"rootDir": "./"
|
||||
},
|
||||
}`,
|
||||
}),
|
||||
commandLineArgs: ["--p", "src/project"],
|
||||
incrementalScenarios: [noChangeRun]
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace ts {
|
||||
describe("unittests:: tsc:: listFilesOnly::", () => {
|
||||
verifyTsc({
|
||||
scenario: "listFilesOnly",
|
||||
subScenario: "combined with watch",
|
||||
fs: () => loadProjectFromFiles({
|
||||
"/src/test.ts": utils.dedent`
|
||||
export const x = 1;`,
|
||||
}),
|
||||
commandLineArgs: ["/src/test.ts", "--watch", "--listFilesOnly"]
|
||||
});
|
||||
|
||||
verifyTsc({
|
||||
scenario: "listFilesOnly",
|
||||
subScenario: "loose file",
|
||||
fs: () => loadProjectFromFiles({
|
||||
"/src/test.ts": utils.dedent`
|
||||
export const x = 1;`,
|
||||
}),
|
||||
commandLineArgs: ["/src/test.ts", "--listFilesOnly"]
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
namespace ts.tscWatch {
|
||||
describe("unittests:: tsc-watch:: Emit times and Error updates in builder after program changes", () => {
|
||||
const currentDirectory = "/user/username/projects/myproject";
|
||||
const config: File = {
|
||||
path: `${currentDirectory}/tsconfig.json`,
|
||||
path: `${projectRoot}/tsconfig.json`,
|
||||
content: `{}`
|
||||
};
|
||||
function getOutputFileStampAndError(host: WatchedSystem, watch: Watch, file: File) {
|
||||
@@ -83,7 +82,7 @@ namespace ts.tscWatch {
|
||||
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 host = createWatchedSystem(files, { currentDirectory: projectRoot });
|
||||
const watch = createWatchOfConfigFile("tsconfig.json", host);
|
||||
checkProgramActualFiles(watch(), [...nonLibFiles.map(f => f.path), libFile.path]);
|
||||
checkOutputErrorsInitial(host, getInitialErrors(watch));
|
||||
@@ -167,7 +166,7 @@ namespace ts.tscWatch {
|
||||
|
||||
describe("deep import changes", () => {
|
||||
const aFile: File = {
|
||||
path: `${currentDirectory}/a.ts`,
|
||||
path: `${projectRoot}/a.ts`,
|
||||
content: `import {B} from './b';
|
||||
declare var console: any;
|
||||
let b = new B();
|
||||
@@ -203,7 +202,7 @@ console.log(b.c.d);`
|
||||
|
||||
describe("updates errors when deep import file changes", () => {
|
||||
const bFile: File = {
|
||||
path: `${currentDirectory}/b.ts`,
|
||||
path: `${projectRoot}/b.ts`,
|
||||
content: `import {C} from './c';
|
||||
export class B
|
||||
{
|
||||
@@ -211,7 +210,7 @@ export class B
|
||||
}`
|
||||
};
|
||||
const cFile: File = {
|
||||
path: `${currentDirectory}/c.ts`,
|
||||
path: `${projectRoot}/c.ts`,
|
||||
content: `export class C
|
||||
{
|
||||
d = 1;
|
||||
@@ -222,7 +221,7 @@ export class B
|
||||
|
||||
describe("updates errors when deep import through declaration file changes", () => {
|
||||
const bFile: File = {
|
||||
path: `${currentDirectory}/b.d.ts`,
|
||||
path: `${projectRoot}/b.d.ts`,
|
||||
content: `import {C} from './c';
|
||||
export class B
|
||||
{
|
||||
@@ -230,7 +229,7 @@ export class B
|
||||
}`
|
||||
};
|
||||
const cFile: File = {
|
||||
path: `${currentDirectory}/c.d.ts`,
|
||||
path: `${projectRoot}/c.d.ts`,
|
||||
content: `export class C
|
||||
{
|
||||
d: number;
|
||||
@@ -242,7 +241,7 @@ export class B
|
||||
|
||||
describe("updates errors in file not exporting a deep multilevel import that changes", () => {
|
||||
const aFile: File = {
|
||||
path: `${currentDirectory}/a.ts`,
|
||||
path: `${projectRoot}/a.ts`,
|
||||
content: `export interface Point {
|
||||
name: string;
|
||||
c: Coords;
|
||||
@@ -253,13 +252,13 @@ export interface Coords {
|
||||
}`
|
||||
};
|
||||
const bFile: File = {
|
||||
path: `${currentDirectory}/b.ts`,
|
||||
path: `${projectRoot}/b.ts`,
|
||||
content: `import { Point } from "./a";
|
||||
export interface PointWrapper extends Point {
|
||||
}`
|
||||
};
|
||||
const cFile: File = {
|
||||
path: `${currentDirectory}/c.ts`,
|
||||
path: `${projectRoot}/c.ts`,
|
||||
content: `import { PointWrapper } from "./b";
|
||||
export function getPoint(): PointWrapper {
|
||||
return {
|
||||
@@ -272,12 +271,12 @@ export function getPoint(): PointWrapper {
|
||||
};`
|
||||
};
|
||||
const dFile: File = {
|
||||
path: `${currentDirectory}/d.ts`,
|
||||
path: `${projectRoot}/d.ts`,
|
||||
content: `import { getPoint } from "./c";
|
||||
getPoint().c.x;`
|
||||
};
|
||||
const eFile: File = {
|
||||
path: `${currentDirectory}/e.ts`,
|
||||
path: `${projectRoot}/e.ts`,
|
||||
content: `import "./d";`
|
||||
};
|
||||
verifyEmitAndErrorUpdates({
|
||||
@@ -301,14 +300,14 @@ getPoint().c.x;`
|
||||
|
||||
describe("updates errors when file transitively exported file changes", () => {
|
||||
const config: File = {
|
||||
path: `${currentDirectory}/tsconfig.json`,
|
||||
path: `${projectRoot}/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
files: ["app.ts"],
|
||||
compilerOptions: { baseUrl: "." }
|
||||
})
|
||||
};
|
||||
const app: File = {
|
||||
path: `${currentDirectory}/app.ts`,
|
||||
path: `${projectRoot}/app.ts`,
|
||||
content: `import { Data } from "lib2/public";
|
||||
export class App {
|
||||
public constructor() {
|
||||
@@ -317,11 +316,11 @@ export class App {
|
||||
}`
|
||||
};
|
||||
const lib2Public: File = {
|
||||
path: `${currentDirectory}/lib2/public.ts`,
|
||||
path: `${projectRoot}/lib2/public.ts`,
|
||||
content: `export * from "./data";`
|
||||
};
|
||||
const lib2Data: File = {
|
||||
path: `${currentDirectory}/lib2/data.ts`,
|
||||
path: `${projectRoot}/lib2/data.ts`,
|
||||
content: `import { ITest } from "lib1/public";
|
||||
export class Data {
|
||||
public test() {
|
||||
@@ -333,15 +332,15 @@ export class Data {
|
||||
}`
|
||||
};
|
||||
const lib1Public: File = {
|
||||
path: `${currentDirectory}/lib1/public.ts`,
|
||||
path: `${projectRoot}/lib1/public.ts`,
|
||||
content: `export * from "./tools/public";`
|
||||
};
|
||||
const lib1ToolsPublic: File = {
|
||||
path: `${currentDirectory}/lib1/tools/public.ts`,
|
||||
path: `${projectRoot}/lib1/tools/public.ts`,
|
||||
content: `export * from "./tools.interface";`
|
||||
};
|
||||
const lib1ToolsInterface: File = {
|
||||
path: `${currentDirectory}/lib1/tools/tools.interface.ts`,
|
||||
path: `${projectRoot}/lib1/tools/tools.interface.ts`,
|
||||
content: `export interface ITest {
|
||||
title: string;
|
||||
}`
|
||||
@@ -372,7 +371,7 @@ export class Data {
|
||||
|
||||
describe("when there are circular import and exports", () => {
|
||||
const lib2Data: File = {
|
||||
path: `${currentDirectory}/lib2/data.ts`,
|
||||
path: `${projectRoot}/lib2/data.ts`,
|
||||
content: `import { ITest } from "lib1/public"; import { Data2 } from "./data2";
|
||||
export class Data {
|
||||
public dat?: Data2; public test() {
|
||||
@@ -384,7 +383,7 @@ export class Data {
|
||||
}`
|
||||
};
|
||||
const lib2Data2: File = {
|
||||
path: `${currentDirectory}/lib2/data2.ts`,
|
||||
path: `${projectRoot}/lib2/data2.ts`,
|
||||
content: `import { Data } from "./data";
|
||||
export class Data2 {
|
||||
public dat?: Data;
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
namespace ts {
|
||||
export const projects = `/user/username/projects`;
|
||||
export const projectRoot = `${projects}/myproject`;
|
||||
}
|
||||
namespace ts.tscWatch {
|
||||
export import WatchedSystem = TestFSWithWatch.TestServerHost;
|
||||
export type File = TestFSWithWatch.File;
|
||||
|
||||
@@ -918,20 +918,19 @@ namespace ts.tscWatch {
|
||||
|
||||
describe("should not trigger should not trigger recompilation because of program emit", () => {
|
||||
function verifyWithOptions(options: CompilerOptions, outputFiles: readonly string[]) {
|
||||
const proj = "/user/username/projects/myproject";
|
||||
const file1: File = {
|
||||
path: `${proj}/file1.ts`,
|
||||
path: `${projectRoot}/file1.ts`,
|
||||
content: "export const c = 30;"
|
||||
};
|
||||
const file2: File = {
|
||||
path: `${proj}/src/file2.ts`,
|
||||
path: `${projectRoot}/src/file2.ts`,
|
||||
content: `import {c} from "file1"; export const d = 30;`
|
||||
};
|
||||
const tsconfig: File = {
|
||||
path: `${proj}/tsconfig.json`,
|
||||
path: `${projectRoot}/tsconfig.json`,
|
||||
content: generateTSConfig(options, emptyArray, "\n")
|
||||
};
|
||||
const host = createWatchedSystem([file1, file2, libFile, tsconfig], { currentDirectory: proj });
|
||||
const host = createWatchedSystem([file1, file2, libFile, tsconfig], { currentDirectory: projectRoot });
|
||||
const watch = createWatchOfConfigFile(tsconfig.path, host, /*optionsToExtend*/ undefined, /*maxNumberOfFilesToIterateForInvalidation*/1);
|
||||
checkProgramActualFiles(watch(), [file1.path, file2.path, libFile.path]);
|
||||
|
||||
@@ -1020,9 +1019,8 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("updates errors correctly when declaration emit is disabled in compiler options", () => {
|
||||
const currentDirectory = "/user/username/projects/myproject";
|
||||
const aFile: File = {
|
||||
path: `${currentDirectory}/a.ts`,
|
||||
path: `${projectRoot}/a.ts`,
|
||||
content: `import test from './b';
|
||||
test(4, 5);`
|
||||
};
|
||||
@@ -1031,11 +1029,11 @@ test(4, 5);`
|
||||
}
|
||||
export default test;`;
|
||||
const bFile: File = {
|
||||
path: `${currentDirectory}/b.ts`,
|
||||
path: `${projectRoot}/b.ts`,
|
||||
content: bFileContent
|
||||
};
|
||||
const tsconfigFile: File = {
|
||||
path: `${currentDirectory}/tsconfig.json`,
|
||||
path: `${projectRoot}/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
compilerOptions: {
|
||||
module: "commonjs",
|
||||
@@ -1045,7 +1043,7 @@ export default test;`;
|
||||
})
|
||||
};
|
||||
const files = [aFile, bFile, libFile, tsconfigFile];
|
||||
const host = createWatchedSystem(files, { currentDirectory });
|
||||
const host = createWatchedSystem(files, { currentDirectory: projectRoot });
|
||||
const watch = createWatchOfConfigFile("tsconfig.json", host);
|
||||
checkOutputErrorsInitial(host, emptyArray);
|
||||
|
||||
@@ -1072,22 +1070,21 @@ export default test;`;
|
||||
});
|
||||
|
||||
it("updates errors when strictNullChecks changes", () => {
|
||||
const currentDirectory = "/user/username/projects/myproject";
|
||||
const aFile: File = {
|
||||
path: `${currentDirectory}/a.ts`,
|
||||
path: `${projectRoot}/a.ts`,
|
||||
content: `declare function foo(): null | { hello: any };
|
||||
foo().hello`
|
||||
};
|
||||
const config: File = {
|
||||
path: `${currentDirectory}/tsconfig.json`,
|
||||
path: `${projectRoot}/tsconfig.json`,
|
||||
content: JSON.stringify({ compilerOptions: {} })
|
||||
};
|
||||
const files = [aFile, config, libFile];
|
||||
const host = createWatchedSystem(files, { currentDirectory });
|
||||
const host = createWatchedSystem(files, { currentDirectory: projectRoot });
|
||||
const watch = createWatchOfConfigFile("tsconfig.json", host);
|
||||
checkProgramActualFiles(watch(), [aFile.path, libFile.path]);
|
||||
checkOutputErrorsInitial(host, emptyArray);
|
||||
const modifiedTimeOfAJs = host.getModifiedTime(`${currentDirectory}/a.js`);
|
||||
const modifiedTimeOfAJs = host.getModifiedTime(`${projectRoot}/a.js`);
|
||||
host.writeFile(config.path, JSON.stringify({ compilerOptions: { strictNullChecks: true } }));
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
const expectedStrictNullErrors = [
|
||||
@@ -1095,39 +1092,38 @@ foo().hello`
|
||||
];
|
||||
checkOutputErrorsIncremental(host, expectedStrictNullErrors);
|
||||
// File a need not be rewritten
|
||||
assert.equal(host.getModifiedTime(`${currentDirectory}/a.js`), modifiedTimeOfAJs);
|
||||
assert.equal(host.getModifiedTime(`${projectRoot}/a.js`), modifiedTimeOfAJs);
|
||||
host.writeFile(config.path, JSON.stringify({ compilerOptions: { strict: true, alwaysStrict: false } })); // Avoid changing 'alwaysStrict' or must re-bind
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkOutputErrorsIncremental(host, expectedStrictNullErrors);
|
||||
// File a need not be rewritten
|
||||
assert.equal(host.getModifiedTime(`${currentDirectory}/a.js`), modifiedTimeOfAJs);
|
||||
assert.equal(host.getModifiedTime(`${projectRoot}/a.js`), modifiedTimeOfAJs);
|
||||
host.writeFile(config.path, JSON.stringify({ compilerOptions: {} }));
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
// File a need not be rewritten
|
||||
assert.equal(host.getModifiedTime(`${currentDirectory}/a.js`), modifiedTimeOfAJs);
|
||||
assert.equal(host.getModifiedTime(`${projectRoot}/a.js`), modifiedTimeOfAJs);
|
||||
});
|
||||
|
||||
it("updates errors when ambient modules of program changes", () => {
|
||||
const currentDirectory = "/user/username/projects/myproject";
|
||||
const aFile: File = {
|
||||
path: `${currentDirectory}/a.ts`,
|
||||
path: `${projectRoot}/a.ts`,
|
||||
content: `declare module 'a' {
|
||||
type foo = number;
|
||||
}`
|
||||
};
|
||||
const config: File = {
|
||||
path: `${currentDirectory}/tsconfig.json`,
|
||||
path: `${projectRoot}/tsconfig.json`,
|
||||
content: "{}"
|
||||
};
|
||||
const files = [aFile, config, libFile];
|
||||
const host = createWatchedSystem(files, { currentDirectory });
|
||||
const host = createWatchedSystem(files, { currentDirectory: projectRoot });
|
||||
const watch = createWatchOfConfigFile("tsconfig.json", host);
|
||||
checkProgramActualFiles(watch(), [aFile.path, libFile.path]);
|
||||
checkOutputErrorsInitial(host, emptyArray);
|
||||
|
||||
// Create bts with same file contents
|
||||
const bTsPath = `${currentDirectory}/b.ts`;
|
||||
const bTsPath = `${projectRoot}/b.ts`;
|
||||
host.writeFile(bTsPath, aFile.content);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkProgramActualFiles(watch(), [aFile.path, "b.ts", libFile.path]);
|
||||
@@ -1144,7 +1140,6 @@ foo().hello`
|
||||
});
|
||||
|
||||
describe("updates errors in lib file", () => {
|
||||
const currentDirectory = "/user/username/projects/myproject";
|
||||
const field = "fullscreen";
|
||||
const fieldWithoutReadonly = `interface Document {
|
||||
${field}: boolean;
|
||||
@@ -1166,7 +1161,7 @@ interface Document {
|
||||
const files = [aFile, libFileWithDocument];
|
||||
|
||||
function verifyLibErrors(options: CompilerOptions) {
|
||||
const host = createWatchedSystem(files, { currentDirectory });
|
||||
const host = createWatchedSystem(files, { currentDirectory: projectRoot });
|
||||
const watch = createWatchOfFilesAndCompilerOptions([aFile.path], host, options);
|
||||
checkProgramActualFiles(watch(), [aFile.path, libFile.path]);
|
||||
checkOutputErrorsInitial(host, getErrors());
|
||||
@@ -1202,7 +1197,7 @@ interface Document {
|
||||
|
||||
describe("when non module file changes", () => {
|
||||
const aFile: File = {
|
||||
path: `${currentDirectory}/a.ts`,
|
||||
path: `${projectRoot}/a.ts`,
|
||||
content: `${fieldWithoutReadonly}
|
||||
var y: number;`
|
||||
};
|
||||
@@ -1211,7 +1206,7 @@ var y: number;`
|
||||
|
||||
describe("when module file with global definitions changes", () => {
|
||||
const aFile: File = {
|
||||
path: `${currentDirectory}/a.ts`,
|
||||
path: `${projectRoot}/a.ts`,
|
||||
content: `export {}
|
||||
declare global {
|
||||
${fieldWithoutReadonly}
|
||||
@@ -1223,16 +1218,15 @@ var y: number;
|
||||
});
|
||||
|
||||
it("when skipLibCheck and skipDefaultLibCheck changes", () => {
|
||||
const currentDirectory = "/user/username/projects/myproject";
|
||||
const field = "fullscreen";
|
||||
const aFile: File = {
|
||||
path: `${currentDirectory}/a.ts`,
|
||||
path: `${projectRoot}/a.ts`,
|
||||
content: `interface Document {
|
||||
${field}: boolean;
|
||||
}`
|
||||
};
|
||||
const bFile: File = {
|
||||
path: `${currentDirectory}/b.d.ts`,
|
||||
path: `${projectRoot}/b.d.ts`,
|
||||
content: `interface Document {
|
||||
${field}: boolean;
|
||||
}`
|
||||
@@ -1245,13 +1239,13 @@ interface Document {
|
||||
}`
|
||||
};
|
||||
const configFile: File = {
|
||||
path: `${currentDirectory}/tsconfig.json`,
|
||||
path: `${projectRoot}/tsconfig.json`,
|
||||
content: "{}"
|
||||
};
|
||||
|
||||
const files = [aFile, bFile, configFile, libFileWithDocument];
|
||||
|
||||
const host = createWatchedSystem(files, { currentDirectory });
|
||||
const host = createWatchedSystem(files, { currentDirectory: projectRoot });
|
||||
const watch = createWatchOfConfigFile("tsconfig.json", host);
|
||||
verifyProgramFiles();
|
||||
checkOutputErrorsInitial(host, [
|
||||
@@ -1284,18 +1278,17 @@ interface Document {
|
||||
});
|
||||
|
||||
it("reports errors correctly with isolatedModules", () => {
|
||||
const currentDirectory = "/user/username/projects/myproject";
|
||||
const aFile: File = {
|
||||
path: `${currentDirectory}/a.ts`,
|
||||
path: `${projectRoot}/a.ts`,
|
||||
content: `export const a: string = "";`
|
||||
};
|
||||
const bFile: File = {
|
||||
path: `${currentDirectory}/b.ts`,
|
||||
path: `${projectRoot}/b.ts`,
|
||||
content: `import { a } from "./a";
|
||||
const b: string = a;`
|
||||
};
|
||||
const configFile: File = {
|
||||
path: `${currentDirectory}/tsconfig.json`,
|
||||
path: `${projectRoot}/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
compilerOptions: {
|
||||
isolatedModules: true
|
||||
@@ -1305,20 +1298,20 @@ const b: string = a;`
|
||||
|
||||
const files = [aFile, bFile, libFile, configFile];
|
||||
|
||||
const host = createWatchedSystem(files, { currentDirectory });
|
||||
const host = createWatchedSystem(files, { currentDirectory: projectRoot });
|
||||
const watch = createWatchOfConfigFile("tsconfig.json", host);
|
||||
verifyProgramFiles();
|
||||
checkOutputErrorsInitial(host, emptyArray);
|
||||
assert.equal(host.readFile(`${currentDirectory}/a.js`), `"use strict";
|
||||
assert.equal(host.readFile(`${projectRoot}/a.js`), `"use strict";
|
||||
exports.__esModule = true;
|
||||
exports.a = "";
|
||||
`, "Contents of a.js");
|
||||
assert.equal(host.readFile(`${currentDirectory}/b.js`), `"use strict";
|
||||
assert.equal(host.readFile(`${projectRoot}/b.js`), `"use strict";
|
||||
exports.__esModule = true;
|
||||
var a_1 = require("./a");
|
||||
var b = a_1.a;
|
||||
`, "Contents of b.js");
|
||||
const modifiedTime = host.getModifiedTime(`${currentDirectory}/b.js`);
|
||||
const modifiedTime = host.getModifiedTime(`${projectRoot}/b.js`);
|
||||
|
||||
host.writeFile(aFile.path, `export const a: number = 1`);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
@@ -1326,11 +1319,11 @@ var b = a_1.a;
|
||||
checkOutputErrorsIncremental(host, [
|
||||
getDiagnosticOfFileFromProgram(watch(), bFile.path, bFile.content.indexOf("b"), 1, Diagnostics.Type_0_is_not_assignable_to_type_1, "number", "string")
|
||||
]);
|
||||
assert.equal(host.readFile(`${currentDirectory}/a.js`), `"use strict";
|
||||
assert.equal(host.readFile(`${projectRoot}/a.js`), `"use strict";
|
||||
exports.__esModule = true;
|
||||
exports.a = 1;
|
||||
`, "Contents of a.js");
|
||||
assert.equal(host.getModifiedTime(`${currentDirectory}/b.js`), modifiedTime, "Timestamp of b.js");
|
||||
assert.equal(host.getModifiedTime(`${projectRoot}/b.js`), modifiedTime, "Timestamp of b.js");
|
||||
|
||||
function verifyProgramFiles() {
|
||||
checkProgramActualFiles(watch(), [aFile.path, bFile.path, libFile.path]);
|
||||
@@ -1338,9 +1331,8 @@ exports.a = 1;
|
||||
});
|
||||
|
||||
it("reports errors correctly with file not in rootDir", () => {
|
||||
const currentDirectory = "/user/username/projects/myproject";
|
||||
const aFile: File = {
|
||||
path: `${currentDirectory}/a.ts`,
|
||||
path: `${projectRoot}/a.ts`,
|
||||
content: `import { x } from "../b";`
|
||||
};
|
||||
const bFile: File = {
|
||||
@@ -1348,7 +1340,7 @@ exports.a = 1;
|
||||
content: `export const x = 10;`
|
||||
};
|
||||
const configFile: File = {
|
||||
path: `${currentDirectory}/tsconfig.json`,
|
||||
path: `${projectRoot}/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
compilerOptions: {
|
||||
rootDir: ".",
|
||||
@@ -1359,10 +1351,10 @@ exports.a = 1;
|
||||
|
||||
const files = [aFile, bFile, libFile, configFile];
|
||||
|
||||
const host = createWatchedSystem(files, { currentDirectory });
|
||||
const host = createWatchedSystem(files, { currentDirectory: projectRoot });
|
||||
const watch = createWatchOfConfigFile("tsconfig.json", host);
|
||||
checkOutputErrorsInitial(host, [
|
||||
getDiagnosticOfFileFromProgram(watch(), aFile.path, aFile.content.indexOf(`"../b"`), `"../b"`.length, Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, bFile.path, currentDirectory)
|
||||
getDiagnosticOfFileFromProgram(watch(), aFile.path, aFile.content.indexOf(`"../b"`), `"../b"`.length, Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, bFile.path, projectRoot)
|
||||
]);
|
||||
const aContent = `
|
||||
|
||||
@@ -1370,7 +1362,7 @@ ${aFile.content}`;
|
||||
host.writeFile(aFile.path, aContent);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkOutputErrorsIncremental(host, [
|
||||
getDiagnosticOfFileFromProgram(watch(), aFile.path, aContent.indexOf(`"../b"`), `"../b"`.length, Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, bFile.path, currentDirectory)
|
||||
getDiagnosticOfFileFromProgram(watch(), aFile.path, aContent.indexOf(`"../b"`), `"../b"`.length, Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, bFile.path, projectRoot)
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -339,42 +339,40 @@ declare module "fs" {
|
||||
});
|
||||
|
||||
it("works when renaming node_modules folder that already contains @types folder", () => {
|
||||
const currentDirectory = "/user/username/projects/myproject";
|
||||
const file: File = {
|
||||
path: `${currentDirectory}/a.ts`,
|
||||
path: `${projectRoot}/a.ts`,
|
||||
content: `import * as q from "qqq";`
|
||||
};
|
||||
const module: File = {
|
||||
path: `${currentDirectory}/node_modules2/@types/qqq/index.d.ts`,
|
||||
path: `${projectRoot}/node_modules2/@types/qqq/index.d.ts`,
|
||||
content: "export {}"
|
||||
};
|
||||
const files = [file, module, libFile];
|
||||
const host = createWatchedSystem(files, { currentDirectory });
|
||||
const host = createWatchedSystem(files, { currentDirectory: projectRoot });
|
||||
const watch = createWatchOfFilesAndCompilerOptions([file.path], host);
|
||||
|
||||
checkProgramActualFiles(watch(), [file.path, libFile.path]);
|
||||
checkOutputErrorsInitial(host, [getDiagnosticModuleNotFoundOfFile(watch(), file, "qqq")]);
|
||||
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
|
||||
checkWatchedDirectories(host, [`${currentDirectory}/node_modules`, `${currentDirectory}/node_modules/@types`], /*recursive*/ true);
|
||||
checkWatchedDirectories(host, [`${projectRoot}/node_modules`, `${projectRoot}/node_modules/@types`], /*recursive*/ true);
|
||||
|
||||
host.renameFolder(`${currentDirectory}/node_modules2`, `${currentDirectory}/node_modules`);
|
||||
host.renameFolder(`${projectRoot}/node_modules2`, `${projectRoot}/node_modules`);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkProgramActualFiles(watch(), [file.path, libFile.path, `${currentDirectory}/node_modules/@types/qqq/index.d.ts`]);
|
||||
checkProgramActualFiles(watch(), [file.path, libFile.path, `${projectRoot}/node_modules/@types/qqq/index.d.ts`]);
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
});
|
||||
|
||||
describe("ignores files/folder changes in node_modules that start with '.'", () => {
|
||||
const projectPath = "/user/username/projects/project";
|
||||
const npmCacheFile: File = {
|
||||
path: `${projectPath}/node_modules/.cache/babel-loader/89c02171edab901b9926470ba6d5677e.ts`,
|
||||
path: `${projectRoot}/node_modules/.cache/babel-loader/89c02171edab901b9926470ba6d5677e.ts`,
|
||||
content: JSON.stringify({ something: 10 })
|
||||
};
|
||||
const file1: File = {
|
||||
path: `${projectPath}/test.ts`,
|
||||
path: `${projectRoot}/test.ts`,
|
||||
content: `import { x } from "somemodule";`
|
||||
};
|
||||
const file2: File = {
|
||||
path: `${projectPath}/node_modules/somemodule/index.d.ts`,
|
||||
path: `${projectRoot}/node_modules/somemodule/index.d.ts`,
|
||||
content: `export const x = 10;`
|
||||
};
|
||||
const files = [libFile, file1, file2];
|
||||
@@ -390,7 +388,7 @@ declare module "fs" {
|
||||
});
|
||||
it("when watching node_modules as part of wild card directories in config project", () => {
|
||||
const config: File = {
|
||||
path: `${projectPath}/tsconfig.json`,
|
||||
path: `${projectRoot}/tsconfig.json`,
|
||||
content: "{}"
|
||||
};
|
||||
const host = createWatchedSystem(files.concat(config));
|
||||
@@ -402,10 +400,50 @@ declare module "fs" {
|
||||
host.checkTimeoutQueueLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
it("when types in compiler option are global and installed at later point", () => {
|
||||
const projectRoot = "/user/username/projects/myproject";
|
||||
const app: File = {
|
||||
path: `${projectRoot}/lib/app.ts`,
|
||||
content: `myapp.component("hello");`
|
||||
};
|
||||
const tsconfig: File = {
|
||||
path: `${projectRoot}/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
compilerOptions: {
|
||||
module: "none",
|
||||
types: ["@myapp/ts-types"]
|
||||
}
|
||||
})
|
||||
};
|
||||
const host = createWatchedSystem([app, tsconfig, libFile]);
|
||||
const watch = createWatchOfConfigFile(tsconfig.path, host);
|
||||
checkProgramActualFiles(watch(), [app.path, libFile.path]);
|
||||
host.checkTimeoutQueueLength(0);
|
||||
checkOutputErrorsInitial(host, [
|
||||
createCompilerDiagnostic(Diagnostics.Cannot_find_type_definition_file_for_0, "@myapp/ts-types")
|
||||
]);
|
||||
|
||||
host.ensureFileOrFolder({
|
||||
path: `${projectRoot}/node_modules/@myapp/ts-types/package.json`,
|
||||
content: JSON.stringify({
|
||||
version: "1.65.1",
|
||||
types: "types/somefile.define.d.ts"
|
||||
})
|
||||
});
|
||||
host.ensureFileOrFolder({
|
||||
path: `${projectRoot}/node_modules/@myapp/ts-types/types/somefile.define.d.ts`,
|
||||
content: `
|
||||
declare namespace myapp {
|
||||
function component(str: string): number;
|
||||
}`
|
||||
});
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
});
|
||||
});
|
||||
|
||||
describe("unittests:: tsc-watch:: resolutionCache:: tsc-watch with modules linked to sibling folder", () => {
|
||||
const projectRoot = "/user/username/projects/project";
|
||||
const mainPackageRoot = `${projectRoot}/main`;
|
||||
const linkedPackageRoot = `${projectRoot}/linked-package`;
|
||||
const mainFile: File = {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace ts.tscWatch {
|
||||
describe("unittests:: tsc-watch:: watchAPI:: tsc-watch with custom module resolution", () => {
|
||||
const projectRoot = "/user/username/projects/project";
|
||||
const configFileJson: any = {
|
||||
compilerOptions: { module: "commonjs", resolveJsonModule: true },
|
||||
files: ["index.ts"]
|
||||
@@ -39,7 +38,6 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
describe("unittests:: tsc-watch:: watchAPI:: tsc-watch expose error count to watch status reporter", () => {
|
||||
const projectRoot = "/user/username/projects/project";
|
||||
const configFileJson: any = {
|
||||
compilerOptions: { module: "commonjs" },
|
||||
files: ["index.ts"]
|
||||
|
||||
@@ -802,7 +802,6 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
describe("unittests:: tsserver:: compileOnSave:: CompileOnSaveAffectedFileListRequest with and without projectFileName in request", () => {
|
||||
const projectRoot = "/user/username/projects/myproject";
|
||||
const core: File = {
|
||||
path: `${projectRoot}/core/core.ts`,
|
||||
content: "let z = 10;"
|
||||
|
||||
@@ -82,7 +82,6 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("add and then remove a config file in a folder with loose files", () => {
|
||||
const projectRoot = "/user/username/projects/project";
|
||||
const configFile: File = {
|
||||
path: `${projectRoot}/tsconfig.json`,
|
||||
content: `{
|
||||
@@ -440,7 +439,6 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("open file become a part of configured project if it is referenced from root file", () => {
|
||||
const projectRoot = "/user/username/projects/project";
|
||||
const file1 = {
|
||||
path: `${projectRoot}/a/b/f1.ts`,
|
||||
content: "export let x = 5"
|
||||
@@ -846,6 +844,67 @@ namespace ts.projectSystem {
|
||||
const edits = project.getLanguageService().getFormattingEditsForDocument(f1.path, options);
|
||||
assert.deepEqual(edits, [{ span: createTextSpan(/*start*/ 7, /*length*/ 3), newText: " " }]);
|
||||
});
|
||||
|
||||
it("when multiple projects are open, detects correct default project", () => {
|
||||
const barConfig: File = {
|
||||
path: `${projectRoot}/bar/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
include: ["index.ts"],
|
||||
compilerOptions: {
|
||||
lib: ["dom", "es2017"]
|
||||
}
|
||||
})
|
||||
};
|
||||
const barIndex: File = {
|
||||
path: `${projectRoot}/bar/index.ts`,
|
||||
content: `
|
||||
export function bar() {
|
||||
console.log("hello world");
|
||||
}`
|
||||
};
|
||||
const fooConfig: File = {
|
||||
path: `${projectRoot}/foo/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
include: ["index.ts"],
|
||||
compilerOptions: {
|
||||
lib: ["es2017"]
|
||||
}
|
||||
})
|
||||
};
|
||||
const fooIndex: File = {
|
||||
path: `${projectRoot}/foo/index.ts`,
|
||||
content: `
|
||||
import { bar } from "bar";
|
||||
bar();`
|
||||
};
|
||||
const barSymLink: SymLink = {
|
||||
path: `${projectRoot}/foo/node_modules/bar`,
|
||||
symLink: `${projectRoot}/bar`
|
||||
};
|
||||
|
||||
const lib2017: File = {
|
||||
path: `${getDirectoryPath(libFile.path)}/lib.es2017.d.ts`,
|
||||
content: libFile.content
|
||||
};
|
||||
const libDom: File = {
|
||||
path: `${getDirectoryPath(libFile.path)}/lib.dom.d.ts`,
|
||||
content: `
|
||||
declare var console: {
|
||||
log(...args: any[]): void;
|
||||
};`
|
||||
};
|
||||
const host = createServerHost([barConfig, barIndex, fooConfig, fooIndex, barSymLink, lib2017, libDom]);
|
||||
const session = createSession(host, { canUseEvents: true, });
|
||||
openFilesForSession([fooIndex, barIndex], session);
|
||||
verifyGetErrRequest({
|
||||
session,
|
||||
host,
|
||||
expected: [
|
||||
{ file: barIndex, syntax: [], semantic: [], suggestion: [] },
|
||||
{ file: fooIndex, syntax: [], semantic: [], suggestion: [] },
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("unittests:: tsserver:: ConfiguredProjects:: non-existing directories listed in config file input array", () => {
|
||||
@@ -903,13 +962,12 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should tolerate invalid include files that start in subDirectory", () => {
|
||||
const projectFolder = "/user/username/projects/myproject";
|
||||
const f = {
|
||||
path: `${projectFolder}/src/server/index.ts`,
|
||||
path: `${projectRoot}/src/server/index.ts`,
|
||||
content: "let x = 1"
|
||||
};
|
||||
const config = {
|
||||
path: `${projectFolder}/src/server/tsconfig.json`,
|
||||
path: `${projectRoot}/src/server/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
compiler: {
|
||||
module: "commonjs",
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
namespace ts.projectSystem {
|
||||
describe("unittests:: tsserver:: document registry in project service", () => {
|
||||
const projectRootPath = "/user/username/projects/project";
|
||||
const importModuleContent = `import {a} from "./module1"`;
|
||||
const file: File = {
|
||||
path: `${projectRootPath}/index.ts`,
|
||||
path: `${projectRoot}/index.ts`,
|
||||
content: importModuleContent
|
||||
};
|
||||
const moduleFile: File = {
|
||||
path: `${projectRootPath}/module1.d.ts`,
|
||||
path: `${projectRoot}/module1.d.ts`,
|
||||
content: "export const a: number;"
|
||||
};
|
||||
const configFile: File = {
|
||||
path: `${projectRootPath}/tsconfig.json`,
|
||||
path: `${projectRoot}/tsconfig.json`,
|
||||
content: JSON.stringify({ files: ["index.ts"] })
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace ts.projectSystem {
|
||||
describe("unittests:: tsserver:: events:: LargeFileReferencedEvent with large file", () => {
|
||||
const projectRoot = "/user/username/projects/project";
|
||||
|
||||
function getLargeFile(useLargeTsFile: boolean) {
|
||||
return `src/large.${useLargeTsFile ? "ts" : "js"}`;
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
namespace ts.projectSystem {
|
||||
describe("unittests:: tsserver:: events:: ProjectLoadingStart and ProjectLoadingFinish events", () => {
|
||||
const projectRoot = "/user/username/projects";
|
||||
const aTs: File = {
|
||||
path: `${projectRoot}/a/a.ts`,
|
||||
path: `${projects}/a/a.ts`,
|
||||
content: "export class A { }"
|
||||
};
|
||||
const configA: File = {
|
||||
path: `${projectRoot}/a/tsconfig.json`,
|
||||
path: `${projects}/a/tsconfig.json`,
|
||||
content: "{}"
|
||||
};
|
||||
const bTsPath = `${projectRoot}/b/b.ts`;
|
||||
const configBPath = `${projectRoot}/b/tsconfig.json`;
|
||||
const bTsPath = `${projects}/b/b.ts`;
|
||||
const configBPath = `${projects}/b/tsconfig.json`;
|
||||
const files = [libFile, aTs, configA];
|
||||
|
||||
function verifyProjectLoadingStartAndFinish(createSession: (host: TestServerHost) => {
|
||||
@@ -84,14 +83,14 @@ namespace ts.projectSystem {
|
||||
|
||||
function verify(disableSourceOfProjectReferenceRedirect?: true) {
|
||||
const aDTs: File = {
|
||||
path: `${projectRoot}/a/a.d.ts`,
|
||||
path: `${projects}/a/a.d.ts`,
|
||||
content: `export declare class A {
|
||||
}
|
||||
//# sourceMappingURL=a.d.ts.map
|
||||
`
|
||||
};
|
||||
const aDTsMap: File = {
|
||||
path: `${projectRoot}/a/a.d.ts.map`,
|
||||
path: `${projects}/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 = {
|
||||
@@ -134,7 +133,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
describe("with external projects and config files ", () => {
|
||||
const projectFileName = `${projectRoot}/a/project.csproj`;
|
||||
const projectFileName = `${projects}/a/project.csproj`;
|
||||
|
||||
function createSession(lazyConfiguredProjectsFromExternalProject: boolean) {
|
||||
const { session, service, verifyEvent: verifyEventWorker, getNumberOfEvents } = createSessionToVerifyEvent(files);
|
||||
|
||||
@@ -824,10 +824,9 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("handles creation of external project with jsconfig before jsconfig creation watcher is invoked", () => {
|
||||
const projectLocation = `/user/username/projects/WebApplication36/WebApplication36`;
|
||||
const projectFileName = `${projectLocation}/WebApplication36.csproj`;
|
||||
const projectFileName = `${projectRoot}/WebApplication36.csproj`;
|
||||
const tsconfig: File = {
|
||||
path: `${projectLocation}/tsconfig.json`,
|
||||
path: `${projectRoot}/tsconfig.json`,
|
||||
content: "{}"
|
||||
};
|
||||
const files = [libFile, tsconfig];
|
||||
@@ -845,7 +844,7 @@ namespace ts.projectSystem {
|
||||
checkProjectActualFiles(configProject, [tsconfig.path]);
|
||||
|
||||
// write js file, open external project and open it for edit
|
||||
const jsFilePath = `${projectLocation}/javascript.js`;
|
||||
const jsFilePath = `${projectRoot}/javascript.js`;
|
||||
host.writeFile(jsFilePath, "");
|
||||
service.openExternalProjects([{
|
||||
projectFileName,
|
||||
@@ -860,7 +859,7 @@ namespace ts.projectSystem {
|
||||
|
||||
// write jsconfig file
|
||||
const jsConfig: File = {
|
||||
path: `${projectLocation}/jsconfig.json`,
|
||||
path: `${projectRoot}/jsconfig.json`,
|
||||
content: "{}"
|
||||
};
|
||||
// Dont invoke file creation watchers as the repro suggests
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
namespace ts.projectSystem {
|
||||
describe("unittests:: tsserver:: Inferred projects", () => {
|
||||
it("create inferred project", () => {
|
||||
const projectRoot = "/user/username/projects/project";
|
||||
const appFile: File = {
|
||||
path: `${projectRoot}/app.ts`,
|
||||
content: `
|
||||
@@ -31,7 +30,6 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should use only one inferred project if 'useOneInferredProject' is set", () => {
|
||||
const projectRoot = "/user/username/projects/project";
|
||||
const file1 = {
|
||||
path: `${projectRoot}/a/b/main.ts`,
|
||||
content: "let x =1;"
|
||||
@@ -347,7 +345,6 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should still retain configured project created while opening the file", () => {
|
||||
const projectRoot = "/user/username/projects/project";
|
||||
const appFile: File = {
|
||||
path: `${projectRoot}/app.ts`,
|
||||
content: `const app = 20;`
|
||||
|
||||
@@ -292,42 +292,21 @@ namespace ts.projectSystem {
|
||||
// Since this is not js project so no typings are queued
|
||||
host.checkTimeoutQueueLength(0);
|
||||
|
||||
const newTimeoutId = host.getNextTimeoutId();
|
||||
const expectedSequenceId = session.getNextSeq();
|
||||
session.executeCommandSeq<protocol.GeterrRequest>({
|
||||
command: server.CommandNames.Geterr,
|
||||
arguments: {
|
||||
delay: 0,
|
||||
files: [untitledFile]
|
||||
}
|
||||
});
|
||||
host.checkTimeoutQueueLength(1);
|
||||
|
||||
// Run the last one = get error request
|
||||
host.runQueuedTimeoutCallbacks(newTimeoutId);
|
||||
|
||||
assert.isFalse(hasError());
|
||||
host.checkTimeoutQueueLength(0);
|
||||
checkErrorMessage(session, "syntaxDiag", { file: untitledFile, diagnostics: [] });
|
||||
session.clearMessages();
|
||||
|
||||
host.runQueuedImmediateCallbacks();
|
||||
assert.isFalse(hasError());
|
||||
const errorOffset = fileContent.indexOf(refPathNotFound1) + 1;
|
||||
checkErrorMessage(session, "semanticDiag", {
|
||||
file: untitledFile,
|
||||
diagnostics: [
|
||||
createDiagnostic({ line: 1, offset: errorOffset }, { line: 1, offset: errorOffset + refPathNotFound1.length }, Diagnostics.File_0_not_found, [refPathNotFound1], "error"),
|
||||
createDiagnostic({ line: 2, offset: errorOffset }, { line: 2, offset: errorOffset + refPathNotFound2.length }, Diagnostics.File_0_not_found, [refPathNotFound2.substr(2)], "error")
|
||||
]
|
||||
verifyGetErrRequest({
|
||||
session,
|
||||
host,
|
||||
expected: [{
|
||||
file: untitledFile,
|
||||
syntax: [],
|
||||
semantic: [
|
||||
createDiagnostic({ line: 1, offset: errorOffset }, { line: 1, offset: errorOffset + refPathNotFound1.length }, Diagnostics.File_0_not_found, [refPathNotFound1], "error"),
|
||||
createDiagnostic({ line: 2, offset: errorOffset }, { line: 2, offset: errorOffset + refPathNotFound2.length }, Diagnostics.File_0_not_found, [refPathNotFound2.substr(2)], "error")
|
||||
],
|
||||
suggestion: []
|
||||
}],
|
||||
onErrEvent: () => assert.isFalse(hasError())
|
||||
});
|
||||
session.clearMessages();
|
||||
|
||||
host.runQueuedImmediateCallbacks(1);
|
||||
assert.isFalse(hasError());
|
||||
checkErrorMessage(session, "suggestionDiag", { file: untitledFile, diagnostics: [] });
|
||||
checkCompleteEvent(session, 2, expectedSequenceId);
|
||||
session.clearMessages();
|
||||
}
|
||||
|
||||
it("has projectRoot", () => {
|
||||
@@ -371,27 +350,16 @@ namespace ts.projectSystem {
|
||||
verifyErrorsInApp();
|
||||
|
||||
function verifyErrorsInApp() {
|
||||
session.clearMessages();
|
||||
const expectedSequenceId = session.getNextSeq();
|
||||
session.executeCommandSeq<protocol.GeterrRequest>({
|
||||
command: server.CommandNames.Geterr,
|
||||
arguments: {
|
||||
delay: 0,
|
||||
files: [app.path]
|
||||
}
|
||||
verifyGetErrRequest({
|
||||
session,
|
||||
host,
|
||||
expected: [{
|
||||
file: app,
|
||||
syntax: [],
|
||||
semantic: [],
|
||||
suggestion: []
|
||||
}],
|
||||
});
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
checkErrorMessage(session, "syntaxDiag", { file: app.path, diagnostics: [] });
|
||||
session.clearMessages();
|
||||
|
||||
host.runQueuedImmediateCallbacks();
|
||||
checkErrorMessage(session, "semanticDiag", { file: app.path, diagnostics: [] });
|
||||
session.clearMessages();
|
||||
|
||||
host.runQueuedImmediateCallbacks(1);
|
||||
checkErrorMessage(session, "suggestionDiag", { file: app.path, diagnostics: [] });
|
||||
checkCompleteEvent(session, 2, expectedSequenceId);
|
||||
session.clearMessages();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -421,7 +389,6 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("Reports errors correctly when file referenced by inferred project root, is opened right after closing the root file", () => {
|
||||
const projectRoot = "/user/username/projects/myproject";
|
||||
const app: File = {
|
||||
path: `${projectRoot}/src/client/app.js`,
|
||||
content: ""
|
||||
@@ -451,32 +418,12 @@ namespace ts.projectSystem {
|
||||
checkErrors([serverUtilities.path, app.path]);
|
||||
|
||||
function checkErrors(openFiles: [string, string]) {
|
||||
const expectedSequenceId = session.getNextSeq();
|
||||
session.executeCommandSeq<protocol.GeterrRequest>({
|
||||
command: protocol.CommandTypes.Geterr,
|
||||
arguments: {
|
||||
delay: 0,
|
||||
files: openFiles
|
||||
}
|
||||
verifyGetErrRequest({
|
||||
session,
|
||||
host,
|
||||
expected: openFiles.map(file => ({ file, syntax: [], semantic: [], suggestion: [] })),
|
||||
existingTimeouts: 2
|
||||
});
|
||||
|
||||
for (const openFile of openFiles) {
|
||||
session.clearMessages();
|
||||
host.checkTimeoutQueueLength(3);
|
||||
host.runQueuedTimeoutCallbacks(host.getNextTimeoutId() - 1);
|
||||
|
||||
checkErrorMessage(session, "syntaxDiag", { file: openFile, diagnostics: [] });
|
||||
session.clearMessages();
|
||||
|
||||
host.runQueuedImmediateCallbacks();
|
||||
checkErrorMessage(session, "semanticDiag", { file: openFile, diagnostics: [] });
|
||||
session.clearMessages();
|
||||
|
||||
host.runQueuedImmediateCallbacks(1);
|
||||
checkErrorMessage(session, "suggestionDiag", { file: openFile, diagnostics: [] });
|
||||
}
|
||||
checkCompleteEvent(session, 2, expectedSequenceId);
|
||||
session.clearMessages();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -531,36 +478,19 @@ declare module '@custom/plugin' {
|
||||
|
||||
function checkErrors() {
|
||||
host.checkTimeoutQueueLength(0);
|
||||
const expectedSequenceId = session.getNextSeq();
|
||||
session.executeCommandSeq<protocol.GeterrRequest>({
|
||||
command: server.CommandNames.Geterr,
|
||||
arguments: {
|
||||
delay: 0,
|
||||
files: [aFile.path],
|
||||
}
|
||||
verifyGetErrRequest({
|
||||
session,
|
||||
host,
|
||||
expected: [{
|
||||
file: aFile,
|
||||
syntax: [],
|
||||
semantic: [],
|
||||
suggestion: [
|
||||
createDiagnostic({ line: 1, offset: 1 }, { line: 1, offset: 44 }, Diagnostics._0_is_declared_but_its_value_is_never_read, ["myModule"], "suggestion", /*reportsUnnecessary*/ true),
|
||||
createDiagnostic({ line: 2, offset: 10 }, { line: 2, offset: 13 }, Diagnostics._0_is_declared_but_its_value_is_never_read, ["foo"], "suggestion", /*reportsUnnecessary*/ true)
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
|
||||
checkErrorMessage(session, "syntaxDiag", { file: aFile.path, diagnostics: [] }, /*isMostRecent*/ true);
|
||||
session.clearMessages();
|
||||
|
||||
host.runQueuedImmediateCallbacks(1);
|
||||
|
||||
checkErrorMessage(session, "semanticDiag", { file: aFile.path, diagnostics: [] });
|
||||
session.clearMessages();
|
||||
|
||||
host.runQueuedImmediateCallbacks(1);
|
||||
|
||||
checkErrorMessage(session, "suggestionDiag", {
|
||||
file: aFile.path,
|
||||
diagnostics: [
|
||||
createDiagnostic({ line: 1, offset: 1 }, { line: 1, offset: 44 }, Diagnostics._0_is_declared_but_its_value_is_never_read, ["myModule"], "suggestion", /*reportsUnnecessary*/ true),
|
||||
createDiagnostic({ line: 2, offset: 10 }, { line: 2, offset: 13 }, Diagnostics._0_is_declared_but_its_value_is_never_read, ["foo"], "suggestion", /*reportsUnnecessary*/ true)
|
||||
],
|
||||
});
|
||||
checkCompleteEvent(session, 2, expectedSequenceId);
|
||||
session.clearMessages();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
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 dependecyLocation = `${projectRoot}/dependency`;
|
||||
const usageLocation = `${projectRoot}/usage`;
|
||||
const dependencyTs: File = {
|
||||
path: `${dependecyLocation}/fns.ts`,
|
||||
content: `export function fn1() { }
|
||||
@@ -294,7 +293,7 @@ exports.fn2 = fn2;
|
||||
${appendJs}`
|
||||
},
|
||||
{
|
||||
path: `${projectLocation}/decls/fns.d.ts`,
|
||||
path: `${projectRoot}/decls/fns.d.ts`,
|
||||
content: `export declare function fn1(): void;
|
||||
export declare function fn2(): void;
|
||||
${appendDts}`
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user