Rewrite nested option parsing

This commit is contained in:
Andrew Branch
2023-11-22 16:18:30 -08:00
parent dc42c72b95
commit 59f1aa775c
22 changed files with 217 additions and 208 deletions
+101 -74
View File
@@ -53,6 +53,7 @@ import {
getFileMatcherPatterns,
getLocaleSpecificMessage,
getNormalizedAbsolutePath,
getOwnKeys,
getRegexFromPattern,
getRegularExpressionForWildcard,
getRegularExpressionsForWildcards,
@@ -85,7 +86,9 @@ import {
ModuleFormatDetectionKind,
ModuleFormatInteropKind,
ModuleKind,
ModuleOptions,
ModuleResolutionKind,
NestedCompilerOption,
NewLineKind,
Node,
NodeArray,
@@ -566,6 +569,7 @@ const moduleSubOptionDeclarations: readonly CommandLineOption[] = [
category: Diagnostics.Modules,
description: Diagnostics.Specify_defaults_for_module_options_suited_for_common_runtimes_and_bundlers,
defaultValueDescription: undefined,
getParentOption: getModuleOptionDeclaration,
},
{
name: "formatDetection",
@@ -581,6 +585,7 @@ const moduleSubOptionDeclarations: readonly CommandLineOption[] = [
category: Diagnostics.Modules,
description: Diagnostics.Specify_how_files_are_determined_to_be_ECMAScript_modules_or_CommonJS_modules,
defaultValueDescription: Diagnostics.node16_when_module_is_node16_nodenext_when_module_is_nodenext_none_otherwise,
getParentOption: getModuleOptionDeclaration,
},
{
name: "formatInterop",
@@ -594,6 +599,7 @@ const moduleSubOptionDeclarations: readonly CommandLineOption[] = [
category: Diagnostics.Modules,
description: Diagnostics.Specify_the_target_runtime_s_rules_for_ESM_CommonJS_interoperation,
defaultValueDescription: Diagnostics.node16_when_module_is_node16_nodenext_when_module_is_nodenext_babel_otherwise,
getParentOption: getModuleOptionDeclaration,
},
{
name: "emit",
@@ -602,33 +608,17 @@ const moduleSubOptionDeclarations: readonly CommandLineOption[] = [
category: Diagnostics.Modules,
description: Diagnostics.Specify_what_module_code_is_generated,
defaultValueDescription: undefined,
getParentOption: getModuleOptionDeclaration,
},
];
/** @internal */
export const moduleOptionDeclaration: CommandLineOptionOfObjectOrShorthandType = {
export const moduleOptionDeclaration = {
name: "module",
shortName: "m",
// HEAD
type: "objectOrShorthand",
shorthandType: moduleKindMap,
elementOptions: commandLineOptionsToMap(moduleSubOptionDeclarations),
//
type: new Map(Object.entries({
none: ModuleKind.None,
commonjs: ModuleKind.CommonJS,
amd: ModuleKind.AMD,
system: ModuleKind.System,
umd: ModuleKind.UMD,
es6: ModuleKind.ES2015,
es2015: ModuleKind.ES2015,
es2020: ModuleKind.ES2020,
es2022: ModuleKind.ES2022,
esnext: ModuleKind.ESNext,
node16: ModuleKind.Node16,
nodenext: ModuleKind.NodeNext,
})),
// parent of 19e60f1a19 (Fix showConfig)
affectsSourceFile: true,
affectsModuleResolution: true,
affectsEmit: true,
@@ -638,7 +628,11 @@ export const moduleOptionDeclaration: CommandLineOptionOfObjectOrShorthandType =
category: Diagnostics.Modules,
description: Diagnostics.Specify_what_module_code_is_generated,
defaultValueDescription: undefined,
};
} satisfies CommandLineOptionOfObjectOrShorthandType;
function getModuleOptionDeclaration() {
return moduleOptionDeclaration;
}
const commandOptionsWithoutBuild: CommandLineOption[] = [
// CommandLine only options
@@ -1228,31 +1222,6 @@ const commandOptionsWithoutBuild: CommandLineOption[] = [
category: Diagnostics.Modules,
description: Diagnostics.Conditions_to_set_in_addition_to_the_resolver_specific_defaults_when_resolving_imports,
},
{
name: "moduleFormatDetection",
type: new Map(Object.entries({
none: ModuleFormatDetectionKind.None,
bundler: ModuleFormatDetectionKind.Bundler,
node16: ModuleFormatDetectionKind.Node16,
nodenext: ModuleFormatDetectionKind.NodeNext,
})),
affectsModuleResolution: true,
category: Diagnostics.Modules,
description: Diagnostics.Specify_how_files_are_determined_to_be_ECMAScript_modules_or_CommonJS_modules,
defaultValueDescription: Diagnostics.node16_when_module_is_node16_nodenext_when_module_is_nodenext_none_otherwise,
},
{
name: "moduleFormatInterop",
type: new Map(Object.entries({
babel: ModuleFormatInteropKind.Babel,
bundlernode: ModuleFormatInteropKind.BundlerNode,
node16: ModuleFormatInteropKind.Node16,
nodenext: ModuleFormatInteropKind.NodeNext,
})),
category: Diagnostics.Modules,
description: Diagnostics.Specify_the_target_runtime_s_rules_for_ESM_CommonJS_interoperation,
defaultValueDescription: Diagnostics.node16_when_module_is_node16_nodenext_when_module_is_nodenext_babel_otherwise,
},
// Source Maps
{
@@ -1770,10 +1739,16 @@ export function createOptionNameMap(optionDeclarations: readonly CommandLineOpti
const optionsNameMap = new Map<string, CommandLineOption>();
const shortOptionNames = new Map<string, string>();
forEach(optionDeclarations, option => {
optionsNameMap.set(option.name.toLowerCase(), option);
const lowerCaseName = option.name.toLowerCase();
optionsNameMap.set(lowerCaseName, option);
if (option.shortName) {
shortOptionNames.set(option.shortName, option.name);
}
if (option.type === "objectOrShorthand") {
forEachEntry(option.elementOptions, (subOption, subOptionName) => {
optionsNameMap.set(`${lowerCaseName}.${subOptionName.toLowerCase()}`, subOption);
});
}
});
return { optionsNameMap, shortOptionNames };
@@ -1806,14 +1781,15 @@ export function createCompilerDiagnosticForInvalidCustomType(opt: CommandLineOpt
return createDiagnosticForInvalidCustomType(opt, createCompilerDiagnostic);
}
function createDiagnosticForInvalidCustomType(opt: CommandLineOptionOfCustomType, createDiagnostic: (message: DiagnosticMessage, ...args: DiagnosticArguments) => Diagnostic): Diagnostic {
const namesOfType = arrayFrom(opt.type.keys());
function createDiagnosticForInvalidCustomType(opt: CommandLineOptionOfCustomType | CommandLineOptionOfObjectOrShorthandType & { shorthandType: CommandLineOptionOfCustomType["type"]; }, createDiagnostic: (message: DiagnosticMessage, ...args: DiagnosticArguments) => Diagnostic): Diagnostic {
const type = opt.type === "objectOrShorthand" ? opt.shorthandType : opt.type;
const namesOfType = arrayFrom(type.keys());
const stringNames = (opt.deprecatedKeys ? namesOfType.filter(k => !opt.deprecatedKeys!.has(k)) : namesOfType).map(key => `'${key}'`).join(", ");
return createDiagnostic(Diagnostics.Argument_for_0_option_must_be_Colon_1, `--${opt.name}`, stringNames);
}
/** @internal */
export function parseCustomTypeOption(opt: CommandLineOptionOfCustomType, value: string | undefined, errors: Diagnostic[]) {
export function parseCustomTypeOption(opt: CommandLineOptionOfCustomType | CommandLineOptionOfObjectOrShorthandType & { shorthandType: CommandLineOptionOfCustomType["type"]; }, value: string | undefined, errors: Diagnostic[]) {
return convertJsonOptionOfCustomType(opt, (value ?? "").trim(), errors);
}
@@ -1992,44 +1968,51 @@ function parseOptionValue(
errors.push(createCompilerDiagnostic(diagnostics.optionTypeMismatchDiagnostic, opt.name, getCompilerOptionValueTypeString(opt)));
}
const parentOption = opt.getParentOption?.();
const [base = {}, name] = parentOption ? [options[parentOption.name] as NestedCompilerOption, opt.name] : [options, opt.name];
if (parentOption) {
options[parentOption.name] = base as NestedCompilerOption;
}
if (args[i] !== "null") {
switch (opt.type) {
switch (opt.type === "objectOrShorthand" ? opt.shorthandType : opt.type) {
case "number":
options[opt.name] = validateJsonOptionValue(opt, parseInt(args[i]), errors);
base[name] = validateJsonOptionValue(opt, parseInt(args[i]), errors);
i++;
break;
case "boolean":
// boolean flag has optional value true, false, others
const optValue = args[i];
options[opt.name] = validateJsonOptionValue(opt, optValue !== "false", errors);
base[name] = validateJsonOptionValue(opt, optValue !== "false", errors);
// consume next argument as boolean flag value
if (optValue === "false" || optValue === "true") {
i++;
}
break;
case "string":
options[opt.name] = validateJsonOptionValue(opt, args[i] || "", errors);
base[name] = validateJsonOptionValue(opt, args[i] || "", errors);
i++;
break;
case "list":
const result = parseListTypeOption(opt, args[i], errors);
options[opt.name] = result || [];
const result = parseListTypeOption(opt as CommandLineOptionOfListType, args[i], errors);
base[name] = result || [];
if (result) {
i++;
}
break;
case "object":
case "listOrElement":
Debug.fail("listOrElement not supported here");
Debug.fail("listOrElement, object, and objectOrShorthand 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);
base[name] = parseCustomTypeOption(opt as CommandLineOptionOfCustomType | CommandLineOptionOfObjectOrShorthandType & { shorthandType: CommandLineOptionOfCustomType["type"]; }, args[i], errors);
i++;
break;
}
}
else {
options[opt.name] = undefined;
base[name] = undefined;
i++;
}
}
@@ -2528,18 +2511,25 @@ function getCompilerOptionValueTypeString(option: CommandLineOption): string {
function isCompilerOptionsValue(option: CommandLineOption | undefined, value: any): value is CompilerOptionsValue {
if (option) {
if (isNullOrUndefined(value)) return !option.disallowNullOrUndefined; // All options are undefinable/nullable
if (option.type === "list") {
return isArray(value);
}
if (option.type === "listOrElement") {
return isArray(value) || isCompilerOptionsValue(option.element, value);
return isCompilerOptionsValueWorker("list", value) || isCompilerOptionsValue(option.element, value);
}
const expectedType = isString(option.type) ? option.type : "string";
return typeof value === expectedType;
if (option.type === "objectOrShorthand") {
return isCompilerOptionsValueWorker(option.shorthandType, value) || typeof value === "object";
}
return isCompilerOptionsValueWorker(option.type, value);
}
return false;
}
function isCompilerOptionsValueWorker(type: CommandLineOption["type"], value: any): value is CompilerOptionsValue {
if (type === "list") {
return isArray(value);
}
const expectedType = isString(type) ? type : "string";
return typeof value === expectedType;
}
/** @internal */
export interface TSConfig {
compilerOptions: CompilerOptions;
@@ -2648,6 +2638,8 @@ function getCustomTypeMapOfCommandLineOption(optionDefinition: CommandLineOption
case "list":
case "listOrElement":
return getCustomTypeMapOfCommandLineOption(optionDefinition.element);
case "objectOrShorthand":
return typeof optionDefinition.shorthandType === "object" ? optionDefinition.shorthandType : undefined;
default:
return optionDefinition.type;
}
@@ -3340,6 +3332,7 @@ function parseOwnConfigOfJsonSourceFile(
const options = getDefaultCompilerOptions(configFileName);
let typeAcquisition: TypeAcquisition | undefined;
let watchOptions: WatchOptions | undefined;
let moduleOption: ModuleOptions | undefined;
let extendedConfigPath: string | string[] | undefined;
let rootCompilerOptions: PropertyName[] | undefined;
@@ -3364,7 +3357,7 @@ function parseOwnConfigOfJsonSourceFile(
keyText: string,
value: any,
propertyAssignment: PropertyAssignment,
parentOption: TsConfigOnlyOption | undefined,
parentOption: TsConfigOnlyOption | CommandLineOptionOfObjectOrShorthandType | undefined,
option: CommandLineOption | undefined,
) {
// Ensure value is verified except for extends which is handled in its own way for error reporting
@@ -3375,6 +3368,7 @@ function parseOwnConfigOfJsonSourceFile(
if (parentOption === compilerOptionsDeclaration) currentOption = options;
else if (parentOption === watchOptionsDeclaration) currentOption = watchOptions ??= {};
else if (parentOption === typeAcquisitionDeclaration) currentOption = typeAcquisition ??= getDefaultTypeAcquisition(configFileName);
else if (parentOption === moduleOptionDeclaration) currentOption = moduleOption ??= {};
else Debug.fail("Unknown option");
currentOption[option.name] = value;
}
@@ -3587,6 +3581,29 @@ export function convertJsonOption(
convertJsonOptionOfListType(opt, value, basePath, errors, propertyAssignment, valueExpression as ArrayLiteralExpression | undefined, sourceFile) :
convertJsonOption(opt.element, value, basePath, errors, propertyAssignment, valueExpression, sourceFile);
}
else if (optType === "objectOrShorthand") {
if (!value || typeof value !== "object") {
if (!isString(opt.shorthandType)) {
return convertJsonOptionOfCustomType(opt as CommandLineOptionOfObjectOrShorthandType & { shorthandType: CommandLineOptionOfCustomType["type"]; }, value as string, errors, valueExpression, sourceFile);
}
const validatedValue = validateJsonOptionValue(opt, value, errors, valueExpression, sourceFile);
return isNullOrUndefined(validatedValue) ? validatedValue : normalizeNonListOptionValue(opt, basePath, validatedValue);
}
else {
// const result: NestedCompilerOption = {};
// for (const id in value as NestedCompilerOption) {
// const property = (value as NestedCompilerOption)[id];
// const subOption = opt.elementOptions.get(id);
// if (subOption) {
// result[opt.name] = convertJsonOption(opt, property, basePath, errors, propertyAssignment, valueExpression, sourceFile);
// }
// else {
// errors.push(createUnknownOptionError(id, opt.extraKeyDiagnostics, /*unknownOptionErrorText*/ undefined, propertyAssignment?.name, sourceFile));
// }
// }
return {};
}
}
else if (!isString(opt.type)) {
return convertJsonOptionOfCustomType(opt as CommandLineOptionOfCustomType, value as string, errors, valueExpression, sourceFile);
}
@@ -3623,7 +3640,7 @@ function validateJsonOptionValue<T extends CompilerOptionsValue>(
}
function convertJsonOptionOfCustomType(
opt: CommandLineOptionOfCustomType,
opt: CommandLineOptionOfCustomType | CommandLineOptionOfObjectOrShorthandType & { shorthandType: CommandLineOptionOfCustomType["type"]; },
value: string,
errors: Diagnostic[],
valueExpression?: Expression,
@@ -3631,7 +3648,8 @@ function convertJsonOptionOfCustomType(
) {
if (isNullOrUndefined(value)) return undefined;
const key = value.toLowerCase();
const val = opt.type.get(key);
const type = opt.type === "objectOrShorthand" ? opt.shorthandType : opt.type;
const val = type.get(key);
if (val !== undefined) {
return validateJsonOptionValue(opt, val, errors, valueExpression, sourceFile);
}
@@ -4026,10 +4044,18 @@ export function convertCompilerOptionsForTelemetry(opts: CompilerOptions): Compi
}
function getOptionValueWithEmptyStrings(value: any, option: CommandLineOption): {} | undefined {
if (value === undefined) return value;
switch (option.type) {
// eslint-disable-next-line no-null/no-null
if (value === undefined || value === null) return value;
const type = option.type === "objectOrShorthand" && typeof value !== "object" ? option.shorthandType : option.type;
switch (type) {
case "object": // "paths". Can't get any useful information from the value since we blank out strings, so just return "".
return "";
case "objectOrShorthand":
return getOwnKeys(value).reduce((acc: any, key) => {
const subOption = (option as CommandLineOptionOfObjectOrShorthandType).elementOptions.get(key.toLowerCase());
acc[key] = subOption ? getOptionValueWithEmptyStrings(value[key], subOption) : "";
return acc;
}, {});
case "string": // Could be any arbitrary string -- use empty string instead.
return "";
case "number": // Allow numbers, but be sure to check it's actually a number.
@@ -4037,13 +4063,13 @@ function getOptionValueWithEmptyStrings(value: any, option: CommandLineOption):
case "boolean":
return typeof value === "boolean" ? value : "";
case "listOrElement":
if (!isArray(value)) return getOptionValueWithEmptyStrings(value, option.element);
if (!isArray(value)) return getOptionValueWithEmptyStrings(value, (option as CommandLineOptionOfListType).element);
// fall through to list
case "list":
const elementType = option.element;
const elementType = (option as CommandLineOptionOfListType).element;
return isArray(value) ? mapDefined(value, v => getOptionValueWithEmptyStrings(v, elementType)) : "";
default:
return forEachEntry(option.type, (optionEnumValue, optionStringValue) => {
return forEachEntry(type, (optionEnumValue, optionStringValue) => {
if (optionEnumValue === value) {
return optionStringValue;
}
@@ -4052,7 +4078,8 @@ function getOptionValueWithEmptyStrings(value: any, option: CommandLineOption):
}
function getDefaultValueForOption(option: CommandLineOption): {} {
switch (option.type) {
const type = option.type === "objectOrShorthand" ? option.shorthandType : option.type;
switch (type) {
case "number":
return 1;
case "boolean":
@@ -4063,11 +4090,11 @@ function getDefaultValueForOption(option: CommandLineOption): {} {
case "list":
return [];
case "listOrElement":
return getDefaultValueForOption(option.element);
return getDefaultValueForOption((option as CommandLineOptionOfListType).element);
case "object":
return {};
default:
const value = firstOrUndefinedIterator(option.type.keys());
const value = firstOrUndefinedIterator((option as CommandLineOptionOfCustomType).type.keys());
if (value !== undefined) return value;
return Debug.fail("Expected 'option.type' to have entries.");
}
+4
View File
@@ -6223,6 +6223,10 @@
"category": "Message",
"code": 6806
},
"Specify defaults for module options suited for common runtimes and bundlers.": {
"category": "Message",
"code": 6807
},
"one of:": {
"category": "Message",
@@ -245,7 +245,7 @@ export function transformECMAScriptModule(context: TransformationContext): (x: S
function visitExportDeclaration(node: ExportDeclaration) {
// `export * as ns` only needs to be transformed in ES2015
if (compilerOptions.module !== undefined && compilerOptions.module > ModuleKind.ES2015) {
if (getEmitModuleKind(compilerOptions) > ModuleKind.ES2015) {
return node;
}
+14 -5
View File
@@ -7090,7 +7090,8 @@ export enum PollingWatchKind {
FixedChunkSize,
}
export type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike<string[]> | PluginImport[] | ProjectReference[] | null | undefined;
export type NestedCompilerOption = ModuleOptions;
export type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike<string[]> | NestedCompilerOption | PluginImport[] | ProjectReference[] | null | undefined;
export interface CompilerOptions {
/** @internal */ all?: boolean;
@@ -7156,9 +7157,7 @@ export interface CompilerOptions {
locale?: string;
mapRoot?: string;
maxNodeModuleJsDepth?: number;
module?: ModuleKind;
moduleFormatDetection?: ModuleFormatDetectionKind;
moduleFormatInterop?: ModuleFormatInteropKind;
module?: ModuleKind | ModuleOptions;
moduleResolution?: ModuleResolutionKind;
moduleSuffixes?: string[];
moduleDetection?: ModuleDetectionKind;
@@ -7297,6 +7296,14 @@ export enum ModuleFormatInteropKind {
NodeNext = 199,
}
export interface ModuleOptions {
preset?: ModuleKind;
formatDetection?: ModuleFormatDetectionKind;
formatInterop?: ModuleFormatInteropKind;
emit?: ModuleKind;
[option: string]: CompilerOptionsValue | undefined;
}
export const enum JsxEmit {
None = 0,
Preserve = 1,
@@ -7441,6 +7448,7 @@ export interface CommandLineOptionBase {
transpileOptionValue?: boolean | undefined; // If set this means that the option should be set to this value when transpiling
extraValidation?: (value: CompilerOptionsValue) => [DiagnosticMessage, ...string[]] | undefined; // Additional validation to be performed for the value to be valid
disallowNullOrUndefined?: true; // If set option does not allow setting null
getParentOption?: () => CommandLineOptionOfObjectOrShorthandType;
}
/** @internal */
@@ -7489,13 +7497,14 @@ export interface TsConfigOnlyOption extends CommandLineOptionBase {
extraKeyDiagnostics?: DidYouMeanOptionsDiagnostics;
}
/** @interface */
/** @internal */
export interface CommandLineOptionOfObjectOrShorthandType extends CommandLineOptionBase {
type: "objectOrShorthand";
shorthandType: "string" | "number" | "boolean" | Map<string, number | string>;
defaultValueDescription?: string | number | boolean | DiagnosticMessage;
elementOptions: Map<string, CommandLineOption>;
extraKeyDiagnostics?: DidYouMeanOptionsDiagnostics;
deprecatedKeys?: Set<string>;
}
/** @internal */
+42 -31
View File
@@ -8560,7 +8560,10 @@ export function getEmitScriptTarget(compilerOptions: { module?: CompilerOptions[
}
/** @internal */
export function getEmitModuleKind(compilerOptions: { module?: CompilerOptions["module"]; target?: CompilerOptions["target"]; }) {
export function getEmitModuleKind(compilerOptions: { module?: CompilerOptions["module"]; target?: CompilerOptions["target"]; }): ModuleKind {
if (typeof compilerOptions.module === "object" && compilerOptions.module.emit !== undefined) {
return compilerOptions.module.emit;
}
return typeof compilerOptions.module === "number" ?
compilerOptions.module :
getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2015 ? ModuleKind.ES2015 : ModuleKind.CommonJS;
@@ -8571,6 +8574,44 @@ export function emitModuleKindIsNonNodeESM(moduleKind: ModuleKind) {
return moduleKind >= ModuleKind.ES2015 && moduleKind <= ModuleKind.ESNext;
}
/** @internal */
export function getModulePreset(compilerOptions: CompilerOptions): ModuleKind | undefined {
if (typeof compilerOptions.module === "object") {
return compilerOptions.module.preset;
}
return compilerOptions.module;
}
/** @internal */
export function getModuleFormatDetectionKind(compilerOptions: CompilerOptions): ModuleFormatDetectionKind {
if (typeof compilerOptions.module === "object" && compilerOptions.module.formatDetection !== undefined) {
return compilerOptions.module.formatDetection;
}
switch (getModulePreset(compilerOptions)) {
case ModuleKind.Node16:
return ModuleFormatDetectionKind.Node16;
case ModuleKind.NodeNext:
return ModuleFormatDetectionKind.NodeNext;
default:
return ModuleFormatDetectionKind.None;
}
}
/** @internal */
export function getModuleFormatInteropKind(compilerOptions: CompilerOptions): ModuleFormatInteropKind {
if (typeof compilerOptions.module === "object" && compilerOptions.module.formatInterop !== undefined) {
return compilerOptions.module.formatInterop;
}
switch (getModulePreset(compilerOptions)) {
case ModuleKind.Node16:
return ModuleFormatInteropKind.Node16;
case ModuleKind.NodeNext:
return ModuleFormatInteropKind.NodeNext;
default:
return ModuleFormatInteropKind.Babel;
}
}
/** @internal */
export function getEmitModuleResolutionKind(compilerOptions: CompilerOptions) {
let moduleResolution = compilerOptions.moduleResolution;
@@ -8593,36 +8634,6 @@ export function getEmitModuleResolutionKind(compilerOptions: CompilerOptions) {
return moduleResolution;
}
/** @internal */
export function getModuleFormatDetectionKind(compilerOptions: CompilerOptions): ModuleFormatDetectionKind {
if (compilerOptions.moduleFormatDetection !== undefined) {
return compilerOptions.moduleFormatDetection;
}
switch (getEmitModuleKind(compilerOptions)) {
case ModuleKind.Node16:
return ModuleFormatDetectionKind.Node16;
case ModuleKind.NodeNext:
return ModuleFormatDetectionKind.NodeNext;
default:
return ModuleFormatDetectionKind.None;
}
}
/** @internal */
export function getModuleFormatInteropKind(compilerOptions: CompilerOptions): ModuleFormatInteropKind {
if (compilerOptions.moduleFormatInterop !== undefined) {
return compilerOptions.moduleFormatInterop;
}
switch (getEmitModuleKind(compilerOptions)) {
case ModuleKind.Node16:
return ModuleFormatInteropKind.Node16;
case ModuleKind.NodeNext:
return ModuleFormatInteropKind.NodeNext;
default:
return ModuleFormatInteropKind.Babel;
}
}
/** @internal */
export function getEmitModuleDetectionKind(options: CompilerOptions) {
return options.moduleDetection ||
+9 -5
View File
@@ -8,6 +8,9 @@ import {
CharacterCodes,
combinePaths,
CommandLineOption,
CommandLineOptionOfCustomType,
CommandLineOptionOfListType,
CommandLineOptionOfObjectOrShorthandType,
compareStringsCaseInsensitive,
CompilerOptions,
contains,
@@ -377,15 +380,16 @@ function generateOptionOutput(sys: System, option: CommandLineOption, rightAlign
function getPossibleValues(option: CommandLineOption) {
let possibleValues: string;
switch (option.type) {
const type = option.type === "objectOrShorthand" ? option.shorthandType : option.type;
switch (type) {
case "string":
case "number":
case "boolean":
possibleValues = option.type;
possibleValues = type;
break;
case "list":
case "listOrElement":
possibleValues = getPossibleValues(option.element);
possibleValues = getPossibleValues((option as CommandLineOptionOfListType).element);
break;
case "object":
possibleValues = "";
@@ -394,8 +398,8 @@ function generateOptionOutput(sys: System, option: CommandLineOption, rightAlign
// Map<string, number | string>
// Group synonyms: es6/es2015
const inverted: { [value: string]: string[]; } = {};
option.type.forEach((value, name) => {
if (!option.deprecatedKeys?.has(name)) {
type.forEach((value, name) => {
if (!(option as CommandLineOptionOfCustomType | CommandLineOptionOfObjectOrShorthandType).deprecatedKeys?.has(name)) {
(inverted[value] ||= []).push(name);
}
});
+21 -12
View File
@@ -332,16 +332,15 @@ export namespace Compiler {
{ name: "fullEmitPaths", type: "boolean", defaultValueDescription: false },
];
let optionsIndex: Map<string, ts.CommandLineOption>;
let harnessOptionsMap: Map<string, ts.CommandLineOption>;
function getCommandLineOption(name: string): ts.CommandLineOption | undefined {
if (!optionsIndex) {
optionsIndex = new Map<string, ts.CommandLineOption>();
const optionDeclarations = harnessOptionDeclarations.concat(ts.optionDeclarations);
for (const option of optionDeclarations) {
optionsIndex.set(option.name.toLowerCase(), option);
if (!harnessOptionsMap) {
harnessOptionsMap = new Map<string, ts.CommandLineOption>();
for (const option of harnessOptionDeclarations) {
harnessOptionsMap.set(option.name.toLowerCase(), option);
}
}
return optionsIndex.get(name.toLowerCase());
return harnessOptionsMap.get(name.toLowerCase()) ?? ts.getOptionFromName(name);
}
export function setCompilerOptionsFromHarnessSetting(settings: TestCaseParser.CompilerSettings, options: ts.CompilerOptions & HarnessOptions): void {
@@ -357,7 +356,13 @@ export namespace Compiler {
const option = getCommandLineOption(name);
if (option) {
const errors: ts.Diagnostic[] = [];
options[option.name] = optionValue(option, value, errors);
const parentOption = option.getParentOption?.();
const [base, name] = parentOption ? [(options[parentOption.name] || {}) as ts.NestedCompilerOption, option.name] : [options, option.name];
if (parentOption) {
options[parentOption.name] = base as ts.NestedCompilerOption;
}
base[name] = optionValue(option, value, errors);
if (errors.length > 0) {
throw new Error(`Unknown value '${value}' for compiler option '${name}'.`);
}
@@ -1109,10 +1114,14 @@ let booleanVaryByStarSettingValues: Map<string, string | number> | undefined;
function getVaryByStarSettingValues(varyBy: string): ReadonlyMap<string, string | number> | undefined {
const option = ts.forEach(ts.optionDeclarations, decl => ts.equateStringsCaseInsensitive(decl.name, varyBy) ? decl : undefined);
if (option) {
if (typeof option.type === "object") {
return option.type;
return getVaryByValuesByType(option.type === "objectOrShorthand" ? option.shorthandType : option.type);
}
function getVaryByValuesByType(type: ts.CommandLineOption["type"]) {
if (typeof type === "object") {
return type;
}
if (option.type === "boolean") {
if (type === "boolean") {
return booleanVaryByStarSettingValues || (booleanVaryByStarSettingValues = new Map(Object.entries({
true: 1,
false: 0,
@@ -1178,7 +1187,7 @@ export namespace TestCaseParser {
}
// Regex for parsing options in the format "@Alpha: Value of any sort"
const optionRegex = /^[/]{2}\s*@(\w+)\s*:\s*([^\r\n]*)/gm; // multiple matches on multiple lines
const optionRegex = /^[/]{2}\s*@([\w.]+)\s*:\s*([^\r\n]*)/gm; // multiple matches on multiple lines
const linkRegex = /^[/]{2}\s*@link\s*:\s*([^\r\n]*)\s*->\s*([^\r\n]*)/gm; // multiple matches on multiple lines
export function parseSymlinkFromTest(line: string, symlinks: vfs.FileSet | undefined, absoluteRootDir?: string) {
+1 -1
View File
@@ -128,7 +128,7 @@ function getResolutionCacheDetails<File, T extends ts.ResolutionWithFailedLookup
addedCacheType = true;
baseline.push(`${indent}${cacheType}:`);
}
baseline.push(`${indent} ${key}: ${mode ? ts.getNameOfCompilerOptionValue(mode, ts.moduleOptionDeclaration.type) + ":" : ""}${getResolvedFileName(resolved)}`);
baseline.push(`${indent} ${key}: ${mode ? ts.getNameOfCompilerOptionValue(mode, ts.moduleOptionDeclaration.shorthandType) + ":" : ""}${getResolvedFileName(resolved)}`);
}, file);
}
+4 -2
View File
@@ -368,8 +368,10 @@ export interface SafeList {
function prepareConvertersForEnumLikeCompilerOptions(commandLineOptions: CommandLineOption[]): Map<string, Map<string, number>> {
const map = new Map<string, Map<string, number>>();
for (const option of commandLineOptions) {
if (typeof option.type === "object") {
const optionMap = option.type as Map<string, number>;
const optionMap = (typeof option.type === "object" ? option.type :
option.type === "objectOrShorthand" && typeof option.shorthandType === "object" ? option.shorthandType :
undefined) as Map<string, number> | undefined;
if (optionMap) {
// verify that map contains only numbers
optionMap.forEach(value => {
Debug.assert(typeof value === "number");
@@ -128,7 +128,8 @@ describe("unittests:: config:: showConfig", () => {
if (option.name === "project") return;
let args: string[];
let optionValue: object | undefined;
switch (option.type) {
const type = option.type === "objectOrShorthand" ? option.shorthandType : option.type;
switch (type) {
case "boolean": {
if (option.isTSConfigOnly) {
args = ["-p", "tsconfig.json"];
@@ -179,7 +180,7 @@ describe("unittests:: config:: showConfig", () => {
break;
}
default: {
const val = ts.firstOrUndefinedIterator(option.type.keys());
const val = ts.firstOrUndefinedIterator(type.keys());
if (val === undefined) return ts.Debug.fail("Expected 'option.type' to have entries");
if (option.isTSConfigOnly) {
args = ["-p", "tsconfig.json"];
+1 -1
View File
@@ -291,7 +291,7 @@ function generateBuildInfoProgramBaseline(sys: ts.System, buildInfoPath: string,
return {
original: ts.isString(original) ? undefined : original,
...info,
impliedFormat: info.impliedFormat && ts.getNameOfCompilerOptionValue(info.impliedFormat, ts.moduleOptionDeclaration.type),
impliedFormat: info.impliedFormat && ts.getNameOfCompilerOptionValue(info.impliedFormat, ts.moduleOptionDeclaration.shorthandType),
};
}
@@ -37,7 +37,7 @@ describe("unittests:: Reuse program structure:: General", () => {
addedHeader = true;
baselines.push(`${cacheType}:`);
}
baselines.push(`${key}: ${mode ? ts.getNameOfCompilerOptionValue(mode, ts.moduleOptionDeclaration.type) + ": " : ""}${jsonToReadableText(resolved)}`);
baselines.push(`${key}: ${mode ? ts.getNameOfCompilerOptionValue(mode, ts.moduleOptionDeclaration.shorthandType) + ": " : ""}${jsonToReadableText(resolved)}`);
}
}
function baselineProgram(baselines: string[], program: ts.Program, host?: TestCompilerHost) {
+10 -4
View File
@@ -7481,7 +7481,8 @@ declare namespace ts {
DynamicPriority = 2,
FixedChunkSize = 3,
}
type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike<string[]> | PluginImport[] | ProjectReference[] | null | undefined;
type NestedCompilerOption = ModuleOptions;
type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike<string[]> | NestedCompilerOption | PluginImport[] | ProjectReference[] | null | undefined;
interface CompilerOptions {
allowImportingTsExtensions?: boolean;
allowJs?: boolean;
@@ -7521,9 +7522,7 @@ declare namespace ts {
locale?: string;
mapRoot?: string;
maxNodeModuleJsDepth?: number;
module?: ModuleKind;
moduleFormatDetection?: ModuleFormatDetectionKind;
moduleFormatInterop?: ModuleFormatInteropKind;
module?: ModuleKind | ModuleOptions;
moduleResolution?: ModuleResolutionKind;
moduleSuffixes?: string[];
moduleDetection?: ModuleDetectionKind;
@@ -7631,6 +7630,13 @@ declare namespace ts {
Node16 = 100,
NodeNext = 199,
}
interface ModuleOptions {
preset?: ModuleKind;
formatDetection?: ModuleFormatDetectionKind;
formatInterop?: ModuleFormatInteropKind;
emit?: ModuleKind;
[option: string]: CompilerOptionsValue | undefined;
}
enum JsxEmit {
None = 0,
Preserve = 1,
@@ -1,36 +0,0 @@
error TS5110: Option 'module' must be set to 'NodeNext' when option 'moduleResolution' is set to 'NodeNext'.
!!! error TS5110: Option 'module' must be set to 'NodeNext' when option 'moduleResolution' is set to 'NodeNext'.
==== package.json (0 errors) ====
{
"name": "test",
"version": "1.0.0",
"description": "",
"type": "module",
"module": "index.mjs"
}
==== index.mts (0 errors) ====
import * as exportAny from "./exportAny.cjs";
import * as exportUnknown from "./exportUnknown.cjs";
import * as exportSymbol from "./exportSymbol.cjs";
import type * as exportAnyType from "./exportAny.cjs";
import type * as exportUnknownType from "./exportUnknown.cjs";
import type * as exportSymbolType from "./exportSymbol.cjs";
==== exportAny.d.cts (0 errors) ====
declare const __: any;
export = __;
==== exportUnknown.d.cts (0 errors) ====
declare const __: unknown;
export = __;
==== exportSymbol.d.cts (0 errors) ====
declare const __: symbol;
export = __;
@@ -15,5 +15,4 @@ import {x} from "foo";
//// [app.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
export {};
@@ -1,18 +0,0 @@
error TS5110: Option 'module' must be set to 'NodeNext' when option 'moduleResolution' is set to 'NodeNext'.
!!! error TS5110: Option 'module' must be set to 'NodeNext' when option 'moduleResolution' is set to 'NodeNext'.
==== /a/node_modules/foo/index.d.ts (0 errors) ====
export declare let x: number
==== /a/node_modules/foo/package.json (0 errors) ====
{
"name": "foo",
"type": "module",
"exports": {
".": "./index.d.ts"
}
}
==== /a/b/c/d/e/app.mts (0 errors) ====
import {x} from "foo";
@@ -16,5 +16,4 @@ import {x} from "foo";
//// [app.mjs]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
export {};
@@ -1,8 +1,6 @@
error TS5107: Option 'target=ES3' is deprecated and will stop functioning in TypeScript 5.5. Specify compilerOption '"ignoreDeprecations": "5.0"' to silence this error.
error TS6046: Argument for '--module' option must be: 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015', 'es2020', 'es2022', 'esnext', 'node16', 'nodenext'.
!!! error TS5107: Option 'target=ES3' is deprecated and will stop functioning in TypeScript 5.5. Specify compilerOption '"ignoreDeprecations": "5.0"' to silence this error.
!!! error TS6046: Argument for '--module' option must be: 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015', 'es2020', 'es2022', 'esnext', 'node16', 'nodenext'.
==== file.ts (0 errors) ====
@@ -1,8 +1,6 @@
error TS5107: Option 'target=ES3' is deprecated and will stop functioning in TypeScript 5.5. Specify compilerOption '"ignoreDeprecations": "5.0"' to silence this error.
error TS6046: Argument for '--module' option must be: 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015', 'es2020', 'es2022', 'esnext', 'node16', 'nodenext'.
!!! error TS5107: Option 'target=ES3' is deprecated and will stop functioning in TypeScript 5.5. Specify compilerOption '"ignoreDeprecations": "5.0"' to silence this error.
!!! error TS6046: Argument for '--module' option must be: 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015', 'es2020', 'es2022', 'esnext', 'node16', 'nodenext'.
==== file.ts (0 errors) ====
@@ -1,8 +1,6 @@
error TS5107: Option 'target=ES3' is deprecated and will stop functioning in TypeScript 5.5. Specify compilerOption '"ignoreDeprecations": "5.0"' to silence this error.
error TS6046: Argument for '--module' option must be: 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015', 'es2020', 'es2022', 'esnext', 'node16', 'nodenext'.
!!! error TS5107: Option 'target=ES3' is deprecated and will stop functioning in TypeScript 5.5. Specify compilerOption '"ignoreDeprecations": "5.0"' to silence this error.
!!! error TS6046: Argument for '--module' option must be: 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015', 'es2020', 'es2022', 'esnext', 'node16', 'nodenext'.
==== file.ts (0 errors) ====
@@ -1,8 +1,6 @@
error TS5107: Option 'target=ES3' is deprecated and will stop functioning in TypeScript 5.5. Specify compilerOption '"ignoreDeprecations": "5.0"' to silence this error.
error TS6046: Argument for '--module' option must be: 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015', 'es2020', 'es2022', 'esnext', 'node16', 'nodenext'.
!!! error TS5107: Option 'target=ES3' is deprecated and will stop functioning in TypeScript 5.5. Specify compilerOption '"ignoreDeprecations": "5.0"' to silence this error.
!!! error TS6046: Argument for '--module' option must be: 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015', 'es2020', 'es2022', 'esnext', 'node16', 'nodenext'.
==== file.ts (0 errors) ====
@@ -1,7 +1,7 @@
// @module: esnext
// @moduleResolution: bundler
// @moduleFormatDetection: bundler
// @moduleFormatInterop: bundlernode
// @module.emit: esnext
// @module.formatDetection: bundler
// @module.formatInterop: bundlernode
// @Filename: /node_modules/dep/package.json
{