Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add verbatimModuleSyntax, deprecate importsNotUsedAsValues and preserveValueImports #52203

Merged
merged 19 commits into from
Jan 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 140 additions & 59 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,13 @@ const commandOptionsWithoutBuild: CommandLineOption[] = [
transpileOptionValue: true,
defaultValueDescription: false,
},
{
name: "verbatimModuleSyntax",
type: "boolean",
category: Diagnostics.Interop_Constraints,
description: Diagnostics.Do_not_transform_or_elide_any_imports_or_exports_not_marked_as_type_only_ensuring_they_are_written_in_the_output_file_s_format_based_on_the_module_setting,
defaultValueDescription: false,
},

// Strict Type Checks
{
Expand Down
74 changes: 65 additions & 9 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@
"category": "Error",
"code": 1203
},
"Re-exporting a type when the '--isolatedModules' flag is provided requires using 'export type'.": {
"Re-exporting a type when '{0}' is enabled requires using 'export type'.": {
"category": "Error",
"code": 1205
},
Expand All @@ -647,10 +647,6 @@
"category": "Error",
"code": 1207
},
"'{0}' cannot be compiled under '--isolatedModules' because it is considered a global script file. Add an import, export, or an empty 'export {}' statement to make it a module.": {
"category": "Error",
"code": 1208
},
"Invalid optional chain from new expression. Did you mean to call '{0}()'?": {
"category": "Error",
"code": 1209
Expand Down Expand Up @@ -871,7 +867,7 @@
"category": "Error",
"code": 1268
},
"Cannot use 'export import' on a type or type-only namespace when the '--isolatedModules' flag is provided.": {
"Cannot use 'export import' on a type or type-only namespace when '{0}' is enabled.": {
"category": "Error",
"code": 1269
},
Expand Down Expand Up @@ -915,6 +911,42 @@
"category": "Error",
"code": 1279
},
"Namespaces are not allowed in global script files when '{0}' is enabled. If this file is not intended to be a global script, set 'moduleDetection' to 'force' or add an empty 'export {}' statement.": {
"category": "Error",
"code": 1280
},
"Cannot access '{0}' from another file without qualification when '{1}' is enabled. Use '{2}' instead.": {
"category": "Error",
"code": 1281
},
"An 'export =' declaration must reference a value when 'verbatimModuleSyntax' is enabled, but '{0}' only refers to a type.": {
"category": "Error",
"code": 1282
},
"An 'export =' declaration must reference a real value when 'verbatimModuleSyntax' is enabled, but '{0}' resolves to a type-only declaration.": {
"category": "Error",
"code": 1283
},
"An 'export default' must reference a value when 'verbatimModuleSyntax' is enabled, but '{0}' only refers to a type.": {
"category": "Error",
"code": 1284
},
"An 'export default' must reference a real value when 'verbatimModuleSyntax' is enabled, but '{0}' resolves to a type-only declaration.": {
"category": "Error",
"code": 1285
},
"ESM syntax is not allowed in a CommonJS module when 'verbatimModuleSyntax' is enabled.": {
"category": "Error",
"code": 1286
},
"A top-level 'export' modifier cannot be used on value declarations in a CommonJS module when 'verbatimModuleSyntax' is enabled.": {
"category": "Error",
"code": 1287
},
"An import alias cannot resolve to a type or type-only declaration when 'verbatimModuleSyntax' is enabled.": {
"category": "Error",
"code": 1288
},

"'with' statements are not allowed in an async function block.": {
"category": "Error",
Expand Down Expand Up @@ -1444,7 +1476,7 @@
"category": "Error",
"code": 1446
},
"'{0}' resolves to a type-only declaration and must be re-exported using a type-only re-export when 'isolatedModules' is enabled.": {
"'{0}' resolves to a type-only declaration and must be re-exported using a type-only re-export when '{1}' is enabled.": {
"category": "Error",
"code": 1448
},
Expand Down Expand Up @@ -1557,6 +1589,14 @@
"category": "Message",
"code": 1483
},
"'{0}' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.": {
"category": "Error",
"code": 1484
},
"'{0}' resolves to a type-only declaration and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.": {
"category": "Error",
"code": 1485
},

"The types of '{0}' are incompatible between these types.": {
"category": "Error",
Expand Down Expand Up @@ -3223,7 +3263,7 @@
"category": "Error",
"code": 2747
},
"Cannot access ambient const enums when the '--isolatedModules' flag is provided.": {
"Cannot access ambient const enums when '{0}' is enabled.": {
"category": "Error",
"code": 2748
},
Expand Down Expand Up @@ -4217,7 +4257,7 @@
"category": "Error",
"code": 5090
},
"Option 'preserveConstEnums' cannot be disabled when 'isolatedModules' is enabled.": {
"Option 'preserveConstEnums' cannot be disabled when '{0}' is enabled.": {
"category": "Error",
"code": 5091
},
Expand Down Expand Up @@ -4269,6 +4309,18 @@
"category": "Error",
"code": 5103
},
"Option '{0}' is redundant and cannot be specified with option '{1}'.": {
"category": "Error",
"code": 5104
},
"Option 'verbatimModuleSyntax' cannot be used when 'module' is set to 'UMD', 'AMD', or 'System'.": {
"category": "Error",
"code": 5105
},
"Use '{0}' instead.": {
"category": "Message",
"code": 5106
},

"Generates a sourcemap for each corresponding '.d.ts' file.": {
"category": "Message",
Expand Down Expand Up @@ -6003,6 +6055,10 @@
"category": "Message",
"code": 6803
},
"Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting.": {
"category": "Message",
"code": 6804
},

"one of:": {
"category": "Message",
Expand Down
106 changes: 86 additions & 20 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
createCompilerDiagnostic,
createCompilerDiagnosticFromMessageChain,
createDiagnosticCollection,
createDiagnosticForNodeFromMessageChain,
createDiagnosticForNodeInSourceFile,
createDiagnosticForRange,
createFileDiagnostic,
Expand Down Expand Up @@ -4117,21 +4118,13 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
const languageVersion = getEmitScriptTarget(options);

const firstNonAmbientExternalModuleSourceFile = find(files, f => isExternalModule(f) && !f.isDeclarationFile);
if (options.isolatedModules) {
if (options.module === ModuleKind.None && languageVersion < ScriptTarget.ES2015) {
if (options.isolatedModules || options.verbatimModuleSyntax) {
if (options.module === ModuleKind.None && languageVersion < ScriptTarget.ES2015 && options.isolatedModules) {
createDiagnosticForOptionName(Diagnostics.Option_isolatedModules_can_only_be_used_when_either_option_module_is_provided_or_option_target_is_ES2015_or_higher, "isolatedModules", "target");
}

if (options.preserveConstEnums === false) {
createDiagnosticForOptionName(Diagnostics.Option_preserveConstEnums_cannot_be_disabled_when_isolatedModules_is_enabled, "preserveConstEnums", "isolatedModules");
}

for (const file of files) {
if (!isExternalModule(file) && !isSourceFileJS(file) && !file.isDeclarationFile && file.scriptKind !== ScriptKind.JSON) {
const span = getErrorSpanForNode(file, file);
programDiagnostics.add(createFileDiagnostic(file, span.start, span.length,
Diagnostics._0_cannot_be_compiled_under_isolatedModules_because_it_is_considered_a_global_script_file_Add_an_import_export_or_an_empty_export_statement_to_make_it_a_module, getBaseFileName(file.fileName)));
}
createDiagnosticForOptionName(Diagnostics.Option_preserveConstEnums_cannot_be_disabled_when_0_is_enabled, options.verbatimModuleSyntax ? "verbatimModuleSyntax" : "isolatedModules", "preserveConstEnums");
}
}
else if (firstNonAmbientExternalModuleSourceFile && languageVersion < ScriptTarget.ES2015 && options.module === ModuleKind.None) {
Expand Down Expand Up @@ -4240,7 +4233,23 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
}

if (options.preserveValueImports && getEmitModuleKind(options) < ModuleKind.ES2015) {
createOptionValueDiagnostic("importsNotUsedAsValues", Diagnostics.Option_preserveValueImports_can_only_be_used_when_module_is_set_to_es2015_or_later);
createDiagnosticForOptionName(Diagnostics.Option_preserveValueImports_can_only_be_used_when_module_is_set_to_es2015_or_later, "preserveValueImports");
}

if (options.verbatimModuleSyntax) {
const moduleKind = getEmitModuleKind(options);
if (moduleKind === ModuleKind.AMD || moduleKind === ModuleKind.UMD || moduleKind === ModuleKind.System) {
createDiagnosticForOptionName(Diagnostics.Option_verbatimModuleSyntax_cannot_be_used_when_module_is_set_to_UMD_AMD_or_System, "verbatimModuleSyntax");
}
if (options.isolatedModules) {
createRedundantOptionDiagnostic("isolatedModules", "verbatimModuleSyntax");
}
if (options.preserveValueImports) {
createRedundantOptionDiagnostic("preserveValueImports", "verbatimModuleSyntax");
}
if (options.importsNotUsedAsValues) {
createRedundantOptionDiagnostic("importsNotUsedAsValues", "verbatimModuleSyntax");
}
}

if (options.allowImportingTsExtensions && !(options.noEmit || options.emitDeclarationOnly)) {
Expand Down Expand Up @@ -4333,15 +4342,35 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
if (options.out) {
createDeprecatedDiagnosticForOption(version, "out");
}
if (options.importsNotUsedAsValues) {
createDeprecatedDiagnosticForOption(version, "importsNotUsedAsValues", /*value*/ undefined, "verbatimModuleSyntax");
}
if (options.preserveValueImports) {
createDeprecatedDiagnosticForOption(version, "preserveValueImports", /*value*/ undefined, "verbatimModuleSyntax");
}
}

function createDeprecatedDiagnosticForOption(version: string, name: string, value?: string) {
function createDeprecatedDiagnosticForOption(version: string, name: string, value?: string, useInstead?: string) {
if (version === DeprecationVersion.v6_0) {
createDiagnosticForOption(/*onKey*/ !value, name, /*option2*/ undefined, Diagnostics.Flag_0_is_deprecated_Please_remove_it_from_your_configuration, value || name);
if (useInstead) {
const details = chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Use_0_instead, useInstead);
const chain = chainDiagnosticMessages(details, Diagnostics.Flag_0_is_deprecated_Please_remove_it_from_your_configuration, value || name);
createDiagnosticForOption(/*onKey*/ !value, name, /*option2*/ undefined, chain);
}
else {
createDiagnosticForOption(/*onKey*/ !value, name, /*option2*/ undefined, Diagnostics.Flag_0_is_deprecated_Please_remove_it_from_your_configuration, value || name);
}
}
else {
createDiagnosticForOption(/*onKey*/ !value, name, /*option2*/ undefined,
Diagnostics.Flag_0_is_deprecated_and_will_stop_functioning_in_TypeScript_1_Specify_ignoreDeprecations_Colon_2_to_silence_this_error, value || name, DeprecationVersion.v5_5, DeprecationVersion.v5_0);
if (useInstead) {
const details = chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Use_0_instead, useInstead);
const chain = chainDiagnosticMessages(details, Diagnostics.Flag_0_is_deprecated_and_will_stop_functioning_in_TypeScript_1_Specify_ignoreDeprecations_Colon_2_to_silence_this_error, value || name, DeprecationVersion.v5_5, DeprecationVersion.v5_0);
createDiagnosticForOption(/*onKey*/ !value, name, /*option2*/ undefined, chain);
}
else {
createDiagnosticForOption(/*onKey*/ !value, name, /*option2*/ undefined,
Diagnostics.Flag_0_is_deprecated_and_will_stop_functioning_in_TypeScript_1_Specify_ignoreDeprecations_Colon_2_to_silence_this_error, value || name, DeprecationVersion.v5_5, DeprecationVersion.v5_0);
}
}
}

Expand Down Expand Up @@ -4590,13 +4619,21 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
}
}

function createDiagnosticForOption(onKey: boolean, option1: string, option2: string | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number) {
function createDiagnosticForOption(onKey: boolean, option1: string, option2: string | undefined, message: DiagnosticMessageChain): void;
function createDiagnosticForOption(onKey: boolean, option1: string, option2: string | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): void;
function createDiagnosticForOption(onKey: boolean, option1: string, option2: string | undefined, message: DiagnosticMessage | DiagnosticMessageChain, arg0?: string | number, arg1?: string | number, arg2?: string | number): void {
const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax();
const needCompilerDiagnostic = !compilerOptionsObjectLiteralSyntax ||
!createOptionDiagnosticInObjectLiteralSyntax(compilerOptionsObjectLiteralSyntax, onKey, option1, option2, message, arg0, arg1, arg2);

if (needCompilerDiagnostic) {
programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1, arg2));
// eslint-disable-next-line local/no-in-operator
if ("messageText" in message) {
programDiagnostics.add(createCompilerDiagnosticFromMessageChain(message));
}
else {
programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1, arg2));
}
}
}

Expand All @@ -4616,14 +4653,43 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
return _compilerOptionsObjectLiteralSyntax || undefined;
}

function createOptionDiagnosticInObjectLiteralSyntax(objectLiteral: ObjectLiteralExpression, onKey: boolean, key1: string, key2: string | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): boolean {
function createOptionDiagnosticInObjectLiteralSyntax(objectLiteral: ObjectLiteralExpression, onKey: boolean, key1: string, key2: string | undefined, messageChain: DiagnosticMessageChain): boolean;
function createOptionDiagnosticInObjectLiteralSyntax(objectLiteral: ObjectLiteralExpression, onKey: boolean, key1: string, key2: string | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): boolean;
function createOptionDiagnosticInObjectLiteralSyntax(objectLiteral: ObjectLiteralExpression, onKey: boolean, key1: string, key2: string | undefined, message: DiagnosticMessage | DiagnosticMessageChain, arg0?: string | number, arg1?: string | number, arg2?: string | number): boolean;
function createOptionDiagnosticInObjectLiteralSyntax(objectLiteral: ObjectLiteralExpression, onKey: boolean, key1: string, key2: string | undefined, message: DiagnosticMessage | DiagnosticMessageChain, arg0?: string | number, arg1?: string | number, arg2?: string | number): boolean {
const props = getPropertyAssignment(objectLiteral, key1, key2);
for (const prop of props) {
programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile!, onKey ? prop.name : prop.initializer, message, arg0, arg1, arg2));
// eslint-disable-next-line local/no-in-operator
if ("messageText" in message) {
programDiagnostics.add(createDiagnosticForNodeFromMessageChain(options.configFile!, onKey ? prop.name : prop.initializer, message));
}
else {
programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile!, onKey ? prop.name : prop.initializer, message, arg0, arg1, arg2));
}
}
return !!props.length;
}

/**
* Only creates a diagnostic on the option key specified by `errorOnOption`.
* If both options are specified in the program in separate config files via `extends`,
* a diagnostic is only created if `errorOnOption` is specified in the leaf config file.
* Useful if `redundantWithOption` represents a superset of the functionality of `errorOnOption`:
* if a user inherits `errorOnOption` from a base config file, it's still valid and useful to
* override it in the leaf config file.
*/
function createRedundantOptionDiagnostic(errorOnOption: string, redundantWithOption: string) {
const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax();
if (compilerOptionsObjectLiteralSyntax) {
// This is a no-op if `errorOnOption` isn't present in the leaf config file.
createOptionDiagnosticInObjectLiteralSyntax(compilerOptionsObjectLiteralSyntax, /*onKey*/ true, errorOnOption, /*key2*/ undefined, Diagnostics.Option_0_is_redundant_and_cannot_be_specified_with_option_1, errorOnOption, redundantWithOption);
}
else {
// There was no config file, so both options were specified on the command line.
createDiagnosticForOptionName(Diagnostics.Option_0_is_redundant_and_cannot_be_specified_with_option_1, errorOnOption, redundantWithOption);
}
}

function blockEmittingOfFile(emitFileName: string, diag: Diagnostic) {
hasEmitBlockingDiagnostics.set(toPath(emitFileName), true);
programDiagnostics.add(diag);
Expand Down
Loading