Skip to content

Commit 5069410

Browse files
committed
fix #3778: import assertions/attributes for node
1 parent 11d568c commit 5069410

File tree

6 files changed

+72
-4
lines changed

6 files changed

+72
-4
lines changed

CHANGELOG.md

+17
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,23 @@
22

33
## Unreleased
44

5+
* Update support for import assertions and import attributes in node ([#3778](https://github.com/evanw/esbuild/issues/3778))
6+
7+
Import assertions (the `assert` keyword) have been removed from node starting in v22.0.0. So esbuild will now strip them and generate a warning with `--target=node22` or above:
8+
9+
```
10+
▲ [WARNING] The "assert" keyword is not supported in the configured target environment ("node22") [assert-to-with]
11+
12+
example.mjs:1:40:
13+
1 │ import json from "esbuild/package.json" assert { type: "json" }
14+
│ ~~~~~~
15+
╵ with
16+
17+
Did you mean to use "with" instead of "assert"?
18+
```
19+
20+
Import attributes (the `with` keyword) have been backported to node 18 starting in v18.20.0. So esbuild will no longer strip them with `--target=node18.N` if `N` is 20 or greater.
21+
522
* Fix `for await` transform when a label is present
623
724
This release fixes a bug where the `for await` transform, which wraps the loop in a `try` statement, previously failed to also move the loop's label into the `try` statement. This bug only affects code that uses both of these features in combination. Here's an example of some affected code:

compat-table/src/index.ts

+17-2
Original file line numberDiff line numberDiff line change
@@ -477,14 +477,29 @@ import('./kangax').then(kangax => {
477477

478478
// Import assertions (note: these were removed from the JavaScript specification and never standardized)
479479
{
480-
// From https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V16.md#16.14.0
481-
js.ImportAssertions.Node = { '16.14': { force: true } }
480+
js.ImportAssertions.Node = {
481+
// From https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V16.md#16.14.0
482+
'16.14': { force: true },
483+
484+
// Manually tested using a binary from https://nodejs.org/en/download/prebuilt-binaries
485+
'22': { force: false },
486+
}
482487

483488
// MDN data is wrong here: https://bugs.webkit.org/show_bug.cgi?id=251600
484489
delete js.ImportAssertions.IOS
485490
delete js.ImportAssertions.Safari
486491
}
487492

493+
// Import attributes (the replacement for import assertions)
494+
{
495+
// Manually tested using binaries from https://nodejs.org/en/download/prebuilt-binaries
496+
js.ImportAttributes.Node = {
497+
'18.20': { force: true },
498+
'19': { force: false },
499+
'20.10': { force: true },
500+
}
501+
}
502+
488503
// MDN data is wrong here: https://www.chromestatus.com/feature/6482797915013120
489504
js.ClassStaticBlocks.Chrome = { 91: { force: true } }
490505

internal/compat/js_table.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -584,14 +584,14 @@ var jsTable = map[JSFeature]map[Engine][]versionRange{
584584
Chrome: {{start: v{91, 0, 0}}},
585585
Deno: {{start: v{1, 17, 0}}},
586586
Edge: {{start: v{91, 0, 0}}},
587-
Node: {{start: v{16, 14, 0}}},
587+
Node: {{start: v{16, 14, 0}, end: v{22, 0, 0}}},
588588
},
589589
ImportAttributes: {
590590
Chrome: {{start: v{123, 0, 0}}},
591591
Deno: {{start: v{1, 37, 0}}},
592592
Edge: {{start: v{123, 0, 0}}},
593593
IOS: {{start: v{17, 2, 0}}},
594-
Node: {{start: v{20, 10, 0}}},
594+
Node: {{start: v{18, 20, 0}, end: v{19, 0, 0}}, {start: v{20, 10, 0}}},
595595
Opera: {{start: v{109, 0, 0}}},
596596
Safari: {{start: v{17, 2, 0}}},
597597
},

internal/js_parser/js_parser.go

+20
Original file line numberDiff line numberDiff line change
@@ -6480,6 +6480,9 @@ func (p *parser) parsePath() (logger.Range, string, *ast.ImportAssertOrWith, ast
64806480

64816481
closeBraceLoc := p.saveExprCommentsHere()
64826482
p.lexer.Expect(js_lexer.TCloseBrace)
6483+
if keyword == ast.AssertKeyword {
6484+
p.maybeWarnAboutAssertKeyword(keywordLoc)
6485+
}
64836486
assertOrWith = &ast.ImportAssertOrWith{
64846487
Entries: entries,
64856488
Keyword: keyword,
@@ -6492,6 +6495,20 @@ func (p *parser) parsePath() (logger.Range, string, *ast.ImportAssertOrWith, ast
64926495
return pathRange, pathText, assertOrWith, flags
64936496
}
64946497

6498+
// Let people know if they probably should be using "with" instead of "assert"
6499+
func (p *parser) maybeWarnAboutAssertKeyword(loc logger.Loc) {
6500+
if p.options.unsupportedJSFeatures.Has(compat.ImportAssertions) && !p.options.unsupportedJSFeatures.Has(compat.ImportAttributes) {
6501+
where := config.PrettyPrintTargetEnvironment(p.options.originalTargetEnv, p.options.unsupportedJSFeatureOverridesMask)
6502+
msg := logger.Msg{
6503+
Kind: logger.Warning,
6504+
Data: p.tracker.MsgData(js_lexer.RangeOfIdentifier(p.source, loc), "The \"assert\" keyword is not supported in "+where),
6505+
Notes: []logger.MsgData{{Text: "Did you mean to use \"with\" instead of \"assert\"?"}},
6506+
}
6507+
msg.Data.Location.Suggestion = "with"
6508+
p.log.AddMsgID(logger.MsgID_JS_AssertToWith, msg)
6509+
}
6510+
}
6511+
64956512
// This assumes the "function" token has already been parsed
64966513
func (p *parser) parseFnStmt(loc logger.Loc, opts parseStmtOpts, isAsync bool, asyncRange logger.Range) js_ast.Stmt {
64976514
isGenerator := p.lexer.Token == js_lexer.TAsterisk
@@ -14277,6 +14294,9 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO
1427714294
break
1427814295
}
1427914296
if entries != nil {
14297+
if keyword == ast.AssertKeyword {
14298+
p.maybeWarnAboutAssertKeyword(prop.Key.Loc)
14299+
}
1428014300
assertOrWith = &ast.ImportAssertOrWith{
1428114301
Entries: entries,
1428214302
Keyword: keyword,

internal/js_parser/js_parser_test.go

+11
Original file line numberDiff line numberDiff line change
@@ -6229,6 +6229,17 @@ func TestImportAttributes(t *testing.T) {
62296229
expectPrintedWithUnsupportedFeatures(t, compat.ImportAssertions|compat.ImportAttributes,
62306230
"import 'x' with {y: 'z'}; import('x', {with: {y: 'z'}})",
62316231
"import \"x\";\nimport(\"x\");\n")
6232+
6233+
// Test the migration warning
6234+
expectParseErrorWithUnsupportedFeatures(t, compat.ImportAssertions,
6235+
"import x from 'y' assert {type: 'json'}",
6236+
"<stdin>: WARNING: The \"assert\" keyword is not supported in the configured target environment\nNOTE: Did you mean to use \"with\" instead of \"assert\"?\n")
6237+
expectParseErrorWithUnsupportedFeatures(t, compat.ImportAssertions,
6238+
"export {default} from 'y' assert {type: 'json'}",
6239+
"<stdin>: WARNING: The \"assert\" keyword is not supported in the configured target environment\nNOTE: Did you mean to use \"with\" instead of \"assert\"?\n")
6240+
expectParseErrorWithUnsupportedFeatures(t, compat.ImportAssertions,
6241+
"import('y', {assert: {type: 'json'}})",
6242+
"<stdin>: WARNING: The \"assert\" keyword is not supported in the configured target environment\nNOTE: Did you mean to use \"with\" instead of \"assert\"?\n")
62326243
}
62336244

62346245
func TestES5(t *testing.T) {

internal/logger/msg_ids.go

+5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const (
1212
MsgID_None MsgID = iota
1313

1414
// JavaScript
15+
MsgID_JS_AssertToWith
1516
MsgID_JS_AssertTypeJSON
1617
MsgID_JS_AssignToConstant
1718
MsgID_JS_AssignToDefine
@@ -96,6 +97,8 @@ const (
9697
func StringToMsgIDs(str string, logLevel LogLevel, overrides map[MsgID]LogLevel) {
9798
switch str {
9899
// JS
100+
case "assert-to-with":
101+
overrides[MsgID_JS_AssertToWith] = logLevel
99102
case "assert-type-json":
100103
overrides[MsgID_JS_AssertTypeJSON] = logLevel
101104
case "assign-to-constant":
@@ -226,6 +229,8 @@ func StringToMsgIDs(str string, logLevel LogLevel, overrides map[MsgID]LogLevel)
226229
func MsgIDToString(id MsgID) string {
227230
switch id {
228231
// JS
232+
case MsgID_JS_AssertToWith:
233+
return "assert-to-with"
229234
case MsgID_JS_AssertTypeJSON:
230235
return "assert-type-json"
231236
case MsgID_JS_AssignToConstant:

0 commit comments

Comments
 (0)