Skip to content

Commit

Permalink
pretend that inaccessible directories are empty (#942)
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw authored Mar 10, 2021
1 parent d8f281e commit cf2f34c
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 0 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 bundling when parent directory is inaccessible ([#938](https://github.com/evanw/esbuild/issues/938))

Previously bundling with esbuild when a parent directory is inaccessible did not work because esbuild would try to read the directory to search for a `node_modules` folder and would then fail the build when that failed. In practice this caused issues in certain Linux environments where a directory close to the root directory was inaccessible (e.g. on Android). With this release, esbuild will treat inaccessible directories as empty to allow for the `node_modules` search to continue past the inaccessible directory and into its parent directory. This means it should now be possible to bundle with esbuild in these situations.

## 0.9.0

**This release contains backwards-incompatible changes.** Since esbuild is before version 1.0.0, these changes have been released as a new minor version to reflect this (as [recommended by npm](https://docs.npmjs.com/cli/v6/using-npm/semver/)). You should either be pinning the exact version of `esbuild` in your `package.json` file or be using a version range syntax that only accepts patch upgrades such as `^0.8.0`. See the documentation about [semver](https://docs.npmjs.com/cli/v6/using-npm/semver/) for more information.
Expand Down
4 changes: 4 additions & 0 deletions internal/fs/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ type DirEntries struct {
data map[string]*Entry
}

func MakeEmptyDirEntries(dir string) DirEntries {
return DirEntries{dir, make(map[string]*Entry)}
}

type DifferentCase struct {
Dir string
Query string
Expand Down
8 changes: 8 additions & 0 deletions internal/resolver/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,14 @@ func (r *resolver) dirInfoUncached(path string) *dirInfo {

// List the directories
entries, err := r.fs.ReadDirectory(path)
if err == syscall.EACCES {
// Just pretend this directory is empty if we can't access it. This is the
// case on Unix for directories that only have the execute permission bit
// set. It means we will just pass through the empty directory and
// continue to check the directories above it, which is now node behaves.
entries = fs.MakeEmptyDirEntries(path)
err = nil
}
if err != nil {
// Ignore "ENOTDIR" here so that calling "ReadDirectory" on a file behaves
// as if there is nothing there at all instead of causing an error due to
Expand Down
37 changes: 37 additions & 0 deletions scripts/js-api-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,43 @@ let buildTests = {
assert.strictEqual(require(bOut).y, true)
},

async pathResolverEACCS({ esbuild, testDir }) {
let outerDir = path.join(testDir, 'outer');
let innerDir = path.join(outerDir, 'inner');
let pkgDir = path.join(testDir, 'node_modules', 'pkg');
let entry = path.join(innerDir, 'entry.js');
let sibling = path.join(innerDir, 'sibling.js');
let index = path.join(pkgDir, 'index.js');
let outfile = path.join(innerDir, 'out.js');
fs.mkdirSync(pkgDir, { recursive: true });
fs.mkdirSync(innerDir, { recursive: true });
fs.writeFileSync(entry, `
import a from "./sibling.js"
import b from "pkg"
export default {a, b}
`);
fs.writeFileSync(sibling, `export default 'sibling'`);
fs.writeFileSync(index, `export default 'pkg'`);
fs.chmodSync(outerDir, 0o111);

try {
await esbuild.build({
entryPoints: [entry],
bundle: true,
outfile,
format: 'cjs',
});

const result = require(outfile);
assert.deepStrictEqual(result.default, { a: 'sibling', b: 'pkg' });
}

finally {
// Restore permission when the test ends so test cleanup works
fs.chmodSync(outerDir, 0o755);
}
},

async nodePathsTest({ esbuild, testDir }) {
let srcDir = path.join(testDir, 'src');
let pkgDir = path.join(testDir, 'pkg');
Expand Down

0 comments on commit cf2f34c

Please sign in to comment.