Skip to content

Commit afe0a2c

Browse files
committed
fix(64627): reject deferred imports without namespace bindings
1 parent 50d70a3 commit afe0a2c

18 files changed

Lines changed: 298 additions & 38 deletions

‎tsc/internal/checker/grammarchecks.go‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2110,7 +2110,10 @@ func (c *Checker) checkGrammarImportClause(node *ast.ImportClause) bool {
21102110
if node.Name() != nil {
21112111
return c.grammarErrorOnNode(&node.Node, diagnostics.Default_imports_are_not_allowed_in_a_deferred_import)
21122112
}
2113-
if node.NamedBindings != nil && node.NamedBindings.Kind == ast.KindNamedImports {
2113+
if node.NamedBindings == nil {
2114+
return c.grammarErrorOnNode(&node.Node, diagnostics.A_deferred_import_must_specify_a_namespace_binding)
2115+
}
2116+
if node.NamedBindings.Kind == ast.KindNamedImports {
21142117
return c.grammarErrorOnNode(&node.Node, diagnostics.Named_imports_are_not_allowed_in_a_deferred_import)
21152118
}
21162119
if c.moduleKind.SupportsDeferredImports() {

‎tsc/internal/compiler/program.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2427,6 +2427,7 @@ var plainJSErrors = collections.NewSetFromItems(
24272427
diagnostics.A_continue_statement_can_only_jump_to_a_label_of_an_enclosing_iteration_statement.Code(),
24282428
diagnostics.A_default_clause_cannot_appear_more_than_once_in_a_switch_statement.Code(),
24292429
diagnostics.A_default_export_must_be_at_the_top_level_of_a_file_or_module_declaration.Code(),
2430+
diagnostics.A_deferred_import_must_specify_a_namespace_binding.Code(),
24302431
diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context.Code(),
24312432
diagnostics.A_destructuring_declaration_must_have_an_initializer.Code(),
24322433
diagnostics.A_get_accessor_cannot_have_parameters.Code(),

‎tsc/internal/diagnostics/diagnosticMessages.generated.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1949,6 +1949,7 @@
19491949
"Source_phase_imports_are_only_supported_when_the_module_option_is_set_to_esnext_nodenext_or_preserve_18113": "Source phase imports are only supported when the '--module' option is set to 'esnext', 'nodenext', or 'preserve'.",
19501950
"Optional_chaining_cannot_be_used_with_import_source_18114": "Optional chaining cannot be used with 'import.source'.",
19511951
"Source_phase_imports_are_not_allowed_on_statements_that_compile_to_CommonJS_require_calls_18115": "Source phase imports are not allowed on statements that compile to CommonJS 'require' calls.",
1952+
"A_deferred_import_must_specify_a_namespace_binding_18116": "A deferred import must specify a namespace binding.",
19521953
"nodenext_if_module_is_nodenext_node16_if_module_is_node16_or_node18_otherwise_bundler_69010": "`nodenext` if `module` is `nodenext`; `node16` if `module` is `node16` or `node18`; otherwise, `bundler`.",
19531954
"File_is_a_CommonJS_module_it_may_be_converted_to_an_ES_module_80001": "File is a CommonJS module; it may be converted to an ES module.",
19541955
"This_constructor_function_may_be_converted_to_a_class_declaration_80002": "This constructor function may be converted to a class declaration.",

‎tsc/internal/diagnostics/diagnosticMessages.json‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8915,5 +8915,9 @@
89158915
"Source phase imports are not allowed on statements that compile to CommonJS 'require' calls.": {
89168916
"category": "Error",
89178917
"code": 18115
8918+
},
8919+
"A deferred import must specify a namespace binding.": {
8920+
"category": "Error",
8921+
"code": 18116
89188922
}
89198923
}

‎tsc/internal/diagnostics/diagnostics_generated.go‎

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎tsc/internal/parser/parser.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2409,7 +2409,7 @@ func (p *Parser) tryParseImportClause(identifier *ast.Node, pos int, phaseModifi
24092409
p.parseExpected(ast.KindFromKeyword)
24102410
return importClause
24112411
}
2412-
if phaseModifier == ast.KindSourceKeyword {
2412+
if phaseModifier == ast.KindDeferKeyword || phaseModifier == ast.KindSourceKeyword {
24132413
return p.finishNode(p.factory.NewImportClause(phaseModifier, nil /*name*/, nil /*namedBindings*/), pos)
24142414
}
24152415
return nil

‎tsc/internal/transformers/moduletransforms/externalmoduleinfo.go‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,5 @@ func getImportNeedsImportStarHelper(node *ast.ImportDeclaration) bool {
385385
func getImportNeedsImportDefaultHelper(node *ast.ImportDeclaration) bool {
386386
// Import default is needed if there's a default import or a default ref and no other refs (meaning an import star helper wasn't requested)
387387
return !getImportNeedsImportStarHelper(node) && (ast.IsDefaultImport(node.AsNode()) || (node.ImportClause != nil &&
388-
ast.IsNamedImports(node.ImportClause.AsImportClause().NamedBindings) &&
389388
containsDefaultReference(node.ImportClause.AsImportClause().NamedBindings)))
390389
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
b.ts(1,8): error TS18058: Default imports are not allowed in a deferred import.
2+
b.ts(5,8): error TS18116: A deferred import must specify a namespace binding.
3+
b.ts(6,8): error TS18116: A deferred import must specify a namespace binding.
4+
b.ts(6,23): error TS2823: Import attributes are only supported when the '--module' option is set to 'esnext', 'node18', 'node20', 'nodenext', or 'preserve'.
5+
c.js(2,8): error TS18116: A deferred import must specify a namespace binding.
6+
c.js(3,8): error TS18116: A deferred import must specify a namespace binding.
7+
c.js(3,23): error TS2823: Import attributes are only supported when the '--module' option is set to 'esnext', 'node18', 'node20', 'nodenext', or 'preserve'.
8+
d.js(1,8): error TS18116: A deferred import must specify a namespace binding.
9+
10+
11+
==== a.ts (0 errors) ====
12+
export default function foo() {
13+
console.log("foo from a");
14+
}
15+
16+
==== b.ts (4 errors) ====
17+
import defer foo from "./a";
18+
~~~~~~~~~
19+
!!! error TS18058: Default imports are not allowed in a deferred import.
20+
21+
foo();
22+
23+
import defer "./a.js";
24+
~~~~~
25+
!!! error TS18116: A deferred import must specify a namespace binding.
26+
import defer "./a.js" with { type: "json" };
27+
~~~~~
28+
!!! error TS18116: A deferred import must specify a namespace binding.
29+
~~~~~~~~~~~~~~~~~~~~~
30+
!!! error TS2823: Import attributes are only supported when the '--module' option is set to 'esnext', 'node18', 'node20', 'nodenext', or 'preserve'.
31+
32+
==== c.js (3 errors) ====
33+
// @ts-check
34+
import defer "./a.js";
35+
~~~~~
36+
!!! error TS18116: A deferred import must specify a namespace binding.
37+
import defer "./a.js" with { type: "json" };
38+
~~~~~
39+
!!! error TS18116: A deferred import must specify a namespace binding.
40+
~~~~~~~~~~~~~~~~~~~~~
41+
!!! error TS2823: Import attributes are only supported when the '--module' option is set to 'esnext', 'node18', 'node20', 'nodenext', or 'preserve'.
42+
43+
==== d.js (1 errors) ====
44+
import defer "./a.js";
45+
~~~~~
46+
!!! error TS18116: A deferred import must specify a namespace binding.
47+
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//// [tests/cases/conformance/importDefer/importDeferInvalidDefault.ts] ////
2+
3+
//// [a.ts]
4+
export default function foo() {
5+
console.log("foo from a");
6+
}
7+
8+
//// [b.ts]
9+
import defer foo from "./a";
10+
11+
foo();
12+
13+
import defer "./a.js";
14+
import defer "./a.js" with { type: "json" };
15+
16+
//// [c.js]
17+
// @ts-check
18+
import defer "./a.js";
19+
import defer "./a.js" with { type: "json" };
20+
21+
//// [d.js]
22+
import defer "./a.js";
23+
24+
25+
//// [a.js]
26+
"use strict";
27+
Object.defineProperty(exports, "__esModule", { value: true });
28+
exports.default = foo;
29+
function foo() {
30+
console.log("foo from a");
31+
}
32+
//// [b.js]
33+
"use strict";
34+
var __importDefault = (this && this.__importDefault) || function (mod) {
35+
return (mod && mod.__esModule) ? mod : { "default": mod };
36+
};
37+
Object.defineProperty(exports, "__esModule", { value: true });
38+
const a_1 = __importDefault(require("./a"));
39+
(0, a_1.default)();
40+
//// [c.js]
41+
"use strict";
42+
Object.defineProperty(exports, "__esModule", { value: true });
43+
// @ts-check
44+
const a_js_1 = require("./a.js");
45+
const a_js_2 = require("./a.js");
46+
//// [d.js]
47+
"use strict";
48+
Object.defineProperty(exports, "__esModule", { value: true });
49+
const a_js_1 = require("./a.js");

tsc/testdata/baselines/reference/conformance/importDeferInvalidDefault.symbols renamed to tsc/testdata/baselines/reference/conformance/importDeferInvalidDefault(module=commonjs).symbols

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,16 @@ import defer foo from "./a";
1717
foo();
1818
>foo : Symbol(foo, Decl(b.ts, 0, 6))
1919

20+
import defer "./a.js";
21+
import defer "./a.js" with { type: "json" };
22+
23+
=== c.js ===
24+
25+
// @ts-check
26+
import defer "./a.js";
27+
import defer "./a.js" with { type: "json" };
28+
29+
=== d.js ===
30+
31+
import defer "./a.js";
32+

0 commit comments

Comments
 (0)