mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-15 04:43:37 -05:00
Allow undefined/null to override all parameters (#18058)
This commit is contained in:
@@ -1057,7 +1057,7 @@ namespace ts {
|
||||
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, element.name, extraKeyDiagnosticMessage, keyText));
|
||||
}
|
||||
const value = convertPropertyValueToJson(element.initializer, option);
|
||||
if (typeof keyText !== "undefined" && typeof value !== "undefined") {
|
||||
if (typeof keyText !== "undefined") {
|
||||
result[keyText] = value;
|
||||
// Notify key value set, if user asked for it
|
||||
if (jsonConversionNotifier &&
|
||||
@@ -1104,7 +1104,7 @@ namespace ts {
|
||||
return false;
|
||||
|
||||
case SyntaxKind.NullKeyword:
|
||||
reportInvalidOptionValue(!!option);
|
||||
reportInvalidOptionValue(option && option.name === "extends"); // "extends" is the only option we don't allow null/undefined for
|
||||
return null; // tslint:disable-line:no-null-keyword
|
||||
|
||||
case SyntaxKind.StringLiteral:
|
||||
@@ -1189,6 +1189,7 @@ namespace ts {
|
||||
|
||||
function isCompilerOptionsValue(option: CommandLineOption, value: any): value is CompilerOptionsValue {
|
||||
if (option) {
|
||||
if (isNullOrUndefined(value)) return true; // All options are undefinable/nullable
|
||||
if (option.type === "list") {
|
||||
return isArray(value);
|
||||
}
|
||||
@@ -1379,6 +1380,11 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function isNullOrUndefined(x: any): x is null | undefined {
|
||||
// tslint:disable-next-line:no-null-keyword
|
||||
return x === undefined || x === null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the contents of a config file from json or json source file (tsconfig.json).
|
||||
* @param json The contents of the config file to parse
|
||||
@@ -1419,7 +1425,7 @@ namespace ts {
|
||||
|
||||
function getFileNames(): ExpandResult {
|
||||
let fileNames: ReadonlyArray<string>;
|
||||
if (hasProperty(raw, "files")) {
|
||||
if (hasProperty(raw, "files") && !isNullOrUndefined(raw["files"])) {
|
||||
if (isArray(raw["files"])) {
|
||||
fileNames = <ReadonlyArray<string>>raw["files"];
|
||||
if (fileNames.length === 0) {
|
||||
@@ -1432,7 +1438,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
let includeSpecs: ReadonlyArray<string>;
|
||||
if (hasProperty(raw, "include")) {
|
||||
if (hasProperty(raw, "include") && !isNullOrUndefined(raw["include"])) {
|
||||
if (isArray(raw["include"])) {
|
||||
includeSpecs = <ReadonlyArray<string>>raw["include"];
|
||||
}
|
||||
@@ -1442,7 +1448,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
let excludeSpecs: ReadonlyArray<string>;
|
||||
if (hasProperty(raw, "exclude")) {
|
||||
if (hasProperty(raw, "exclude") && !isNullOrUndefined(raw["exclude"])) {
|
||||
if (isArray(raw["exclude"])) {
|
||||
excludeSpecs = <ReadonlyArray<string>>raw["exclude"];
|
||||
}
|
||||
@@ -1461,7 +1467,7 @@ namespace ts {
|
||||
includeSpecs = ["**/*"];
|
||||
}
|
||||
|
||||
const result = matchFileNames(fileNames, includeSpecs, excludeSpecs, basePath, options, host, errors, extraFileExtensions, sourceFile);
|
||||
const result = matchFileNames(fileNames, includeSpecs, excludeSpecs, configFileName ? getDirectoryPath(toPath(configFileName, basePath, createGetCanonicalFileName(host.useCaseSensitiveFileNames))) : basePath, options, host, errors, extraFileExtensions, sourceFile);
|
||||
|
||||
if (result.fileNames.length === 0 && !hasProperty(raw, "files") && resolutionStack.length === 0) {
|
||||
errors.push(
|
||||
@@ -1552,7 +1558,7 @@ namespace ts {
|
||||
host: ParseConfigHost,
|
||||
basePath: string,
|
||||
getCanonicalFileName: (fileName: string) => string,
|
||||
configFileName: string,
|
||||
configFileName: string | undefined,
|
||||
errors: Push<Diagnostic>
|
||||
): ParsedTsconfig {
|
||||
if (hasProperty(json, "excludes")) {
|
||||
@@ -1571,7 +1577,8 @@ namespace ts {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "extends", "string"));
|
||||
}
|
||||
else {
|
||||
extendedConfigPath = getExtendsConfigPath(json.extends, host, basePath, getCanonicalFileName, errors, createCompilerDiagnostic);
|
||||
const newBase = configFileName ? getDirectoryPath(toPath(configFileName, basePath, getCanonicalFileName)) : basePath;
|
||||
extendedConfigPath = getExtendsConfigPath(json.extends, host, newBase, getCanonicalFileName, errors, createCompilerDiagnostic);
|
||||
}
|
||||
}
|
||||
return { raw: json, options, typeAcquisition, extendedConfigPath };
|
||||
@@ -1582,7 +1589,7 @@ namespace ts {
|
||||
host: ParseConfigHost,
|
||||
basePath: string,
|
||||
getCanonicalFileName: (fileName: string) => string,
|
||||
configFileName: string,
|
||||
configFileName: string | undefined,
|
||||
errors: Push<Diagnostic>
|
||||
): ParsedTsconfig {
|
||||
const options = getDefaultCompilerOptions(configFileName);
|
||||
@@ -1603,10 +1610,11 @@ namespace ts {
|
||||
onSetValidOptionKeyValueInRoot(key: string, _keyNode: PropertyName, value: CompilerOptionsValue, valueNode: Expression) {
|
||||
switch (key) {
|
||||
case "extends":
|
||||
const newBase = configFileName ? getDirectoryPath(toPath(configFileName, basePath, getCanonicalFileName)) : basePath;
|
||||
extendedConfigPath = getExtendsConfigPath(
|
||||
<string>value,
|
||||
host,
|
||||
basePath,
|
||||
newBase,
|
||||
getCanonicalFileName,
|
||||
errors,
|
||||
(message, arg0) =>
|
||||
@@ -1803,6 +1811,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function normalizeOptionValue(option: CommandLineOption, basePath: string, value: any): CompilerOptionsValue {
|
||||
if (isNullOrUndefined(value)) return undefined;
|
||||
if (option.type === "list") {
|
||||
const listOption = <CommandLineOptionOfListType>option;
|
||||
if (listOption.element.isFilePath || typeof listOption.element.type !== "string") {
|
||||
@@ -1827,6 +1836,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function convertJsonOptionOfCustomType(opt: CommandLineOptionOfCustomType, value: string, errors: Push<Diagnostic>) {
|
||||
if (isNullOrUndefined(value)) return undefined;
|
||||
const key = value.toLowerCase();
|
||||
const val = opt.type.get(key);
|
||||
if (val !== undefined) {
|
||||
@@ -1977,7 +1987,7 @@ namespace ts {
|
||||
// remove a literal file.
|
||||
if (fileNames) {
|
||||
for (const fileName of fileNames) {
|
||||
const file = combinePaths(basePath, fileName);
|
||||
const file = getNormalizedAbsolutePath(fileName, basePath);
|
||||
literalFileMap.set(keyMapper(file), file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3583,7 +3583,7 @@ namespace ts {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike<string[]> | PluginImport[];
|
||||
export type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike<string[]> | PluginImport[] | null | undefined;
|
||||
|
||||
export interface CompilerOptions {
|
||||
/*@internal*/ all?: boolean;
|
||||
|
||||
@@ -78,6 +78,23 @@ namespace ts {
|
||||
},
|
||||
include: ["../supplemental.*"]
|
||||
},
|
||||
"/dev/configs/third.json": {
|
||||
extends: "./second",
|
||||
compilerOptions: {
|
||||
// tslint:disable-next-line:no-null-keyword
|
||||
module: null
|
||||
},
|
||||
include: ["../supplemental.*"]
|
||||
},
|
||||
"/dev/configs/fourth.json": {
|
||||
extends: "./third",
|
||||
compilerOptions: {
|
||||
module: "system"
|
||||
},
|
||||
// tslint:disable-next-line:no-null-keyword
|
||||
include: null,
|
||||
files: ["../main.ts"]
|
||||
},
|
||||
"/dev/extends.json": { extends: 42 },
|
||||
"/dev/extends2.json": { extends: "configs/base" },
|
||||
"/dev/main.ts": "",
|
||||
@@ -106,7 +123,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
describe("Configuration Extension", () => {
|
||||
describe("configurationExtension", () => {
|
||||
forEach<[string, string, Utils.MockParseConfigHost], void>([
|
||||
["under a case insensitive host", caseInsensitiveBasePath, caseInsensitiveHost],
|
||||
["under a case sensitive host", caseSensitiveBasePath, caseSensitiveHost]
|
||||
@@ -206,6 +223,24 @@ namespace ts {
|
||||
category: DiagnosticCategory.Error,
|
||||
messageText: `A path in an 'extends' option must be relative or rooted, but 'configs/base' is not.`
|
||||
}]);
|
||||
|
||||
testSuccess("can overwrite compiler options using extended 'null'", "configs/third.json", {
|
||||
allowJs: true,
|
||||
noImplicitAny: true,
|
||||
strictNullChecks: true,
|
||||
module: undefined // Technically, this is distinct from the key never being set; but within the compiler we don't make the distinction
|
||||
}, [
|
||||
combinePaths(basePath, "supplemental.ts")
|
||||
]);
|
||||
|
||||
testSuccess("can overwrite top-level options using extended 'null'", "configs/fourth.json", {
|
||||
allowJs: true,
|
||||
noImplicitAny: true,
|
||||
strictNullChecks: true,
|
||||
module: ModuleKind.System
|
||||
}, [
|
||||
combinePaths(basePath, "main.ts")
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user