Skip to content

Commit

Permalink
preserve comments in import assertions too
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Jan 4, 2023
1 parent f8311c4 commit 98bd7c3
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 27 deletions.
11 changes: 10 additions & 1 deletion internal/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func (flags ImportRecordFlags) Has(flag ImportRecordFlags) bool {
}

type ImportRecord struct {
Assertions *[]AssertEntry
Assertions *ImportAssertions
Path logger.Path
Range logger.Range

Expand All @@ -149,6 +149,15 @@ type ImportRecord struct {
Kind ImportKind
}

type ImportAssertions struct {
Entries []AssertEntry
AssertLoc logger.Loc
InnerOpenBraceLoc logger.Loc
InnerCloseBraceLoc logger.Loc
OuterOpenBraceLoc logger.Loc
OuterCloseBraceLoc logger.Loc
}

type AssertEntry struct {
Key []uint16 // An identifier or a string
Value []uint16 // Always a string
Expand Down
2 changes: 1 addition & 1 deletion internal/bundler/bundler.go
Original file line number Diff line number Diff line change
Expand Up @@ -1929,7 +1929,7 @@ func (s *scanner) processScannedFiles(entryPointMeta []graph.EntryPoint) []scann
s.log.AddErrorWithNotes(&tracker, record.Range,
fmt.Sprintf("The file %q was loaded with the %q loader", otherFile.inputFile.Source.PrettyPath, config.LoaderToString[otherFile.inputFile.Loader]),
[]logger.MsgData{
tracker.MsgData(js_lexer.RangeOfImportAssertion(result.file.inputFile.Source, *ast.FindAssertion(*record.Assertions, "type")),
tracker.MsgData(js_lexer.RangeOfImportAssertion(result.file.inputFile.Source, *ast.FindAssertion(record.Assertions.Entries, "type")),
"This import assertion requires the loader to be \"json\" instead:"),
{Text: "You need to either reconfigure esbuild to ensure that the loader for this file is \"json\" or you need to remove this import assertion."}})
}
Expand Down
35 changes: 35 additions & 0 deletions internal/bundler_tests/bundler_default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7560,6 +7560,17 @@ func TestCommentPreservation(t *testing.T) {
import('foo' /* after */),
)
console.log(
import('foo', /* before */ { assert: { type: 'json' } }),
import('foo', { /* before */ assert: { type: 'json' } }),
import('foo', { assert: /* before */ { type: 'json' } }),
import('foo', { assert: { /* before */ type: 'json' } }),
import('foo', { assert: { type: /* before */ 'json' } }),
import('foo', { assert: { type: 'json' /* before */ } }),
import('foo', { assert: { type: 'json' } /* before */ }),
import('foo', { assert: { type: 'json' } } /* before */),
)
console.log(
require(/* before */ foo),
require(/* before */ 'foo'),
Expand Down Expand Up @@ -7689,6 +7700,30 @@ func TestCommentPreservation(t *testing.T) {
})
}

func TestCommentPreservationImportAssertions(t *testing.T) {
default_suite.expectBundled(t, bundled{
files: map[string]string{
"/entry.jsx": `
import 'foo' /* before */ assert { type: 'json' }
import 'foo' assert /* before */ { type: 'json' }
import 'foo' assert { /* before */ type: 'json' }
import 'foo' assert { type: /* before */ 'json' }
import 'foo' assert { type: 'json' /* before */ }
`,
},
entryPaths: []string{"/entry.jsx"},
options: config.Options{
Mode: config.ModeBundle,
AbsOutputDir: "/out",
ExternalSettings: config.ExternalSettings{
PreResolve: config.ExternalMatchers{
Exact: map[string]bool{"foo": true},
},
},
},
})
}

func TestCommentPreservationTransformJSX(t *testing.T) {
default_suite.expectBundled(t, bundled{
files: map[string]string{
Expand Down
58 changes: 58 additions & 0 deletions internal/bundler_tests/snapshots/snapshots_default.txt
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,44 @@ console.log(
/* after */
)
);
console.log(
import(
"foo",
/* before */
{ assert: { type: "json" } }
),
import("foo", {
/* before */
assert: { type: "json" }
}),
import("foo", {
assert:
/* before */
{ type: "json" }
}),
import("foo", { assert: {
/* before */
type: "json"
} }),
import("foo", { assert: {
type:
/* before */
"json"
} }),
import("foo", { assert: {
type: "json"
/* before */
} }),
import("foo", {
assert: { type: "json" }
/* before */
}),
import(
"foo",
{ assert: { type: "json" } }
/* before */
)
);
console.log(
require(
/* before */
Expand Down Expand Up @@ -559,6 +597,26 @@ console.log(
a ? b : c
);

================================================================================
TestCommentPreservationImportAssertions
---------- /out/entry.js ----------
// entry.jsx
import "foo" assert { type: "json" };
import "foo" assert { type: "json" };
import "foo" assert {
/* before */
type: "json"
};
import "foo" assert {
type:
/* before */
"json"
};
import "foo" assert {
type: "json"
/* before */
};

================================================================================
TestCommentPreservationPreserveJSX
---------- /out/entry.js ----------
Expand Down
35 changes: 25 additions & 10 deletions internal/js_parser/js_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -1858,7 +1858,7 @@ func (p *parser) checkForLegacyOctalLiteral(e js_ast.E) {

func (p *parser) notesForAssertTypeJSON(record *ast.ImportRecord, alias string) []logger.MsgData {
return []logger.MsgData{p.tracker.MsgData(
js_lexer.RangeOfImportAssertion(p.source, *ast.FindAssertion(*record.Assertions, "type")),
js_lexer.RangeOfImportAssertion(p.source, *ast.FindAssertion(record.Assertions.Entries, "type")),
"This is considered an import of a standard JSON module because of the import assertion here:"),
{Text: fmt.Sprintf("You can either keep the import assertion and only use the \"default\" import, "+
"or you can remove the import assertion and use the %q import (which is non-standard behavior).", alias)}}
Expand Down Expand Up @@ -5951,7 +5951,7 @@ func (p *parser) parseLabelName() *js_ast.LocRef {
return &name
}

func (p *parser) parsePath() (logger.Loc, string, *[]ast.AssertEntry, ast.ImportRecordFlags) {
func (p *parser) parsePath() (logger.Loc, string, *ast.ImportAssertions, ast.ImportRecordFlags) {
var flags ast.ImportRecordFlags
pathLoc := p.lexer.Loc()
pathText := helpers.UTF16ToString(p.lexer.StringLiteral())
Expand All @@ -5962,17 +5962,19 @@ func (p *parser) parsePath() (logger.Loc, string, *[]ast.AssertEntry, ast.Import
}

// See https://github.com/tc39/proposal-import-assertions for more info
var assertions *[]ast.AssertEntry
var assertions *ast.ImportAssertions
if !p.lexer.HasNewlineBefore && p.lexer.IsContextualKeyword("assert") {
// "import './foo.json' assert { type: 'json' }"
var entries []ast.AssertEntry
duplicates := make(map[string]logger.Range)
assertLoc := p.saveExprCommentsHere()
p.lexer.Next()
openBraceLoc := p.saveExprCommentsHere()
p.lexer.Expect(js_lexer.TOpenBrace)

for p.lexer.Token != js_lexer.TCloseBrace {
// Parse the key
keyLoc := p.lexer.Loc()
keyLoc := p.saveExprCommentsHere()
preferQuotedKey := false
var key []uint16
var keyText string
Expand All @@ -5995,7 +5997,7 @@ func (p *parser) parsePath() (logger.Loc, string, *[]ast.AssertEntry, ast.Import
p.lexer.Expect(js_lexer.TColon)

// Parse the value
valueLoc := p.lexer.Loc()
valueLoc := p.saveExprCommentsHere()
value := p.lexer.StringLiteral()
p.lexer.Expect(js_lexer.TStringLiteral)

Expand All @@ -6018,8 +6020,14 @@ func (p *parser) parsePath() (logger.Loc, string, *[]ast.AssertEntry, ast.Import
p.lexer.Next()
}

closeBraceLoc := p.saveExprCommentsHere()
p.lexer.Expect(js_lexer.TCloseBrace)
assertions = &entries
assertions = &ast.ImportAssertions{
Entries: entries,
AssertLoc: assertLoc,
InnerOpenBraceLoc: openBraceLoc,
InnerCloseBraceLoc: closeBraceLoc,
}
}

return pathLoc, pathText, assertions, flags
Expand Down Expand Up @@ -6413,7 +6421,7 @@ func (p *parser) parseStmt(opts parseStmtOpts) js_ast.Stmt {
var alias *js_ast.ExportStarAlias
var pathLoc logger.Loc
var pathText string
var assertions *[]ast.AssertEntry
var assertions *ast.ImportAssertions
var flags ast.ImportRecordFlags

if p.lexer.IsContextualKeyword("as") {
Expand Down Expand Up @@ -7359,7 +7367,7 @@ func extractDeclsForBinding(binding js_ast.Binding, decls []js_ast.Decl) []js_as
return decls
}

func (p *parser) addImportRecord(kind ast.ImportKind, loc logger.Loc, text string, assertions *[]ast.AssertEntry, flags ast.ImportRecordFlags) uint32 {
func (p *parser) addImportRecord(kind ast.ImportKind, loc logger.Loc, text string, assertions *ast.ImportAssertions, flags ast.ImportRecordFlags) uint32 {
index := uint32(len(p.importRecords))
p.importRecords = append(p.importRecords, ast.ImportRecord{
Kind: kind,
Expand Down Expand Up @@ -13540,7 +13548,7 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO
isThenCatchTarget := e == p.thenCatchChain.nextTarget && p.thenCatchChain.hasCatch
e.Expr = p.visitExpr(e.Expr)

var assertions *[]ast.AssertEntry
var assertions *ast.ImportAssertions
var flags ast.ImportRecordFlags
if e.OptionsOrNil.Data != nil {
e.OptionsOrNil = p.visitExpr(e.OptionsOrNil)
Expand Down Expand Up @@ -13593,7 +13601,14 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO
break
}
if entries != nil {
assertions = &entries
assertions = &ast.ImportAssertions{
Entries: entries,
AssertLoc: prop.Key.Loc,
InnerOpenBraceLoc: prop.ValueOrNil.Loc,
InnerCloseBraceLoc: value.CloseBraceLoc,
OuterOpenBraceLoc: e.OptionsOrNil.Loc,
OuterCloseBraceLoc: object.CloseBraceLoc,
}
why = ""
}
} else {
Expand Down
Loading

0 comments on commit 98bd7c3

Please sign in to comment.