@pyreon/vite-plugin 0.24.4 → 0.24.5

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":"e04d9c6c-1"}]},{"name":"rocketstyle-collapse-C4eMAnwR.js","children":[{"name":"src/rocketstyle-collapse.ts","uid":"e04d9c6c-3"}]}],"isRoot":true},"nodeParts":{"e04d9c6c-1":{"renderedLength":45559,"gzipLength":14773,"brotliLength":0,"metaUid":"e04d9c6c-0"},"e04d9c6c-3":{"renderedLength":3424,"gzipLength":1530,"brotliLength":0,"metaUid":"e04d9c6c-2"}},"nodeMetas":{"e04d9c6c-0":{"id":"/src/index.ts","moduleParts":{"index.js":"e04d9c6c-1"},"imported":[{"uid":"e04d9c6c-4"},{"uid":"e04d9c6c-5"},{"uid":"e04d9c6c-6"},{"uid":"e04d9c6c-2","dynamic":true},{"uid":"e04d9c6c-7","dynamic":true}],"importedBy":[],"isEntry":true},"e04d9c6c-2":{"id":"/src/rocketstyle-collapse.ts","moduleParts":{"rocketstyle-collapse-C4eMAnwR.js":"e04d9c6c-3"},"imported":[{"uid":"e04d9c6c-8","dynamic":true}],"importedBy":[{"uid":"e04d9c6c-0"}]},"e04d9c6c-4":{"id":"node:fs","moduleParts":{},"imported":[],"importedBy":[{"uid":"e04d9c6c-0"}]},"e04d9c6c-5":{"id":"node:path","moduleParts":{},"imported":[],"importedBy":[{"uid":"e04d9c6c-0"}]},"e04d9c6c-6":{"id":"@pyreon/compiler","moduleParts":{},"imported":[],"importedBy":[{"uid":"e04d9c6c-0"}]},"e04d9c6c-7":{"id":"node:fs/promises","moduleParts":{},"imported":[],"importedBy":[{"uid":"e04d9c6c-0"}]},"e04d9c6c-8":{"id":"vite","moduleParts":{},"imported":[],"importedBy":[{"uid":"e04d9c6c-2"}]}},"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":"4f3120a0-1"}]},{"name":"rocketstyle-collapse-C4eMAnwR.js","children":[{"name":"src/rocketstyle-collapse.ts","uid":"4f3120a0-3"}]}],"isRoot":true},"nodeParts":{"4f3120a0-1":{"renderedLength":45599,"gzipLength":14790,"brotliLength":0,"metaUid":"4f3120a0-0"},"4f3120a0-3":{"renderedLength":3424,"gzipLength":1530,"brotliLength":0,"metaUid":"4f3120a0-2"}},"nodeMetas":{"4f3120a0-0":{"id":"/src/index.ts","moduleParts":{"index.js":"4f3120a0-1"},"imported":[{"uid":"4f3120a0-4"},{"uid":"4f3120a0-5"},{"uid":"4f3120a0-6"},{"uid":"4f3120a0-2","dynamic":true},{"uid":"4f3120a0-7","dynamic":true}],"importedBy":[],"isEntry":true},"4f3120a0-2":{"id":"/src/rocketstyle-collapse.ts","moduleParts":{"rocketstyle-collapse-C4eMAnwR.js":"4f3120a0-3"},"imported":[{"uid":"4f3120a0-8","dynamic":true}],"importedBy":[{"uid":"4f3120a0-0"}]},"4f3120a0-4":{"id":"node:fs","moduleParts":{},"imported":[],"importedBy":[{"uid":"4f3120a0-0"}]},"4f3120a0-5":{"id":"node:path","moduleParts":{},"imported":[],"importedBy":[{"uid":"4f3120a0-0"}]},"4f3120a0-6":{"id":"@pyreon/compiler","moduleParts":{},"imported":[],"importedBy":[{"uid":"4f3120a0-0"}]},"4f3120a0-7":{"id":"node:fs/promises","moduleParts":{},"imported":[],"importedBy":[{"uid":"4f3120a0-0"}]},"4f3120a0-8":{"id":"vite","moduleParts":{},"imported":[],"importedBy":[{"uid":"4f3120a0-2"}]}},"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
@@ -236,6 +236,7 @@ function pyreonPlugin(options) {
236
236
  const pyreonExclude = scanPyreonDeps(projectRoot);
237
237
  return {
238
238
  resolve: { conditions: ["bun"] },
239
+ ssr: { noExternal: [/@pyreon\//] },
239
240
  optimizeDeps: { exclude: Array.from(new Set([...compatExclude, ...pyreonExclude])) },
240
241
  oxc: { jsx: {
241
242
  runtime: "automatic",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyreon/vite-plugin",
3
- "version": "0.24.4",
3
+ "version": "0.24.5",
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.24.4"
46
+ "@pyreon/compiler": "^0.24.5"
47
47
  },
48
48
  "devDependencies": {
49
49
  "vite": "^8.0.0"
package/src/index.ts CHANGED
@@ -504,6 +504,28 @@ export default function pyreonPlugin(options?: PyreonPluginOptions): Plugin {
504
504
  // Use "bun" condition for workspace resolution — source .ts/.tsx files
505
505
  // for HMR, fast refresh, and type-safe imports.
506
506
  resolve: { conditions: ['bun'] },
507
+ // Force every `@pyreon/*` package through Vite's transform pipeline
508
+ // for SSR. Without this, Vite externalizes some `@pyreon/*` packages
509
+ // (loads via Node's `import()`) while transforming others — producing
510
+ // TWO module instances of `@pyreon/core` (one at `lib/index.js`, one
511
+ // at `src/index.ts` via the `bun` condition). The two instances have
512
+ // SEPARATE `_current` lifecycle state, so `runWithHooks` sets
513
+ // `_current` on instance A while `provide()` reads `_current` from
514
+ // instance B → null → `provide() outside setup` warning storm.
515
+ //
516
+ // Real-app symptom (bokisch.com dev-404 SSR, 0.24.4): 17 spurious
517
+ // `[Pyreon] onUnmount() called outside component setup` warnings
518
+ // per unmatched URL hit, even though every `provide()` IS structurally
519
+ // inside a `runWithHooks` setup window. Fix is purely a Vite
520
+ // module-graph reconciliation; no runtime behavior change.
521
+ //
522
+ // The regex `/@pyreon\//` matches every framework package + every
523
+ // user-side `@pyreon/*` import. Internal `@pyreon/*` resolution
524
+ // chains (zero → runtime-server → core; user `_layout.tsx` →
525
+ // ui-core → core) all converge on the same module instance.
526
+ ssr: {
527
+ noExternal: [/@pyreon\//],
528
+ },
507
529
  optimizeDeps: {
508
530
  exclude: optimizeDepsExclude,
509
531
  },
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Regression lock: `@pyreon/vite-plugin` must set
3
+ * `ssr.noExternal: [/@pyreon\//]` so every framework package goes through
4
+ * Vite's transform pipeline. Without this, Vite externalizes some
5
+ * `@pyreon/*` packages (loads via Node's `import()`) while transforming
6
+ * others — producing TWO module instances of `@pyreon/core` (one at
7
+ * `lib/index.js` via `import` condition, one at `src/index.ts` via the
8
+ * `bun` condition). The two instances have SEPARATE `_current` lifecycle
9
+ * state → `provide()` outside setup warning storm.
10
+ *
11
+ * Real-app symptom (bokisch.com dev-404 SSR, 0.24.4): 17 spurious
12
+ * `[Pyreon] onUnmount() called outside component setup` warnings per
13
+ * unmatched URL hit, even though every `provide()` IS structurally
14
+ * inside a `runWithHooks` setup window.
15
+ *
16
+ * Reproduction trace (the differential):
17
+ * at onUnmount (.../core/lib/index.js:68) ← LIB
18
+ * at provide (.../core/lib/index.js:427) ← LIB
19
+ * at HeadProvider (.../head/lib/provider.js:44) ← LIB
20
+ * at runWithHooks (.../core/src/component.ts:34) ← SRC ❗
21
+ * at renderComponent (.../runtime-server/lib/index.js:308)
22
+ *
23
+ * `head/lib` and `runtime-server/lib` resolve `@pyreon/core` via
24
+ * different Vite paths — `head` lands at `lib/`, `runtime-server` lands
25
+ * at `src/`. Two `_current` variables, two `setCurrentHooks` slots, the
26
+ * one that gets set is NOT the one `provide()` reads from.
27
+ *
28
+ * Bisect-verified: removing `ssr.noExternal` from `vite-plugin`'s
29
+ * `config()` return → bokisch reproduces 17 warnings on `/xyzzy-404`.
30
+ * Restored → 1 (the residual is a separate `useWindowResize` bug class).
31
+ */
32
+ import pyreon from '@pyreon/vite-plugin'
33
+ import { describe, expect, it } from 'vitest'
34
+
35
+ describe('@pyreon/vite-plugin — ssr.noExternal regression lock', () => {
36
+ it('config() return includes ssr.noExternal matching @pyreon/* via regex', () => {
37
+ const plugin = pyreon()
38
+ const cfg = (plugin as { config: (u: unknown, e: unknown) => unknown }).config(
39
+ { root: process.cwd() },
40
+ { command: 'serve' as const, mode: 'development', isPreview: false, isSsrBuild: false },
41
+ ) as { ssr?: { noExternal?: unknown } }
42
+
43
+ expect(cfg.ssr).toBeDefined()
44
+ expect(cfg.ssr?.noExternal).toBeDefined()
45
+ expect(Array.isArray(cfg.ssr?.noExternal)).toBe(true)
46
+
47
+ const arr = cfg.ssr?.noExternal as readonly (string | RegExp)[]
48
+ expect(arr.length).toBeGreaterThan(0)
49
+
50
+ // The regex must match every @pyreon/* package name. Without this,
51
+ // Vite externalizes some packages and the module-instance duplication
52
+ // bug returns.
53
+ const matches = (name: string) =>
54
+ arr.some((entry) => (entry instanceof RegExp ? entry.test(name) : entry === name))
55
+
56
+ expect(matches('@pyreon/core'), '@pyreon/core must be noExternal').toBe(true)
57
+ expect(matches('@pyreon/runtime-server'), '@pyreon/runtime-server must be noExternal').toBe(true)
58
+ expect(matches('@pyreon/router'), '@pyreon/router must be noExternal').toBe(true)
59
+ expect(matches('@pyreon/head'), '@pyreon/head must be noExternal').toBe(true)
60
+ expect(matches('@pyreon/ui-core'), '@pyreon/ui-core must be noExternal').toBe(true)
61
+ expect(matches('@pyreon/styler'), '@pyreon/styler must be noExternal').toBe(true)
62
+ expect(matches('@pyreon/elements'), '@pyreon/elements must be noExternal').toBe(true)
63
+ expect(matches('@pyreon/rocketstyle'), '@pyreon/rocketstyle must be noExternal').toBe(true)
64
+ // Hypothetical third-party @pyreon package — the regex should match too.
65
+ expect(matches('@pyreon/anything-new'), 'regex must match future packages').toBe(true)
66
+ })
67
+
68
+ it('regex does NOT match non-@pyreon packages', () => {
69
+ const plugin = pyreon()
70
+ const cfg = (plugin as { config: (u: unknown, e: unknown) => unknown }).config(
71
+ { root: process.cwd() },
72
+ { command: 'serve' as const, mode: 'development', isPreview: false, isSsrBuild: false },
73
+ ) as { ssr?: { noExternal?: readonly (string | RegExp)[] } }
74
+ const arr = cfg.ssr?.noExternal ?? []
75
+ const matches = (name: string) =>
76
+ arr.some((entry) => (entry instanceof RegExp ? entry.test(name) : entry === name))
77
+
78
+ expect(matches('react'), 'react must not be noExternal').toBe(false)
79
+ expect(matches('vite'), 'vite must not be noExternal').toBe(false)
80
+ expect(matches('pyreon'), 'unscoped pyreon must not be noExternal').toBe(false)
81
+ })
82
+ })