mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Fix(29118): tsconfig.extends as array (#50403)
* tsconfig.extends as array * Updated baselines * Changes for pr * Changes for pr comments * Fixed formatting and edited a test * Resolved errors after a merge conflict * Added "string | list" type implentation * Removed string | list type implementation * Fixed formatting * Added compiler test * Resolving programUpdate errors * Fixing commandLineParser error
This commit is contained in:
@@ -1161,6 +1161,7 @@ function getBuildInfo(state: BuilderProgramState, bundle: BundleBuildInfo | unde
|
||||
|
||||
function convertToReusableCompilerOptionValue(option: CommandLineOption | undefined, value: CompilerOptionsValue, relativeToBuildInfo: (path: string) => string) {
|
||||
if (option) {
|
||||
Debug.assert(option.type !== "listOrElement");
|
||||
if (option.type === "list") {
|
||||
const values = value as readonly (string | number)[];
|
||||
if (option.element.isFilePath && values.length) {
|
||||
|
||||
@@ -103,6 +103,7 @@ import {
|
||||
returnTrue,
|
||||
ScriptTarget,
|
||||
startsWith,
|
||||
stringContains,
|
||||
StringLiteral,
|
||||
SyntaxKind,
|
||||
sys,
|
||||
@@ -1658,11 +1659,14 @@ export function parseCustomTypeOption(opt: CommandLineOptionOfCustomType, value:
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function parseListTypeOption(opt: CommandLineOptionOfListType, value = "", errors: Push<Diagnostic>): (string | number)[] | undefined {
|
||||
export function parseListTypeOption(opt: CommandLineOptionOfListType, value = "", errors: Push<Diagnostic>): string | (string | number)[] | undefined {
|
||||
value = trimString(value);
|
||||
if (startsWith(value, "-")) {
|
||||
return undefined;
|
||||
}
|
||||
if (opt.type === "listOrElement" && !stringContains(value, ",")) {
|
||||
return validateJsonOptionValue(opt, value, errors);
|
||||
}
|
||||
if (value === "") {
|
||||
return [];
|
||||
}
|
||||
@@ -1672,6 +1676,9 @@ export function parseListTypeOption(opt: CommandLineOptionOfListType, value = ""
|
||||
return mapDefined(values, v => validateJsonOptionValue(opt.element, parseInt(v), errors));
|
||||
case "string":
|
||||
return mapDefined(values, v => validateJsonOptionValue(opt.element, v || "", errors));
|
||||
case "boolean":
|
||||
case "object":
|
||||
return Debug.fail(`List of ${opt.element.type} is not yet supported.`);
|
||||
default:
|
||||
return mapDefined(values, v => parseCustomTypeOption(opt.element as CommandLineOptionOfCustomType, v, errors));
|
||||
}
|
||||
@@ -1850,6 +1857,9 @@ function parseOptionValue(
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
case "listOrElement":
|
||||
Debug.fail("listOrElement not supported here");
|
||||
break;
|
||||
// If not a primitive, the possible types are specified in what is effectively a map of options.
|
||||
default:
|
||||
options[opt.name] = parseCustomTypeOption(opt as CommandLineOptionOfCustomType, args[i], errors);
|
||||
@@ -2090,6 +2100,15 @@ function getCommandLineTypeAcquisitionMap() {
|
||||
return commandLineTypeAcquisitionMapCache || (commandLineTypeAcquisitionMapCache = commandLineOptionsToMap(typeAcquisitionDeclarations));
|
||||
}
|
||||
|
||||
const extendsOptionDeclaration: CommandLineOptionOfListType = {
|
||||
name: "extends",
|
||||
type: "listOrElement",
|
||||
element: {
|
||||
name: "extends",
|
||||
type: "string"
|
||||
},
|
||||
category: Diagnostics.File_Management,
|
||||
};
|
||||
let _tsconfigRootOptions: TsConfigOnlyOption;
|
||||
function getTsconfigRootOptionsMap() {
|
||||
if (_tsconfigRootOptions === undefined) {
|
||||
@@ -2121,11 +2140,7 @@ function getTsconfigRootOptionsMap() {
|
||||
elementOptions: getCommandLineTypeAcquisitionMap(),
|
||||
extraKeyDiagnostics: typeAcquisitionDidYouMeanDiagnostics
|
||||
},
|
||||
{
|
||||
name: "extends",
|
||||
type: "string",
|
||||
category: Diagnostics.File_Management,
|
||||
},
|
||||
extendsOptionDeclaration,
|
||||
{
|
||||
name: "references",
|
||||
type: "list",
|
||||
@@ -2339,11 +2354,11 @@ export function convertToObjectWorker(
|
||||
let invalidReported: boolean | undefined;
|
||||
switch (valueExpression.kind) {
|
||||
case SyntaxKind.TrueKeyword:
|
||||
reportInvalidOptionValue(option && option.type !== "boolean");
|
||||
reportInvalidOptionValue(option && option.type !== "boolean" && (option.type !== "listOrElement" || option.element.type !== "boolean"));
|
||||
return validateValue(/*value*/ true);
|
||||
|
||||
case SyntaxKind.FalseKeyword:
|
||||
reportInvalidOptionValue(option && option.type !== "boolean");
|
||||
reportInvalidOptionValue(option && option.type !== "boolean"&& (option.type !== "listOrElement" || option.element.type !== "boolean"));
|
||||
return validateValue(/*value*/ false);
|
||||
|
||||
case SyntaxKind.NullKeyword:
|
||||
@@ -2354,8 +2369,11 @@ export function convertToObjectWorker(
|
||||
if (!isDoubleQuotedString(valueExpression)) {
|
||||
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, valueExpression, Diagnostics.String_literal_with_double_quotes_expected));
|
||||
}
|
||||
reportInvalidOptionValue(option && (isString(option.type) && option.type !== "string"));
|
||||
reportInvalidOptionValue(option && isString(option.type) && option.type !== "string" && (option.type !== "listOrElement" || (isString(option.element.type) && option.element.type !== "string")));
|
||||
const text = (valueExpression as StringLiteral).text;
|
||||
if (option) {
|
||||
Debug.assert(option.type !== "listOrElement" || option.element.type === "string", "Only string or array of string is handled for now");
|
||||
}
|
||||
if (option && !isString(option.type)) {
|
||||
const customOption = option as CommandLineOptionOfCustomType;
|
||||
// Validate custom option type
|
||||
@@ -2372,18 +2390,18 @@ export function convertToObjectWorker(
|
||||
return validateValue(text);
|
||||
|
||||
case SyntaxKind.NumericLiteral:
|
||||
reportInvalidOptionValue(option && option.type !== "number");
|
||||
reportInvalidOptionValue(option && option.type !== "number" && (option.type !== "listOrElement" || option.element.type !== "number"));
|
||||
return validateValue(Number((valueExpression as NumericLiteral).text));
|
||||
|
||||
case SyntaxKind.PrefixUnaryExpression:
|
||||
if ((valueExpression as PrefixUnaryExpression).operator !== SyntaxKind.MinusToken || (valueExpression as PrefixUnaryExpression).operand.kind !== SyntaxKind.NumericLiteral) {
|
||||
break; // not valid JSON syntax
|
||||
}
|
||||
reportInvalidOptionValue(option && option.type !== "number");
|
||||
reportInvalidOptionValue(option && option.type !== "number" && (option.type !== "listOrElement" || option.element.type !== "number"));
|
||||
return validateValue(-Number(((valueExpression as PrefixUnaryExpression).operand as NumericLiteral).text));
|
||||
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
reportInvalidOptionValue(option && option.type !== "object");
|
||||
reportInvalidOptionValue(option && option.type !== "object" && (option.type !== "listOrElement" || option.element.type !== "object"));
|
||||
const objectLiteralExpression = valueExpression as ObjectLiteralExpression;
|
||||
|
||||
// Currently having element option declaration in the tsconfig with type "object"
|
||||
@@ -2404,7 +2422,7 @@ export function convertToObjectWorker(
|
||||
}
|
||||
|
||||
case SyntaxKind.ArrayLiteralExpression:
|
||||
reportInvalidOptionValue(option && option.type !== "list");
|
||||
reportInvalidOptionValue(option && option.type !== "list" && option.type !== "listOrElement");
|
||||
return validateValue(convertArrayLiteralExpressionToJson(
|
||||
(valueExpression as ArrayLiteralExpression).elements,
|
||||
option && (option as CommandLineOptionOfListType).element));
|
||||
@@ -2444,8 +2462,10 @@ export function convertToObjectWorker(
|
||||
}
|
||||
}
|
||||
|
||||
function getCompilerOptionValueTypeString(option: CommandLineOption) {
|
||||
return option.type === "list" ?
|
||||
function getCompilerOptionValueTypeString(option: CommandLineOption): string {
|
||||
return (option.type === "listOrElement") ?
|
||||
`${getCompilerOptionValueTypeString(option.element)} or Array`:
|
||||
option.type === "list" ?
|
||||
"Array" :
|
||||
isString(option.type) ? option.type : "string";
|
||||
}
|
||||
@@ -2456,6 +2476,9 @@ function isCompilerOptionsValue(option: CommandLineOption | undefined, value: an
|
||||
if (option.type === "list") {
|
||||
return isArray(value);
|
||||
}
|
||||
if (option.type === "listOrElement") {
|
||||
return isArray(value) || isCompilerOptionsValue(option.element, value);
|
||||
}
|
||||
const expectedType = isString(option.type) ? option.type : "string";
|
||||
return typeof value === expectedType;
|
||||
}
|
||||
@@ -2560,15 +2583,18 @@ function matchesSpecs(path: string, includeSpecs: readonly string[] | undefined,
|
||||
}
|
||||
|
||||
function getCustomTypeMapOfCommandLineOption(optionDefinition: CommandLineOption): Map<string, string | number> | undefined {
|
||||
if (optionDefinition.type === "string" || optionDefinition.type === "number" || optionDefinition.type === "boolean" || optionDefinition.type === "object") {
|
||||
// this is of a type CommandLineOptionOfPrimitiveType
|
||||
return undefined;
|
||||
}
|
||||
else if (optionDefinition.type === "list") {
|
||||
return getCustomTypeMapOfCommandLineOption(optionDefinition.element);
|
||||
}
|
||||
else {
|
||||
return optionDefinition.type;
|
||||
switch (optionDefinition.type) {
|
||||
case "string":
|
||||
case "number":
|
||||
case "boolean":
|
||||
case "object":
|
||||
// this is of a type CommandLineOptionOfPrimitiveType
|
||||
return undefined;
|
||||
case "list":
|
||||
case "listOrElement":
|
||||
return getCustomTypeMapOfCommandLineOption(optionDefinition.element);
|
||||
default:
|
||||
return optionDefinition.type;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2612,6 +2638,7 @@ function serializeOptionBaseObject(
|
||||
const value = options[name] as CompilerOptionsValue;
|
||||
const optionDefinition = optionsNameMap.get(name.toLowerCase());
|
||||
if (optionDefinition) {
|
||||
Debug.assert(optionDefinition.type !== "listOrElement");
|
||||
const customTypeMap = getCustomTypeMapOfCommandLineOption(optionDefinition);
|
||||
if (!customTypeMap) {
|
||||
// There is no map associated with this compiler option then use the value as-is
|
||||
@@ -2805,6 +2832,7 @@ function convertToOptionValueWithAbsolutePaths(option: CommandLineOption | undef
|
||||
else if (option.isFilePath) {
|
||||
return toAbsolutePath(value as string);
|
||||
}
|
||||
Debug.assert(option.type !== "listOrElement");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -3076,13 +3104,22 @@ export interface ParsedTsconfig {
|
||||
/**
|
||||
* Note that the case of the config path has not yet been normalized, as no files have been imported into the project yet
|
||||
*/
|
||||
extendedConfigPath?: string;
|
||||
extendedConfigPath?: string | string[];
|
||||
}
|
||||
|
||||
function isSuccessfulParsedTsconfig(value: ParsedTsconfig) {
|
||||
return !!value.options;
|
||||
}
|
||||
|
||||
interface ExtendsResult {
|
||||
options: CompilerOptions;
|
||||
watchOptions?: WatchOptions;
|
||||
include?: string[];
|
||||
exclude?: string[];
|
||||
files?: string[];
|
||||
compileOnSave?: boolean;
|
||||
extendedSourceFiles?: Set<string>
|
||||
}
|
||||
/**
|
||||
* This *just* extracts options/include/exclude/files out of a config file.
|
||||
* It does *not* resolve the included files.
|
||||
@@ -3119,34 +3156,52 @@ function parseConfig(
|
||||
if (ownConfig.extendedConfigPath) {
|
||||
// copy the resolution stack so it is never reused between branches in potential diamond-problem scenarios.
|
||||
resolutionStack = resolutionStack.concat([resolvedPath]);
|
||||
const extendedConfig = getExtendedConfig(sourceFile, ownConfig.extendedConfigPath, host, resolutionStack, errors, extendedConfigCache);
|
||||
const result: ExtendsResult = { options:{} };
|
||||
if (isString(ownConfig.extendedConfigPath)) {
|
||||
applyExtendedConfig(result, ownConfig.extendedConfigPath);
|
||||
}
|
||||
else {
|
||||
ownConfig.extendedConfigPath.forEach(extendedConfigPath => applyExtendedConfig(result, extendedConfigPath));
|
||||
}
|
||||
if (!ownConfig.raw.include && result.include) ownConfig.raw.include = result.include;
|
||||
if (!ownConfig.raw.exclude && result.exclude) ownConfig.raw.exclude = result.exclude;
|
||||
if (!ownConfig.raw.files && result.files) ownConfig.raw.files = result.files;
|
||||
if (ownConfig.raw.compileOnSave === undefined && result.compileOnSave) ownConfig.raw.compileOnSave = result.compileOnSave;
|
||||
if (sourceFile && result.extendedSourceFiles) sourceFile.extendedSourceFiles = arrayFrom(result.extendedSourceFiles.keys());
|
||||
|
||||
ownConfig.options = assign(result.options, ownConfig.options);
|
||||
ownConfig.watchOptions = ownConfig.watchOptions && result.watchOptions ?
|
||||
assign(result.watchOptions, ownConfig.watchOptions) :
|
||||
ownConfig.watchOptions || result.watchOptions;
|
||||
}
|
||||
return ownConfig;
|
||||
|
||||
function applyExtendedConfig(result: ExtendsResult, extendedConfigPath: string){
|
||||
const extendedConfig = getExtendedConfig(sourceFile, extendedConfigPath, host, resolutionStack, errors, extendedConfigCache, result);
|
||||
if (extendedConfig && isSuccessfulParsedTsconfig(extendedConfig)) {
|
||||
const baseRaw = extendedConfig.raw;
|
||||
const raw = ownConfig.raw;
|
||||
let relativeDifference: string | undefined;
|
||||
const setPropertyInRawIfNotUndefined = (propertyName: string) => {
|
||||
if (!raw[propertyName] && baseRaw[propertyName]) {
|
||||
raw[propertyName] = map(baseRaw[propertyName], (path: string) => isRootedDiskPath(path) ? path : combinePaths(
|
||||
relativeDifference ||= convertToRelativePath(getDirectoryPath(ownConfig.extendedConfigPath!), basePath, createGetCanonicalFileName(host.useCaseSensitiveFileNames)),
|
||||
const extendsRaw = extendedConfig.raw;
|
||||
let relativeDifference: string | undefined ;
|
||||
const setPropertyInResultIfNotUndefined = (propertyName: "include" | "exclude" | "files") => {
|
||||
if (extendsRaw[propertyName]) {
|
||||
result[propertyName] = map(extendsRaw[propertyName], (path: string) => isRootedDiskPath(path) ? path : combinePaths(
|
||||
relativeDifference ||= convertToRelativePath(getDirectoryPath(extendedConfigPath), basePath, createGetCanonicalFileName(host.useCaseSensitiveFileNames)),
|
||||
path
|
||||
));
|
||||
}
|
||||
};
|
||||
setPropertyInRawIfNotUndefined("include");
|
||||
setPropertyInRawIfNotUndefined("exclude");
|
||||
setPropertyInRawIfNotUndefined("files");
|
||||
if (raw.compileOnSave === undefined) {
|
||||
raw.compileOnSave = baseRaw.compileOnSave;
|
||||
setPropertyInResultIfNotUndefined("include");
|
||||
setPropertyInResultIfNotUndefined("exclude");
|
||||
setPropertyInResultIfNotUndefined("files");
|
||||
if (extendsRaw.compileOnSave !== undefined) {
|
||||
result.compileOnSave = extendsRaw.compileOnSave;
|
||||
}
|
||||
ownConfig.options = assign({}, extendedConfig.options, ownConfig.options);
|
||||
ownConfig.watchOptions = ownConfig.watchOptions && extendedConfig.watchOptions ?
|
||||
assign({}, extendedConfig.watchOptions, ownConfig.watchOptions) :
|
||||
ownConfig.watchOptions || extendedConfig.watchOptions;
|
||||
assign(result.options, extendedConfig.options);
|
||||
result.watchOptions = result.watchOptions && extendedConfig.watchOptions ?
|
||||
assign({}, result.watchOptions, extendedConfig.watchOptions) :
|
||||
result.watchOptions || extendedConfig.watchOptions;
|
||||
// TODO extend type typeAcquisition
|
||||
}
|
||||
}
|
||||
|
||||
return ownConfig;
|
||||
}
|
||||
|
||||
function parseOwnConfigOfJson(
|
||||
@@ -3166,15 +3221,28 @@ function parseOwnConfigOfJson(
|
||||
const typeAcquisition = convertTypeAcquisitionFromJsonWorker(json.typeAcquisition || json.typingOptions, basePath, errors, configFileName);
|
||||
const watchOptions = convertWatchOptionsFromJsonWorker(json.watchOptions, basePath, errors);
|
||||
json.compileOnSave = convertCompileOnSaveOptionFromJson(json, basePath, errors);
|
||||
let extendedConfigPath: string | undefined;
|
||||
let extendedConfigPath: string | string[] | undefined;
|
||||
|
||||
if (json.extends) {
|
||||
if (!isString(json.extends)) {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "extends", "string"));
|
||||
if (!isCompilerOptionsValue(extendsOptionDeclaration, json.extends)) {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "extends", getCompilerOptionValueTypeString(extendsOptionDeclaration)));
|
||||
}
|
||||
else {
|
||||
const newBase = configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath;
|
||||
extendedConfigPath = getExtendsConfigPath(json.extends, host, newBase, errors, createCompilerDiagnostic);
|
||||
if (isString(json.extends)) {
|
||||
extendedConfigPath = getExtendsConfigPath(json.extends, host, newBase, errors, createCompilerDiagnostic);
|
||||
}
|
||||
else {
|
||||
extendedConfigPath = [];
|
||||
for (const fileName of json.extends as unknown[]) {
|
||||
if (isString(fileName)) {
|
||||
extendedConfigPath = append(extendedConfigPath, getExtendsConfigPath(fileName, host, newBase, errors, createCompilerDiagnostic));
|
||||
}
|
||||
else {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "extends", getCompilerOptionValueTypeString(extendsOptionDeclaration.element)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return { raw: json, options, watchOptions, typeAcquisition, extendedConfigPath };
|
||||
@@ -3190,7 +3258,7 @@ function parseOwnConfigOfJsonSourceFile(
|
||||
const options = getDefaultCompilerOptions(configFileName);
|
||||
let typeAcquisition: TypeAcquisition | undefined, typingOptionstypeAcquisition: TypeAcquisition | undefined;
|
||||
let watchOptions: WatchOptions | undefined;
|
||||
let extendedConfigPath: string | undefined;
|
||||
let extendedConfigPath: string | string[] | undefined;
|
||||
let rootCompilerOptions: PropertyName[] | undefined;
|
||||
|
||||
const optionsIterator: JsonConversionNotifier = {
|
||||
@@ -3219,15 +3287,33 @@ function parseOwnConfigOfJsonSourceFile(
|
||||
switch (key) {
|
||||
case "extends":
|
||||
const newBase = configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath;
|
||||
if (isString(value)) {
|
||||
extendedConfigPath = getExtendsConfigPath(
|
||||
value as string,
|
||||
value,
|
||||
host,
|
||||
newBase,
|
||||
errors,
|
||||
(message, arg0) =>
|
||||
createDiagnosticForNodeInSourceFile(sourceFile, valueNode, message, arg0)
|
||||
);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
extendedConfigPath = [];
|
||||
for (let index = 0; index < (value as unknown[]).length; index++) {
|
||||
const fileName = (value as unknown[])[index];
|
||||
if (isString(fileName)) {
|
||||
extendedConfigPath = append(extendedConfigPath, getExtendsConfigPath(
|
||||
fileName,
|
||||
host,
|
||||
newBase,
|
||||
errors,
|
||||
(message, arg0) =>
|
||||
createDiagnosticForNodeInSourceFile(sourceFile, (valueNode as ArrayLiteralExpression).elements[index], message, arg0)
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
},
|
||||
onSetUnknownOptionKeyValueInRoot(key: string, keyNode: PropertyName, _value: CompilerOptionsValue, _valueNode: Expression) {
|
||||
@@ -3301,7 +3387,8 @@ function getExtendedConfig(
|
||||
host: ParseConfigHost,
|
||||
resolutionStack: string[],
|
||||
errors: Push<Diagnostic>,
|
||||
extendedConfigCache?: Map<string, ExtendedConfigCacheEntry>
|
||||
extendedConfigCache: Map<string, ExtendedConfigCacheEntry> | undefined,
|
||||
result: ExtendsResult
|
||||
): ParsedTsconfig | undefined {
|
||||
const path = host.useCaseSensitiveFileNames ? extendedConfigPath : toFileNameLowerCase(extendedConfigPath);
|
||||
let value: ExtendedConfigCacheEntry | undefined;
|
||||
@@ -3321,9 +3408,11 @@ function getExtendedConfig(
|
||||
}
|
||||
}
|
||||
if (sourceFile) {
|
||||
sourceFile.extendedSourceFiles = [extendedResult.fileName];
|
||||
(result.extendedSourceFiles ??= new Set()).add(extendedResult.fileName);
|
||||
if (extendedResult.extendedSourceFiles) {
|
||||
sourceFile.extendedSourceFiles.push(...extendedResult.extendedSourceFiles);
|
||||
for (const extenedSourceFile of extendedResult.extendedSourceFiles) {
|
||||
result.extendedSourceFiles.add(extenedSourceFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (extendedResult.parseDiagnostics.length) {
|
||||
@@ -3416,10 +3505,15 @@ function convertOptionsFromJson(optionsNameMap: Map<string, CommandLineOption>,
|
||||
export function convertJsonOption(opt: CommandLineOption, value: any, basePath: string, errors: Push<Diagnostic>): CompilerOptionsValue {
|
||||
if (isCompilerOptionsValue(opt, value)) {
|
||||
const optType = opt.type;
|
||||
if (optType === "list" && isArray(value)) {
|
||||
if ((optType === "list") && isArray(value)) {
|
||||
return convertJsonOptionOfListType(opt, value, basePath, errors);
|
||||
}
|
||||
else if (!isString(optType)) {
|
||||
else if (optType === "listOrElement") {
|
||||
return isArray(value) ?
|
||||
convertJsonOptionOfListType(opt, value, basePath, errors) :
|
||||
convertJsonOption(opt.element, value, basePath, errors);
|
||||
}
|
||||
else if (!isString(opt.type)) {
|
||||
return convertJsonOptionOfCustomType(opt as CommandLineOptionOfCustomType, value as string, errors);
|
||||
}
|
||||
const validatedValue = validateJsonOptionValue(opt, value, errors);
|
||||
@@ -3432,7 +3526,8 @@ export function convertJsonOption(opt: CommandLineOption, value: any, basePath:
|
||||
|
||||
function normalizeOptionValue(option: CommandLineOption, basePath: string, value: any): CompilerOptionsValue {
|
||||
if (isNullOrUndefined(value)) return undefined;
|
||||
if (option.type === "list") {
|
||||
if (option.type === "listOrElement" && !isArray(value)) return normalizeOptionValue(option.element, basePath, value);
|
||||
else if (option.type === "list" || option.type === "listOrElement") {
|
||||
const listOption = option;
|
||||
if (listOption.element.isFilePath || !isString(listOption.element.type)) {
|
||||
return filter(map(value, v => normalizeOptionValue(listOption.element, basePath, v)), v => listOption.listPreserveFalsyValues ? true : !!v) as CompilerOptionsValue;
|
||||
@@ -3860,6 +3955,9 @@ function getOptionValueWithEmptyStrings(value: any, option: CommandLineOption):
|
||||
return typeof value === "number" ? value : "";
|
||||
case "boolean":
|
||||
return typeof value === "boolean" ? value : "";
|
||||
case "listOrElement":
|
||||
if (!isArray(value)) return getOptionValueWithEmptyStrings(value, option.element);
|
||||
// fall through to list
|
||||
case "list":
|
||||
const elementType = option.element;
|
||||
return isArray(value) ? value.map(v => getOptionValueWithEmptyStrings(v, elementType)) : "";
|
||||
@@ -3873,7 +3971,7 @@ function getOptionValueWithEmptyStrings(value: any, option: CommandLineOption):
|
||||
}
|
||||
|
||||
|
||||
function getDefaultValueForOption(option: CommandLineOption) {
|
||||
function getDefaultValueForOption(option: CommandLineOption): {} {
|
||||
switch (option.type) {
|
||||
case "number":
|
||||
return 1;
|
||||
@@ -3884,6 +3982,8 @@ function getDefaultValueForOption(option: CommandLineOption) {
|
||||
return option.isFilePath ? `./${defaultValue && typeof defaultValue === "string" ? defaultValue : ""}` : "";
|
||||
case "list":
|
||||
return [];
|
||||
case "listOrElement":
|
||||
return getDefaultValueForOption(option.element);
|
||||
case "object":
|
||||
return {};
|
||||
default:
|
||||
|
||||
@@ -6977,7 +6977,7 @@ export interface CreateProgramOptions {
|
||||
/** @internal */
|
||||
export interface CommandLineOptionBase {
|
||||
name: string;
|
||||
type: "string" | "number" | "boolean" | "object" | "list" | Map<string, number | string>; // a value of a primitive type, or an object literal mapping named values to actual values
|
||||
type: "string" | "number" | "boolean" | "object" | "list" | "listOrElement" | Map<string, number | string>; // a value of a primitive type, or an object literal mapping named values to actual values
|
||||
isFilePath?: boolean; // True if option value is a path or fileName
|
||||
shortName?: string; // A short mnemonic for convenience - for instance, 'h' can be used in place of 'help'
|
||||
description?: DiagnosticMessage; // The message describing what the command line switch does.
|
||||
@@ -7047,7 +7047,7 @@ export interface TsConfigOnlyOption extends CommandLineOptionBase {
|
||||
|
||||
/** @internal */
|
||||
export interface CommandLineOptionOfListType extends CommandLineOptionBase {
|
||||
type: "list";
|
||||
type: "list" | "listOrElement";
|
||||
element: CommandLineOptionOfCustomType | CommandLineOptionOfStringType | CommandLineOptionOfNumberType | CommandLineOptionOfBooleanType | TsConfigOnlyOption;
|
||||
listPreserveFalsyValues?: boolean;
|
||||
}
|
||||
|
||||
@@ -253,9 +253,9 @@ function generateOptionOutput(sys: System, option: CommandLineOption, rightAlign
|
||||
typeof option.defaultValueDescription === "object"
|
||||
? getDiagnosticText(option.defaultValueDescription)
|
||||
: formatDefaultValue(
|
||||
option.defaultValueDescription,
|
||||
option.type === "list" ? option.element.type : option.type
|
||||
);
|
||||
option.defaultValueDescription,
|
||||
option.type === "list" || option.type === "listOrElement" ? option.element.type : option.type
|
||||
);
|
||||
const terminalWidth = sys.getWidthOfTerminal?.() ?? 0;
|
||||
|
||||
// Note: child_process might return `terminalWidth` as undefined.
|
||||
@@ -365,6 +365,7 @@ function generateOptionOutput(sys: System, option: CommandLineOption, rightAlign
|
||||
};
|
||||
|
||||
function getValueType(option: CommandLineOption) {
|
||||
Debug.assert(option.type !== "listOrElement");
|
||||
switch (option.type) {
|
||||
case "string":
|
||||
case "number":
|
||||
@@ -386,7 +387,7 @@ function generateOptionOutput(sys: System, option: CommandLineOption, rightAlign
|
||||
possibleValues = option.type;
|
||||
break;
|
||||
case "list":
|
||||
// TODO: check infinite loop
|
||||
case "listOrElement":
|
||||
possibleValues = getPossibleValues(option.element);
|
||||
break;
|
||||
case "object":
|
||||
|
||||
@@ -380,6 +380,7 @@ export namespace Compiler {
|
||||
}
|
||||
// If not a primitive, the possible types are specified in what is effectively a map of options.
|
||||
case "list":
|
||||
case "listOrElement":
|
||||
return ts.parseListTypeOption(option, value, errors);
|
||||
default:
|
||||
return ts.parseCustomTypeOption(option as ts.CommandLineOptionOfCustomType, value, errors);
|
||||
|
||||
@@ -120,7 +120,7 @@ function updateTsconfigFiles(program: Program, changeTracker: textChanges.Change
|
||||
if (foundExactMatch || propertyName !== "include" || !isArrayLiteralExpression(property.initializer)) return;
|
||||
const includes = mapDefined(property.initializer.elements, e => isStringLiteral(e) ? e.text : undefined);
|
||||
if (includes.length === 0) return;
|
||||
const matchers = getFileMatcherPatterns(configDir, /*excludes*/ [], includes, useCaseSensitiveFileNames, currentDirectory);
|
||||
const matchers = getFileMatcherPatterns(configDir, /*excludes*/[], includes, useCaseSensitiveFileNames, currentDirectory);
|
||||
// If there isn't some include for this, add a new one.
|
||||
if (getRegexFromPattern(Debug.checkDefined(matchers.includeFilePattern), useCaseSensitiveFileNames).test(oldFileOrDirPath) &&
|
||||
!getRegexFromPattern(Debug.checkDefined(matchers.includeFilePattern), useCaseSensitiveFileNames).test(newFileOrDirPath)) {
|
||||
@@ -131,6 +131,7 @@ function updateTsconfigFiles(program: Program, changeTracker: textChanges.Change
|
||||
case "compilerOptions":
|
||||
forEachProperty(property.initializer, (property, propertyName) => {
|
||||
const option = getOptionFromName(propertyName);
|
||||
Debug.assert(option?.type !== "listOrElement");
|
||||
if (option && (option.isFilePath || option.type === "list" && option.element.isFilePath)) {
|
||||
updatePaths(property);
|
||||
}
|
||||
|
||||
@@ -186,7 +186,47 @@ function createFileSystem(ignoreCase: boolean, cwd: string, root: string) {
|
||||
"dev/tests/unit/spec.ts": "",
|
||||
"dev/tests/utils.ts": "",
|
||||
"dev/tests/scenarios/first.json": "",
|
||||
"dev/tests/baselines/first/output.ts": ""
|
||||
"dev/tests/baselines/first/output.ts": "",
|
||||
"dev/configs/extendsArrayFirst.json": JSON.stringify({
|
||||
compilerOptions: {
|
||||
allowJs: true,
|
||||
noImplicitAny: true,
|
||||
strictNullChecks: true
|
||||
}
|
||||
}),
|
||||
"dev/configs/extendsArraySecond.json": JSON.stringify({
|
||||
compilerOptions: {
|
||||
module: "amd"
|
||||
},
|
||||
include: ["../supplemental.*"]
|
||||
}),
|
||||
"dev/configs/extendsArrayThird.json": JSON.stringify({
|
||||
compilerOptions: {
|
||||
module: null, // eslint-disable-line no-null/no-null
|
||||
noImplicitAny: false
|
||||
},
|
||||
extends: "./extendsArrayFirst",
|
||||
include: ["../supplemental.*"]
|
||||
}),
|
||||
"dev/configs/extendsArrayFourth.json": JSON.stringify({
|
||||
compilerOptions: {
|
||||
module: "system",
|
||||
strictNullChecks: false
|
||||
},
|
||||
include: null, // eslint-disable-line no-null/no-null
|
||||
files: ["../main.ts"]
|
||||
}),
|
||||
"dev/configs/extendsArrayFifth.json": JSON.stringify({
|
||||
extends: ["./extendsArrayFirst", "./extendsArraySecond", "./extendsArrayThird", "./extendsArrayFourth"],
|
||||
files: [],
|
||||
}),
|
||||
"dev/extendsArrayFails.json": JSON.stringify({
|
||||
extends: ["./missingFile"],
|
||||
compilerOptions: {
|
||||
types: []
|
||||
}
|
||||
}),
|
||||
"dev/extendsArrayFails2.json": JSON.stringify({ extends: [42] }),
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -295,9 +335,9 @@ describe("unittests:: config:: configurationExtension", () => {
|
||||
messageText: `Unknown option 'excludes'. Did you mean 'exclude'?`
|
||||
}]);
|
||||
|
||||
testFailure("can error when 'extends' is not a string", "extends.json", [{
|
||||
testFailure("can error when 'extends' is not a string or Array", "extends.json", [{
|
||||
code: 5024,
|
||||
messageText: `Compiler option 'extends' requires a value of type string.`
|
||||
messageText: `Compiler option 'extends' requires a value of type string or Array.`
|
||||
}]);
|
||||
|
||||
testSuccess("can overwrite compiler options using extended 'null'", "configs/third.json", {
|
||||
@@ -352,5 +392,40 @@ describe("unittests:: config:: configurationExtension", () => {
|
||||
assert.deepEqual(sourceFile.extendedSourceFiles, expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe(testName, () => {
|
||||
it("adds extendedSourceFiles from an array only once", () => {
|
||||
const sourceFile = ts.readJsonConfigFile("configs/extendsArrayFifth.json", (path) => host.readFile(path));
|
||||
const dir = ts.combinePaths(basePath, "configs");
|
||||
const expected = [
|
||||
ts.combinePaths(dir, "extendsArrayFirst.json"),
|
||||
ts.combinePaths(dir, "extendsArraySecond.json"),
|
||||
ts.combinePaths(dir, "extendsArrayThird.json"),
|
||||
ts.combinePaths(dir, "extendsArrayFourth.json"),
|
||||
];
|
||||
ts.parseJsonSourceFileConfigFileContent(sourceFile, host, dir, {}, "extendsArrayFifth.json");
|
||||
assert.deepEqual(sourceFile.extendedSourceFiles, expected);
|
||||
ts.parseJsonSourceFileConfigFileContent(sourceFile, host, dir, {}, "extendsArrayFifth.json");
|
||||
assert.deepEqual(sourceFile.extendedSourceFiles, expected);
|
||||
});
|
||||
|
||||
testSuccess("can overwrite top-level compilerOptions", "configs/extendsArrayFifth.json", {
|
||||
allowJs: true,
|
||||
noImplicitAny: false,
|
||||
strictNullChecks: false,
|
||||
module: ts.ModuleKind.System
|
||||
}, []);
|
||||
|
||||
testFailure("can report missing configurations", "extendsArrayFails.json", [{
|
||||
code: 6053,
|
||||
messageText: `File './missingFile' not found.`
|
||||
}]);
|
||||
|
||||
testFailure("can error when 'extends' is not a string or Array2", "extendsArrayFails2.json", [{
|
||||
code: 5024,
|
||||
messageText: `Compiler option 'extends' requires a value of type string.`
|
||||
}]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -147,6 +147,10 @@ describe("unittests:: config:: showConfig", () => {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "listOrElement": {
|
||||
ts.Debug.fail();
|
||||
break;
|
||||
}
|
||||
case "string": {
|
||||
if (option.isTSConfigOnly) {
|
||||
args = ["-p", "tsconfig.json"];
|
||||
|
||||
@@ -597,7 +597,7 @@ export function someFn() { }`),
|
||||
verifyTscWatch({
|
||||
scenario: "programUpdates",
|
||||
subScenario: "works with extended source files",
|
||||
commandLineArgs: ["-b", "-w", "-v", "project1.tsconfig.json", "project2.tsconfig.json"],
|
||||
commandLineArgs: ["-b", "-w", "-v", "project1.tsconfig.json", "project2.tsconfig.json", "project3.tsconfig.json"],
|
||||
sys: () => {
|
||||
const alphaExtendedConfigFile: File = {
|
||||
path: "/a/b/alpha.tsconfig.json",
|
||||
@@ -633,10 +633,49 @@ export function someFn() { }`),
|
||||
files: [otherFile.path]
|
||||
})
|
||||
};
|
||||
const otherFile2: File = {
|
||||
path: "/a/b/other2.ts",
|
||||
content: "let k = 0;",
|
||||
};
|
||||
const extendsConfigFile1: File = {
|
||||
path: "/a/b/extendsConfig1.tsconfig.json",
|
||||
content: JSON.stringify({
|
||||
compilerOptions: {
|
||||
composite: true,
|
||||
}
|
||||
})
|
||||
};
|
||||
const extendsConfigFile2: File = {
|
||||
path: "/a/b/extendsConfig2.tsconfig.json",
|
||||
content: JSON.stringify({
|
||||
compilerOptions: {
|
||||
strictNullChecks: false,
|
||||
}
|
||||
})
|
||||
};
|
||||
const extendsConfigFile3: File = {
|
||||
path: "/a/b/extendsConfig3.tsconfig.json",
|
||||
content: JSON.stringify({
|
||||
compilerOptions: {
|
||||
noImplicitAny: true,
|
||||
}
|
||||
})
|
||||
};
|
||||
const project3Config: File = {
|
||||
path: "/a/b/project3.tsconfig.json",
|
||||
content: JSON.stringify({
|
||||
extends: ["./extendsConfig1.tsconfig.json", "./extendsConfig2.tsconfig.json", "./extendsConfig3.tsconfig.json"],
|
||||
compilerOptions: {
|
||||
composite: false,
|
||||
},
|
||||
files: [otherFile2.path]
|
||||
})
|
||||
};
|
||||
return createWatchedSystem([
|
||||
libFile,
|
||||
alphaExtendedConfigFile, project1Config, commonFile1, commonFile2,
|
||||
bravoExtendedConfigFile, project2Config, otherFile
|
||||
bravoExtendedConfigFile, project2Config, otherFile, otherFile2,
|
||||
extendsConfigFile1, extendsConfigFile2, extendsConfigFile3, project3Config
|
||||
], { currentDirectory: "/a/b" });
|
||||
},
|
||||
edits: [
|
||||
@@ -689,7 +728,37 @@ export function someFn() { }`),
|
||||
sys.checkTimeoutQueueLength(0);
|
||||
},
|
||||
},
|
||||
]
|
||||
{
|
||||
caption: "Modify extendsConfigFile2",
|
||||
edit: sys => sys.writeFile("/a/b/extendsConfig2.tsconfig.json", JSON.stringify({
|
||||
compilerOptions: { strictNullChecks: true }
|
||||
})),
|
||||
timeouts: sys => { // Build project3
|
||||
sys.checkTimeoutQueueLengthAndRun(1);
|
||||
sys.checkTimeoutQueueLength(0);
|
||||
},
|
||||
},
|
||||
{
|
||||
caption: "Modify project 3",
|
||||
edit: sys => sys.writeFile("/a/b/project3.tsconfig.json", JSON.stringify({
|
||||
extends: ["./extendsConfig1.tsconfig.json", "./extendsConfig2.tsconfig.json"],
|
||||
compilerOptions: { composite: false },
|
||||
files: ["/a/b/other2.ts"]
|
||||
})),
|
||||
timeouts: sys => { // Build project3
|
||||
sys.checkTimeoutQueueLengthAndRun(1);
|
||||
sys.checkTimeoutQueueLength(0);
|
||||
},
|
||||
},
|
||||
{
|
||||
caption: "Delete extendedConfigFile2 and report error",
|
||||
edit: sys => sys.deleteFile("./extendsConfig2.tsconfig.json"),
|
||||
timeouts: sys => { // Build project3
|
||||
sys.checkTimeoutQueueLengthAndRun(1);
|
||||
sys.checkTimeoutQueueLength(0);
|
||||
},
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
verifyTscWatch({
|
||||
|
||||
+1
-1
@@ -9170,7 +9170,7 @@ declare namespace ts {
|
||||
/**
|
||||
* Note that the case of the config path has not yet been normalized, as no files have been imported into the project yet
|
||||
*/
|
||||
extendedConfigPath?: string;
|
||||
extendedConfigPath?: string | string[];
|
||||
}
|
||||
interface ExtendedConfigCacheEntry {
|
||||
extendedResult: TsConfigSourceFile;
|
||||
|
||||
+1
-1
@@ -5236,7 +5236,7 @@ declare namespace ts {
|
||||
/**
|
||||
* Note that the case of the config path has not yet been normalized, as no files have been imported into the project yet
|
||||
*/
|
||||
extendedConfigPath?: string;
|
||||
extendedConfigPath?: string | string[];
|
||||
}
|
||||
interface ExtendedConfigCacheEntry {
|
||||
extendedResult: TsConfigSourceFile;
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/index.ts(1,12): error TS7006: Parameter 'x' implicitly has an 'any' type.
|
||||
/index.ts(3,1): error TS2454: Variable 'y' is used before being assigned.
|
||||
|
||||
|
||||
==== /tsconfig.json (0 errors) ====
|
||||
{
|
||||
"extends": ["./tsconfig1.json", "./tsconfig2.json"]
|
||||
}
|
||||
|
||||
==== /tsconfig1.json (0 errors) ====
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strictNullChecks": true
|
||||
}
|
||||
}
|
||||
|
||||
==== /tsconfig2.json (0 errors) ====
|
||||
{
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": true
|
||||
}
|
||||
}
|
||||
|
||||
==== /index.ts (2 errors) ====
|
||||
function f(x) { } // noImplicitAny error
|
||||
~
|
||||
!!! error TS7006: Parameter 'x' implicitly has an 'any' type.
|
||||
let y: string;
|
||||
y.toLowerCase(); // strictNullChecks error
|
||||
~
|
||||
!!! error TS2454: Variable 'y' is used before being assigned.
|
||||
@@ -0,0 +1,25 @@
|
||||
//// [tests/cases/compiler/configFileExtendsAsList.ts] ////
|
||||
|
||||
//// [tsconfig1.json]
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strictNullChecks": true
|
||||
}
|
||||
}
|
||||
|
||||
//// [tsconfig2.json]
|
||||
{
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": true
|
||||
}
|
||||
}
|
||||
|
||||
//// [index.ts]
|
||||
function f(x) { } // noImplicitAny error
|
||||
let y: string;
|
||||
y.toLowerCase(); // strictNullChecks error
|
||||
|
||||
//// [index.js]
|
||||
function f(x) { } // noImplicitAny error
|
||||
var y;
|
||||
y.toLowerCase(); // strictNullChecks error
|
||||
@@ -0,0 +1,13 @@
|
||||
=== /index.ts ===
|
||||
function f(x) { } // noImplicitAny error
|
||||
>f : Symbol(f, Decl(index.ts, 0, 0))
|
||||
>x : Symbol(x, Decl(index.ts, 0, 11))
|
||||
|
||||
let y: string;
|
||||
>y : Symbol(y, Decl(index.ts, 1, 3))
|
||||
|
||||
y.toLowerCase(); // strictNullChecks error
|
||||
>y.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
|
||||
>y : Symbol(y, Decl(index.ts, 1, 3))
|
||||
>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
=== /index.ts ===
|
||||
function f(x) { } // noImplicitAny error
|
||||
>f : (x: any) => void
|
||||
>x : any
|
||||
|
||||
let y: string;
|
||||
>y : string
|
||||
|
||||
y.toLowerCase(); // strictNullChecks error
|
||||
>y.toLowerCase() : string
|
||||
>y.toLowerCase : () => string
|
||||
>y : string
|
||||
>toLowerCase : () => string
|
||||
|
||||
+336
-31
@@ -33,25 +33,45 @@ let y = 1
|
||||
//// [/a/b/other.ts]
|
||||
let z = 0;
|
||||
|
||||
//// [/a/b/other2.ts]
|
||||
let k = 0;
|
||||
|
||||
/a/lib/tsc.js -b -w -v project1.tsconfig.json project2.tsconfig.json
|
||||
//// [/a/b/extendsConfig1.tsconfig.json]
|
||||
{"compilerOptions":{"composite":true}}
|
||||
|
||||
//// [/a/b/extendsConfig2.tsconfig.json]
|
||||
{"compilerOptions":{"strictNullChecks":false}}
|
||||
|
||||
//// [/a/b/extendsConfig3.tsconfig.json]
|
||||
{"compilerOptions":{"noImplicitAny":true}}
|
||||
|
||||
//// [/a/b/project3.tsconfig.json]
|
||||
{"extends":["./extendsConfig1.tsconfig.json","./extendsConfig2.tsconfig.json","./extendsConfig3.tsconfig.json"],"compilerOptions":{"composite":false},"files":["/a/b/other2.ts"]}
|
||||
|
||||
|
||||
/a/lib/tsc.js -b -w -v project1.tsconfig.json project2.tsconfig.json project3.tsconfig.json
|
||||
Output::
|
||||
>> Screen clear
|
||||
[[90m12:00:25 AM[0m] Starting compilation in watch mode...
|
||||
[[90m12:00:35 AM[0m] Starting compilation in watch mode...
|
||||
|
||||
[[90m12:00:26 AM[0m] Projects in this build:
|
||||
[[90m12:00:36 AM[0m] Projects in this build:
|
||||
* project1.tsconfig.json
|
||||
* project2.tsconfig.json
|
||||
* project2.tsconfig.json
|
||||
* project3.tsconfig.json
|
||||
|
||||
[[90m12:00:27 AM[0m] Project 'project1.tsconfig.json' is out of date because output file 'project1.tsconfig.tsbuildinfo' does not exist
|
||||
[[90m12:00:37 AM[0m] Project 'project1.tsconfig.json' is out of date because output file 'project1.tsconfig.tsbuildinfo' does not exist
|
||||
|
||||
[[90m12:00:28 AM[0m] Building project '/a/b/project1.tsconfig.json'...
|
||||
[[90m12:00:38 AM[0m] Building project '/a/b/project1.tsconfig.json'...
|
||||
|
||||
[[90m12:00:42 AM[0m] Project 'project2.tsconfig.json' is out of date because output file 'project2.tsconfig.tsbuildinfo' does not exist
|
||||
[[90m12:00:52 AM[0m] Project 'project2.tsconfig.json' is out of date because output file 'project2.tsconfig.tsbuildinfo' does not exist
|
||||
|
||||
[[90m12:00:43 AM[0m] Building project '/a/b/project2.tsconfig.json'...
|
||||
[[90m12:00:53 AM[0m] Building project '/a/b/project2.tsconfig.json'...
|
||||
|
||||
[[90m12:00:53 AM[0m] Found 0 errors. Watching for file changes.
|
||||
[[90m12:01:03 AM[0m] Project 'project3.tsconfig.json' is out of date because output file 'other2.js' does not exist
|
||||
|
||||
[[90m12:01:04 AM[0m] Building project '/a/b/project3.tsconfig.json'...
|
||||
|
||||
[[90m12:01:08 AM[0m] Found 0 errors. Watching for file changes.
|
||||
|
||||
|
||||
|
||||
@@ -88,6 +108,21 @@ Shape signatures in builder refreshed for::
|
||||
/a/lib/lib.d.ts (used version)
|
||||
/a/b/other.ts (computed .d.ts during emit)
|
||||
|
||||
Program root files: ["/a/b/other2.ts"]
|
||||
Program options: {"composite":false,"strictNullChecks":false,"noImplicitAny":true,"watch":true,"configFilePath":"/a/b/project3.tsconfig.json"}
|
||||
Program structureReused: Not
|
||||
Program files::
|
||||
/a/lib/lib.d.ts
|
||||
/a/b/other2.ts
|
||||
|
||||
Semantic diagnostics in builder refreshed for::
|
||||
/a/lib/lib.d.ts
|
||||
/a/b/other2.ts
|
||||
|
||||
Shape signatures in builder refreshed for::
|
||||
/a/lib/lib.d.ts (used version)
|
||||
/a/b/other2.ts (used version)
|
||||
|
||||
PolledWatches::
|
||||
|
||||
FsWatches::
|
||||
@@ -105,6 +140,16 @@ FsWatches::
|
||||
{}
|
||||
/a/b/other.ts:
|
||||
{}
|
||||
/a/b/project3.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig1.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig2.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig3.tsconfig.json:
|
||||
{}
|
||||
/a/b/other2.ts:
|
||||
{}
|
||||
|
||||
FsWatchesRecursive::
|
||||
|
||||
@@ -238,6 +283,10 @@ declare let z: number;
|
||||
"size": 755
|
||||
}
|
||||
|
||||
//// [/a/b/other2.js]
|
||||
var k = 0;
|
||||
|
||||
|
||||
|
||||
Change:: Modify alpha config
|
||||
|
||||
@@ -248,11 +297,11 @@ Input::
|
||||
|
||||
Output::
|
||||
>> Screen clear
|
||||
[[90m12:00:57 AM[0m] File change detected. Starting incremental compilation...
|
||||
[[90m12:01:12 AM[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[[90m12:00:58 AM[0m] Project 'project1.tsconfig.json' is out of date because output 'project1.tsconfig.tsbuildinfo' is older than input 'alpha.tsconfig.json'
|
||||
[[90m12:01:13 AM[0m] Project 'project1.tsconfig.json' is out of date because output 'project1.tsconfig.tsbuildinfo' is older than input 'alpha.tsconfig.json'
|
||||
|
||||
[[90m12:00:59 AM[0m] Building project '/a/b/project1.tsconfig.json'...
|
||||
[[90m12:01:14 AM[0m] Building project '/a/b/project1.tsconfig.json'...
|
||||
|
||||
|
||||
|
||||
@@ -288,6 +337,16 @@ FsWatches::
|
||||
{}
|
||||
/a/b/other.ts:
|
||||
{}
|
||||
/a/b/project3.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig1.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig2.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig3.tsconfig.json:
|
||||
{}
|
||||
/a/b/other2.ts:
|
||||
{}
|
||||
|
||||
FsWatchesRecursive::
|
||||
|
||||
@@ -368,11 +427,11 @@ Change:: Build project 2
|
||||
Input::
|
||||
|
||||
Output::
|
||||
[[90m12:01:13 AM[0m] Project 'project2.tsconfig.json' is out of date because output 'project2.tsconfig.tsbuildinfo' is older than input 'alpha.tsconfig.json'
|
||||
[[90m12:01:28 AM[0m] Project 'project2.tsconfig.json' is out of date because output 'project2.tsconfig.tsbuildinfo' is older than input 'alpha.tsconfig.json'
|
||||
|
||||
[[90m12:01:14 AM[0m] Building project '/a/b/project2.tsconfig.json'...
|
||||
[[90m12:01:29 AM[0m] Building project '/a/b/project2.tsconfig.json'...
|
||||
|
||||
[[90m12:01:25 AM[0m] Found 0 errors. Watching for file changes.
|
||||
[[90m12:01:40 AM[0m] Found 0 errors. Watching for file changes.
|
||||
|
||||
|
||||
|
||||
@@ -406,6 +465,16 @@ FsWatches::
|
||||
{}
|
||||
/a/b/other.ts:
|
||||
{}
|
||||
/a/b/project3.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig1.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig2.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig3.tsconfig.json:
|
||||
{}
|
||||
/a/b/other2.ts:
|
||||
{}
|
||||
|
||||
FsWatchesRecursive::
|
||||
|
||||
@@ -473,13 +542,13 @@ Input::
|
||||
|
||||
Output::
|
||||
>> Screen clear
|
||||
[[90m12:01:29 AM[0m] File change detected. Starting incremental compilation...
|
||||
[[90m12:01:44 AM[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[[90m12:01:30 AM[0m] Project 'project2.tsconfig.json' is out of date because output 'project2.tsconfig.tsbuildinfo' is older than input 'bravo.tsconfig.json'
|
||||
[[90m12:01:45 AM[0m] Project 'project2.tsconfig.json' is out of date because output 'project2.tsconfig.tsbuildinfo' is older than input 'bravo.tsconfig.json'
|
||||
|
||||
[[90m12:01:31 AM[0m] Building project '/a/b/project2.tsconfig.json'...
|
||||
[[90m12:01:46 AM[0m] Building project '/a/b/project2.tsconfig.json'...
|
||||
|
||||
[[90m12:01:42 AM[0m] Found 0 errors. Watching for file changes.
|
||||
[[90m12:01:57 AM[0m] Found 0 errors. Watching for file changes.
|
||||
|
||||
|
||||
|
||||
@@ -513,6 +582,16 @@ FsWatches::
|
||||
{}
|
||||
/a/b/other.ts:
|
||||
{}
|
||||
/a/b/project3.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig1.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig2.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig3.tsconfig.json:
|
||||
{}
|
||||
/a/b/other2.ts:
|
||||
{}
|
||||
|
||||
FsWatchesRecursive::
|
||||
|
||||
@@ -579,17 +658,17 @@ Input::
|
||||
|
||||
Output::
|
||||
>> Screen clear
|
||||
[[90m12:01:46 AM[0m] File change detected. Starting incremental compilation...
|
||||
[[90m12:02:01 AM[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[[90m12:01:47 AM[0m] Project 'project2.tsconfig.json' is out of date because output 'commonFile1.js' is older than input 'project2.tsconfig.json'
|
||||
[[90m12:02:02 AM[0m] Project 'project2.tsconfig.json' is out of date because output 'other2.js' is older than input 'project2.tsconfig.json'
|
||||
|
||||
[[90m12:01:48 AM[0m] Building project '/a/b/project2.tsconfig.json'...
|
||||
[[90m12:02:03 AM[0m] Building project '/a/b/project2.tsconfig.json'...
|
||||
|
||||
[[90m12:01:59 AM[0m] Found 0 errors. Watching for file changes.
|
||||
[[90m12:02:17 AM[0m] Found 0 errors. Watching for file changes.
|
||||
|
||||
|
||||
|
||||
Program root files: ["/a/b/commonFile1.ts","/a/b/commonFile2.ts","/a/b/other.ts"]
|
||||
Program root files: ["/a/b/commonFile1.ts","/a/b/commonFile2.ts","/a/b/other.ts","/a/b/other2.ts"]
|
||||
Program options: {"strict":true,"watch":true,"configFilePath":"/a/b/project2.tsconfig.json"}
|
||||
Program structureReused: Not
|
||||
Program files::
|
||||
@@ -597,17 +676,20 @@ Program files::
|
||||
/a/b/commonFile1.ts
|
||||
/a/b/commonFile2.ts
|
||||
/a/b/other.ts
|
||||
/a/b/other2.ts
|
||||
|
||||
Semantic diagnostics in builder refreshed for::
|
||||
/a/lib/lib.d.ts
|
||||
/a/b/commonFile1.ts
|
||||
/a/b/commonFile2.ts
|
||||
/a/b/other.ts
|
||||
/a/b/other2.ts
|
||||
|
||||
Shape signatures in builder refreshed for::
|
||||
/a/b/commonfile1.ts (computed .d.ts)
|
||||
/a/b/commonfile2.ts (computed .d.ts)
|
||||
/a/b/other.ts (computed .d.ts)
|
||||
/a/b/other2.ts (computed .d.ts)
|
||||
|
||||
PolledWatches::
|
||||
|
||||
@@ -624,6 +706,16 @@ FsWatches::
|
||||
{}
|
||||
/a/b/other.ts:
|
||||
{}
|
||||
/a/b/project3.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig1.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig2.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig3.tsconfig.json:
|
||||
{}
|
||||
/a/b/other2.ts:
|
||||
{}
|
||||
|
||||
FsWatchesRecursive::
|
||||
/a/b:
|
||||
@@ -638,6 +730,11 @@ exitCode:: ExitStatus.undefined
|
||||
var z = 0;
|
||||
|
||||
|
||||
//// [/a/b/other2.js]
|
||||
"use strict";
|
||||
var k = 0;
|
||||
|
||||
|
||||
|
||||
Change:: update aplha config
|
||||
|
||||
@@ -648,11 +745,11 @@ Input::
|
||||
|
||||
Output::
|
||||
>> Screen clear
|
||||
[[90m12:02:04 AM[0m] File change detected. Starting incremental compilation...
|
||||
[[90m12:02:22 AM[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[[90m12:02:05 AM[0m] Project 'project1.tsconfig.json' is out of date because output 'project1.tsconfig.tsbuildinfo' is older than input 'alpha.tsconfig.json'
|
||||
[[90m12:02:23 AM[0m] Project 'project1.tsconfig.json' is out of date because output 'project1.tsconfig.tsbuildinfo' is older than input 'alpha.tsconfig.json'
|
||||
|
||||
[[90m12:02:06 AM[0m] Building project '/a/b/project1.tsconfig.json'...
|
||||
[[90m12:02:24 AM[0m] Building project '/a/b/project1.tsconfig.json'...
|
||||
|
||||
|
||||
|
||||
@@ -686,6 +783,16 @@ FsWatches::
|
||||
{}
|
||||
/a/b/other.ts:
|
||||
{}
|
||||
/a/b/project3.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig1.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig2.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig3.tsconfig.json:
|
||||
{}
|
||||
/a/b/other2.ts:
|
||||
{}
|
||||
|
||||
FsWatchesRecursive::
|
||||
/a/b:
|
||||
@@ -765,15 +872,15 @@ Change:: Build project 2
|
||||
Input::
|
||||
|
||||
Output::
|
||||
[[90m12:02:20 AM[0m] Project 'project2.tsconfig.json' is out of date because output 'commonFile1.js' is older than input 'alpha.tsconfig.json'
|
||||
[[90m12:02:38 AM[0m] Project 'project2.tsconfig.json' is out of date because output 'commonFile1.js' is older than input 'alpha.tsconfig.json'
|
||||
|
||||
[[90m12:02:21 AM[0m] Building project '/a/b/project2.tsconfig.json'...
|
||||
[[90m12:02:39 AM[0m] Building project '/a/b/project2.tsconfig.json'...
|
||||
|
||||
[[90m12:02:32 AM[0m] Found 0 errors. Watching for file changes.
|
||||
[[90m12:02:53 AM[0m] Found 0 errors. Watching for file changes.
|
||||
|
||||
|
||||
|
||||
Program root files: ["/a/b/commonFile1.ts","/a/b/commonFile2.ts","/a/b/other.ts"]
|
||||
Program root files: ["/a/b/commonFile1.ts","/a/b/commonFile2.ts","/a/b/other.ts","/a/b/other2.ts"]
|
||||
Program options: {"watch":true,"configFilePath":"/a/b/project2.tsconfig.json"}
|
||||
Program structureReused: Not
|
||||
Program files::
|
||||
@@ -781,12 +888,14 @@ Program files::
|
||||
/a/b/commonFile1.ts
|
||||
/a/b/commonFile2.ts
|
||||
/a/b/other.ts
|
||||
/a/b/other2.ts
|
||||
|
||||
Semantic diagnostics in builder refreshed for::
|
||||
/a/lib/lib.d.ts
|
||||
/a/b/commonFile1.ts
|
||||
/a/b/commonFile2.ts
|
||||
/a/b/other.ts
|
||||
/a/b/other2.ts
|
||||
|
||||
No shapes updated in the builder::
|
||||
|
||||
@@ -805,6 +914,16 @@ FsWatches::
|
||||
{}
|
||||
/a/b/other.ts:
|
||||
{}
|
||||
/a/b/project3.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig1.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig2.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig3.tsconfig.json:
|
||||
{}
|
||||
/a/b/other2.ts:
|
||||
{}
|
||||
|
||||
FsWatchesRecursive::
|
||||
/a/b:
|
||||
@@ -818,3 +937,189 @@ exitCode:: ExitStatus.undefined
|
||||
var z = 0;
|
||||
|
||||
|
||||
//// [/a/b/other2.js]
|
||||
var k = 0;
|
||||
|
||||
|
||||
|
||||
Change:: Modify extendsConfigFile2
|
||||
|
||||
Input::
|
||||
//// [/a/b/extendsConfig2.tsconfig.json]
|
||||
{"compilerOptions":{"strictNullChecks":true}}
|
||||
|
||||
|
||||
Output::
|
||||
>> Screen clear
|
||||
[[90m12:02:57 AM[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[[90m12:02:58 AM[0m] Project 'project3.tsconfig.json' is out of date because output 'other2.js' is older than input 'extendsConfig2.tsconfig.json'
|
||||
|
||||
[[90m12:02:59 AM[0m] Building project '/a/b/project3.tsconfig.json'...
|
||||
|
||||
[[90m12:03:00 AM[0m] Updating unchanged output timestamps of project '/a/b/project3.tsconfig.json'...
|
||||
|
||||
[[90m12:03:02 AM[0m] Found 0 errors. Watching for file changes.
|
||||
|
||||
|
||||
|
||||
Program root files: ["/a/b/other2.ts"]
|
||||
Program options: {"composite":false,"strictNullChecks":true,"noImplicitAny":true,"watch":true,"configFilePath":"/a/b/project3.tsconfig.json"}
|
||||
Program structureReused: Not
|
||||
Program files::
|
||||
/a/lib/lib.d.ts
|
||||
/a/b/other2.ts
|
||||
|
||||
Semantic diagnostics in builder refreshed for::
|
||||
/a/lib/lib.d.ts
|
||||
/a/b/other2.ts
|
||||
|
||||
No shapes updated in the builder::
|
||||
|
||||
PolledWatches::
|
||||
|
||||
FsWatches::
|
||||
/a/b/project1.tsconfig.json:
|
||||
{}
|
||||
/a/b/alpha.tsconfig.json:
|
||||
{}
|
||||
/a/b/commonfile1.ts:
|
||||
{}
|
||||
/a/b/commonfile2.ts:
|
||||
{}
|
||||
/a/b/project2.tsconfig.json:
|
||||
{}
|
||||
/a/b/other.ts:
|
||||
{}
|
||||
/a/b/project3.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig1.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig2.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig3.tsconfig.json:
|
||||
{}
|
||||
/a/b/other2.ts:
|
||||
{}
|
||||
|
||||
FsWatchesRecursive::
|
||||
/a/b:
|
||||
{}
|
||||
|
||||
exitCode:: ExitStatus.undefined
|
||||
|
||||
//// [/a/b/other2.js] file changed its modified time
|
||||
|
||||
Change:: Modify project 3
|
||||
|
||||
Input::
|
||||
//// [/a/b/project3.tsconfig.json]
|
||||
{"extends":["./extendsConfig1.tsconfig.json","./extendsConfig2.tsconfig.json"],"compilerOptions":{"composite":false},"files":["/a/b/other2.ts"]}
|
||||
|
||||
|
||||
Output::
|
||||
>> Screen clear
|
||||
[[90m12:03:06 AM[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[[90m12:03:07 AM[0m] Project 'project3.tsconfig.json' is out of date because output 'other2.js' is older than input 'project3.tsconfig.json'
|
||||
|
||||
[[90m12:03:08 AM[0m] Building project '/a/b/project3.tsconfig.json'...
|
||||
|
||||
[[90m12:03:09 AM[0m] Updating unchanged output timestamps of project '/a/b/project3.tsconfig.json'...
|
||||
|
||||
[[90m12:03:11 AM[0m] Found 0 errors. Watching for file changes.
|
||||
|
||||
|
||||
|
||||
Program root files: ["/a/b/other2.ts"]
|
||||
Program options: {"composite":false,"strictNullChecks":true,"watch":true,"configFilePath":"/a/b/project3.tsconfig.json"}
|
||||
Program structureReused: Not
|
||||
Program files::
|
||||
/a/lib/lib.d.ts
|
||||
/a/b/other2.ts
|
||||
|
||||
Semantic diagnostics in builder refreshed for::
|
||||
/a/lib/lib.d.ts
|
||||
/a/b/other2.ts
|
||||
|
||||
No shapes updated in the builder::
|
||||
|
||||
PolledWatches::
|
||||
|
||||
FsWatches::
|
||||
/a/b/project1.tsconfig.json:
|
||||
{}
|
||||
/a/b/alpha.tsconfig.json:
|
||||
{}
|
||||
/a/b/commonfile1.ts:
|
||||
{}
|
||||
/a/b/commonfile2.ts:
|
||||
{}
|
||||
/a/b/project2.tsconfig.json:
|
||||
{}
|
||||
/a/b/other.ts:
|
||||
{}
|
||||
/a/b/project3.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig1.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig2.tsconfig.json:
|
||||
{}
|
||||
/a/b/other2.ts:
|
||||
{}
|
||||
|
||||
FsWatchesRecursive::
|
||||
/a/b:
|
||||
{}
|
||||
|
||||
exitCode:: ExitStatus.undefined
|
||||
|
||||
//// [/a/b/other2.js] file changed its modified time
|
||||
|
||||
Change:: Delete extendedConfigFile2 and report error
|
||||
|
||||
Input::
|
||||
//// [/a/b/extendsConfig2.tsconfig.json] deleted
|
||||
|
||||
Output::
|
||||
>> Screen clear
|
||||
[[90m12:03:13 AM[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[[90m12:03:14 AM[0m] Project 'project3.tsconfig.json' is up to date because newest input 'other2.ts' is older than output 'other2.js'
|
||||
|
||||
[91merror[0m[90m TS5083: [0mCannot read file '/a/b/extendsConfig2.tsconfig.json'.
|
||||
|
||||
[[90m12:03:15 AM[0m] Found 1 error. Watching for file changes.
|
||||
|
||||
|
||||
|
||||
PolledWatches::
|
||||
|
||||
FsWatches::
|
||||
/a/b/project1.tsconfig.json:
|
||||
{}
|
||||
/a/b/alpha.tsconfig.json:
|
||||
{}
|
||||
/a/b/commonfile1.ts:
|
||||
{}
|
||||
/a/b/commonfile2.ts:
|
||||
{}
|
||||
/a/b/project2.tsconfig.json:
|
||||
{}
|
||||
/a/b/other.ts:
|
||||
{}
|
||||
/a/b/project3.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig1.tsconfig.json:
|
||||
{}
|
||||
/a/b/extendsconfig2.tsconfig.json:
|
||||
{}
|
||||
/a/b/other2.ts:
|
||||
{}
|
||||
|
||||
FsWatchesRecursive::
|
||||
/a/b:
|
||||
{}
|
||||
|
||||
exitCode:: ExitStatus.undefined
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// @Filename: /tsconfig1.json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strictNullChecks": true
|
||||
}
|
||||
}
|
||||
|
||||
// @Filename: /tsconfig2.json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": true
|
||||
}
|
||||
}
|
||||
|
||||
// @Filename: /tsconfig.json
|
||||
{
|
||||
"extends": ["./tsconfig1.json", "./tsconfig2.json"]
|
||||
}
|
||||
|
||||
// @Filename: /index.ts
|
||||
function f(x) { } // noImplicitAny error
|
||||
let y: string;
|
||||
y.toLowerCase(); // strictNullChecks error
|
||||
Reference in New Issue
Block a user