Skip to content

Commit 22956af

Browse files
committed
use the file namespace for file entry points
1 parent 3702817 commit 22956af

File tree

3 files changed

+67
-22
lines changed

3 files changed

+67
-22
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,12 @@
102102

103103
Previously esbuild would determine the output path for an entry point by looking at the post-resolved path. For example, running `esbuild --bundle react --outdir=out` would generate the output path `out/index.js` because the input path `react` was resolved to `node_modules/react/index.js`. With this release, the output path is now determined by looking at the pre-resolved path. For example, For example, running `esbuild --bundle react --outdir=out` now generates the output path `out/react.js`. If you need to keep using the output path that esbuild previously generated with the old behavior, you can use the custom output path feature (described above).
104104

105+
* Use the `file` namespace for file entry points ([#791](https://github.com/evanw/esbuild/issues/791))
106+
107+
Plugins that contain an `onResolve` callback with the `file` filter don't apply to entry point paths because it's not clear that entry point paths are files. For example, you could potentially bundle an entry point of `https://www.example.com/file.js` with a HTTP plugin that automatically downloads data from the server at that URL. But this behavior can be unexpected for people writing plugins.
108+
109+
With this release, esbuild will do a quick check first to see if the entry point path exists on the file system before running plugins. If it exists as a file, the namespace will now be `file` for that entry point path. This only checks the exact entry point name and doesn't attempt to search for the file, so for example it won't handle cases where you pass a package path as an entry point or where you pass an entry point without an extension. Hopefully this should help improve this situation in the common case where the entry point is an exact path.
110+
105111
## Unreleased
106112

107113
* Warn about mutation of private methods ([#1067](https://github.com/evanw/esbuild/pull/1067))

internal/bundler/bundler.go

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ func parseFile(args parseArgs) {
463463
&args.caches.FSCache,
464464
&source,
465465
record.Range,
466+
source.KeyPath.Namespace,
466467
record.Path.Text,
467468
record.Kind,
468469
absResolveDir,
@@ -698,6 +699,7 @@ func runOnResolvePlugins(
698699
fsCache *cache.FSCache,
699700
importSource *logger.Source,
700701
importPathRange logger.Range,
702+
importNamespace string,
701703
path string,
702704
kind ast.ImportKind,
703705
absResolveDir string,
@@ -709,10 +711,14 @@ func runOnResolvePlugins(
709711
Kind: kind,
710712
PluginData: pluginData,
711713
}
712-
applyPath := logger.Path{Text: path}
714+
applyPath := logger.Path{
715+
Text: path,
716+
Namespace: importNamespace,
717+
}
713718
if importSource != nil {
714719
resolverArgs.Importer = importSource.KeyPath
715-
applyPath.Namespace = importSource.KeyPath.Namespace
720+
} else {
721+
resolverArgs.Importer.Namespace = importNamespace
716722
}
717723

718724
// Apply resolver plugins in order until one succeeds
@@ -975,6 +981,7 @@ type scanner struct {
975981
type EntryPoint struct {
976982
InputPath string
977983
OutputPath string
984+
IsFile bool
978985
}
979986

980987
func ScanBundle(
@@ -1263,27 +1270,34 @@ func (s *scanner) addEntryPoints(entryPoints []EntryPoint) []entryMeta {
12631270
})
12641271
}
12651272

1266-
// Entry point paths without a leading "./" are interpreted as package
1267-
// paths. This happens because they go through general path resolution
1268-
// like all other import paths so that plugins can run on them. Requiring
1269-
// a leading "./" for a relative path simplifies writing plugins because
1270-
// entry points aren't a special case.
1271-
//
1272-
// However, requiring a leading "./" also breaks backward compatibility
1273-
// and makes working with the CLI more difficult. So attempt to insert
1274-
// "./" automatically when needed. We don't want to unconditionally insert
1275-
// a leading "./" because the path may not be a file system path. For
1276-
// example, it may be a URL. So only insert a leading "./" when the path
1277-
// is an exact match for an existing file.
1273+
// Check each entry point ahead of time to see if it's a real file
12781274
entryPointAbsResolveDir := s.fs.Cwd()
1279-
for i, entryPoint := range entryPoints {
1280-
if !s.fs.IsAbs(entryPoint.InputPath) && resolver.IsPackagePath(entryPoint.InputPath) {
1281-
absPath := s.fs.Join(entryPointAbsResolveDir, entryPoint.InputPath)
1282-
dir := s.fs.Dir(absPath)
1283-
base := s.fs.Base(absPath)
1284-
if entries, err := s.fs.ReadDirectory(dir); err == nil {
1285-
if entry, _ := entries.Get(base); entry != nil && entry.Kind(s.fs) == fs.FileEntry {
1286-
entryPoints[i].InputPath = "./" + entryPoint.InputPath
1275+
for i := range entryPoints {
1276+
entryPoint := &entryPoints[i]
1277+
absPath := entryPoint.InputPath
1278+
if !s.fs.IsAbs(absPath) {
1279+
absPath = s.fs.Join(entryPointAbsResolveDir, absPath)
1280+
}
1281+
dir := s.fs.Dir(absPath)
1282+
base := s.fs.Base(absPath)
1283+
if entries, err := s.fs.ReadDirectory(dir); err == nil {
1284+
if entry, _ := entries.Get(base); entry != nil && entry.Kind(s.fs) == fs.FileEntry {
1285+
entryPoint.IsFile = true
1286+
1287+
// Entry point paths without a leading "./" are interpreted as package
1288+
// paths. This happens because they go through general path resolution
1289+
// like all other import paths so that plugins can run on them. Requiring
1290+
// a leading "./" for a relative path simplifies writing plugins because
1291+
// entry points aren't a special case.
1292+
//
1293+
// However, requiring a leading "./" also breaks backward compatibility
1294+
// and makes working with the CLI more difficult. So attempt to insert
1295+
// "./" automatically when needed. We don't want to unconditionally insert
1296+
// a leading "./" because the path may not be a file system path. For
1297+
// example, it may be a URL. So only insert a leading "./" when the path
1298+
// is an exact match for an existing file.
1299+
if !s.fs.IsAbs(entryPoint.InputPath) && resolver.IsPackagePath(entryPoint.InputPath) {
1300+
entryPoint.InputPath = "./" + entryPoint.InputPath
12871301
}
12881302
}
12891303
}
@@ -1297,6 +1311,11 @@ func (s *scanner) addEntryPoints(entryPoints []EntryPoint) []entryMeta {
12971311
entryPointWaitGroup.Add(len(entryPoints))
12981312
for i, entryPoint := range entryPoints {
12991313
go func(i int, entryPoint EntryPoint) {
1314+
namespace := ""
1315+
if entryPoint.IsFile {
1316+
namespace = "file"
1317+
}
1318+
13001319
// Run the resolver and log an error if the path couldn't be resolved
13011320
resolveResult, didLogError, debug := runOnResolvePlugins(
13021321
s.options.Plugins,
@@ -1306,6 +1325,7 @@ func (s *scanner) addEntryPoints(entryPoints []EntryPoint) []entryMeta {
13061325
&s.caches.FSCache,
13071326
nil,
13081327
logger.Range{},
1328+
namespace,
13091329
entryPoint.InputPath,
13101330
ast.ImportEntryPoint,
13111331
entryPointAbsResolveDir,

scripts/plugin-tests.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,25 @@ let pluginTests = {
863863
assert.strictEqual(result.outputFiles[3].text, `// virtual-ns:input a/b/c.d.e\nconsole.log("input a/b/c.d.e");\n`)
864864
},
865865

866+
async entryPointFileNamespace({ esbuild, testDir }) {
867+
const input = path.join(testDir, 'in.js')
868+
let worked = false
869+
await writeFileAsync(input, 'stuff')
870+
await esbuild.build({
871+
entryPoints: [input],
872+
write: false,
873+
plugins: [{
874+
name: 'name',
875+
setup(build) {
876+
build.onResolve({ filter: /.*/, namespace: 'file' }, () => {
877+
worked = true
878+
})
879+
},
880+
}],
881+
})
882+
assert(worked)
883+
},
884+
866885
async stdinImporter({ esbuild, testDir }) {
867886
const output = path.join(testDir, 'out.js')
868887
await esbuild.build({

0 commit comments

Comments
 (0)