vite-plugin-sinwan 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +46 -0
- package/.github/workflows/release.yml +83 -0
- package/LICENSE +21 -0
- package/README.md +55 -7
- package/__tests__/plugin.test.ts +18 -0
- package/build.ts +39 -0
- package/bun.lock +198 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.js +6 -0
- package/dist/compiler/hmr.d.ts +21 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.js +147 -0
- package/package.json +21 -7
- package/src/cli.ts +13 -0
- package/src/compiler/hmr.ts +134 -0
- package/src/index.ts +143 -1
- package/tsconfig.build.json +30 -0
- package/tsconfig.json +40 -19
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, develop]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, develop]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: ci-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
build:
|
|
18
|
+
name: Build
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
timeout-minutes: 10
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
|
|
24
|
+
- name: Setup Bun
|
|
25
|
+
uses: oven-sh/setup-bun@v2
|
|
26
|
+
with:
|
|
27
|
+
bun-version: latest
|
|
28
|
+
|
|
29
|
+
- name: Install dependencies
|
|
30
|
+
run: bun install --frozen-lockfile
|
|
31
|
+
|
|
32
|
+
- name: Build
|
|
33
|
+
run: bun run build
|
|
34
|
+
|
|
35
|
+
- name: Verify dist
|
|
36
|
+
run: |
|
|
37
|
+
test -f dist/index.js
|
|
38
|
+
test -f dist/index.d.ts
|
|
39
|
+
echo "dist verified"
|
|
40
|
+
|
|
41
|
+
- name: Upload build artifacts
|
|
42
|
+
uses: actions/upload-artifact@v4
|
|
43
|
+
with:
|
|
44
|
+
name: dist
|
|
45
|
+
path: dist/
|
|
46
|
+
retention-days: 7
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
verify:
|
|
13
|
+
name: Verify (build)
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
timeout-minutes: 15
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Setup Bun
|
|
20
|
+
uses: oven-sh/setup-bun@v2
|
|
21
|
+
with:
|
|
22
|
+
bun-version: latest
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: bun install --frozen-lockfile
|
|
26
|
+
|
|
27
|
+
- name: Build
|
|
28
|
+
run: bun run build
|
|
29
|
+
|
|
30
|
+
publish:
|
|
31
|
+
name: Publish to npm
|
|
32
|
+
needs: verify
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
timeout-minutes: 5
|
|
35
|
+
environment: release
|
|
36
|
+
permissions:
|
|
37
|
+
id-token: write
|
|
38
|
+
contents: read
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@v4
|
|
41
|
+
|
|
42
|
+
- name: Setup Bun
|
|
43
|
+
uses: oven-sh/setup-bun@v2
|
|
44
|
+
with:
|
|
45
|
+
bun-version: latest
|
|
46
|
+
|
|
47
|
+
- name: Install dependencies
|
|
48
|
+
run: bun install --frozen-lockfile
|
|
49
|
+
|
|
50
|
+
- name: Build
|
|
51
|
+
run: bun run build
|
|
52
|
+
|
|
53
|
+
- name: Setup Node for npm publish
|
|
54
|
+
uses: actions/setup-node@v4
|
|
55
|
+
with:
|
|
56
|
+
node-version: '24'
|
|
57
|
+
registry-url: 'https://registry.npmjs.org'
|
|
58
|
+
|
|
59
|
+
- name: Publish to npm
|
|
60
|
+
run: npm publish --provenance --access public
|
|
61
|
+
|
|
62
|
+
release:
|
|
63
|
+
name: Create GitHub Release
|
|
64
|
+
needs: verify
|
|
65
|
+
runs-on: ubuntu-latest
|
|
66
|
+
timeout-minutes: 5
|
|
67
|
+
permissions:
|
|
68
|
+
contents: write
|
|
69
|
+
steps:
|
|
70
|
+
- uses: actions/checkout@v4
|
|
71
|
+
|
|
72
|
+
- name: Extract version from tag
|
|
73
|
+
id: version
|
|
74
|
+
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
|
|
75
|
+
|
|
76
|
+
- name: Create GitHub Release
|
|
77
|
+
uses: softprops/action-gh-release@v2
|
|
78
|
+
with:
|
|
79
|
+
tag_name: ${{ github.ref_name }}
|
|
80
|
+
name: v${{ steps.version.outputs.VERSION }}
|
|
81
|
+
generate_release_notes: true
|
|
82
|
+
draft: false
|
|
83
|
+
prerelease: ${{ contains(github.ref_name, '-') }}
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mohammed Ben Cheikh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,15 +1,63 @@
|
|
|
1
1
|
# vite-plugin-sinwan
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Vite plugin for [Sinwan](https://sinwanjs.com) — JSX transformation with template hoisting, reactive expression wrapping, and plugin-free Fast Refresh powered by `sinwan-compiler`.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
bun add -d vite-plugin-sinwan
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
// vite.config.ts
|
|
15
|
+
import { defineConfig } from "vite";
|
|
16
|
+
import { sinwan } from "vite-plugin-sinwan";
|
|
17
|
+
|
|
18
|
+
export default defineConfig({
|
|
19
|
+
plugins: [sinwan()],
|
|
20
|
+
});
|
|
7
21
|
```
|
|
8
22
|
|
|
9
|
-
|
|
23
|
+
## Options
|
|
10
24
|
|
|
11
|
-
```
|
|
12
|
-
|
|
25
|
+
```ts
|
|
26
|
+
sinwan({
|
|
27
|
+
// Enable template hoisting (default: true)
|
|
28
|
+
hoist: true,
|
|
29
|
+
// Emit explicit binding descriptors (default: false)
|
|
30
|
+
explicitBindings: false,
|
|
31
|
+
// Path to reactive-props metadata from `sinwan analyze`
|
|
32
|
+
analyze: "./.sinwan/props.json",
|
|
33
|
+
// Incremental cross-file analysis for dev/HMR
|
|
34
|
+
cache: {
|
|
35
|
+
root: process.cwd(),
|
|
36
|
+
tsConfigPath: "./tsconfig.json",
|
|
37
|
+
cachePath: "./.sinwan/cache.json",
|
|
38
|
+
bunfigPath: "./bunfig.toml",
|
|
39
|
+
},
|
|
40
|
+
// Plugin-free Fast Refresh (default: true, dev server only)
|
|
41
|
+
fastRefresh: true,
|
|
42
|
+
});
|
|
13
43
|
```
|
|
14
44
|
|
|
15
|
-
|
|
45
|
+
| Option | Type | Default | Description |
|
|
46
|
+
| ------------------ | ------------------------------- | ----------- | ----------------------------------------------------- |
|
|
47
|
+
| `hoist` | `boolean` | `true` | Hoist static DOM to module-level templates |
|
|
48
|
+
| `explicitBindings` | `boolean` | `false` | Emit compiler-driven binding descriptors |
|
|
49
|
+
| `analyze` | `string` | `undefined` | Path to reactive-props metadata from `sinwan analyze` |
|
|
50
|
+
| `cache` | `boolean \| SinwanCacheOptions` | `false` | Enable incremental in-memory cross-file analysis |
|
|
51
|
+
| `fastRefresh` | `boolean` | `true` | Inject per-component HMR boundaries (dev server only) |
|
|
52
|
+
|
|
53
|
+
## How it works
|
|
54
|
+
|
|
55
|
+
- Runs with `enforce: "pre"` so it executes before any other transform
|
|
56
|
+
- Static DOM elements are hoisted to module-level template objects
|
|
57
|
+
- Dynamic expressions are wrapped in zero-arity functions for fine-grained reactivity
|
|
58
|
+
- Component calls (capitalised tags) are left for the runtime
|
|
59
|
+
- Fast Refresh injects per-component HMR boundaries in `vite serve` — no extra plugin needed
|
|
60
|
+
|
|
61
|
+
## License
|
|
62
|
+
|
|
63
|
+
MIT © Mohammed Ben Cheikh
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, it, expect } from "bun:test";
|
|
2
|
+
import { sinwan } from "../src/index";
|
|
3
|
+
|
|
4
|
+
describe("sinwan vite plugin", () => {
|
|
5
|
+
it("can be created with cache enabled", () => {
|
|
6
|
+
const plugin = sinwan({ cache: true });
|
|
7
|
+
expect(plugin.name).toBe("sinwan");
|
|
8
|
+
expect(typeof plugin.configResolved).toBe("function");
|
|
9
|
+
expect(typeof plugin.transform).toBe("function");
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("can be created with a custom cache root", () => {
|
|
13
|
+
const plugin = sinwan({
|
|
14
|
+
cache: { root: "/project", tsConfigPath: "/project/tsconfig.json" },
|
|
15
|
+
});
|
|
16
|
+
expect(plugin.name).toBe("sinwan");
|
|
17
|
+
});
|
|
18
|
+
});
|
package/build.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { $ } from "bun";
|
|
2
|
+
import { rm } from "node:fs/promises";
|
|
3
|
+
|
|
4
|
+
const ROOT = import.meta.dir;
|
|
5
|
+
const DIST = `${ROOT}/dist`;
|
|
6
|
+
|
|
7
|
+
async function cleanDist() {
|
|
8
|
+
console.log("🧹 Cleaning dist/");
|
|
9
|
+
await rm(DIST, { recursive: true, force: true });
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function emitDeclarations() {
|
|
13
|
+
console.log("📝 Emitting type declarations");
|
|
14
|
+
await $`bunx tsc -p tsconfig.build.json`.cwd(ROOT);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function buildBundle() {
|
|
18
|
+
console.log("📦 Bundling with Bun.build");
|
|
19
|
+
await Bun.build({
|
|
20
|
+
entrypoints: [`${ROOT}/src/index.ts`, `${ROOT}/src/cli.ts`],
|
|
21
|
+
outdir: DIST,
|
|
22
|
+
root: `${ROOT}/src`,
|
|
23
|
+
target: "bun",
|
|
24
|
+
splitting: true,
|
|
25
|
+
packages: "external",
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function main() {
|
|
30
|
+
await cleanDist();
|
|
31
|
+
await emitDeclarations();
|
|
32
|
+
await buildBundle();
|
|
33
|
+
console.log("✅ Build complete → dist/");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
main().catch((err) => {
|
|
37
|
+
console.error("Build failed:", err);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
});
|
package/bun.lock
CHANGED
|
@@ -4,23 +4,221 @@
|
|
|
4
4
|
"workspaces": {
|
|
5
5
|
"": {
|
|
6
6
|
"name": "vite-plugin-sinwan",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@babel/parser": "^7.24.0",
|
|
9
|
+
"sinwan-compiler": "^0.2.0",
|
|
10
|
+
},
|
|
7
11
|
"devDependencies": {
|
|
8
12
|
"@types/bun": "latest",
|
|
13
|
+
"typescript": "^5",
|
|
9
14
|
},
|
|
10
15
|
"peerDependencies": {
|
|
11
16
|
"typescript": "^5",
|
|
17
|
+
"vite": "^5",
|
|
12
18
|
},
|
|
13
19
|
},
|
|
14
20
|
},
|
|
15
21
|
"packages": {
|
|
22
|
+
"@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="],
|
|
23
|
+
|
|
24
|
+
"@babel/compat-data": ["@babel/compat-data@7.29.3", "", {}, "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg=="],
|
|
25
|
+
|
|
26
|
+
"@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="],
|
|
27
|
+
|
|
28
|
+
"@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="],
|
|
29
|
+
|
|
30
|
+
"@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="],
|
|
31
|
+
|
|
32
|
+
"@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="],
|
|
33
|
+
|
|
34
|
+
"@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="],
|
|
35
|
+
|
|
36
|
+
"@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="],
|
|
37
|
+
|
|
38
|
+
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
|
|
39
|
+
|
|
40
|
+
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
|
|
41
|
+
|
|
42
|
+
"@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
|
|
43
|
+
|
|
44
|
+
"@babel/helpers": ["@babel/helpers@7.29.2", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.29.0" } }, "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw=="],
|
|
45
|
+
|
|
46
|
+
"@babel/parser": ["@babel/parser@7.29.3", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA=="],
|
|
47
|
+
|
|
48
|
+
"@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="],
|
|
49
|
+
|
|
50
|
+
"@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="],
|
|
51
|
+
|
|
52
|
+
"@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="],
|
|
53
|
+
|
|
54
|
+
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
|
|
55
|
+
|
|
56
|
+
"@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="],
|
|
57
|
+
|
|
58
|
+
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="],
|
|
59
|
+
|
|
60
|
+
"@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="],
|
|
61
|
+
|
|
62
|
+
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="],
|
|
63
|
+
|
|
64
|
+
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="],
|
|
65
|
+
|
|
66
|
+
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="],
|
|
67
|
+
|
|
68
|
+
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="],
|
|
69
|
+
|
|
70
|
+
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="],
|
|
71
|
+
|
|
72
|
+
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="],
|
|
73
|
+
|
|
74
|
+
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="],
|
|
75
|
+
|
|
76
|
+
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="],
|
|
77
|
+
|
|
78
|
+
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="],
|
|
79
|
+
|
|
80
|
+
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="],
|
|
81
|
+
|
|
82
|
+
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="],
|
|
83
|
+
|
|
84
|
+
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="],
|
|
85
|
+
|
|
86
|
+
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="],
|
|
87
|
+
|
|
88
|
+
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="],
|
|
89
|
+
|
|
90
|
+
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="],
|
|
91
|
+
|
|
92
|
+
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="],
|
|
93
|
+
|
|
94
|
+
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="],
|
|
95
|
+
|
|
96
|
+
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="],
|
|
97
|
+
|
|
98
|
+
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="],
|
|
99
|
+
|
|
100
|
+
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
|
101
|
+
|
|
102
|
+
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
|
|
103
|
+
|
|
104
|
+
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
|
105
|
+
|
|
106
|
+
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
|
107
|
+
|
|
108
|
+
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
|
109
|
+
|
|
110
|
+
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.60.4", "", { "os": "android", "cpu": "arm" }, "sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ=="],
|
|
111
|
+
|
|
112
|
+
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.60.4", "", { "os": "android", "cpu": "arm64" }, "sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw=="],
|
|
113
|
+
|
|
114
|
+
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.60.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA=="],
|
|
115
|
+
|
|
116
|
+
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.60.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg=="],
|
|
117
|
+
|
|
118
|
+
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.60.4", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g=="],
|
|
119
|
+
|
|
120
|
+
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.60.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw=="],
|
|
121
|
+
|
|
122
|
+
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.60.4", "", { "os": "linux", "cpu": "arm" }, "sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA=="],
|
|
123
|
+
|
|
124
|
+
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.60.4", "", { "os": "linux", "cpu": "arm" }, "sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w=="],
|
|
125
|
+
|
|
126
|
+
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.60.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg=="],
|
|
127
|
+
|
|
128
|
+
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.60.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A=="],
|
|
129
|
+
|
|
130
|
+
"@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.60.4", "", { "os": "linux", "cpu": "none" }, "sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ=="],
|
|
131
|
+
|
|
132
|
+
"@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.60.4", "", { "os": "linux", "cpu": "none" }, "sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw=="],
|
|
133
|
+
|
|
134
|
+
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.60.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg=="],
|
|
135
|
+
|
|
136
|
+
"@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.60.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A=="],
|
|
137
|
+
|
|
138
|
+
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.60.4", "", { "os": "linux", "cpu": "none" }, "sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA=="],
|
|
139
|
+
|
|
140
|
+
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.60.4", "", { "os": "linux", "cpu": "none" }, "sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw=="],
|
|
141
|
+
|
|
142
|
+
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.60.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ=="],
|
|
143
|
+
|
|
144
|
+
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.60.4", "", { "os": "linux", "cpu": "x64" }, "sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ=="],
|
|
145
|
+
|
|
146
|
+
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.60.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg=="],
|
|
147
|
+
|
|
148
|
+
"@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.60.4", "", { "os": "openbsd", "cpu": "x64" }, "sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA=="],
|
|
149
|
+
|
|
150
|
+
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.60.4", "", { "os": "none", "cpu": "arm64" }, "sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg=="],
|
|
151
|
+
|
|
152
|
+
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.60.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw=="],
|
|
153
|
+
|
|
154
|
+
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.60.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA=="],
|
|
155
|
+
|
|
156
|
+
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.60.4", "", { "os": "win32", "cpu": "x64" }, "sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw=="],
|
|
157
|
+
|
|
158
|
+
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.60.4", "", { "os": "win32", "cpu": "x64" }, "sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw=="],
|
|
159
|
+
|
|
16
160
|
"@types/bun": ["@types/bun@1.3.13", "", { "dependencies": { "bun-types": "1.3.13" } }, "sha512-9fqXWk5YIHGGnUau9TEi+qdlTYDAnOj+xLCmSTwXfAIqXr2x4tytJb43E9uCvt09zJURKXwAtkoH4nLQfzeTXw=="],
|
|
17
161
|
|
|
162
|
+
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
|
163
|
+
|
|
18
164
|
"@types/node": ["@types/node@25.6.2", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-sokuT28dxf9JT5Kady1fsXOvI4HVpjZa95NKT5y9PNTIrs2AsobR4GFAA90ZG8M+nxVRLysCXsVj6eGC7Vbrlw=="],
|
|
19
165
|
|
|
166
|
+
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.29", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ=="],
|
|
167
|
+
|
|
168
|
+
"browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="],
|
|
169
|
+
|
|
20
170
|
"bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="],
|
|
21
171
|
|
|
172
|
+
"caniuse-lite": ["caniuse-lite@1.0.30001792", "", {}, "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw=="],
|
|
173
|
+
|
|
174
|
+
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
|
175
|
+
|
|
176
|
+
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
|
177
|
+
|
|
178
|
+
"electron-to-chromium": ["electron-to-chromium@1.5.356", "", {}, "sha512-9NgFd7m5t5MCJ5rUSjJITUXAH9mEGlrlofnMf4YEr+pz6JlP7cWmTAH+JFmbPnaSW8koVTkuW7pacORWAnA5Yw=="],
|
|
179
|
+
|
|
180
|
+
"esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="],
|
|
181
|
+
|
|
182
|
+
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
|
183
|
+
|
|
184
|
+
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
|
185
|
+
|
|
186
|
+
"gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
|
|
187
|
+
|
|
188
|
+
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
|
189
|
+
|
|
190
|
+
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
|
191
|
+
|
|
192
|
+
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
|
|
193
|
+
|
|
194
|
+
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
|
195
|
+
|
|
196
|
+
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
|
197
|
+
|
|
198
|
+
"nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="],
|
|
199
|
+
|
|
200
|
+
"node-releases": ["node-releases@2.0.44", "", {}, "sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ=="],
|
|
201
|
+
|
|
202
|
+
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
|
203
|
+
|
|
204
|
+
"postcss": ["postcss@8.5.14", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg=="],
|
|
205
|
+
|
|
206
|
+
"rollup": ["rollup@4.60.4", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.4", "@rollup/rollup-android-arm64": "4.60.4", "@rollup/rollup-darwin-arm64": "4.60.4", "@rollup/rollup-darwin-x64": "4.60.4", "@rollup/rollup-freebsd-arm64": "4.60.4", "@rollup/rollup-freebsd-x64": "4.60.4", "@rollup/rollup-linux-arm-gnueabihf": "4.60.4", "@rollup/rollup-linux-arm-musleabihf": "4.60.4", "@rollup/rollup-linux-arm64-gnu": "4.60.4", "@rollup/rollup-linux-arm64-musl": "4.60.4", "@rollup/rollup-linux-loong64-gnu": "4.60.4", "@rollup/rollup-linux-loong64-musl": "4.60.4", "@rollup/rollup-linux-ppc64-gnu": "4.60.4", "@rollup/rollup-linux-ppc64-musl": "4.60.4", "@rollup/rollup-linux-riscv64-gnu": "4.60.4", "@rollup/rollup-linux-riscv64-musl": "4.60.4", "@rollup/rollup-linux-s390x-gnu": "4.60.4", "@rollup/rollup-linux-x64-gnu": "4.60.4", "@rollup/rollup-linux-x64-musl": "4.60.4", "@rollup/rollup-openbsd-x64": "4.60.4", "@rollup/rollup-openharmony-arm64": "4.60.4", "@rollup/rollup-win32-arm64-msvc": "4.60.4", "@rollup/rollup-win32-ia32-msvc": "4.60.4", "@rollup/rollup-win32-x64-gnu": "4.60.4", "@rollup/rollup-win32-x64-msvc": "4.60.4", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g=="],
|
|
207
|
+
|
|
208
|
+
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
|
209
|
+
|
|
210
|
+
"sinwan-compiler": ["sinwan-compiler@0.2.0", "", { "dependencies": { "@babel/core": "^7.24.0", "@babel/generator": "^7.24.0", "@babel/parser": "^7.24.0", "@babel/traverse": "^7.24.0", "@babel/types": "^7.24.0" }, "peerDependencies": { "typescript": "^5" }, "bin": { "sinwan": "dist/cli.js" } }, "sha512-a66GYxxo8VtJRV6R07C13oHkgjRZ8PQsGO9KXDF3rQ6hcPW4laYG1t1WlQxrV1GopIejC7LMifZNUPqq4en14A=="],
|
|
211
|
+
|
|
212
|
+
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
|
213
|
+
|
|
22
214
|
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
|
23
215
|
|
|
24
216
|
"undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="],
|
|
217
|
+
|
|
218
|
+
"update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
|
|
219
|
+
|
|
220
|
+
"vite": ["vite@5.4.21", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="],
|
|
221
|
+
|
|
222
|
+
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
|
25
223
|
}
|
|
26
224
|
}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sinwan Fast Refresh — HMR boundary injection (DEV only).
|
|
3
|
+
*
|
|
4
|
+
* For each module that exports component(s), we append a self-accepting HMR
|
|
5
|
+
* footer. On edit, the footer maps the module's old export(s) to the new
|
|
6
|
+
* one(s) via `$$sinwanReplace` and triggers a root re-render via
|
|
7
|
+
* `$$sinwanRefresh`. Because each component module becomes its own HMR
|
|
8
|
+
* boundary, Vite reports the actual edited file (e.g. `hmr update /src/Foo.tsx`)
|
|
9
|
+
* instead of falling back to the entry — and state is preserved by the
|
|
10
|
+
* runtime's resolve-to-latest mechanism.
|
|
11
|
+
*
|
|
12
|
+
* Detection is intentionally conservative: only top-level exports whose name
|
|
13
|
+
* starts with an uppercase letter (the component naming convention) are
|
|
14
|
+
* registered. The runtime safely ignores anything that is not used as a
|
|
15
|
+
* component, so over-matching is harmless.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Append a Fast Refresh HMR footer to a component module's source.
|
|
19
|
+
* Returns the augmented code, or `null` when the module exports no components.
|
|
20
|
+
*/
|
|
21
|
+
export declare function injectComponentHmr(code: string, filename: string): string | null;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { transformJSX } from "sinwan-compiler";
|
|
2
|
+
export type SinwanCacheOptions = boolean | {
|
|
3
|
+
root?: string;
|
|
4
|
+
tsConfigPath?: string;
|
|
5
|
+
/** Path to a JSON cache file for persisting the analyzer state. */
|
|
6
|
+
cachePath?: string;
|
|
7
|
+
/** Path to bunfig.toml for Bun alias resolution. */
|
|
8
|
+
bunfigPath?: string;
|
|
9
|
+
/** Workspace file or package paths for cross-package analysis. */
|
|
10
|
+
workspaces?: import("sinwan-compiler").WorkspacesConfig;
|
|
11
|
+
};
|
|
12
|
+
export interface SinwanOptions {
|
|
13
|
+
/** Enable template hoisting (default: true) */
|
|
14
|
+
hoist?: boolean;
|
|
15
|
+
/** Emit explicit compiler-driven binding descriptors (Phase 2, default: false). */
|
|
16
|
+
explicitBindings?: boolean;
|
|
17
|
+
/** Path to the reactive-props metadata file produced by `sinwan analyze`. */
|
|
18
|
+
analyze?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Enable incremental in-memory cross-file analysis for dev/HMR. When true,
|
|
21
|
+
* the plugin analyzes each transformed file and passes the current metadata
|
|
22
|
+
* to the transform without requiring a separate `sinwan analyze` step.
|
|
23
|
+
*/
|
|
24
|
+
cache?: SinwanCacheOptions;
|
|
25
|
+
/**
|
|
26
|
+
* Enable plugin-free Fast Refresh — inject per-component HMR boundaries in
|
|
27
|
+
* the dev server so editing a component updates that file in place (Vite
|
|
28
|
+
* reports the real filename) while preserving state. Default: true.
|
|
29
|
+
* Only active in `vite serve`; never affects production builds.
|
|
30
|
+
*/
|
|
31
|
+
fastRefresh?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Unified Sinwan Vite plugin.
|
|
35
|
+
*
|
|
36
|
+
* Handles JSX compilation (with optional template hoisting).
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* // JSX transform only (default)
|
|
41
|
+
* sinwan()
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare function sinwan(options?: SinwanOptions): {
|
|
45
|
+
name: string;
|
|
46
|
+
enforce: string;
|
|
47
|
+
configResolved(config: any): void;
|
|
48
|
+
transform(code: string, id: string): {
|
|
49
|
+
code: string;
|
|
50
|
+
map?: any;
|
|
51
|
+
} | null;
|
|
52
|
+
};
|
|
53
|
+
export { transformJSX };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/index.ts
|
|
3
|
+
import { transformJSX, AnalyzerCache } from "sinwan-compiler";
|
|
4
|
+
|
|
5
|
+
// src/compiler/hmr.ts
|
|
6
|
+
import { parse } from "@babel/parser";
|
|
7
|
+
function isComponentName(name) {
|
|
8
|
+
return !!name && name[0] === name[0].toUpperCase() && /^[A-Z]/.test(name);
|
|
9
|
+
}
|
|
10
|
+
function collectExportedComponents(code, filename) {
|
|
11
|
+
let ast;
|
|
12
|
+
try {
|
|
13
|
+
ast = parse(code, {
|
|
14
|
+
sourceType: "module",
|
|
15
|
+
plugins: ["jsx", "typescript"],
|
|
16
|
+
sourceFilename: filename
|
|
17
|
+
});
|
|
18
|
+
} catch {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
const found = [];
|
|
22
|
+
const seen = new Set;
|
|
23
|
+
const add = (local, key) => {
|
|
24
|
+
const sig = `${local}::${key}`;
|
|
25
|
+
if (seen.has(sig))
|
|
26
|
+
return;
|
|
27
|
+
seen.add(sig);
|
|
28
|
+
found.push({ local, key });
|
|
29
|
+
};
|
|
30
|
+
for (const node of ast.program.body) {
|
|
31
|
+
if (node.type === "ExportNamedDeclaration") {
|
|
32
|
+
if (node.declaration) {
|
|
33
|
+
const decl = node.declaration;
|
|
34
|
+
if (decl.type === "FunctionDeclaration" && decl.id && isComponentName(decl.id.name)) {
|
|
35
|
+
add(decl.id.name, decl.id.name);
|
|
36
|
+
} else if (decl.type === "VariableDeclaration") {
|
|
37
|
+
for (const d of decl.declarations) {
|
|
38
|
+
if (d.id?.type === "Identifier" && isComponentName(d.id.name)) {
|
|
39
|
+
add(d.id.name, d.id.name);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
} else if (node.specifiers && !node.source) {
|
|
44
|
+
for (const spec of node.specifiers) {
|
|
45
|
+
if (spec.type === "ExportSpecifier" && spec.local?.type === "Identifier" && spec.exported?.type === "Identifier" && isComponentName(spec.exported.name)) {
|
|
46
|
+
add(spec.local.name, spec.exported.name);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
} else if (node.type === "ExportDefaultDeclaration") {
|
|
51
|
+
const decl = node.declaration;
|
|
52
|
+
if (decl.type === "Identifier") {
|
|
53
|
+
add(decl.name, "default");
|
|
54
|
+
} else if (decl.type === "FunctionDeclaration" && decl.id) {
|
|
55
|
+
add(decl.id.name, "default");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return found;
|
|
60
|
+
}
|
|
61
|
+
function injectComponentHmr(code, filename) {
|
|
62
|
+
const components = collectExportedComponents(code, filename);
|
|
63
|
+
if (components.length === 0)
|
|
64
|
+
return null;
|
|
65
|
+
if (code.includes("$$sinwanReplace") || code.includes("__$sinwanRefresh")) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
const replaces = components.map(({ local, key }) => ` __$sinwanReplace(${local}, __m.${key});`).join(`
|
|
69
|
+
`);
|
|
70
|
+
const footer = `
|
|
71
|
+
|
|
72
|
+
import { $$sinwanReplace as __$sinwanReplace, $$sinwanRefresh as __$sinwanRefresh } from "sinwan/react-client";
|
|
73
|
+
if (import.meta.hot) {
|
|
74
|
+
import.meta.hot.accept((__m) => {
|
|
75
|
+
if (!__m) return;
|
|
76
|
+
${replaces}
|
|
77
|
+
__$sinwanRefresh();
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
`;
|
|
81
|
+
return code + footer;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/index.ts
|
|
85
|
+
import * as fs from "fs";
|
|
86
|
+
import * as path from "path";
|
|
87
|
+
var DEFAULT_SINWAN_OPTIONS = {
|
|
88
|
+
hoist: true,
|
|
89
|
+
explicitBindings: false,
|
|
90
|
+
fastRefresh: true,
|
|
91
|
+
analyze: undefined
|
|
92
|
+
};
|
|
93
|
+
function sinwan(options = {}) {
|
|
94
|
+
const opts = { ...DEFAULT_SINWAN_OPTIONS, ...options };
|
|
95
|
+
let isServe = false;
|
|
96
|
+
let projectRoot;
|
|
97
|
+
let cache = null;
|
|
98
|
+
return {
|
|
99
|
+
name: "sinwan",
|
|
100
|
+
enforce: "pre",
|
|
101
|
+
configResolved(config) {
|
|
102
|
+
isServe = config?.command === "serve";
|
|
103
|
+
projectRoot = config?.root;
|
|
104
|
+
if (opts.cache) {
|
|
105
|
+
const root = typeof opts.cache === "object" ? opts.cache.root ?? projectRoot ?? process.cwd() : projectRoot ?? process.cwd();
|
|
106
|
+
const tsConfigPath = typeof opts.cache === "object" ? opts.cache.tsConfigPath : undefined;
|
|
107
|
+
const cachePath = typeof opts.cache === "object" ? opts.cache.cachePath : undefined;
|
|
108
|
+
const bunfigPath = typeof opts.cache === "object" ? opts.cache.bunfigPath ?? path.join(root, "bunfig.toml") : path.join(root, "bunfig.toml");
|
|
109
|
+
const workspaces = typeof opts.cache === "object" ? opts.cache.workspaces : undefined;
|
|
110
|
+
cache = new AnalyzerCache({
|
|
111
|
+
root,
|
|
112
|
+
tsConfigPath,
|
|
113
|
+
cachePath,
|
|
114
|
+
bunfigPath: fs.existsSync(bunfigPath) ? bunfigPath : undefined,
|
|
115
|
+
workspaces
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
transform(code, id) {
|
|
120
|
+
let result = null;
|
|
121
|
+
if (/\.[tj]sx$/.test(id)) {
|
|
122
|
+
if (cache) {
|
|
123
|
+
cache.update(id, code);
|
|
124
|
+
}
|
|
125
|
+
result = transformJSX(code, id, {
|
|
126
|
+
hoist: opts.hoist,
|
|
127
|
+
explicitBindings: opts.explicitBindings,
|
|
128
|
+
analyze: opts.analyze,
|
|
129
|
+
analyzeMetadata: cache ? cache.reactiveProps : undefined
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
const idPath = id.split("?")[0];
|
|
133
|
+
if (opts.fastRefresh && isServe && /\.[tj]sx$/.test(idPath) && !id.includes("/node_modules/") && !id.startsWith("\x00")) {
|
|
134
|
+
const base = result ? result.code : code;
|
|
135
|
+
const withHmr = injectComponentHmr(base, id);
|
|
136
|
+
if (withHmr !== null) {
|
|
137
|
+
return { code: withHmr, map: result ? result.map : null };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
export {
|
|
145
|
+
transformJSX,
|
|
146
|
+
sinwan
|
|
147
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-sinwan",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Vite plugin for
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Vite plugin for sinwan",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
5
8
|
"keywords": [
|
|
6
9
|
"sinwan",
|
|
7
10
|
"reactive",
|
|
@@ -16,17 +19,28 @@
|
|
|
16
19
|
],
|
|
17
20
|
"author": "Mohammed Ben Cheikh",
|
|
18
21
|
"license": "MIT",
|
|
19
|
-
"bugs": "https://github.com/sinwanjs/sinwan/issues",
|
|
22
|
+
"bugs": "https://github.com/sinwanjs/vite-plugin-sinwan/issues",
|
|
20
23
|
"homepage": "https://sinwanjs.com",
|
|
21
24
|
"repository": {
|
|
22
25
|
"type": "git",
|
|
23
|
-
"url": "https://github.com/sinwanjs/sinwan"
|
|
24
|
-
|
|
26
|
+
"url": "https://github.com/sinwanjs/vite-plugin-sinwan"
|
|
27
|
+
},
|
|
28
|
+
"bin": {
|
|
29
|
+
"sinwan": "./dist/cli.js"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "bun run build.ts"
|
|
25
33
|
},
|
|
26
34
|
"devDependencies": {
|
|
27
|
-
"@types/bun": "latest"
|
|
35
|
+
"@types/bun": "latest",
|
|
36
|
+
"typescript": "^5"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@babel/parser": "^7.24.0",
|
|
40
|
+
"sinwan-compiler": "^0.2.0"
|
|
28
41
|
},
|
|
29
42
|
"peerDependencies": {
|
|
30
|
-
"typescript": "^5"
|
|
43
|
+
"typescript": "^5",
|
|
44
|
+
"vite": "^5"
|
|
31
45
|
}
|
|
32
46
|
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* SinwanJS CLI — project-wide reactive prop analyzer.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* bunx vite-plugin-sinwan analyze [root] [outFile]
|
|
7
|
+
*
|
|
8
|
+
* Writes `.sinwan/reactive-props.json` by default.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { runAnalyzeCli } from "sinwan-compiler";
|
|
12
|
+
|
|
13
|
+
runAnalyzeCli();
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sinwan Fast Refresh — HMR boundary injection (DEV only).
|
|
3
|
+
*
|
|
4
|
+
* For each module that exports component(s), we append a self-accepting HMR
|
|
5
|
+
* footer. On edit, the footer maps the module's old export(s) to the new
|
|
6
|
+
* one(s) via `$$sinwanReplace` and triggers a root re-render via
|
|
7
|
+
* `$$sinwanRefresh`. Because each component module becomes its own HMR
|
|
8
|
+
* boundary, Vite reports the actual edited file (e.g. `hmr update /src/Foo.tsx`)
|
|
9
|
+
* instead of falling back to the entry — and state is preserved by the
|
|
10
|
+
* runtime's resolve-to-latest mechanism.
|
|
11
|
+
*
|
|
12
|
+
* Detection is intentionally conservative: only top-level exports whose name
|
|
13
|
+
* starts with an uppercase letter (the component naming convention) are
|
|
14
|
+
* registered. The runtime safely ignores anything that is not used as a
|
|
15
|
+
* component, so over-matching is harmless.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { parse } from "@babel/parser";
|
|
19
|
+
|
|
20
|
+
interface ExportedComponent {
|
|
21
|
+
/** Local binding name in the module (referenced in the accept callback). */
|
|
22
|
+
local: string;
|
|
23
|
+
/** Export key on the module namespace ("default" for the default export). */
|
|
24
|
+
key: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isComponentName(name: string | undefined): name is string {
|
|
28
|
+
return !!name && name[0] === name[0]!.toUpperCase() && /^[A-Z]/.test(name);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Collect top-level exported component bindings from a module.
|
|
33
|
+
*/
|
|
34
|
+
function collectExportedComponents(
|
|
35
|
+
code: string,
|
|
36
|
+
filename: string,
|
|
37
|
+
): ExportedComponent[] {
|
|
38
|
+
let ast: any;
|
|
39
|
+
try {
|
|
40
|
+
ast = parse(code, {
|
|
41
|
+
sourceType: "module",
|
|
42
|
+
plugins: ["jsx", "typescript"],
|
|
43
|
+
sourceFilename: filename,
|
|
44
|
+
});
|
|
45
|
+
} catch {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const found: ExportedComponent[] = [];
|
|
50
|
+
const seen = new Set<string>();
|
|
51
|
+
const add = (local: string, key: string) => {
|
|
52
|
+
const sig = `${local}::${key}`;
|
|
53
|
+
if (seen.has(sig)) return;
|
|
54
|
+
seen.add(sig);
|
|
55
|
+
found.push({ local, key });
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
for (const node of ast.program.body as any[]) {
|
|
59
|
+
if (node.type === "ExportNamedDeclaration") {
|
|
60
|
+
if (node.declaration) {
|
|
61
|
+
const decl = node.declaration;
|
|
62
|
+
if (
|
|
63
|
+
decl.type === "FunctionDeclaration" &&
|
|
64
|
+
decl.id &&
|
|
65
|
+
isComponentName(decl.id.name)
|
|
66
|
+
) {
|
|
67
|
+
add(decl.id.name, decl.id.name);
|
|
68
|
+
} else if (decl.type === "VariableDeclaration") {
|
|
69
|
+
for (const d of decl.declarations) {
|
|
70
|
+
if (d.id?.type === "Identifier" && isComponentName(d.id.name)) {
|
|
71
|
+
add(d.id.name, d.id.name);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} else if (node.specifiers && !node.source) {
|
|
76
|
+
// export { A, B as C }
|
|
77
|
+
for (const spec of node.specifiers) {
|
|
78
|
+
if (
|
|
79
|
+
spec.type === "ExportSpecifier" &&
|
|
80
|
+
spec.local?.type === "Identifier" &&
|
|
81
|
+
spec.exported?.type === "Identifier" &&
|
|
82
|
+
isComponentName(spec.exported.name)
|
|
83
|
+
) {
|
|
84
|
+
add(spec.local.name, spec.exported.name);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
} else if (node.type === "ExportDefaultDeclaration") {
|
|
89
|
+
const decl = node.declaration;
|
|
90
|
+
if (decl.type === "Identifier") {
|
|
91
|
+
add(decl.name, "default");
|
|
92
|
+
} else if (decl.type === "FunctionDeclaration" && decl.id) {
|
|
93
|
+
add(decl.id.name, "default");
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return found;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Append a Fast Refresh HMR footer to a component module's source.
|
|
103
|
+
* Returns the augmented code, or `null` when the module exports no components.
|
|
104
|
+
*/
|
|
105
|
+
export function injectComponentHmr(
|
|
106
|
+
code: string,
|
|
107
|
+
filename: string,
|
|
108
|
+
): string | null {
|
|
109
|
+
const components = collectExportedComponents(code, filename);
|
|
110
|
+
if (components.length === 0) return null;
|
|
111
|
+
|
|
112
|
+
// Guard: if the module already wired HMR manually, do not double-inject.
|
|
113
|
+
if (code.includes("$$sinwanReplace") || code.includes("__$sinwanRefresh")) {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const replaces = components
|
|
118
|
+
.map(({ local, key }) => ` __$sinwanReplace(${local}, __m.${key});`)
|
|
119
|
+
.join("\n");
|
|
120
|
+
|
|
121
|
+
const footer = `
|
|
122
|
+
|
|
123
|
+
import { $$sinwanReplace as __$sinwanReplace, $$sinwanRefresh as __$sinwanRefresh } from "sinwan/react-client";
|
|
124
|
+
if (import.meta.hot) {
|
|
125
|
+
import.meta.hot.accept((__m) => {
|
|
126
|
+
if (!__m) return;
|
|
127
|
+
${replaces}
|
|
128
|
+
__$sinwanRefresh();
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
`;
|
|
132
|
+
|
|
133
|
+
return code + footer;
|
|
134
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1 +1,143 @@
|
|
|
1
|
-
|
|
1
|
+
import { transformJSX, AnalyzerCache } from "sinwan-compiler";
|
|
2
|
+
import { injectComponentHmr } from "./compiler/hmr";
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
|
|
6
|
+
export type SinwanCacheOptions =
|
|
7
|
+
| boolean
|
|
8
|
+
| {
|
|
9
|
+
root?: string;
|
|
10
|
+
tsConfigPath?: string;
|
|
11
|
+
/** Path to a JSON cache file for persisting the analyzer state. */
|
|
12
|
+
cachePath?: string;
|
|
13
|
+
/** Path to bunfig.toml for Bun alias resolution. */
|
|
14
|
+
bunfigPath?: string;
|
|
15
|
+
/** Workspace file or package paths for cross-package analysis. */
|
|
16
|
+
workspaces?: import("sinwan-compiler").WorkspacesConfig;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export interface SinwanOptions {
|
|
20
|
+
/** Enable template hoisting (default: true) */
|
|
21
|
+
hoist?: boolean;
|
|
22
|
+
/** Emit explicit compiler-driven binding descriptors (Phase 2, default: false). */
|
|
23
|
+
explicitBindings?: boolean;
|
|
24
|
+
/** Path to the reactive-props metadata file produced by `sinwan analyze`. */
|
|
25
|
+
analyze?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Enable incremental in-memory cross-file analysis for dev/HMR. When true,
|
|
28
|
+
* the plugin analyzes each transformed file and passes the current metadata
|
|
29
|
+
* to the transform without requiring a separate `sinwan analyze` step.
|
|
30
|
+
*/
|
|
31
|
+
cache?: SinwanCacheOptions;
|
|
32
|
+
/**
|
|
33
|
+
* Enable plugin-free Fast Refresh — inject per-component HMR boundaries in
|
|
34
|
+
* the dev server so editing a component updates that file in place (Vite
|
|
35
|
+
* reports the real filename) while preserving state. Default: true.
|
|
36
|
+
* Only active in `vite serve`; never affects production builds.
|
|
37
|
+
*/
|
|
38
|
+
fastRefresh?: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const DEFAULT_SINWAN_OPTIONS: Required<
|
|
42
|
+
Pick<SinwanOptions, "hoist" | "explicitBindings" | "fastRefresh" | "analyze">
|
|
43
|
+
> = {
|
|
44
|
+
hoist: true,
|
|
45
|
+
explicitBindings: false,
|
|
46
|
+
fastRefresh: true,
|
|
47
|
+
analyze: undefined as any,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Unified Sinwan Vite plugin.
|
|
52
|
+
*
|
|
53
|
+
* Handles JSX compilation (with optional template hoisting).
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* // JSX transform only (default)
|
|
58
|
+
* sinwan()
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export function sinwan(options: SinwanOptions = {}) {
|
|
62
|
+
const opts = { ...DEFAULT_SINWAN_OPTIONS, ...options };
|
|
63
|
+
|
|
64
|
+
// Whether we are running the dev server (`vite serve`). Fast Refresh
|
|
65
|
+
// injection only happens here; production builds are never touched.
|
|
66
|
+
let isServe = false;
|
|
67
|
+
let projectRoot: string | undefined;
|
|
68
|
+
|
|
69
|
+
// Optional incremental cross-file analyzer for dev/HMR.
|
|
70
|
+
let cache: AnalyzerCache | null = null;
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
name: "sinwan",
|
|
74
|
+
enforce: "pre",
|
|
75
|
+
|
|
76
|
+
configResolved(config: any) {
|
|
77
|
+
isServe = config?.command === "serve";
|
|
78
|
+
projectRoot = config?.root;
|
|
79
|
+
|
|
80
|
+
if (opts.cache) {
|
|
81
|
+
const root =
|
|
82
|
+
typeof opts.cache === "object"
|
|
83
|
+
? (opts.cache.root ?? projectRoot ?? process.cwd())
|
|
84
|
+
: (projectRoot ?? process.cwd());
|
|
85
|
+
const tsConfigPath =
|
|
86
|
+
typeof opts.cache === "object" ? opts.cache.tsConfigPath : undefined;
|
|
87
|
+
const cachePath =
|
|
88
|
+
typeof opts.cache === "object" ? opts.cache.cachePath : undefined;
|
|
89
|
+
const bunfigPath =
|
|
90
|
+
typeof opts.cache === "object"
|
|
91
|
+
? (opts.cache.bunfigPath ?? path.join(root, "bunfig.toml"))
|
|
92
|
+
: path.join(root, "bunfig.toml");
|
|
93
|
+
const workspaces =
|
|
94
|
+
typeof opts.cache === "object" ? opts.cache.workspaces : undefined;
|
|
95
|
+
cache = new AnalyzerCache({
|
|
96
|
+
root,
|
|
97
|
+
tsConfigPath,
|
|
98
|
+
cachePath,
|
|
99
|
+
bunfigPath: fs.existsSync(bunfigPath) ? bunfigPath : undefined,
|
|
100
|
+
workspaces,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
transform(code: string, id: string) {
|
|
106
|
+
let result: { code: string; map?: any } | null = null;
|
|
107
|
+
if (/\.[tj]sx$/.test(id)) {
|
|
108
|
+
if (cache) {
|
|
109
|
+
cache.update(id, code);
|
|
110
|
+
}
|
|
111
|
+
result = transformJSX(code, id, {
|
|
112
|
+
hoist: opts.hoist,
|
|
113
|
+
explicitBindings: opts.explicitBindings,
|
|
114
|
+
analyze: opts.analyze,
|
|
115
|
+
analyzeMetadata: cache ? cache.reactiveProps : undefined,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Fast Refresh: append per-component HMR boundaries in the dev server.
|
|
120
|
+
// Skip dependencies and virtual/query modules.
|
|
121
|
+
const idPath = id.split("?")[0];
|
|
122
|
+
if (
|
|
123
|
+
opts.fastRefresh &&
|
|
124
|
+
isServe &&
|
|
125
|
+
/\.[tj]sx$/.test(idPath) &&
|
|
126
|
+
!id.includes("/node_modules/") &&
|
|
127
|
+
!id.startsWith("\0")
|
|
128
|
+
) {
|
|
129
|
+
const base = result ? result.code : code;
|
|
130
|
+
const withHmr = injectComponentHmr(base, id);
|
|
131
|
+
if (withHmr !== null) {
|
|
132
|
+
// Footer is appended (imports are hoisted by ESM), so existing line
|
|
133
|
+
// numbers — and thus the JSX source map — stay valid.
|
|
134
|
+
return { code: withHmr, map: result ? result.map : null };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return result;
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export { transformJSX };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// tsconfig.build.json — emit type declarations only (Bun bundles the JS)
|
|
2
|
+
{
|
|
3
|
+
"extends": "./tsconfig.json",
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
// ─── Unlock emit ───────────────────────────────────
|
|
6
|
+
"noEmit": false,
|
|
7
|
+
"emitDeclarationOnly": true,
|
|
8
|
+
|
|
9
|
+
// ─── Output ───────────────────────────────────────────
|
|
10
|
+
"outDir": "./dist",
|
|
11
|
+
"rootDir": "./src",
|
|
12
|
+
"declaration": true,
|
|
13
|
+
// "declarationMap": true,
|
|
14
|
+
|
|
15
|
+
// ─── Inherit module / resolution from base (Bundler) ──
|
|
16
|
+
// "allowImportingTsExtensions": true (inherited) — works with emitDeclarationOnly
|
|
17
|
+
|
|
18
|
+
// ─── Lib (declarations need DOM types for renderer/hydration) ──
|
|
19
|
+
"lib": ["ESNext", "DOM", "DOM.Iterable"]
|
|
20
|
+
},
|
|
21
|
+
"include": ["src/**/*"],
|
|
22
|
+
"exclude": [
|
|
23
|
+
"src/**/*.test.ts",
|
|
24
|
+
"src/**/*.spec.ts",
|
|
25
|
+
"node_modules",
|
|
26
|
+
"dist",
|
|
27
|
+
"docs",
|
|
28
|
+
"__tests__"
|
|
29
|
+
]
|
|
30
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -1,30 +1,51 @@
|
|
|
1
|
+
// tsconfig.json — base config (typecheck only, no emit)
|
|
1
2
|
{
|
|
2
3
|
"compilerOptions": {
|
|
3
|
-
// Environment
|
|
4
|
-
"lib": ["ESNext"],
|
|
4
|
+
// ─── Environment ───────────────────────────────────────
|
|
5
5
|
"target": "ESNext",
|
|
6
|
-
"
|
|
7
|
-
"moduleDetection": "force",
|
|
8
|
-
"jsx": "react-jsx",
|
|
9
|
-
"allowJs": true,
|
|
6
|
+
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
|
10
7
|
"types": ["bun"],
|
|
11
8
|
|
|
12
|
-
//
|
|
13
|
-
"
|
|
9
|
+
// ─── Module ────────────────────────────────────────────
|
|
10
|
+
"module": "ESNext",
|
|
11
|
+
"moduleResolution": "Bundler",
|
|
14
12
|
"allowImportingTsExtensions": true,
|
|
15
|
-
"
|
|
16
|
-
"
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"esModuleInterop": true,
|
|
15
|
+
"isolatedModules": true,
|
|
16
|
+
"verbatimModuleSyntax": false,
|
|
17
|
+
"skipLibCheck": true,
|
|
18
|
+
"forceConsistentCasingInFileNames": true,
|
|
17
19
|
|
|
18
|
-
//
|
|
20
|
+
// ─── Strictness ────────────────────────────────────────
|
|
19
21
|
"strict": true,
|
|
20
|
-
"
|
|
22
|
+
"noUncheckedIndexedAccess": false,
|
|
21
23
|
"noFallthroughCasesInSwitch": true,
|
|
22
|
-
"noUncheckedIndexedAccess": true,
|
|
23
|
-
"noImplicitOverride": true,
|
|
24
24
|
|
|
25
|
-
//
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
// ─── JSX ───────────────────────────────────────────────
|
|
26
|
+
"jsx": "react-jsx",
|
|
27
|
+
"jsxImportSource": "sinwan", // custom JSX runtime
|
|
28
|
+
|
|
29
|
+
// ─── No Emit (typecheck only) ──────────────────────────
|
|
30
|
+
"noEmit": true,
|
|
31
|
+
|
|
32
|
+
// ─── Paths ─────────────────────────────────────────────
|
|
33
|
+
"paths": {
|
|
34
|
+
"sinwan": ["./src/index.ts"],
|
|
35
|
+
"sinwan/jsx-runtime": ["./src/jsx/jsx-runtime.ts"],
|
|
36
|
+
"sinwan/jsx-dev-runtime": ["./src/jsx/jsx-dev-runtime.ts"]
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
// ─── Decorators (for DI / metadata) ───────────────────
|
|
40
|
+
"experimentalDecorators": true,
|
|
41
|
+
"emitDecoratorMetadata": false // handled by @sinwan/reflector
|
|
42
|
+
},
|
|
43
|
+
"include": [
|
|
44
|
+
"src/**/*",
|
|
45
|
+
"__tests__/**/*",
|
|
46
|
+
"examples/**/*",
|
|
47
|
+
"build.ts",
|
|
48
|
+
"__tests__"
|
|
49
|
+
],
|
|
50
|
+
"exclude": ["dist", "node_modules"]
|
|
30
51
|
}
|