@pyreon/vite-plugin 0.16.0 → 0.19.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 +11 -4
- package/package.json +2 -2
- package/src/index.ts +52 -4
- package/src/tests/vite-plugin.test.ts +13 -2
|
@@ -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":"88d6c780-1"}]}],"isRoot":true},"nodeParts":{"88d6c780-1":{"renderedLength":29326,"gzipLength":9623,"brotliLength":0,"metaUid":"88d6c780-0"}},"nodeMetas":{"88d6c780-0":{"id":"/src/index.ts","moduleParts":{"index.js":"88d6c780-1"},"imported":[{"uid":"88d6c780-2"},{"uid":"88d6c780-3"},{"uid":"88d6c780-4"}],"importedBy":[],"isEntry":true},"88d6c780-2":{"id":"node:fs","moduleParts":{},"imported":[],"importedBy":[{"uid":"88d6c780-0"}]},"88d6c780-3":{"id":"node:path","moduleParts":{},"imported":[],"importedBy":[{"uid":"88d6c780-0"}]},"88d6c780-4":{"id":"@pyreon/compiler","moduleParts":{},"imported":[],"importedBy":[{"uid":"88d6c780-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,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
|
-
import { generateContext, transformJSX } from "@pyreon/compiler";
|
|
3
|
+
import { generateContext, transformDeferInline, transformJSX } from "@pyreon/compiler";
|
|
4
4
|
|
|
5
5
|
//#region src/index.ts
|
|
6
6
|
/**
|
|
@@ -222,8 +222,11 @@ function pyreonPlugin(options) {
|
|
|
222
222
|
}
|
|
223
223
|
scanSignalExports(code, normalizeModuleId(id), signalExportRegistry);
|
|
224
224
|
if (islandsEnabled) scanIslandDeclarations(code, id, islandRegistry);
|
|
225
|
-
const
|
|
226
|
-
const
|
|
225
|
+
const deferResult = transformDeferInline(code, id);
|
|
226
|
+
const sourceForJsx = deferResult.changed ? deferResult.code : code;
|
|
227
|
+
for (const w of deferResult.warnings) this.warn(`${w.message} (${id}:${w.line}:${w.column})`);
|
|
228
|
+
const knownSignals = await resolveImportedSignals(sourceForJsx, id, signalExportRegistry, this, resolveCache);
|
|
229
|
+
const result = transformJSX(sourceForJsx, id, {
|
|
227
230
|
ssr: transformOptions?.ssr === true,
|
|
228
231
|
knownSignals
|
|
229
232
|
});
|
|
@@ -440,7 +443,11 @@ function injectHmr(code, moduleId) {
|
|
|
440
443
|
if (hasSignals) lines.push(`import { __hmr_signal, __hmr_dispose } from "${HMR_RUNTIME_IMPORT}";`);
|
|
441
444
|
lines.push(`if (import.meta.hot) {`);
|
|
442
445
|
if (hasSignals) lines.push(` import.meta.hot.dispose(() => __hmr_dispose(${escapedId}));`);
|
|
443
|
-
lines.push(` import.meta.hot.accept()
|
|
446
|
+
lines.push(` import.meta.hot.accept((__m) => {`);
|
|
447
|
+
lines.push(` const __s = globalThis.__pyreon_hmr_swap__;`);
|
|
448
|
+
lines.push(` if (typeof __s === "function" && __m && __s(${escapedId}, __m)) return;`);
|
|
449
|
+
lines.push(` import.meta.hot.invalidate();`);
|
|
450
|
+
lines.push(` });`);
|
|
444
451
|
lines.push(`}`);
|
|
445
452
|
output = `${output}\n\n${lines.join("\n")}\n`;
|
|
446
453
|
return output;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/vite-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.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": {
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"prepublishOnly": "bun run build"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@pyreon/compiler": "^0.
|
|
46
|
+
"@pyreon/compiler": "^0.19.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"vite": "^8.0.0"
|
package/src/index.ts
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
|
|
35
35
|
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'
|
|
36
36
|
import { dirname, join as pathJoin } from 'node:path'
|
|
37
|
-
import { generateContext, transformJSX } from '@pyreon/compiler'
|
|
37
|
+
import { generateContext, transformDeferInline, transformJSX } from '@pyreon/compiler'
|
|
38
38
|
import type { Plugin, ViteDevServer } from 'vite'
|
|
39
39
|
|
|
40
40
|
// Virtual module ID for the HMR runtime
|
|
@@ -429,16 +429,31 @@ export default function pyreonPlugin(options?: PyreonPluginOptions): Plugin {
|
|
|
429
429
|
// next dev-server module reload.
|
|
430
430
|
if (islandsEnabled) scanIslandDeclarations(code, id, islandRegistry)
|
|
431
431
|
|
|
432
|
+
// ── Inline-Defer pre-pass ──────────────────────────────────────────
|
|
433
|
+
// Rewrites `<Defer when={x}><Modal /></Defer>` into the explicit
|
|
434
|
+
// chunk-prop form so Rolldown emits a proper per-Defer chunk and
|
|
435
|
+
// the main bundle drops the static `import { Modal } from ...`
|
|
436
|
+
// when it's exclusively used inside this Defer's subtree. Runs
|
|
437
|
+
// BEFORE the JSX→runtime transform so the downstream pipeline
|
|
438
|
+
// sees an already-explicit `<Defer chunk={...}>` shape with no
|
|
439
|
+
// special-casing needed in `transformJSX`. See
|
|
440
|
+
// `@pyreon/compiler/defer-inline` for the rewrite contract.
|
|
441
|
+
const deferResult = transformDeferInline(code, id)
|
|
442
|
+
const sourceForJsx = deferResult.changed ? deferResult.code : code
|
|
443
|
+
for (const w of deferResult.warnings) {
|
|
444
|
+
this.warn(`${w.message} (${id}:${w.line}:${w.column})`)
|
|
445
|
+
}
|
|
446
|
+
|
|
432
447
|
// ── Resolve imported signals from the registry ─────────────────────
|
|
433
448
|
// Check each import in this file: if the imported module has signal
|
|
434
449
|
// exports in the registry, pass them as knownSignals to the compiler.
|
|
435
|
-
const knownSignals = await resolveImportedSignals(
|
|
450
|
+
const knownSignals = await resolveImportedSignals(sourceForJsx, id, signalExportRegistry, this, resolveCache)
|
|
436
451
|
|
|
437
452
|
// Vite passes `ssr: true` when transforming for the SSR module graph
|
|
438
453
|
// (both build --ssr and dev `ssrLoadModule`). The compiler emits plain
|
|
439
454
|
// `h()` calls in that mode so `runtime-server` can render to a string.
|
|
440
455
|
const isSsr = transformOptions?.ssr === true
|
|
441
|
-
const result = transformJSX(
|
|
456
|
+
const result = transformJSX(sourceForJsx, id, { ssr: isSsr, knownSignals })
|
|
442
457
|
// Surface compiler warnings in the terminal
|
|
443
458
|
for (const w of result.warnings) {
|
|
444
459
|
this.warn(`${w.message} (${id}:${w.line}:${w.column})`)
|
|
@@ -730,7 +745,40 @@ function injectHmr(code: string, moduleId: string): string {
|
|
|
730
745
|
lines.push(` import.meta.hot.dispose(() => __hmr_dispose(${escapedId}));`)
|
|
731
746
|
}
|
|
732
747
|
|
|
733
|
-
|
|
748
|
+
// Self-accept the module, then drive Pyreon's HMR coordinator.
|
|
749
|
+
//
|
|
750
|
+
// The OLD code emitted a bare `import.meta.hot.accept()` (no callback):
|
|
751
|
+
// Vite re-evaluated the module but NOTHING re-rendered the mounted tree,
|
|
752
|
+
// AND the self-accept suppressed Vite's full-reload fallback — so a
|
|
753
|
+
// component/JSX edit produced a silently-stale UI until a MANUAL refresh.
|
|
754
|
+
//
|
|
755
|
+
// Now: the accept callback hands the FRESH module namespace Vite already
|
|
756
|
+
// re-evaluated straight to `globalThis.__pyreon_hmr_swap__` (registered
|
|
757
|
+
// by `@pyreon/router` in a dev browser — zero import coupling, same
|
|
758
|
+
// pattern as the perf-harness counter sink), keyed by THIS module's id.
|
|
759
|
+
// The coordinator finds every active matched route record whose lazy
|
|
760
|
+
// `_hmrId` matches and swaps in the new component, re-rendering ONLY
|
|
761
|
+
// that subtree IN PLACE (no page reload → `__pyreon_hmr_registry__`
|
|
762
|
+
// survives → `__hmr_signal` restores module-scope signal values).
|
|
763
|
+
//
|
|
764
|
+
// Using the namespace Vite passes (not a re-run of the lazy thunk)
|
|
765
|
+
// sidesteps the stale-`?t=` trap: the dynamic-import thunk lives in the
|
|
766
|
+
// virtual routes module, which is NOT invalidated when this leaf route
|
|
767
|
+
// self-accepts — re-importing it would return the OLD module.
|
|
768
|
+
//
|
|
769
|
+
// `__pyreon_hmr_swap__` returns falsy when the edit was outside the
|
|
770
|
+
// active route tree (nested non-route component, unrelated route,
|
|
771
|
+
// signal-only module) OR no coordinator is registered (plain
|
|
772
|
+
// `@pyreon/runtime-dom` app, or module loaded before any router
|
|
773
|
+
// mounted). Then `import.meta.hot.invalidate()` → Vite propagates → an
|
|
774
|
+
// AUTOMATIC full reload. Either way the user never refreshes by hand.
|
|
775
|
+
lines.push(` import.meta.hot.accept((__m) => {`)
|
|
776
|
+
lines.push(` const __s = globalThis.__pyreon_hmr_swap__;`)
|
|
777
|
+
lines.push(
|
|
778
|
+
` if (typeof __s === "function" && __m && __s(${escapedId}, __m)) return;`,
|
|
779
|
+
)
|
|
780
|
+
lines.push(` import.meta.hot.invalidate();`)
|
|
781
|
+
lines.push(` });`)
|
|
734
782
|
lines.push(`}`)
|
|
735
783
|
|
|
736
784
|
output = `${output}\n\n${lines.join('\n')}\n`
|
|
@@ -58,7 +58,7 @@ async function transform(plugin: ReturnType<typeof pyreonPlugin>, code: string,
|
|
|
58
58
|
// ─── HMR injection ──────────────────────────────────────────────────────────
|
|
59
59
|
|
|
60
60
|
describe('HMR injection', () => {
|
|
61
|
-
it('injects HMR accept for modules with component exports', async () => {
|
|
61
|
+
it('injects a coordinator-driven HMR accept for modules with component exports', async () => {
|
|
62
62
|
const plugin = createPlugin()
|
|
63
63
|
const code = `
|
|
64
64
|
import { h } from "@pyreon/core"
|
|
@@ -66,7 +66,18 @@ export function App() { return h("div", null, "hello") }
|
|
|
66
66
|
`
|
|
67
67
|
const result = await transform(plugin, code, '/src/App.tsx')
|
|
68
68
|
expect(result).toBeDefined()
|
|
69
|
-
|
|
69
|
+
// Self-accept WITH a callback receiving the fresh module (the bare
|
|
70
|
+
// `accept()` was the bug — it suppressed Vite's reload fallback while
|
|
71
|
+
// re-rendering nothing).
|
|
72
|
+
expect(result!.code).toContain('import.meta.hot.accept((__m) => {')
|
|
73
|
+
expect(result!.code).not.toContain('import.meta.hot.accept();')
|
|
74
|
+
// Hands the fresh module to the router-registered HMR coordinator,
|
|
75
|
+
// keyed by THIS module's id (zero import coupling).
|
|
76
|
+
expect(result!.code).toContain('globalThis.__pyreon_hmr_swap__')
|
|
77
|
+
expect(result!.code).toContain('__s("/src/App.tsx", __m)')
|
|
78
|
+
// Falls back to an automatic full reload when the edit was outside the
|
|
79
|
+
// active route tree or no coordinator is registered.
|
|
80
|
+
expect(result!.code).toContain('import.meta.hot.invalidate()')
|
|
70
81
|
})
|
|
71
82
|
|
|
72
83
|
it('injects HMR for exported const components', async () => {
|