@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.
- package/lib/analysis/index.js.html +1 -1
- package/lib/index.js +69 -12
- package/package.json +3 -2
- package/src/index.ts +106 -3
- package/src/tests/compat-resolve.test.ts +82 -0
- package/src/tests/vite-plugin.test.ts +35 -2
- package/lib/index.js.map +0 -1
- package/lib/types/index.d.ts.map +0 -1
|
@@ -5386,7 +5386,7 @@ var drawChart = (function (exports) {
|
|
|
5386
5386
|
</script>
|
|
5387
5387
|
<script>
|
|
5388
5388
|
/*<!--*/
|
|
5389
|
-
const data = {"version":2,"tree":{"name":"root","children":[{"name":"index.js","children":[{"name":"src/index.ts","uid":"
|
|
5389
|
+
const data = {"version":2,"tree":{"name":"root","children":[{"name":"index.js","children":[{"name":"src/index.ts","uid":"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:
|
|
151
|
+
optimizeDeps: { exclude: compat ? Object.keys(COMPAT_ALIASES[compat]) : [] },
|
|
106
152
|
oxc: { jsx: {
|
|
107
153
|
runtime: "automatic",
|
|
108
|
-
importSource:
|
|
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
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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 =
|
|
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('
|
|
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/
|
|
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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"}
|
package/lib/types/index.d.ts.map
DELETED
|
@@ -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"}
|