Skip to content

Commit

Permalink
fix #1404: "file" loader always copies to "outdir"
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Jul 1, 2021
1 parent 67f61ba commit 963ce60
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 45 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

* Fix the `file` loader with custom namespaces ([#1404](https:/evanw/esbuild/issues/1404))

This fixes a regression from version 0.12.12 where using a plugin to load an input file with the `file` loader in a custom namespace caused esbuild to write the contents of that input file to the path associated with that namespace instead of to a path inside of the output directory. With this release, the `file` loader should now always copy the file somewhere inside of the output directory.

## 0.12.13

* Fix using JS synchronous API from from non-main threads ([#1406](https:/evanw/esbuild/issues/1406))
Expand Down
95 changes: 53 additions & 42 deletions internal/bundler/bundler.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,28 +288,8 @@ func parseFile(args parseArgs) {
result.file.inputFile.Repr = &graph.JSRepr{AST: ast}
result.ok = true

// Optionally add metadata about the file
var jsonMetadataChunk string
if args.options.NeedsMetafile {
inputs := fmt.Sprintf("{\n %s: {\n \"bytesInOutput\": %d\n }\n }",
js_printer.QuoteForJSON(source.PrettyPath, args.options.ASCIIOnly),
len(source.Contents),
)
jsonMetadataChunk = fmt.Sprintf(
"{\n \"imports\": [],\n \"exports\": [],\n \"inputs\": %s,\n \"bytes\": %d\n }",
inputs,
len(source.Contents),
)
}

// Copy the file using an additional file payload to make sure we only copy
// the file if the module isn't removed due to tree shaking.
result.file.inputFile.UniqueKey = uniqueKey
result.file.inputFile.AdditionalFiles = []graph.OutputFile{{
AbsPath: source.KeyPath.Text,
Contents: []byte(source.Contents),
JSONMetadataChunk: jsonMetadataChunk,
}}
// Mark that this file is from the "file" loader
result.file.inputFile.UniqueKeyForFileLoader = uniqueKey

default:
var message string
Expand Down Expand Up @@ -1753,31 +1733,62 @@ func (s *scanner) processScannedFiles() []scannerFile {
sb.WriteString("]\n }")
}

// Turn all additional file paths from input paths into output paths by
// rewriting the output base directory to the output directory
for j, additionalFile := range result.file.inputFile.AdditionalFiles {
if relPath, ok := s.fs.Rel(s.options.AbsOutputBase, additionalFile.AbsPath); ok {
var hash string
result.file.jsonMetadataChunk = sb.String()

// Add a hash to the file name to prevent multiple files with the same name
// but different contents from colliding
if config.HasPlaceholder(s.options.AssetPathTemplate, config.HashPlaceholder) {
h := xxhash.New()
h.Write(additionalFile.Contents)
hash = hashForFileName(h.Sum(nil))
}
// If this file is from the "file" loader, generate an additional file
if result.file.inputFile.UniqueKeyForFileLoader != "" {
bytes := []byte(result.file.inputFile.Source.Contents)

dir, base, ext := logger.PlatformIndependentPathDirBaseExt(relPath)
relPath = config.TemplateToString(config.SubstituteTemplate(s.options.AssetPathTemplate, config.PathPlaceholders{
Dir: &dir,
Name: &base,
Hash: &hash,
})) + ext
result.file.inputFile.AdditionalFiles[j].AbsPath = s.fs.Join(s.options.AbsOutputDir, relPath)
// Add a hash to the file name to prevent multiple files with the same name
// but different contents from colliding
var hash string
if config.HasPlaceholder(s.options.AssetPathTemplate, config.HashPlaceholder) {
h := xxhash.New()
h.Write(bytes)
hash = hashForFileName(h.Sum(nil))
}

// Generate the input for the template
_, _, originalExt := logger.PlatformIndependentPathDirBaseExt(result.file.inputFile.Source.KeyPath.Text)
dir, base, ext := pathRelativeToOutbase(
&result.file.inputFile,
&s.options,
s.fs,
originalExt,
/* avoidIndex */ false,
/* customFilePath */ "",
)

// Apply the asset path template
relPath := config.TemplateToString(config.SubstituteTemplate(s.options.AssetPathTemplate, config.PathPlaceholders{
Dir: &dir,
Name: &base,
Hash: &hash,
})) + ext

// Optionally add metadata about the file
var jsonMetadataChunk string
if s.options.NeedsMetafile {
inputs := fmt.Sprintf("{\n %s: {\n \"bytesInOutput\": %d\n }\n }",
js_printer.QuoteForJSON(result.file.inputFile.Source.PrettyPath, s.options.ASCIIOnly),
len(bytes),
)
jsonMetadataChunk = fmt.Sprintf(
"{\n \"imports\": [],\n \"exports\": [],\n \"inputs\": %s,\n \"bytes\": %d\n }",
inputs,
len(bytes),
)
}

// Generate the additional file to copy into the output directory
result.file.inputFile.AdditionalFiles = []graph.OutputFile{{
AbsPath: s.fs.Join(s.options.AbsOutputDir, relPath),
Contents: bytes,
JSONMetadataChunk: jsonMetadataChunk,
}}
}

s.results[i].file.jsonMetadataChunk = sb.String()
s.results[i] = result
}

// The linker operates on an array of files, so construct that now. This
Expand Down
2 changes: 1 addition & 1 deletion internal/bundler/linker.go
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ func (c *linkerContext) substituteFinalPaths(

importPath := modifyPath(relPath)
j.AddString(importPath)
shift.Before.AdvanceString(file.InputFile.UniqueKey)
shift.Before.AdvanceString(file.InputFile.UniqueKeyForFileLoader)
shift.After.AdvanceString(importPath)
shifts = append(shifts, shift)

Expand Down
4 changes: 2 additions & 2 deletions internal/graph/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ type InputFile struct {
// If this file ends up being used in the bundle, these are additional files
// that must be written to the output directory. It's used by the "file"
// loader.
AdditionalFiles []OutputFile
UniqueKey string
AdditionalFiles []OutputFile
UniqueKeyForFileLoader string

SideEffects SideEffects
Loader config.Loader
Expand Down
33 changes: 33 additions & 0 deletions scripts/plugin-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -2004,6 +2004,39 @@ let pluginTests = {
}],
})
},

async fileLoaderCustomNamespaceIssue1404({ esbuild, testDir }) {
const input = path.join(testDir, 'in.data')
const outdir = path.join(testDir, 'out')
await writeFileAsync(input, `some data`)
await esbuild.build({
entryPoints: [path.basename(input)],
absWorkingDir: testDir,
logLevel: 'silent',
outdir,
assetNames: '[name]',
plugins: [{
name: 'plugin',
setup(build) {
build.onResolve({ filter: /\.data$/ }, args => {
return {
path: args.path,
namespace: 'ns',
}
})
build.onLoad({ filter: /.*/, namespace: 'ns' }, async (args) => {
const data = await readFileAsync(path.join(testDir, args.path), 'utf8')
return {
contents: data.split('').reverse().join(''),
loader: 'file',
}
})
},
}],
})
assert.strictEqual(await readFileAsync(input, 'utf8'), `some data`)
assert.strictEqual(require(path.join(outdir, 'in.js')), `./in.data`)
},
}

// These tests have to run synchronously
Expand Down

0 comments on commit 963ce60

Please sign in to comment.