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.
@@ -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
- To install dependencies:
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
- ```bash
6
- bun install
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
- To run:
23
+ ## Options
10
24
 
11
- ```bash
12
- bun run src/index.ts
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
- This project was created using `bun init` in bun v1.3.13. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
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
@@ -0,0 +1,10 @@
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
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bun
2
+ // @bun
3
+
4
+ // src/cli.ts
5
+ import { runAnalyzeCli } from "sinwan-compiler";
6
+ runAnalyzeCli();
@@ -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;
@@ -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.1.0",
4
- "description": "Vite plugin for Sinwan UI",
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
- "directory": "packages/vite-plugin-sinwan"
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
- console.log("Hello via Bun!");
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 setup & latest features
4
- "lib": ["ESNext"],
4
+ // ─── Environment ───────────────────────────────────────
5
5
  "target": "ESNext",
6
- "module": "Preserve",
7
- "moduleDetection": "force",
8
- "jsx": "react-jsx",
9
- "allowJs": true,
6
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
10
7
  "types": ["bun"],
11
8
 
12
- // Bundler mode
13
- "moduleResolution": "bundler",
9
+ // ─── Module ────────────────────────────────────────────
10
+ "module": "ESNext",
11
+ "moduleResolution": "Bundler",
14
12
  "allowImportingTsExtensions": true,
15
- "verbatimModuleSyntax": true,
16
- "noEmit": true,
13
+ "resolveJsonModule": true,
14
+ "esModuleInterop": true,
15
+ "isolatedModules": true,
16
+ "verbatimModuleSyntax": false,
17
+ "skipLibCheck": true,
18
+ "forceConsistentCasingInFileNames": true,
17
19
 
18
- // Best practices
20
+ // ─── Strictness ────────────────────────────────────────
19
21
  "strict": true,
20
- "skipLibCheck": true,
22
+ "noUncheckedIndexedAccess": false,
21
23
  "noFallthroughCasesInSwitch": true,
22
- "noUncheckedIndexedAccess": true,
23
- "noImplicitOverride": true,
24
24
 
25
- // Some stricter flags (disabled by default)
26
- "noUnusedLocals": false,
27
- "noUnusedParameters": false,
28
- "noPropertyAccessFromIndexSignature": false
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
  }