@pyreon/vite-plugin 0.13.1 → 0.14.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":"e8011ffc-1"}]}],"isRoot":true},"nodeParts":{"e8011ffc-1":{"renderedLength":13659,"gzipLength":4823,"brotliLength":0,"metaUid":"e8011ffc-0"}},"nodeMetas":{"e8011ffc-0":{"id":"/src/index.ts","moduleParts":{"index.js":"e8011ffc-1"},"imported":[{"uid":"e8011ffc-2"},{"uid":"e8011ffc-3"},{"uid":"e8011ffc-4"}],"importedBy":[],"isEntry":true},"e8011ffc-2":{"id":"node:fs","moduleParts":{},"imported":[],"importedBy":[{"uid":"e8011ffc-0"}]},"e8011ffc-3":{"id":"node:path","moduleParts":{},"imported":[],"importedBy":[{"uid":"e8011ffc-0"}]},"e8011ffc-4":{"id":"@pyreon/compiler","moduleParts":{},"imported":[],"importedBy":[{"uid":"e8011ffc-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":"92b4c3bc-1"}]}],"isRoot":true},"nodeParts":{"92b4c3bc-1":{"renderedLength":19400,"gzipLength":6492,"brotliLength":0,"metaUid":"92b4c3bc-0"}},"nodeMetas":{"92b4c3bc-0":{"id":"/src/index.ts","moduleParts":{"index.js":"92b4c3bc-1"},"imported":[{"uid":"92b4c3bc-2"},{"uid":"92b4c3bc-3"},{"uid":"92b4c3bc-4"}],"importedBy":[],"isEntry":true},"92b4c3bc-2":{"id":"node:fs","moduleParts":{},"imported":[],"importedBy":[{"uid":"92b4c3bc-0"}]},"92b4c3bc-3":{"id":"node:path","moduleParts":{},"imported":[],"importedBy":[{"uid":"92b4c3bc-0"}]},"92b4c3bc-4":{"id":"@pyreon/compiler","moduleParts":{},"imported":[],"importedBy":[{"uid":"92b4c3bc-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,4 +1,4 @@
1
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { generateContext, transformJSX } from "@pyreon/compiler";
4
4
 
@@ -90,6 +90,8 @@ function pyreonPlugin(options) {
90
90
  const compat = options?.compat;
91
91
  let isBuild = false;
92
92
  let projectRoot = "";
93
+ const signalExportRegistry = /* @__PURE__ */ new Map();
94
+ const resolveCache = /* @__PURE__ */ new Map();
93
95
  return {
94
96
  name: "pyreon",
95
97
  enforce: "pre",
@@ -111,6 +113,9 @@ function pyreonPlugin(options) {
111
113
  } } : {}
112
114
  };
113
115
  },
116
+ async buildStart() {
117
+ await prescanSignalExports(projectRoot, signalExportRegistry);
118
+ },
114
119
  async resolveId(id, importer) {
115
120
  if (id === HMR_RUNTIME_IMPORT) return HMR_RUNTIME_ID;
116
121
  const target = getCompatTarget(compat, id);
@@ -120,7 +125,7 @@ function pyreonPlugin(options) {
120
125
  load(id) {
121
126
  if (id === HMR_RUNTIME_ID) return HMR_RUNTIME_SOURCE;
122
127
  },
123
- transform(code, id, transformOptions) {
128
+ async transform(code, id, transformOptions) {
124
129
  const ext = getExt(id);
125
130
  if (ext !== ".tsx" && ext !== ".jsx" && ext !== ".pyreon") return;
126
131
  if (compat === "react" || compat === "preact" || compat === "vue" || compat === "solid") {
@@ -133,7 +138,12 @@ function pyreonPlugin(options) {
133
138
  }
134
139
  return;
135
140
  }
136
- const result = transformJSX(code, id, { ssr: transformOptions?.ssr === true });
141
+ scanSignalExports(code, normalizeModuleId(id), signalExportRegistry);
142
+ const knownSignals = await resolveImportedSignals(code, id, signalExportRegistry, this, resolveCache);
143
+ const result = transformJSX(code, id, {
144
+ ssr: transformOptions?.ssr === true,
145
+ knownSignals
146
+ });
137
147
  for (const w of result.warnings) this.warn(`${w.message} (${id}:${w.line}:${w.column})`);
138
148
  let output = result.code;
139
149
  if (!isBuild) {
@@ -366,6 +376,137 @@ function getExt(id) {
366
376
  function isAssetRequest(url) {
367
377
  return url.startsWith("/@") || url.startsWith("/__") || url.includes("/node_modules/") || /\.(css|js|ts|tsx|jsx|json|ico|png|jpg|jpeg|gif|svg|woff2?|ttf|eot|map)(\?|$)/.test(url);
368
378
  }
379
+ /**
380
+ * Normalize a Vite module ID by stripping query strings (?v=..., ?t=...)
381
+ * and resolving to an absolute path for consistent registry lookups.
382
+ */
383
+ function normalizeModuleId(id) {
384
+ const queryIndex = id.indexOf("?");
385
+ return queryIndex >= 0 ? id.slice(0, queryIndex) : id;
386
+ }
387
+ /**
388
+ * Pre-scan all source files in the project for signal exports.
389
+ *
390
+ * Called from `buildStart` so the registry is fully populated before any
391
+ * transforms run. This solves the build ordering problem where component.tsx
392
+ * is transformed before store.ts — without pre-scanning, the registry would
393
+ * be empty and imported signals would not be auto-called.
394
+ */
395
+ async function prescanSignalExports(root, registry) {
396
+ const files = [];
397
+ function walk(dir) {
398
+ try {
399
+ for (const entry of readdirSync(dir)) {
400
+ if (entry.startsWith(".") || entry === "node_modules" || entry === "dist" || entry === "lib" || entry === "build") continue;
401
+ const full = join(dir, entry);
402
+ try {
403
+ if (statSync(full).isDirectory()) walk(full);
404
+ else if (/\.(ts|tsx|js|jsx)$/.test(entry)) files.push(full);
405
+ } catch {}
406
+ }
407
+ } catch {}
408
+ }
409
+ walk(root);
410
+ for (const file of files) try {
411
+ scanSignalExports(readFileSync(file, "utf-8"), file, registry);
412
+ } catch {}
413
+ }
414
+ /**
415
+ * Scan a module's source for exported signal declarations and register them.
416
+ *
417
+ * Detects patterns:
418
+ * 1. `export const x = signal(...)` or `export const x = computed(...)` — inline export
419
+ * 2. `const x = signal(...); export { x }` — separate declaration + named export
420
+ * 3. `export default signal(...)` — default export (tracked as 'default')
421
+ *
422
+ * Re-exports (`export { x } from './signals'`) are NOT detected — the source
423
+ * module must be scanned directly. This is a known limitation.
424
+ *
425
+ * Uses simple regex — no AST parse needed.
426
+ */
427
+ function scanSignalExports(code, moduleId, registry) {
428
+ const normalizedId = normalizeModuleId(moduleId);
429
+ let match;
430
+ const signals = /* @__PURE__ */ new Set();
431
+ const EXPORT_CONST_RE = /export\s+const\s+(\w+)\s*=\s*(?:signal|computed)\s*[<(]/g;
432
+ while ((match = EXPORT_CONST_RE.exec(code)) !== null) signals.add(match[1]);
433
+ const localSignals = /* @__PURE__ */ new Set();
434
+ const LOCAL_SIGNAL_RE = /(?:^|[\s;])const\s+(\w+)\s*=\s*(?:signal|computed)\s*[<(]/gm;
435
+ while ((match = LOCAL_SIGNAL_RE.exec(code)) !== null) localSignals.add(match[1]);
436
+ if (localSignals.size > 0) {
437
+ const NAMED_EXPORT_RE = /export\s*\{([^}]+)\}/g;
438
+ while ((match = NAMED_EXPORT_RE.exec(code)) !== null) {
439
+ if (code.slice(match.index + match[0].length).trimStart().startsWith("from")) continue;
440
+ for (const spec of match[1].split(",")) {
441
+ const trimmed = spec.trim();
442
+ if (!trimmed) continue;
443
+ const parts = trimmed.split(/\s+as\s+/);
444
+ const localName = parts[0].trim();
445
+ const exportedName = (parts[1] ?? parts[0]).trim();
446
+ if (localSignals.has(localName)) signals.add(exportedName);
447
+ }
448
+ }
449
+ }
450
+ if (/export\s+default\s+(?:signal|computed)\s*[<(]/.test(code)) signals.add("default");
451
+ if (signals.size > 0) registry.set(normalizedId, signals);
452
+ else registry.delete(normalizedId);
453
+ }
454
+ /**
455
+ * Resolve imported signal names from the signal export registry.
456
+ *
457
+ * For each import in the source, resolves the module and checks if it has
458
+ * signal exports in the registry. Returns the local names of imported signals.
459
+ *
460
+ * Handles named imports (`import { x } from ...`) and default imports
461
+ * (`import x from ...` — matched against 'default' in the registry).
462
+ */
463
+ async function resolveImportedSignals(code, _moduleId, registry, pluginCtx, resolveCache) {
464
+ if (registry.size === 0) return [];
465
+ const knownSignals = [];
466
+ let match;
467
+ /** Resolve a source specifier to a normalized module ID, using the cache. */
468
+ async function resolveSource(source) {
469
+ const cacheKey = `${_moduleId}::${source}`;
470
+ if (resolveCache.has(cacheKey)) return resolveCache.get(cacheKey) ?? null;
471
+ let resolvedId = null;
472
+ try {
473
+ const resolved = await pluginCtx.resolve(source, _moduleId, { skipSelf: true });
474
+ resolvedId = resolved?.id ? normalizeModuleId(resolved.id) : null;
475
+ } catch {}
476
+ resolveCache.set(cacheKey, resolvedId);
477
+ return resolvedId;
478
+ }
479
+ const IMPORT_RE = /import\s+(?!type\s)\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]/g;
480
+ while ((match = IMPORT_RE.exec(code)) !== null) {
481
+ const specifiers = match[1];
482
+ const source = match[2];
483
+ const resolvedId = await resolveSource(source);
484
+ if (!resolvedId) continue;
485
+ const exportedSignals = registry.get(resolvedId);
486
+ if (!exportedSignals) continue;
487
+ for (const spec of specifiers.split(",")) {
488
+ const trimmed = spec.trim();
489
+ if (!trimmed) continue;
490
+ const parts = trimmed.split(/\s+as\s+/);
491
+ const importedName = parts[0].trim();
492
+ const localName = (parts[1] ?? parts[0]).trim();
493
+ if (exportedSignals.has(importedName)) knownSignals.push(localName);
494
+ }
495
+ }
496
+ const DEFAULT_IMPORT_RE = /import\s+(?!type\s)(\w+)\s+from\s*['"]([^'"]+)['"]/g;
497
+ while ((match = DEFAULT_IMPORT_RE.exec(code)) !== null) {
498
+ const fullMatch = match[0];
499
+ if (/import\s+type\s+/.test(fullMatch)) continue;
500
+ const localName = match[1];
501
+ const source = match[2];
502
+ const resolvedId = await resolveSource(source);
503
+ if (!resolvedId) continue;
504
+ const exportedSignals = registry.get(resolvedId);
505
+ if (!exportedSignals) continue;
506
+ if (exportedSignals.has("default")) knownSignals.push(localName);
507
+ }
508
+ return knownSignals;
509
+ }
369
510
  const HMR_RUNTIME_SOURCE = `
370
511
  const REGISTRY_KEY = "__pyreon_hmr_registry__";
371
512
 
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["pathJoin"],"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 { existsSync, mkdirSync, writeFileSync } from 'node:fs'\nimport { join as pathJoin } from 'node:path'\nimport { generateContext, 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 * 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\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 const jsxSource = compat ? COMPAT_JSX_SOURCE[compat] : '@pyreon/core'\n\n return {\n // Use \"bun\" condition for workspace resolution — source .ts/.tsx files\n // for HMR, fast refresh, and type-safe imports.\n resolve: { conditions: ['bun'] },\n optimizeDeps: {\n exclude: optimizeDepsExclude,\n },\n // Vite 8 uses oxc for JSX transform (not esbuildOptions or rolldownOptions)\n oxc: {\n jsx: {\n runtime: 'automatic',\n importSource: jsxSource,\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 async resolveId(id, importer) {\n if (id === HMR_RUNTIME_IMPORT) return HMR_RUNTIME_ID\n const target = getCompatTarget(compat, id)\n if (!target) return\n\n // Vite 8 resolves the \"bun\" condition natively via resolve.conditions.\n // Delegate to Vite's resolver instead of manual package.json parsing.\n const resolved = await this.resolve(target, importer, { skipSelf: true })\n return resolved?.id\n },\n\n load(id) {\n if (id === HMR_RUNTIME_ID) {\n return HMR_RUNTIME_SOURCE\n }\n },\n\n transform(code, id, transformOptions) {\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 but apply\n // attribute renames (className → class, htmlFor → for) so source code\n // that uses React-style attribute names works correctly.\n if (compat === 'react' || compat === 'preact' || compat === 'vue' || compat === 'solid') {\n if (compat === 'react' || compat === 'preact') {\n const transformed = transformCompatAttributes(code)\n if (transformed !== code) return { code: transformed, map: null }\n }\n return\n }\n\n // Vite passes `ssr: true` when transforming for the SSR module graph\n // (both build --ssr and dev `ssrLoadModule`). The compiler emits plain\n // `h()` calls in that mode so `runtime-server` can render to a string.\n const isSsr = transformOptions?.ssr === true\n const result = transformJSX(code, id, { ssr: isSsr })\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 // ── Dev-only transforms ────────────────────────────────────────────\n if (!isBuild) {\n output = injectHmr(output, id)\n // Inject debug names for signal() calls not rewritten by HMR\n output = injectSignalNames(output)\n }\n\n return { code: output, map: null }\n },\n\n // ── SSR dev middleware ───────────────────────────────────────────────────\n configureServer(server: ViteDevServer) {\n // Generate .pyreon/context.json for AI tools on dev server start\n generateProjectContext(projectRoot)\n\n // Debounced regeneration on file changes\n let contextTimer: ReturnType<typeof setTimeout> | null = null\n server.watcher.on('change', (file) => {\n if (/\\.(tsx|jsx|ts|js)$/.test(file) && !file.includes('node_modules')) {\n if (contextTimer) clearTimeout(contextTimer)\n contextTimer = setTimeout(() => generateProjectContext(projectRoot), 500)\n }\n })\n\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// ── AI context generation ─────────────────────────────────────────────────────\n\n/**\n * Generate .pyreon/context.json — project map for AI coding assistants.\n * Delegates to @pyreon/compiler's unified project scanner.\n */\nfunction generateProjectContext(root: string): void {\n try {\n const context = generateContext(root)\n const outDir = pathJoin(root, '.pyreon')\n if (!existsSync(outDir)) mkdirSync(outDir, { recursive: true })\n writeFileSync(pathJoin(outDir, 'context.json'), JSON.stringify(context, null, 2), 'utf-8')\n } catch {\n // Silently fail — context generation is best-effort\n }\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\n/** Check if an argument string contains a top-level comma (i.e. has multiple arguments). */\nfunction hasMultipleArgs(args: string): boolean {\n let depth = 0\n for (const ch of args) {\n if (ch === '(' || ch === '[' || ch === '{') depth++\n else if (ch === ')' || ch === ']' || ch === '}') depth--\n else if (ch === ',' && depth === 0) return true\n }\n return false\n}\n\n/**\n * Inject `{ name: \"varName\" }` into signal() calls that don't already have\n * an options argument. Only runs in dev mode for debugging/devtools.\n *\n * `const count = signal(0)` → `const count = signal(0, { name: \"count\" })`\n *\n * Module-scope signals rewritten to __hmr_signal() are naturally skipped\n * because the regex matches `signal(` not `__hmr_signal(`.\n */\nfunction injectSignalNames(code: string): string {\n const re = /(?:const|let)\\s+(\\w+)\\s*=\\s*signal\\(/gm\n const matches: { start: number; end: number; name: string; args: string }[] = []\n\n let m: RegExpExecArray | null = 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 && !hasMultipleArgs(args)) {\n matches.push({ start: argsStart, end: argsStart + args.length, name: m[1] ?? '', args })\n }\n m = re.exec(code)\n }\n re.lastIndex = 0\n\n let output = code\n for (let i = matches.length - 1; i >= 0; i--) {\n const { start, end, name, args } = matches[i] as (typeof matches)[number]\n output = `${output.slice(0, start)}${args}, { name: ${JSON.stringify(name)} }${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// ── Compat attribute transforms ──────────────────────────────────────────────\n\n/**\n * Transform React-style JSX attribute names to standard HTML attribute names.\n * This is a lightweight string transform that runs on JSX source before OXC's\n * JSX transform converts it to jsx() calls.\n *\n * - `className` → `class`\n * - `htmlFor` → `for`\n *\n * Only matches attribute position in JSX (after `<tag ` or whitespace).\n * Does not transform property access (e.g. `props.className` stays as-is since\n * the compat JSX runtime handles that at call time).\n */\nfunction transformCompatAttributes(code: string): string {\n // Match className/htmlFor in JSX attribute position:\n // After < and tag name, or after whitespace between attributes\n // Pattern: word boundary + attribute name + = (with optional whitespace)\n return code\n .replace(/(\\s)className(\\s*=)/g, '$1class$2')\n .replace(/(\\s)htmlFor(\\s*=)/g, '$1for$2')\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, { name: name });\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":["pathJoin"],"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 { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'\nimport { join as pathJoin } from 'node:path'\nimport { generateContext, 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 * 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\n // ── Cross-module signal export registry ─────────────────────────────────\n // Tracks which modules export signal() declarations so imported signals\n // can be auto-called in JSX across file boundaries.\n // Key: normalized module ID, Value: set of exported signal names\n const signalExportRegistry = new Map<string, Set<string>>()\n // Cache resolved import specifiers to avoid redundant resolution calls\n const resolveCache = new Map<string, string | null>()\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 const jsxSource = compat ? COMPAT_JSX_SOURCE[compat] : '@pyreon/core'\n\n return {\n // Use \"bun\" condition for workspace resolution — source .ts/.tsx files\n // for HMR, fast refresh, and type-safe imports.\n resolve: { conditions: ['bun'] },\n optimizeDeps: {\n exclude: optimizeDepsExclude,\n },\n // Vite 8 uses oxc for JSX transform (not esbuildOptions or rolldownOptions)\n oxc: {\n jsx: {\n runtime: 'automatic',\n importSource: jsxSource,\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 // ── Pre-scan all source files for signal exports ──────────────────────\n async buildStart() {\n // Pre-scan all source files for signal exports so the registry\n // is complete before any transforms run. This solves the build\n // ordering problem where component.tsx is transformed before\n // store.ts — without pre-scanning, the registry would be empty.\n await prescanSignalExports(projectRoot, signalExportRegistry)\n },\n\n // ── Virtual module + compat alias resolution ─────────────────────────────\n async resolveId(id, importer) {\n if (id === HMR_RUNTIME_IMPORT) return HMR_RUNTIME_ID\n const target = getCompatTarget(compat, id)\n if (!target) return\n\n // Vite 8 resolves the \"bun\" condition natively via resolve.conditions.\n // Delegate to Vite's resolver instead of manual package.json parsing.\n const resolved = await this.resolve(target, importer, { skipSelf: true })\n return resolved?.id\n },\n\n load(id) {\n if (id === HMR_RUNTIME_ID) {\n return HMR_RUNTIME_SOURCE\n }\n },\n\n async transform(code, id, transformOptions) {\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 but apply\n // attribute renames (className → class, htmlFor → for) so source code\n // that uses React-style attribute names works correctly.\n if (compat === 'react' || compat === 'preact' || compat === 'vue' || compat === 'solid') {\n if (compat === 'react' || compat === 'preact') {\n const transformed = transformCompatAttributes(code)\n if (transformed !== code) return { code: transformed, map: null }\n }\n return\n }\n\n // ── Scan for exported signal declarations (populate registry) ──────\n // This runs on every .tsx/.jsx file so the registry is built\n // incrementally. buildStart pre-scans all files, but this handles\n // files created/modified after buildStart (dev mode HMR).\n scanSignalExports(code, normalizeModuleId(id), signalExportRegistry)\n\n // ── Resolve imported signals from the registry ─────────────────────\n // Check each import in this file: if the imported module has signal\n // exports in the registry, pass them as knownSignals to the compiler.\n const knownSignals = await resolveImportedSignals(code, id, signalExportRegistry, this, resolveCache)\n\n // Vite passes `ssr: true` when transforming for the SSR module graph\n // (both build --ssr and dev `ssrLoadModule`). The compiler emits plain\n // `h()` calls in that mode so `runtime-server` can render to a string.\n const isSsr = transformOptions?.ssr === true\n const result = transformJSX(code, id, { ssr: isSsr, knownSignals })\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 // ── Dev-only transforms ────────────────────────────────────────────\n if (!isBuild) {\n output = injectHmr(output, id)\n // Inject debug names for signal() calls not rewritten by HMR\n output = injectSignalNames(output)\n }\n\n return { code: output, map: null }\n },\n\n // ── SSR dev middleware ───────────────────────────────────────────────────\n configureServer(server: ViteDevServer) {\n // Generate .pyreon/context.json for AI tools on dev server start\n generateProjectContext(projectRoot)\n\n // Debounced regeneration on file changes\n let contextTimer: ReturnType<typeof setTimeout> | null = null\n server.watcher.on('change', (file) => {\n if (/\\.(tsx|jsx|ts|js)$/.test(file) && !file.includes('node_modules')) {\n if (contextTimer) clearTimeout(contextTimer)\n contextTimer = setTimeout(() => generateProjectContext(projectRoot), 500)\n }\n })\n\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// ── AI context generation ─────────────────────────────────────────────────────\n\n/**\n * Generate .pyreon/context.json — project map for AI coding assistants.\n * Delegates to @pyreon/compiler's unified project scanner.\n */\nfunction generateProjectContext(root: string): void {\n try {\n const context = generateContext(root)\n const outDir = pathJoin(root, '.pyreon')\n if (!existsSync(outDir)) mkdirSync(outDir, { recursive: true })\n writeFileSync(pathJoin(outDir, 'context.json'), JSON.stringify(context, null, 2), 'utf-8')\n } catch {\n // Silently fail — context generation is best-effort\n }\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\n/** Check if an argument string contains a top-level comma (i.e. has multiple arguments). */\nfunction hasMultipleArgs(args: string): boolean {\n let depth = 0\n for (const ch of args) {\n if (ch === '(' || ch === '[' || ch === '{') depth++\n else if (ch === ')' || ch === ']' || ch === '}') depth--\n else if (ch === ',' && depth === 0) return true\n }\n return false\n}\n\n/**\n * Inject `{ name: \"varName\" }` into signal() calls that don't already have\n * an options argument. Only runs in dev mode for debugging/devtools.\n *\n * `const count = signal(0)` → `const count = signal(0, { name: \"count\" })`\n *\n * Module-scope signals rewritten to __hmr_signal() are naturally skipped\n * because the regex matches `signal(` not `__hmr_signal(`.\n */\nfunction injectSignalNames(code: string): string {\n const re = /(?:const|let)\\s+(\\w+)\\s*=\\s*signal\\(/gm\n const matches: { start: number; end: number; name: string; args: string }[] = []\n\n let m: RegExpExecArray | null = 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 && !hasMultipleArgs(args)) {\n matches.push({ start: argsStart, end: argsStart + args.length, name: m[1] ?? '', args })\n }\n m = re.exec(code)\n }\n re.lastIndex = 0\n\n let output = code\n for (let i = matches.length - 1; i >= 0; i--) {\n const { start, end, name, args } = matches[i] as (typeof matches)[number]\n output = `${output.slice(0, start)}${args}, { name: ${JSON.stringify(name)} }${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// ── Compat attribute transforms ──────────────────────────────────────────────\n\n/**\n * Transform React-style JSX attribute names to standard HTML attribute names.\n * This is a lightweight string transform that runs on JSX source before OXC's\n * JSX transform converts it to jsx() calls.\n *\n * - `className` → `class`\n * - `htmlFor` → `for`\n *\n * Only matches attribute position in JSX (after `<tag ` or whitespace).\n * Does not transform property access (e.g. `props.className` stays as-is since\n * the compat JSX runtime handles that at call time).\n */\nfunction transformCompatAttributes(code: string): string {\n // Match className/htmlFor in JSX attribute position:\n // After < and tag name, or after whitespace between attributes\n // Pattern: word boundary + attribute name + = (with optional whitespace)\n return code\n .replace(/(\\s)className(\\s*=)/g, '$1class$2')\n .replace(/(\\s)htmlFor(\\s*=)/g, '$1for$2')\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\n// ─── Cross-module signal auto-call helpers ──────────────────────────────────\n\n/**\n * Normalize a Vite module ID by stripping query strings (?v=..., ?t=...)\n * and resolving to an absolute path for consistent registry lookups.\n */\nfunction normalizeModuleId(id: string): string {\n const queryIndex = id.indexOf('?')\n return queryIndex >= 0 ? id.slice(0, queryIndex) : id\n}\n\n/**\n * Pre-scan all source files in the project for signal exports.\n *\n * Called from `buildStart` so the registry is fully populated before any\n * transforms run. This solves the build ordering problem where component.tsx\n * is transformed before store.ts — without pre-scanning, the registry would\n * be empty and imported signals would not be auto-called.\n */\nasync function prescanSignalExports(root: string, registry: Map<string, Set<string>>): Promise<void> {\n const files: string[] = []\n\n function walk(dir: string) {\n try {\n for (const entry of readdirSync(dir)) {\n if (entry.startsWith('.') || entry === 'node_modules' || entry === 'dist' || entry === 'lib' || entry === 'build') continue\n const full = pathJoin(dir, entry)\n try {\n const stat = statSync(full)\n if (stat.isDirectory()) walk(full)\n else if (/\\.(ts|tsx|js|jsx)$/.test(entry)) files.push(full)\n } catch {\n /* permission error, etc. */\n }\n }\n } catch {\n /* dir doesn't exist */\n }\n }\n\n walk(root)\n\n for (const file of files) {\n try {\n const code = readFileSync(file, 'utf-8')\n scanSignalExports(code, file, registry)\n } catch {\n /* read error */\n }\n }\n}\n\n/**\n * Scan a module's source for exported signal declarations and register them.\n *\n * Detects patterns:\n * 1. `export const x = signal(...)` or `export const x = computed(...)` — inline export\n * 2. `const x = signal(...); export { x }` — separate declaration + named export\n * 3. `export default signal(...)` — default export (tracked as 'default')\n *\n * Re-exports (`export { x } from './signals'`) are NOT detected — the source\n * module must be scanned directly. This is a known limitation.\n *\n * Uses simple regex — no AST parse needed.\n */\nfunction scanSignalExports(code: string, moduleId: string, registry: Map<string, Set<string>>): void {\n const normalizedId = normalizeModuleId(moduleId)\n let match: RegExpExecArray | null\n const signals = new Set<string>()\n\n // Pattern 1: export const x = signal(...) or export const x = computed(...)\n const EXPORT_CONST_RE = /export\\s+const\\s+(\\w+)\\s*=\\s*(?:signal|computed)\\s*[<(]/g\n while ((match = EXPORT_CONST_RE.exec(code)) !== null) {\n signals.add(match[1]!)\n }\n\n // Pattern 2: const x = signal(...) followed by export { x }\n // First, find all local `const x = signal(` or `const x = computed(` declarations\n const localSignals = new Set<string>()\n const LOCAL_SIGNAL_RE = /(?:^|[\\s;])const\\s+(\\w+)\\s*=\\s*(?:signal|computed)\\s*[<(]/gm\n while ((match = LOCAL_SIGNAL_RE.exec(code)) !== null) {\n localSignals.add(match[1]!)\n }\n\n // Then check named exports: export { x, y as z }\n if (localSignals.size > 0) {\n const NAMED_EXPORT_RE = /export\\s*\\{([^}]+)\\}/g\n while ((match = NAMED_EXPORT_RE.exec(code)) !== null) {\n // Skip re-exports (export { x } from '...')\n const afterBrace = code.slice(match.index + match[0].length).trimStart()\n if (afterBrace.startsWith('from')) continue\n\n for (const spec of match[1]!.split(',')) {\n const trimmed = spec.trim()\n if (!trimmed) continue\n const parts = trimmed.split(/\\s+as\\s+/)\n const localName = parts[0]!.trim()\n const exportedName = (parts[1] ?? parts[0])!.trim()\n if (localSignals.has(localName)) {\n signals.add(exportedName)\n }\n }\n }\n }\n\n // Pattern 3: export default signal(...) or export default computed(...) — tracked as 'default'\n if (/export\\s+default\\s+(?:signal|computed)\\s*[<(]/.test(code)) {\n signals.add('default')\n }\n\n if (signals.size > 0) {\n registry.set(normalizedId, signals)\n } else {\n // Clean up if module no longer exports signals (e.g. after edit)\n registry.delete(normalizedId)\n }\n}\n\n/**\n * Resolve imported signal names from the signal export registry.\n *\n * For each import in the source, resolves the module and checks if it has\n * signal exports in the registry. Returns the local names of imported signals.\n *\n * Handles named imports (`import { x } from ...`) and default imports\n * (`import x from ...` — matched against 'default' in the registry).\n */\nasync function resolveImportedSignals(\n code: string,\n _moduleId: string,\n registry: Map<string, Set<string>>,\n pluginCtx: { resolve: (id: string, importer?: string, options?: { skipSelf: boolean }) => Promise<{ id: string } | null> },\n resolveCache: Map<string, string | null>,\n): Promise<string[]> {\n if (registry.size === 0) return []\n\n const knownSignals: string[] = []\n let match: RegExpExecArray | null\n\n /** Resolve a source specifier to a normalized module ID, using the cache. */\n async function resolveSource(source: string): Promise<string | null> {\n const cacheKey = `${_moduleId}::${source}`\n if (resolveCache.has(cacheKey)) return resolveCache.get(cacheKey) ?? null\n let resolvedId: string | null = null\n try {\n const resolved = await pluginCtx.resolve(source, _moduleId, { skipSelf: true })\n resolvedId = resolved?.id ? normalizeModuleId(resolved.id) : null\n } catch {\n /* resolve error */\n }\n resolveCache.set(cacheKey, resolvedId)\n return resolvedId\n }\n\n // Named imports: import { name1, name2 as alias } from 'source'\n // Excludes `import type { ... }` — type-only imports have no runtime value\n const IMPORT_RE = /import\\s+(?!type\\s)\\{([^}]+)\\}\\s*from\\s*['\"]([^'\"]+)['\"]/g\n while ((match = IMPORT_RE.exec(code)) !== null) {\n const specifiers = match[1]!\n const source = match[2]!\n\n const resolvedId = await resolveSource(source)\n if (!resolvedId) continue\n const exportedSignals = registry.get(resolvedId)\n if (!exportedSignals) continue\n\n // Parse import specifiers: \"count, theme as t, other\"\n for (const spec of specifiers.split(',')) {\n const trimmed = spec.trim()\n if (!trimmed) continue\n\n const parts = trimmed.split(/\\s+as\\s+/)\n const importedName = parts[0]!.trim()\n const localName = (parts[1] ?? parts[0])!.trim()\n\n if (exportedSignals.has(importedName)) {\n knownSignals.push(localName)\n }\n }\n }\n\n // Default imports: import count from './store'\n // Excludes: `import { ... }`, `import type X`, `import * as X`\n const DEFAULT_IMPORT_RE = /import\\s+(?!type\\s)(\\w+)\\s+from\\s*['\"]([^'\"]+)['\"]/g\n while ((match = DEFAULT_IMPORT_RE.exec(code)) !== null) {\n // Skip if this is actually a `import type X from` pattern\n const fullMatch = match[0]\n if (/import\\s+type\\s+/.test(fullMatch)) continue\n\n const localName = match[1]!\n const source = match[2]!\n\n const resolvedId = await resolveSource(source)\n if (!resolvedId) continue\n const exportedSignals = registry.get(resolvedId)\n if (!exportedSignals) continue\n\n if (exportedSignals.has('default')) {\n knownSignals.push(localName)\n }\n }\n\n return knownSignals\n}\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, { name: name });\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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyreon/vite-plugin",
3
- "version": "0.13.1",
3
+ "version": "0.14.0",
4
4
  "description": "Vite plugin for Pyreon — .pyreon SFC support, HMR, compiler integration",
5
5
  "homepage": "https://github.com/pyreon/pyreon/tree/main/packages/vite-plugin#readme",
6
6
  "bugs": {
@@ -42,7 +42,7 @@
42
42
  "prepublishOnly": "bun run build"
43
43
  },
44
44
  "dependencies": {
45
- "@pyreon/compiler": "^0.13.1"
45
+ "@pyreon/compiler": "^0.14.0"
46
46
  },
47
47
  "devDependencies": {
48
48
  "vite": "^8.0.0"