diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 94643aa..def0979 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -16,7 +16,7 @@ jobs:
fail-fast: false
matrix:
node-version:
- - '22.15.0'
+ - '22.17.0'
- '24.11.1'
steps:
- name: Checkout
diff --git a/README.md b/README.md
index c887e5b..5d13ddc 100644
--- a/README.md
+++ b/README.md
@@ -198,6 +198,16 @@ CSS Modules hash class names after the loader extracts selectors, so the stylesh
```
+### Stable selector type generation
+
+Run `npx knighted-css-generate-types --root .` to scan your project for `?knighted-css&types` imports. The CLI:
+
+- extracts selectors via the loader, then writes literal module declarations into `node_modules/@knighted/css/node_modules/.knighted-css`
+- updates the packaged stub at `node_modules/@knighted/css/types-stub/index.d.ts`
+- exposes the declarations automatically because `types.d.ts` references the stub, so no `tsconfig` wiring is required
+
+Re-run the command whenever imports change (add it to a `types:css` npm script or your build). If you need a different destination, pass `--out-dir` and/or `--types-root` to override the defaults.
+
Sass/Less projects can import the shared mixins directly:
```scss
@@ -238,15 +248,84 @@ Need a zero-JS approach? Import the optional layer helper and co-locate your fal
Override the namespace via `:root { --knighted-stable-namespace: 'acme'; }` if you want a different prefix in pure CSS.
+#### Type-safe selector maps (`?knighted-css&types`)
+
+Append `&types` to any loader import to receive a literal map of the discovered stable selectors alongside the raw CSS:
+
+```ts
+import { knightedCss, stableSelectors } from './styles.css?knighted-css&types'
+
+stableSelectors.demo // "knighted-demo"
+type StableSelectors = typeof stableSelectors
+```
+
+The map ships as `as const`, so every key/value pair is type-safe without additional tooling. Need the combined import? Add the flag there too and destructure everything from one place:
+
+```ts
+import type { KnightedCssCombinedModule } from '@knighted/css/loader'
+import combined, { stableSelectors } from './button.js?knighted-css&combined&types'
+
+const { knightedCss } = combined as KnightedCssCombinedModule<
+ typeof import('./button.js')
+>
+
+stableSelectors.demo // "knighted-demo"
+```
+
+Namespaces default to `knighted`, but you can configure a global fallback via the loader’s `stableNamespace` option:
+
+```js
+{
+ loader: '@knighted/css/loader',
+ options: {
+ stableNamespace: 'storybook',
+ },
+}
+```
+
+All imports share the namespace resolved by the loader (or the `knighted-css-generate-types` CLI). Use the loader option or CLI flag to align runtime + type generation, and the loader still emits highlighted warnings when the namespace trims to an empty value or when no selectors match. For best editor support, keep `&types` at the end of the query (`?knighted-css&combined&types`, `?knighted-css&combined&named-only&types`, etc.).
+
#### TypeScript support for loader queries
Loader query types ship directly with `@knighted/css`. Reference them once in your project—either by adding `"types": ["@knighted/css/loader-queries"]` to `tsconfig.json` or dropping `///
` into a global `.d.ts`—and the following ambient modules become available everywhere:
- `*?knighted-css` imports expose a `knightedCss: string` export.
-- `*?knighted-css&combined` (and any query that includes both flags) expose `knightedCss` and return the original module exports, which you can narrow with `KnightedCssCombinedModule` before destructuring named members.
+- `*?knighted-css&types` exposes both `knightedCss` and `stableSelectors`, the readonly selector map.
+- `*?knighted-css&combined` (plus `&named-only` / `&no-default`) mirror the source module exports while adding `knightedCss`, which you can narrow with `KnightedCssCombinedModule` before destructuring named members.
+- `*?knighted-css&combined&types` variants add the same `stableSelectors` map on top of the combined behavior so a single import can surface everything.
No vendor copies are necessary—the declarations live inside `@knighted/css`, you just need to point your TypeScript config at the shipped `loader-queries` subpath once.
+#### Generate literal selector types
+
+The runtime `stableSelectors` export is always a literal `as const` map, but TypeScript can only see those exact tokens if your project emits matching `.d.ts` files. Run the bundled CLI whenever you change a module that imports `?knighted-css&types` (or any `&combined&types` variants):
+
+```bash
+npx knighted-css-generate-types --root .
+```
+
+or wire it into `package.json` for local workflows:
+
+```json
+{
+ "scripts": {
+ "knighted:types": "knighted-css-generate-types --root . --include src"
+ }
+}
+```
+
+The CLI scans every file you include (by default the project root, skipping `node_modules`, `dist`, etc.), finds imports containing `?knighted-css&types`, reuses the loader to extract CSS, and writes deterministic `.d.ts` files into `node_modules/.knighted-css/knt-*.d.ts`. It also maintains `node_modules/@knighted/css/types-stub/index.d.ts`, so TypeScript picks up the generated declarations automatically—no extra `typeRoots` configuration is required.
+
+Key flags:
+
+- `--root` / `-r` – project root (defaults to `process.cwd()`).
+- `--include` / `-i` – additional directories or files to scan (repeatable).
+- `--out-dir` – custom output folder for the generated `knt-*` declarations.
+- `--types-root` – override the `@types` directory used for the aggregator.
+- `--stable-namespace` – namespace prefix for the generated selector map.
+
+Re-run the CLI (or add it to a pre-build hook) whenever selectors change so new tokens land in the literal declaration files.
+
#### Combined module + CSS import
If you prefer a single import that returns both your module exports and the compiled stylesheet, append `&combined` to the query. Then narrow the import once so TypeScript understands the shape:
diff --git a/codecov.yml b/codecov.yml
index 8960148..8b780dd 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -8,6 +8,7 @@ coverage:
- packages/css/src
patch:
default:
+ target: 80.0
threshold: 5.0
paths:
- packages/css/src
diff --git a/docs/combined-queries.md b/docs/combined-queries.md
index 3b4e4ea..f4a73b0 100644
--- a/docs/combined-queries.md
+++ b/docs/combined-queries.md
@@ -4,11 +4,14 @@ This document summarizes how `?knighted-css&combined` behaves for different modu
## Decision Matrix
-| Source module exports | Recommended query | TypeScript import pattern | Notes |
-| --------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
-| **Named exports only** | `?knighted-css&combined&named-only` | [Snippet](#named-exports-only) | `&named-only` disables the synthetic default export so you only destructure the original named members plus `knightedCss`. |
-| **Default export only** | `?knighted-css&combined` | [Snippet](#default-export-only) | Loader mirrors the default export and adds `knightedCss`, so default-import code keeps working. |
-| **Default + named exports** | `?knighted-css&combined` (append `&named-only` when you never consume the default) | [Snippet](#default-and-named-exports) | Without the flag you get both default + named exports; adding it drops the synthetic default for stricter codebases. |
+| Source module exports | Recommended query | TypeScript import pattern | Notes |
+| ---------------------------------------------- | ---------------------------------------------------------------------------------- | ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
+| **Named exports only** | `?knighted-css&combined&named-only` | [Snippet](#named-exports-only) | `&named-only` disables the synthetic default export so you only destructure the original named members plus `knightedCss`. |
+| **Default export only** | `?knighted-css&combined` | [Snippet](#default-export-only) | Loader mirrors the default export and adds `knightedCss`, so default-import code keeps working. |
+| **Default + named exports** | `?knighted-css&combined` (append `&named-only` when you never consume the default) | [Snippet](#default-and-named-exports) | Without the flag you get both default + named exports; adding it drops the synthetic default for stricter codebases. |
+| **Named exports + stable selector map** | `?knighted-css&combined&named-only&types` | [Snippet](#named-exports-with-stable-selectors) | Adds a `stableSelectors` named export; configure namespaces via the loader option or CLI flag. |
+| **Default export only + stable selector map** | `?knighted-css&combined&types` | [Snippet](#default-export-with-stable-selectors) | Keep your default-import flow and add `stableSelectors`; namespaces come from loader/CLI configuration. |
+| **Default + named exports + stable selectors** | `?knighted-css&combined&types` (append `&named-only` if you skip the default) | [Snippet](#default-and-named-exports-with-stable-selectors) | Best of both worlds—`stableSelectors` is exported alongside `knightedCss`; add `&named-only` if you don’t use the default. |
## Named exports only
@@ -50,8 +53,56 @@ const {
Prefer `?knighted-css&combined&named-only` plus the [named exports only](#named-exports-only) snippet when you intentionally avoid default exports but still need the named members and `knightedCss`.
+## Named exports with stable selectors
+
+```ts
+import type { KnightedCssCombinedModule } from '@knighted/css/loader'
+import combined, {
+ stableSelectors,
+} from './module.js?knighted-css&combined&named-only&types'
+
+const { Component, knightedCss } = combined as KnightedCssCombinedModule<
+ typeof import('./module.js')
+>
+
+stableSelectors.card // "knighted-card"
+```
+
+Need a different prefix? Configure the loader’s `stableNamespace` option (or pass `--stable-namespace` to the CLI) so both runtime and generated types stay in sync.
+
+## Default export with stable selectors
+
+```ts
+import type { KnightedCssCombinedModule } from '@knighted/css/loader'
+import combined, { stableSelectors } from './module.js?knighted-css&combined&types'
+
+const { default: Component, knightedCss } = combined as KnightedCssCombinedModule<
+ typeof import('./module.js')
+>
+
+stableSelectors.badge // "knighted-badge"
+```
+
+## Default and named exports with stable selectors
+
+```ts
+import type { KnightedCssCombinedModule } from '@knighted/css/loader'
+import combined, { stableSelectors } from './module.js?knighted-css&combined&types'
+
+const {
+ default: Component,
+ helper,
+ knightedCss,
+} = combined as KnightedCssCombinedModule
+
+stableSelectors.card // "knighted-card" (or your configured namespace)
+```
+
+Append `&named-only` before `&types` when you want to drop the synthetic default export while still receiving `stableSelectors`.
+
## Key Takeaways
- The loader always injects `knightedCss` alongside the module’s exports.
- To avoid synthetic defaults (and TypeScript warnings) for modules that only expose named exports, add `&named-only` and use a namespace import.
- Namespace imports plus `KnightedCssCombinedModule` work universally; default imports are optional conveniences when the source module exposes a default you actually consume.
+- Add `&types` when you also need the `stableSelectors` map. Configure the namespace globally (loader option or CLI flag) so runtime + generated types stay consistent.
diff --git a/package-lock.json b/package-lock.json
index a82be7b..c8e291b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -36,8 +36,11 @@
"typescript": "^5.9.3"
},
"engines": {
- "node": ">= 22.15.0",
+ "node": ">= 22.17.0",
"npm": ">= 10.9.0"
+ },
+ "optionalDependencies": {
+ "@oxc-parser/binding-wasm32-wasi": "^0.99.0"
}
},
"node_modules/@babel/code-frame": {
@@ -395,9 +398,9 @@
"license": "MIT"
},
"node_modules/@esbuild/aix-ppc64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz",
- "integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz",
+ "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==",
"cpu": [
"ppc64"
],
@@ -412,9 +415,9 @@
}
},
"node_modules/@esbuild/android-arm": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz",
- "integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz",
+ "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==",
"cpu": [
"arm"
],
@@ -429,9 +432,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz",
- "integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz",
+ "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==",
"cpu": [
"arm64"
],
@@ -446,9 +449,9 @@
}
},
"node_modules/@esbuild/android-x64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz",
- "integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz",
+ "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==",
"cpu": [
"x64"
],
@@ -463,9 +466,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz",
- "integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz",
+ "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==",
"cpu": [
"arm64"
],
@@ -480,9 +483,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz",
- "integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz",
+ "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==",
"cpu": [
"x64"
],
@@ -497,9 +500,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz",
- "integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==",
"cpu": [
"arm64"
],
@@ -514,9 +517,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz",
- "integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz",
+ "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==",
"cpu": [
"x64"
],
@@ -531,9 +534,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz",
- "integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz",
+ "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==",
"cpu": [
"arm"
],
@@ -548,9 +551,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz",
- "integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz",
+ "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==",
"cpu": [
"arm64"
],
@@ -565,9 +568,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz",
- "integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz",
+ "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==",
"cpu": [
"ia32"
],
@@ -582,9 +585,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz",
- "integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz",
+ "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==",
"cpu": [
"loong64"
],
@@ -599,9 +602,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz",
- "integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz",
+ "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==",
"cpu": [
"mips64el"
],
@@ -616,9 +619,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz",
- "integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz",
+ "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==",
"cpu": [
"ppc64"
],
@@ -633,9 +636,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz",
- "integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz",
+ "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==",
"cpu": [
"riscv64"
],
@@ -650,9 +653,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz",
- "integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz",
+ "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==",
"cpu": [
"s390x"
],
@@ -667,9 +670,9 @@
}
},
"node_modules/@esbuild/linux-x64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz",
- "integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz",
+ "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==",
"cpu": [
"x64"
],
@@ -684,9 +687,9 @@
}
},
"node_modules/@esbuild/netbsd-arm64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz",
- "integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==",
"cpu": [
"arm64"
],
@@ -701,9 +704,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz",
- "integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz",
+ "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==",
"cpu": [
"x64"
],
@@ -718,9 +721,9 @@
}
},
"node_modules/@esbuild/openbsd-arm64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz",
- "integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==",
"cpu": [
"arm64"
],
@@ -735,9 +738,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz",
- "integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz",
+ "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==",
"cpu": [
"x64"
],
@@ -752,9 +755,9 @@
}
},
"node_modules/@esbuild/openharmony-arm64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz",
- "integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz",
+ "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==",
"cpu": [
"arm64"
],
@@ -769,9 +772,9 @@
}
},
"node_modules/@esbuild/sunos-x64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz",
- "integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz",
+ "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==",
"cpu": [
"x64"
],
@@ -786,9 +789,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz",
- "integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz",
+ "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==",
"cpu": [
"arm64"
],
@@ -803,9 +806,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz",
- "integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz",
+ "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==",
"cpu": [
"ia32"
],
@@ -820,9 +823,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz",
- "integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz",
+ "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==",
"cpu": [
"x64"
],
@@ -933,6 +936,18 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
+ "node_modules/@isaacs/fs-minipass": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
+ "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.4"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
"node_modules/@istanbuljs/schema": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
@@ -1162,16 +1177,20 @@
}
},
"node_modules/@knighted/jsx": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@knighted/jsx/-/jsx-1.2.1.tgz",
- "integrity": "sha512-zqkzK7wC1IZRdcEaAJGPqiy+YzqtJ5qOBCWiXMiNdipLAJaJgTXMxYTJtZLQCs4ux+HGSm5/OYYdq9tD7JZG0g==",
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@knighted/jsx/-/jsx-1.4.1.tgz",
+ "integrity": "sha512-JHdbNEeTgOL4jBYHFMqBCuHrQ3XnMZ1RZBvjFeam2fNW+S20kLJjxjp4Lz6CO9DCGcAZtbb80UO8XVyCMGb/cQ==",
"license": "MIT",
"dependencies": {
"magic-string": "^0.30.21",
- "oxc-parser": "^0.99.0"
+ "oxc-parser": "^0.99.0",
+ "tar": "^7.4.3"
+ },
+ "bin": {
+ "jsx": "dist/cli/init.js"
},
"engines": {
- "node": ">=22.3.0"
+ "node": ">=22.17.0"
},
"optionalDependencies": {
"@oxc-parser/binding-darwin-arm64": "^0.99.0",
@@ -1387,22 +1406,6 @@
"node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@knighted/jsx/node_modules/@oxc-parser/binding-wasm32-wasi": {
- "version": "0.99.0",
- "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.99.0.tgz",
- "integrity": "sha512-DKA4j0QerUWSMADziLM5sAyM7V53Fj95CV9SjP77bPfEfT7MnvFKnneaRMqPK1cpzjAGiQF52OBUIKyk0dwOQA==",
- "cpu": [
- "wasm32"
- ],
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "@napi-rs/wasm-runtime": "^1.0.7"
- },
- "engines": {
- "node": ">=14.0.0"
- }
- },
"node_modules/@knighted/jsx/node_modules/@oxc-parser/binding-win32-arm64-msvc": {
"version": "0.99.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.99.0.tgz",
@@ -1714,23 +1717,6 @@
"node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@knighted/specifier/node_modules/@oxc-parser/binding-wasm32-wasi": {
- "version": "0.99.0",
- "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.99.0.tgz",
- "integrity": "sha512-DKA4j0QerUWSMADziLM5sAyM7V53Fj95CV9SjP77bPfEfT7MnvFKnneaRMqPK1cpzjAGiQF52OBUIKyk0dwOQA==",
- "cpu": [
- "wasm32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "@napi-rs/wasm-runtime": "^1.0.7"
- },
- "engines": {
- "node": ">=14.0.0"
- }
- },
"node_modules/@knighted/specifier/node_modules/@oxc-parser/binding-win32-arm64-msvc": {
"version": "0.99.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.99.0.tgz",
@@ -2122,17 +2108,16 @@
}
},
"node_modules/@oxc-parser/binding-wasm32-wasi": {
- "version": "0.78.0",
- "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.78.0.tgz",
- "integrity": "sha512-Pq0uT2CuN3J7Tv3KLuO7Sh4C7zTuqdJl0IDg3zB5keKx0BSbaEWewJL2CUNYUlG8txf+sMpUV+bkAIS5MEcKAw==",
+ "version": "0.99.0",
+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.99.0.tgz",
+ "integrity": "sha512-DKA4j0QerUWSMADziLM5sAyM7V53Fj95CV9SjP77bPfEfT7MnvFKnneaRMqPK1cpzjAGiQF52OBUIKyk0dwOQA==",
"cpu": [
"wasm32"
],
- "dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
- "@napi-rs/wasm-runtime": "^1.0.1"
+ "@napi-rs/wasm-runtime": "^1.0.7"
},
"engines": {
"node": ">=14.0.0"
@@ -2639,28 +2624,28 @@
"license": "MIT"
},
"node_modules/@rspack/binding": {
- "version": "1.6.7",
- "resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-1.6.7.tgz",
- "integrity": "sha512-7ICabuBN3gHc6PPN52+m1kruz3ogiJjg1C0gSWdLRk18m/4jlcM2aAy6wfXjgODJdB0Yh2ro/lIpBbj+AYWUGA==",
+ "version": "1.6.8",
+ "resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-1.6.8.tgz",
+ "integrity": "sha512-lUeL4mbwGo+nqRKqFDCm9vH2jv9FNMVt1X8jqayWRcOCPlj/2UVMEFgqjR7Pp2vlvnTKq//31KbDBJmDZq31RQ==",
"dev": true,
"license": "MIT",
"optionalDependencies": {
- "@rspack/binding-darwin-arm64": "1.6.7",
- "@rspack/binding-darwin-x64": "1.6.7",
- "@rspack/binding-linux-arm64-gnu": "1.6.7",
- "@rspack/binding-linux-arm64-musl": "1.6.7",
- "@rspack/binding-linux-x64-gnu": "1.6.7",
- "@rspack/binding-linux-x64-musl": "1.6.7",
- "@rspack/binding-wasm32-wasi": "1.6.7",
- "@rspack/binding-win32-arm64-msvc": "1.6.7",
- "@rspack/binding-win32-ia32-msvc": "1.6.7",
- "@rspack/binding-win32-x64-msvc": "1.6.7"
+ "@rspack/binding-darwin-arm64": "1.6.8",
+ "@rspack/binding-darwin-x64": "1.6.8",
+ "@rspack/binding-linux-arm64-gnu": "1.6.8",
+ "@rspack/binding-linux-arm64-musl": "1.6.8",
+ "@rspack/binding-linux-x64-gnu": "1.6.8",
+ "@rspack/binding-linux-x64-musl": "1.6.8",
+ "@rspack/binding-wasm32-wasi": "1.6.8",
+ "@rspack/binding-win32-arm64-msvc": "1.6.8",
+ "@rspack/binding-win32-ia32-msvc": "1.6.8",
+ "@rspack/binding-win32-x64-msvc": "1.6.8"
}
},
"node_modules/@rspack/binding-darwin-arm64": {
- "version": "1.6.7",
- "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.6.7.tgz",
- "integrity": "sha512-QiIAP8JTAtht0j8/xZZEQTJRB9e+KrOm9c7JJm73CewVg55rDWRrwopiVfBNlTu1coem1ztUHJYdQhg2uXfqww==",
+ "version": "1.6.8",
+ "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.6.8.tgz",
+ "integrity": "sha512-e8CTQtzaeGnf+BIzR7wRMUwKfIg0jd/sxMRc1Vd0bCMHBhSN9EsGoMuJJaKeRrSmy2nwMCNWHIG+TvT1CEKg+A==",
"cpu": [
"arm64"
],
@@ -2672,9 +2657,9 @@
]
},
"node_modules/@rspack/binding-darwin-x64": {
- "version": "1.6.7",
- "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-1.6.7.tgz",
- "integrity": "sha512-DpQRxxTXkMMNPmBXeJBaAB8HmWKxH2IfvHv7vU+kBhJ3xdPtXU4/xBv1W3biluoNRG11gc1WLIgjzeGgaLCxmw==",
+ "version": "1.6.8",
+ "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-1.6.8.tgz",
+ "integrity": "sha512-ku1XpTEPt6Za11zhpFWhfwrTQogcgi9RJrOUVC4FESiPO9aKyd4hJ+JiPgLY0MZOqsptK6vEAgOip+uDVXrCpg==",
"cpu": [
"x64"
],
@@ -2686,9 +2671,9 @@
]
},
"node_modules/@rspack/binding-linux-arm64-gnu": {
- "version": "1.6.7",
- "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.6.7.tgz",
- "integrity": "sha512-211/XoBiooGGgUo/NxNpsrzGUXtH1d7g/4+UTtjYtfc8QHwu7ZMHcsqg0wss53fXzn/yyxd0DZ56vBHq52BiFw==",
+ "version": "1.6.8",
+ "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.6.8.tgz",
+ "integrity": "sha512-fvZX6xZPvBT8qipSpvkKMX5M7yd2BSpZNCZXcefw6gA3uC7LI3gu+er0LrDXY1PtPzVuHTyDx+abwWpagV3PiQ==",
"cpu": [
"arm64"
],
@@ -2700,9 +2685,9 @@
]
},
"node_modules/@rspack/binding-linux-arm64-musl": {
- "version": "1.6.7",
- "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.6.7.tgz",
- "integrity": "sha512-0WnqAWz3WPDsXGvOOA++or7cHpoidVsH3FlqNaAfRu6ni6n7ig/s0/jKUB+C5FtXOgmGjAGkZHfFgNHsvZ0FWw==",
+ "version": "1.6.8",
+ "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.6.8.tgz",
+ "integrity": "sha512-++XMKcMNrt59HcFBLnRaJcn70k3X0GwkAegZBVpel8xYIAgvoXT5+L8P1ExId/yTFxqedaz8DbcxQnNmMozviw==",
"cpu": [
"arm64"
],
@@ -2714,9 +2699,9 @@
]
},
"node_modules/@rspack/binding-linux-x64-gnu": {
- "version": "1.6.7",
- "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.6.7.tgz",
- "integrity": "sha512-iMrE0Q4IuYpkE0MjpaOVaUDYbQFiCRI9D3EPoXzlXJj4kJSdNheODpHTBVRlWt8Xp7UAoWuIFXCvKFKcSMm3aQ==",
+ "version": "1.6.8",
+ "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.6.8.tgz",
+ "integrity": "sha512-tv3BWkTE1TndfX+DsE1rSTg8fBevCxujNZ3MlfZ22Wfy9x1FMXTJlWG8VIOXmaaJ1wUHzv8S7cE2YUUJ2LuiCg==",
"cpu": [
"x64"
],
@@ -2728,9 +2713,9 @@
]
},
"node_modules/@rspack/binding-linux-x64-musl": {
- "version": "1.6.7",
- "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-1.6.7.tgz",
- "integrity": "sha512-e7gKFxpdEQwYGk7lTC/hukTgNtaoAstBXehnZNk4k3kuU6+86WDrkn18Cd949iNqfIPtIG/wIsFNGbkHsH69hQ==",
+ "version": "1.6.8",
+ "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-1.6.8.tgz",
+ "integrity": "sha512-DCGgZ5/in1O3FjHWqXnDsncRy+48cMhfuUAAUyl0yDj1NpsZu9pP+xfGLvGcQTiYrVl7IH9Aojf1eShP/77WGA==",
"cpu": [
"x64"
],
@@ -2742,9 +2727,9 @@
]
},
"node_modules/@rspack/binding-wasm32-wasi": {
- "version": "1.6.7",
- "resolved": "https://registry.npmjs.org/@rspack/binding-wasm32-wasi/-/binding-wasm32-wasi-1.6.7.tgz",
- "integrity": "sha512-yx88EFdE9RP3hh7VhjjW6uc6wGU0KcpOcZp8T8E/a+X8L98fX0aVrtM1IDbndhmdluIMqGbfJNap2+QqOCY9Mw==",
+ "version": "1.6.8",
+ "resolved": "https://registry.npmjs.org/@rspack/binding-wasm32-wasi/-/binding-wasm32-wasi-1.6.8.tgz",
+ "integrity": "sha512-VUwdhl/lI4m6o1OGCZ9JwtMjTV/yLY5VZTQdEPKb40JMTlmZ5MBlr5xk7ByaXXYHr6I+qnqEm73iMKQvg6iknw==",
"cpu": [
"wasm32"
],
@@ -2769,9 +2754,9 @@
}
},
"node_modules/@rspack/binding-win32-arm64-msvc": {
- "version": "1.6.7",
- "resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.6.7.tgz",
- "integrity": "sha512-vgxVYpFK8P5ulSXQQA+EbX78R/SUU+WIf0JIY+LoUoP89gZOsise/lKAJMAybzpeTJ1t0ndLchFznDYnzq+l4Q==",
+ "version": "1.6.8",
+ "resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.6.8.tgz",
+ "integrity": "sha512-23YX7zlOZlub+nPGDBUzktb4D5D6ETUAluKjXEeHIZ9m7fSlEYBnGL66YE+3t1DHXGd0OqsdwlvrNGcyo6EXDQ==",
"cpu": [
"arm64"
],
@@ -2783,9 +2768,9 @@
]
},
"node_modules/@rspack/binding-win32-ia32-msvc": {
- "version": "1.6.7",
- "resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.6.7.tgz",
- "integrity": "sha512-bV5RTW0Va0UQKJm9HWLt7fWNBPaBBBxCJOA2pJT3nGGm6CCXKnZSyEiVbFUk4jI/uiwBfqenlLkzaGoMRbeDhA==",
+ "version": "1.6.8",
+ "resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.6.8.tgz",
+ "integrity": "sha512-cFgRE3APxrY4AEdooVk2LtipwNNT/9mrnjdC5lVbsIsz+SxvGbZR231bxDJEqP15+RJOaD07FO1sIjINFqXMEg==",
"cpu": [
"ia32"
],
@@ -2797,9 +2782,9 @@
]
},
"node_modules/@rspack/binding-win32-x64-msvc": {
- "version": "1.6.7",
- "resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.6.7.tgz",
- "integrity": "sha512-8xlbuJQtYktlBjZupOHlO8FeZqSIhsV3ih7xBSiOYar6LI6uQzA7XiO3I5kaPSDirBMMMKv1Z4rKCxWx10a3TQ==",
+ "version": "1.6.8",
+ "resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.6.8.tgz",
+ "integrity": "sha512-cIuhVsZYd3o3Neo1JSAhJYw6BDvlxaBoqvgwRkG1rs0ExFmEmgYyG7ip9pFKnKNWph/tmW3rDYypmEfjs1is7g==",
"cpu": [
"x64"
],
@@ -2811,9 +2796,9 @@
]
},
"node_modules/@rspack/cli": {
- "version": "1.6.7",
- "resolved": "https://registry.npmjs.org/@rspack/cli/-/cli-1.6.7.tgz",
- "integrity": "sha512-lWU4Gfw3HIysXnBuN1Ta43BeQHewoHd3MAzRlxmEYsUPxau26h6oTpL5WnLsT/Eh4G7XqnW1NJ8COujnn9EGfg==",
+ "version": "1.6.8",
+ "resolved": "https://registry.npmjs.org/@rspack/cli/-/cli-1.6.8.tgz",
+ "integrity": "sha512-pFMYsov8Av7bNWEU9l0HCTk2A5vOPaaZBkZSkCs68U07tkMOQ58IvUiC5Uy1B780bqE2jBt/b6yA41uNmXScZg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2830,15 +2815,15 @@
}
},
"node_modules/@rspack/core": {
- "version": "1.6.7",
- "resolved": "https://registry.npmjs.org/@rspack/core/-/core-1.6.7.tgz",
- "integrity": "sha512-tkd4nSzTf+pDa9OAE4INi/JEa93HNszjWy5C9+trf4ZCXLLHsHxHQFbzoreuz4Vv2PlCWajgvAdiPMV1vGIkuw==",
+ "version": "1.6.8",
+ "resolved": "https://registry.npmjs.org/@rspack/core/-/core-1.6.8.tgz",
+ "integrity": "sha512-FolcIAH5FW4J2FET+qwjd1kNeFbCkd0VLuIHO0thyolEjaPSxw5qxG67DA7BZGm6PVcoiSgPLks1DL6eZ8c+fA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@module-federation/runtime-tools": "0.21.6",
- "@rspack/binding": "1.6.7",
+ "@rspack/binding": "1.6.8",
"@rspack/lite-tapable": "1.1.0"
},
"engines": {
@@ -3123,9 +3108,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "25.0.0",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.0.tgz",
- "integrity": "sha512-rl78HwuZlaDIUSeUKkmogkhebA+8K1Hy7tddZuJ3D0xV8pZSfsYGTsliGUol1JPzu9EKnTxPC4L1fiWouStRew==",
+ "version": "25.0.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz",
+ "integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==",
"devOptional": true,
"license": "MIT",
"dependencies": {
@@ -3273,13 +3258,13 @@
}
},
"node_modules/@typescript-eslint/project-service": {
- "version": "8.49.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.49.0.tgz",
- "integrity": "sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g==",
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.50.0.tgz",
+ "integrity": "sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ==",
"license": "MIT",
"dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.49.0",
- "@typescript-eslint/types": "^8.49.0",
+ "@typescript-eslint/tsconfig-utils": "^8.50.0",
+ "@typescript-eslint/types": "^8.50.0",
"debug": "^4.3.4"
},
"engines": {
@@ -3294,9 +3279,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
- "version": "8.49.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.49.0.tgz",
- "integrity": "sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==",
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.50.0.tgz",
+ "integrity": "sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w==",
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3310,9 +3295,9 @@
}
},
"node_modules/@typescript-eslint/types": {
- "version": "8.49.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.49.0.tgz",
- "integrity": "sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==",
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.50.0.tgz",
+ "integrity": "sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w==",
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3323,15 +3308,15 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.49.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.49.0.tgz",
- "integrity": "sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==",
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.50.0.tgz",
+ "integrity": "sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ==",
"license": "MIT",
"dependencies": {
- "@typescript-eslint/project-service": "8.49.0",
- "@typescript-eslint/tsconfig-utils": "8.49.0",
- "@typescript-eslint/types": "8.49.0",
- "@typescript-eslint/visitor-keys": "8.49.0",
+ "@typescript-eslint/project-service": "8.50.0",
+ "@typescript-eslint/tsconfig-utils": "8.50.0",
+ "@typescript-eslint/types": "8.50.0",
+ "@typescript-eslint/visitor-keys": "8.50.0",
"debug": "^4.3.4",
"minimatch": "^9.0.4",
"semver": "^7.6.0",
@@ -3377,12 +3362,12 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.49.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.49.0.tgz",
- "integrity": "sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==",
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.50.0.tgz",
+ "integrity": "sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q==",
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.49.0",
+ "@typescript-eslint/types": "8.50.0",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
@@ -3404,9 +3389,9 @@
}
},
"node_modules/@vanilla-extract/css": {
- "version": "1.17.5",
- "resolved": "https://registry.npmjs.org/@vanilla-extract/css/-/css-1.17.5.tgz",
- "integrity": "sha512-u29cUVL5Z2qjJ2Eh8pusT1ToGtTeA4eb/y0ygaw2vWv9XFQSixtkBYEsVkrJExSI/0+SR1g8n5NYas4KlWOdfA==",
+ "version": "1.18.0",
+ "resolved": "https://registry.npmjs.org/@vanilla-extract/css/-/css-1.18.0.tgz",
+ "integrity": "sha512-/p0dwOjr0o8gE5BRQ5O9P0u/2DjUd6Zfga2JGmE4KaY7ZITWMszTzk4x4CPlM5cKkRr2ZGzbE6XkuPNfp9shSQ==",
"devOptional": true,
"license": "MIT",
"peer": true,
@@ -3426,9 +3411,9 @@
}
},
"node_modules/@vanilla-extract/integration": {
- "version": "8.0.6",
- "resolved": "https://registry.npmjs.org/@vanilla-extract/integration/-/integration-8.0.6.tgz",
- "integrity": "sha512-BlDtXtb6Fin8XEGwf4BhsJkKQh0rhj/YiN6ylNNOqXtRU0+DQmzE5WGE056ScKg3p5e0IFaeH7PPxuWJca9aXw==",
+ "version": "8.0.7",
+ "resolved": "https://registry.npmjs.org/@vanilla-extract/integration/-/integration-8.0.7.tgz",
+ "integrity": "sha512-ILob4F9cEHXpbWAVt3Y2iaQJpqYq/c/5TJC8Fz58C2XmX3QW2Y589krvViiyJhQfydCGK3EbwPQhVFjQaBeKfg==",
"devOptional": true,
"license": "MIT",
"peer": true,
@@ -3436,7 +3421,7 @@
"@babel/core": "^7.23.9",
"@babel/plugin-syntax-typescript": "^7.23.3",
"@vanilla-extract/babel-plugin-debug-ids": "^1.2.2",
- "@vanilla-extract/css": "^1.17.5",
+ "@vanilla-extract/css": "^1.18.0",
"dedent": "^1.5.3",
"esbuild": "npm:esbuild@>=0.17.6 <0.28.0",
"eval": "0.1.8",
@@ -3551,13 +3536,13 @@
}
},
"node_modules/@vanilla-extract/webpack-plugin": {
- "version": "2.3.24",
- "resolved": "https://registry.npmjs.org/@vanilla-extract/webpack-plugin/-/webpack-plugin-2.3.24.tgz",
- "integrity": "sha512-8NcZaDn4HuV/P882uCZLjabnXX9TQbm70I9kLcqkCFRVE3EzX0EdSrx9YQzr7Hg5wFZpN5oedjK+3AumhSSL3A==",
+ "version": "2.3.25",
+ "resolved": "https://registry.npmjs.org/@vanilla-extract/webpack-plugin/-/webpack-plugin-2.3.25.tgz",
+ "integrity": "sha512-tnaYN3KGF8V9JZ5FuQY9T9/KmLTfiPxGCw+hhIBD7lgn8AKpg53fm+4lJDDSudTFFN3YNiO2WDjHH62IuoyLIQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vanilla-extract/integration": "^8.0.6",
+ "@vanilla-extract/integration": "^8.0.7",
"debug": "^4.3.1",
"loader-utils": "^2.0.0",
"picocolors": "^1.0.0"
@@ -3567,14 +3552,14 @@
}
},
"node_modules/@vue/compiler-core": {
- "version": "3.5.25",
- "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.25.tgz",
- "integrity": "sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==",
+ "version": "3.5.26",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.26.tgz",
+ "integrity": "sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.28.5",
- "@vue/shared": "3.5.25",
- "entities": "^4.5.0",
+ "@vue/shared": "3.5.26",
+ "entities": "^7.0.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.2.1"
}
@@ -3586,26 +3571,26 @@
"license": "MIT"
},
"node_modules/@vue/compiler-dom": {
- "version": "3.5.25",
- "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.25.tgz",
- "integrity": "sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==",
+ "version": "3.5.26",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.26.tgz",
+ "integrity": "sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-core": "3.5.25",
- "@vue/shared": "3.5.25"
+ "@vue/compiler-core": "3.5.26",
+ "@vue/shared": "3.5.26"
}
},
"node_modules/@vue/compiler-sfc": {
- "version": "3.5.25",
- "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.25.tgz",
- "integrity": "sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag==",
+ "version": "3.5.26",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.26.tgz",
+ "integrity": "sha512-egp69qDTSEZcf4bGOSsprUr4xI73wfrY5oRs6GSgXFTiHrWj4Y3X5Ydtip9QMqiCMCPVwLglB9GBxXtTadJ3mA==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.28.5",
- "@vue/compiler-core": "3.5.25",
- "@vue/compiler-dom": "3.5.25",
- "@vue/compiler-ssr": "3.5.25",
- "@vue/shared": "3.5.25",
+ "@vue/compiler-core": "3.5.26",
+ "@vue/compiler-dom": "3.5.26",
+ "@vue/compiler-ssr": "3.5.26",
+ "@vue/shared": "3.5.26",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.21",
"postcss": "^8.5.6",
@@ -3619,19 +3604,19 @@
"license": "MIT"
},
"node_modules/@vue/compiler-ssr": {
- "version": "3.5.25",
- "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.25.tgz",
- "integrity": "sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==",
+ "version": "3.5.26",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.26.tgz",
+ "integrity": "sha512-lZT9/Y0nSIRUPVvapFJEVDbEXruZh2IYHMk2zTtEgJSlP5gVOqeWXH54xDKAaFS4rTnDeDBQUYDtxKyoW9FwDw==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-dom": "3.5.25",
- "@vue/shared": "3.5.25"
+ "@vue/compiler-dom": "3.5.26",
+ "@vue/shared": "3.5.26"
}
},
"node_modules/@vue/shared": {
- "version": "3.5.25",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.25.tgz",
- "integrity": "sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==",
+ "version": "3.5.26",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.26.tgz",
+ "integrity": "sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==",
"license": "MIT"
},
"node_modules/@webassemblyjs/ast": {
@@ -4105,9 +4090,9 @@
"license": "MIT"
},
"node_modules/baseline-browser-mapping": {
- "version": "2.9.6",
- "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.6.tgz",
- "integrity": "sha512-v9BVVpOTLB59C9E7aSnmIF8h7qRsFpx+A2nugVMTszEOMcfjlZMsXRm4LF23I3Z9AJxc8ANpIvzbzONoX9VJlg==",
+ "version": "2.9.11",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz",
+ "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==",
"devOptional": true,
"license": "Apache-2.0",
"bin": {
@@ -4494,9 +4479,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001760",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz",
- "integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==",
+ "version": "1.0.30001761",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001761.tgz",
+ "integrity": "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==",
"devOptional": true,
"funding": [
{
@@ -4556,6 +4541,15 @@
"fsevents": "~2.3.2"
}
},
+ "node_modules/chownr": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
+ "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/chrome-trace-event": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz",
@@ -5019,9 +5013,9 @@
}
},
"node_modules/dedent": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz",
- "integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==",
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz",
+ "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==",
"devOptional": true,
"license": "MIT",
"peerDependencies": {
@@ -5414,9 +5408,9 @@
}
},
"node_modules/entities": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
- "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.0.tgz",
+ "integrity": "sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
@@ -5486,10 +5480,9 @@
}
},
"node_modules/es-module-lexer": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
- "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
- "dev": true,
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz",
+ "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==",
"license": "MIT"
},
"node_modules/es-object-atoms": {
@@ -5506,9 +5499,9 @@
}
},
"node_modules/esbuild": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz",
- "integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz",
+ "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==",
"devOptional": true,
"hasInstallScript": true,
"license": "MIT",
@@ -5519,32 +5512,32 @@
"node": ">=18"
},
"optionalDependencies": {
- "@esbuild/aix-ppc64": "0.27.1",
- "@esbuild/android-arm": "0.27.1",
- "@esbuild/android-arm64": "0.27.1",
- "@esbuild/android-x64": "0.27.1",
- "@esbuild/darwin-arm64": "0.27.1",
- "@esbuild/darwin-x64": "0.27.1",
- "@esbuild/freebsd-arm64": "0.27.1",
- "@esbuild/freebsd-x64": "0.27.1",
- "@esbuild/linux-arm": "0.27.1",
- "@esbuild/linux-arm64": "0.27.1",
- "@esbuild/linux-ia32": "0.27.1",
- "@esbuild/linux-loong64": "0.27.1",
- "@esbuild/linux-mips64el": "0.27.1",
- "@esbuild/linux-ppc64": "0.27.1",
- "@esbuild/linux-riscv64": "0.27.1",
- "@esbuild/linux-s390x": "0.27.1",
- "@esbuild/linux-x64": "0.27.1",
- "@esbuild/netbsd-arm64": "0.27.1",
- "@esbuild/netbsd-x64": "0.27.1",
- "@esbuild/openbsd-arm64": "0.27.1",
- "@esbuild/openbsd-x64": "0.27.1",
- "@esbuild/openharmony-arm64": "0.27.1",
- "@esbuild/sunos-x64": "0.27.1",
- "@esbuild/win32-arm64": "0.27.1",
- "@esbuild/win32-ia32": "0.27.1",
- "@esbuild/win32-x64": "0.27.1"
+ "@esbuild/aix-ppc64": "0.27.2",
+ "@esbuild/android-arm": "0.27.2",
+ "@esbuild/android-arm64": "0.27.2",
+ "@esbuild/android-x64": "0.27.2",
+ "@esbuild/darwin-arm64": "0.27.2",
+ "@esbuild/darwin-x64": "0.27.2",
+ "@esbuild/freebsd-arm64": "0.27.2",
+ "@esbuild/freebsd-x64": "0.27.2",
+ "@esbuild/linux-arm": "0.27.2",
+ "@esbuild/linux-arm64": "0.27.2",
+ "@esbuild/linux-ia32": "0.27.2",
+ "@esbuild/linux-loong64": "0.27.2",
+ "@esbuild/linux-mips64el": "0.27.2",
+ "@esbuild/linux-ppc64": "0.27.2",
+ "@esbuild/linux-riscv64": "0.27.2",
+ "@esbuild/linux-s390x": "0.27.2",
+ "@esbuild/linux-x64": "0.27.2",
+ "@esbuild/netbsd-arm64": "0.27.2",
+ "@esbuild/netbsd-x64": "0.27.2",
+ "@esbuild/openbsd-arm64": "0.27.2",
+ "@esbuild/openbsd-x64": "0.27.2",
+ "@esbuild/openharmony-arm64": "0.27.2",
+ "@esbuild/sunos-x64": "0.27.2",
+ "@esbuild/win32-arm64": "0.27.2",
+ "@esbuild/win32-ia32": "0.27.2",
+ "@esbuild/win32-x64": "0.27.2"
}
},
"node_modules/escalade": {
@@ -5598,15 +5591,6 @@
"source-map": "~0.6.1"
}
},
- "node_modules/escodegen/node_modules/estraverse": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
- "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=4.0"
- }
- },
"node_modules/eslint-scope": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@@ -5621,6 +5605,16 @@
"node": ">=8.0.0"
}
},
+ "node_modules/eslint-scope/node_modules/estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
"node_modules/eslint-visitor-keys": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
@@ -5659,21 +5653,10 @@
"node": ">=4.0"
}
},
- "node_modules/esrecurse/node_modules/estraverse": {
+ "node_modules/estraverse": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
- "dev": true,
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=4.0"
- }
- },
- "node_modules/estraverse": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
- "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
- "dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=4.0"
@@ -7106,10 +7089,11 @@
}
},
"node_modules/less": {
- "version": "4.4.2",
- "resolved": "https://registry.npmjs.org/less/-/less-4.4.2.tgz",
- "integrity": "sha512-j1n1IuTX1VQjIy3tT7cyGbX7nvQOsFLoIqobZv4ttI5axP923gA44zUj6miiA6R5Aoms4sEGVIIcucXUbRI14g==",
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/less/-/less-4.5.1.tgz",
+ "integrity": "sha512-UKgI3/KON4u6ngSsnDADsUERqhZknsVZbnuzlRZXLQCmfC/MDld42fTydUE9B+Mla1AL6SJ/Pp6SlEFi/AVGfw==",
"devOptional": true,
+ "hasInstallScript": true,
"license": "Apache-2.0",
"peer": true,
"dependencies": {
@@ -7858,12 +7842,23 @@
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
- "dev": true,
"license": "ISC",
"engines": {
"node": ">=16 || 14 >=14.17"
}
},
+ "node_modules/minizlib": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz",
+ "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
"node_modules/mlly": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz",
@@ -8112,7 +8107,6 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/node-module-type/-/node-module-type-1.0.4.tgz",
"integrity": "sha512-jJEWdpQFDFhjA87debF8AgaBXPkHyFCw/6atsRwOGu/bpVwje1ZAl8/0WuwxrYNRUdWQ5YNUOT0ARSZeqjFlGA==",
- "dev": true,
"license": "MIT",
"workspaces": [
"test/ambiguous",
@@ -8371,7 +8365,6 @@
"integrity": "sha512-Kw6DlVJCG1HwArP3uF9kXc6nnAahpGaW7kZ7x1O7OugxbjSzkQqdKdA9loXCv7OeksFF/DfnLDupwqUjr1EOYQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@oxc-project/types": "^0.78.0"
},
@@ -8399,6 +8392,23 @@
"@oxc-parser/binding-win32-x64-msvc": "0.78.0"
}
},
+ "node_modules/oxc-parser/node_modules/@oxc-parser/binding-wasm32-wasi": {
+ "version": "0.78.0",
+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.78.0.tgz",
+ "integrity": "sha512-Pq0uT2CuN3J7Tv3KLuO7Sh4C7zTuqdJl0IDg3zB5keKx0BSbaEWewJL2CUNYUlG8txf+sMpUV+bkAIS5MEcKAw==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@napi-rs/wasm-runtime": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
"node_modules/oxlint": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/oxlint/-/oxlint-0.4.4.tgz",
@@ -9344,9 +9354,9 @@
"license": "MIT"
},
"node_modules/sass": {
- "version": "1.96.0",
- "resolved": "https://registry.npmjs.org/sass/-/sass-1.96.0.tgz",
- "integrity": "sha512-8u4xqqUeugGNCYwr9ARNtQKTOj4KmYiJAVKXf2CTIivTCR51j96htbMKWDru8H5SaQWpyVgTfOF8Ylyf5pun1Q==",
+ "version": "1.97.1",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.1.tgz",
+ "integrity": "sha512-uf6HoO8fy6ClsrShvMgaKUn14f2EHQLQRtpsZZLeU/Mv0Q1K5P0+x2uvH6Cub39TVVbWNSrraUhDAoFph6vh0A==",
"devOptional": true,
"license": "MIT",
"peer": true,
@@ -9493,9 +9503,9 @@
}
},
"node_modules/send": {
- "version": "0.19.1",
- "resolved": "https://registry.npmjs.org/send/-/send-0.19.1.tgz",
- "integrity": "sha512-p4rRk4f23ynFEfcD9LA0xRYngj+IyGiEYyqqOak8kaN0TvNmuxC2dcVeBn62GpCeR2CpWqyHCNScTP91QbAVFg==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
+ "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9505,13 +9515,13 @@
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
- "fresh": "0.5.2",
- "http-errors": "2.0.0",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.1",
"mime": "1.6.0",
"ms": "2.1.3",
- "on-finished": "2.4.1",
+ "on-finished": "~2.4.1",
"range-parser": "~1.2.1",
- "statuses": "2.0.1"
+ "statuses": "~2.0.2"
},
"engines": {
"node": ">= 0.8.0"
@@ -9534,33 +9544,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/send/node_modules/http-errors": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
- "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "depd": "2.0.0",
- "inherits": "2.0.4",
- "setprototypeof": "1.2.0",
- "statuses": "2.0.1",
- "toidentifier": "1.0.1"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/send/node_modules/statuses": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
- "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
"node_modules/serialize-javascript": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
@@ -9658,100 +9641,21 @@
}
},
"node_modules/serve-static": {
- "version": "1.16.2",
- "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
- "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
+ "version": "1.16.3",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",
+ "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
"dev": true,
"license": "MIT",
"dependencies": {
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
- "send": "0.19.0"
+ "send": "~0.19.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
- "node_modules/serve-static/node_modules/debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ms": "2.0.0"
- }
- },
- "node_modules/serve-static/node_modules/debug/node_modules/ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/serve-static/node_modules/http-errors": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
- "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "depd": "2.0.0",
- "inherits": "2.0.4",
- "setprototypeof": "1.2.0",
- "statuses": "2.0.1",
- "toidentifier": "1.0.1"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/serve-static/node_modules/send": {
- "version": "0.19.0",
- "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
- "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "debug": "2.6.9",
- "depd": "2.0.0",
- "destroy": "1.2.0",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "etag": "~1.8.1",
- "fresh": "0.5.2",
- "http-errors": "2.0.0",
- "mime": "1.6.0",
- "ms": "2.1.3",
- "on-finished": "2.4.1",
- "range-parser": "~1.2.1",
- "statuses": "2.0.1"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/serve-static/node_modules/send/node_modules/encodeurl": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
- "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/serve-static/node_modules/statuses": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
- "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
@@ -10327,6 +10231,31 @@
"url": "https://opencollective.com/webpack"
}
},
+ "node_modules/tar": {
+ "version": "7.5.2",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz",
+ "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/fs-minipass": "^4.0.0",
+ "chownr": "^3.0.0",
+ "minipass": "^7.1.2",
+ "minizlib": "^3.1.0",
+ "yallist": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tar/node_modules/yallist": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
+ "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/terser": {
"version": "5.44.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz",
@@ -10795,9 +10724,9 @@
}
},
"node_modules/update-browserslist-db": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz",
- "integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==",
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
"devOptional": true,
"funding": [
{
@@ -10940,9 +10869,9 @@
}
},
"node_modules/webpack": {
- "version": "5.103.0",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.103.0.tgz",
- "integrity": "sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==",
+ "version": "5.104.1",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.104.1.tgz",
+ "integrity": "sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -10955,10 +10884,10 @@
"@webassemblyjs/wasm-parser": "^1.14.1",
"acorn": "^8.15.0",
"acorn-import-phases": "^1.0.3",
- "browserslist": "^4.26.3",
+ "browserslist": "^4.28.1",
"chrome-trace-event": "^1.0.2",
- "enhanced-resolve": "^5.17.3",
- "es-module-lexer": "^1.2.1",
+ "enhanced-resolve": "^5.17.4",
+ "es-module-lexer": "^2.0.0",
"eslint-scope": "5.1.1",
"events": "^3.2.0",
"glob-to-regexp": "^0.4.1",
@@ -10969,7 +10898,7 @@
"neo-async": "^2.6.2",
"schema-utils": "^4.3.3",
"tapable": "^2.3.0",
- "terser-webpack-plugin": "^5.3.11",
+ "terser-webpack-plugin": "^5.3.16",
"watchpack": "^2.4.4",
"webpack-sources": "^3.3.3"
},
@@ -11054,7 +10983,6 @@
"integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@discoveryjs/json-ext": "^0.5.0",
"@webpack-cli/configtest": "^2.1.1",
@@ -11592,12 +11520,16 @@
},
"packages/css": {
"name": "@knighted/css",
- "version": "1.0.0-rc.7",
+ "version": "1.0.0-rc.8",
"license": "MIT",
"dependencies": {
"dependency-tree": "^11.2.0",
"es-module-lexer": "^2.0.0",
- "lightningcss": "^1.30.2"
+ "lightningcss": "^1.30.2",
+ "node-module-type": "^1.0.1"
+ },
+ "bin": {
+ "knighted-css-generate-types": "bin/generate-types.js"
},
"peerDependencies": {
"@vanilla-extract/integration": "^8.0.0",
@@ -11616,18 +11548,12 @@
}
}
},
- "packages/css/node_modules/es-module-lexer": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz",
- "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==",
- "license": "MIT"
- },
"packages/playwright": {
"name": "@knighted/css-playwright-fixture",
"version": "0.0.0",
"dependencies": {
- "@knighted/css": "1.0.0-rc.7",
- "@knighted/jsx": "^1.2.1",
+ "@knighted/css": "1.0.0-rc.8",
+ "@knighted/jsx": "^1.4.1",
"lit": "^3.2.1",
"react": "^19.0.0",
"react-dom": "^19.0.0"
diff --git a/package.json b/package.json
index 624555b..c33cd52 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,7 @@
"packages/playwright"
],
"engines": {
- "node": ">= 22.15.0",
+ "node": ">= 22.17.0",
"npm": ">= 10.9.0"
},
"engineStrict": true,
@@ -26,7 +26,7 @@
"clean:deps": "find . -name node_modules -type d -prune -exec rm -rf {} +",
"clean:dist": "find . -name dist -type d -prune -exec rm -rf {} +",
"clean": "npm run clean:deps && npm run clean:dist",
- "setup:jsx-wasm": "node scripts/setup-jsx-wasm.mjs"
+ "setup:jsx-wasm": "npx @knighted/jsx init"
},
"devDependencies": {
"@emnapi/core": "^1.2.0",
@@ -69,5 +69,8 @@
"*": "npm run check:cycles",
"*.{js,jsx,ts,tsx,mjs,cjs,cts,mts}": "oxlint",
"*.{js,jsx,ts,tsx,mjs,cjs,cts,mts,json,md,css,scss,html}": "prettier --check"
+ },
+ "optionalDependencies": {
+ "@oxc-parser/binding-wasm32-wasi": "^0.99.0"
}
}
diff --git a/packages/css/bin/generate-types.js b/packages/css/bin/generate-types.js
new file mode 100755
index 0000000..731f9db
--- /dev/null
+++ b/packages/css/bin/generate-types.js
@@ -0,0 +1,31 @@
+#!/usr/bin/env node
+
+import path from 'node:path'
+import { fileURLToPath, pathToFileURL } from 'node:url'
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url))
+const distPath = path.resolve(__dirname, '../dist/generateTypes.js')
+
+async function main() {
+ try {
+ const mod = await import(pathToFileURL(distPath).href)
+ const runner =
+ typeof mod.runGenerateTypesCli === 'function'
+ ? mod.runGenerateTypesCli
+ : typeof mod.default === 'function'
+ ? mod.default
+ : undefined
+ if (typeof runner !== 'function') {
+ console.error('[knighted-css] Unable to load generateTypes CLI entry point.')
+ process.exitCode = 1
+ return
+ }
+ await runner(process.argv.slice(2))
+ } catch (error) {
+ console.error('[knighted-css] Failed to run generateTypes CLI.')
+ console.error(error)
+ process.exitCode = 1
+ }
+}
+
+void main()
diff --git a/packages/css/loader-queries.d.ts b/packages/css/loader-queries.d.ts
index 0a83678..ef1bfa7 100644
--- a/packages/css/loader-queries.d.ts
+++ b/packages/css/loader-queries.d.ts
@@ -6,6 +6,13 @@ declare module '*?knighted-css' {
export const knightedCss: string
}
+type KnightedCssStableSelectorMap = Readonly>
+
+declare module '*?knighted-css&types' {
+ export const knightedCss: string
+ export const stableSelectors: KnightedCssStableSelectorMap
+}
+
/**
* Ambient declaration for combined loader imports (e.g. "./file.tsx?knighted-css&combined").
* These modules behave like the original module with an additional `knightedCss` export.
@@ -31,3 +38,24 @@ declare module '*?knighted-css&combined&no-default' {
export default combined
export const knightedCss: string
}
+
+declare module '*?knighted-css&combined&types' {
+ const combined: KnightedCssCombinedModule>
+ export default combined
+ export const knightedCss: string
+ export const stableSelectors: KnightedCssStableSelectorMap
+}
+
+declare module '*?knighted-css&combined&named-only&types' {
+ const combined: KnightedCssCombinedModule>
+ export default combined
+ export const knightedCss: string
+ export const stableSelectors: KnightedCssStableSelectorMap
+}
+
+declare module '*?knighted-css&combined&no-default&types' {
+ const combined: KnightedCssCombinedModule>
+ export default combined
+ export const knightedCss: string
+ export const stableSelectors: KnightedCssStableSelectorMap
+}
diff --git a/packages/css/package.json b/packages/css/package.json
index a6a797d..d15f117 100644
--- a/packages/css/package.json
+++ b/packages/css/package.json
@@ -1,6 +1,6 @@
{
"name": "@knighted/css",
- "version": "1.0.0-rc.7",
+ "version": "1.0.0-rc.8",
"description": "A build-time utility that traverses JavaScript/TypeScript module dependency graphs to extract, compile, and optimize all imported CSS into a single, in-memory string.",
"type": "module",
"main": "./dist/css.js",
@@ -13,6 +13,9 @@
"loader-queries": [
"./loader-queries.d.ts"
],
+ "generate-types": [
+ "./dist/generateTypes.d.ts"
+ ],
"*": [
"./types.d.ts"
]
@@ -33,6 +36,11 @@
"types": "./loader-queries.d.ts",
"default": "./loader-queries.d.ts"
},
+ "./generate-types": {
+ "types": "./dist/generateTypes.d.ts",
+ "import": "./dist/generateTypes.js",
+ "default": "./dist/generateTypes.js"
+ },
"./stableSelectors": {
"types": "./dist/stableSelectors.d.ts",
"import": "./dist/stableSelectors.js",
@@ -58,16 +66,20 @@
"vanilla-extract",
"lightningcss"
],
+ "bin": {
+ "knighted-css-generate-types": "./bin/generate-types.js"
+ },
"scripts": {
- "build": "duel",
- "check-types": "tsc --noEmit",
+ "build": "duel && node ./scripts/copy-types-stub.js",
+ "check-types": "tsc --noEmit --project tsconfig.json && tsc --noEmit --project tsconfig.tests.json",
"test": "c8 --reporter=text --reporter=text-summary --reporter=lcov --include \"src/**/*.ts\" tsx --test test/**/*.test.ts",
"prepack": "npm run build"
},
"dependencies": {
"dependency-tree": "^11.2.0",
"es-module-lexer": "^2.0.0",
- "lightningcss": "^1.30.2"
+ "lightningcss": "^1.30.2",
+ "node-module-type": "^1.0.1"
},
"overrides": {
"module-lookup-amd": {
@@ -94,7 +106,9 @@
"dist",
"loader-queries.d.ts",
"types.d.ts",
- "stable"
+ "stable",
+ "bin",
+ "types-stub"
],
"author": "KCM ",
"license": "MIT",
diff --git a/packages/css/scripts/copy-types-stub.js b/packages/css/scripts/copy-types-stub.js
new file mode 100644
index 0000000..6dcbfc5
--- /dev/null
+++ b/packages/css/scripts/copy-types-stub.js
@@ -0,0 +1,24 @@
+import fs from 'node:fs/promises'
+import path from 'node:path'
+import { fileURLToPath } from 'node:url'
+
+const __filename = fileURLToPath(import.meta.url)
+const __dirname = path.dirname(__filename)
+const packageRoot = path.resolve(__dirname, '..')
+const sourceDir = path.join(packageRoot, 'src', 'types-stub')
+const targetDir = path.join(packageRoot, 'types-stub')
+
+async function copyTypesStub() {
+ try {
+ await fs.rm(targetDir, { recursive: true, force: true })
+ await fs.cp(sourceDir, targetDir, { recursive: true })
+ } catch (error) {
+ console.error('[knighted-css] Failed to copy types stub from src/types-stub.')
+ throw error
+ }
+}
+
+copyTypesStub().catch(error => {
+ console.error(error)
+ process.exitCode = 1
+})
diff --git a/packages/css/src/css.ts b/packages/css/src/css.ts
index 09d1b1b..4a2b84c 100644
--- a/packages/css/src/css.ts
+++ b/packages/css/src/css.ts
@@ -237,14 +237,62 @@ async function compileSass(
'Sass',
peerResolver,
)
- const sass = sassModule
+ const sass = resolveSassNamespace(sassModule)
const importer = createSassImporter({ cwd, resolver })
- const result = await sass.compileAsync(filePath, {
- style: 'expanded',
- loadPaths: buildSassLoadPaths(filePath),
- importers: importer ? [importer] : undefined,
+ const loadPaths = buildSassLoadPaths(filePath)
+
+ if (typeof (sass as { compileAsync?: Function }).compileAsync === 'function') {
+ const result = await (
+ sass as { compileAsync: typeof import('sass').compileAsync }
+ ).compileAsync(filePath, {
+ style: 'expanded',
+ loadPaths,
+ importers: importer ? [importer] : undefined,
+ })
+ return result.css
+ }
+
+ if (typeof (sass as { render?: Function }).render === 'function') {
+ return renderLegacySass(
+ sass as { render: typeof import('sass').render },
+ filePath,
+ indented,
+ loadPaths,
+ )
+ }
+
+ throw new Error(
+ '@knighted/css: Installed "sass" package does not expose compileAsync or render APIs. Please update "sass" to a supported version.',
+ )
+}
+
+function renderLegacySass(
+ sass: { render: typeof import('sass').render },
+ filePath: string,
+ indented: boolean,
+ loadPaths: string[],
+): Promise {
+ return new Promise((resolve, reject) => {
+ sass.render(
+ {
+ file: filePath,
+ indentedSyntax: indented,
+ outputStyle: 'expanded',
+ includePaths: loadPaths,
+ },
+ (error, result) => {
+ if (error) {
+ reject(error)
+ return
+ }
+ if (!result || typeof result.css === 'undefined') {
+ resolve('')
+ return
+ }
+ resolve(result.css.toString())
+ },
+ )
})
- return result.css
}
// Ensure Sass can resolve bare module specifiers by walking node_modules folders.
@@ -362,6 +410,31 @@ async function optionalPeer(
}
}
+function resolveSassNamespace(mod: unknown): typeof import('sass') {
+ if (isSassNamespace(mod)) {
+ return mod
+ }
+ if (
+ typeof mod === 'object' &&
+ mod !== null &&
+ 'default' in (mod as Record)
+ ) {
+ const candidate = (mod as Record).default
+ if (isSassNamespace(candidate)) {
+ return candidate
+ }
+ }
+ return mod as typeof import('sass')
+}
+
+function isSassNamespace(candidate: unknown): candidate is typeof import('sass') {
+ if (typeof candidate !== 'object' || !candidate) {
+ return false
+ }
+ const namespace = candidate as Record
+ return typeof namespace.compile === 'function' || typeof namespace.render === 'function'
+}
+
function unwrapModuleNamespace(mod: T): T {
if (
typeof mod === 'object' &&
diff --git a/packages/css/src/generateTypes.ts b/packages/css/src/generateTypes.ts
new file mode 100644
index 0000000..92b49ba
--- /dev/null
+++ b/packages/css/src/generateTypes.ts
@@ -0,0 +1,656 @@
+import crypto from 'node:crypto'
+import fs from 'node:fs/promises'
+import path from 'node:path'
+import { createRequire } from 'node:module'
+import { fileURLToPath, pathToFileURL } from 'node:url'
+
+import { init, parse } from 'es-module-lexer'
+import { moduleType } from 'node-module-type'
+
+import { cssWithMeta } from './css.js'
+import {
+ determineSelectorVariant,
+ hasQueryFlag,
+ TYPES_QUERY_FLAG,
+ type SelectorTypeVariant,
+} from './loaderInternals.js'
+import { buildStableSelectorsLiteral } from './stableSelectorsLiteral.js'
+import { resolveStableNamespace } from './stableNamespace.js'
+
+interface ManifestEntry {
+ file: string
+ hash: string
+}
+
+type Manifest = Record
+
+interface ImportMatch {
+ specifier: string
+ importer: string
+}
+
+interface DeclarationRecord {
+ specifier: string
+ filePath: string
+}
+
+interface GenerateTypesInternalOptions {
+ rootDir: string
+ include: string[]
+ outDir: string
+ typesRoot: string
+ stableNamespace?: string
+}
+
+export interface GenerateTypesResult {
+ written: number
+ removed: number
+ declarations: DeclarationRecord[]
+ warnings: string[]
+ outDir: string
+ typesIndexPath: string
+}
+
+export interface GenerateTypesOptions {
+ rootDir?: string
+ include?: string[]
+ outDir?: string
+ typesRoot?: string
+ stableNamespace?: string
+}
+
+const DEFAULT_SKIP_DIRS = new Set([
+ 'node_modules',
+ '.git',
+ 'dist',
+ 'build',
+ 'coverage',
+ '.knighted-css',
+ '.next',
+ '.nuxt',
+ '.svelte-kit',
+ '.output',
+ 'tmp',
+])
+
+const SUPPORTED_EXTENSIONS = new Set([
+ '.ts',
+ '.tsx',
+ '.js',
+ '.jsx',
+ '.mts',
+ '.cts',
+ '.mjs',
+ '.cjs',
+])
+
+function resolvePackageRoot(): string {
+ const detectedType = moduleType()
+ if (detectedType === 'commonjs' && typeof __dirname === 'string') {
+ return path.resolve(__dirname, '..')
+ }
+ const moduleUrl = getImportMetaUrl()
+ if (moduleUrl) {
+ return path.resolve(path.dirname(fileURLToPath(moduleUrl)), '..')
+ }
+ return path.resolve(process.cwd(), 'node_modules', '@knighted', 'css')
+}
+
+function getImportMetaUrl(): string | undefined {
+ try {
+ return (0, eval)('import.meta.url') as string
+ } catch {
+ return undefined
+ }
+}
+
+const PACKAGE_ROOT = resolvePackageRoot()
+const DEFAULT_TYPES_ROOT = path.join(PACKAGE_ROOT, 'types-stub')
+const DEFAULT_OUT_DIR = path.join(PACKAGE_ROOT, 'node_modules', '.knighted-css')
+
+export async function generateTypes(
+ options: GenerateTypesOptions = {},
+): Promise {
+ const rootDir = path.resolve(options.rootDir ?? process.cwd())
+ const include = normalizeIncludeOptions(options.include, rootDir)
+ const outDir = path.resolve(options.outDir ?? DEFAULT_OUT_DIR)
+ const typesRoot = path.resolve(options.typesRoot ?? DEFAULT_TYPES_ROOT)
+ await init
+ await fs.mkdir(outDir, { recursive: true })
+ await fs.mkdir(typesRoot, { recursive: true })
+
+ const internalOptions: GenerateTypesInternalOptions = {
+ rootDir,
+ include,
+ outDir,
+ typesRoot,
+ stableNamespace: options.stableNamespace,
+ }
+
+ return generateDeclarations(internalOptions)
+}
+
+async function generateDeclarations(
+ options: GenerateTypesInternalOptions,
+): Promise {
+ const peerResolver = createProjectPeerResolver(options.rootDir)
+ const files = await collectCandidateFiles(options.include)
+ const manifestPath = path.join(options.outDir, 'manifest.json')
+ const previousManifest = await readManifest(manifestPath)
+ const nextManifest: Manifest = {}
+ const selectorCache = new Map>()
+ const processedSpecifiers = new Set()
+ const declarations: DeclarationRecord[] = []
+ const warnings: string[] = []
+ let writes = 0
+
+ for (const filePath of files) {
+ const matches = await findSpecifierImports(filePath)
+ for (const match of matches) {
+ const cleaned = match.specifier.trim()
+ const inlineFree = stripInlineLoader(cleaned)
+ if (!inlineFree.includes('?knighted-css')) continue
+ const { resource, query } = splitResourceAndQuery(inlineFree)
+ if (!query || !hasQueryFlag(query, TYPES_QUERY_FLAG)) {
+ continue
+ }
+ if (processedSpecifiers.has(cleaned)) {
+ continue
+ }
+ const resolvedNamespace = resolveStableNamespace(options.stableNamespace)
+ const resolvedPath = await resolveImportPath(
+ resource,
+ match.importer,
+ options.rootDir,
+ )
+ if (!resolvedPath) {
+ warnings.push(
+ `Unable to resolve ${resource} referenced by ${relativeToRoot(match.importer, options.rootDir)}.`,
+ )
+ continue
+ }
+
+ const cacheKey = `${resolvedPath}::${resolvedNamespace}`
+ let selectorMap = selectorCache.get(cacheKey)
+ if (!selectorMap) {
+ try {
+ const { css } = await cssWithMeta(resolvedPath, {
+ cwd: options.rootDir,
+ peerResolver,
+ })
+ selectorMap = buildStableSelectorsLiteral({
+ css,
+ namespace: resolvedNamespace,
+ resourcePath: resolvedPath,
+ emitWarning: message => warnings.push(message),
+ }).selectorMap
+ } catch (error) {
+ warnings.push(
+ `Failed to extract CSS for ${relativeToRoot(resolvedPath, options.rootDir)}: ${formatErrorMessage(error)}`,
+ )
+ continue
+ }
+ selectorCache.set(cacheKey, selectorMap)
+ }
+
+ const variant = determineSelectorVariant(query)
+ const declaration = formatModuleDeclaration(cleaned, variant, selectorMap)
+ const declarationHash = hashContent(declaration)
+ const fileName = buildDeclarationFileName(cleaned)
+ const targetPath = path.join(options.outDir, fileName)
+ const previousEntry = previousManifest[cleaned]
+ const needsWrite =
+ previousEntry?.hash !== declarationHash || !(await fileExists(targetPath))
+ if (needsWrite) {
+ await fs.writeFile(targetPath, declaration, 'utf8')
+ writes += 1
+ }
+ nextManifest[cleaned] = { file: fileName, hash: declarationHash }
+ if (needsWrite) {
+ declarations.push({ specifier: cleaned, filePath: targetPath })
+ }
+ processedSpecifiers.add(cleaned)
+ }
+ }
+
+ const removed = await removeStaleDeclarations(
+ previousManifest,
+ nextManifest,
+ options.outDir,
+ )
+ await writeManifest(manifestPath, nextManifest)
+ const typesIndexPath = path.join(options.typesRoot, 'index.d.ts')
+ await writeTypesIndex(typesIndexPath, nextManifest, options.outDir)
+
+ if (Object.keys(nextManifest).length === 0) {
+ declarations.length = 0
+ }
+
+ return {
+ written: writes,
+ removed,
+ declarations,
+ warnings,
+ outDir: options.outDir,
+ typesIndexPath,
+ }
+}
+
+function normalizeIncludeOptions(
+ include: string[] | undefined,
+ rootDir: string,
+): string[] {
+ if (!include || include.length === 0) {
+ return [rootDir]
+ }
+ return include.map(entry =>
+ path.isAbsolute(entry) ? entry : path.resolve(rootDir, entry),
+ )
+}
+
+async function collectCandidateFiles(entries: string[]): Promise {
+ const files: string[] = []
+ const visited = new Set()
+
+ async function walk(entryPath: string): Promise {
+ const resolved = path.resolve(entryPath)
+ if (visited.has(resolved)) {
+ return
+ }
+ visited.add(resolved)
+ let stat
+ try {
+ stat = await fs.stat(resolved)
+ } catch {
+ return
+ }
+ if (stat.isDirectory()) {
+ const base = path.basename(resolved)
+ if (DEFAULT_SKIP_DIRS.has(base)) {
+ return
+ }
+ const children = await fs.readdir(resolved, { withFileTypes: true })
+ for (const child of children) {
+ await walk(path.join(resolved, child.name))
+ }
+ return
+ }
+ if (!stat.isFile()) {
+ return
+ }
+ const ext = path.extname(resolved).toLowerCase()
+ if (!SUPPORTED_EXTENSIONS.has(ext)) {
+ return
+ }
+ files.push(resolved)
+ }
+
+ for (const entry of entries) {
+ await walk(entry)
+ }
+
+ return files
+}
+
+async function findSpecifierImports(filePath: string): Promise {
+ let source: string
+ try {
+ source = await fs.readFile(filePath, 'utf8')
+ } catch {
+ return []
+ }
+ if (!source.includes('?knighted-css')) {
+ return []
+ }
+ const matches: ImportMatch[] = []
+ const [imports] = parse(source, filePath)
+ for (const record of imports) {
+ const specifier = record.n ?? source.slice(record.s, record.e)
+ if (specifier && specifier.includes('?knighted-css')) {
+ matches.push({ specifier, importer: filePath })
+ }
+ }
+ const requireRegex = /require\((['"])([^'"`]+?\?knighted-css[^'"`]*)\1\)/g
+ let reqMatch: RegExpExecArray | null
+ while ((reqMatch = requireRegex.exec(source)) !== null) {
+ const spec = reqMatch[2]
+ if (spec) {
+ matches.push({ specifier: spec, importer: filePath })
+ }
+ }
+ return matches
+}
+
+function stripInlineLoader(specifier: string): string {
+ const idx = specifier.lastIndexOf('!')
+ return idx >= 0 ? specifier.slice(idx + 1) : specifier
+}
+
+function splitResourceAndQuery(specifier: string): { resource: string; query: string } {
+ const hashIndex = specifier.indexOf('#')
+ const trimmed = hashIndex >= 0 ? specifier.slice(0, hashIndex) : specifier
+ const queryIndex = trimmed.indexOf('?')
+ if (queryIndex < 0) {
+ return { resource: trimmed, query: '' }
+ }
+ return { resource: trimmed.slice(0, queryIndex), query: trimmed.slice(queryIndex) }
+}
+
+const projectRequireCache = new Map>()
+
+async function resolveImportPath(
+ resourceSpecifier: string,
+ importerPath: string,
+ rootDir: string,
+): Promise {
+ if (!resourceSpecifier) return undefined
+ if (resourceSpecifier.startsWith('.')) {
+ return path.resolve(path.dirname(importerPath), resourceSpecifier)
+ }
+ if (resourceSpecifier.startsWith('/')) {
+ return path.resolve(rootDir, resourceSpecifier.slice(1))
+ }
+ const requireFromRoot = getProjectRequire(rootDir)
+ try {
+ return requireFromRoot.resolve(resourceSpecifier)
+ } catch {
+ return undefined
+ }
+}
+
+function buildDeclarationFileName(specifier: string): string {
+ const digest = crypto.createHash('sha1').update(specifier).digest('hex').slice(0, 12)
+ return `knt-${digest}.d.ts`
+}
+
+function formatModuleDeclaration(
+ specifier: string,
+ variant: SelectorTypeVariant,
+ selectors: Map,
+): string {
+ const literalSpecifier = JSON.stringify(specifier)
+ const selectorType = formatSelectorType(selectors)
+ const header = `declare module ${literalSpecifier} {`
+ const footer = '}'
+ if (variant === 'types') {
+ return `${header}
+ export const knightedCss: string
+ export const stableSelectors: ${selectorType}
+${footer}
+`
+ }
+ const stableLine = ` export const stableSelectors: ${selectorType}`
+ const shared = ` const combined: KnightedCssCombinedModule>
+ export const knightedCss: string
+${stableLine}`
+ if (variant === 'combined') {
+ return `${header}
+${shared}
+ export default combined
+${footer}
+`
+ }
+ return `${header}
+${shared}
+${footer}
+`
+}
+
+function formatSelectorType(selectors: Map): string {
+ if (selectors.size === 0) {
+ return 'Readonly>'
+ }
+ const entries = Array.from(selectors.entries()).sort(([a], [b]) => a.localeCompare(b))
+ const lines = entries.map(
+ ([token, selector]) =>
+ ` readonly ${JSON.stringify(token)}: ${JSON.stringify(selector)}`,
+ )
+ return `Readonly<{
+${lines.join('\n')}
+ }>`
+}
+
+function hashContent(content: string): string {
+ return crypto.createHash('sha1').update(content).digest('hex')
+}
+
+async function readManifest(manifestPath: string): Promise {
+ try {
+ const raw = await fs.readFile(manifestPath, 'utf8')
+ return JSON.parse(raw) as Manifest
+ } catch {
+ return {}
+ }
+}
+
+async function writeManifest(manifestPath: string, manifest: Manifest): Promise {
+ await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2), 'utf8')
+}
+
+async function removeStaleDeclarations(
+ previous: Manifest,
+ next: Manifest,
+ outDir: string,
+): Promise {
+ const stale = Object.entries(previous).filter(([specifier]) => !next[specifier])
+ let removed = 0
+ for (const [, entry] of stale) {
+ const targetPath = path.join(outDir, entry.file)
+ try {
+ await fs.unlink(targetPath)
+ removed += 1
+ } catch {
+ // ignore
+ }
+ }
+ return removed
+}
+
+async function writeTypesIndex(
+ indexPath: string,
+ manifest: Manifest,
+ outDir: string,
+): Promise {
+ const header = '// Generated by @knighted/css/generate-types\n// Do not edit.\n'
+ const references = Object.values(manifest)
+ .sort((a, b) => a.file.localeCompare(b.file))
+ .map(entry => {
+ const rel = path
+ .relative(path.dirname(indexPath), path.join(outDir, entry.file))
+ .split(path.sep)
+ .join('/')
+ return `/// `
+ })
+ const content =
+ references.length > 0
+ ? `${header}
+${references.join('\n')}
+`
+ : `${header}
+`
+ await fs.writeFile(indexPath, content, 'utf8')
+}
+
+function formatErrorMessage(error: unknown): string {
+ if (error instanceof Error && typeof error.message === 'string') {
+ return error.message
+ }
+ return String(error)
+}
+
+function relativeToRoot(filePath: string, rootDir: string): string {
+ return path.relative(rootDir, filePath) || filePath
+}
+
+async function fileExists(target: string): Promise {
+ try {
+ await fs.access(target)
+ return true
+ } catch {
+ return false
+ }
+}
+
+function createProjectPeerResolver(rootDir: string) {
+ const resolver = getProjectRequire(rootDir)
+ return async (name: string) => {
+ const resolved = resolver.resolve(name)
+ return import(pathToFileURL(resolved).href)
+ }
+}
+
+function getProjectRequire(rootDir: string): ReturnType {
+ const cached = projectRequireCache.get(rootDir)
+ if (cached) {
+ return cached
+ }
+ const anchor = path.join(rootDir, 'package.json')
+ let loader: ReturnType
+ try {
+ loader = createRequire(anchor)
+ } catch {
+ loader = createRequire(path.join(process.cwd(), 'package.json'))
+ }
+ projectRequireCache.set(rootDir, loader)
+ return loader
+}
+
+export async function runGenerateTypesCli(argv = process.argv.slice(2)): Promise {
+ let parsed: ParsedCliArgs
+ try {
+ parsed = parseCliArgs(argv)
+ } catch (error) {
+ console.error(`[knighted-css] ${formatErrorMessage(error)}`)
+ process.exitCode = 1
+ return
+ }
+ if (parsed.help) {
+ printHelp()
+ return
+ }
+ try {
+ const result = await generateTypes({
+ rootDir: parsed.rootDir,
+ include: parsed.include,
+ outDir: parsed.outDir,
+ typesRoot: parsed.typesRoot,
+ stableNamespace: parsed.stableNamespace,
+ })
+ reportCliResult(result)
+ } catch (error) {
+ console.error('[knighted-css] generate-types failed.')
+ console.error(error)
+ process.exitCode = 1
+ }
+}
+
+export interface ParsedCliArgs {
+ rootDir: string
+ include?: string[]
+ outDir?: string
+ typesRoot?: string
+ stableNamespace?: string
+ help?: boolean
+}
+
+function parseCliArgs(argv: string[]): ParsedCliArgs {
+ let rootDir = process.cwd()
+ const include: string[] = []
+ let outDir: string | undefined
+ let typesRoot: string | undefined
+ let stableNamespace: string | undefined
+
+ for (let i = 0; i < argv.length; i += 1) {
+ const arg = argv[i]
+ if (arg === '--help' || arg === '-h') {
+ return { rootDir, include, outDir, typesRoot, stableNamespace, help: true }
+ }
+ if (arg === '--root' || arg === '-r') {
+ const value = argv[++i]
+ if (!value) {
+ throw new Error('Missing value for --root')
+ }
+ rootDir = path.resolve(value)
+ continue
+ }
+ if (arg === '--include' || arg === '-i') {
+ const value = argv[++i]
+ if (!value) {
+ throw new Error('Missing value for --include')
+ }
+ include.push(value)
+ continue
+ }
+ if (arg === '--out-dir') {
+ const value = argv[++i]
+ if (!value) {
+ throw new Error('Missing value for --out-dir')
+ }
+ outDir = value
+ continue
+ }
+ if (arg === '--types-root') {
+ const value = argv[++i]
+ if (!value) {
+ throw new Error('Missing value for --types-root')
+ }
+ typesRoot = value
+ continue
+ }
+ if (arg === '--stable-namespace') {
+ const value = argv[++i]
+ if (!value) {
+ throw new Error('Missing value for --stable-namespace')
+ }
+ stableNamespace = value
+ continue
+ }
+ if (arg.startsWith('-')) {
+ throw new Error(`Unknown flag: ${arg}`)
+ }
+ include.push(arg)
+ }
+
+ return { rootDir, include, outDir, typesRoot, stableNamespace }
+}
+
+function printHelp(): void {
+ console.log(`Usage: knighted-css-generate-types [options]
+
+Options:
+ -r, --root Project root directory (default: cwd)
+ -i, --include Additional directories/files to scan (repeatable)
+ --out-dir Output directory for generated declarations
+ --types-root Directory for generated @types entrypoint
+ --stable-namespace Stable namespace prefix for generated selector maps
+ -h, --help Show this help message
+`)
+}
+
+function reportCliResult(result: GenerateTypesResult): void {
+ if (result.written === 0 && result.removed === 0) {
+ console.log(
+ '[knighted-css] No changes to ?knighted-css&types declarations (cache is up to date).',
+ )
+ } else {
+ console.log(
+ `[knighted-css] Updated ${result.written} declaration(s), removed ${result.removed}, output in ${result.outDir}.`,
+ )
+ }
+ console.log(`[knighted-css] Type references: ${result.typesIndexPath}`)
+ for (const warning of result.warnings) {
+ console.warn(`[knighted-css] ${warning}`)
+ }
+}
+
+export const __generateTypesInternals = {
+ stripInlineLoader,
+ splitResourceAndQuery,
+ buildDeclarationFileName,
+ formatModuleDeclaration,
+ formatSelectorType,
+ normalizeIncludeOptions,
+ parseCliArgs,
+ printHelp,
+ reportCliResult,
+}
diff --git a/packages/css/src/loader.ts b/packages/css/src/loader.ts
index 125c900..825a1fd 100644
--- a/packages/css/src/loader.ts
+++ b/packages/css/src/loader.ts
@@ -8,13 +8,15 @@ import { cssWithMeta, compileVanillaModule, type CssOptions } from './css.js'
import { detectModuleDefaultExport, type ModuleDefaultSignal } from './moduleInfo.js'
import {
buildSanitizedQuery,
- COMBINED_QUERY_FLAG,
- isQueryFlag,
- NAMED_ONLY_QUERY_FLAGS,
+ hasCombinedQuery,
+ hasNamedOnlyQueryFlag,
+ hasQueryFlag,
shouldEmitCombinedDefault,
shouldForwardDefaultExport,
- splitQuery,
+ TYPES_QUERY_FLAG,
} from './loaderInternals.js'
+import { buildStableSelectorsLiteral } from './stableSelectorsLiteral.js'
+import { resolveStableNamespace } from './stableNamespace.js'
export type KnightedCssCombinedModule = TModule & {
knightedCss: string
@@ -26,6 +28,7 @@ export interface KnightedCssVanillaOptions {
export interface KnightedCssLoaderOptions extends CssOptions {
vanilla?: KnightedCssVanillaOptions
+ stableNamespace?: string
}
const DEFAULT_EXPORT_NAME = 'knightedCss'
@@ -33,9 +36,25 @@ const DEFAULT_EXPORT_NAME = 'knightedCss'
const loader: LoaderDefinitionFunction = async function loader(
source: string | Buffer,
) {
- const { cssOptions, vanillaOptions } = resolveLoaderOptions(this)
+ const {
+ cssOptions,
+ vanillaOptions,
+ stableNamespace: optionNamespace,
+ } = resolveLoaderOptions(this)
+ const resolvedNamespace = resolveStableNamespace(optionNamespace)
+ const typesRequested = hasQueryFlag(this.resourceQuery, TYPES_QUERY_FLAG)
const css = await extractCss(this, cssOptions)
- const injection = buildInjection(css)
+ const stableSelectorsLiteral = typesRequested
+ ? buildStableSelectorsLiteral({
+ css,
+ namespace: resolvedNamespace,
+ resourcePath: this.resourcePath,
+ emitWarning: message => emitKnightedWarning(this, message),
+ })
+ : undefined
+ const injection = buildInjection(css, {
+ stableSelectorsLiteral: stableSelectorsLiteral?.literal,
+ })
const isStyleModule = this.resourcePath.endsWith('.css.ts')
if (isStyleModule) {
const { source: compiledSource } = await compileVanillaModule(
@@ -91,7 +110,9 @@ export const pitch: PitchLoaderDefinitionFunction =
}
const request = buildProxyRequest(this)
- const { cssOptions } = resolveLoaderOptions(this)
+ const { cssOptions, stableNamespace: optionNamespace } = resolveLoaderOptions(this)
+ const typesRequested = hasQueryFlag(this.resourceQuery, TYPES_QUERY_FLAG)
+ const resolvedNamespace = resolveStableNamespace(optionNamespace)
const skipSyntheticDefault = hasNamedOnlyQueryFlag(this.resourceQuery)
const defaultSignalPromise = skipSyntheticDefault
? Promise.resolve('unknown')
@@ -104,7 +125,18 @@ export const pitch: PitchLoaderDefinitionFunction =
skipSyntheticDefault,
detection: defaultSignal,
})
- return createCombinedModule(request, css, { emitDefault })
+ const stableSelectorsLiteral = typesRequested
+ ? buildStableSelectorsLiteral({
+ css,
+ namespace: resolvedNamespace,
+ resourcePath: this.resourcePath,
+ emitWarning: message => emitKnightedWarning(this, message),
+ })
+ : undefined
+ return createCombinedModule(request, css, {
+ emitDefault,
+ stableSelectorsLiteral: stableSelectorsLiteral?.literal,
+ })
},
)
}
@@ -115,11 +147,12 @@ export default loader
function resolveLoaderOptions(ctx: LoaderContext): {
cssOptions: CssOptions
vanillaOptions?: KnightedCssVanillaOptions
+ stableNamespace?: string
} {
const rawOptions = (
typeof ctx.getOptions === 'function' ? ctx.getOptions() : {}
) as KnightedCssLoaderOptions
- const { vanilla, ...rest } = rawOptions
+ const { vanilla, stableNamespace, ...rest } = rawOptions
const cssOptions: CssOptions = {
...rest,
cwd: rest.cwd ?? ctx.rootContext ?? process.cwd(),
@@ -127,6 +160,7 @@ function resolveLoaderOptions(ctx: LoaderContext): {
return {
cssOptions,
vanillaOptions: vanilla,
+ stableNamespace,
}
}
@@ -146,26 +180,15 @@ function toSourceString(source: string | Buffer): string {
return typeof source === 'string' ? source : source.toString('utf8')
}
-function buildInjection(css: string): string {
- return `\n\nexport const ${DEFAULT_EXPORT_NAME} = ${JSON.stringify(css)};\n`
-}
-
-function hasCombinedQuery(query?: string | null): boolean {
- if (!query) return false
- const trimmed = query.startsWith('?') ? query.slice(1) : query
- if (!trimmed) return false
- return trimmed
- .split('&')
- .filter(Boolean)
- .some(part => isQueryFlag(part, COMBINED_QUERY_FLAG))
-}
-
-function hasNamedOnlyQueryFlag(query?: string | null): boolean {
- if (!query) return false
- const entries = splitQuery(query)
- return entries.some(part =>
- NAMED_ONLY_QUERY_FLAGS.some(flag => isQueryFlag(part, flag)),
- )
+function buildInjection(
+ css: string,
+ extras?: { stableSelectorsLiteral?: string },
+): string {
+ const lines = [`\n\nexport const ${DEFAULT_EXPORT_NAME} = ${JSON.stringify(css)};\n`]
+ if (extras?.stableSelectorsLiteral) {
+ lines.push(extras.stableSelectorsLiteral)
+ }
+ return lines.join('')
}
function buildProxyRequest(ctx: LoaderContext): string {
@@ -203,6 +226,7 @@ function stripResourceQuery(request: string): string {
interface CombinedModuleOptions {
emitDefault?: boolean
+ stableSelectorsLiteral?: string
}
function createCombinedModule(
@@ -227,6 +251,21 @@ typeof __knightedModule.default !== 'undefined'
)
}
- lines.push(buildInjection(css))
+ lines.push(
+ buildInjection(css, { stableSelectorsLiteral: options?.stableSelectorsLiteral }),
+ )
return lines.join('\n')
}
+
+function emitKnightedWarning(
+ ctx: LoaderContext,
+ message: string,
+): void {
+ const formatted = `\x1b[33m@knighted/css warning\x1b[0m ${message}`
+ if (typeof ctx.emitWarning === 'function') {
+ ctx.emitWarning(new Error(formatted))
+ return
+ }
+ // eslint-disable-next-line no-console
+ console.warn(formatted)
+}
diff --git a/packages/css/src/loaderInternals.ts b/packages/css/src/loaderInternals.ts
index e57c7eb..8576793 100644
--- a/packages/css/src/loaderInternals.ts
+++ b/packages/css/src/loaderInternals.ts
@@ -1,7 +1,9 @@
import type { ModuleDefaultSignal } from './moduleInfo.js'
export const COMBINED_QUERY_FLAG = 'combined'
+export const TYPES_QUERY_FLAG = 'types'
export const NAMED_ONLY_QUERY_FLAGS = ['named-only', 'no-default'] as const
+export type SelectorTypeVariant = 'types' | 'combined' | 'combinedWithoutDefault'
export function splitQuery(query: string): string[] {
const trimmed = query.startsWith('?') ? query.slice(1) : query
@@ -27,6 +29,9 @@ export function buildSanitizedQuery(query?: string | null): string {
if (isQueryFlag(part, 'knighted-css')) {
return false
}
+ if (isQueryFlag(part, TYPES_QUERY_FLAG)) {
+ return false
+ }
if (NAMED_ONLY_QUERY_FLAGS.some(flag => isQueryFlag(part, flag))) {
return false
}
@@ -35,6 +40,21 @@ export function buildSanitizedQuery(query?: string | null): string {
return entries.length > 0 ? `?${entries.join('&')}` : ''
}
+export function hasQueryFlag(query: string | null | undefined, flag: string): boolean {
+ if (!query) return false
+ const entries = splitQuery(query)
+ if (entries.length === 0) return false
+ return entries.some(part => isQueryFlag(part, flag))
+}
+
+function safeDecode(value: string): string {
+ try {
+ return decodeURIComponent(value)
+ } catch {
+ return value
+ }
+}
+
export function shouldForwardDefaultExport(request: string): boolean {
const [pathPart] = request.split('?')
if (!pathPart) return true
@@ -45,6 +65,21 @@ export function shouldForwardDefaultExport(request: string): boolean {
return true
}
+export function hasCombinedQuery(query?: string | null): boolean {
+ return hasQueryFlag(query, COMBINED_QUERY_FLAG)
+}
+
+export function hasNamedOnlyQueryFlag(query?: string | null): boolean {
+ return NAMED_ONLY_QUERY_FLAGS.some(flag => hasQueryFlag(query, flag))
+}
+
+export function determineSelectorVariant(query?: string | null): SelectorTypeVariant {
+ if (hasCombinedQuery(query)) {
+ return hasNamedOnlyQueryFlag(query) ? 'combinedWithoutDefault' : 'combined'
+ }
+ return 'types'
+}
+
export function shouldEmitCombinedDefault(options: {
detection: ModuleDefaultSignal
request: string
@@ -68,4 +103,5 @@ export function shouldEmitCombinedDefault(options: {
export const __loaderInternals = {
buildSanitizedQuery,
shouldEmitCombinedDefault,
+ determineSelectorVariant,
}
diff --git a/packages/css/src/stableNamespace.ts b/packages/css/src/stableNamespace.ts
new file mode 100644
index 0000000..e2bc489
--- /dev/null
+++ b/packages/css/src/stableNamespace.ts
@@ -0,0 +1,10 @@
+const DEFAULT_STABLE_NAMESPACE = 'knighted'
+
+export function resolveStableNamespace(optionNamespace?: string): string {
+ if (typeof optionNamespace === 'string') {
+ return optionNamespace
+ }
+ return DEFAULT_STABLE_NAMESPACE
+}
+
+export { DEFAULT_STABLE_NAMESPACE }
diff --git a/packages/css/src/stableSelectorsLiteral.ts b/packages/css/src/stableSelectorsLiteral.ts
new file mode 100644
index 0000000..2086c2c
--- /dev/null
+++ b/packages/css/src/stableSelectorsLiteral.ts
@@ -0,0 +1,131 @@
+import { transform as lightningTransform } from 'lightningcss'
+
+import { escapeRegex, serializeSelector } from './helpers.js'
+
+export interface StableSelectorsLiteralResult {
+ literal: string
+ selectorMap: Map
+}
+
+export function buildStableSelectorsLiteral(options: {
+ css: string
+ namespace: string
+ resourcePath: string
+ emitWarning: (message: string) => void
+}): StableSelectorsLiteralResult {
+ const trimmedNamespace = options.namespace.trim()
+ if (!trimmedNamespace) {
+ options.emitWarning(
+ `stableSelectors requested for ${options.resourcePath} but "stableNamespace" resolved to an empty value.`,
+ )
+ return {
+ literal: 'export const stableSelectors = {} as const;\n',
+ selectorMap: new Map(),
+ }
+ }
+
+ const selectorMap = collectStableSelectors(
+ options.css,
+ trimmedNamespace,
+ options.resourcePath,
+ )
+ if (selectorMap.size === 0) {
+ options.emitWarning(
+ `stableSelectors requested for ${options.resourcePath} but no selectors matched namespace "${trimmedNamespace}".`,
+ )
+ }
+
+ return {
+ literal: `export const stableSelectors = ${formatStableSelectorMap(selectorMap)} as const;\n`,
+ selectorMap,
+ }
+}
+
+export function collectStableSelectors(
+ css: string,
+ namespace: string,
+ filename?: string,
+): Map {
+ if (!namespace) return new Map()
+ const astResult = collectStableSelectorsFromAst(css, namespace, filename)
+ if (astResult) {
+ return astResult
+ }
+ return collectStableSelectorsByRegex(css, namespace)
+}
+
+function collectStableSelectorsFromAst(
+ css: string,
+ namespace: string,
+ filename?: string,
+): Map | undefined {
+ try {
+ const tokens = new Map()
+ const escaped = escapeRegex(namespace)
+ const pattern = new RegExp(`\\.${escaped}-([A-Za-z0-9_-]+)`, 'g')
+ lightningTransform({
+ filename: filename ?? 'knighted-types-probe.css',
+ code: Buffer.from(css),
+ minify: false,
+ visitor: {
+ Rule: {
+ style(rule: any) {
+ const target = Array.isArray(rule?.selectors)
+ ? rule
+ : rule?.value && Array.isArray(rule.value.selectors)
+ ? rule.value
+ : undefined
+ if (!target) return rule
+ for (const selector of target.selectors) {
+ const selectorStr = serializeSelector(selector as any)
+ pattern.lastIndex = 0
+ let match: RegExpExecArray | null
+ while ((match = pattern.exec(selectorStr)) !== null) {
+ const token = match[1]
+ if (!token) continue
+ tokens.set(token, `${namespace}-${token}`)
+ }
+ }
+ return rule
+ },
+ },
+ },
+ })
+ return tokens
+ } catch {
+ return undefined
+ }
+}
+
+function collectStableSelectorsByRegex(
+ css: string,
+ namespace: string,
+): Map {
+ const escaped = escapeRegex(namespace)
+ const pattern = new RegExp(`\\.${escaped}-([A-Za-z0-9_-]+)`, 'g')
+ const tokens = new Map()
+ let match: RegExpExecArray | null
+ while ((match = pattern.exec(css)) !== null) {
+ const token = match[1]
+ if (!token) continue
+ tokens.set(token, `${namespace}-${token}`)
+ }
+ return tokens
+}
+
+export function formatStableSelectorMap(map: Map): string {
+ if (map.size === 0) {
+ return 'Object.freeze({})'
+ }
+ const entries = Array.from(map.entries()).sort(([a], [b]) => a.localeCompare(b))
+ const lines = entries.map(([token, selector]) => {
+ return ` ${JSON.stringify(token)}: ${JSON.stringify(selector)}`
+ })
+ return `Object.freeze({\n${lines.join(',\n')}\n})`
+}
+
+export const __stableSelectorsLiteralInternals = {
+ collectStableSelectors,
+ collectStableSelectorsByRegex,
+ formatStableSelectorMap,
+}
diff --git a/packages/css/src/types-stub/index.d.ts b/packages/css/src/types-stub/index.d.ts
new file mode 100644
index 0000000..80195d5
--- /dev/null
+++ b/packages/css/src/types-stub/index.d.ts
@@ -0,0 +1,5 @@
+// Placeholder stub for @knighted/css stable selector declarations.
+// Running `knighted-css-generate-types` replaces this file with project-specific
+// references into the generated `.knighted-css` manifest.
+
+export {}
diff --git a/packages/css/test/compileSassFallbacks.test.ts b/packages/css/test/compileSassFallbacks.test.ts
new file mode 100644
index 0000000..4ad548e
--- /dev/null
+++ b/packages/css/test/compileSassFallbacks.test.ts
@@ -0,0 +1,150 @@
+import assert from 'node:assert/strict'
+import path from 'node:path'
+import test from 'node:test'
+import { fileURLToPath } from 'node:url'
+
+import { cssWithMeta } from '../src/css.js'
+
+import type { LegacyException, LegacyFileOptions, LegacyResult } from 'sass'
+
+type CompileAsyncArgs = Parameters<(typeof import('sass'))['compileAsync']>
+type RenderArgs = Parameters<(typeof import('sass'))['render']>
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url))
+const sassEntry = path.join(__dirname, 'fixtures/dialects/sass/styles.scss')
+const lessEntry = path.join(__dirname, 'fixtures/dialects/less/theme.less')
+
+function createLegacyResult(css: string): LegacyResult {
+ const timestamp = Date.now()
+ return {
+ css: Buffer.from(css),
+ map: undefined,
+ stats: {
+ entry: sassEntry,
+ start: timestamp,
+ end: timestamp,
+ duration: 0,
+ includedFiles: [],
+ },
+ }
+}
+
+function createLegacyException(message: string): LegacyException {
+ const error = new Error(message) as LegacyException
+ error.formatted = message
+ error.status = 1
+ return error
+}
+
+test('compileSass prefers compileAsync from the namespace default export', async () => {
+ const calls: CompileAsyncArgs[] = []
+ const cssOutput = '.async { color: salmon; }'
+
+ const peerResolver = async (name: string) => {
+ assert.equal(name, 'sass')
+ return {
+ default: {
+ compile() {},
+ async compileAsync(...args: CompileAsyncArgs) {
+ calls.push(args)
+ return { css: cssOutput }
+ },
+ },
+ }
+ }
+
+ const result = await cssWithMeta(sassEntry, { peerResolver })
+
+ assert.equal(result.css, cssOutput)
+ assert.ok(calls.length >= 1)
+ const [filePath, options] = calls[0]
+ assert.equal(filePath, sassEntry)
+ if (!options) {
+ assert.fail('compileAsync was invoked without options')
+ }
+ const loadPaths = Array.isArray(options.loadPaths) ? options.loadPaths : []
+ assert.ok(loadPaths.length > 0)
+})
+
+test('compileSass falls back to render when compileAsync is missing', async () => {
+ const calls: RenderArgs[] = []
+ const cssOutput = '.legacy { color: teal; }'
+
+ const peerResolver = async () => ({
+ render(options: RenderArgs[0], callback: RenderArgs[1]) {
+ calls.push([options, callback])
+ setImmediate(() => callback(undefined, createLegacyResult(cssOutput)))
+ },
+ })
+
+ const result = await cssWithMeta(sassEntry, { peerResolver })
+
+ assert.equal(result.css, cssOutput)
+ assert.equal(calls.length, 1)
+ const [options] = calls[0] as [LegacyFileOptions<'async'>, RenderArgs[1]]
+ assert.equal(options.file, sassEntry)
+ assert.ok(Array.isArray(options.includePaths))
+})
+
+test('renderLegacySass resolves empty string when result payload is missing', async () => {
+ const peerResolver = async () => ({
+ render(options: RenderArgs[0], callback: RenderArgs[1]) {
+ setImmediate(() => callback(undefined, undefined))
+ },
+ })
+
+ const result = await cssWithMeta(sassEntry, { peerResolver })
+
+ assert.equal(result.css, '')
+})
+
+test('renderLegacySass rejects when the renderer throws', async () => {
+ const peerResolver = async () => ({
+ render(options: RenderArgs[0], callback: RenderArgs[1]) {
+ setImmediate(() => callback(createLegacyException('sass boom')))
+ },
+ })
+
+ await assert.rejects(() => cssWithMeta(sassEntry, { peerResolver }), /sass boom/)
+})
+
+test('compileSass throws when Sass peer lacks supported APIs', async () => {
+ const peerResolver = async () => ({})
+
+ await assert.rejects(
+ () => cssWithMeta(sassEntry, { peerResolver }),
+ /does not expose compileAsync or render APIs/i,
+ )
+})
+
+test('resolveSassNamespace ignores falsy default exports', async () => {
+ const peerResolver = async () => ({
+ default: null,
+ })
+
+ await assert.rejects(
+ () => cssWithMeta(sassEntry, { peerResolver }),
+ /does not expose compileAsync or render APIs/i,
+ )
+})
+
+test('compileLess accepts modules without default exports', async () => {
+ const peerResolver = async (name: string) => {
+ if (name === 'less') {
+ return {
+ render() {
+ return Promise.resolve({
+ css: '.less { color: blue; }',
+ map: '',
+ imports: [],
+ })
+ },
+ }
+ }
+ return import(name)
+ }
+
+ const result = await cssWithMeta(lessEntry, { peerResolver })
+
+ assert.equal(result.css, '.less { color: blue; }')
+})
diff --git a/packages/css/test/fixtures/dialects/basic/styles.css b/packages/css/test/fixtures/dialects/basic/styles.css
index 4584ac8..e537e8c 100644
--- a/packages/css/test/fixtures/dialects/basic/styles.css
+++ b/packages/css/test/fixtures/dialects/basic/styles.css
@@ -1,3 +1,15 @@
.demo {
color: rebeccapurple;
}
+
+.knighted-demo {
+ color: teal;
+}
+
+.knighted-icon {
+ display: inline-flex;
+}
+
+.acme-card {
+ border: 1px solid currentColor;
+}
diff --git a/packages/css/test/generateTypes.test.ts b/packages/css/test/generateTypes.test.ts
new file mode 100644
index 0000000..d1c9277
--- /dev/null
+++ b/packages/css/test/generateTypes.test.ts
@@ -0,0 +1,190 @@
+import assert from 'node:assert/strict'
+import fs from 'node:fs/promises'
+import os from 'node:os'
+import path from 'node:path'
+import test from 'node:test'
+import { fileURLToPath } from 'node:url'
+
+import {
+ generateTypes,
+ __generateTypesInternals,
+ type ParsedCliArgs,
+} from '../src/generateTypes.ts'
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url))
+
+async function setupFixtureProject(): Promise<{
+ root: string
+ cleanup: () => Promise
+}> {
+ const tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'knighted-generate-types-'))
+ const srcDir = path.join(tmpRoot, 'src')
+ await fs.mkdir(srcDir, { recursive: true })
+ const fixtureSource = path.join(__dirname, 'fixtures', 'dialects', 'basic', 'entry.js')
+ const relativeImport = path.relative(srcDir, fixtureSource).split(path.sep).join('/')
+ const specifier = `${relativeImport}?knighted-css&types`
+ const entrySource = `import { stableSelectors } from '${specifier}'
+console.log(stableSelectors.demo)
+`
+ await fs.writeFile(path.join(srcDir, 'entry.ts'), entrySource)
+ return {
+ root: tmpRoot,
+ cleanup: () => fs.rm(tmpRoot, { recursive: true, force: true }),
+ }
+}
+
+test('generateTypes emits declarations and reuses cache', async () => {
+ const project = await setupFixtureProject()
+ try {
+ const outDir = path.join(project.root, '.knighted-css-test')
+ const typesRoot = path.join(project.root, '.knighted-css-types')
+ const sharedOptions = { rootDir: project.root, include: ['src'], outDir, typesRoot }
+
+ const firstRun = await generateTypes(sharedOptions)
+ assert.ok(firstRun.written >= 1)
+ assert.equal(firstRun.removed, 0)
+ assert.equal(firstRun.warnings.length, 0)
+
+ const manifestPath = path.join(firstRun.outDir, 'manifest.json')
+ const manifestRaw = await fs.readFile(manifestPath, 'utf8')
+ const manifest = JSON.parse(manifestRaw) as Record
+ const entries = Object.values(manifest)
+ assert.equal(entries.length, 1)
+ const declarationPath = path.join(firstRun.outDir, entries[0]?.file ?? '')
+ const declaration = await fs.readFile(declarationPath, 'utf8')
+ assert.ok(declaration.includes('stableSelectors'))
+ assert.ok(declaration.includes('knighted-demo'))
+
+ const indexContent = await fs.readFile(firstRun.typesIndexPath, 'utf8')
+ assert.ok(indexContent.includes(entries[0]?.file ?? ''))
+
+ const secondRun = await generateTypes(sharedOptions)
+ assert.equal(secondRun.written, 0)
+ assert.equal(secondRun.removed, 0)
+ assert.equal(secondRun.warnings.length, 0)
+ } finally {
+ await project.cleanup()
+ }
+})
+
+test('generateTypes internals format selector-aware declarations', () => {
+ const {
+ stripInlineLoader,
+ splitResourceAndQuery,
+ buildDeclarationFileName,
+ formatSelectorType,
+ formatModuleDeclaration,
+ normalizeIncludeOptions,
+ parseCliArgs,
+ printHelp,
+ reportCliResult,
+ } = __generateTypesInternals
+
+ assert.equal(
+ stripInlineLoader('style-loader!css-loader!./demo.css?knighted-css&types'),
+ './demo.css?knighted-css&types',
+ )
+
+ assert.deepEqual(splitResourceAndQuery('./demo.css?knighted-css#hash'), {
+ resource: './demo.css',
+ query: '?knighted-css',
+ })
+
+ const selectorMap = new Map([
+ ['beta', 'knighted-beta'],
+ ['alpha', 'knighted-alpha'],
+ ])
+ const hashedName = buildDeclarationFileName('./demo.css?knighted-css')
+ assert.match(hashedName, /^knt-[a-f0-9]{12}\.d\.ts$/)
+ const selectorType = formatSelectorType(selectorMap)
+ assert.match(selectorType, /readonly "alpha": "knighted-alpha"/)
+ assert.match(selectorType, /readonly "beta": "knighted-beta"/)
+
+ const declaration = formatModuleDeclaration(
+ './demo.css?knighted-css&combined',
+ 'combined',
+ selectorMap,
+ )
+ assert.match(declaration, /declare module/)
+ assert.match(declaration, /export const stableSelectors/)
+
+ const withoutDefault = formatModuleDeclaration(
+ './demo.css?knighted-css&combined&named-only',
+ 'combinedWithoutDefault',
+ selectorMap,
+ )
+ assert.doesNotMatch(withoutDefault, /export default/)
+
+ const normalized = normalizeIncludeOptions(undefined, '/tmp/demo')
+ assert.deepEqual(normalized, ['/tmp/demo'])
+ assert.deepEqual(normalizeIncludeOptions(['./src'], '/tmp/demo'), [
+ path.resolve('/tmp/demo', './src'),
+ ])
+
+ const parsed = parseCliArgs([
+ '--root',
+ '/tmp/project',
+ '--include',
+ 'src',
+ '--stable-namespace',
+ 'storybook',
+ '--out-dir',
+ '.knighted-css',
+ '--types-root',
+ './types',
+ ]) as ParsedCliArgs
+ assert.equal(parsed.rootDir, path.resolve('/tmp/project'))
+ assert.deepEqual(parsed.include, ['src'])
+ assert.equal(parsed.stableNamespace, 'storybook')
+
+ assert.throws(() => parseCliArgs(['--root']), /Missing value/)
+ assert.throws(() => parseCliArgs(['--stable-namespace']), /Missing value/)
+ assert.throws(() => parseCliArgs(['--wat']), /Unknown flag/)
+
+ const printed: string[] = []
+ const logged = console.log
+ try {
+ console.log = (message: string) => printed.push(message)
+ printHelp()
+ } finally {
+ console.log = logged
+ }
+ assert.ok(printed.join('\n').includes('Usage: knighted-css-generate-types'))
+
+ const summaryLogs: string[] = []
+ const summaryWarns: string[] = []
+ const originalLog = console.log
+ const originalWarn = console.warn
+ try {
+ console.log = (msg: string) => summaryLogs.push(msg)
+ console.warn = (msg: string) => summaryWarns.push(msg)
+ reportCliResult({
+ written: 0,
+ removed: 0,
+ declarations: [],
+ warnings: ['warn'],
+ outDir: '/tmp/types',
+ typesIndexPath: '/tmp/types/index.d.ts',
+ })
+ reportCliResult({
+ written: 2,
+ removed: 1,
+ declarations: [],
+ warnings: [],
+ outDir: '/tmp/types',
+ typesIndexPath: '/tmp/types/index.d.ts',
+ })
+ } finally {
+ console.log = originalLog
+ console.warn = originalWarn
+ }
+ assert.ok(
+ summaryLogs.some(log =>
+ log.includes(
+ 'No changes to ?knighted-css&types declarations (cache is up to date).',
+ ),
+ ),
+ )
+ assert.ok(summaryLogs.some(log => log.includes('Updated 2 declaration(s)')))
+ assert.equal(summaryWarns.length, 1)
+})
diff --git a/packages/css/test/helpers/resolver-fixture.ts b/packages/css/test/helpers/resolver-fixture.ts
index 46b1347..1314f37 100644
--- a/packages/css/test/helpers/resolver-fixture.ts
+++ b/packages/css/test/helpers/resolver-fixture.ts
@@ -1,6 +1,6 @@
import path from 'node:path'
-import type { CssResolver } from '../../src/css'
+import type { CssResolver } from '../../src/css.js'
const fixturesRoot = path.resolve(
path.dirname(new URL(import.meta.url).pathname),
@@ -44,7 +44,7 @@ export function createResolverFixture(name: FixtureName): ResolverFixture {
const projectDir = path.join(fixturesRoot, name)
const entryFile = path.join(projectDir, config.entry)
- const resolver: CssResolver = async specifier => {
+ const resolver: CssResolver = async (specifier: string) => {
if (specifier === config.specifier) {
return entryFile
}
diff --git a/packages/css/test/loaderInternals.test.ts b/packages/css/test/loaderInternals.test.ts
index b2c0017..e754a74 100644
--- a/packages/css/test/loaderInternals.test.ts
+++ b/packages/css/test/loaderInternals.test.ts
@@ -9,6 +9,12 @@ test('buildSanitizedQuery strips loader-specific flags', () => {
assert.equal(buildSanitizedQuery(query), '?foo=bar&baz=qux')
})
+test('buildSanitizedQuery leaves arbitrary params intact', () => {
+ const { buildSanitizedQuery } = __loaderInternals
+ const query = '?knighted-css&types&stableNamespace=acme&foo=1'
+ assert.equal(buildSanitizedQuery(query), '?stableNamespace=acme&foo=1')
+})
+
test('shouldEmitCombinedDefault honors skip flag, detection signals, and css modules', () => {
const { shouldEmitCombinedDefault } = __loaderInternals
@@ -57,3 +63,13 @@ test('shouldEmitCombinedDefault honors skip flag, detection signals, and css mod
false,
)
})
+
+test('determineSelectorVariant differentiates combined permutations', () => {
+ const { determineSelectorVariant } = __loaderInternals
+ assert.equal(determineSelectorVariant('?knighted-css&types'), 'types')
+ assert.equal(determineSelectorVariant('?knighted-css&combined'), 'combined')
+ assert.equal(
+ determineSelectorVariant('?knighted-css&combined&named-only'),
+ 'combinedWithoutDefault',
+ )
+})
diff --git a/packages/css/test/loader_unit.test.ts b/packages/css/test/loader_unit.test.ts
index dfaad61..6678763 100644
--- a/packages/css/test/loader_unit.test.ts
+++ b/packages/css/test/loader_unit.test.ts
@@ -158,6 +158,75 @@ test('loader falls back to process.cwd when no cwd hints are provided', async ()
assert.ok(ctx.added.size > 0, 'should still register dependencies')
})
+test('loader emits stableSelectors export when ?types flag is present', async () => {
+ const resourcePath = path.resolve(__dirname, 'fixtures/dialects/basic/entry.js')
+ const ctx = createMockContext({
+ resourcePath,
+ resourceQuery: '?knighted-css&types',
+ })
+ const output = String(
+ await loader.call(
+ ctx as LoaderContext,
+ "export const noop = ''",
+ ),
+ )
+
+ assert.match(output, /export const stableSelectors = /)
+ assert.match(
+ output,
+ /export const stableSelectors = Object\.freeze\(\{\s*"demo": "knighted-demo",\s*"icon": "knighted-icon"\s*\}\) as const;/,
+ 'should emit map of detected selectors using default namespace',
+ )
+})
+
+test('loader respects stableNamespace loader option', async () => {
+ const resourcePath = path.resolve(__dirname, 'fixtures/dialects/basic/entry.js')
+ const ctx = createMockContext({
+ resourcePath,
+ resourceQuery: '?knighted-css&types',
+ getOptions: () => ({ stableNamespace: 'acme' }),
+ })
+ const output = String(
+ await loader.call(
+ ctx as LoaderContext,
+ "export const noop = ''",
+ ),
+ )
+
+ assert.match(
+ output,
+ /export const stableSelectors = Object\.freeze\(\{\s*"card": "acme-card"\s*\}\) as const;/,
+ 'should scope selector discovery to provided namespace',
+ )
+})
+
+test('loader warns when stableNamespace option resolves to empty value', async () => {
+ const resourcePath = path.resolve(__dirname, 'fixtures/dialects/basic/entry.js')
+ const warnings: string[] = []
+ const ctx = createMockContext({
+ resourcePath,
+ resourceQuery: '?knighted-css&types',
+ getOptions: () => ({ stableNamespace: ' ' }),
+ emitWarning: (err: Error) => {
+ warnings.push(err.message)
+ },
+ })
+ const output = String(
+ await loader.call(
+ ctx as LoaderContext,
+ "export const noop = ''",
+ ),
+ )
+
+ assert.match(output, /export const stableSelectors = \{\} as const;/)
+ assert.equal(warnings.length, 1)
+ assert.match(
+ warnings[0] ?? '',
+ /empty value/,
+ 'warning should describe empty namespace configuration',
+ )
+})
+
test('pitch returns combined module when query includes combined flag', async () => {
const resourcePath = path.resolve(__dirname, 'fixtures/dialects/basic/entry.js')
const ctx = createMockContext({
@@ -184,6 +253,31 @@ test('pitch returns combined module when query includes combined flag', async ()
assert.ok(ctx.added.size > 0, 'pitch should still register dependencies')
})
+test('pitch injects stableSelectors export when combined types query is used', async () => {
+ const resourcePath = path.resolve(__dirname, 'fixtures/dialects/basic/entry.js')
+ const ctx = createMockContext({
+ resourcePath,
+ resourceQuery: '?knighted-css&combined&types',
+ loadModule: (_request, callback) => {
+ callback(null, 'export const Button = () => "ok";')
+ },
+ })
+
+ const result = await pitch.call(
+ ctx as LoaderContext,
+ `${resourcePath}?knighted-css&combined&types`,
+ '',
+ {},
+ )
+
+ const combinedOutput = String(result ?? '')
+ assert.match(
+ combinedOutput,
+ /export const stableSelectors = Object\.freeze\(\{\s*"demo": "knighted-demo",\s*"icon": "knighted-icon"\s*\}\) as const;/,
+ 'combined proxy should forward stable selector map',
+ )
+})
+
test('pitch returns undefined when combined flag is missing', async () => {
const resourcePath = path.resolve(__dirname, 'fixtures/dialects/basic/entry.js')
const ctx = createMockContext({
diff --git a/packages/css/test/resolvers.test.ts b/packages/css/test/resolvers.test.ts
index 6864cc6..351782f 100644
--- a/packages/css/test/resolvers.test.ts
+++ b/packages/css/test/resolvers.test.ts
@@ -1,8 +1,8 @@
import assert from 'node:assert/strict'
import test from 'node:test'
-import { css } from '../src/css'
-import { createResolverFixture } from './helpers/resolver-fixture'
+import { css } from '../src/css.js'
+import { createResolverFixture } from './helpers/resolver-fixture.js'
const projects = ['rspack', 'vite', 'webpack'] as const
diff --git a/packages/css/test/stableSelectorsLiteral.test.ts b/packages/css/test/stableSelectorsLiteral.test.ts
new file mode 100644
index 0000000..c29ef4e
--- /dev/null
+++ b/packages/css/test/stableSelectorsLiteral.test.ts
@@ -0,0 +1,40 @@
+import assert from 'node:assert/strict'
+import test from 'node:test'
+
+import {
+ buildStableSelectorsLiteral,
+ __stableSelectorsLiteralInternals,
+} from '../src/stableSelectorsLiteral.ts'
+
+test('buildStableSelectorsLiteral warns when namespace is empty', () => {
+ const warnings: string[] = []
+ const result = buildStableSelectorsLiteral({
+ css: '.knighted-card {}',
+ namespace: ' ',
+ resourcePath: 'demo.css',
+ emitWarning: message => warnings.push(message),
+ })
+ assert.equal(result.literal.trim(), 'export const stableSelectors = {} as const;')
+ assert.equal(result.selectorMap.size, 0)
+ assert.equal(warnings.length, 1)
+})
+
+test('collectStableSelectors captures selectors and formats map output', () => {
+ const { collectStableSelectors, formatStableSelectorMap } =
+ __stableSelectorsLiteralInternals
+ const css = '.knighted-card {} .knighted-badge {}'
+ const map = collectStableSelectors(css, 'knighted')
+ assert.equal(map.size, 2)
+ assert.equal(map.get('card'), 'knighted-card')
+ const formatted = formatStableSelectorMap(map)
+ assert.match(formatted, /"badge": "knighted-badge"/)
+ assert.match(formatted, /"card": "knighted-card"/)
+})
+
+test('collectStableSelectorsByRegex handles parser fallbacks', () => {
+ const { collectStableSelectorsByRegex } = __stableSelectorsLiteralInternals
+ const css = '.custom-card {} .custom-chip {}'
+ const map = collectStableSelectorsByRegex(css, 'custom')
+ assert.equal(map.size, 2)
+ assert.equal(map.get('chip'), 'custom-chip')
+})
diff --git a/packages/css/tsconfig.tests.json b/packages/css/tsconfig.tests.json
new file mode 100644
index 0000000..7b90de0
--- /dev/null
+++ b/packages/css/tsconfig.tests.json
@@ -0,0 +1,8 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "allowImportingTsExtensions": true,
+ "noEmit": true
+ },
+ "include": ["test/**/*.ts"]
+}
diff --git a/packages/css/types-stub/index.d.ts b/packages/css/types-stub/index.d.ts
new file mode 100644
index 0000000..80195d5
--- /dev/null
+++ b/packages/css/types-stub/index.d.ts
@@ -0,0 +1,5 @@
+// Placeholder stub for @knighted/css stable selector declarations.
+// Running `knighted-css-generate-types` replaces this file with project-specific
+// references into the generated `.knighted-css` manifest.
+
+export {}
diff --git a/packages/css/types.d.ts b/packages/css/types.d.ts
index 5c2bda0..de0f4d6 100644
--- a/packages/css/types.d.ts
+++ b/packages/css/types.d.ts
@@ -1,3 +1,4 @@
///
+///
export * from './dist/css.js'
diff --git a/packages/playwright/package.json b/packages/playwright/package.json
index b20bb56..99b9191 100644
--- a/packages/playwright/package.json
+++ b/packages/playwright/package.json
@@ -14,8 +14,8 @@
"pretest": "npm run build"
},
"dependencies": {
- "@knighted/css": "1.0.0-rc.7",
- "@knighted/jsx": "^1.2.1",
+ "@knighted/css": "1.0.0-rc.8",
+ "@knighted/jsx": "^1.4.1",
"lit": "^3.2.1",
"react": "^19.0.0",
"react-dom": "^19.0.0"
diff --git a/scripts/setup-jsx-wasm.mjs b/scripts/setup-jsx-wasm.mjs
deleted file mode 100644
index 4a1ad46..0000000
--- a/scripts/setup-jsx-wasm.mjs
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/usr/bin/env node
-import { execSync } from 'node:child_process'
-import { existsSync, mkdirSync, rmSync } from 'node:fs'
-import path from 'node:path'
-import { fileURLToPath } from 'node:url'
-
-const __filename = fileURLToPath(import.meta.url)
-const __dirname = path.dirname(__filename)
-const ROOT = path.resolve(__dirname, '..')
-const BINDING_VERSION = process.env.KNIGHTED_JSX_WASM_VERSION ?? '0.99.0'
-const NODE_MODULES = path.join(ROOT, 'node_modules')
-const bindingDir = path.join(NODE_MODULES, '@oxc-parser', 'binding-wasm32-wasi')
-const sentinel = path.join(bindingDir, 'parser.wasi.cjs')
-
-if (existsSync(sentinel)) {
- console.log('[setup:jsx-wasm] Binding already present, skipping install.')
- process.exit(0)
-}
-
-const tarball = `oxc-parser-binding-wasm32-wasi-${BINDING_VERSION}.tgz`
-
-function run(command) {
- execSync(command, { cwd: ROOT, stdio: 'inherit' })
-}
-
-try {
- console.log(
- `[setup:jsx-wasm] Packing @oxc-parser/binding-wasm32-wasi@${BINDING_VERSION}...`,
- )
- run(`npm pack @oxc-parser/binding-wasm32-wasi@${BINDING_VERSION}`)
-
- mkdirSync(bindingDir, { recursive: true })
-
- console.log('[setup:jsx-wasm] Extracting tarball into node_modules...')
- run(`tar -xzf ${tarball} -C ${bindingDir} --strip-components=1`)
-
- console.log('[setup:jsx-wasm] Cleaning up tarball...')
- rmSync(path.join(ROOT, tarball))
-
- console.log('[setup:jsx-wasm] Installed WASM parser binding successfully.')
-} catch (error) {
- console.error('[setup:jsx-wasm] Failed to install WASM binding:', error)
- process.exitCode = 1
-}