Skip to content

Commit

Permalink
fix #634: use ".._/" for paths outside of outbase
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Jan 2, 2021
1 parent 3f41c69 commit 5272bd9
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 1 deletion.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

## Unreleased

* Allow entry points outside of the `outbase` directory ([#634](https://github.com/evanw/esbuild/issues/634))

When esbuild generates the output path for a bundled entry point, it computes the relative path from [the `outbase` directory](https://esbuild.github.io/api/#outbase) to the input entry point file and then joins that relative path to the output directory. For example, if there are two entry points `src/pages/home/index.ts` and `src/pages/about/index.ts`, the outbase directory is `src`, and the output directory is `out`, the output directory will contain `out/pages/home/index.js` and `out/pages/about/index.js`.

However, this means that the `outbase` directory is expected to contain all entry point files (even implicit entry point files from `import()` expressions). If an entry point isn't under the outbase directory then esbuild will to try to write the output file outside of the output directory, since the path of the entry point relative to `outbase` will start with `../` which is then joined to the output directory. This is unintentional. All output files are supposed to be written inside of the output directory.

This release fixes the problem by creating a directory with the name `.._` in the output directory for output file paths of entry points that are not inside the `outbase` directory. So if the previous example was bundled with an outbase directory of `temp`, the output directory will contain `out/.._/pages/home/index.js` and `out/.._/pages/about/index.js`. Doing this instead of stripping the leading `../` off the relative path is necessary to avoid collisions between different entry points with the same path suffix.

* Minification improvements

This release contains the following minification improvements:
Expand Down
18 changes: 18 additions & 0 deletions internal/bundler/bundler_default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3879,3 +3879,21 @@ ts.ts: warning: Cannot construct "a" because it's an import namespace object, no
`,
})
}

func TestBundlingFilesOutsideOfOutbase(t *testing.T) {
splitting_suite.expectBundled(t, bundled{
files: map[string]string{
"/src/entry.js": `
console.log('test')
`,
},
entryPaths: []string{"/src/entry.js"},
options: config.Options{
Mode: config.ModeBundle,
CodeSplitting: true,
OutputFormat: config.FormatESModule,
AbsOutputBase: "/some/nested/directory",
AbsOutputDir: "/out",
},
})
}
21 changes: 20 additions & 1 deletion internal/bundler/linker.go
Original file line number Diff line number Diff line change
Expand Up @@ -2586,6 +2586,26 @@ func (c *linkerContext) computeChunks() []chunkInfo {
} else if relPath, ok := c.fs.Rel(c.options.AbsOutputBase, source.KeyPath.Text); ok {
relDir = c.fs.Dir(relPath)
baseName = c.fs.Base(relPath)
relDir = strings.ReplaceAll(relDir, "\\", "/")

// Replace leading "../" so we don't try to write outside of the output
// directory. This normally can't happen because "AbsOutputBase" is
// automatically computed to contain all entry point files, but it can
// happen if someone sets it manually via the "outbase" API option.
//
// Note that we can't just strip any leading "../" because that could
// cause two separate entry point paths to collide. For example, there
// could be both "src/index.js" and "../src/index.js" as entry points.
dotDotCount := 0
for strings.HasPrefix(relDir[dotDotCount*3:], "../") {
dotDotCount++
}
if dotDotCount > 0 {
// The use of ".._" here is somewhat arbitrary but it is unlikely to
// collide with a folder named by a human and it works on Windows
// (Windows doesn't like names that end with a ".").
relDir = strings.Repeat(".._/", dotDotCount) + relDir[dotDotCount*3:]
}
} else {
baseName = c.fs.Base(source.KeyPath.Text)
}
Expand All @@ -2602,7 +2622,6 @@ func (c *linkerContext) computeChunks() []chunkInfo {
}

// Always use cross-platform path separators to avoid problems with Windows
relDir = strings.ReplaceAll(relDir, "\\", "/")
file.entryPointRelPath = path.Join(relDir, baseName)

// Create a chunk for the entry point here to ensure that the chunk is
Expand Down
6 changes: 6 additions & 0 deletions internal/bundler/snapshots/snapshots_splitting.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
TestBundlingFilesOutsideOfOutbase
---------- /out/.._/.._/.._/src/entry.js ----------
// src/entry.js
console.log("test");

================================================================================
TestSplittingAssignToLocal
---------- /out/a.js ----------
import {
Expand Down

0 comments on commit 5272bd9

Please sign in to comment.