Skip to content

Commit

Permalink
fix #655: add "kind" to imports in metafile
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Jan 14, 2021
1 parent 5dfa5f0 commit 4c997fc
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 31 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,22 @@

Certain import path metadata in the JSON file generated by the `--metafile` setting could be incorrect in scenarios with code splitting active and multiple entry points in different subdirectories. The incorrect paths referred to cross-chunk imports of other generated code splitting chunks and were incorrectly relative to the subdirectory inside the output directory instead of relative to the output directory itself. This issue has been fixed.

* Add `kind` to import paths in metafile JSON ([#655](https://github.com/evanw/esbuild/issues/655))

The `--metafile` flag generates build metadata in JSON format describing the input and output files in the build. Previously import path objects only had a `path` property. With this release, they now also have a `kind` property that describes the way the file was imported. The value is a string that is equal to one of the following values:

For JavaScript files:

* `import-statement`
* `require-call`
* `dynamic-import`
* `require-resolve`

For CSS files:

* `import-rule`
* `url-token`

* Add support for TypeScript 4.2 syntax

Most of the new features included in the [TypeScript 4.2 beta announcement](https://devblogs.microsoft.com/typescript/announcing-typescript-4-2-beta/) are type system features that don't apply to esbuild. But there's one upcoming feature that adds new syntax: `abstract` construct signatures. They look like this:
Expand Down
21 changes: 21 additions & 0 deletions internal/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,27 @@ const (
ImportEntryPoint
)

func (kind ImportKind) StringForMetafile() string {
switch kind {
case ImportStmt:
return "import-statement"
case ImportRequire:
return "require-call"
case ImportDynamic:
return "dynamic-import"
case ImportRequireResolve:
return "require-resolve"
case ImportAt:
return "import-rule"
case ImportURL:
return "url-token"
case ImportEntryPoint:
return "entry-point"
default:
panic("Internal error")
}
}

func (kind ImportKind) IsFromCSS() bool {
return kind == ImportAt || kind == ImportURL
}
Expand Down
5 changes: 3 additions & 2 deletions internal/bundler/bundler.go
Original file line number Diff line number Diff line change
Expand Up @@ -1246,8 +1246,9 @@ func (s *scanner) processScannedFiles() []file {
} else {
j.AddString(",\n ")
}
j.AddString(fmt.Sprintf("{\n \"path\": %s\n }",
js_printer.QuoteForJSON(s.results[*record.SourceIndex].file.source.PrettyPath, s.options.ASCIIOnly)))
j.AddString(fmt.Sprintf("{\n \"path\": %s,\n \"kind\": %s\n }",
js_printer.QuoteForJSON(s.results[*record.SourceIndex].file.source.PrettyPath, s.options.ASCIIOnly),
js_printer.QuoteForJSON(record.Kind.StringForMetafile(), s.options.ASCIIOnly)))
}

// Importing a JavaScript file from a CSS file is not allowed.
Expand Down
14 changes: 8 additions & 6 deletions internal/bundler/linker.go
Original file line number Diff line number Diff line change
Expand Up @@ -3644,14 +3644,15 @@ func (repr *chunkReprJS) generate(c *linkerContext, chunk *chunkInfo) func(gener
// Print imports
isFirstMeta := true
jMeta.AddString("{\n \"imports\": [")
for _, importAbsPath := range continueData.crossChunkAbsPaths {
for i, importAbsPath := range continueData.crossChunkAbsPaths {
if isFirstMeta {
isFirstMeta = false
} else {
jMeta.AddString(",")
}
jMeta.AddString(fmt.Sprintf("\n {\n \"path\": %s\n }",
js_printer.QuoteForJSON(c.res.PrettyPath(logger.Path{Text: importAbsPath, Namespace: "file"}), c.options.ASCIIOnly)))
jMeta.AddString(fmt.Sprintf("\n {\n \"path\": %s,\n \"kind\": %s\n }",
js_printer.QuoteForJSON(c.res.PrettyPath(logger.Path{Text: importAbsPath, Namespace: "file"}), c.options.ASCIIOnly),
js_printer.QuoteForJSON(continueData.crossChunkImportRecords[i].Kind.StringForMetafile(), c.options.ASCIIOnly)))
}
if !isFirstMeta {
jMeta.AddString("\n ")
Expand Down Expand Up @@ -4052,14 +4053,15 @@ func (repr *chunkReprCSS) generate(c *linkerContext, chunk *chunkInfo) func(gene
if c.options.AbsMetadataFile != "" {
isFirstMeta := true
jMeta.AddString("{\n \"imports\": [")
for _, importAbsPath := range continueData.crossChunkAbsPaths {
for i, importAbsPath := range continueData.crossChunkAbsPaths {
if isFirstMeta {
isFirstMeta = false
} else {
jMeta.AddString(",")
}
jMeta.AddString(fmt.Sprintf("\n {\n \"path\": %s\n }",
js_printer.QuoteForJSON(c.res.PrettyPath(logger.Path{Text: importAbsPath, Namespace: "file"}), c.options.ASCIIOnly)))
jMeta.AddString(fmt.Sprintf("\n {\n \"path\": %s,\n \"kind\": %s\n }",
js_printer.QuoteForJSON(c.res.PrettyPath(logger.Path{Text: importAbsPath, Namespace: "file"}), c.options.ASCIIOnly),
js_printer.QuoteForJSON(continueData.crossChunkImportRecords[i].Kind.StringForMetafile(), c.options.ASCIIOnly)))
}
if !isFirstMeta {
jMeta.AddString("\n ")
Expand Down
13 changes: 13 additions & 0 deletions lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,13 +216,25 @@ export interface PartialMessage {
detail?: any;
}

export type MetadataImportKind =
// JS
| 'import-statement'
| 'require-call'
| 'dynamic-import'
| 'require-resolve'

// CSS
| 'import-rule'
| 'url-token'

// This is the type information for the "metafile" JSON format
export interface Metadata {
inputs: {
[path: string]: {
bytes: number
imports: {
path: string
kind: MetadataImportKind
}[]
}
}
Expand All @@ -236,6 +248,7 @@ export interface Metadata {
}
imports: {
path: string
kind: MetadataImportKind
}[]
exports: string[]
}
Expand Down
68 changes: 45 additions & 23 deletions scripts/js-api-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ body {
const meta = path.join(testDir, 'meta.json')
await writeFileAsync(entry, `
import x from "./imported"
import y from "./text.txt"
const y = require("./text.txt")
import * as z from "./example.css"
console.log(x, y, z)
`)
Expand All @@ -427,11 +427,11 @@ body {
const makePath = absPath => path.relative(cwd, absPath).split(path.sep).join('/')

// Check inputs
assert.deepStrictEqual(json.inputs[makePath(entry)].bytes, 139)
assert.deepStrictEqual(json.inputs[makePath(entry)].bytes, 144)
assert.deepStrictEqual(json.inputs[makePath(entry)].imports, [
{ path: makePath(imported) },
{ path: makePath(text) },
{ path: makePath(css) },
{ path: makePath(imported), kind: 'import-statement' },
{ path: makePath(css), kind: 'import-statement' },
{ path: makePath(text), kind: 'require-call' },
])
assert.deepStrictEqual(json.inputs[makePath(imported)].bytes, 18)
assert.deepStrictEqual(json.inputs[makePath(imported)].imports, [])
Expand Down Expand Up @@ -501,12 +501,12 @@ body {
const outEntry2 = makeOutPath(path.basename(entry2));
const outChunk = makeOutPath(chunk);

assert.deepStrictEqual(json.inputs[inEntry1], { bytes: 94, imports: [{ path: inImported }] })
assert.deepStrictEqual(json.inputs[inEntry2], { bytes: 107, imports: [{ path: inImported }] })
assert.deepStrictEqual(json.inputs[inEntry1], { bytes: 94, imports: [{ path: inImported, kind: 'import-statement' }] })
assert.deepStrictEqual(json.inputs[inEntry2], { bytes: 107, imports: [{ path: inImported, kind: 'import-statement' }] })
assert.deepStrictEqual(json.inputs[inImported], { bytes: 118, imports: [] })

assert.deepStrictEqual(json.outputs[outEntry1].imports, [{ path: makeOutPath(chunk) }])
assert.deepStrictEqual(json.outputs[outEntry2].imports, [{ path: makeOutPath(chunk) }])
assert.deepStrictEqual(json.outputs[outEntry1].imports, [{ path: makeOutPath(chunk), kind: 'import-statement' }])
assert.deepStrictEqual(json.outputs[outEntry2].imports, [{ path: makeOutPath(chunk), kind: 'import-statement' }])
assert.deepStrictEqual(json.outputs[outChunk].imports, [])

assert.deepStrictEqual(json.outputs[outEntry1].exports, ['x'])
Expand Down Expand Up @@ -566,12 +566,12 @@ body {
const outEntry2 = makeOutPath(path.basename(entry2));
const outChunk = makeOutPath(chunk);

assert.deepStrictEqual(json.inputs[inEntry1], { bytes: 94, imports: [{ path: inImported }] })
assert.deepStrictEqual(json.inputs[inEntry2], { bytes: 107, imports: [{ path: inImported }] })
assert.deepStrictEqual(json.inputs[inEntry1], { bytes: 94, imports: [{ path: inImported, kind: 'import-statement' }] })
assert.deepStrictEqual(json.inputs[inEntry2], { bytes: 107, imports: [{ path: inImported, kind: 'import-statement' }] })
assert.deepStrictEqual(json.inputs[inImported], { bytes: 118, imports: [] })

assert.deepStrictEqual(json.outputs[outEntry1].imports, [{ path: makeOutPath(chunk) }])
assert.deepStrictEqual(json.outputs[outEntry2].imports, [{ path: makeOutPath(chunk) }])
assert.deepStrictEqual(json.outputs[outEntry1].imports, [{ path: makeOutPath(chunk), kind: 'import-statement' }])
assert.deepStrictEqual(json.outputs[outEntry2].imports, [{ path: makeOutPath(chunk), kind: 'import-statement' }])
assert.deepStrictEqual(json.outputs[outChunk].imports, [])

assert.deepStrictEqual(json.outputs[outEntry1].exports, ['x'])
Expand Down Expand Up @@ -634,14 +634,31 @@ body {
const outImport2 = makeOutPath(path.relative(testDir, import2));
const outChunk = makeOutPath(chunk);

assert.deepStrictEqual(json.inputs[inEntry], { bytes: 112, imports: [{ path: inShared }, { path: inImport1 }, { path: inImport2 }] })
assert.deepStrictEqual(json.inputs[inImport1], { bytes: 35, imports: [{ path: inShared }] })
assert.deepStrictEqual(json.inputs[inImport2], { bytes: 35, imports: [{ path: inShared }] })
assert.deepStrictEqual(json.inputs[inEntry], {
bytes: 112,
imports: [
{ path: inShared, kind: 'import-statement' },
{ path: inImport1, kind: 'dynamic-import' },
{ path: inImport2, kind: 'dynamic-import' },
]
})
assert.deepStrictEqual(json.inputs[inImport1], {
bytes: 35,
imports: [
{ path: inShared, kind: 'import-statement' },
]
})
assert.deepStrictEqual(json.inputs[inImport2], {
bytes: 35,
imports: [
{ path: inShared, kind: 'import-statement' },
]
})
assert.deepStrictEqual(json.inputs[inShared], { bytes: 38, imports: [] })

assert.deepStrictEqual(json.outputs[outEntry].imports, [{ path: makeOutPath(chunk) }])
assert.deepStrictEqual(json.outputs[outImport1].imports, [{ path: makeOutPath(chunk) }])
assert.deepStrictEqual(json.outputs[outImport2].imports, [{ path: makeOutPath(chunk) }])
assert.deepStrictEqual(json.outputs[outEntry].imports, [{ path: makeOutPath(chunk), kind: 'import-statement' }])
assert.deepStrictEqual(json.outputs[outImport1].imports, [{ path: makeOutPath(chunk), kind: 'import-statement' }])
assert.deepStrictEqual(json.outputs[outImport2].imports, [{ path: makeOutPath(chunk), kind: 'import-statement' }])
assert.deepStrictEqual(json.outputs[outChunk].imports, [])

assert.deepStrictEqual(json.outputs[outEntry].exports, [])
Expand Down Expand Up @@ -803,8 +820,13 @@ body {
const cwd = process.cwd()
const makePath = pathname => path.relative(cwd, pathname).split(path.sep).join('/')
const json = JSON.parse(await readFileAsync(metafile))
assert.deepStrictEqual(json.inputs[makePath(entry)].imports, [{ path: makePath(nested1) }, { path: makePath(nested2) }])
assert.deepStrictEqual(json.inputs[makePath(nested1)].imports, [{ path: makePath(nested3) }])
assert.deepStrictEqual(json.inputs[makePath(entry)].imports, [
{ path: makePath(nested1), kind: 'import-statement' },
{ path: makePath(nested2), kind: 'import-statement' },
])
assert.deepStrictEqual(json.inputs[makePath(nested1)].imports, [
{ path: makePath(nested3), kind: 'import-statement' },
])
assert.deepStrictEqual(json.inputs[makePath(nested2)].imports, [])
assert.deepStrictEqual(json.inputs[makePath(nested3)].imports, [])
assert.deepStrictEqual(json.outputs[makePath(outfile)].imports, [])
Expand Down Expand Up @@ -843,9 +865,9 @@ body {
// Check inputs
assert.deepStrictEqual(json, {
inputs: {
[makePath(entry)]: { bytes: 98, imports: [{ path: makePath(imported) }] },
[makePath(entry)]: { bytes: 98, imports: [{ path: makePath(imported), kind: 'import-rule' }] },
[makePath(image)]: { bytes: 8, imports: [] },
[makePath(imported)]: { bytes: 48, imports: [{ path: makePath(image) }] },
[makePath(imported)]: { bytes: 48, imports: [{ path: makePath(image), kind: 'url-token' }] },
},
outputs: {
[makePath(output)]: {
Expand Down

0 comments on commit 4c997fc

Please sign in to comment.