@pyreon/vite-plugin 0.24.5 → 0.25.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":"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}};
5389
+ const data = {"version":2,"tree":{"name":"root","children":[{"name":"index.js","children":[{"name":"src/index.ts","uid":"3bd95fb3-1"}]},{"name":"rocketstyle-collapse-DGnwgDhC.js","children":[{"name":"src/rocketstyle-collapse.ts","uid":"3bd95fb3-3"}]}],"isRoot":true},"nodeParts":{"3bd95fb3-1":{"renderedLength":48036,"gzipLength":15642,"brotliLength":0,"metaUid":"3bd95fb3-0"},"3bd95fb3-3":{"renderedLength":4657,"gzipLength":2132,"brotliLength":0,"metaUid":"3bd95fb3-2"}},"nodeMetas":{"3bd95fb3-0":{"id":"/src/index.ts","moduleParts":{"index.js":"3bd95fb3-1"},"imported":[{"uid":"3bd95fb3-4"},{"uid":"3bd95fb3-5"},{"uid":"3bd95fb3-6"},{"uid":"3bd95fb3-2","dynamic":true},{"uid":"3bd95fb3-7","dynamic":true}],"importedBy":[],"isEntry":true},"3bd95fb3-2":{"id":"/src/rocketstyle-collapse.ts","moduleParts":{"rocketstyle-collapse-DGnwgDhC.js":"3bd95fb3-3"},"imported":[{"uid":"3bd95fb3-8"},{"uid":"3bd95fb3-9","dynamic":true}],"importedBy":[{"uid":"3bd95fb3-0"}]},"3bd95fb3-4":{"id":"node:fs","moduleParts":{},"imported":[],"importedBy":[{"uid":"3bd95fb3-0"}]},"3bd95fb3-5":{"id":"node:path","moduleParts":{},"imported":[],"importedBy":[{"uid":"3bd95fb3-0"}]},"3bd95fb3-6":{"id":"@pyreon/compiler","moduleParts":{},"imported":[],"importedBy":[{"uid":"3bd95fb3-0"}]},"3bd95fb3-7":{"id":"node:fs/promises","moduleParts":{},"imported":[],"importedBy":[{"uid":"3bd95fb3-0"}]},"3bd95fb3-8":{"id":"@pyreon/reactivity","moduleParts":{},"imported":[],"importedBy":[{"uid":"3bd95fb3-2"}]},"3bd95fb3-9":{"id":"vite","moduleParts":{},"imported":[],"importedBy":[{"uid":"3bd95fb3-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
@@ -40,7 +40,7 @@ const __DEV__ = typeof process !== "undefined" && process.env.NODE_ENV !== "prod
40
40
  const _countSink = globalThis;
41
41
  let _createCollapseResolver = null;
42
42
  async function loadCreateCollapseResolver() {
43
- if (!_createCollapseResolver) _createCollapseResolver = (await import("./rocketstyle-collapse-C4eMAnwR.js")).createCollapseResolver;
43
+ if (!_createCollapseResolver) _createCollapseResolver = (await import("./rocketstyle-collapse-DGnwgDhC.js")).createCollapseResolver;
44
44
  return _createCollapseResolver;
45
45
  }
46
46
  const HMR_RUNTIME_ID = "\0pyreon/hmr-runtime";
@@ -175,6 +175,56 @@ function scanPyreonDeps(root) {
175
175
  return [];
176
176
  }
177
177
  }
178
+ /**
179
+ * Walk up from `root` looking for the nearest `node_modules/@pyreon`
180
+ * directory, then return every subdirectory name as `@pyreon/<name>`.
181
+ *
182
+ * This is the TRANSITIVE @pyreon/* dependency list — direct + every
183
+ * indirect dep that any direct dep pulled in. Required for `resolve.dedupe`
184
+ * because the original `scanPyreonDeps()` reads `package.json` only and
185
+ * misses anything a direct dep transitively requires (a user with only
186
+ * `@pyreon/zero` declared transitively pulls @pyreon/core, @pyreon/router,
187
+ * @pyreon/runtime-dom, etc. — none of which appear in their package.json).
188
+ *
189
+ * Bun / npm / pnpm all create `node_modules/@pyreon/<name>` entries for
190
+ * every resolved version, so a filesystem walk gives the exact set the
191
+ * bundler will see. Returns an empty array if no `node_modules/@pyreon`
192
+ * directory is reachable (fresh project before `bun install`, etc.) —
193
+ * dedupe then has nothing to do, which is the correct degradation.
194
+ */
195
+ function scanPyreonDepsTransitive(root) {
196
+ let dir = root;
197
+ for (let i = 0; i < 10; i++) {
198
+ const candidate = join(dir, "node_modules", "@pyreon");
199
+ if (existsSync(candidate)) try {
200
+ return readdirSync(candidate, { withFileTypes: true }).filter((e) => e.isDirectory() || e.isSymbolicLink()).map((e) => `@pyreon/${e.name}`).sort();
201
+ } catch {
202
+ return [];
203
+ }
204
+ const parent = dirname(dir);
205
+ if (parent === dir) break;
206
+ dir = parent;
207
+ }
208
+ return [];
209
+ }
210
+ /**
211
+ * Truthy env-var parser. Accepts `1` / `true` / `yes` / `on`
212
+ * (case-insensitive). Returns `false` for `undefined`, empty string,
213
+ * `0`, `false`, `no`, `off`, or any unrecognized value.
214
+ *
215
+ * Exported via `_internal` for unit-testability — the previous strict
216
+ * `=== '1'` shape caught a user reporting `PYREON_DISABLE_DEDUPE=true`
217
+ * silently not working. Env-var hatches are usually reached for under
218
+ * stress; rejecting alternatives is exactly the wrong moment to be strict.
219
+ *
220
+ * @internal
221
+ */
222
+ function _isTruthyEnv(v) {
223
+ if (v === void 0) return false;
224
+ const lower = v.toLowerCase();
225
+ return lower === "1" || lower === "true" || lower === "yes" || lower === "on";
226
+ }
227
+ const isTruthyEnv = _isTruthyEnv;
178
228
  function pyreonPlugin(options) {
179
229
  const ssrConfig = options?.ssr;
180
230
  const compat = options?.compat;
@@ -234,13 +284,19 @@ function pyreonPlugin(options) {
234
284
  projectRoot = userConfig.root ?? process.cwd();
235
285
  const compatExclude = compat ? Object.keys(COMPAT_ALIASES[compat]) : [];
236
286
  const pyreonExclude = scanPyreonDeps(projectRoot);
287
+ const optimizeDepsExclude = Array.from(new Set([...compatExclude, ...pyreonExclude]));
288
+ const dedupeList = isTruthyEnv((typeof process !== "undefined" && process.env ? process.env : void 0)?.PYREON_DISABLE_DEDUPE) ? [] : scanPyreonDepsTransitive(projectRoot);
289
+ const jsxSource = "@pyreon/core";
237
290
  return {
238
- resolve: { conditions: ["bun"] },
291
+ resolve: {
292
+ conditions: ["bun"],
293
+ ...dedupeList.length > 0 ? { dedupe: dedupeList } : {}
294
+ },
239
295
  ssr: { noExternal: [/@pyreon\//] },
240
- optimizeDeps: { exclude: Array.from(new Set([...compatExclude, ...pyreonExclude])) },
296
+ optimizeDeps: { exclude: optimizeDepsExclude },
241
297
  oxc: { jsx: {
242
298
  runtime: "automatic",
243
- importSource: "@pyreon/core"
299
+ importSource: jsxSource
244
300
  } },
245
301
  ...env.isSsrBuild && ssrConfig ? { build: {
246
302
  ssr: true,
@@ -1243,5 +1299,5 @@ export function __hmr_dispose(moduleId) {
1243
1299
  `;
1244
1300
 
1245
1301
  //#endregion
1246
- export { _computeLineStarts, _maskStringsAndComments, _offsetToLineCol, buildLpihClientScript, pyreonPlugin as default, registerLpihMiddleware, resolveLpihCachePath, writeLpihCacheFile };
1302
+ export { _computeLineStarts, _isTruthyEnv, _maskStringsAndComments, _offsetToLineCol, buildLpihClientScript, pyreonPlugin as default, registerLpihMiddleware, resolveLpihCachePath, writeLpihCacheFile };
1247
1303
  //# sourceMappingURL=index.js.map
@@ -1,4 +1,29 @@
1
+ import { withSilent } from "@pyreon/reactivity";
2
+
1
3
  //#region src/rocketstyle-collapse.ts
4
+ /**
5
+ * P0 — build-time rocketstyle-collapse resolver.
6
+ *
7
+ * For a collapsible call site (`<Button state="primary" size="md">Save</Button>`
8
+ * — every dimension prop a string literal, children static text) this
9
+ * resolves the FULL rocketstyle/styler pipeline ONCE by SSR-rendering the
10
+ * REAL component, light AND dark, and returns: the resolved styler class
11
+ * per mode, the styler rule text, and a class-stripped `_tpl` template.
12
+ *
13
+ * The render runs through a programmatic Vite SSR server bound to the
14
+ * CONSUMER's own `vite.config` — so module resolution is identical to
15
+ * the app's real build (workspace `bun` condition, app aliases,
16
+ * app-local relative imports, whatever). Parity with the runtime-mounted
17
+ * class is then guaranteed BY CONSTRUCTION: it is literally the same
18
+ * `renderToString` + `@pyreon/styler` code path the client uses, and
19
+ * styler's FNV-1a class hashing is identical in SSR and DOM (styler's
20
+ * hydration contract). No reimplementation, no closure re-execution, no
21
+ * drift (RFC decision 2).
22
+ *
23
+ * Every failure returns `null` (graceful bail → the call site keeps its
24
+ * normal rocketstyle mount). Correct-but-slow is acceptable; wrong
25
+ * output is not.
26
+ */
2
27
  function fnv1a(str) {
3
28
  let h = 2166136261;
4
29
  for (let i = 0; i < str.length; i++) {
@@ -44,7 +69,7 @@ function deriveCollapse(lightHtml, darkHtml, rules) {
44
69
  */
45
70
  async function createCollapseResolver(projectRoot) {
46
71
  const { createServer } = await import("vite");
47
- let server = await createServer({
72
+ const inline = {
48
73
  root: projectRoot,
49
74
  server: { middlewareMode: true },
50
75
  appType: "custom",
@@ -53,10 +78,11 @@ async function createCollapseResolver(projectRoot) {
53
78
  noDiscovery: true,
54
79
  include: []
55
80
  }
56
- });
81
+ };
82
+ let server = await withSilent(() => createServer(inline));
57
83
  const cache = /* @__PURE__ */ new Map();
58
84
  async function load(spec) {
59
- return await server.ssrLoadModule(spec);
85
+ return withSilent(() => server.ssrLoadModule(spec));
60
86
  }
61
87
  return {
62
88
  async resolve(input) {
@@ -110,4 +136,4 @@ async function createCollapseResolver(projectRoot) {
110
136
 
111
137
  //#endregion
112
138
  export { createCollapseResolver };
113
- //# sourceMappingURL=rocketstyle-collapse-C4eMAnwR.js.map
139
+ //# sourceMappingURL=rocketstyle-collapse-DGnwgDhC.js.map
@@ -146,6 +146,19 @@ interface PyreonLpihOptions {
146
146
  */
147
147
  cachePath?: string;
148
148
  }
149
+ /**
150
+ * Truthy env-var parser. Accepts `1` / `true` / `yes` / `on`
151
+ * (case-insensitive). Returns `false` for `undefined`, empty string,
152
+ * `0`, `false`, `no`, `off`, or any unrecognized value.
153
+ *
154
+ * Exported via `_internal` for unit-testability — the previous strict
155
+ * `=== '1'` shape caught a user reporting `PYREON_DISABLE_DEDUPE=true`
156
+ * silently not working. Env-var hatches are usually reached for under
157
+ * stress; rejecting alternatives is exactly the wrong moment to be strict.
158
+ *
159
+ * @internal
160
+ */
161
+ declare function _isTruthyEnv(v: string | undefined): boolean;
149
162
  declare function pyreonPlugin(options?: PyreonPluginOptions): Plugin;
150
163
  /**
151
164
  * Resolve the LPIH cache-file path for a given project root. Matches the
@@ -231,5 +244,5 @@ declare function _offsetToLineCol(offset: number, lineStarts: number[]): {
231
244
  col: number;
232
245
  };
233
246
  //#endregion
234
- export { CompatFramework, PyreonCollapseOptions, PyreonLpihOptions, PyreonPluginOptions, _computeLineStarts, _maskStringsAndComments, _offsetToLineCol, buildLpihClientScript, pyreonPlugin as default, registerLpihMiddleware, resolveLpihCachePath, writeLpihCacheFile };
247
+ export { CompatFramework, PyreonCollapseOptions, PyreonLpihOptions, PyreonPluginOptions, _computeLineStarts, _isTruthyEnv, _maskStringsAndComments, _offsetToLineCol, buildLpihClientScript, pyreonPlugin as default, registerLpihMiddleware, resolveLpihCachePath, writeLpihCacheFile };
235
248
  //# sourceMappingURL=index2.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyreon/vite-plugin",
3
- "version": "0.24.5",
3
+ "version": "0.25.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": {
@@ -15,7 +15,6 @@
15
15
  "files": [
16
16
  "lib",
17
17
  "!lib/**/*.map",
18
- "src",
19
18
  "README.md",
20
19
  "LICENSE"
21
20
  ],
@@ -26,7 +25,6 @@
26
25
  "types": "./lib/types/index.d.ts",
27
26
  "exports": {
28
27
  ".": {
29
- "bun": "./src/index.ts",
30
28
  "import": "./lib/index.js",
31
29
  "types": "./lib/types/index.d.ts"
32
30
  }
@@ -43,7 +41,8 @@
43
41
  "prepublishOnly": "bun run build"
44
42
  },
45
43
  "dependencies": {
46
- "@pyreon/compiler": "^0.24.5"
44
+ "@pyreon/compiler": "^0.25.0",
45
+ "@pyreon/reactivity": "^0.25.0"
47
46
  },
48
47
  "devDependencies": {
49
48
  "vite": "^8.0.0"
@@ -1,92 +0,0 @@
1
- /**
2
- * Pyreon HMR Runtime — preserves signal state across hot module reloads.
3
- *
4
- * Served as a virtual module `\0pyreon/hmr-runtime` and injected into every
5
- * .tsx/.jsx module during development.
6
- *
7
- * ## How it works
8
- *
9
- * 1. The Vite plugin rewrites top-level `signal()` calls:
10
- * `const count = signal(0)` → `const count = __hmr_signal("src/App.tsx", "count", signal, 0)`
11
- *
12
- * 2. `__hmr_signal` checks a global registry for a saved value under the
13
- * composite key `moduleId + ":" + name`. If found, it creates the signal
14
- * with the preserved value instead of the initial one.
15
- *
16
- * 3. When `import.meta.hot.accept()` fires, a `dispose` callback saves every
17
- * registered signal's current value into the registry before the old module
18
- * is discarded.
19
- *
20
- * The registry lives on `globalThis` so it survives module re-execution.
21
- */
22
-
23
- interface SignalLike {
24
- peek(): unknown
25
- set(value: unknown): void
26
- }
27
-
28
- interface ModuleSignals {
29
- entries: Map<string, SignalLike>
30
- }
31
-
32
- const REGISTRY_KEY = '__pyreon_hmr_registry__'
33
-
34
- type Registry = Map<string, Map<string, unknown>>
35
-
36
- function getRegistry(): Registry {
37
- const g = globalThis as Record<string, unknown>
38
- if (!g[REGISTRY_KEY]) {
39
- g[REGISTRY_KEY] = new Map()
40
- }
41
- return g[REGISTRY_KEY] as Registry
42
- }
43
-
44
- const moduleSignals = new Map<string, ModuleSignals>()
45
-
46
- /**
47
- * Called in place of `signal(initialValue)` for module-scope signals.
48
- * Restores the previous value if the module is being hot-reloaded.
49
- */
50
- export function __hmr_signal<T>(
51
- moduleId: string,
52
- name: string,
53
- signalFn: (value: T, options?: { name?: string }) => SignalLike,
54
- initialValue: T,
55
- ): ReturnType<typeof signalFn> {
56
- const registry = getRegistry()
57
- const saved = registry.get(moduleId)
58
-
59
- // Use saved value if available (hot reload), otherwise use initial
60
- const value = saved?.has(name) ? (saved.get(name) as T) : initialValue
61
-
62
- const s = signalFn(value, { name })
63
-
64
- // Track this signal for future disposal
65
- let mod = moduleSignals.get(moduleId)
66
- if (!mod) {
67
- mod = { entries: new Map() }
68
- moduleSignals.set(moduleId, mod)
69
- }
70
- mod.entries.set(name, s)
71
-
72
- return s
73
- }
74
-
75
- /**
76
- * Called in the `import.meta.hot.dispose` callback.
77
- * Saves all registered signal values for the module before it is discarded.
78
- */
79
- export function __hmr_dispose(moduleId: string): void {
80
- const mod = moduleSignals.get(moduleId)
81
- if (!mod) return
82
-
83
- const registry = getRegistry()
84
- const saved = new Map<string, unknown>()
85
- for (const [name, s] of mod.entries) {
86
- saved.set(name, s.peek())
87
- }
88
- registry.set(moduleId, saved)
89
-
90
- // Clear entries so the new module can re-register
91
- moduleSignals.delete(moduleId)
92
- }