Skip to content

Commit

Permalink
add external imports to metafile
Browse files Browse the repository at this point in the history
fix #905
fix #1933
fix #1939
  • Loading branch information
evanw committed Dec 14, 2022
1 parent 7fcfe73 commit f2f78ef
Show file tree
Hide file tree
Showing 9 changed files with 254 additions and 29 deletions.
73 changes: 73 additions & 0 deletions CHANGELOG.md
Expand Up @@ -16,6 +16,79 @@
* Leading and trailing `.` such as `0.` and `.0`
* Numbers with a space after the `-` such as `- 1`

* Add external imports to metafile ([#905](https://github.com/evanw/esbuild/issues/905), [#1933](https://github.com/evanw/esbuild/issues/1933), [#1939](https://github.com/evanw/esbuild/issues/1939))

External imports now appear in `imports` arrays in the metafile (which is present when bundling with `metafile: true`) next to normal imports, but additionally have `external: true` to set them apart. This applies both to files in the `inputs` section and the `outputs` section. Here's an example:

```diff
{
"inputs": {
"style.css": {
"bytes": 83,
"imports": [
+ {
+ "path": "https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css",
+ "kind": "import-rule",
+ "external": true
+ }
]
},
"app.js": {
"bytes": 100,
"imports": [
+ {
+ "path": "https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.min.js",
+ "kind": "import-statement",
+ "external": true
+ },
{
"path": "style.css",
"kind": "import-statement"
}
]
}
},
"outputs": {
"out/app.js": {
"imports": [
+ {
+ "path": "https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.min.js",
+ "kind": "require-call",
+ "external": true
+ }
],
"exports": [],
"entryPoint": "app.js",
"cssBundle": "out/app.css",
"inputs": {
"app.js": {
"bytesInOutput": 113
},
"style.css": {
"bytesInOutput": 0
}
},
"bytes": 528
},
"out/app.css": {
"imports": [
+ {
+ "path": "https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css",
+ "kind": "import-rule",
+ "external": true
+ }
],
"inputs": {
"style.css": {
"bytesInOutput": 0
}
},
"bytes": 100
}
}
}
```

## 0.16.5

* Make it easy to exclude all packages from a bundle ([#1958](https://github.com/evanw/esbuild/issues/1958), [#1975](https://github.com/evanw/esbuild/issues/1975), [#2164](https://github.com/evanw/esbuild/issues/2164), [#2246](https://github.com/evanw/esbuild/issues/2246), [#2542](https://github.com/evanw/esbuild/issues/2542))
Expand Down
3 changes: 3 additions & 0 deletions internal/ast/ast.go
Expand Up @@ -116,6 +116,9 @@ const (

// If true, "assert { type: 'json' }" was present
AssertTypeJSON

// If true, do not generate "external": true in the metafile
ShouldNotBeExternalInMetafile
)

func (flags ImportRecordFlags) Has(flag ImportRecordFlags) bool {
Expand Down
11 changes: 11 additions & 0 deletions internal/bundler/bundler.go
Expand Up @@ -1819,6 +1819,17 @@ func (s *scanner) processScannedFiles(entryPointMeta []graph.EntryPoint) []scann
// Skip this import record if the previous resolver call failed
resolveResult := result.resolveResults[importRecordIndex]
if resolveResult == nil || !record.SourceIndex.IsValid() {
if s.options.NeedsMetafile {
if isFirstImport {
isFirstImport = false
sb.WriteString("\n ")
} else {
sb.WriteString(",\n ")
}
sb.WriteString(fmt.Sprintf("{\n \"path\": %s,\n \"kind\": %s,\n \"external\": true\n }",
helpers.QuoteForJSON(record.Path.Text, s.options.ASCIIOnly),
helpers.QuoteForJSON(record.Kind.StringForMetafile(), s.options.ASCIIOnly)))
}
continue
}

Expand Down
54 changes: 42 additions & 12 deletions internal/bundler/linker.go
Expand Up @@ -874,6 +874,7 @@ func (c *linkerContext) computeCrossChunkDependencies(chunks []chunkInfo) {
otherChunkIndex := c.graph.Files[record.SourceIndex.GetIndex()].EntryPointChunkIndex
record.Path.Text = chunks[otherChunkIndex].uniqueKey
record.SourceIndex = ast.Index32{}
record.Flags |= ast.ShouldNotBeExternalInMetafile

// Track this cross-chunk dynamic import so we make sure to
// include its hash when we're calculating the hashes of all
Expand Down Expand Up @@ -1228,6 +1229,7 @@ func (c *linkerContext) scanImportsAndExports() {
record.Path.Text = otherRepr.AST.URLForCSS
record.Path.Namespace = ""
record.SourceIndex = ast.Index32{}
record.Flags |= ast.ShouldNotBeExternalInMetafile

// Copy the additional files to the output directory
additionalFiles = append(additionalFiles, otherFile.InputFile.AdditionalFiles...)
Expand All @@ -1238,6 +1240,7 @@ func (c *linkerContext) scanImportsAndExports() {
record.Path.Text = otherRepr.URLForCode
record.Path.Namespace = ""
record.CopySourceIndex = ast.Index32{}
record.Flags |= ast.ShouldNotBeExternalInMetafile

// Copy the additional files to the output directory
additionalFiles = append(additionalFiles, otherFile.InputFile.AdditionalFiles...)
Expand All @@ -1255,6 +1258,7 @@ func (c *linkerContext) scanImportsAndExports() {
record.Path.Text = otherRepr.URLForCode
record.Path.Namespace = ""
record.CopySourceIndex = ast.Index32{}
record.Flags |= ast.ShouldNotBeExternalInMetafile

// Copy the additional files to the output directory
additionalFiles = append(additionalFiles, otherFile.InputFile.AdditionalFiles...)
Expand Down Expand Up @@ -4209,6 +4213,7 @@ func (c *linkerContext) generateCodeForFileInChunkJS(
LineOffsetTables: lineOffsetTables,
RequireOrImportMetaForSource: c.requireOrImportMetaForSource,
MangledProps: c.mangledProps,
NeedsMetafile: c.options.NeedsMetafile,
}
tree := repr.AST
tree.Directive = "" // This is handled elsewhere
Expand Down Expand Up @@ -4826,6 +4831,7 @@ func (c *linkerContext) generateChunkJS(chunks []chunkInfo, chunkIndex int, chun
// Also generate the cross-chunk binding code
var crossChunkPrefix []byte
var crossChunkSuffix []byte
var jsonMetadataImports []string
{
// Indent the file if everything is wrapped in an IIFE
indent := 0
Expand All @@ -4838,18 +4844,22 @@ func (c *linkerContext) generateChunkJS(chunks []chunkInfo, chunkIndex int, chun
MinifyIdentifiers: c.options.MinifyIdentifiers,
MinifyWhitespace: c.options.MinifyWhitespace,
MinifySyntax: c.options.MinifySyntax,
NeedsMetafile: c.options.NeedsMetafile,
}
crossChunkImportRecords := make([]ast.ImportRecord, len(chunk.crossChunkImports))
for i, chunkImport := range chunk.crossChunkImports {
crossChunkImportRecords[i] = ast.ImportRecord{
Kind: chunkImport.importKind,
Path: logger.Path{Text: chunks[chunkImport.chunkIndex].uniqueKey},
Kind: chunkImport.importKind,
Path: logger.Path{Text: chunks[chunkImport.chunkIndex].uniqueKey},
Flags: ast.ShouldNotBeExternalInMetafile,
}
}
crossChunkPrefix = js_printer.Print(js_ast.AST{
crossChunkResult := js_printer.Print(js_ast.AST{
ImportRecords: crossChunkImportRecords,
Parts: []js_ast.Part{{Stmts: chunkRepr.crossChunkPrefixStmts}},
}, c.graph.Symbols, r, printOptions).JS
}, c.graph.Symbols, r, printOptions)
crossChunkPrefix = crossChunkResult.JS
jsonMetadataImports = crossChunkResult.JSONMetadataImports
crossChunkSuffix = js_printer.Print(js_ast.AST{
Parts: []js_ast.Part{{Stmts: chunkRepr.crossChunkSuffixStmts}},
}, c.graph.Symbols, r, printOptions).JS
Expand Down Expand Up @@ -4949,15 +4959,23 @@ func (c *linkerContext) generateChunkJS(chunks []chunkInfo, chunkIndex int, chun
// Print imports
isFirstMeta := true
jMeta.AddString("{\n \"imports\": [")
for _, chunkImport := range chunk.crossChunkImports {
for _, json := range jsonMetadataImports {
if isFirstMeta {
isFirstMeta = false
} else {
jMeta.AddString(",")
}
jMeta.AddString(fmt.Sprintf("\n {\n \"path\": %s,\n \"kind\": %s\n }",
helpers.QuoteForJSON(c.res.PrettyPath(logger.Path{Text: chunks[chunkImport.chunkIndex].uniqueKey, Namespace: "file"}), c.options.ASCIIOnly),
helpers.QuoteForJSON(chunkImport.importKind.StringForMetafile(), c.options.ASCIIOnly)))
jMeta.AddString(json)
}
for _, compileResult := range compileResults {
for _, json := range compileResult.JSONMetadataImports {
if isFirstMeta {
isFirstMeta = false
} else {
jMeta.AddString(",")
}
jMeta.AddString(json)
}
}
if !isFirstMeta {
jMeta.AddString("\n ")
Expand Down Expand Up @@ -5330,6 +5348,7 @@ func (c *linkerContext) generateChunkCSS(chunks []chunkInfo, chunkIndex int, chu
AddSourceMappings: addSourceMappings,
InputSourceMap: inputSourceMap,
LineOffsetTables: lineOffsetTables,
NeedsMetafile: c.options.NeedsMetafile,
}
compileResult.PrintResult = css_printer.Print(asts[i], cssOptions)
compileResult.sourceIndex = sourceIndex
Expand All @@ -5352,6 +5371,7 @@ func (c *linkerContext) generateChunkCSS(chunks []chunkInfo, chunkIndex int, chu
}

// Generate any prefix rules now
var jsonMetadataImports []string
{
tree := css_ast.AST{}

Expand Down Expand Up @@ -5383,7 +5403,9 @@ func (c *linkerContext) generateChunkCSS(chunks []chunkInfo, chunkIndex int, chu
result := css_printer.Print(tree, css_printer.Options{
MinifyWhitespace: c.options.MinifyWhitespace,
ASCIIOnly: c.options.ASCIIOnly,
NeedsMetafile: c.options.NeedsMetafile,
})
jsonMetadataImports = result.JSONMetadataImports
if len(result.CSS) > 0 {
prevOffset.AdvanceBytes(result.CSS)
j.AddBytes(result.CSS)
Expand All @@ -5397,15 +5419,23 @@ func (c *linkerContext) generateChunkCSS(chunks []chunkInfo, chunkIndex int, chu
if c.options.NeedsMetafile {
isFirstMeta := true
jMeta.AddString("{\n \"imports\": [")
for _, chunkImport := range chunk.crossChunkImports {
for _, json := range jsonMetadataImports {
if isFirstMeta {
isFirstMeta = false
} else {
jMeta.AddString(",")
}
jMeta.AddString(fmt.Sprintf("\n {\n \"path\": %s,\n \"kind\": %s\n }",
helpers.QuoteForJSON(c.res.PrettyPath(logger.Path{Text: chunks[chunkImport.chunkIndex].uniqueKey, Namespace: "file"}), c.options.ASCIIOnly),
helpers.QuoteForJSON(chunkImport.importKind.StringForMetafile(), c.options.ASCIIOnly)))
jMeta.AddString(json)
}
for _, compileResult := range compileResults {
for _, json := range compileResult.JSONMetadataImports {
if isFirstMeta {
isFirstMeta = false
} else {
jMeta.AddString(",")
}
jMeta.AddString(json)
}
}
if !isFirstMeta {
jMeta.AddString("\n ")
Expand Down
65 changes: 61 additions & 4 deletions internal/bundler/snapshots/snapshots_loader.txt
Expand Up @@ -1087,6 +1087,11 @@ d {
"project/entry.js": {
"bytes": 333,
"imports": [
{
"path": "extern-esm",
"kind": "import-statement",
"external": true
},
{
"path": "project/esm.js",
"kind": "import-statement"
Expand All @@ -1103,6 +1108,11 @@ d {
"path": "project/copy.copy",
"kind": "import-statement"
},
{
"path": "extern-cjs",
"kind": "require-call",
"external": true
},
{
"path": "project/cjs.js",
"kind": "require-call"
Expand All @@ -1120,6 +1130,11 @@ d {
"project/entry.css": {
"bytes": 180,
"imports": [
{
"path": "extern.css",
"kind": "import-rule",
"external": true
},
{
"path": "project/inline.svg",
"kind": "url-token"
Expand All @@ -1131,6 +1146,11 @@ d {
{
"path": "project/copy.copy",
"kind": "url-token"
},
{
"path": "extern.png",
"kind": "url-token",
"external": true
}
]
}
Expand Down Expand Up @@ -1159,12 +1179,26 @@ d {
"out/entry.js": {
"imports": [
{
"path": "out/dynamic-4QVDQQPM.js",
"kind": "dynamic-import"
"path": "out/chunk-3MN5TIYV.js",
"kind": "import-statement"
},
{
"path": "out/chunk-3MN5TIYV.js",
"path": "extern-esm",
"kind": "import-statement",
"external": true
},
{
"path": "out/copy-O3Y5SCJE.copy",
"kind": "import-statement"
},
{
"path": "extern-cjs",
"kind": "require-call",
"external": true
},
{
"path": "out/dynamic-4QVDQQPM.js",
"kind": "dynamic-import"
}
],
"exports": [
Expand Down Expand Up @@ -1218,7 +1252,30 @@ d {
"bytes": 38
},
"out/entry.css": {
"imports": [],
"imports": [
{
"path": "extern.css",
"kind": "import-rule",
"external": true
},
{
"path": "data:image/svg+xml,<svg/>",
"kind": "url-token"
},
{
"path": "out/file-NVISQQTV.file",
"kind": "url-token"
},
{
"path": "out/copy-O3Y5SCJE.copy",
"kind": "url-token"
},
{
"path": "extern.png",
"kind": "url-token",
"external": true
}
],
"entryPoint": "project/entry.css",
"inputs": {
"project/entry.css": {
Expand Down

0 comments on commit f2f78ef

Please sign in to comment.