@pyreon/vite-plugin 0.25.1 → 0.26.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.
@@ -81,49 +81,68 @@ async function createCollapseResolver(projectRoot) {
81
81
  };
82
82
  let server = await withSilent(() => createServer(inline));
83
83
  const cache = /* @__PURE__ */ new Map();
84
+ let resolveChain = Promise.resolve();
84
85
  async function load(spec) {
85
86
  return withSilent(() => server.ssrLoadModule(spec));
86
87
  }
87
- return {
88
- async resolve(input) {
89
- const ck = JSON.stringify([
90
- input.component,
91
- input.props,
92
- input.childrenText,
93
- input.config.provider,
94
- input.config.theme
95
- ]);
96
- if (cache.has(ck)) return cache.get(ck) ?? null;
97
- try {
98
- if (!server) return null;
99
- const rs = await load("@pyreon/runtime-server");
100
- const core = await load("@pyreon/core");
101
- const styler = await load("@pyreon/styler");
102
- const prov = await load(input.config.provider.source);
103
- const thm = await load(input.config.theme.source);
104
- const comp = await load(input.component.source);
105
- const renderToString = rs.renderToString;
106
- const h = core.h;
107
- const Provider = prov[input.config.provider.name];
108
- const themeVal = thm[input.config.theme.name];
109
- const Component = comp[input.component.name];
110
- const sheet = styler.sheet;
111
- if (typeof Component !== "function" || Provider == null || themeVal == null) {
112
- cache.set(ck, null);
113
- return null;
114
- }
115
- const childArgs = input.childrenText ? [input.childrenText] : [];
116
- const node = (mode) => h(Provider, {
117
- theme: themeVal,
118
- mode
119
- }, h(Component, input.props, ...childArgs));
120
- const result = deriveCollapse(await renderToString(node("light")), await renderToString(node("dark")), sheet.getStyleRules().slice());
121
- cache.set(ck, result);
122
- return result;
123
- } catch {
88
+ /**
89
+ * Rebuild real child VNodes from a static child subtree via `h()`.
90
+ * `tag` is a lowercase DOM string (component children never reach
91
+ * here — the detector bails on them), `props` are string literals,
92
+ * and children are text strings or nested static elements. The
93
+ * resulting VNodes render byte-faithfully to a real mount because the
94
+ * tree was produced with the compiler's own JSX-text normalization.
95
+ */
96
+ function buildChildVNodes(tree, h) {
97
+ return tree.map((c) => typeof c === "string" ? c : h(c.tag, c.props, ...buildChildVNodes(c.children, h)));
98
+ }
99
+ async function doResolve(input) {
100
+ const ck = JSON.stringify([
101
+ input.component,
102
+ input.props,
103
+ input.childrenText,
104
+ input.childTree ?? null,
105
+ input.config.provider,
106
+ input.config.theme
107
+ ]);
108
+ if (cache.has(ck)) return cache.get(ck) ?? null;
109
+ try {
110
+ if (!server) return null;
111
+ const rs = await load("@pyreon/runtime-server");
112
+ const core = await load("@pyreon/core");
113
+ const styler = await load("@pyreon/styler");
114
+ const prov = await load(input.config.provider.source);
115
+ const thm = await load(input.config.theme.source);
116
+ const comp = await load(input.component.source);
117
+ const renderToString = rs.renderToString;
118
+ const h = core.h;
119
+ const Provider = prov[input.config.provider.name];
120
+ const themeVal = thm[input.config.theme.name];
121
+ const Component = comp[input.component.name];
122
+ const sheet = styler.sheet;
123
+ if (typeof Component !== "function" || Provider == null || themeVal == null) {
124
124
  cache.set(ck, null);
125
125
  return null;
126
126
  }
127
+ sheet.resetSSRBuffer();
128
+ const childArgs = input.childTree ? buildChildVNodes(input.childTree, h) : input.childrenText ? [input.childrenText] : [];
129
+ const node = (mode) => h(Provider, {
130
+ theme: themeVal,
131
+ mode
132
+ }, h(Component, input.props, ...childArgs));
133
+ const result = deriveCollapse(await renderToString(node("light")), await renderToString(node("dark")), sheet.getStyleRules().slice());
134
+ cache.set(ck, result);
135
+ return result;
136
+ } catch {
137
+ cache.set(ck, null);
138
+ return null;
139
+ }
140
+ }
141
+ return {
142
+ resolve(input) {
143
+ const next = resolveChain.then(() => doResolve(input), () => doResolve(input));
144
+ resolveChain = next;
145
+ return next;
127
146
  },
128
147
  async dispose() {
129
148
  const s = server;
@@ -136,4 +155,4 @@ async function createCollapseResolver(projectRoot) {
136
155
 
137
156
  //#endregion
138
157
  export { createCollapseResolver };
139
- //# sourceMappingURL=rocketstyle-collapse-DGnwgDhC.js.map
158
+ //# sourceMappingURL=rocketstyle-collapse-XIiHU85Y.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rocketstyle-collapse-XIiHU85Y.js","names":[],"sources":["../src/rocketstyle-collapse.ts"],"sourcesContent":["/**\n * P0 — build-time rocketstyle-collapse resolver.\n *\n * For a collapsible call site (`<Button state=\"primary\" size=\"md\">Save</Button>`\n * — every dimension prop a string literal, children static text) this\n * resolves the FULL rocketstyle/styler pipeline ONCE by SSR-rendering the\n * REAL component, light AND dark, and returns: the resolved styler class\n * per mode, the styler rule text, and a class-stripped `_tpl` template.\n *\n * The render runs through a programmatic Vite SSR server bound to the\n * CONSUMER's own `vite.config` — so module resolution is identical to\n * the app's real build (workspace `bun` condition, app aliases,\n * app-local relative imports, whatever). Parity with the runtime-mounted\n * class is then guaranteed BY CONSTRUCTION: it is literally the same\n * `renderToString` + `@pyreon/styler` code path the client uses, and\n * styler's FNV-1a class hashing is identical in SSR and DOM (styler's\n * hydration contract). No reimplementation, no closure re-execution, no\n * drift (RFC decision 2).\n *\n * Every failure returns `null` (graceful bail → the call site keeps its\n * normal rocketstyle mount). Correct-but-slow is acceptable; wrong\n * output is not.\n */\nimport { withSilent } from '@pyreon/reactivity'\nimport type { StaticChild } from '@pyreon/compiler'\nimport type { InlineConfig, ViteDevServer } from 'vite'\n\n// Inline FNV-1a (same algorithm as @pyreon/styler/hash) — avoids pulling\n// the styler module graph into the vite-plugin's cheap entry path.\nfunction fnv1a(str: string): string {\n let h = 2166136261\n for (let i = 0; i < str.length; i++) {\n h ^= str.charCodeAt(i)\n h = Math.imul(h, 16777619)\n }\n return (h >>> 0).toString(36)\n}\n\nexport interface CollapseImportSpec {\n /** Imported binding name, e.g. `PyreonUI` / `theme` / `useMode`. */\n name: string\n /** Module specifier, e.g. `@pyreon/ui-core` / `@pyreon/ui-theme`. */\n source: string\n}\n\nexport interface CollapseConfig {\n /** Theme/mode provider component. Default: PyreonUI from @pyreon/ui-core. */\n provider: CollapseImportSpec\n /** Theme object. Default: theme from @pyreon/ui-theme. */\n theme: CollapseImportSpec\n /** Live mode accessor — emitted into the collapsed site for dual-emit. */\n mode: CollapseImportSpec\n}\n\nexport const DEFAULT_COLLAPSE_CONFIG: CollapseConfig = {\n provider: { name: 'PyreonUI', source: '@pyreon/ui-core' },\n theme: { name: 'theme', source: '@pyreon/ui-theme' },\n mode: { name: 'useMode', source: '@pyreon/ui-core' },\n}\n\nexport interface ResolveInput {\n /** The collapsible component's import. */\n component: CollapseImportSpec\n /** Literal dimension/HTML props, e.g. `{ state: 'primary', size: 'md' }`. */\n props: Record<string, string>\n /** Static text children (empty ⇒ no children). For element-child sites\n * this is the `serializeStaticChildren` key-string (cache discriminator\n * only — the actual children come from `childTree`). */\n childrenText: string\n /** Element-child sites only: the recursively-static child subtree.\n * When present, the resolver rebuilds it via `h()` and renders the real\n * child VNodes instead of `childrenText` as a string (PR 2). */\n childTree?: StaticChild[]\n config: CollapseConfig\n}\n\nexport interface ResolvedCollapse {\n /** Element HTML with the root `class=\"...\"` removed (the `_tpl` template). */\n templateHtml: string\n lightClass: string\n darkClass: string\n /** Pre-resolved styler rule text (full snapshot) for `injectRules`. */\n rules: string[]\n /** FNV over the rule set — `injectRules` idempotency + cross-site dedupe. */\n key: string\n}\n\nconst FIRST_CLASS_RE = /^(\\s*<[a-zA-Z][\\w-]*)([^>]*?)\\sclass=\"([^\"]*)\"([^>]*>)/\n\n/** Strip the FIRST element's `class=\"...\"`, returning [stripped, class]. */\nexport function stripRootClass(html: string): { stripped: string; cls: string } | null {\n const m = FIRST_CLASS_RE.exec(html)\n if (!m) return null\n const stripped = html.replace(FIRST_CLASS_RE, '$1$2$4')\n return { stripped, cls: m[3] ?? '' }\n}\n\n/**\n * Pure extraction half — given the two rendered HTML strings and the\n * styler rule snapshot, derive the ResolvedCollapse (or null on a shape\n * the slice doesn't collapse). Separated for direct unit-testing without\n * spinning Vite.\n */\nexport function deriveCollapse(\n lightHtml: string,\n darkHtml: string,\n rules: string[],\n): ResolvedCollapse | null {\n const light = stripRootClass(lightHtml)\n const dark = stripRootClass(darkHtml)\n if (!light || !dark || !light.cls || !dark.cls) return null\n // The structural template must be identical between modes (only the\n // class differs). Divergent markup ⇒ not a simple single-root\n // collapsible — bail.\n if (light.stripped !== dark.stripped) return null\n return {\n templateHtml: light.stripped.trim(),\n lightClass: light.cls,\n darkClass: dark.cls,\n rules,\n key: fnv1a(rules.join('\\u0000')),\n }\n}\n\nexport interface CollapseResolver {\n resolve(input: ResolveInput): Promise<ResolvedCollapse | null>\n dispose(): Promise<void>\n}\n\n/**\n * Create a resolver backed by ONE programmatic Vite SSR server bound to\n * `projectRoot`'s vite config. Reused across every call site in a build;\n * `dispose()` at buildEnd. Module loads are cached by Vite's own SSR\n * module graph (provider/theme/component import once).\n */\nexport async function createCollapseResolver(projectRoot: string): Promise<CollapseResolver> {\n const { createServer } = (await import('vite')) as typeof import('vite')\n const inline: InlineConfig = {\n // No `configFile` override — Vite auto-loads the project's own\n // vite.config from `root`, so module resolution (workspace `bun`\n // condition, app aliases) matches the real build exactly.\n root: projectRoot,\n server: { middlewareMode: true },\n appType: 'custom',\n logLevel: 'silent',\n optimizeDeps: { noDiscovery: true, include: [] },\n }\n // `createServer` evaluates the consumer's vite.config + plugin chain,\n // which may itself load `@pyreon/*` packages via the `node` condition\n // (different path than the outer process's `bun`-conditioned `src/`).\n // That's a legitimate dual-load — scope the opt-out across the entire\n // server lifecycle since every `load(spec)` below also touches the\n // dual graph.\n let server: ViteDevServer | null = await withSilent(() => createServer(inline))\n\n // Resolved-bundle cache — identical input must hit the same result\n // without a second double-render (deterministic by construction).\n const cache = new Map<string, ResolvedCollapse | null>()\n\n // Serialize all `resolve()` calls. The Vite `transform` hook runs in\n // parallel across files (audit #7), so two concurrent `resolve()` calls\n // would interleave their renderToString invocations against the SAME\n // singleton styler sheet — A's rules pollute B's `getStyleRules()` slice\n // and vice versa. The chain forces strict FIFO ordering; combined with\n // the `sheet.resetSSRBuffer()` call at the head of every `doResolve`\n // (audit #8), each render-pair sees a fresh capture buffer.\n // `.then(success, failure)` pair: a single rejected resolve must not\n // poison the chain for subsequent calls.\n let resolveChain: Promise<unknown> = Promise.resolve()\n\n async function load(spec: string): Promise<Record<string, unknown>> {\n // The nested Vite SSR server loads its own copy of @pyreon/* packages for\n // the SSR snapshot. This is a legitimate dual-load — the outer process has\n // its own @pyreon/* graph; the nested server has its own. `withSilent`\n // from @pyreon/reactivity scopes the sentinel opt-out via a refcount\n // (race-safe under concurrency; the prior env-var dance leaked `silent`\n // permanently when N opt-out scopes overlapped — see withSilent JSDoc).\n return withSilent(\n () => server!.ssrLoadModule(spec) as Promise<Record<string, unknown>>,\n )\n }\n\n /**\n * Rebuild real child VNodes from a static child subtree via `h()`.\n * `tag` is a lowercase DOM string (component children never reach\n * here — the detector bails on them), `props` are string literals,\n * and children are text strings or nested static elements. The\n * resulting VNodes render byte-faithfully to a real mount because the\n * tree was produced with the compiler's own JSX-text normalization.\n */\n function buildChildVNodes(\n tree: StaticChild[],\n h: (t: unknown, p: unknown, ...c: unknown[]) => unknown,\n ): unknown[] {\n return tree.map((c) =>\n typeof c === 'string' ? c : h(c.tag, c.props, ...buildChildVNodes(c.children, h)),\n )\n }\n\n async function doResolve(input: ResolveInput): Promise<ResolvedCollapse | null> {\n const ck = JSON.stringify([\n input.component,\n input.props,\n input.childrenText,\n input.childTree ?? null,\n input.config.provider,\n input.config.theme,\n ])\n // Cache-hit fast path: short-circuit BEFORE touching the sheet, so\n // repeat lookups don't clobber an in-flight neighbouring resolve's\n // buffer. The chain serialization already guarantees ordering for\n // in-flight calls; a cache hit is observably free either way.\n if (cache.has(ck)) return cache.get(ck) ?? null\n try {\n if (!server) return null\n const rs = await load('@pyreon/runtime-server')\n const core = await load('@pyreon/core')\n const styler = await load('@pyreon/styler')\n const prov = await load(input.config.provider.source)\n const thm = await load(input.config.theme.source)\n const comp = await load(input.component.source)\n const renderToString = rs.renderToString as (n: unknown) => Promise<string>\n const h = core.h as (t: unknown, p: unknown, ...c: unknown[]) => unknown\n const Provider = prov[input.config.provider.name]\n const themeVal = thm[input.config.theme.name]\n const Component = comp[input.component.name]\n const sheet = styler.sheet as {\n getStyleRules(): readonly string[]\n resetSSRBuffer(): void\n }\n if (typeof Component !== 'function' || Provider == null || themeVal == null) {\n cache.set(ck, null)\n return null\n }\n // Reset the SSR rule-capture buffer IMMEDIATELY BEFORE the render\n // pair (audit #8). Sheet identity invariant: `ssrLoadModule` caches\n // by spec, so every `load('@pyreon/styler')` returns the SAME module\n // namespace and hence the SAME singleton sheet — reset + getStyleRules\n // target the same instance by construction. The chain serialization\n // (`resolveChain` above) ensures no other in-flight `doResolve` can\n // append between this reset and the `getStyleRules()` capture below.\n sheet.resetSSRBuffer()\n // Element-child sites carry a structured `childTree` — rebuild\n // the real child VNodes via `h()` so the SSR render bakes the\n // full subtree HTML (byte-faithful to a real mount because the\n // tree was normalized with the compiler's own `cleanJsxText`).\n // Full / dynamic sites use `childrenText` as a plain string.\n const childArgs: unknown[] = input.childTree\n ? buildChildVNodes(input.childTree, h)\n : input.childrenText\n ? [input.childrenText]\n : []\n const node = (mode: string) =>\n h(Provider, { theme: themeVal, mode }, h(Component, input.props, ...childArgs))\n const lightHtml = await renderToString(node('light'))\n const darkHtml = await renderToString(node('dark'))\n const rules = sheet.getStyleRules().slice()\n const result = deriveCollapse(lightHtml, darkHtml, rules)\n cache.set(ck, result)\n return result\n } catch {\n cache.set(ck, null)\n return null\n }\n }\n\n return {\n resolve(input) {\n // Chain every call through `resolveChain` so the resets + renders\n // run in strict FIFO. `.then(success, failure)` pair: an upstream\n // rejection must not poison the chain — both branches route through\n // a fresh `doResolve`, so the next caller always gets a real result.\n const next = resolveChain.then(\n () => doResolve(input),\n () => doResolve(input),\n )\n resolveChain = next\n return next\n },\n async dispose() {\n const s = server\n server = null\n cache.clear()\n if (s) await s.close()\n },\n }\n}\n"],"mappings":"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"}
@@ -95,6 +95,68 @@ interface PyreonPluginOptions {
95
95
  * @example pyreon({ collapse: { components: ['Button', 'Badge'] } })
96
96
  */
97
97
  collapse?: boolean | PyreonCollapseOptions;
98
+ /**
99
+ * **JSX auto-import for canonical primitives** — closes the Phase D2
100
+ * gap toward "literally same .tsx file across all three targets".
101
+ *
102
+ * When `true` (the default), scans each `.tsx` file for bare JSX
103
+ * references to canonical primitives (`<Stack>`, `<Inline>`, `<Text>`,
104
+ * `<Button>`, `<Press>`, `<Field>`, `<Toggle>`) and auto-injects
105
+ * `import { ... } from '@pyreon/primitives'` at the top of the file
106
+ * for every primitive used but not yet imported.
107
+ *
108
+ * **Why this matters**: the PMTC native compiler resolves bare JSX
109
+ * tags via its canonical-primitives table at compile time + emits
110
+ * SwiftUI/Kotlin directly — imports would be no-ops on native targets
111
+ * (treated as type-only). So the native source stays import-free for
112
+ * minimal surface. The web build needs the imports for symbol
113
+ * resolution. This auto-import lets ONE `.tsx` file work on all three
114
+ * targets without manual import maintenance.
115
+ *
116
+ * Already-imported names pass through untouched (the plugin diffs the
117
+ * existing imports against the used set before injecting). Components
118
+ * with the same name imported from another source (e.g. a user-defined
119
+ * `<Button>` from a local file) take precedence — the auto-import
120
+ * only fires when the name is used + NOT already in scope.
121
+ *
122
+ * Defaults to `true`. Set to `false` to opt out (e.g. if your
123
+ * project defines its own primitives or you want explicit imports
124
+ * throughout). The scan is regex-based and runs only on `.tsx`
125
+ * files; cost is negligible vs the JSX transform itself.
126
+ *
127
+ * @example pyreon({ jsxAutoImport: true })
128
+ * @example pyreon({ jsxAutoImport: false })
129
+ * @example pyreon({ jsxAutoImport: { source: '@my/primitives', names: ['Box', 'Row'] } })
130
+ */
131
+ jsxAutoImport?: boolean | PyreonJsxAutoImportOptions;
132
+ }
133
+ /**
134
+ * Override the source / name mappings for the JSX auto-import pass.
135
+ * Default: imports the 7 implemented canonical primitives (`Stack` /
136
+ * `Inline` / `Text` / `Button` / `Press` / `Field` / `Toggle`) from
137
+ * `@pyreon/primitives` AND the 2 most-used control-flow helpers
138
+ * (`For` / `Show`) from `@pyreon/core` — so a TSX source written for
139
+ * native (zero imports — PMTC resolves bare tags) also runs on web
140
+ * with no manual imports.
141
+ *
142
+ * Pass a custom mappings array to apply the same mechanism to a
143
+ * different primitive library, OR to extend the default set with
144
+ * project-specific primitives.
145
+ */
146
+ interface PyreonJsxAutoImportOptions {
147
+ /**
148
+ * Source → names mappings. Each entry says "auto-import these names
149
+ * from this source". Order matters: the first mapping that includes
150
+ * a used name wins.
151
+ */
152
+ mappings?: Array<{
153
+ source: string;
154
+ names: string[];
155
+ }>;
156
+ /** @deprecated use `mappings` instead. Kept for back-compat. */
157
+ source?: string;
158
+ /** @deprecated use `mappings` instead. Kept for back-compat. */
159
+ names?: string[];
98
160
  }
99
161
  interface PyreonCollapseOptions {
100
162
  /**
@@ -146,6 +208,40 @@ interface PyreonLpihOptions {
146
208
  */
147
209
  cachePath?: string;
148
210
  }
211
+ /**
212
+ * Detect whether a file id resolves to a `@pyreon/*` framework-package source
213
+ * (i.e. a published Pyreon package whose .tsx is being pulled in via the
214
+ * `bun` condition workspace-link, NOT user code, NOT an example app).
215
+ *
216
+ * Why this exists: in compat mode, OXC's per-project `importSource` is set
217
+ * to `@pyreon/core` and the resolveId hook redirects `@pyreon/core/jsx-runtime`
218
+ * to the compat package. That's correct for user code (the whole point of
219
+ * compat mode) but WRONG for framework-internal sources like
220
+ * `@pyreon/zero/src/link.tsx`, which need the real `@pyreon/core` runtime.
221
+ * The fix skips the redirect when the importer is a `@pyreon/*` framework
222
+ * file. Result: published-package consumers (where `@pyreon/zero` resolves
223
+ * to its pre-built `lib/`) and workspace-dev consumers (where it resolves
224
+ * to source) both get correct JSX runtime resolution.
225
+ *
226
+ * Detection heuristic: walk to nearest `package.json`, require BOTH:
227
+ * 1. `name` starts with `@pyreon/` (workspace member of the @pyreon scope)
228
+ * 2. file path contains `/packages/` AND NOT `/examples/`
229
+ *
230
+ * Step 2 excludes the existing `@pyreon/example-{react,vue,solid,preact}-compat`
231
+ * apps under `examples/`. Without it, user code in those apps would skip the
232
+ * compat-mode JSX-runtime redirect and import `@pyreon/core/jsx-runtime`
233
+ * directly — breaking the React/Vue/Solid/Preact compat layer's contract.
234
+ *
235
+ * Result cached per directory. The `/packages/` + `/examples/` check is a
236
+ * structural property of the monorepo (workspace layout), not the package
237
+ * name — so it's robust against renames.
238
+ */
239
+ declare function _isPyreonWorkspaceFile(id: string, cache: Map<string, boolean>): boolean;
240
+ /**
241
+ * Return the Pyreon compat target for an import specifier, or undefined if
242
+ * the import should not be redirected.
243
+ */
244
+ declare function _getCompatTarget(compat: CompatFramework | undefined, id: string): string | undefined;
149
245
  /**
150
246
  * Truthy env-var parser. Accepts `1` / `true` / `yes` / `on`
151
247
  * (case-insensitive). Returns `false` for `undefined`, empty string,
@@ -159,7 +255,8 @@ interface PyreonLpihOptions {
159
255
  * @internal
160
256
  */
161
257
  declare function _isTruthyEnv(v: string | undefined): boolean;
162
- declare function pyreonPlugin(options?: PyreonPluginOptions): Plugin;
258
+ declare function pyreonPlugin(options?: PyreonPluginOptions): Plugin<any>;
259
+ declare function _handleSsrRequest(server: ViteDevServer, entry: string, url: string, req: import('node:http').IncomingMessage, res: import('node:http').ServerResponse, next: (err?: unknown) => void): Promise<void>;
163
260
  /**
164
261
  * Resolve the LPIH cache-file path for a given project root. Matches the
165
262
  * convention `@pyreon/reactivity/lpih`'s `getDefaultLpihCachePath()` uses
@@ -201,6 +298,8 @@ declare function writeLpihCacheFile(path: string, body: string): Promise<void>;
201
298
  * @internal — exported for tests.
202
299
  */
203
300
  declare function buildLpihClientScript(intervalMs: number): string;
301
+ declare function _skipStringLiteral(code: string, start: number, quote: string): number;
302
+ declare function _extractBalancedArgs(code: string, start: number): string | null;
204
303
  /**
205
304
  * Mask string-literal / template-literal / comment regions in `code` by
206
305
  * replacing their content with spaces. Returns a SAME-LENGTH string so
@@ -243,6 +342,28 @@ declare function _offsetToLineCol(offset: number, lineStarts: number[]): {
243
342
  line: number;
244
343
  col: number;
245
344
  };
345
+ /**
346
+ * Replace JS comments with spaces while preserving line/column
347
+ * positions. Used by the auto-import scanner so `<Stack>` text inside
348
+ * JSDoc doesn't false-positive as a JSX usage. Position preservation
349
+ * lets the caller use the masked code for regex SEARCH and the
350
+ * original code for SPLICE.
351
+ *
352
+ * String literals are deliberately LEFT VISIBLE — they often carry
353
+ * the package name we need to match (`from '@pyreon/primitives'`).
354
+ * The trade-off: a literal `'<Stack/>'` inside a string would
355
+ * false-positive, but that's vanishingly rare compared to JSDoc
356
+ * examples + the cost of either making string-aware AST parsing OR
357
+ * masking only the LITERAL TEXT (not the quotes) is not worth it
358
+ * for the marginal correctness gain.
359
+ *
360
+ * Handles:
361
+ * - line comments `// ... newline`
362
+ * - block comments `/* ... *​/`
363
+ */
364
+ declare function _maskCommentsAndStrings(code: string): string;
365
+ /** Collect every name imported via `import { ... }` / `import X` / `import * as X`. */
366
+ declare function _collectImportedNames(code: string): Set<string>;
246
367
  //#endregion
247
- export { CompatFramework, PyreonCollapseOptions, PyreonLpihOptions, PyreonPluginOptions, _computeLineStarts, _isTruthyEnv, _maskStringsAndComments, _offsetToLineCol, buildLpihClientScript, pyreonPlugin as default, registerLpihMiddleware, resolveLpihCachePath, writeLpihCacheFile };
368
+ export { CompatFramework, PyreonCollapseOptions, PyreonJsxAutoImportOptions, PyreonLpihOptions, PyreonPluginOptions, _collectImportedNames, _computeLineStarts, _extractBalancedArgs, _getCompatTarget, _handleSsrRequest, _isPyreonWorkspaceFile, _isTruthyEnv, _maskCommentsAndStrings, _maskStringsAndComments, _offsetToLineCol, _skipStringLiteral, buildLpihClientScript, pyreonPlugin as default, registerLpihMiddleware, resolveLpihCachePath, writeLpihCacheFile };
248
369
  //# sourceMappingURL=index2.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index2.d.ts","names":[],"sources":["../../../src/index.ts"],"mappings":";;;KA4EY,eAAA;AAAA,UAEK,mBAAA;EAsHf;;;;;;;AAE6B;AAG/B;;;;AAiBW;EA9HT,MAAA,GAAS,eAAA;EA8UiB;;;AAAsB;AAIjD;;;;;;EAtUC,GAAA;IAyUyE,4DAvUvE,KAAA;EAAA;EAm7BgC;;;AAAoB;AAaxD;;;;;;;;;;AAG4B;AAoD5B;;;;;;;;AAA6E;AAsD7E;EAjhCE,OAAA;;;AAihCsD;AA4UxD;;;;AAAoD;AAuHpD;;;;AAA+C;AAc/C;;;;;;;;;AAGsB;EA58CpB,IAAA,aAAiB,iBAAA;;;;;;;;;;;;;;;EAgBjB,QAAA,aAAqB,qBAAA;AAAA;AAAA,UAGN,qBAAA;;;;;;;;EAQf,OAAA;;;;;;EAMA,UAAA;;EAEA,QAAA;IAAa,IAAA;IAAc,MAAA;EAAA;;EAE3B,KAAA;IAAU,IAAA;IAAc,MAAA;EAAA;;EAExB,IAAA;IAAS,IAAA;IAAc,MAAA;EAAA;AAAA;AAAA,UAGR,iBAAA;;;;;;;;;;EAUf,UAAA;;;;;;;EAOA,SAAS;AAAA;;;;;;;;;;;;;iBAgNK,YAAA,CAAa,CAAqB;AAAA,iBAO1B,YAAA,CAAa,OAAA,GAAU,mBAAA,GAAsB,MAAM;;;;;;;;iBA4mB3D,oBAAA,CAAqB,WAAmB;;;;;;;;;;iBAaxC,sBAAA,CACd,MAAA,EAAQ,aAAA,EACR,WAAA,UACA,OAAA,EAAS,iBAAiB;;;;;;;;;;iBAoDN,kBAAA,CAAmB,IAAA,UAAc,IAAA,WAAe,OAAO;;;;;;;;;;;;;iBAsD7D,qBAAA,CAAsB,UAAkB;;;;;;;;;;;;;;;;;;;;;;;;iBA4UxC,uBAAA,CAAwB,IAAY;;;;;;;;iBAuHpC,kBAAA,CAAmB,IAAY;;;;;;;iBAc/B,gBAAA,CACd,MAAA,UACA,UAAA;EACG,IAAA;EAAc,GAAA;AAAA"}
1
+ {"version":3,"file":"index2.d.ts","names":[],"sources":["../../../src/index.ts"],"mappings":";;;KA4EY,eAAA;AAAA,UAEK,mBAAA;EA0JI;;;;;AAId;AAGP;;;;;;;EAnJE,MAAA,GAAS,eAAA;EAmKkB;;;;;;;;AAIE;AAG/B;EA9JE,GAAA;IA8JgC,4DA5J9B,KAAA;EAAA;EAgPY;;;;;;;;AAA8D;AA2C9E;;;;;;;;AAAgF;AAkGhF;;;;AAAkD;AAIjD;;EArWC,OAAA;EAgXyE;;;;;AAAA;AAonB3E;;;;;;;;;;;;;;;;AAOU;EAl9BR,IAAA,aAAiB,iBAAA;EA2gCiB;;;AAAoB;AAaxD;;;;;;;;;;EAxgCE,QAAA,aAAqB,qBAAA;EA+jCD;;;;;;;;AAAuD;AAsD7E;;;;AAAwD;AA6ExD;;;;;;;;AAA6E;AAa7E;;;;AAAgE;AAkPhE;;;;AAAoD;EA95ClD,aAAA,aAA0B,0BAAA;AAAA;;;AAqhDmB;AAc/C;;;;;;;;;AAGsB;UAthDL,0BAAA;EAuyDsB;;;AAAa;AAgCpD;EAj0DE,QAAA,GAAW,KAAK;IAAG,MAAA;IAAgB,KAAA;EAAA;;EAEnC,MAAA;;EAEA,KAAA;AAAA;AAAA,UAGe,qBAAA;;;;;;;;EAQf,OAAA;;;;;;EAMA,UAAA;;EAEA,QAAA;IAAa,IAAA;IAAc,MAAA;EAAA;;EAE3B,KAAA;IAAU,IAAA;IAAc,MAAA;EAAA;;EAExB,IAAA;IAAS,IAAA;IAAc,MAAA;EAAA;AAAA;AAAA,UAGR,iBAAA;;;;;;;;;;EAUf,UAAA;;;;;;;EAOA,SAAS;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAmEK,sBAAA,CAAuB,EAAA,UAAY,KAAA,EAAO,GAAG;;;;;iBA2C7C,gBAAA,CAAiB,MAAA,EAAQ,eAAe,cAAc,EAAA;;;;;;;;;;;;;iBAkGtD,YAAA,CAAa,CAAqB;AAAA,iBAe1B,YAAA,CAAa,OAAA,GAAU,mBAAA,GAAsB,MAAM;AAAA,iBAonBrD,iBAAA,CACpB,MAAA,EAAQ,aAAA,EACR,KAAA,UACA,GAAA,UACA,GAAA,sBAAyB,eAAA,EACzB,GAAA,sBAAyB,cAAA,EACzB,IAAA,GAAO,GAAA,sBACN,OAAO;;;;;;;;iBAyDM,oBAAA,CAAqB,WAAmB;;;;;;;;;;iBAaxC,sBAAA,CACd,MAAA,EAAQ,aAAA,EACR,WAAA,UACA,OAAA,EAAS,iBAAiB;;;;;;;;;;iBAoDN,kBAAA,CAAmB,IAAA,UAAc,IAAA,WAAe,OAAO;;;;;;;;;;;;;iBAsD7D,qBAAA,CAAsB,UAAkB;AAAA,iBA6ExC,kBAAA,CAAmB,IAAA,UAAc,KAAA,UAAe,KAAA;AAAA,iBAahD,oBAAA,CAAqB,IAAA,UAAc,KAAa;;;;;;;;;;;;;;;;;;;;;;;;iBAkPhD,uBAAA,CAAwB,IAAY;;;;;;;;iBAuHpC,kBAAA,CAAmB,IAAY;;;;;;;iBAc/B,gBAAA,CACd,MAAA,UACA,UAAA;EACG,IAAA;EAAc,GAAA;AAAA;;;;;;;;;;;;;;;;;;;;iBAiRH,uBAAA,CAAwB,IAAY;;iBAgCpC,qBAAA,CAAsB,IAAA,WAAe,GAAG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyreon/vite-plugin",
3
- "version": "0.25.1",
3
+ "version": "0.26.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": {
@@ -40,13 +40,15 @@
40
40
  "prepublishOnly": "bun run build"
41
41
  },
42
42
  "dependencies": {
43
- "@pyreon/compiler": "^0.25.1",
44
- "@pyreon/reactivity": "^0.25.1"
43
+ "@pyreon/compiler": "^0.26.0",
44
+ "@pyreon/reactivity": "^0.26.0"
45
45
  },
46
46
  "devDependencies": {
47
- "vite": "^8.0.0"
47
+ "@pyreon/vitest-config": "0.13.1",
48
+ "vite": "^8.0.14"
48
49
  },
49
50
  "peerDependencies": {
51
+ "@pyreon/runtime-dom": "^0.26.0",
50
52
  "vite": ">=8.0.0"
51
53
  }
52
54
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"rocketstyle-collapse-DGnwgDhC.js","names":[],"sources":["../src/rocketstyle-collapse.ts"],"sourcesContent":["/**\n * P0 — build-time rocketstyle-collapse resolver.\n *\n * For a collapsible call site (`<Button state=\"primary\" size=\"md\">Save</Button>`\n * — every dimension prop a string literal, children static text) this\n * resolves the FULL rocketstyle/styler pipeline ONCE by SSR-rendering the\n * REAL component, light AND dark, and returns: the resolved styler class\n * per mode, the styler rule text, and a class-stripped `_tpl` template.\n *\n * The render runs through a programmatic Vite SSR server bound to the\n * CONSUMER's own `vite.config` — so module resolution is identical to\n * the app's real build (workspace `bun` condition, app aliases,\n * app-local relative imports, whatever). Parity with the runtime-mounted\n * class is then guaranteed BY CONSTRUCTION: it is literally the same\n * `renderToString` + `@pyreon/styler` code path the client uses, and\n * styler's FNV-1a class hashing is identical in SSR and DOM (styler's\n * hydration contract). No reimplementation, no closure re-execution, no\n * drift (RFC decision 2).\n *\n * Every failure returns `null` (graceful bail → the call site keeps its\n * normal rocketstyle mount). Correct-but-slow is acceptable; wrong\n * output is not.\n */\nimport { withSilent } from '@pyreon/reactivity'\nimport type { InlineConfig, ViteDevServer } from 'vite'\n\n// Inline FNV-1a (same algorithm as @pyreon/styler/hash) — avoids pulling\n// the styler module graph into the vite-plugin's cheap entry path.\nfunction fnv1a(str: string): string {\n let h = 2166136261\n for (let i = 0; i < str.length; i++) {\n h ^= str.charCodeAt(i)\n h = Math.imul(h, 16777619)\n }\n return (h >>> 0).toString(36)\n}\n\nexport interface CollapseImportSpec {\n /** Imported binding name, e.g. `PyreonUI` / `theme` / `useMode`. */\n name: string\n /** Module specifier, e.g. `@pyreon/ui-core` / `@pyreon/ui-theme`. */\n source: string\n}\n\nexport interface CollapseConfig {\n /** Theme/mode provider component. Default: PyreonUI from @pyreon/ui-core. */\n provider: CollapseImportSpec\n /** Theme object. Default: theme from @pyreon/ui-theme. */\n theme: CollapseImportSpec\n /** Live mode accessor — emitted into the collapsed site for dual-emit. */\n mode: CollapseImportSpec\n}\n\nexport const DEFAULT_COLLAPSE_CONFIG: CollapseConfig = {\n provider: { name: 'PyreonUI', source: '@pyreon/ui-core' },\n theme: { name: 'theme', source: '@pyreon/ui-theme' },\n mode: { name: 'useMode', source: '@pyreon/ui-core' },\n}\n\nexport interface ResolveInput {\n /** The collapsible component's import. */\n component: CollapseImportSpec\n /** Literal dimension/HTML props, e.g. `{ state: 'primary', size: 'md' }`. */\n props: Record<string, string>\n /** Static text children (empty ⇒ no children). */\n childrenText: string\n config: CollapseConfig\n}\n\nexport interface ResolvedCollapse {\n /** Element HTML with the root `class=\"...\"` removed (the `_tpl` template). */\n templateHtml: string\n lightClass: string\n darkClass: string\n /** Pre-resolved styler rule text (full snapshot) for `injectRules`. */\n rules: string[]\n /** FNV over the rule set — `injectRules` idempotency + cross-site dedupe. */\n key: string\n}\n\nconst FIRST_CLASS_RE = /^(\\s*<[a-zA-Z][\\w-]*)([^>]*?)\\sclass=\"([^\"]*)\"([^>]*>)/\n\n/** Strip the FIRST element's `class=\"...\"`, returning [stripped, class]. */\nexport function stripRootClass(html: string): { stripped: string; cls: string } | null {\n const m = FIRST_CLASS_RE.exec(html)\n if (!m) return null\n const stripped = html.replace(FIRST_CLASS_RE, '$1$2$4')\n return { stripped, cls: m[3] ?? '' }\n}\n\n/**\n * Pure extraction half — given the two rendered HTML strings and the\n * styler rule snapshot, derive the ResolvedCollapse (or null on a shape\n * the slice doesn't collapse). Separated for direct unit-testing without\n * spinning Vite.\n */\nexport function deriveCollapse(\n lightHtml: string,\n darkHtml: string,\n rules: string[],\n): ResolvedCollapse | null {\n const light = stripRootClass(lightHtml)\n const dark = stripRootClass(darkHtml)\n if (!light || !dark || !light.cls || !dark.cls) return null\n // The structural template must be identical between modes (only the\n // class differs). Divergent markup ⇒ not a simple single-root\n // collapsible — bail.\n if (light.stripped !== dark.stripped) return null\n return {\n templateHtml: light.stripped.trim(),\n lightClass: light.cls,\n darkClass: dark.cls,\n rules,\n key: fnv1a(rules.join('\\u0000')),\n }\n}\n\nexport interface CollapseResolver {\n resolve(input: ResolveInput): Promise<ResolvedCollapse | null>\n dispose(): Promise<void>\n}\n\n/**\n * Create a resolver backed by ONE programmatic Vite SSR server bound to\n * `projectRoot`'s vite config. Reused across every call site in a build;\n * `dispose()` at buildEnd. Module loads are cached by Vite's own SSR\n * module graph (provider/theme/component import once).\n */\nexport async function createCollapseResolver(projectRoot: string): Promise<CollapseResolver> {\n const { createServer } = (await import('vite')) as typeof import('vite')\n const inline: InlineConfig = {\n // No `configFile` override — Vite auto-loads the project's own\n // vite.config from `root`, so module resolution (workspace `bun`\n // condition, app aliases) matches the real build exactly.\n root: projectRoot,\n server: { middlewareMode: true },\n appType: 'custom',\n logLevel: 'silent',\n optimizeDeps: { noDiscovery: true, include: [] },\n }\n // `createServer` evaluates the consumer's vite.config + plugin chain,\n // which may itself load `@pyreon/*` packages via the `node` condition\n // (different path than the outer process's `bun`-conditioned `src/`).\n // That's a legitimate dual-load — scope the opt-out across the entire\n // server lifecycle since every `load(spec)` below also touches the\n // dual graph.\n let server: ViteDevServer | null = await withSilent(() => createServer(inline))\n\n // Resolved-bundle cache — identical input must hit the same result\n // without a second double-render (deterministic by construction).\n const cache = new Map<string, ResolvedCollapse | null>()\n\n async function load(spec: string): Promise<Record<string, unknown>> {\n // The nested Vite SSR server loads its own copy of @pyreon/* packages for\n // the SSR snapshot. This is a legitimate dual-load — the outer process has\n // its own @pyreon/* graph; the nested server has its own. `withSilent`\n // from @pyreon/reactivity scopes the sentinel opt-out via a refcount\n // (race-safe under concurrency; the prior env-var dance leaked `silent`\n // permanently when N opt-out scopes overlapped — see withSilent JSDoc).\n return withSilent(\n () => server!.ssrLoadModule(spec) as Promise<Record<string, unknown>>,\n )\n }\n\n return {\n async resolve(input) {\n const ck = JSON.stringify([\n input.component,\n input.props,\n input.childrenText,\n input.config.provider,\n input.config.theme,\n ])\n if (cache.has(ck)) return cache.get(ck) ?? null\n try {\n if (!server) return null\n const rs = await load('@pyreon/runtime-server')\n const core = await load('@pyreon/core')\n const styler = await load('@pyreon/styler')\n const prov = await load(input.config.provider.source)\n const thm = await load(input.config.theme.source)\n const comp = await load(input.component.source)\n const renderToString = rs.renderToString as (n: unknown) => Promise<string>\n const h = core.h as (t: unknown, p: unknown, ...c: unknown[]) => unknown\n const Provider = prov[input.config.provider.name]\n const themeVal = thm[input.config.theme.name]\n const Component = comp[input.component.name]\n const sheet = styler.sheet as { getStyleRules(): readonly string[] }\n if (typeof Component !== 'function' || Provider == null || themeVal == null) {\n cache.set(ck, null)\n return null\n }\n const childArgs = input.childrenText ? [input.childrenText] : []\n const node = (mode: string) =>\n h(Provider, { theme: themeVal, mode }, h(Component, input.props, ...childArgs))\n const lightHtml = await renderToString(node('light'))\n const darkHtml = await renderToString(node('dark'))\n const rules = sheet.getStyleRules().slice()\n const result = deriveCollapse(lightHtml, darkHtml, rules)\n cache.set(ck, result)\n return result\n } catch {\n cache.set(ck, null)\n return null\n }\n },\n async dispose() {\n const s = server\n server = null\n cache.clear()\n if (s) await s.close()\n },\n }\n}\n"],"mappings":"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"}