@pyreon/vite-plugin 0.2.1 → 0.3.1
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/lib/analysis/index.js.html +1 -1
- package/lib/index.js +108 -41
- package/lib/index.js.map +1 -1
- package/lib/types/index.d.ts +95 -40
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index2.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +149 -54
|
@@ -5386,7 +5386,7 @@ var drawChart = (function (exports) {
|
|
|
5386
5386
|
</script>
|
|
5387
5387
|
<script>
|
|
5388
5388
|
/*<!--*/
|
|
5389
|
-
const data = {"version":2,"tree":{"name":"root","children":[{"name":"index.js","children":[{"name":"src/index.ts","uid":"
|
|
5389
|
+
const data = {"version":2,"tree":{"name":"root","children":[{"name":"index.js","children":[{"name":"src/index.ts","uid":"1cff14ac-1"}]}],"isRoot":true},"nodeParts":{"1cff14ac-1":{"renderedLength":11698,"gzipLength":4255,"brotliLength":0,"metaUid":"1cff14ac-0"}},"nodeMetas":{"1cff14ac-0":{"id":"/src/index.ts","moduleParts":{"index.js":"1cff14ac-1"},"imported":[{"uid":"1cff14ac-2"},{"uid":"1cff14ac-3"},{"uid":"1cff14ac-4"}],"importedBy":[],"isEntry":true},"1cff14ac-2":{"id":"node:fs","moduleParts":{},"imported":[],"importedBy":[{"uid":"1cff14ac-0"}]},"1cff14ac-3":{"id":"node:path","moduleParts":{},"imported":[],"importedBy":[{"uid":"1cff14ac-0"}]},"1cff14ac-4":{"id":"@pyreon/compiler","moduleParts":{},"imported":[],"importedBy":[{"uid":"1cff14ac-0"}]}},"env":{"rollup":"4.23.0"},"options":{"gzip":true,"brotli":false,"sourcemap":false}};
|
|
5390
5390
|
|
|
5391
5391
|
const run = () => {
|
|
5392
5392
|
const width = window.innerWidth;
|
package/lib/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
1
3
|
import { transformJSX } from "@pyreon/compiler";
|
|
2
4
|
|
|
3
5
|
//#region src/index.ts
|
|
@@ -36,42 +38,95 @@ import { transformJSX } from "@pyreon/compiler";
|
|
|
36
38
|
*/
|
|
37
39
|
const HMR_RUNTIME_ID = "\0pyreon/hmr-runtime";
|
|
38
40
|
const HMR_RUNTIME_IMPORT = "virtual:pyreon/hmr-runtime";
|
|
41
|
+
const COMPAT_JSX_SOURCE = {
|
|
42
|
+
react: "@pyreon/react-compat",
|
|
43
|
+
preact: "@pyreon/preact-compat",
|
|
44
|
+
vue: "@pyreon/vue-compat",
|
|
45
|
+
solid: "@pyreon/solid-compat"
|
|
46
|
+
};
|
|
39
47
|
const COMPAT_ALIASES = {
|
|
40
48
|
react: {
|
|
41
49
|
react: "@pyreon/react-compat",
|
|
42
|
-
"react/jsx-runtime": "@pyreon/
|
|
43
|
-
"react/jsx-dev-runtime": "@pyreon/
|
|
50
|
+
"react/jsx-runtime": "@pyreon/react-compat/jsx-runtime",
|
|
51
|
+
"react/jsx-dev-runtime": "@pyreon/react-compat/jsx-runtime",
|
|
44
52
|
"react-dom": "@pyreon/react-compat/dom",
|
|
45
53
|
"react-dom/client": "@pyreon/react-compat/dom"
|
|
46
54
|
},
|
|
47
55
|
preact: {
|
|
48
56
|
preact: "@pyreon/preact-compat",
|
|
49
57
|
"preact/hooks": "@pyreon/preact-compat/hooks",
|
|
58
|
+
"preact/jsx-runtime": "@pyreon/preact-compat/jsx-runtime",
|
|
59
|
+
"preact/jsx-dev-runtime": "@pyreon/preact-compat/jsx-runtime",
|
|
50
60
|
"@preact/signals": "@pyreon/preact-compat/signals"
|
|
51
61
|
},
|
|
52
|
-
vue: {
|
|
53
|
-
|
|
62
|
+
vue: {
|
|
63
|
+
vue: "@pyreon/vue-compat",
|
|
64
|
+
"vue/jsx-runtime": "@pyreon/vue-compat/jsx-runtime",
|
|
65
|
+
"vue/jsx-dev-runtime": "@pyreon/vue-compat/jsx-runtime"
|
|
66
|
+
},
|
|
67
|
+
solid: {
|
|
68
|
+
"solid-js": "@pyreon/solid-compat",
|
|
69
|
+
"solid-js/jsx-runtime": "@pyreon/solid-compat/jsx-runtime",
|
|
70
|
+
"solid-js/jsx-dev-runtime": "@pyreon/solid-compat/jsx-runtime"
|
|
71
|
+
}
|
|
54
72
|
};
|
|
73
|
+
/**
|
|
74
|
+
* Resolve a package specifier to an absolute source path, respecting the "bun"
|
|
75
|
+
* export condition. Falls back to the "import" condition.
|
|
76
|
+
*
|
|
77
|
+
* This is needed because Vite 8's resolve pipeline doesn't consistently apply
|
|
78
|
+
* custom conditions from `resolve.conditions` during the `vite:import-analysis`
|
|
79
|
+
* phase for aliased workspace packages.
|
|
80
|
+
*/
|
|
81
|
+
function resolveWithBunCondition(specifier, projectRoot) {
|
|
82
|
+
const pkgName = (specifier.startsWith("@") ? specifier.split("/").slice(0, 2) : specifier.split("/").slice(0, 1)).join("/");
|
|
83
|
+
const subpath = specifier.slice(pkgName.length) || ".";
|
|
84
|
+
const exportKey = subpath === "." ? "." : `.${subpath}`;
|
|
85
|
+
try {
|
|
86
|
+
const pkgDir = resolve(projectRoot, "node_modules", pkgName);
|
|
87
|
+
const pkgJsonPath = resolve(pkgDir, "package.json");
|
|
88
|
+
const exp = JSON.parse(readFileSync(pkgJsonPath, "utf-8")).exports?.[exportKey];
|
|
89
|
+
if (!exp) return void 0;
|
|
90
|
+
if (typeof exp === "string") return resolve(pkgDir, exp);
|
|
91
|
+
const target = exp.bun ?? exp.import ?? exp.default;
|
|
92
|
+
return target ? resolve(pkgDir, target) : void 0;
|
|
93
|
+
} catch {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Return the Pyreon compat target for an import specifier, or undefined if
|
|
99
|
+
* the import should not be redirected.
|
|
100
|
+
*/
|
|
101
|
+
function getCompatTarget(compat, id) {
|
|
102
|
+
if (!compat) return void 0;
|
|
103
|
+
const aliased = COMPAT_ALIASES[compat][id];
|
|
104
|
+
if (aliased) return aliased;
|
|
105
|
+
if (id === "@pyreon/core/jsx-runtime" || id === "@pyreon/core/jsx-dev-runtime") {
|
|
106
|
+
if (compat === "react") return "@pyreon/react-compat/jsx-runtime";
|
|
107
|
+
if (compat === "preact") return "@pyreon/preact-compat/jsx-runtime";
|
|
108
|
+
if (compat === "vue") return "@pyreon/vue-compat/jsx-runtime";
|
|
109
|
+
if (compat === "solid") return "@pyreon/solid-compat/jsx-runtime";
|
|
110
|
+
}
|
|
111
|
+
}
|
|
55
112
|
function pyreonPlugin(options) {
|
|
56
113
|
const ssrConfig = options?.ssr;
|
|
57
114
|
const compat = options?.compat;
|
|
58
115
|
let isBuild = false;
|
|
116
|
+
let projectRoot = "";
|
|
117
|
+
const resolvedAliases = /* @__PURE__ */ new Map();
|
|
59
118
|
return {
|
|
60
119
|
name: "pyreon",
|
|
61
120
|
enforce: "pre",
|
|
62
|
-
config(
|
|
121
|
+
config(userConfig, env) {
|
|
63
122
|
isBuild = env.command === "build";
|
|
123
|
+
projectRoot = userConfig.root ?? process.cwd();
|
|
64
124
|
return {
|
|
65
|
-
resolve: {
|
|
66
|
-
|
|
67
|
-
find,
|
|
68
|
-
replacement
|
|
69
|
-
})) : [],
|
|
70
|
-
conditions: ["bun"]
|
|
71
|
-
},
|
|
125
|
+
resolve: { conditions: ["bun"] },
|
|
126
|
+
optimizeDeps: { exclude: compat ? Object.keys(COMPAT_ALIASES[compat]) : [] },
|
|
72
127
|
oxc: { jsx: {
|
|
73
128
|
runtime: "automatic",
|
|
74
|
-
importSource: "@pyreon/core"
|
|
129
|
+
importSource: compat ? COMPAT_JSX_SOURCE[compat] : "@pyreon/core"
|
|
75
130
|
} },
|
|
76
131
|
...env.isSsrBuild && ssrConfig ? { build: {
|
|
77
132
|
ssr: true,
|
|
@@ -81,6 +136,14 @@ function pyreonPlugin(options) {
|
|
|
81
136
|
},
|
|
82
137
|
resolveId(id) {
|
|
83
138
|
if (id === HMR_RUNTIME_IMPORT) return HMR_RUNTIME_ID;
|
|
139
|
+
const target = getCompatTarget(compat, id);
|
|
140
|
+
if (!target) return;
|
|
141
|
+
let resolved = resolvedAliases.get(target);
|
|
142
|
+
if (!resolved) {
|
|
143
|
+
resolved = resolveWithBunCondition(target, projectRoot);
|
|
144
|
+
if (resolved) resolvedAliases.set(target, resolved);
|
|
145
|
+
}
|
|
146
|
+
return resolved;
|
|
84
147
|
},
|
|
85
148
|
load(id) {
|
|
86
149
|
if (id === HMR_RUNTIME_ID) return HMR_RUNTIME_SOURCE;
|
|
@@ -88,6 +151,7 @@ function pyreonPlugin(options) {
|
|
|
88
151
|
transform(code, id) {
|
|
89
152
|
const ext = getExt(id);
|
|
90
153
|
if (ext !== ".tsx" && ext !== ".jsx" && ext !== ".pyreon") return;
|
|
154
|
+
if (compat === "react" || compat === "preact" || compat === "vue" || compat === "solid") return;
|
|
91
155
|
const result = transformJSX(code, id);
|
|
92
156
|
for (const w of result.warnings) this.warn(`${w.message} (${id}:${w.line}:${w.column})`);
|
|
93
157
|
let output = result.code;
|
|
@@ -189,38 +253,41 @@ function braceDepthAt(code, pos) {
|
|
|
189
253
|
}
|
|
190
254
|
return depth;
|
|
191
255
|
}
|
|
256
|
+
/** Rewrite module-scope `signal()` calls to `__hmr_signal()` for state preservation. */
|
|
257
|
+
function rewriteSignals(code, moduleId) {
|
|
258
|
+
const escapedId = JSON.stringify(moduleId);
|
|
259
|
+
const matches = [];
|
|
260
|
+
let m = SIGNAL_PREFIX_RE.exec(code);
|
|
261
|
+
while (m !== null) {
|
|
262
|
+
const argsStart = m.index + m[0].length;
|
|
263
|
+
const args = extractBalancedArgs(code, argsStart);
|
|
264
|
+
if (args === null) {
|
|
265
|
+
m = SIGNAL_PREFIX_RE.exec(code);
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
if (braceDepthAt(code, m.index) === 0) matches.push({
|
|
269
|
+
start: m.index,
|
|
270
|
+
end: argsStart + args.length + 1,
|
|
271
|
+
prefix: m[1] ?? "",
|
|
272
|
+
name: m[2] ?? "",
|
|
273
|
+
args
|
|
274
|
+
});
|
|
275
|
+
m = SIGNAL_PREFIX_RE.exec(code);
|
|
276
|
+
}
|
|
277
|
+
SIGNAL_PREFIX_RE.lastIndex = 0;
|
|
278
|
+
let output = code;
|
|
279
|
+
for (let i = matches.length - 1; i >= 0; i--) {
|
|
280
|
+
const { start, end, prefix, name, args } = matches[i];
|
|
281
|
+
const replacement = `${prefix}__hmr_signal(${escapedId}, ${JSON.stringify(name)}, signal, ${args})`;
|
|
282
|
+
output = output.slice(0, start) + replacement + output.slice(end);
|
|
283
|
+
}
|
|
284
|
+
return output;
|
|
285
|
+
}
|
|
192
286
|
function injectHmr(code, moduleId) {
|
|
193
287
|
const hasSignals = SIGNAL_PREFIX_RE.test(code);
|
|
194
288
|
SIGNAL_PREFIX_RE.lastIndex = 0;
|
|
195
289
|
if (!EXPORT_COMPONENT_RE.test(code) && !hasSignals) return code;
|
|
196
|
-
let output = code;
|
|
197
|
-
if (hasSignals) {
|
|
198
|
-
const escapedId = JSON.stringify(moduleId);
|
|
199
|
-
const matches = [];
|
|
200
|
-
let m = SIGNAL_PREFIX_RE.exec(code);
|
|
201
|
-
while (m !== null) {
|
|
202
|
-
const argsStart = m.index + m[0].length;
|
|
203
|
-
const args = extractBalancedArgs(code, argsStart);
|
|
204
|
-
if (args === null) {
|
|
205
|
-
m = SIGNAL_PREFIX_RE.exec(code);
|
|
206
|
-
continue;
|
|
207
|
-
}
|
|
208
|
-
if (braceDepthAt(code, m.index) === 0) matches.push({
|
|
209
|
-
start: m.index,
|
|
210
|
-
end: argsStart + args.length + 1,
|
|
211
|
-
prefix: m[1] ?? "",
|
|
212
|
-
name: m[2] ?? "",
|
|
213
|
-
args
|
|
214
|
-
});
|
|
215
|
-
m = SIGNAL_PREFIX_RE.exec(code);
|
|
216
|
-
}
|
|
217
|
-
SIGNAL_PREFIX_RE.lastIndex = 0;
|
|
218
|
-
for (let i = matches.length - 1; i >= 0; i--) {
|
|
219
|
-
const { start, end, prefix, name, args } = matches[i];
|
|
220
|
-
const replacement = `${prefix}__hmr_signal(${escapedId}, ${JSON.stringify(name)}, signal, ${args})`;
|
|
221
|
-
output = output.slice(0, start) + replacement + output.slice(end);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
290
|
+
let output = hasSignals ? rewriteSignals(code, moduleId) : code;
|
|
224
291
|
const escapedId = JSON.stringify(moduleId);
|
|
225
292
|
const lines = [];
|
|
226
293
|
if (hasSignals) lines.push(`import { __hmr_signal, __hmr_dispose } from "${HMR_RUNTIME_IMPORT}";`);
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @pyreon/vite-plugin — Vite integration for Pyreon framework.\n *\n * Applies Pyreon's JSX reactive transform to .tsx, .jsx, and .pyreon files,\n * and configures Vite to use Pyreon's JSX runtime.\n *\n * ## Basic usage (SPA)\n *\n * import pyreon from \"@pyreon/vite-plugin\"\n * export default { plugins: [pyreon()] }\n *\n * ## Drop-in compat mode (zero code changes)\n *\n * import pyreon from \"@pyreon/vite-plugin\"\n * export default { plugins: [pyreon({ compat: \"react\" })] }\n *\n * Aliases `react`, `react-dom`, `vue`, `solid-js`, or `preact` imports to\n * Pyreon's compat packages — existing code works without changing imports.\n *\n * ## SSR mode\n *\n * import pyreon from \"@pyreon/vite-plugin\"\n * export default { plugins: [pyreon({ ssr: { entry: \"./src/entry-server.ts\" } })] }\n *\n * In SSR mode, the plugin adds dev server middleware that:\n * 1. Loads your server entry via Vite's `ssrLoadModule`\n * 2. Calls the exported `handler` or default export (Request → Response)\n * 3. Returns the SSR'd HTML for every non-asset request\n *\n * For production, build separately:\n * vite build # client bundle\n * vite build --ssr src/entry-server.ts --outDir dist/server # server bundle\n */\n\nimport { transformJSX } from \"@pyreon/compiler\"\nimport type { Plugin, ViteDevServer } from \"vite\"\n\n// Virtual module ID for the HMR runtime\nconst HMR_RUNTIME_ID = \"\\0pyreon/hmr-runtime\"\nconst HMR_RUNTIME_IMPORT = \"virtual:pyreon/hmr-runtime\"\n\nexport type CompatFramework = \"react\" | \"preact\" | \"vue\" | \"solid\"\n\nexport interface PyreonPluginOptions {\n /**\n * Alias imports from an existing framework to Pyreon's compat layer.\n *\n * This lets you drop Pyreon into an existing project with zero code changes —\n * `import { useState } from \"react\"` will resolve to `@pyreon/react-compat`.\n *\n * @example\n * pyreon({ compat: \"react\" }) // react + react-dom → @pyreon/react-compat\n * pyreon({ compat: \"vue\" }) // vue → @pyreon/vue-compat\n * pyreon({ compat: \"solid\" }) // solid-js → @pyreon/solid-compat\n * pyreon({ compat: \"preact\" }) // preact + hooks + signals → @pyreon/preact-compat\n */\n compat?: CompatFramework\n\n /**\n * Enable SSR dev middleware.\n *\n * Pass an object with `entry` pointing to your server entry file.\n * The entry must export a `handler` function: `(req: Request) => Promise<Response>`\n * or a default export of the same type.\n *\n * @example\n * pyreonPlugin({ ssr: { entry: \"./src/entry-server.ts\" } })\n */\n ssr?: {\n /** Server entry file path (e.g. \"./src/entry-server.ts\") */\n entry: string\n }\n}\n\n// ── Compat alias maps ─────────────────────────────────────────────────────────\n\nconst COMPAT_ALIASES: Record<CompatFramework, Record<string, string>> = {\n react: {\n react: \"@pyreon/react-compat\",\n \"react/jsx-runtime\": \"@pyreon/core/jsx-runtime\",\n \"react/jsx-dev-runtime\": \"@pyreon/core/jsx-runtime\",\n \"react-dom\": \"@pyreon/react-compat/dom\",\n \"react-dom/client\": \"@pyreon/react-compat/dom\",\n },\n preact: {\n preact: \"@pyreon/preact-compat\",\n \"preact/hooks\": \"@pyreon/preact-compat/hooks\",\n \"@preact/signals\": \"@pyreon/preact-compat/signals\",\n },\n vue: {\n vue: \"@pyreon/vue-compat\",\n },\n solid: {\n \"solid-js\": \"@pyreon/solid-compat\",\n },\n}\n\nexport default function pyreonPlugin(options?: PyreonPluginOptions): Plugin {\n const ssrConfig = options?.ssr\n const compat = options?.compat\n let isBuild = false\n\n return {\n name: \"pyreon\",\n enforce: \"pre\",\n\n config(_, env) {\n isBuild = env.command === \"build\"\n\n const aliases = compat\n ? Object.entries(COMPAT_ALIASES[compat]).map(([find, replacement]) => ({\n find,\n replacement,\n }))\n : []\n\n return {\n resolve: {\n alias: aliases,\n conditions: [\"bun\"],\n },\n oxc: {\n jsx: {\n runtime: \"automatic\",\n importSource: \"@pyreon/core\",\n },\n },\n // In SSR build mode, configure the entry\n ...(env.isSsrBuild && ssrConfig\n ? {\n build: {\n ssr: true,\n rollupOptions: {\n input: ssrConfig.entry,\n },\n },\n }\n : {}),\n }\n },\n\n // ── Virtual module: HMR runtime ─────────────────────────────────────────\n resolveId(id) {\n if (id === HMR_RUNTIME_IMPORT) return HMR_RUNTIME_ID\n },\n\n load(id) {\n if (id === HMR_RUNTIME_ID) {\n return HMR_RUNTIME_SOURCE\n }\n },\n\n transform(code, id) {\n const ext = getExt(id)\n if (ext !== \".tsx\" && ext !== \".jsx\" && ext !== \".pyreon\") return\n const result = transformJSX(code, id)\n // Surface compiler warnings in the terminal\n for (const w of result.warnings) {\n this.warn(`${w.message} (${id}:${w.line}:${w.column})`)\n }\n\n let output = result.code\n\n // ── HMR injection (dev only) ────────────────────────────────────────\n if (!isBuild) {\n output = injectHmr(output, id)\n }\n\n return { code: output, map: null }\n },\n\n // ── SSR dev middleware ───────────────────────────────────────────────────\n configureServer(server: ViteDevServer) {\n if (!ssrConfig) return\n\n // Return a function so the middleware runs AFTER Vite's built-in middleware\n // (static files, HMR, etc.) — only handle requests that Vite doesn't serve.\n return () => {\n server.middlewares.use(async (req, res, next) => {\n if (req.method !== \"GET\") return next()\n const url = req.url ?? \"/\"\n if (isAssetRequest(url)) return next()\n\n try {\n await handleSsrRequest(server, ssrConfig.entry, url, req, res, next)\n } catch (err) {\n server.ssrFixStacktrace(err as Error)\n next(err)\n }\n })\n }\n },\n }\n}\n\nasync function handleSsrRequest(\n server: ViteDevServer,\n entry: string,\n url: string,\n req: import(\"node:http\").IncomingMessage,\n res: import(\"node:http\").ServerResponse,\n next: (err?: unknown) => void,\n): Promise<void> {\n const mod = await server.ssrLoadModule(entry)\n const handler = mod.handler ?? mod.default\n\n if (typeof handler !== \"function\") {\n next()\n return\n }\n\n const origin = `http://${req.headers.host ?? \"localhost\"}`\n const fullUrl = new URL(url, origin)\n const request = new Request(fullUrl.href, {\n method: req.method ?? \"GET\",\n headers: Object.entries(req.headers).reduce((h, [k, v]) => {\n if (v) h.set(k, Array.isArray(v) ? v.join(\", \") : v)\n return h\n }, new Headers()),\n })\n\n const response: Response = await handler(request)\n let html = await response.text()\n\n html = await server.transformIndexHtml(url, html)\n\n res.statusCode = response.status\n response.headers.forEach((v, k) => {\n res.setHeader(k, v)\n })\n res.end(html)\n}\n\n// ── HMR injection ─────────────────────────────────────────────────────────────\n\n/**\n * Regex that detects signal declarations (prefix + variable name).\n * The arguments are extracted via balanced-paren matching in `injectHmr`.\n * A brace-depth check filters out matches inside functions/blocks — only\n * module-scope (depth 0) signals are rewritten for HMR state preservation.\n */\nconst SIGNAL_PREFIX_RE = /^((?:export\\s+)?(?:const|let)\\s+(\\w+)\\s*=\\s*)signal\\(/gm\n\n/**\n * Detect whether the module exports any component-like functions\n * (uppercase first letter — standard convention for JSX components).\n */\nconst EXPORT_COMPONENT_RE =\n /export\\s+(?:default\\s+)?(?:function\\s+([A-Z]\\w*)|const\\s+([A-Z]\\w*)\\s*[=:])/\n\nfunction skipStringLiteral(code: string, start: number, quote: string): number {\n let j = start + 1\n while (j < code.length) {\n if (code[j] === \"\\\\\") {\n j += 2\n continue\n }\n if (code[j] === quote) break\n j++\n }\n return j\n}\n\nfunction extractBalancedArgs(code: string, start: number): string | null {\n let depth = 1\n for (let i = start; i < code.length; i++) {\n const ch = code[i]\n if (ch === \"(\") depth++\n else if (ch === \")\") {\n depth--\n if (depth === 0) return code.slice(start, i)\n } else if (ch === '\"' || ch === \"'\" || ch === \"`\") {\n i = skipStringLiteral(code, i, ch)\n }\n }\n return null\n}\n\n/**\n * Compute brace depth at position `pos` — returns 0 for module scope.\n * Skips string literals to avoid counting braces inside strings.\n */\nfunction braceDepthAt(code: string, pos: number): number {\n let depth = 0\n for (let i = 0; i < pos; i++) {\n const ch = code[i]\n if (ch === \"{\") depth++\n else if (ch === \"}\") depth--\n else if (ch === '\"' || ch === \"'\" || ch === \"`\") {\n i = skipStringLiteral(code, i, ch)\n }\n }\n return depth\n}\n\nfunction injectHmr(code: string, moduleId: string): string {\n const hasSignals = SIGNAL_PREFIX_RE.test(code)\n SIGNAL_PREFIX_RE.lastIndex = 0\n\n const hasComponentExport = EXPORT_COMPONENT_RE.test(code)\n\n // Only inject HMR if the module exports components or has module-scope signals\n if (!hasComponentExport && !hasSignals) return code\n\n let output = code\n\n // Rewrite top-level signal() calls to use __hmr_signal for state preservation\n if (hasSignals) {\n const escapedId = JSON.stringify(moduleId)\n // Process matches in reverse order so indices stay valid after replacement\n const matches: {\n start: number\n end: number\n prefix: string\n name: string\n args: string\n }[] = []\n let m: RegExpExecArray | null = SIGNAL_PREFIX_RE.exec(code)\n while (m !== null) {\n const argsStart = m.index + m[0].length\n const args = extractBalancedArgs(code, argsStart)\n if (args === null) {\n m = SIGNAL_PREFIX_RE.exec(code)\n continue // unbalanced — skip\n }\n // Only rewrite module-scope signals (brace depth 0).\n if (braceDepthAt(code, m.index) === 0) {\n matches.push({\n start: m.index,\n end: argsStart + args.length + 1, // +1 for closing paren\n prefix: m[1] ?? \"\",\n name: m[2] ?? \"\",\n args,\n })\n }\n m = SIGNAL_PREFIX_RE.exec(code)\n }\n SIGNAL_PREFIX_RE.lastIndex = 0\n\n // Replace in reverse to preserve offsets\n for (let i = matches.length - 1; i >= 0; i--) {\n const { start, end, prefix, name, args } = matches[i] as (typeof matches)[number]\n const replacement = `${prefix}__hmr_signal(${escapedId}, ${JSON.stringify(name)}, signal, ${args})`\n output = output.slice(0, start) + replacement + output.slice(end)\n }\n }\n\n // Build the HMR footer\n const escapedId = JSON.stringify(moduleId)\n const lines: string[] = []\n\n if (hasSignals) {\n lines.push(`import { __hmr_signal, __hmr_dispose } from \"${HMR_RUNTIME_IMPORT}\";`)\n }\n\n lines.push(`if (import.meta.hot) {`)\n\n if (hasSignals) {\n lines.push(` import.meta.hot.dispose(() => __hmr_dispose(${escapedId}));`)\n }\n\n lines.push(` import.meta.hot.accept();`)\n lines.push(`}`)\n\n output = `${output}\\n\\n${lines.join(\"\\n\")}\\n`\n\n return output\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction getExt(id: string): string {\n const clean = id.split(\"?\")[0] ?? id\n const dot = clean.lastIndexOf(\".\")\n return dot >= 0 ? clean.slice(dot) : \"\"\n}\n\n/** Skip Vite-handled asset requests (CSS, images, HMR, etc.) */\nfunction isAssetRequest(url: string): boolean {\n return (\n url.startsWith(\"/@\") || // @vite/client, @id, @fs, etc.\n url.startsWith(\"/__\") || // __open-in-editor, etc.\n url.includes(\"/node_modules/\") ||\n /\\.(css|js|ts|tsx|jsx|json|ico|png|jpg|jpeg|gif|svg|woff2?|ttf|eot|map)(\\?|$)/.test(url)\n )\n}\n\n// ── HMR runtime source (served as virtual module) ─────────────────────────────\n//\n// Inlined here so it's available without a filesystem read. This is the\n// compiled-to-JS version of hmr-runtime.ts — kept in sync manually.\n\nconst HMR_RUNTIME_SOURCE = `\nconst REGISTRY_KEY = \"__pyreon_hmr_registry__\";\n\nfunction getRegistry() {\n if (!globalThis[REGISTRY_KEY]) {\n globalThis[REGISTRY_KEY] = new Map();\n }\n return globalThis[REGISTRY_KEY];\n}\n\nconst moduleSignals = new Map();\n\nexport function __hmr_signal(moduleId, name, signalFn, initialValue) {\n const registry = getRegistry();\n const saved = registry.get(moduleId);\n const value = saved?.has(name) ? saved.get(name) : initialValue;\n const s = signalFn(value);\n\n let mod = moduleSignals.get(moduleId);\n if (!mod) {\n mod = { entries: new Map() };\n moduleSignals.set(moduleId, mod);\n }\n mod.entries.set(name, s);\n\n return s;\n}\n\nexport function __hmr_dispose(moduleId) {\n const mod = moduleSignals.get(moduleId);\n if (!mod) return;\n\n const registry = getRegistry();\n const saved = new Map();\n for (const [name, s] of mod.entries) {\n saved.set(name, s.peek());\n }\n registry.set(moduleId, saved);\n moduleSignals.delete(moduleId);\n}\n`\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["pathResolve"],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @pyreon/vite-plugin — Vite integration for Pyreon framework.\n *\n * Applies Pyreon's JSX reactive transform to .tsx, .jsx, and .pyreon files,\n * and configures Vite to use Pyreon's JSX runtime.\n *\n * ## Basic usage (SPA)\n *\n * import pyreon from \"@pyreon/vite-plugin\"\n * export default { plugins: [pyreon()] }\n *\n * ## Drop-in compat mode (zero code changes)\n *\n * import pyreon from \"@pyreon/vite-plugin\"\n * export default { plugins: [pyreon({ compat: \"react\" })] }\n *\n * Aliases `react`, `react-dom`, `vue`, `solid-js`, or `preact` imports to\n * Pyreon's compat packages — existing code works without changing imports.\n *\n * ## SSR mode\n *\n * import pyreon from \"@pyreon/vite-plugin\"\n * export default { plugins: [pyreon({ ssr: { entry: \"./src/entry-server.ts\" } })] }\n *\n * In SSR mode, the plugin adds dev server middleware that:\n * 1. Loads your server entry via Vite's `ssrLoadModule`\n * 2. Calls the exported `handler` or default export (Request → Response)\n * 3. Returns the SSR'd HTML for every non-asset request\n *\n * For production, build separately:\n * vite build # client bundle\n * vite build --ssr src/entry-server.ts --outDir dist/server # server bundle\n */\n\nimport { readFileSync } from \"node:fs\"\nimport { resolve as pathResolve } from \"node:path\"\nimport { transformJSX } from \"@pyreon/compiler\"\nimport type { Plugin, ViteDevServer } from \"vite\"\n\n// Virtual module ID for the HMR runtime\nconst HMR_RUNTIME_ID = \"\\0pyreon/hmr-runtime\"\nconst HMR_RUNTIME_IMPORT = \"virtual:pyreon/hmr-runtime\"\n\nexport type CompatFramework = \"react\" | \"preact\" | \"vue\" | \"solid\"\n\nexport interface PyreonPluginOptions {\n /**\n * Alias imports from an existing framework to Pyreon's compat layer.\n *\n * This lets you drop Pyreon into an existing project with zero code changes —\n * `import { useState } from \"react\"` will resolve to `@pyreon/react-compat`.\n *\n * @example\n * pyreon({ compat: \"react\" }) // react + react-dom → @pyreon/react-compat\n * pyreon({ compat: \"vue\" }) // vue → @pyreon/vue-compat\n * pyreon({ compat: \"solid\" }) // solid-js → @pyreon/solid-compat\n * pyreon({ compat: \"preact\" }) // preact + hooks + signals → @pyreon/preact-compat\n */\n compat?: CompatFramework\n\n /**\n * Enable SSR dev middleware.\n *\n * Pass an object with `entry` pointing to your server entry file.\n * The entry must export a `handler` function: `(req: Request) => Promise<Response>`\n * or a default export of the same type.\n *\n * @example\n * pyreonPlugin({ ssr: { entry: \"./src/entry-server.ts\" } })\n */\n ssr?: {\n /** Server entry file path (e.g. \"./src/entry-server.ts\") */\n entry: string\n }\n}\n\n// ── Compat JSX import sources ─────────────────────────────────────────────────\n\nconst COMPAT_JSX_SOURCE: Record<CompatFramework, string> = {\n react: \"@pyreon/react-compat\",\n preact: \"@pyreon/preact-compat\",\n vue: \"@pyreon/vue-compat\",\n solid: \"@pyreon/solid-compat\",\n}\n\n// ── Compat alias maps ─────────────────────────────────────────────────────────\n\nconst COMPAT_ALIASES: Record<CompatFramework, Record<string, string>> = {\n react: {\n react: \"@pyreon/react-compat\",\n \"react/jsx-runtime\": \"@pyreon/react-compat/jsx-runtime\",\n \"react/jsx-dev-runtime\": \"@pyreon/react-compat/jsx-runtime\",\n \"react-dom\": \"@pyreon/react-compat/dom\",\n \"react-dom/client\": \"@pyreon/react-compat/dom\",\n },\n preact: {\n preact: \"@pyreon/preact-compat\",\n \"preact/hooks\": \"@pyreon/preact-compat/hooks\",\n \"preact/jsx-runtime\": \"@pyreon/preact-compat/jsx-runtime\",\n \"preact/jsx-dev-runtime\": \"@pyreon/preact-compat/jsx-runtime\",\n \"@preact/signals\": \"@pyreon/preact-compat/signals\",\n },\n vue: {\n vue: \"@pyreon/vue-compat\",\n \"vue/jsx-runtime\": \"@pyreon/vue-compat/jsx-runtime\",\n \"vue/jsx-dev-runtime\": \"@pyreon/vue-compat/jsx-runtime\",\n },\n solid: {\n \"solid-js\": \"@pyreon/solid-compat\",\n \"solid-js/jsx-runtime\": \"@pyreon/solid-compat/jsx-runtime\",\n \"solid-js/jsx-dev-runtime\": \"@pyreon/solid-compat/jsx-runtime\",\n },\n}\n\n/**\n * Resolve a package specifier to an absolute source path, respecting the \"bun\"\n * export condition. Falls back to the \"import\" condition.\n *\n * This is needed because Vite 8's resolve pipeline doesn't consistently apply\n * custom conditions from `resolve.conditions` during the `vite:import-analysis`\n * phase for aliased workspace packages.\n */\nfunction resolveWithBunCondition(specifier: string, projectRoot: string): string | undefined {\n // Split specifier: \"@pyreon/react-compat/dom\" → pkg=\"@pyreon/react-compat\", subpath=\"./dom\"\n const parts = specifier.startsWith(\"@\")\n ? specifier.split(\"/\").slice(0, 2)\n : specifier.split(\"/\").slice(0, 1)\n const pkgName = parts.join(\"/\")\n const subpath = specifier.slice(pkgName.length) || \".\"\n const exportKey = subpath === \".\" ? \".\" : `.${subpath}`\n\n try {\n // Walk up from project root to find node_modules containing the package\n const pkgDir = pathResolve(projectRoot, \"node_modules\", pkgName)\n const pkgJsonPath = pathResolve(pkgDir, \"package.json\")\n const pkgJson = JSON.parse(readFileSync(pkgJsonPath, \"utf-8\")) as {\n exports?: Record<string, Record<string, string> | string>\n }\n\n const exp = pkgJson.exports?.[exportKey]\n if (!exp) return undefined\n\n if (typeof exp === \"string\") return pathResolve(pkgDir, exp)\n // Prefer bun → import → default\n const target = exp.bun ?? exp.import ?? exp.default\n return target ? pathResolve(pkgDir, target) : undefined\n } catch {\n return undefined\n }\n}\n\n/**\n * Return the Pyreon compat target for an import specifier, or undefined if\n * the import should not be redirected.\n */\nfunction getCompatTarget(compat: CompatFramework | undefined, id: string): string | undefined {\n if (!compat) return undefined\n const aliased = COMPAT_ALIASES[compat][id]\n if (aliased) return aliased\n // OXC's JSX transform reads jsxImportSource from tsconfig (@pyreon/core),\n // not from our plugin config. Redirect JSX runtime imports in compat mode.\n if (id === \"@pyreon/core/jsx-runtime\" || id === \"@pyreon/core/jsx-dev-runtime\") {\n if (compat === \"react\") return \"@pyreon/react-compat/jsx-runtime\"\n if (compat === \"preact\") return \"@pyreon/preact-compat/jsx-runtime\"\n if (compat === \"vue\") return \"@pyreon/vue-compat/jsx-runtime\"\n if (compat === \"solid\") return \"@pyreon/solid-compat/jsx-runtime\"\n }\n return undefined\n}\n\nexport default function pyreonPlugin(options?: PyreonPluginOptions): Plugin {\n const ssrConfig = options?.ssr\n const compat = options?.compat\n let isBuild = false\n let projectRoot = \"\"\n // Cache resolved absolute paths for compat aliases\n const resolvedAliases = new Map<string, string>()\n\n return {\n name: \"pyreon\",\n enforce: \"pre\",\n\n config(userConfig, env) {\n isBuild = env.command === \"build\"\n // Capture the project root for package resolution in resolveId\n projectRoot = userConfig.root ?? process.cwd()\n\n // Tell Vite's dep scanner not to pre-bundle the aliased framework imports —\n // they resolve to workspace packages via our resolveId hook, not node_modules.\n const optimizeDepsExclude = compat ? Object.keys(COMPAT_ALIASES[compat]) : []\n\n return {\n resolve: {\n conditions: [\"bun\"],\n },\n optimizeDeps: {\n exclude: optimizeDepsExclude,\n },\n oxc: {\n jsx: {\n runtime: \"automatic\",\n importSource: compat ? COMPAT_JSX_SOURCE[compat] : \"@pyreon/core\",\n },\n },\n // In SSR build mode, configure the entry\n ...(env.isSsrBuild && ssrConfig\n ? {\n build: {\n ssr: true,\n rollupOptions: {\n input: ssrConfig.entry,\n },\n },\n }\n : {}),\n }\n },\n\n // ── Virtual module + compat alias resolution ─────────────────────────────\n resolveId(id) {\n if (id === HMR_RUNTIME_IMPORT) return HMR_RUNTIME_ID\n const target = getCompatTarget(compat, id)\n if (!target) return\n\n // Use cached resolution or resolve with bun condition\n let resolved = resolvedAliases.get(target)\n if (!resolved) {\n resolved = resolveWithBunCondition(target, projectRoot)\n if (resolved) resolvedAliases.set(target, resolved)\n }\n return resolved\n },\n\n load(id) {\n if (id === HMR_RUNTIME_ID) {\n return HMR_RUNTIME_SOURCE\n }\n },\n\n transform(code, id) {\n const ext = getExt(id)\n if (ext !== \".tsx\" && ext !== \".jsx\" && ext !== \".pyreon\") return\n\n // In compat mode, skip Pyreon's reactive JSX transform.\n // OXC's built-in JSX transform handles jsx() calls; the compat\n // JSX runtime wraps components for re-render support.\n if (compat === \"react\" || compat === \"preact\" || compat === \"vue\" || compat === \"solid\")\n return\n\n const result = transformJSX(code, id)\n // Surface compiler warnings in the terminal\n for (const w of result.warnings) {\n this.warn(`${w.message} (${id}:${w.line}:${w.column})`)\n }\n\n let output = result.code\n\n // ── HMR injection (dev only) ────────────────────────────────────────\n if (!isBuild) {\n output = injectHmr(output, id)\n }\n\n return { code: output, map: null }\n },\n\n // ── SSR dev middleware ───────────────────────────────────────────────────\n configureServer(server: ViteDevServer) {\n if (!ssrConfig) return\n\n // Return a function so the middleware runs AFTER Vite's built-in middleware\n // (static files, HMR, etc.) — only handle requests that Vite doesn't serve.\n return () => {\n server.middlewares.use(async (req, res, next) => {\n if (req.method !== \"GET\") return next()\n const url = req.url ?? \"/\"\n if (isAssetRequest(url)) return next()\n\n try {\n await handleSsrRequest(server, ssrConfig.entry, url, req, res, next)\n } catch (err) {\n server.ssrFixStacktrace(err as Error)\n next(err)\n }\n })\n }\n },\n }\n}\n\nasync function handleSsrRequest(\n server: ViteDevServer,\n entry: string,\n url: string,\n req: import(\"node:http\").IncomingMessage,\n res: import(\"node:http\").ServerResponse,\n next: (err?: unknown) => void,\n): Promise<void> {\n const mod = await server.ssrLoadModule(entry)\n const handler = mod.handler ?? mod.default\n\n if (typeof handler !== \"function\") {\n next()\n return\n }\n\n const origin = `http://${req.headers.host ?? \"localhost\"}`\n const fullUrl = new URL(url, origin)\n const request = new Request(fullUrl.href, {\n method: req.method ?? \"GET\",\n headers: Object.entries(req.headers).reduce((h, [k, v]) => {\n if (v) h.set(k, Array.isArray(v) ? v.join(\", \") : v)\n return h\n }, new Headers()),\n })\n\n const response: Response = await handler(request)\n let html = await response.text()\n\n html = await server.transformIndexHtml(url, html)\n\n res.statusCode = response.status\n response.headers.forEach((v, k) => {\n res.setHeader(k, v)\n })\n res.end(html)\n}\n\n// ── HMR injection ─────────────────────────────────────────────────────────────\n\n/**\n * Regex that detects signal declarations (prefix + variable name).\n * The arguments are extracted via balanced-paren matching in `injectHmr`.\n * A brace-depth check filters out matches inside functions/blocks — only\n * module-scope (depth 0) signals are rewritten for HMR state preservation.\n */\nconst SIGNAL_PREFIX_RE = /^((?:export\\s+)?(?:const|let)\\s+(\\w+)\\s*=\\s*)signal\\(/gm\n\n/**\n * Detect whether the module exports any component-like functions\n * (uppercase first letter — standard convention for JSX components).\n */\nconst EXPORT_COMPONENT_RE =\n /export\\s+(?:default\\s+)?(?:function\\s+([A-Z]\\w*)|const\\s+([A-Z]\\w*)\\s*[=:])/\n\nfunction skipStringLiteral(code: string, start: number, quote: string): number {\n let j = start + 1\n while (j < code.length) {\n if (code[j] === \"\\\\\") {\n j += 2\n continue\n }\n if (code[j] === quote) break\n j++\n }\n return j\n}\n\nfunction extractBalancedArgs(code: string, start: number): string | null {\n let depth = 1\n for (let i = start; i < code.length; i++) {\n const ch = code[i]\n if (ch === \"(\") depth++\n else if (ch === \")\") {\n depth--\n if (depth === 0) return code.slice(start, i)\n } else if (ch === '\"' || ch === \"'\" || ch === \"`\") {\n i = skipStringLiteral(code, i, ch)\n }\n }\n return null\n}\n\n/**\n * Compute brace depth at position `pos` — returns 0 for module scope.\n * Skips string literals to avoid counting braces inside strings.\n */\nfunction braceDepthAt(code: string, pos: number): number {\n let depth = 0\n for (let i = 0; i < pos; i++) {\n const ch = code[i]\n if (ch === \"{\") depth++\n else if (ch === \"}\") depth--\n else if (ch === '\"' || ch === \"'\" || ch === \"`\") {\n i = skipStringLiteral(code, i, ch)\n }\n }\n return depth\n}\n\n/** Rewrite module-scope `signal()` calls to `__hmr_signal()` for state preservation. */\nfunction rewriteSignals(code: string, moduleId: string): string {\n const escapedId = JSON.stringify(moduleId)\n const matches: {\n start: number\n end: number\n prefix: string\n name: string\n args: string\n }[] = []\n let m: RegExpExecArray | null = SIGNAL_PREFIX_RE.exec(code)\n while (m !== null) {\n const argsStart = m.index + m[0].length\n const args = extractBalancedArgs(code, argsStart)\n if (args === null) {\n m = SIGNAL_PREFIX_RE.exec(code)\n continue // unbalanced — skip\n }\n // Only rewrite module-scope signals (brace depth 0).\n if (braceDepthAt(code, m.index) === 0) {\n matches.push({\n start: m.index,\n end: argsStart + args.length + 1, // +1 for closing paren\n prefix: m[1] ?? \"\",\n name: m[2] ?? \"\",\n args,\n })\n }\n m = SIGNAL_PREFIX_RE.exec(code)\n }\n SIGNAL_PREFIX_RE.lastIndex = 0\n\n // Replace in reverse to preserve offsets\n let output = code\n for (let i = matches.length - 1; i >= 0; i--) {\n const { start, end, prefix, name, args } = matches[i] as (typeof matches)[number]\n const replacement = `${prefix}__hmr_signal(${escapedId}, ${JSON.stringify(name)}, signal, ${args})`\n output = output.slice(0, start) + replacement + output.slice(end)\n }\n return output\n}\n\nfunction injectHmr(code: string, moduleId: string): string {\n const hasSignals = SIGNAL_PREFIX_RE.test(code)\n SIGNAL_PREFIX_RE.lastIndex = 0\n\n const hasComponentExport = EXPORT_COMPONENT_RE.test(code)\n\n // Only inject HMR if the module exports components or has module-scope signals\n if (!hasComponentExport && !hasSignals) return code\n\n let output = hasSignals ? rewriteSignals(code, moduleId) : code\n\n // Build the HMR footer\n const escapedId = JSON.stringify(moduleId)\n const lines: string[] = []\n\n if (hasSignals) {\n lines.push(`import { __hmr_signal, __hmr_dispose } from \"${HMR_RUNTIME_IMPORT}\";`)\n }\n\n lines.push(`if (import.meta.hot) {`)\n\n if (hasSignals) {\n lines.push(` import.meta.hot.dispose(() => __hmr_dispose(${escapedId}));`)\n }\n\n lines.push(` import.meta.hot.accept();`)\n lines.push(`}`)\n\n output = `${output}\\n\\n${lines.join(\"\\n\")}\\n`\n\n return output\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction getExt(id: string): string {\n const clean = id.split(\"?\")[0] ?? id\n const dot = clean.lastIndexOf(\".\")\n return dot >= 0 ? clean.slice(dot) : \"\"\n}\n\n/** Skip Vite-handled asset requests (CSS, images, HMR, etc.) */\nfunction isAssetRequest(url: string): boolean {\n return (\n url.startsWith(\"/@\") || // @vite/client, @id, @fs, etc.\n url.startsWith(\"/__\") || // __open-in-editor, etc.\n url.includes(\"/node_modules/\") ||\n /\\.(css|js|ts|tsx|jsx|json|ico|png|jpg|jpeg|gif|svg|woff2?|ttf|eot|map)(\\?|$)/.test(url)\n )\n}\n\n// ── HMR runtime source (served as virtual module) ─────────────────────────────\n//\n// Inlined here so it's available without a filesystem read. This is the\n// compiled-to-JS version of hmr-runtime.ts — kept in sync manually.\n\nconst HMR_RUNTIME_SOURCE = `\nconst REGISTRY_KEY = \"__pyreon_hmr_registry__\";\n\nfunction getRegistry() {\n if (!globalThis[REGISTRY_KEY]) {\n globalThis[REGISTRY_KEY] = new Map();\n }\n return globalThis[REGISTRY_KEY];\n}\n\nconst moduleSignals = new Map();\n\nexport function __hmr_signal(moduleId, name, signalFn, initialValue) {\n const registry = getRegistry();\n const saved = registry.get(moduleId);\n const value = saved?.has(name) ? saved.get(name) : initialValue;\n const s = signalFn(value);\n\n let mod = moduleSignals.get(moduleId);\n if (!mod) {\n mod = { entries: new Map() };\n moduleSignals.set(moduleId, mod);\n }\n mod.entries.set(name, s);\n\n return s;\n}\n\nexport function __hmr_dispose(moduleId) {\n const mod = moduleSignals.get(moduleId);\n if (!mod) return;\n\n const registry = getRegistry();\n const saved = new Map();\n for (const [name, s] of mod.entries) {\n saved.set(name, s.peek());\n }\n registry.set(moduleId, saved);\n moduleSignals.delete(moduleId);\n}\n`\n"],"mappings":"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"}
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
1
3
|
import { transformJSX } from "@pyreon/compiler";
|
|
2
4
|
|
|
3
5
|
//#region src/index.ts
|
|
@@ -35,27 +37,68 @@ import { transformJSX } from "@pyreon/compiler";
|
|
|
35
37
|
* vite build --ssr src/entry-server.ts --outDir dist/server # server bundle
|
|
36
38
|
*/
|
|
37
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Resolve a package specifier to an absolute source path, respecting the "bun"
|
|
42
|
+
* export condition. Falls back to the "import" condition.
|
|
43
|
+
*
|
|
44
|
+
* This is needed because Vite 8's resolve pipeline doesn't consistently apply
|
|
45
|
+
* custom conditions from `resolve.conditions` during the `vite:import-analysis`
|
|
46
|
+
* phase for aliased workspace packages.
|
|
47
|
+
*/
|
|
48
|
+
function resolveWithBunCondition(specifier, projectRoot) {
|
|
49
|
+
const pkgName = (specifier.startsWith("@") ? specifier.split("/").slice(0, 2) : specifier.split("/").slice(0, 1)).join("/");
|
|
50
|
+
const subpath = specifier.slice(pkgName.length) || ".";
|
|
51
|
+
const exportKey = subpath === "." ? "." : `.${subpath}`;
|
|
52
|
+
try {
|
|
53
|
+
const pkgDir = resolve(projectRoot, "node_modules", pkgName);
|
|
54
|
+
const pkgJsonPath = resolve(pkgDir, "package.json");
|
|
55
|
+
const exp = JSON.parse(readFileSync(pkgJsonPath, "utf-8")).exports?.[exportKey];
|
|
56
|
+
if (!exp) return void 0;
|
|
57
|
+
if (typeof exp === "string") return resolve(pkgDir, exp);
|
|
58
|
+
const target = exp.bun ?? exp.import ?? exp.default;
|
|
59
|
+
return target ? resolve(pkgDir, target) : void 0;
|
|
60
|
+
} catch {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Return the Pyreon compat target for an import specifier, or undefined if
|
|
66
|
+
* the import should not be redirected.
|
|
67
|
+
*/
|
|
68
|
+
function getCompatTarget(compat, id) {
|
|
69
|
+
if (!compat) return void 0;
|
|
70
|
+
const aliased = COMPAT_ALIASES[compat][id];
|
|
71
|
+
if (aliased) return aliased;
|
|
72
|
+
if (id === "@pyreon/core/jsx-runtime" || id === "@pyreon/core/jsx-dev-runtime") {
|
|
73
|
+
if (compat === "react") return "@pyreon/react-compat/jsx-runtime";
|
|
74
|
+
if (compat === "preact") return "@pyreon/preact-compat/jsx-runtime";
|
|
75
|
+
if (compat === "vue") return "@pyreon/vue-compat/jsx-runtime";
|
|
76
|
+
if (compat === "solid") return "@pyreon/solid-compat/jsx-runtime";
|
|
77
|
+
}
|
|
78
|
+
}
|
|
38
79
|
function pyreonPlugin(options) {
|
|
39
80
|
const ssrConfig = options?.ssr;
|
|
40
81
|
const compat = options?.compat;
|
|
41
82
|
let isBuild = false;
|
|
83
|
+
let projectRoot = "";
|
|
84
|
+
const resolvedAliases = /* @__PURE__ */new Map();
|
|
42
85
|
return {
|
|
43
86
|
name: "pyreon",
|
|
44
87
|
enforce: "pre",
|
|
45
|
-
config(
|
|
88
|
+
config(userConfig, env) {
|
|
46
89
|
isBuild = env.command === "build";
|
|
90
|
+
projectRoot = userConfig.root ?? process.cwd();
|
|
47
91
|
return {
|
|
48
92
|
resolve: {
|
|
49
|
-
alias: compat ? Object.entries(COMPAT_ALIASES[compat]).map(([find, replacement]) => ({
|
|
50
|
-
find,
|
|
51
|
-
replacement
|
|
52
|
-
})) : [],
|
|
53
93
|
conditions: ["bun"]
|
|
54
94
|
},
|
|
95
|
+
optimizeDeps: {
|
|
96
|
+
exclude: compat ? Object.keys(COMPAT_ALIASES[compat]) : []
|
|
97
|
+
},
|
|
55
98
|
oxc: {
|
|
56
99
|
jsx: {
|
|
57
100
|
runtime: "automatic",
|
|
58
|
-
importSource: "@pyreon/core"
|
|
101
|
+
importSource: compat ? COMPAT_JSX_SOURCE[compat] : "@pyreon/core"
|
|
59
102
|
}
|
|
60
103
|
},
|
|
61
104
|
...(env.isSsrBuild && ssrConfig ? {
|
|
@@ -70,6 +113,14 @@ function pyreonPlugin(options) {
|
|
|
70
113
|
},
|
|
71
114
|
resolveId(id) {
|
|
72
115
|
if (id === HMR_RUNTIME_IMPORT) return HMR_RUNTIME_ID;
|
|
116
|
+
const target = getCompatTarget(compat, id);
|
|
117
|
+
if (!target) return;
|
|
118
|
+
let resolved = resolvedAliases.get(target);
|
|
119
|
+
if (!resolved) {
|
|
120
|
+
resolved = resolveWithBunCondition(target, projectRoot);
|
|
121
|
+
if (resolved) resolvedAliases.set(target, resolved);
|
|
122
|
+
}
|
|
123
|
+
return resolved;
|
|
73
124
|
},
|
|
74
125
|
load(id) {
|
|
75
126
|
if (id === HMR_RUNTIME_ID) return HMR_RUNTIME_SOURCE;
|
|
@@ -77,6 +128,7 @@ function pyreonPlugin(options) {
|
|
|
77
128
|
transform(code, id) {
|
|
78
129
|
const ext = getExt(id);
|
|
79
130
|
if (ext !== ".tsx" && ext !== ".jsx" && ext !== ".pyreon") return;
|
|
131
|
+
if (compat === "react" || compat === "preact" || compat === "vue" || compat === "solid") return;
|
|
80
132
|
const result = transformJSX(code, id);
|
|
81
133
|
for (const w of result.warnings) this.warn(`${w.message} (${id}:${w.line}:${w.column})`);
|
|
82
134
|
let output = result.code;
|
|
@@ -170,44 +222,47 @@ function braceDepthAt(code, pos) {
|
|
|
170
222
|
}
|
|
171
223
|
return depth;
|
|
172
224
|
}
|
|
225
|
+
/** Rewrite module-scope `signal()` calls to `__hmr_signal()` for state preservation. */
|
|
226
|
+
function rewriteSignals(code, moduleId) {
|
|
227
|
+
const escapedId = JSON.stringify(moduleId);
|
|
228
|
+
const matches = [];
|
|
229
|
+
let m = SIGNAL_PREFIX_RE.exec(code);
|
|
230
|
+
while (m !== null) {
|
|
231
|
+
const argsStart = m.index + m[0].length;
|
|
232
|
+
const args = extractBalancedArgs(code, argsStart);
|
|
233
|
+
if (args === null) {
|
|
234
|
+
m = SIGNAL_PREFIX_RE.exec(code);
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
if (braceDepthAt(code, m.index) === 0) matches.push({
|
|
238
|
+
start: m.index,
|
|
239
|
+
end: argsStart + args.length + 1,
|
|
240
|
+
prefix: m[1] ?? "",
|
|
241
|
+
name: m[2] ?? "",
|
|
242
|
+
args
|
|
243
|
+
});
|
|
244
|
+
m = SIGNAL_PREFIX_RE.exec(code);
|
|
245
|
+
}
|
|
246
|
+
SIGNAL_PREFIX_RE.lastIndex = 0;
|
|
247
|
+
let output = code;
|
|
248
|
+
for (let i = matches.length - 1; i >= 0; i--) {
|
|
249
|
+
const {
|
|
250
|
+
start,
|
|
251
|
+
end,
|
|
252
|
+
prefix,
|
|
253
|
+
name,
|
|
254
|
+
args
|
|
255
|
+
} = matches[i];
|
|
256
|
+
const replacement = `${prefix}__hmr_signal(${escapedId}, ${JSON.stringify(name)}, signal, ${args})`;
|
|
257
|
+
output = output.slice(0, start) + replacement + output.slice(end);
|
|
258
|
+
}
|
|
259
|
+
return output;
|
|
260
|
+
}
|
|
173
261
|
function injectHmr(code, moduleId) {
|
|
174
262
|
const hasSignals = SIGNAL_PREFIX_RE.test(code);
|
|
175
263
|
SIGNAL_PREFIX_RE.lastIndex = 0;
|
|
176
264
|
if (!EXPORT_COMPONENT_RE.test(code) && !hasSignals) return code;
|
|
177
|
-
let output = code;
|
|
178
|
-
if (hasSignals) {
|
|
179
|
-
const escapedId = JSON.stringify(moduleId);
|
|
180
|
-
const matches = [];
|
|
181
|
-
let m = SIGNAL_PREFIX_RE.exec(code);
|
|
182
|
-
while (m !== null) {
|
|
183
|
-
const argsStart = m.index + m[0].length;
|
|
184
|
-
const args = extractBalancedArgs(code, argsStart);
|
|
185
|
-
if (args === null) {
|
|
186
|
-
m = SIGNAL_PREFIX_RE.exec(code);
|
|
187
|
-
continue;
|
|
188
|
-
}
|
|
189
|
-
if (braceDepthAt(code, m.index) === 0) matches.push({
|
|
190
|
-
start: m.index,
|
|
191
|
-
end: argsStart + args.length + 1,
|
|
192
|
-
prefix: m[1] ?? "",
|
|
193
|
-
name: m[2] ?? "",
|
|
194
|
-
args
|
|
195
|
-
});
|
|
196
|
-
m = SIGNAL_PREFIX_RE.exec(code);
|
|
197
|
-
}
|
|
198
|
-
SIGNAL_PREFIX_RE.lastIndex = 0;
|
|
199
|
-
for (let i = matches.length - 1; i >= 0; i--) {
|
|
200
|
-
const {
|
|
201
|
-
start,
|
|
202
|
-
end,
|
|
203
|
-
prefix,
|
|
204
|
-
name,
|
|
205
|
-
args
|
|
206
|
-
} = matches[i];
|
|
207
|
-
const replacement = `${prefix}__hmr_signal(${escapedId}, ${JSON.stringify(name)}, signal, ${args})`;
|
|
208
|
-
output = output.slice(0, start) + replacement + output.slice(end);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
265
|
+
let output = hasSignals ? rewriteSignals(code, moduleId) : code;
|
|
211
266
|
const escapedId = JSON.stringify(moduleId);
|
|
212
267
|
const lines = [];
|
|
213
268
|
if (hasSignals) lines.push(`import { __hmr_signal, __hmr_dispose } from "${HMR_RUNTIME_IMPORT}";`);
|
package/lib/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":["pathResolve"],"sources":["../../src/index.ts"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index2.d.ts","names":[],"sources":["../../src/index.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index2.d.ts","names":[],"sources":["../../src/index.ts"],"mappings":";;;KA2CY,eAAA;AAAA,UAEK,mBAAA;;;;;;;;;;;;;EAaf,MAAA,GAAS,eAAA;;;;;;;;;;;EAYT,GAAA;gEAEE,KAAA;EAAA;AAAA;AAAA,iBAkGoB,YAAA,CAAa,OAAA,GAAU,mBAAA,GAAsB,MAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/vite-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Vite plugin for Pyreon — .pyreon SFC support, HMR, compiler integration",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"prepublishOnly": "bun run build"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@pyreon/compiler": "^0.
|
|
42
|
+
"@pyreon/compiler": "^0.3.1"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"vite": ">=8.0.0"
|
package/src/index.ts
CHANGED
|
@@ -32,6 +32,8 @@
|
|
|
32
32
|
* vite build --ssr src/entry-server.ts --outDir dist/server # server bundle
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
|
+
import { readFileSync } from "node:fs"
|
|
36
|
+
import { resolve as pathResolve } from "node:path"
|
|
35
37
|
import { transformJSX } from "@pyreon/compiler"
|
|
36
38
|
import type { Plugin, ViteDevServer } from "vite"
|
|
37
39
|
|
|
@@ -72,57 +74,132 @@ export interface PyreonPluginOptions {
|
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
|
|
77
|
+
// ── Compat JSX import sources ─────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
const COMPAT_JSX_SOURCE: Record<CompatFramework, string> = {
|
|
80
|
+
react: "@pyreon/react-compat",
|
|
81
|
+
preact: "@pyreon/preact-compat",
|
|
82
|
+
vue: "@pyreon/vue-compat",
|
|
83
|
+
solid: "@pyreon/solid-compat",
|
|
84
|
+
}
|
|
85
|
+
|
|
75
86
|
// ── Compat alias maps ─────────────────────────────────────────────────────────
|
|
76
87
|
|
|
77
88
|
const COMPAT_ALIASES: Record<CompatFramework, Record<string, string>> = {
|
|
78
89
|
react: {
|
|
79
90
|
react: "@pyreon/react-compat",
|
|
80
|
-
"react/jsx-runtime": "@pyreon/
|
|
81
|
-
"react/jsx-dev-runtime": "@pyreon/
|
|
91
|
+
"react/jsx-runtime": "@pyreon/react-compat/jsx-runtime",
|
|
92
|
+
"react/jsx-dev-runtime": "@pyreon/react-compat/jsx-runtime",
|
|
82
93
|
"react-dom": "@pyreon/react-compat/dom",
|
|
83
94
|
"react-dom/client": "@pyreon/react-compat/dom",
|
|
84
95
|
},
|
|
85
96
|
preact: {
|
|
86
97
|
preact: "@pyreon/preact-compat",
|
|
87
98
|
"preact/hooks": "@pyreon/preact-compat/hooks",
|
|
99
|
+
"preact/jsx-runtime": "@pyreon/preact-compat/jsx-runtime",
|
|
100
|
+
"preact/jsx-dev-runtime": "@pyreon/preact-compat/jsx-runtime",
|
|
88
101
|
"@preact/signals": "@pyreon/preact-compat/signals",
|
|
89
102
|
},
|
|
90
103
|
vue: {
|
|
91
104
|
vue: "@pyreon/vue-compat",
|
|
105
|
+
"vue/jsx-runtime": "@pyreon/vue-compat/jsx-runtime",
|
|
106
|
+
"vue/jsx-dev-runtime": "@pyreon/vue-compat/jsx-runtime",
|
|
92
107
|
},
|
|
93
108
|
solid: {
|
|
94
109
|
"solid-js": "@pyreon/solid-compat",
|
|
110
|
+
"solid-js/jsx-runtime": "@pyreon/solid-compat/jsx-runtime",
|
|
111
|
+
"solid-js/jsx-dev-runtime": "@pyreon/solid-compat/jsx-runtime",
|
|
95
112
|
},
|
|
96
113
|
}
|
|
97
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Resolve a package specifier to an absolute source path, respecting the "bun"
|
|
117
|
+
* export condition. Falls back to the "import" condition.
|
|
118
|
+
*
|
|
119
|
+
* This is needed because Vite 8's resolve pipeline doesn't consistently apply
|
|
120
|
+
* custom conditions from `resolve.conditions` during the `vite:import-analysis`
|
|
121
|
+
* phase for aliased workspace packages.
|
|
122
|
+
*/
|
|
123
|
+
function resolveWithBunCondition(specifier: string, projectRoot: string): string | undefined {
|
|
124
|
+
// Split specifier: "@pyreon/react-compat/dom" → pkg="@pyreon/react-compat", subpath="./dom"
|
|
125
|
+
const parts = specifier.startsWith("@")
|
|
126
|
+
? specifier.split("/").slice(0, 2)
|
|
127
|
+
: specifier.split("/").slice(0, 1)
|
|
128
|
+
const pkgName = parts.join("/")
|
|
129
|
+
const subpath = specifier.slice(pkgName.length) || "."
|
|
130
|
+
const exportKey = subpath === "." ? "." : `.${subpath}`
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
// Walk up from project root to find node_modules containing the package
|
|
134
|
+
const pkgDir = pathResolve(projectRoot, "node_modules", pkgName)
|
|
135
|
+
const pkgJsonPath = pathResolve(pkgDir, "package.json")
|
|
136
|
+
const pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8")) as {
|
|
137
|
+
exports?: Record<string, Record<string, string> | string>
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const exp = pkgJson.exports?.[exportKey]
|
|
141
|
+
if (!exp) return undefined
|
|
142
|
+
|
|
143
|
+
if (typeof exp === "string") return pathResolve(pkgDir, exp)
|
|
144
|
+
// Prefer bun → import → default
|
|
145
|
+
const target = exp.bun ?? exp.import ?? exp.default
|
|
146
|
+
return target ? pathResolve(pkgDir, target) : undefined
|
|
147
|
+
} catch {
|
|
148
|
+
return undefined
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Return the Pyreon compat target for an import specifier, or undefined if
|
|
154
|
+
* the import should not be redirected.
|
|
155
|
+
*/
|
|
156
|
+
function getCompatTarget(compat: CompatFramework | undefined, id: string): string | undefined {
|
|
157
|
+
if (!compat) return undefined
|
|
158
|
+
const aliased = COMPAT_ALIASES[compat][id]
|
|
159
|
+
if (aliased) return aliased
|
|
160
|
+
// OXC's JSX transform reads jsxImportSource from tsconfig (@pyreon/core),
|
|
161
|
+
// not from our plugin config. Redirect JSX runtime imports in compat mode.
|
|
162
|
+
if (id === "@pyreon/core/jsx-runtime" || id === "@pyreon/core/jsx-dev-runtime") {
|
|
163
|
+
if (compat === "react") return "@pyreon/react-compat/jsx-runtime"
|
|
164
|
+
if (compat === "preact") return "@pyreon/preact-compat/jsx-runtime"
|
|
165
|
+
if (compat === "vue") return "@pyreon/vue-compat/jsx-runtime"
|
|
166
|
+
if (compat === "solid") return "@pyreon/solid-compat/jsx-runtime"
|
|
167
|
+
}
|
|
168
|
+
return undefined
|
|
169
|
+
}
|
|
170
|
+
|
|
98
171
|
export default function pyreonPlugin(options?: PyreonPluginOptions): Plugin {
|
|
99
172
|
const ssrConfig = options?.ssr
|
|
100
173
|
const compat = options?.compat
|
|
101
174
|
let isBuild = false
|
|
175
|
+
let projectRoot = ""
|
|
176
|
+
// Cache resolved absolute paths for compat aliases
|
|
177
|
+
const resolvedAliases = new Map<string, string>()
|
|
102
178
|
|
|
103
179
|
return {
|
|
104
180
|
name: "pyreon",
|
|
105
181
|
enforce: "pre",
|
|
106
182
|
|
|
107
|
-
config(
|
|
183
|
+
config(userConfig, env) {
|
|
108
184
|
isBuild = env.command === "build"
|
|
185
|
+
// Capture the project root for package resolution in resolveId
|
|
186
|
+
projectRoot = userConfig.root ?? process.cwd()
|
|
109
187
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
replacement,
|
|
114
|
-
}))
|
|
115
|
-
: []
|
|
188
|
+
// Tell Vite's dep scanner not to pre-bundle the aliased framework imports —
|
|
189
|
+
// they resolve to workspace packages via our resolveId hook, not node_modules.
|
|
190
|
+
const optimizeDepsExclude = compat ? Object.keys(COMPAT_ALIASES[compat]) : []
|
|
116
191
|
|
|
117
192
|
return {
|
|
118
193
|
resolve: {
|
|
119
|
-
alias: aliases,
|
|
120
194
|
conditions: ["bun"],
|
|
121
195
|
},
|
|
196
|
+
optimizeDeps: {
|
|
197
|
+
exclude: optimizeDepsExclude,
|
|
198
|
+
},
|
|
122
199
|
oxc: {
|
|
123
200
|
jsx: {
|
|
124
201
|
runtime: "automatic",
|
|
125
|
-
importSource: "@pyreon/core",
|
|
202
|
+
importSource: compat ? COMPAT_JSX_SOURCE[compat] : "@pyreon/core",
|
|
126
203
|
},
|
|
127
204
|
},
|
|
128
205
|
// In SSR build mode, configure the entry
|
|
@@ -139,9 +216,19 @@ export default function pyreonPlugin(options?: PyreonPluginOptions): Plugin {
|
|
|
139
216
|
}
|
|
140
217
|
},
|
|
141
218
|
|
|
142
|
-
// ── Virtual module
|
|
219
|
+
// ── Virtual module + compat alias resolution ─────────────────────────────
|
|
143
220
|
resolveId(id) {
|
|
144
221
|
if (id === HMR_RUNTIME_IMPORT) return HMR_RUNTIME_ID
|
|
222
|
+
const target = getCompatTarget(compat, id)
|
|
223
|
+
if (!target) return
|
|
224
|
+
|
|
225
|
+
// Use cached resolution or resolve with bun condition
|
|
226
|
+
let resolved = resolvedAliases.get(target)
|
|
227
|
+
if (!resolved) {
|
|
228
|
+
resolved = resolveWithBunCondition(target, projectRoot)
|
|
229
|
+
if (resolved) resolvedAliases.set(target, resolved)
|
|
230
|
+
}
|
|
231
|
+
return resolved
|
|
145
232
|
},
|
|
146
233
|
|
|
147
234
|
load(id) {
|
|
@@ -153,6 +240,13 @@ export default function pyreonPlugin(options?: PyreonPluginOptions): Plugin {
|
|
|
153
240
|
transform(code, id) {
|
|
154
241
|
const ext = getExt(id)
|
|
155
242
|
if (ext !== ".tsx" && ext !== ".jsx" && ext !== ".pyreon") return
|
|
243
|
+
|
|
244
|
+
// In compat mode, skip Pyreon's reactive JSX transform.
|
|
245
|
+
// OXC's built-in JSX transform handles jsx() calls; the compat
|
|
246
|
+
// JSX runtime wraps components for re-render support.
|
|
247
|
+
if (compat === "react" || compat === "preact" || compat === "vue" || compat === "solid")
|
|
248
|
+
return
|
|
249
|
+
|
|
156
250
|
const result = transformJSX(code, id)
|
|
157
251
|
// Surface compiler warnings in the terminal
|
|
158
252
|
for (const w of result.warnings) {
|
|
@@ -293,6 +387,48 @@ function braceDepthAt(code: string, pos: number): number {
|
|
|
293
387
|
return depth
|
|
294
388
|
}
|
|
295
389
|
|
|
390
|
+
/** Rewrite module-scope `signal()` calls to `__hmr_signal()` for state preservation. */
|
|
391
|
+
function rewriteSignals(code: string, moduleId: string): string {
|
|
392
|
+
const escapedId = JSON.stringify(moduleId)
|
|
393
|
+
const matches: {
|
|
394
|
+
start: number
|
|
395
|
+
end: number
|
|
396
|
+
prefix: string
|
|
397
|
+
name: string
|
|
398
|
+
args: string
|
|
399
|
+
}[] = []
|
|
400
|
+
let m: RegExpExecArray | null = SIGNAL_PREFIX_RE.exec(code)
|
|
401
|
+
while (m !== null) {
|
|
402
|
+
const argsStart = m.index + m[0].length
|
|
403
|
+
const args = extractBalancedArgs(code, argsStart)
|
|
404
|
+
if (args === null) {
|
|
405
|
+
m = SIGNAL_PREFIX_RE.exec(code)
|
|
406
|
+
continue // unbalanced — skip
|
|
407
|
+
}
|
|
408
|
+
// Only rewrite module-scope signals (brace depth 0).
|
|
409
|
+
if (braceDepthAt(code, m.index) === 0) {
|
|
410
|
+
matches.push({
|
|
411
|
+
start: m.index,
|
|
412
|
+
end: argsStart + args.length + 1, // +1 for closing paren
|
|
413
|
+
prefix: m[1] ?? "",
|
|
414
|
+
name: m[2] ?? "",
|
|
415
|
+
args,
|
|
416
|
+
})
|
|
417
|
+
}
|
|
418
|
+
m = SIGNAL_PREFIX_RE.exec(code)
|
|
419
|
+
}
|
|
420
|
+
SIGNAL_PREFIX_RE.lastIndex = 0
|
|
421
|
+
|
|
422
|
+
// Replace in reverse to preserve offsets
|
|
423
|
+
let output = code
|
|
424
|
+
for (let i = matches.length - 1; i >= 0; i--) {
|
|
425
|
+
const { start, end, prefix, name, args } = matches[i] as (typeof matches)[number]
|
|
426
|
+
const replacement = `${prefix}__hmr_signal(${escapedId}, ${JSON.stringify(name)}, signal, ${args})`
|
|
427
|
+
output = output.slice(0, start) + replacement + output.slice(end)
|
|
428
|
+
}
|
|
429
|
+
return output
|
|
430
|
+
}
|
|
431
|
+
|
|
296
432
|
function injectHmr(code: string, moduleId: string): string {
|
|
297
433
|
const hasSignals = SIGNAL_PREFIX_RE.test(code)
|
|
298
434
|
SIGNAL_PREFIX_RE.lastIndex = 0
|
|
@@ -302,48 +438,7 @@ function injectHmr(code: string, moduleId: string): string {
|
|
|
302
438
|
// Only inject HMR if the module exports components or has module-scope signals
|
|
303
439
|
if (!hasComponentExport && !hasSignals) return code
|
|
304
440
|
|
|
305
|
-
let output = code
|
|
306
|
-
|
|
307
|
-
// Rewrite top-level signal() calls to use __hmr_signal for state preservation
|
|
308
|
-
if (hasSignals) {
|
|
309
|
-
const escapedId = JSON.stringify(moduleId)
|
|
310
|
-
// Process matches in reverse order so indices stay valid after replacement
|
|
311
|
-
const matches: {
|
|
312
|
-
start: number
|
|
313
|
-
end: number
|
|
314
|
-
prefix: string
|
|
315
|
-
name: string
|
|
316
|
-
args: string
|
|
317
|
-
}[] = []
|
|
318
|
-
let m: RegExpExecArray | null = SIGNAL_PREFIX_RE.exec(code)
|
|
319
|
-
while (m !== null) {
|
|
320
|
-
const argsStart = m.index + m[0].length
|
|
321
|
-
const args = extractBalancedArgs(code, argsStart)
|
|
322
|
-
if (args === null) {
|
|
323
|
-
m = SIGNAL_PREFIX_RE.exec(code)
|
|
324
|
-
continue // unbalanced — skip
|
|
325
|
-
}
|
|
326
|
-
// Only rewrite module-scope signals (brace depth 0).
|
|
327
|
-
if (braceDepthAt(code, m.index) === 0) {
|
|
328
|
-
matches.push({
|
|
329
|
-
start: m.index,
|
|
330
|
-
end: argsStart + args.length + 1, // +1 for closing paren
|
|
331
|
-
prefix: m[1] ?? "",
|
|
332
|
-
name: m[2] ?? "",
|
|
333
|
-
args,
|
|
334
|
-
})
|
|
335
|
-
}
|
|
336
|
-
m = SIGNAL_PREFIX_RE.exec(code)
|
|
337
|
-
}
|
|
338
|
-
SIGNAL_PREFIX_RE.lastIndex = 0
|
|
339
|
-
|
|
340
|
-
// Replace in reverse to preserve offsets
|
|
341
|
-
for (let i = matches.length - 1; i >= 0; i--) {
|
|
342
|
-
const { start, end, prefix, name, args } = matches[i] as (typeof matches)[number]
|
|
343
|
-
const replacement = `${prefix}__hmr_signal(${escapedId}, ${JSON.stringify(name)}, signal, ${args})`
|
|
344
|
-
output = output.slice(0, start) + replacement + output.slice(end)
|
|
345
|
-
}
|
|
346
|
-
}
|
|
441
|
+
let output = hasSignals ? rewriteSignals(code, moduleId) : code
|
|
347
442
|
|
|
348
443
|
// Build the HMR footer
|
|
349
444
|
const escapedId = JSON.stringify(moduleId)
|