From 7bf440d1a8bc839afee0f6f2ed492b5b231b60fc Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Wed, 14 Dec 2022 12:17:06 -0500 Subject: [PATCH] fix #2730: alias now strips a trailing slash --- CHANGELOG.md | 4 ++++ internal/bundler/bundler.go | 2 +- internal/bundler/bundler_default_test.go | 3 +++ internal/bundler/snapshots/snapshots_loader.txt | 3 +++ internal/resolver/resolver.go | 16 +++++++++++++++- 5 files changed, 26 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7822e1f2b9d..d970c586b6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,10 @@ } ``` +* The alias feature now strips a trailing slash ([#2730](https://github.com/evanw/esbuild/issues/2730)) + + People sometimes add a trailing slash to the name of one of node's built-in modules to force node to import from the file system instead of importing the built-in module. For example, importing `util` imports node's built-in module called `util` but importing `util/` tries to find a package called `util` on the file system. Previously attempting to use esbuild's package alias feature to replace imports to `util` with a specific file would fail because the file path would also gain a trailing slash (e.g. mapping `util` to `./file.js` turned `util/` into `./file.js/`). With this release, esbuild will now omit the path suffix if it's a single trailing slash, which should now allow you to successfully apply aliases to these import paths. + ## 0.16.6 * Do not mark subpath imports as external with `--packages=external` ([#2741](https://github.com/evanw/esbuild/issues/2741)) diff --git a/internal/bundler/bundler.go b/internal/bundler/bundler.go index cbdb9d59386..cf7f15b060e 100644 --- a/internal/bundler/bundler.go +++ b/internal/bundler/bundler.go @@ -548,7 +548,7 @@ func ResolveFailureErrorTextSuggestionNotes( } hint := "" - if resolver.IsPackagePath(path) { + if resolver.IsPackagePath(path) && !fs.IsAbs(path) { hint = fmt.Sprintf("You can mark the path %q as external to exclude it from the bundle, which will remove this error.", path) if kind == ast.ImportRequire { hint += " You can also surround this \"require\" call with a try/catch block to handle this failure at run-time instead of bundle-time." diff --git a/internal/bundler/bundler_default_test.go b/internal/bundler/bundler_default_test.go index 1d1ce4b7afe..d90a0adc958 100644 --- a/internal/bundler/bundler_default_test.go +++ b/internal/bundler/bundler_default_test.go @@ -6924,6 +6924,7 @@ func TestPackageAlias(t *testing.T) { import "@abs-path/pkg6" import "@abs-path/pkg7/foo" import "@scope-only/pkg8" + import "slash/" `, "/nested3/index.js": `import "pkg3"`, "/nested3/node_modules/alias3/index.js": `test failure`, @@ -6935,6 +6936,7 @@ func TestPackageAlias(t *testing.T) { "/alias6/dir/index.js": `console.log(6)`, "/alias7/dir/foo/index.js": `console.log(7)`, "/alias8/dir/pkg8/index.js": `console.log(8)`, + "/alias9/some/file.js": `console.log(9)`, }, entryPaths: []string{"/entry.js"}, options: config.Options{ @@ -6949,6 +6951,7 @@ func TestPackageAlias(t *testing.T) { "@abs-path/pkg6": `/alias6/dir`, "@abs-path/pkg7": `/alias7/dir`, "@scope-only": "/alias8/dir", + "slash": "/alias9/some/file.js", }, }, }) diff --git a/internal/bundler/snapshots/snapshots_loader.txt b/internal/bundler/snapshots/snapshots_loader.txt index acf62eaa25d..682119ced25 100644 --- a/internal/bundler/snapshots/snapshots_loader.txt +++ b/internal/bundler/snapshots/snapshots_loader.txt @@ -1528,6 +1528,9 @@ console.log(7); // alias8/dir/pkg8/index.js console.log(8); +// alias9/some/file.js +console.log(9); + ================================================================================ TestRequireCustomExtensionBase64 ---------- /out.js ---------- diff --git a/internal/resolver/resolver.go b/internal/resolver/resolver.go index fe918feae4f..1c025838026 100644 --- a/internal/resolver/resolver.go +++ b/internal/resolver/resolver.go @@ -313,7 +313,21 @@ func (rr *resolver) Resolve(sourceDir string, importPath string, kind ast.Import // when using Yarn PnP because Yarn PnP doesn't allow nested packages // to "reach outside" of their normal dependency lists. sourceDir = r.fs.Cwd() - debugMeta.ModifiedImportPath = value + importPath[len(key):] + if tail := importPath[len(key):]; tail != "/" { + // Don't include the trailing characters if they are equal to a + // single slash. This comes up because you can abuse this quirk of + // node's path resolution to force node to load the package from the + // file system instead of as a built-in module. For example, "util" + // is node's built-in module while "util/" is one on the file system. + // Leaving the trailing slash in place causes problems for people: + // https://github.com/evanw/esbuild/issues/2730. It should be ok to + // always strip the trailing slash even when using the alias feature + // to swap one package for another (except when you swap a reference + // to one built-in node module with another but really why would you + // do that). + value += tail + } + debugMeta.ModifiedImportPath = value if r.debugLogs != nil { r.debugLogs.addNote(fmt.Sprintf(" Matched with alias from %q to %q", key, value)) r.debugLogs.addNote(fmt.Sprintf(" Modified import path from %q to %q", importPath, debugMeta.ModifiedImportPath))