Reproduction for vitejs/vite-plugin-react-swc#74
I think the Bindings not found
error occurs when the lockfile is missing an entry for @swc/core-xxxx
platform-specific binary for the current platform.
In a Vite + React + SWC project, this error can occur when starting Vite's dev server especially after refreshing the package-lock.json
file without deleting node_modules
beforehand:
Bindings not found.
12:08:10 AM [vite] Internal server error: Bindings not found.
Plugin: vite:react-swc
File: /Users/tony/src/tmp/vite-plugin-react-swc-bug-demo/src/main.tsx
at Compiler.<anonymous> (/Users/tony/src/tmp/vite-plugin-react-swc-bug-demo/node_modules/@swc/core/index.js:225:19)
at Generator.next (<anonymous>)
at /Users/tony/src/tmp/vite-plugin-react-swc-bug-demo/node_modules/@swc/core/index.js:34:71
at new Promise (<anonymous>)
at __awaiter (/Users/tony/src/tmp/vite-plugin-react-swc-bug-demo/node_modules/@swc/core/index.js:30:12)
at Compiler.transform (/Users/tony/src/tmp/vite-plugin-react-swc-bug-demo/node_modules/@swc/core/index.js:202:16)
at transform (/Users/tony/src/tmp/vite-plugin-react-swc-bug-demo/node_modules/@swc/core/index.js:344:21)
at transformWithOptions (file:///Users/tony/src/tmp/vite-plugin-react-swc-bug-demo/node_modules/@vitejs/plugin-react-swc/index.mjs:130:20)
at TransformContext.transform (file:///Users/tony/src/tmp/vite-plugin-react-swc-bug-demo/node_modules/@vitejs/plugin-react-swc/index.mjs:55:30)
at Object.transform (file:///Users/tony/src/tmp/vite-plugin-react-swc-bug-demo/node_modules/vite/dist/node/chunks/dep-a178814b.js:42877:44)
This might be happening because @swc/core
's post-install script (which presumably installs the @swc/core-xxxx
binary for the current platform) doesn't run in the scenario above.
-
Scaffold a new Vite project with React+SWC (or clone this repo), and CD into the project directory:
npm init vite@latest vite-plugin-react-swc-bug-demo -- --template react-swc cd vite-plugin-react-swc-bug-demo
-
Install dependencies:
npm install
-
Observe the resulting lockfile contains
node_modules/@swc/core-xxxx
entries for all supported platforms:grep -E 'node_modules/@swc/core-.*' package-lock.json
-
Refresh the lockfile by deleting it and reinstalling dependencies (NOTE: Do not delete
node_modules
beforehand):rm package-lock.json npm install
-
Observe the resulting lockfile contains only the
@swc/core-xxxx
binary for the current platform:grep -E 'node_modules/@swc/core-.*' package-lock.json
-
Observe the corresponding
node_modules/@swc/core-xxxx
exists:ls ./node_modules/@swc/core-*
-
To simulate what occurs when an
npm install
is run on a different platform, delete the@swc/core-xxxx
entry inpackage-lock.json
and regenerate the lockfile.# Manually delete the @swc/core-xxxx entry from package-lock.json ... # OR run this jq command (edit platform as necessary): jq -r 'del(.packages."node_modules/@swc/core-darwin-arm64")' package-lock.json > package-lock.json.new && mv package-lock.json.new package-lock.json # and then regenerate package-lock.json npm install
-
Observe the module from step 6 no longer exists:
ls ./node_modules/@swc/core-*
-
Start Vite's dev server:
npm run dev
-
Open http://localhost:5173 (or the port that Vite chooses)
-
See the
Bindings not found
error fromvite:react-swc
in the terminal
The most reliable workaround I found is to force-install the optional dependencies of @swc/core
as optional dependencies of your own project:
npm install --force --save-optional \
@swc/core-darwin-arm64 \
@swc/core-darwin-x64 \
@swc/core-linux-arm-gnueabihf \
@swc/core-linux-arm64-gnu \
@swc/core-linux-arm64-musl \
@swc/core-linux-x64-gnu \
@swc/core-linux-x64-musl \
@swc/core-win32-arm64-msvc \
@swc/core-win32-ia32-msvc \
@swc/core-win32-x64-msvc
Alternatively, you could use this Node script to merge the optionalDependencies
from @swc/core
into your own package.json
:
/* eslint-env node */
const path = require('path')
const fs = require('fs')
const optionalDepsFromSwcCore = require('@swc/core/package.json').optionalDependencies
if (!Object.keys(optionalDepsFromSwcCore).length) {
console.log('@swc/core has no optional deps')
process.exit(1)
}
console.log('@swc/core optional deps', optionalDepsFromSwcCore)
// merge @swc/core's optional deps with our own from package.json
const pkgJsonPath = path.resolve('package.json')
const pkgJson = require(pkgJsonPath)
const optionalDeps = {
...pkgJson.optionalDependencies,
...optionalDepsFromSwcCore,
}
// sort the optional deps by their keys
pkgJson.optionalDependencies = Object.keys(optionalDeps)
.sort()
.reduce((acc, key) => {
acc[key] = pkgJson.optionalDependencies[key]
return acc
}, {})
console.log('merging optionalDependencies in', pkgJsonPath)
fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2) + '\n')
Or you can simply use these shell commands:
npm query "#@swc/core" | jq ".[] |= {optionalDependencies} | .[0]" > swc-core-opt-deps.json
jq -s '.[0] * .[1]' package.json swc-core-opt-deps.json > package.json.new
mv package.json.new package.json
rm swc-core-opt-deps.json
With those optional deps specified in your project's package.json
, the lockfile will always contain entries for those deps even using the lockfile refresh method above. Only the dep that is supported on your platform would actually be installed.