Skip to content

Commit ec42001

Browse files
fix: allow destructured require under module preserve + verbatimModuleSyntax
checkAliasSymbol already treated bare `const x = require(...)` as valid CommonJS under `--module preserve` by excluding VariableDeclaration from the TS1293 ESM-syntax check, but object-destructuring aliases (`const { x } = require(...)`) are BindingElements and still errored. Exclude BindingElement the same way so CJS require destructuring is allowed while real ESM import/export syntax in .cjs/.cts files remains an error. Fixes #63696
1 parent b465fdb commit ec42001

5 files changed

Lines changed: 180 additions & 0 deletions

File tree

src/compiler/checker.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48526,6 +48526,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4852648526
moduleKind === ModuleKind.Preserve &&
4852748527
node.kind !== SyntaxKind.ImportEqualsDeclaration &&
4852848528
node.kind !== SyntaxKind.VariableDeclaration &&
48529+
// `const { x } = require(...)` is CommonJS alias syntax (BindingElement), not ESM.
48530+
node.kind !== SyntaxKind.BindingElement &&
4852948531
host.getEmitModuleFormatOfFile(getSourceFileOfNode(node)) === ModuleKind.CommonJS
4853048532
) {
4853148533
// In `--module preserve`, ESM input syntax emits ESM output syntax, but there will be times
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/file.cjs(11,10): error TS1293: ECMAScript module syntax is not allowed in a CommonJS module when 'module' is set to 'preserve'.
2+
3+
4+
==== /file.cjs (1 errors) ====
5+
// Destructured and whole-module require are valid CommonJS and must not error under
6+
// --module preserve + --verbatimModuleSyntax (https://github.com/microsoft/TypeScript/issues/63696).
7+
const { x } = require("./mod");
8+
const { y: renamed } = require("./mod");
9+
const whole = require("./mod");
10+
x;
11+
renamed;
12+
whole.y;
13+
14+
// True ESM syntax in a .cjs file remains an error.
15+
import { x as x2 } from "./mod";
16+
~~~~~~~
17+
!!! error TS1293: ECMAScript module syntax is not allowed in a CommonJS module when 'module' is set to 'preserve'.
18+
19+
==== /mod.js (0 errors) ====
20+
exports.x = 1;
21+
exports.y = 2;
22+
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//// [tests/cases/compiler/modulePreserveDestructuredRequire.ts] ////
2+
3+
=== /file.cjs ===
4+
// Destructured and whole-module require are valid CommonJS and must not error under
5+
// --module preserve + --verbatimModuleSyntax (https://github.com/microsoft/TypeScript/issues/63696).
6+
const { x } = require("./mod");
7+
>x : Symbol(x, Decl(file.cjs, 2, 7))
8+
>require : Symbol(require)
9+
>"./mod" : Symbol(whole, Decl(mod.js, 0, 0))
10+
11+
const { y: renamed } = require("./mod");
12+
>y : Symbol(renamed, Decl(mod.js, 0, 14))
13+
>renamed : Symbol(renamed, Decl(file.cjs, 3, 7))
14+
>require : Symbol(require)
15+
>"./mod" : Symbol(whole, Decl(mod.js, 0, 0))
16+
17+
const whole = require("./mod");
18+
>whole : Symbol(whole, Decl(file.cjs, 4, 5))
19+
>require : Symbol(require)
20+
>"./mod" : Symbol(whole, Decl(mod.js, 0, 0))
21+
22+
x;
23+
>x : Symbol(x, Decl(file.cjs, 2, 7))
24+
25+
renamed;
26+
>renamed : Symbol(renamed, Decl(file.cjs, 3, 7))
27+
28+
whole.y;
29+
>whole.y : Symbol(renamed, Decl(mod.js, 0, 14))
30+
>whole : Symbol(whole, Decl(file.cjs, 4, 5))
31+
>y : Symbol(renamed, Decl(mod.js, 0, 14))
32+
33+
// True ESM syntax in a .cjs file remains an error.
34+
import { x as x2 } from "./mod";
35+
>x : Symbol(x, Decl(mod.js, 0, 0))
36+
>x2 : Symbol(x2, Decl(file.cjs, 10, 8))
37+
38+
=== /mod.js ===
39+
exports.x = 1;
40+
>exports.x : Symbol(x, Decl(mod.js, 0, 0))
41+
>exports : Symbol(x, Decl(mod.js, 0, 0))
42+
>x : Symbol(x, Decl(mod.js, 0, 0))
43+
44+
exports.y = 2;
45+
>exports.y : Symbol(y, Decl(mod.js, 0, 14))
46+
>exports : Symbol(y, Decl(mod.js, 0, 14))
47+
>y : Symbol(y, Decl(mod.js, 0, 14))
48+
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//// [tests/cases/compiler/modulePreserveDestructuredRequire.ts] ////
2+
3+
=== /file.cjs ===
4+
// Destructured and whole-module require are valid CommonJS and must not error under
5+
// --module preserve + --verbatimModuleSyntax (https://github.com/microsoft/TypeScript/issues/63696).
6+
const { x } = require("./mod");
7+
>x : 1
8+
> : ^
9+
>require("./mod") : typeof whole
10+
> : ^^^^^^^^^^^^
11+
>require : any
12+
> : ^^^
13+
>"./mod" : "./mod"
14+
> : ^^^^^^^
15+
16+
const { y: renamed } = require("./mod");
17+
>y : any
18+
> : ^^^
19+
>renamed : 2
20+
> : ^
21+
>require("./mod") : typeof whole
22+
> : ^^^^^^^^^^^^
23+
>require : any
24+
> : ^^^
25+
>"./mod" : "./mod"
26+
> : ^^^^^^^
27+
28+
const whole = require("./mod");
29+
>whole : typeof whole
30+
> : ^^^^^^^^^^^^
31+
>require("./mod") : typeof whole
32+
> : ^^^^^^^^^^^^
33+
>require : any
34+
> : ^^^
35+
>"./mod" : "./mod"
36+
> : ^^^^^^^
37+
38+
x;
39+
>x : 1
40+
> : ^
41+
42+
renamed;
43+
>renamed : 2
44+
> : ^
45+
46+
whole.y;
47+
>whole.y : 2
48+
> : ^
49+
>whole : typeof whole
50+
> : ^^^^^^^^^^^^
51+
>y : 2
52+
> : ^
53+
54+
// True ESM syntax in a .cjs file remains an error.
55+
import { x as x2 } from "./mod";
56+
>x : 1
57+
> : ^
58+
>x2 : 1
59+
> : ^
60+
61+
=== /mod.js ===
62+
exports.x = 1;
63+
>exports.x = 1 : 1
64+
> : ^
65+
>exports.x : 1
66+
> : ^
67+
>exports : typeof import("./mod")
68+
> : ^^^^^^^^^^^^^^^^^^^^^^
69+
>x : 1
70+
> : ^
71+
>1 : 1
72+
> : ^
73+
74+
exports.y = 2;
75+
>exports.y = 2 : 2
76+
> : ^
77+
>exports.y : 2
78+
> : ^
79+
>exports : typeof import("./mod")
80+
> : ^^^^^^^^^^^^^^^^^^^^^^
81+
>y : 2
82+
> : ^
83+
>2 : 2
84+
> : ^
85+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// @module: preserve
2+
// @target: esnext
3+
// @verbatimModuleSyntax: true
4+
// @checkJs: true
5+
// @noEmit: true
6+
// @strict: true
7+
8+
// @Filename: /mod.js
9+
exports.x = 1;
10+
exports.y = 2;
11+
12+
// @Filename: /file.cjs
13+
// Destructured and whole-module require are valid CommonJS and must not error under
14+
// --module preserve + --verbatimModuleSyntax (https://github.com/microsoft/TypeScript/issues/63696).
15+
const { x } = require("./mod");
16+
const { y: renamed } = require("./mod");
17+
const whole = require("./mod");
18+
x;
19+
renamed;
20+
whole.y;
21+
22+
// True ESM syntax in a .cjs file remains an error.
23+
import { x as x2 } from "./mod";

0 commit comments

Comments
 (0)