@pyreon/vite-plugin 0.2.0 → 0.3.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.
@@ -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":"807e1cdb-1"}]}],"isRoot":true},"nodeParts":{"807e1cdb-1":{"renderedLength":7799,"gzipLength":3133,"brotliLength":0,"metaUid":"807e1cdb-0"}},"nodeMetas":{"807e1cdb-0":{"id":"/src/index.ts","moduleParts":{"index.js":"807e1cdb-1"},"imported":[{"uid":"807e1cdb-2"}],"importedBy":[],"isEntry":true},"807e1cdb-2":{"id":"@pyreon/compiler","moduleParts":{},"imported":[],"importedBy":[{"uid":"807e1cdb-0"}]}},"env":{"rollup":"4.23.0"},"options":{"gzip":true,"brotli":false,"sourcemap":false}};
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
@@ -12,6 +14,14 @@ import { transformJSX } from "@pyreon/compiler";
12
14
  * import pyreon from "@pyreon/vite-plugin"
13
15
  * export default { plugins: [pyreon()] }
14
16
  *
17
+ * ## Drop-in compat mode (zero code changes)
18
+ *
19
+ * import pyreon from "@pyreon/vite-plugin"
20
+ * export default { plugins: [pyreon({ compat: "react" })] }
21
+ *
22
+ * Aliases `react`, `react-dom`, `vue`, `solid-js`, or `preact` imports to
23
+ * Pyreon's compat packages — existing code works without changing imports.
24
+ *
15
25
  * ## SSR mode
16
26
  *
17
27
  * import pyreon from "@pyreon/vite-plugin"
@@ -28,19 +38,95 @@ import { transformJSX } from "@pyreon/compiler";
28
38
  */
29
39
  const HMR_RUNTIME_ID = "\0pyreon/hmr-runtime";
30
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
+ };
47
+ const COMPAT_ALIASES = {
48
+ react: {
49
+ react: "@pyreon/react-compat",
50
+ "react/jsx-runtime": "@pyreon/react-compat/jsx-runtime",
51
+ "react/jsx-dev-runtime": "@pyreon/react-compat/jsx-runtime",
52
+ "react-dom": "@pyreon/react-compat/dom",
53
+ "react-dom/client": "@pyreon/react-compat/dom"
54
+ },
55
+ preact: {
56
+ preact: "@pyreon/preact-compat",
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",
60
+ "@preact/signals": "@pyreon/preact-compat/signals"
61
+ },
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
+ }
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
+ }
31
112
  function pyreonPlugin(options) {
32
113
  const ssrConfig = options?.ssr;
114
+ const compat = options?.compat;
33
115
  let isBuild = false;
116
+ let projectRoot = "";
117
+ const resolvedAliases = /* @__PURE__ */ new Map();
34
118
  return {
35
119
  name: "pyreon",
36
120
  enforce: "pre",
37
- config(_, env) {
121
+ config(userConfig, env) {
38
122
  isBuild = env.command === "build";
123
+ projectRoot = userConfig.root ?? process.cwd();
39
124
  return {
40
125
  resolve: { conditions: ["bun"] },
126
+ optimizeDeps: { exclude: compat ? Object.keys(COMPAT_ALIASES[compat]) : [] },
41
127
  oxc: { jsx: {
42
128
  runtime: "automatic",
43
- importSource: "@pyreon/core"
129
+ importSource: compat ? COMPAT_JSX_SOURCE[compat] : "@pyreon/core"
44
130
  } },
45
131
  ...env.isSsrBuild && ssrConfig ? { build: {
46
132
  ssr: true,
@@ -50,6 +136,14 @@ function pyreonPlugin(options) {
50
136
  },
51
137
  resolveId(id) {
52
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;
53
147
  },
54
148
  load(id) {
55
149
  if (id === HMR_RUNTIME_ID) return HMR_RUNTIME_SOURCE;
@@ -57,6 +151,7 @@ function pyreonPlugin(options) {
57
151
  transform(code, id) {
58
152
  const ext = getExt(id);
59
153
  if (ext !== ".tsx" && ext !== ".jsx" && ext !== ".pyreon") return;
154
+ if (compat === "react" || compat === "preact" || compat === "vue" || compat === "solid") return;
60
155
  const result = transformJSX(code, id);
61
156
  for (const w of result.warnings) this.warn(`${w.message} (${id}:${w.line}:${w.column})`);
62
157
  let output = result.code;
@@ -158,38 +253,41 @@ function braceDepthAt(code, pos) {
158
253
  }
159
254
  return depth;
160
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
+ }
161
286
  function injectHmr(code, moduleId) {
162
287
  const hasSignals = SIGNAL_PREFIX_RE.test(code);
163
288
  SIGNAL_PREFIX_RE.lastIndex = 0;
164
289
  if (!EXPORT_COMPONENT_RE.test(code) && !hasSignals) return code;
165
- let output = code;
166
- if (hasSignals) {
167
- const escapedId = JSON.stringify(moduleId);
168
- const matches = [];
169
- let m = SIGNAL_PREFIX_RE.exec(code);
170
- while (m !== null) {
171
- const argsStart = m.index + m[0].length;
172
- const args = extractBalancedArgs(code, argsStart);
173
- if (args === null) {
174
- m = SIGNAL_PREFIX_RE.exec(code);
175
- continue;
176
- }
177
- if (braceDepthAt(code, m.index) === 0) matches.push({
178
- start: m.index,
179
- end: argsStart + args.length + 1,
180
- prefix: m[1] ?? "",
181
- name: m[2] ?? "",
182
- args
183
- });
184
- m = SIGNAL_PREFIX_RE.exec(code);
185
- }
186
- SIGNAL_PREFIX_RE.lastIndex = 0;
187
- for (let i = matches.length - 1; i >= 0; i--) {
188
- const { start, end, prefix, name, args } = matches[i];
189
- const replacement = `${prefix}__hmr_signal(${escapedId}, ${JSON.stringify(name)}, signal, ${args})`;
190
- output = output.slice(0, start) + replacement + output.slice(end);
191
- }
192
- }
290
+ let output = hasSignals ? rewriteSignals(code, moduleId) : code;
193
291
  const escapedId = JSON.stringify(moduleId);
194
292
  const lines = [];
195
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 * ## 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 interface PyreonPluginOptions {\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\nexport default function pyreonPlugin(options?: PyreonPluginOptions): Plugin {\n const ssrConfig = options?.ssr\n let isBuild = false\n\n return {\n name: \"pyreon\",\n enforce: \"pre\",\n\n config(_, env) {\n isBuild = env.command === \"build\"\n\n return {\n resolve: {\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 // esbuild may strip indentation, so we can't rely on column position.\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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"}
@@ -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
@@ -12,6 +14,14 @@ import { transformJSX } from "@pyreon/compiler";
12
14
  * import pyreon from "@pyreon/vite-plugin"
13
15
  * export default { plugins: [pyreon()] }
14
16
  *
17
+ * ## Drop-in compat mode (zero code changes)
18
+ *
19
+ * import pyreon from "@pyreon/vite-plugin"
20
+ * export default { plugins: [pyreon({ compat: "react" })] }
21
+ *
22
+ * Aliases `react`, `react-dom`, `vue`, `solid-js`, or `preact` imports to
23
+ * Pyreon's compat packages — existing code works without changing imports.
24
+ *
15
25
  * ## SSR mode
16
26
  *
17
27
  * import pyreon from "@pyreon/vite-plugin"
@@ -27,22 +37,68 @@ import { transformJSX } from "@pyreon/compiler";
27
37
  * vite build --ssr src/entry-server.ts --outDir dist/server # server bundle
28
38
  */
29
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
+ }
30
79
  function pyreonPlugin(options) {
31
80
  const ssrConfig = options?.ssr;
81
+ const compat = options?.compat;
32
82
  let isBuild = false;
83
+ let projectRoot = "";
84
+ const resolvedAliases = /* @__PURE__ */new Map();
33
85
  return {
34
86
  name: "pyreon",
35
87
  enforce: "pre",
36
- config(_, env) {
88
+ config(userConfig, env) {
37
89
  isBuild = env.command === "build";
90
+ projectRoot = userConfig.root ?? process.cwd();
38
91
  return {
39
92
  resolve: {
40
93
  conditions: ["bun"]
41
94
  },
95
+ optimizeDeps: {
96
+ exclude: compat ? Object.keys(COMPAT_ALIASES[compat]) : []
97
+ },
42
98
  oxc: {
43
99
  jsx: {
44
100
  runtime: "automatic",
45
- importSource: "@pyreon/core"
101
+ importSource: compat ? COMPAT_JSX_SOURCE[compat] : "@pyreon/core"
46
102
  }
47
103
  },
48
104
  ...(env.isSsrBuild && ssrConfig ? {
@@ -57,6 +113,14 @@ function pyreonPlugin(options) {
57
113
  },
58
114
  resolveId(id) {
59
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;
60
124
  },
61
125
  load(id) {
62
126
  if (id === HMR_RUNTIME_ID) return HMR_RUNTIME_SOURCE;
@@ -64,6 +128,7 @@ function pyreonPlugin(options) {
64
128
  transform(code, id) {
65
129
  const ext = getExt(id);
66
130
  if (ext !== ".tsx" && ext !== ".jsx" && ext !== ".pyreon") return;
131
+ if (compat === "react" || compat === "preact" || compat === "vue" || compat === "solid") return;
67
132
  const result = transformJSX(code, id);
68
133
  for (const w of result.warnings) this.warn(`${w.message} (${id}:${w.line}:${w.column})`);
69
134
  let output = result.code;
@@ -157,44 +222,47 @@ function braceDepthAt(code, pos) {
157
222
  }
158
223
  return depth;
159
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
+ }
160
261
  function injectHmr(code, moduleId) {
161
262
  const hasSignals = SIGNAL_PREFIX_RE.test(code);
162
263
  SIGNAL_PREFIX_RE.lastIndex = 0;
163
264
  if (!EXPORT_COMPONENT_RE.test(code) && !hasSignals) return code;
164
- let output = code;
165
- if (hasSignals) {
166
- const escapedId = JSON.stringify(moduleId);
167
- const matches = [];
168
- let m = SIGNAL_PREFIX_RE.exec(code);
169
- while (m !== null) {
170
- const argsStart = m.index + m[0].length;
171
- const args = extractBalancedArgs(code, argsStart);
172
- if (args === null) {
173
- m = SIGNAL_PREFIX_RE.exec(code);
174
- continue;
175
- }
176
- if (braceDepthAt(code, m.index) === 0) matches.push({
177
- start: m.index,
178
- end: argsStart + args.length + 1,
179
- prefix: m[1] ?? "",
180
- name: m[2] ?? "",
181
- args
182
- });
183
- m = SIGNAL_PREFIX_RE.exec(code);
184
- }
185
- SIGNAL_PREFIX_RE.lastIndex = 0;
186
- for (let i = matches.length - 1; i >= 0; i--) {
187
- const {
188
- start,
189
- end,
190
- prefix,
191
- name,
192
- args
193
- } = matches[i];
194
- const replacement = `${prefix}__hmr_signal(${escapedId}, ${JSON.stringify(name)}, signal, ${args})`;
195
- output = output.slice(0, start) + replacement + output.slice(end);
196
- }
197
- }
265
+ let output = hasSignals ? rewriteSignals(code, moduleId) : code;
198
266
  const escapedId = JSON.stringify(moduleId);
199
267
  const lines = [];
200
268
  if (hasSignals) lines.push(`import { __hmr_signal, __hmr_dispose } from "${HMR_RUNTIME_IMPORT}";`);
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/index.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":["pathResolve"],"sources":["../../src/index.ts"],"mappings":"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"}
@@ -1,7 +1,21 @@
1
1
  import { Plugin } from "vite";
2
2
 
3
3
  //#region src/index.d.ts
4
+ type CompatFramework = "react" | "preact" | "vue" | "solid";
4
5
  interface PyreonPluginOptions {
6
+ /**
7
+ * Alias imports from an existing framework to Pyreon's compat layer.
8
+ *
9
+ * This lets you drop Pyreon into an existing project with zero code changes —
10
+ * `import { useState } from "react"` will resolve to `@pyreon/react-compat`.
11
+ *
12
+ * @example
13
+ * pyreon({ compat: "react" }) // react + react-dom → @pyreon/react-compat
14
+ * pyreon({ compat: "vue" }) // vue → @pyreon/vue-compat
15
+ * pyreon({ compat: "solid" }) // solid-js → @pyreon/solid-compat
16
+ * pyreon({ compat: "preact" }) // preact + hooks + signals → @pyreon/preact-compat
17
+ */
18
+ compat?: CompatFramework;
5
19
  /**
6
20
  * Enable SSR dev middleware.
7
21
  *
@@ -18,5 +32,5 @@ interface PyreonPluginOptions {
18
32
  }
19
33
  declare function pyreonPlugin(options?: PyreonPluginOptions): Plugin;
20
34
  //#endregion
21
- export { PyreonPluginOptions, pyreonPlugin as default };
35
+ export { CompatFramework, PyreonPluginOptions, pyreonPlugin as default };
22
36
  //# sourceMappingURL=index2.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index2.d.ts","names":[],"sources":["../../src/index.ts"],"mappings":";;;UAiCiB,mBAAA;;;;;;;;;;;EAWf,GAAA;gEAEE,KAAA;EAAA;AAAA;AAAA,iBAIoB,YAAA,CAAa,OAAA,GAAU,mBAAA,GAAsB,MAAA"}
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.2.0",
3
+ "version": "0.3.0",
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": "workspace:*"
42
+ "@pyreon/compiler": "^0.3.0"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "vite": ">=8.0.0"
package/src/index.ts CHANGED
@@ -9,6 +9,14 @@
9
9
  * import pyreon from "@pyreon/vite-plugin"
10
10
  * export default { plugins: [pyreon()] }
11
11
  *
12
+ * ## Drop-in compat mode (zero code changes)
13
+ *
14
+ * import pyreon from "@pyreon/vite-plugin"
15
+ * export default { plugins: [pyreon({ compat: "react" })] }
16
+ *
17
+ * Aliases `react`, `react-dom`, `vue`, `solid-js`, or `preact` imports to
18
+ * Pyreon's compat packages — existing code works without changing imports.
19
+ *
12
20
  * ## SSR mode
13
21
  *
14
22
  * import pyreon from "@pyreon/vite-plugin"
@@ -24,6 +32,8 @@
24
32
  * vite build --ssr src/entry-server.ts --outDir dist/server # server bundle
25
33
  */
26
34
 
35
+ import { readFileSync } from "node:fs"
36
+ import { resolve as pathResolve } from "node:path"
27
37
  import { transformJSX } from "@pyreon/compiler"
28
38
  import type { Plugin, ViteDevServer } from "vite"
29
39
 
@@ -31,7 +41,23 @@ import type { Plugin, ViteDevServer } from "vite"
31
41
  const HMR_RUNTIME_ID = "\0pyreon/hmr-runtime"
32
42
  const HMR_RUNTIME_IMPORT = "virtual:pyreon/hmr-runtime"
33
43
 
44
+ export type CompatFramework = "react" | "preact" | "vue" | "solid"
45
+
34
46
  export interface PyreonPluginOptions {
47
+ /**
48
+ * Alias imports from an existing framework to Pyreon's compat layer.
49
+ *
50
+ * This lets you drop Pyreon into an existing project with zero code changes —
51
+ * `import { useState } from "react"` will resolve to `@pyreon/react-compat`.
52
+ *
53
+ * @example
54
+ * pyreon({ compat: "react" }) // react + react-dom → @pyreon/react-compat
55
+ * pyreon({ compat: "vue" }) // vue → @pyreon/vue-compat
56
+ * pyreon({ compat: "solid" }) // solid-js → @pyreon/solid-compat
57
+ * pyreon({ compat: "preact" }) // preact + hooks + signals → @pyreon/preact-compat
58
+ */
59
+ compat?: CompatFramework
60
+
35
61
  /**
36
62
  * Enable SSR dev middleware.
37
63
  *
@@ -48,25 +74,132 @@ export interface PyreonPluginOptions {
48
74
  }
49
75
  }
50
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
+
86
+ // ── Compat alias maps ─────────────────────────────────────────────────────────
87
+
88
+ const COMPAT_ALIASES: Record<CompatFramework, Record<string, string>> = {
89
+ react: {
90
+ react: "@pyreon/react-compat",
91
+ "react/jsx-runtime": "@pyreon/react-compat/jsx-runtime",
92
+ "react/jsx-dev-runtime": "@pyreon/react-compat/jsx-runtime",
93
+ "react-dom": "@pyreon/react-compat/dom",
94
+ "react-dom/client": "@pyreon/react-compat/dom",
95
+ },
96
+ preact: {
97
+ preact: "@pyreon/preact-compat",
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",
101
+ "@preact/signals": "@pyreon/preact-compat/signals",
102
+ },
103
+ vue: {
104
+ vue: "@pyreon/vue-compat",
105
+ "vue/jsx-runtime": "@pyreon/vue-compat/jsx-runtime",
106
+ "vue/jsx-dev-runtime": "@pyreon/vue-compat/jsx-runtime",
107
+ },
108
+ solid: {
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",
112
+ },
113
+ }
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
+
51
171
  export default function pyreonPlugin(options?: PyreonPluginOptions): Plugin {
52
172
  const ssrConfig = options?.ssr
173
+ const compat = options?.compat
53
174
  let isBuild = false
175
+ let projectRoot = ""
176
+ // Cache resolved absolute paths for compat aliases
177
+ const resolvedAliases = new Map<string, string>()
54
178
 
55
179
  return {
56
180
  name: "pyreon",
57
181
  enforce: "pre",
58
182
 
59
- config(_, env) {
183
+ config(userConfig, env) {
60
184
  isBuild = env.command === "build"
185
+ // Capture the project root for package resolution in resolveId
186
+ projectRoot = userConfig.root ?? process.cwd()
187
+
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]) : []
61
191
 
62
192
  return {
63
193
  resolve: {
64
194
  conditions: ["bun"],
65
195
  },
196
+ optimizeDeps: {
197
+ exclude: optimizeDepsExclude,
198
+ },
66
199
  oxc: {
67
200
  jsx: {
68
201
  runtime: "automatic",
69
- importSource: "@pyreon/core",
202
+ importSource: compat ? COMPAT_JSX_SOURCE[compat] : "@pyreon/core",
70
203
  },
71
204
  },
72
205
  // In SSR build mode, configure the entry
@@ -83,9 +216,19 @@ export default function pyreonPlugin(options?: PyreonPluginOptions): Plugin {
83
216
  }
84
217
  },
85
218
 
86
- // ── Virtual module: HMR runtime ─────────────────────────────────────────
219
+ // ── Virtual module + compat alias resolution ─────────────────────────────
87
220
  resolveId(id) {
88
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
89
232
  },
90
233
 
91
234
  load(id) {
@@ -97,6 +240,13 @@ export default function pyreonPlugin(options?: PyreonPluginOptions): Plugin {
97
240
  transform(code, id) {
98
241
  const ext = getExt(id)
99
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
+
100
250
  const result = transformJSX(code, id)
101
251
  // Surface compiler warnings in the terminal
102
252
  for (const w of result.warnings) {
@@ -237,6 +387,48 @@ function braceDepthAt(code: string, pos: number): number {
237
387
  return depth
238
388
  }
239
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
+
240
432
  function injectHmr(code: string, moduleId: string): string {
241
433
  const hasSignals = SIGNAL_PREFIX_RE.test(code)
242
434
  SIGNAL_PREFIX_RE.lastIndex = 0
@@ -246,49 +438,7 @@ function injectHmr(code: string, moduleId: string): string {
246
438
  // Only inject HMR if the module exports components or has module-scope signals
247
439
  if (!hasComponentExport && !hasSignals) return code
248
440
 
249
- let output = code
250
-
251
- // Rewrite top-level signal() calls to use __hmr_signal for state preservation
252
- if (hasSignals) {
253
- const escapedId = JSON.stringify(moduleId)
254
- // Process matches in reverse order so indices stay valid after replacement
255
- const matches: {
256
- start: number
257
- end: number
258
- prefix: string
259
- name: string
260
- args: string
261
- }[] = []
262
- let m: RegExpExecArray | null = SIGNAL_PREFIX_RE.exec(code)
263
- while (m !== null) {
264
- const argsStart = m.index + m[0].length
265
- const args = extractBalancedArgs(code, argsStart)
266
- if (args === null) {
267
- m = SIGNAL_PREFIX_RE.exec(code)
268
- continue // unbalanced — skip
269
- }
270
- // Only rewrite module-scope signals (brace depth 0).
271
- // esbuild may strip indentation, so we can't rely on column position.
272
- if (braceDepthAt(code, m.index) === 0) {
273
- matches.push({
274
- start: m.index,
275
- end: argsStart + args.length + 1, // +1 for closing paren
276
- prefix: m[1] ?? "",
277
- name: m[2] ?? "",
278
- args,
279
- })
280
- }
281
- m = SIGNAL_PREFIX_RE.exec(code)
282
- }
283
- SIGNAL_PREFIX_RE.lastIndex = 0
284
-
285
- // Replace in reverse to preserve offsets
286
- for (let i = matches.length - 1; i >= 0; i--) {
287
- const { start, end, prefix, name, args } = matches[i] as (typeof matches)[number]
288
- const replacement = `${prefix}__hmr_signal(${escapedId}, ${JSON.stringify(name)}, signal, ${args})`
289
- output = output.slice(0, start) + replacement + output.slice(end)
290
- }
291
- }
441
+ let output = hasSignals ? rewriteSignals(code, moduleId) : code
292
442
 
293
443
  // Build the HMR footer
294
444
  const escapedId = JSON.stringify(moduleId)