Skip to content

Commit 3449be5

Browse files
committed
attempt a better x-platform error message (#1819)
1 parent 4276739 commit 3449be5

File tree

2 files changed

+119
-6
lines changed

2 files changed

+119
-6
lines changed

CHANGELOG.md

+49
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,54 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
* Attempt to explain why esbuild can't run ([#1819](https://github.com/evanw/esbuild/issues/1819))
6+
7+
People sometimes try to install esbuild on one OS and then copy the `node_modules` directory over to another OS without reinstalling. This works with JavaScript code but doesn't work with esbuild because esbuild is a native binary executable. This release attempts to offer a helpful error message when this happens. It looks like this:
8+
9+
```
10+
$ ./node_modules/.bin/esbuild
11+
./node_modules/esbuild/bin/esbuild:106
12+
throw new Error(`
13+
^
14+
15+
Error:
16+
You installed esbuild on another platform than the one you're currently using.
17+
This won't work because esbuild is written with native code and needs to
18+
install a platform-specific binary executable.
19+
20+
Specifically the "esbuild-linux-arm64" package is present but this platform
21+
needs the "esbuild-darwin-arm64" package instead. People often get into this
22+
situation by installing esbuild on Windows or macOS and copying "node_modules"
23+
into a Docker image that runs Linux, or by copying "node_modules" between
24+
Windows and WSL environments.
25+
26+
If you are installing with npm, you can try not copying the "node_modules"
27+
directory when you copy the files over, and running "npm ci" or "npm install"
28+
on the destination platform after the copy. Or you could consider using yarn
29+
instead which has built-in support for installing a package on multiple
30+
platforms simultaneously.
31+
32+
If you are installing with yarn, you can try listing both this platform and the
33+
other platform in your ".yarnrc.yml" file using the "supportedArchitectures"
34+
feature: https://yarnpkg.com/configuration/yarnrc/#supportedArchitectures
35+
Keep in mind that this means multiple copies of esbuild will be present.
36+
37+
Another alternative is to use the "esbuild-wasm" package instead, which works
38+
the same way on all platforms. But it comes with a heavy performance cost and
39+
can sometimes be 10x slower than the "esbuild" package, so you may also not
40+
want to do that.
41+
42+
at generateBinPath (./node_modules/esbuild/bin/esbuild:106:17)
43+
at Object.<anonymous> (./node_modules/esbuild/bin/esbuild:161:39)
44+
at Module._compile (node:internal/modules/cjs/loader:1101:14)
45+
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
46+
at Module.load (node:internal/modules/cjs/loader:981:32)
47+
at Function.Module._load (node:internal/modules/cjs/loader:822:12)
48+
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
49+
at node:internal/main/run_main_module:17:47
50+
```
51+
352
## 0.14.12
453

554
* Ignore invalid `@import` rules in CSS ([#1946](https://github.com/evanw/esbuild/issues/1946))

lib/npm/node-platform.ts

+70-6
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,31 @@ export function pkgAndSubpathForCurrentPlatform(): { pkg: string, subpath: strin
5353
return { pkg, subpath };
5454
}
5555

56+
function pkgForSomeOtherPlatform(): string | null {
57+
const libMainJS = require.resolve('esbuild');
58+
const nodeModulesDirectory = path.dirname(path.dirname(path.dirname(libMainJS)));
59+
60+
if (path.basename(nodeModulesDirectory) === 'node_modules') {
61+
for (const unixKey in knownUnixlikePackages) {
62+
try {
63+
const pkg = knownUnixlikePackages[unixKey];
64+
if (fs.existsSync(path.join(nodeModulesDirectory, pkg))) return pkg;
65+
} catch {
66+
}
67+
}
68+
69+
for (const windowsKey in knownWindowsPackages) {
70+
try {
71+
const pkg = knownWindowsPackages[windowsKey];
72+
if (fs.existsSync(path.join(nodeModulesDirectory, pkg))) return pkg;
73+
} catch {
74+
}
75+
}
76+
}
77+
78+
return null;
79+
}
80+
5681
export function downloadedBinPath(pkg: string, subpath: string): string {
5782
const esbuildLibDir = path.dirname(require.resolve('esbuild'));
5883
return path.join(esbuildLibDir, `downloaded-${pkg}-${path.basename(subpath)}`);
@@ -79,15 +104,54 @@ export function generateBinPath(): string {
79104
// by manually downloading the package instead. Check for that next.
80105
binPath = downloadedBinPath(pkg, subpath);
81106
if (!fs.existsSync(binPath)) {
82-
// If that didn't work too, then we're out of options. This can happen
83-
// when someone installs esbuild with both the "--no-optional" and the
84-
// "--ignore-scripts" flags. The fix for this is to just not do that.
85-
//
86-
// In that case we try to have a nice error message if we think we know
87-
// what's happening. Otherwise we just rethrow the original error message.
107+
// If that didn't work too, check to see whether the package is even there
108+
// at all. It may not be (for a few different reasons).
88109
try {
89110
require.resolve(pkg);
90111
} catch {
112+
// If we can't find the package for this platform, then it's possible
113+
// that someone installed this for some other platform and is trying
114+
// to use it without reinstalling. That won't work of course, but
115+
// people do this all the time with systems like Docker. Try to be
116+
// helpful in that case.
117+
const otherPkg = pkgForSomeOtherPlatform();
118+
if (otherPkg) {
119+
throw new Error(`
120+
You installed esbuild on another platform than the one you're currently using.
121+
This won't work because esbuild is written with native code and needs to
122+
install a platform-specific binary executable.
123+
124+
Specifically the "${otherPkg}" package is present but this platform
125+
needs the "${pkg}" package instead. People often get into this
126+
situation by installing esbuild on Windows or macOS and copying "node_modules"
127+
into a Docker image that runs Linux, or by copying "node_modules" between
128+
Windows and WSL environments.
129+
130+
If you are installing with npm, you can try not copying the "node_modules"
131+
directory when you copy the files over, and running "npm ci" or "npm install"
132+
on the destination platform after the copy. Or you could consider using yarn
133+
instead which has built-in support for installing a package on multiple
134+
platforms simultaneously.
135+
136+
If you are installing with yarn, you can try listing both this platform and the
137+
other platform in your ".yarnrc.yml" file using the "supportedArchitectures"
138+
feature: https://yarnpkg.com/configuration/yarnrc/#supportedArchitectures
139+
Keep in mind that this means multiple copies of esbuild will be present.
140+
141+
Another alternative is to use the "esbuild-wasm" package instead, which works
142+
the same way on all platforms. But it comes with a heavy performance cost and
143+
can sometimes be 10x slower than the "esbuild" package, so you may also not
144+
want to do that.
145+
`);
146+
}
147+
148+
// If that didn't work too, then maybe someone installed esbuild with
149+
// both the "--no-optional" and the "--ignore-scripts" flags. The fix
150+
// for this is to just not do that. We don't attempt to handle this
151+
// case at all.
152+
//
153+
// In that case we try to have a nice error message if we think we know
154+
// what's happening. Otherwise we just rethrow the original error message.
91155
throw new Error(`The package "${pkg}" could not be found, and is needed by esbuild.
92156
93157
If you are installing esbuild with npm, make sure that you don't specify the

0 commit comments

Comments
 (0)