Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with:
node-version: 24.20.0
node-version: 24.21.0
package-manager-cache: false

- name: Install Pnpm
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24.20.0
node-version: 24.21.0
package-manager-cache: false

- name: Setup Pnpm
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/reusable-native-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with:
node-version: 24.20.0
node-version: 24.21.0
package-manager-cache: false

- name: Install Pnpm
Expand All @@ -55,7 +55,7 @@ jobs:

- name: Install cargo-zigbuild
if: contains(inputs.target, 'musl')
uses: taiki-e/install-action@d438492cf8a250514fa2d34b30bc3c0dc37c65ff # v2.87.8
uses: taiki-e/install-action@fa23953489c080190314742a9b907f8e97c6767c # v2.87.10
with:
tool: cargo-zigbuild@0.23.0

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/reusable-native-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with:
node-version: 24.20.0
node-version: 24.21.0
package-manager-cache: false

- name: Install Pnpm
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
if: steps.changes.outputs.changed == 'true'
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with:
node-version: 24.20.0
node-version: 24.21.0
package-manager-cache: false

- name: Install Pnpm
Expand Down
12 changes: 6 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ repository = "https://github.com/rstackjs/rstack-cli"

[workspace.dependencies]
ignore = { version = "0.4.33", default-features = false }
napi = { version = "3.12.4", default-features = false, features = ["napi9"] }
napi-build = "2.4.2"
napi-derive = "3.6.5"
napi = { version = "3.12.6", default-features = false, features = ["napi9"] }
napi-build = "2.4.3"
napi-derive = "3.6.7"
pathdiff = "0.2.3"
rstack-ignore = { path = "crates/rstack-ignore" }

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@
"rstack": "workspace:*",
"typescript": "catalog:"
},
"packageManager": "pnpm@12.4.1"
"packageManager": "pnpm@12.4.2"
}
2 changes: 1 addition & 1 deletion packages/create-rstack/template-app-lit-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"lit": "^3.3.3"
},
"devDependencies": {
"@types/node": "^26.5.1",
"@types/node": "^26.6.1",
"happy-dom": "^20.14.5",
"rstack": "^0.7.5",
"typescript": "^7.0.2"
Expand Down
2 changes: 1 addition & 1 deletion packages/create-rstack/template-app-preact-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"@rsbuild/plugin-preact": "^2.1.0",
"@testing-library/jest-dom": "^7.0.1",
"@testing-library/preact": "^3.2.4",
"@types/node": "^26.5.1",
"@types/node": "^26.6.1",
"happy-dom": "^20.14.5",
"rstack": "^0.7.5",
"typescript": "^7.0.2"
Expand Down
2 changes: 1 addition & 1 deletion packages/create-rstack/template-app-react-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"@testing-library/dom": "^10.4.2",
"@testing-library/jest-dom": "^7.0.1",
"@testing-library/react": "^16.3.3",
"@types/node": "^26.5.1",
"@types/node": "^26.6.1",
"@types/react": "^19.3.0",
"@types/react-dom": "^19.3.0",
"happy-dom": "^20.14.5",
Expand Down
2 changes: 1 addition & 1 deletion packages/create-rstack/template-app-solid-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"@rsbuild/plugin-solid": "^1.2.2",
"@solidjs/testing-library": "^0.8.10",
"@testing-library/jest-dom": "^7.0.1",
"@types/node": "^26.5.1",
"@types/node": "^26.6.1",
"happy-dom": "^20.14.5",
"rstack": "^0.7.5",
"typescript": "^7.0.2"
Expand Down
2 changes: 1 addition & 1 deletion packages/create-rstack/template-app-svelte-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"@rsbuild/plugin-svelte": "^2.0.1",
"@testing-library/jest-dom": "^7.0.1",
"@testing-library/svelte": "^5.4.2",
"@types/node": "^26.5.1",
"@types/node": "^26.6.1",
"happy-dom": "^20.14.5",
"prettier-plugin-svelte": "^4.1.1",
"rstack": "^0.7.5",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"devDependencies": {
"@testing-library/dom": "^10.4.2",
"@testing-library/jest-dom": "^7.0.1",
"@types/node": "^26.5.1",
"@types/node": "^26.6.1",
"happy-dom": "^20.14.5",
"rstack": "^0.7.5",
"typescript": "^7.0.2"
Expand Down
4 changes: 2 additions & 2 deletions packages/create-rstack/template-app-vue-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
"devDependencies": {
"@rsbuild/plugin-vue": "^2.0.1",
"@testing-library/jest-dom": "^7.0.1",
"@types/node": "^26.5.1",
"@vue/test-utils": "^2.5.0",
"@types/node": "^26.6.1",
"@vue/test-utils": "^2.5.1",
"happy-dom": "^20.14.5",
"rstack": "^0.7.5",
"typescript": "^6.0.3",
Expand Down
2 changes: 1 addition & 1 deletion packages/create-rstack/template-app-vue/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"devDependencies": {
"@rsbuild/plugin-vue": "^2.0.1",
"@testing-library/jest-dom": "^7.0.1",
"@vue/test-utils": "^2.5.0",
"@vue/test-utils": "^2.5.1",
"happy-dom": "^20.14.5",
"rstack": "^0.7.5"
}
Expand Down
4 changes: 2 additions & 2 deletions packages/create-rstack/template-doc-i18n/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
"preview": "rs doc preview"
},
"devDependencies": {
"@rspress/core": "^2.0.21",
"@types/node": "^26.5.1",
"@rspress/core": "^2.0.22",
"@types/node": "^26.6.1",
"@types/react": "^19.3.0",
"@types/react-dom": "^19.3.0",
"react": "^19.3.0",
Expand Down
4 changes: 2 additions & 2 deletions packages/create-rstack/template-doc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
"preview": "rs doc preview"
},
"devDependencies": {
"@rspress/core": "^2.0.21",
"@types/node": "^26.5.1",
"@rspress/core": "^2.0.22",
"@types/node": "^26.6.1",
"@types/react": "^19.3.0",
"@types/react-dom": "^19.3.0",
"react": "^19.3.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/create-rstack/template-lib-node-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"test:watch": "rs test --watch"
},
"devDependencies": {
"@types/node": "^26.5.1",
"@types/node": "^26.6.1",
"rstack": "^0.7.5",
"typescript": "^7.0.2"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/create-rstack/template-lib-react-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"@testing-library/dom": "^10.4.2",
"@testing-library/jest-dom": "^7.0.1",
"@testing-library/react": "^16.3.3",
"@types/node": "^26.5.1",
"@types/node": "^26.6.1",
"@types/react": "^19.3.0",
"@types/react-dom": "^19.3.0",
"happy-dom": "^20.14.5",
Expand Down
2 changes: 1 addition & 1 deletion packages/create-rstack/template-lib-solid-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"@rsbuild/plugin-solid": "^1.2.2",
"@solidjs/testing-library": "^0.8.10",
"@testing-library/jest-dom": "^7.0.1",
"@types/node": "^26.5.1",
"@types/node": "^26.6.1",
"happy-dom": "^20.14.5",
"rstack": "^0.7.5",
"solid-js": "^1.9.15",
Expand Down
2 changes: 1 addition & 1 deletion packages/create-rstack/template-lib-svelte-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
},
"devDependencies": {
"@rsbuild/plugin-svelte": "^2.0.1",
"@types/node": "^26.5.1",
"@types/node": "^26.6.1",
"happy-dom": "^20.14.5",
"prettier-plugin-svelte": "^4.1.1",
"rstack": "^0.7.5",
Expand Down
4 changes: 2 additions & 2 deletions packages/create-rstack/template-lib-vue-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
"devDependencies": {
"@rsbuild/plugin-vue": "^2.0.1",
"@testing-library/jest-dom": "^7.0.1",
"@types/node": "^26.5.1",
"@vue/test-utils": "^2.5.0",
"@types/node": "^26.6.1",
"@vue/test-utils": "^2.5.1",
"happy-dom": "^20.14.5",
"rstack": "^0.7.5",
"typescript": "^6.0.3",
Expand Down
2 changes: 1 addition & 1 deletion packages/create-rstack/template-lib-vue/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"devDependencies": {
"@rsbuild/plugin-vue": "^2.0.1",
"@testing-library/jest-dom": "^7.0.1",
"@vue/test-utils": "^2.5.0",
"@vue/test-utils": "^2.5.1",
"happy-dom": "^20.14.5",
"rstack": "^0.7.5",
"vue": "^3.5.42"
Expand Down
6 changes: 3 additions & 3 deletions packages/create-rstack/template-turborepo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
"test": "turbo run test"
},
"devDependencies": {
"@types/node": "^26.5.1",
"@types/node": "^26.6.1",
"rstack": "^0.7.5",
"turbo": "^2.10.12",
"turbo": "^2.10.13",
"typescript": "^7.0.2"
},
"packageManager": "pnpm@12.4.1"
"packageManager": "pnpm@12.4.2"
}
81 changes: 80 additions & 1 deletion packages/rstack/binding.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

const { readFileSync } = require('fs')
let nativeBinding = null
// Which artifact actually loaded. The WASI fallback chain overwrites it with
// the flavor it resolved; the late native retry below leaves it alone because
// it only runs while no WASI candidate has been loaded.
let __napiLoadedBindingTarget = 'native'
const loadErrors = []

const isMusl = () => {
Expand Down Expand Up @@ -62,7 +66,16 @@ const isMuslFromChildProcess = () => {
function requireNative() {
if (process.env.NAPI_RS_NATIVE_LIBRARY_PATH) {
try {
return require(process.env.NAPI_RS_NATIVE_LIBRARY_PATH)
const overrideBinding = require(process.env.NAPI_RS_NATIVE_LIBRARY_PATH)
// The override may be a generated WASI loader, which already reports its
// own flavor. Adopt it: `module.exports` aliases this object, so claiming
// 'native' would both misreport the artifact and overwrite the loader's
// marker through the alias.
__napiLoadedBindingTarget =
overrideBinding && typeof overrideBinding.__napiBindingTarget === 'string'
? overrideBinding.__napiBindingTarget
: 'native'
return overrideBinding
} catch (err) {
loadErrors.push(err)
}
Expand Down Expand Up @@ -627,6 +640,7 @@ if (!nativeBinding || forceWasi) {
if (!candidateFailed) {
wasiBinding = require('./rstack.wasi.cjs')
nativeBinding = wasiBinding
__napiLoadedBindingTarget = 'wasm32-wasi'
wasiBindingLoaded = true
}
} catch (err) {
Expand All @@ -653,6 +667,7 @@ if (!nativeBinding || forceWasi) {
}
wasiBinding = require('@rstackjs/cli-wasm32-wasi')
nativeBinding = wasiBinding
__napiLoadedBindingTarget = 'wasm32-wasi'
wasiBindingLoaded = true
}
} catch (err) {
Expand Down Expand Up @@ -698,6 +713,70 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}

function __napiStampBindingTarget(exportsObject, target) {
if (
Object.prototype.hasOwnProperty.call(exportsObject, '__napiBindingTarget')
) {
if (exportsObject.__napiBindingTarget === target) {
// Already ours: the root entry aliases the object it loaded, so a WASI
// fallback candidate — or a `NAPI_RS_NATIVE_LIBRARY_PATH` override that
// is a generated loader — arrives already stamped with this same value.
return target
}
const error = new Error(
'`__napiBindingTarget` is reserved by the generated binding loader, but the loaded binding already exports it. Rename the export, e.g. #[napi(js_name = "...")].',
)
error.code = 'ERR_NAPI_BINDING_TARGET_CONFLICT'
throw error
}
if (!Object.isExtensible(exportsObject)) {
// A `#[napi(module_exports)]` hook may seal or freeze this object
// (`Object::seal` / `Object::freeze`). Reporting the artifact is metadata,
// never a reason to fail an otherwise successful load, so the stamp is
// skipped. What a consumer still sees then follows the entry point: the
// browser and deferred loaders declare `__napiBindingTarget` at module
// level and go on reporting it, while the CommonJS entries hand back this
// very object as `module.exports`, so there the value is absent.
return target
}
try {
// [[Define]], not [[Set]]: an ordinary assignment walks the prototype
// chain, so an inherited accessor could swallow the value or throw and
// fail an otherwise successful load. The descriptor is what a successful
// assignment would have produced.
Object.defineProperty(exportsObject, '__napiBindingTarget', {
configurable: true,
enumerable: true,
value: target,
writable: true,
})
} catch {
// Same rule as the non-extensible skip above: reporting the artifact is
// metadata, never a reason to fail an otherwise successful load. An exotic
// object (a Proxy whose defineProperty trap refuses) is skipped, not
// thrown over.
}
// The CommonJS loaders assign this return value so `cjs-module-lexer` — and
// therefore Node's CJS -> ESM named export detection — can see
// `__napiBindingTarget` statically.
return target
}
// Stamp before the alias, not after. The guard only reads `nativeBinding`
// (`hasOwnProperty` plus a comparison), which is safe against any addon
// accessor; an assignment is not, because a `#[napi(module_exports)]` hook can
// expose a getter reporting this very value and a setter that throws. So the
// assignment lands on the loader's own `module.exports`, still the original
// object here, and the alias below replaces it.
//
// The assignment is what keeps the marker a statically visible CommonJS export:
// `cjs-module-lexer` is Node's CJS -> ESM named export detection, it cannot see
// a bare call, and the later `module.exports = nativeBinding` does not undo the
// detection. The assignment itself always succeeds — its target is this
// loader's own, still extensible `module.exports` — and the alias below then
// discards the value it wrote. What a consumer reads is whatever the guard put
// on `nativeBinding`, so on a frozen binding, where the guard skips, the
// linked import resolves to `undefined`.
module.exports.__napiBindingTarget = __napiStampBindingTarget(nativeBinding, __napiLoadedBindingTarget)
module.exports = nativeBinding
module.exports.GitIgnoreMatcher = nativeBinding.GitIgnoreMatcher
module.exports.IgnoreMatcher = nativeBinding.IgnoreMatcher
Loading
Loading