@pyreon/vite-plugin 0.14.0 → 0.15.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":"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}};
5389
+ const data = {"version":2,"tree":{"name":"root","children":[{"name":"index.js","children":[{"name":"src/index.ts","uid":"170e8aa8-1"}]}],"isRoot":true},"nodeParts":{"170e8aa8-1":{"renderedLength":22365,"gzipLength":7629,"brotliLength":0,"metaUid":"170e8aa8-0"}},"nodeMetas":{"170e8aa8-0":{"id":"/src/index.ts","moduleParts":{"index.js":"170e8aa8-1"},"imported":[{"uid":"170e8aa8-2"},{"uid":"170e8aa8-3"},{"uid":"170e8aa8-4"}],"importedBy":[],"isEntry":true},"170e8aa8-2":{"id":"node:fs","moduleParts":{},"imported":[],"importedBy":[{"uid":"170e8aa8-0"}]},"170e8aa8-3":{"id":"node:path","moduleParts":{},"imported":[],"importedBy":[{"uid":"170e8aa8-0"}]},"170e8aa8-4":{"id":"@pyreon/compiler","moduleParts":{},"imported":[],"importedBy":[{"uid":"170e8aa8-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,5 +1,5 @@
1
1
  import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
2
- import { join } from "node:path";
2
+ import { dirname, join } from "node:path";
3
3
  import { generateContext, transformJSX } from "@pyreon/compiler";
4
4
 
5
5
  //#region src/index.ts
@@ -38,12 +38,6 @@ import { generateContext, transformJSX } from "@pyreon/compiler";
38
38
  */
39
39
  const HMR_RUNTIME_ID = "\0pyreon/hmr-runtime";
40
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
41
  const COMPAT_ALIASES = {
48
42
  react: {
49
43
  react: "@pyreon/react-compat",
@@ -71,6 +65,59 @@ const COMPAT_ALIASES = {
71
65
  }
72
66
  };
73
67
  /**
68
+ * Detect whether a file id resolves to a `@pyreon/*` framework-package source
69
+ * (i.e. a published Pyreon package whose .tsx is being pulled in via the
70
+ * `bun` condition workspace-link, NOT user code, NOT an example app).
71
+ *
72
+ * Why this exists: in compat mode, OXC's per-project `importSource` is set
73
+ * to `@pyreon/core` and the resolveId hook redirects `@pyreon/core/jsx-runtime`
74
+ * to the compat package. That's correct for user code (the whole point of
75
+ * compat mode) but WRONG for framework-internal sources like
76
+ * `@pyreon/zero/src/link.tsx`, which need the real `@pyreon/core` runtime.
77
+ * The fix skips the redirect when the importer is a `@pyreon/*` framework
78
+ * file. Result: published-package consumers (where `@pyreon/zero` resolves
79
+ * to its pre-built `lib/`) and workspace-dev consumers (where it resolves
80
+ * to source) both get correct JSX runtime resolution.
81
+ *
82
+ * Detection heuristic: walk to nearest `package.json`, require BOTH:
83
+ * 1. `name` starts with `@pyreon/` (workspace member of the @pyreon scope)
84
+ * 2. file path contains `/packages/` AND NOT `/examples/`
85
+ *
86
+ * Step 2 excludes the existing `@pyreon/example-{react,vue,solid,preact}-compat`
87
+ * apps under `examples/`. Without it, user code in those apps would skip the
88
+ * compat-mode JSX-runtime redirect and import `@pyreon/core/jsx-runtime`
89
+ * directly — breaking the React/Vue/Solid/Preact compat layer's contract.
90
+ *
91
+ * Result cached per directory. The `/packages/` + `/examples/` check is a
92
+ * structural property of the monorepo (workspace layout), not the package
93
+ * name — so it's robust against renames.
94
+ */
95
+ function isPyreonWorkspaceFile(id, cache) {
96
+ const queryIdx = id.indexOf("?");
97
+ const filePath = queryIdx === -1 ? id : id.slice(0, queryIdx);
98
+ if (!filePath || filePath[0] === "\0") return false;
99
+ if (!filePath.includes("/packages/") || filePath.includes("/examples/")) return false;
100
+ let dir = dirname(filePath);
101
+ for (let i = 0; i < 12; i++) {
102
+ const cached = cache.get(dir);
103
+ if (cached !== void 0) return cached;
104
+ const pkgPath = join(dir, "package.json");
105
+ if (existsSync(pkgPath)) {
106
+ let isPyreon = false;
107
+ try {
108
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
109
+ isPyreon = typeof pkg.name === "string" && pkg.name.startsWith("@pyreon/");
110
+ } catch {}
111
+ cache.set(dir, isPyreon);
112
+ return isPyreon;
113
+ }
114
+ const parent = dirname(dir);
115
+ if (parent === dir) break;
116
+ dir = parent;
117
+ }
118
+ return false;
119
+ }
120
+ /**
74
121
  * Return the Pyreon compat target for an import specifier, or undefined if
75
122
  * the import should not be redirected.
76
123
  */
@@ -92,20 +139,19 @@ function pyreonPlugin(options) {
92
139
  let projectRoot = "";
93
140
  const signalExportRegistry = /* @__PURE__ */ new Map();
94
141
  const resolveCache = /* @__PURE__ */ new Map();
142
+ const pyreonWorkspaceDirCache = /* @__PURE__ */ new Map();
95
143
  return {
96
144
  name: "pyreon",
97
145
  enforce: "pre",
98
146
  config(userConfig, env) {
99
147
  isBuild = env.command === "build";
100
148
  projectRoot = userConfig.root ?? process.cwd();
101
- const optimizeDepsExclude = compat ? Object.keys(COMPAT_ALIASES[compat]) : [];
102
- const jsxSource = compat ? COMPAT_JSX_SOURCE[compat] : "@pyreon/core";
103
149
  return {
104
150
  resolve: { conditions: ["bun"] },
105
- optimizeDeps: { exclude: optimizeDepsExclude },
151
+ optimizeDeps: { exclude: compat ? Object.keys(COMPAT_ALIASES[compat]) : [] },
106
152
  oxc: { jsx: {
107
153
  runtime: "automatic",
108
- importSource: jsxSource
154
+ importSource: "@pyreon/core"
109
155
  } },
110
156
  ...env.isSsrBuild && ssrConfig ? { build: {
111
157
  ssr: true,
@@ -118,6 +164,7 @@ function pyreonPlugin(options) {
118
164
  },
119
165
  async resolveId(id, importer) {
120
166
  if (id === HMR_RUNTIME_IMPORT) return HMR_RUNTIME_ID;
167
+ if (compat && (id === "@pyreon/core/jsx-runtime" || id === "@pyreon/core/jsx-dev-runtime") && importer && isPyreonWorkspaceFile(importer, pyreonWorkspaceDirCache)) return;
121
168
  const target = getCompatTarget(compat, id);
122
169
  if (!target) return;
123
170
  return (await this.resolve(target, importer, { skipSelf: true }))?.id;
@@ -222,8 +269,18 @@ function generateProjectContext(root) {
222
269
  * The arguments are extracted via balanced-paren matching in `injectHmr`.
223
270
  * A brace-depth check filters out matches inside functions/blocks — only
224
271
  * module-scope (depth 0) signals are rewritten for HMR state preservation.
272
+ *
273
+ * The optional `<...>` group accepts a TypeScript type parameter so that
274
+ * `signal<T>(initial)` declarations are also rewritten — without it, any
275
+ * generic-typed module-scope signal silently skipped HMR preservation.
276
+ *
277
+ * The inner `(?:[^<>]|<[^<>]*>)*` permits one level of generic nesting
278
+ * (e.g. `signal<Array<Row>>([])`, `signal<Map<string, number>>(m)`).
279
+ * Deeper nesting (`signal<Array<{ id: T<U> }>>(...)`) falls back to
280
+ * not-rewritten — tracked as a follow-up if real consumers need it,
281
+ * but unlikely at module scope where generics are usually shallow.
225
282
  */
226
- const SIGNAL_PREFIX_RE = /^((?:export\s+)?(?:const|let)\s+(\w+)\s*=\s*)signal\(/gm;
283
+ const SIGNAL_PREFIX_RE = /^((?:export\s+)?(?:const|let)\s+(\w+)\s*=\s*)signal(?:<(?:[^<>]|<[^<>]*>)*>)?\(/gm;
227
284
  /**
228
285
  * Detect whether the module exports any component-like functions
229
286
  * (uppercase first letter — standard convention for JSX components).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyreon/vite-plugin",
3
- "version": "0.14.0",
3
+ "version": "0.15.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": {
@@ -14,6 +14,7 @@
14
14
  },
15
15
  "files": [
16
16
  "lib",
17
+ "!lib/**/*.map",
17
18
  "src",
18
19
  "README.md",
19
20
  "LICENSE"
@@ -42,7 +43,7 @@
42
43
  "prepublishOnly": "bun run build"
43
44
  },
44
45
  "dependencies": {
45
- "@pyreon/compiler": "^0.14.0"
46
+ "@pyreon/compiler": "^0.15.0"
46
47
  },
47
48
  "devDependencies": {
48
49
  "vite": "^8.0.0"
package/src/index.ts CHANGED
@@ -33,7 +33,7 @@
33
33
  */
34
34
 
35
35
  import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'
36
- import { join as pathJoin } from 'node:path'
36
+ import { dirname, join as pathJoin } from 'node:path'
37
37
  import { generateContext, transformJSX } from '@pyreon/compiler'
38
38
  import type { Plugin, ViteDevServer } from 'vite'
39
39
 
@@ -112,6 +112,73 @@ const COMPAT_ALIASES: Record<CompatFramework, Record<string, string>> = {
112
112
  },
113
113
  }
114
114
 
115
+ /**
116
+ * Detect whether a file id resolves to a `@pyreon/*` framework-package source
117
+ * (i.e. a published Pyreon package whose .tsx is being pulled in via the
118
+ * `bun` condition workspace-link, NOT user code, NOT an example app).
119
+ *
120
+ * Why this exists: in compat mode, OXC's per-project `importSource` is set
121
+ * to `@pyreon/core` and the resolveId hook redirects `@pyreon/core/jsx-runtime`
122
+ * to the compat package. That's correct for user code (the whole point of
123
+ * compat mode) but WRONG for framework-internal sources like
124
+ * `@pyreon/zero/src/link.tsx`, which need the real `@pyreon/core` runtime.
125
+ * The fix skips the redirect when the importer is a `@pyreon/*` framework
126
+ * file. Result: published-package consumers (where `@pyreon/zero` resolves
127
+ * to its pre-built `lib/`) and workspace-dev consumers (where it resolves
128
+ * to source) both get correct JSX runtime resolution.
129
+ *
130
+ * Detection heuristic: walk to nearest `package.json`, require BOTH:
131
+ * 1. `name` starts with `@pyreon/` (workspace member of the @pyreon scope)
132
+ * 2. file path contains `/packages/` AND NOT `/examples/`
133
+ *
134
+ * Step 2 excludes the existing `@pyreon/example-{react,vue,solid,preact}-compat`
135
+ * apps under `examples/`. Without it, user code in those apps would skip the
136
+ * compat-mode JSX-runtime redirect and import `@pyreon/core/jsx-runtime`
137
+ * directly — breaking the React/Vue/Solid/Preact compat layer's contract.
138
+ *
139
+ * Result cached per directory. The `/packages/` + `/examples/` check is a
140
+ * structural property of the monorepo (workspace layout), not the package
141
+ * name — so it's robust against renames.
142
+ */
143
+ function isPyreonWorkspaceFile(id: string, cache: Map<string, boolean>): boolean {
144
+ // Strip query strings (e.g. `?vue&type=script`) to get the bare path.
145
+ const queryIdx = id.indexOf('?')
146
+ const filePath = queryIdx === -1 ? id : id.slice(0, queryIdx)
147
+ if (!filePath || filePath[0] === '\0') return false
148
+
149
+ // Path-based filter first (cheap): file must live under `<root>/packages/`
150
+ // and not under `<root>/examples/`. This excludes example apps even when
151
+ // they have `@pyreon/example-*` names.
152
+ if (!filePath.includes('/packages/') || filePath.includes('/examples/')) {
153
+ return false
154
+ }
155
+
156
+ let dir = dirname(filePath)
157
+ // Walk up at most ~12 levels — enough for any realistic monorepo depth.
158
+ for (let i = 0; i < 12; i++) {
159
+ const cached = cache.get(dir)
160
+ if (cached !== undefined) return cached
161
+
162
+ const pkgPath = pathJoin(dir, 'package.json')
163
+ if (existsSync(pkgPath)) {
164
+ let isPyreon = false
165
+ try {
166
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as { name?: string }
167
+ isPyreon = typeof pkg.name === 'string' && pkg.name.startsWith('@pyreon/')
168
+ } catch {
169
+ // Malformed package.json — treat as not-pyreon.
170
+ }
171
+ cache.set(dir, isPyreon)
172
+ return isPyreon
173
+ }
174
+
175
+ const parent = dirname(dir)
176
+ if (parent === dir) break // reached filesystem root
177
+ dir = parent
178
+ }
179
+ return false
180
+ }
181
+
115
182
  /**
116
183
  * Return the Pyreon compat target for an import specifier, or undefined if
117
184
  * the import should not be redirected.
@@ -144,6 +211,9 @@ export default function pyreonPlugin(options?: PyreonPluginOptions): Plugin {
144
211
  const signalExportRegistry = new Map<string, Set<string>>()
145
212
  // Cache resolved import specifiers to avoid redundant resolution calls
146
213
  const resolveCache = new Map<string, string | null>()
214
+ // Cache `isPyreonWorkspaceFile` lookups by directory — package.json reads
215
+ // happen at most once per containing directory across the build.
216
+ const pyreonWorkspaceDirCache = new Map<string, boolean>()
147
217
 
148
218
  return {
149
219
  name: 'pyreon',
@@ -158,7 +228,13 @@ export default function pyreonPlugin(options?: PyreonPluginOptions): Plugin {
158
228
  // they resolve to workspace packages via our resolveId hook, not node_modules.
159
229
  const optimizeDepsExclude = compat ? Object.keys(COMPAT_ALIASES[compat]) : []
160
230
 
161
- const jsxSource = compat ? COMPAT_JSX_SOURCE[compat] : '@pyreon/core'
231
+ // Always set OXC's JSX importSource to `@pyreon/core`. In compat mode,
232
+ // we redirect `@pyreon/core/jsx-runtime` imports to the compat package
233
+ // VIA `resolveId` — but ONLY for user code, never for `@pyreon/*`
234
+ // workspace-package files (zero, router, runtime-dom, etc.). Setting
235
+ // OXC's importSource directly to the compat package would force the
236
+ // compat runtime on framework internals too, which they cannot handle.
237
+ const jsxSource = '@pyreon/core'
162
238
 
163
239
  return {
164
240
  // Use "bun" condition for workspace resolution — source .ts/.tsx files
@@ -200,6 +276,22 @@ export default function pyreonPlugin(options?: PyreonPluginOptions): Plugin {
200
276
  // ── Virtual module + compat alias resolution ─────────────────────────────
201
277
  async resolveId(id, importer) {
202
278
  if (id === HMR_RUNTIME_IMPORT) return HMR_RUNTIME_ID
279
+
280
+ // `@pyreon/core/jsx-runtime` resolves to the compat package only for
281
+ // user code — never for `@pyreon/*` framework files (zero, router,
282
+ // runtime-dom, etc.). Without this importer guard, every JSX file in
283
+ // the build (including framework internals resolved via the `bun`
284
+ // workspace condition) would get redirected to a compat runtime that
285
+ // doesn't match the framework's JSX shape. Caught by `cpa-smoke-app-*-compat`.
286
+ if (
287
+ compat &&
288
+ (id === '@pyreon/core/jsx-runtime' || id === '@pyreon/core/jsx-dev-runtime') &&
289
+ importer &&
290
+ isPyreonWorkspaceFile(importer, pyreonWorkspaceDirCache)
291
+ ) {
292
+ return // let Vite resolve to the real `@pyreon/core/jsx-runtime`
293
+ }
294
+
203
295
  const target = getCompatTarget(compat, id)
204
296
  if (!target) return
205
297
 
@@ -361,8 +453,19 @@ function generateProjectContext(root: string): void {
361
453
  * The arguments are extracted via balanced-paren matching in `injectHmr`.
362
454
  * A brace-depth check filters out matches inside functions/blocks — only
363
455
  * module-scope (depth 0) signals are rewritten for HMR state preservation.
456
+ *
457
+ * The optional `<...>` group accepts a TypeScript type parameter so that
458
+ * `signal<T>(initial)` declarations are also rewritten — without it, any
459
+ * generic-typed module-scope signal silently skipped HMR preservation.
460
+ *
461
+ * The inner `(?:[^<>]|<[^<>]*>)*` permits one level of generic nesting
462
+ * (e.g. `signal<Array<Row>>([])`, `signal<Map<string, number>>(m)`).
463
+ * Deeper nesting (`signal<Array<{ id: T<U> }>>(...)`) falls back to
464
+ * not-rewritten — tracked as a follow-up if real consumers need it,
465
+ * but unlikely at module scope where generics are usually shallow.
364
466
  */
365
- const SIGNAL_PREFIX_RE = /^((?:export\s+)?(?:const|let)\s+(\w+)\s*=\s*)signal\(/gm
467
+ const SIGNAL_PREFIX_RE =
468
+ /^((?:export\s+)?(?:const|let)\s+(\w+)\s*=\s*)signal(?:<(?:[^<>]|<[^<>]*>)*>)?\(/gm
366
469
 
367
470
  /**
368
471
  * Detect whether the module exports any component-like functions
@@ -5,6 +5,7 @@
5
5
  * resolveId hook + the JSX-runtime aliasing branch.
6
6
  */
7
7
 
8
+ import { resolve } from 'node:path'
8
9
  import { describe, expect, it } from 'vitest'
9
10
  import pyreonPlugin, { type PyreonPluginOptions } from '../index'
10
11
 
@@ -36,6 +37,7 @@ async function callResolveId(
36
37
  plugin: ReturnType<typeof pyreonPlugin>,
37
38
  id: string,
38
39
  resolveMap: Record<string, string> = {},
40
+ importer?: string,
39
41
  ): Promise<string | undefined> {
40
42
  const hook = plugin.resolveId as ResolveIdHook
41
43
  return hook.call(
@@ -46,6 +48,7 @@ async function callResolveId(
46
48
  },
47
49
  },
48
50
  id,
51
+ importer,
49
52
  )
50
53
  }
51
54
 
@@ -159,6 +162,85 @@ describe('compat-mode resolveId — solid', () => {
159
162
  })
160
163
  })
161
164
 
165
+ describe('compat-mode resolveId — framework-importer carve-out', () => {
166
+ // Regression: in compat mode, `@pyreon/core/jsx-runtime` must NOT be
167
+ // redirected to the compat package when the importer is itself a
168
+ // `@pyreon/*` workspace-package source file (zero, router, runtime-dom,
169
+ // etc.). Pre-fix, OXC's project-wide importSource was set to the compat
170
+ // package, so framework-internal JSX got rewritten to import a runtime
171
+ // shape it doesn't speak. The fix sets OXC to `@pyreon/core` always and
172
+ // redirects in `resolveId` only for non-framework importers. Caught by
173
+ // `cpa-smoke-app-*-compat` cells in `scripts/scaffold-smoke.ts`.
174
+ // Bisect-verified: dropping the `isPyreonWorkspaceFile(importer)` guard
175
+ // makes these tests fail with the redirected jsx-runtime path.
176
+
177
+ const repoRoot = resolve(import.meta.dirname, '../../../../..')
178
+ const frameworkImporter = `${repoRoot}/packages/zero/zero/src/link.tsx`
179
+ const userImporter = `${repoRoot}/examples/some-user-app/src/foo.tsx`
180
+ // The 4 existing compat-layer example apps under `examples/` have
181
+ // package.json names like `@pyreon/example-react-compat`. The carve-out
182
+ // helper must NOT treat their source files as framework files — doing so
183
+ // skips the JSX-runtime redirect and breaks the compat layer end-to-end.
184
+ // Bisect-verified: when the helper checked `name.startsWith('@pyreon/')`
185
+ // alone (without the `/examples/` exclusion), all 4 compat-layer e2e
186
+ // suites failed in CI with `section.demo` never rendering.
187
+ const exampleAppImporter = `${repoRoot}/examples/react-compat/src/Foo.tsx`
188
+
189
+ it('does NOT redirect @pyreon/core/jsx-runtime when imported FROM @pyreon/zero workspace source (react)', async () => {
190
+ const plugin = bootstrap({ compat: 'react' })
191
+ const resolved = await callResolveId(
192
+ plugin,
193
+ '@pyreon/core/jsx-runtime',
194
+ { '@pyreon/react-compat/jsx-runtime': '/abs/react-compat/jsx-runtime.ts' },
195
+ frameworkImporter,
196
+ )
197
+ expect(resolved).toBeUndefined() // pass through to Vite's resolver
198
+ })
199
+
200
+ it('does NOT redirect @pyreon/core/jsx-dev-runtime when imported FROM framework source (preact)', async () => {
201
+ const plugin = bootstrap({ compat: 'preact' })
202
+ const resolved = await callResolveId(
203
+ plugin,
204
+ '@pyreon/core/jsx-dev-runtime',
205
+ { '@pyreon/preact-compat/jsx-runtime': '/abs/preact-compat/jsx-runtime.ts' },
206
+ frameworkImporter,
207
+ )
208
+ expect(resolved).toBeUndefined()
209
+ })
210
+
211
+ it('STILL redirects @pyreon/core/jsx-runtime when imported FROM user code (react)', async () => {
212
+ const plugin = bootstrap({ compat: 'react' })
213
+ const resolved = await callResolveId(
214
+ plugin,
215
+ '@pyreon/core/jsx-runtime',
216
+ { '@pyreon/react-compat/jsx-runtime': '/abs/react-compat/jsx-runtime.ts' },
217
+ userImporter,
218
+ )
219
+ expect(resolved).toBe('/abs/react-compat/jsx-runtime.ts')
220
+ })
221
+
222
+ it('STILL redirects @pyreon/core/jsx-runtime when no importer (entry point)', async () => {
223
+ const plugin = bootstrap({ compat: 'react' })
224
+ const resolved = await callResolveId(
225
+ plugin,
226
+ '@pyreon/core/jsx-runtime',
227
+ { '@pyreon/react-compat/jsx-runtime': '/abs/react-compat/jsx-runtime.ts' },
228
+ )
229
+ expect(resolved).toBe('/abs/react-compat/jsx-runtime.ts')
230
+ })
231
+
232
+ it('STILL redirects @pyreon/core/jsx-runtime when imported FROM an example app under examples/ (e.g. @pyreon/example-react-compat)', async () => {
233
+ const plugin = bootstrap({ compat: 'react' })
234
+ const resolved = await callResolveId(
235
+ plugin,
236
+ '@pyreon/core/jsx-runtime',
237
+ { '@pyreon/react-compat/jsx-runtime': '/abs/react-compat/jsx-runtime.ts' },
238
+ exampleAppImporter,
239
+ )
240
+ expect(resolved).toBe('/abs/react-compat/jsx-runtime.ts')
241
+ })
242
+ })
243
+
162
244
  describe('compat-mode resolveId — no compat', () => {
163
245
  it('returns undefined for any framework alias when compat is unset', async () => {
164
246
  const plugin = bootstrap()
@@ -133,6 +133,32 @@ export function App() { return null }
133
133
  expect(result!.code).toContain('__hmr_signal("/src/theme.tsx", "theme", signal, "light")')
134
134
  })
135
135
 
136
+ it('rewrites generic-typed signals (signal<T>(value))', async () => {
137
+ // Regression for the silent-skip bug: SIGNAL_PREFIX_RE used to match
138
+ // `signal(` but not `signal<T>(`. Pre-rewrite TypeScript still has type
139
+ // parameters; declarations like `signal<string>('')` would skip HMR
140
+ // preservation silently and produce an empty-string-valued signal that
141
+ // — under a separate `__hmr_signal` interaction — could read as
142
+ // undefined. Discovered via PR #329 (perf-dashboard form section).
143
+ const plugin = createPlugin()
144
+ const code = `
145
+ import { signal } from "@pyreon/reactivity"
146
+ export const password = signal<string>("")
147
+ export const items = signal<Array<{ id: number }>>([])
148
+ export const count = signal<number>(0)
149
+ export function App() { return null }
150
+ `
151
+ const result = await transform(plugin, code, '/src/state.tsx')
152
+ expect(result).toBeDefined()
153
+ expect(result!.code).toContain(
154
+ '__hmr_signal("/src/state.tsx", "password", signal, "")',
155
+ )
156
+ expect(result!.code).toContain(
157
+ '__hmr_signal("/src/state.tsx", "items", signal, [])',
158
+ )
159
+ expect(result!.code).toContain('__hmr_signal("/src/state.tsx", "count", signal, 0)')
160
+ })
161
+
136
162
  it('does not rewrite signal() inside functions to __hmr_signal (but injects name)', async () => {
137
163
  const plugin = createPlugin()
138
164
  const code = `
@@ -299,12 +325,19 @@ describe('plugin config', () => {
299
325
  expect(config.oxc.jsx.importSource).toBe('@pyreon/core')
300
326
  })
301
327
 
302
- it('sets JSX import source to compat package in compat mode', async () => {
328
+ it('keeps JSX import source as @pyreon/core in compat mode', async () => {
329
+ // OXC's `importSource` is project-wide (one setting for the whole build),
330
+ // so pointing it at the compat package would force the compat runtime
331
+ // on `@pyreon/*` framework files too — which they cannot handle. Instead
332
+ // the plugin keeps OXC at `@pyreon/core` and redirects the resulting
333
+ // `@pyreon/core/jsx-runtime` import to the compat package via `resolveId`,
334
+ // gated on the importer (user code only). See `compat-resolve.test.ts`
335
+ // "framework-importer carve-out". Caught by `cpa-smoke-app-*-compat`.
303
336
  const plugin = pyreonPlugin({ compat: 'react' })
304
337
  const config = getConfigHook(plugin)({}, { command: 'serve' }) as {
305
338
  oxc: { jsx: { importSource: string } }
306
339
  }
307
- expect(config.oxc.jsx.importSource).toBe('@pyreon/react-compat')
340
+ expect(config.oxc.jsx.importSource).toBe('@pyreon/core')
308
341
  })
309
342
 
310
343
  it('excludes compat packages from optimizeDeps', async () => {
package/lib/index.js.map DELETED
@@ -1 +0,0 @@
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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"}
@@ -1 +0,0 @@
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,iBA6DoB,YAAA,CAAa,OAAA,GAAU,mBAAA,GAAsB,MAAA"}