@pyreon/vite-plugin 0.14.0 → 0.16.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 +228 -12
- package/lib/types/index.d.ts +26 -0
- package/package.json +3 -2
- package/src/index.ts +400 -13
- package/src/tests/compat-resolve.test.ts +82 -0
- package/src/tests/islands-registry.test.ts +236 -0
- package/src/tests/vite-plugin.test.ts +116 -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":"8d2ce63a-1"}]}],"isRoot":true},"nodeParts":{"8d2ce63a-1":{"renderedLength":28850,"gzipLength":9498,"brotliLength":0,"metaUid":"8d2ce63a-0"}},"nodeMetas":{"8d2ce63a-0":{"id":"/src/index.ts","moduleParts":{"index.js":"8d2ce63a-1"},"imported":[{"uid":"8d2ce63a-2"},{"uid":"8d2ce63a-3"},{"uid":"8d2ce63a-4"}],"importedBy":[],"isEntry":true},"8d2ce63a-2":{"id":"node:fs","moduleParts":{},"imported":[],"importedBy":[{"uid":"8d2ce63a-0"}]},"8d2ce63a-3":{"id":"node:path","moduleParts":{},"imported":[],"importedBy":[{"uid":"8d2ce63a-0"}]},"8d2ce63a-4":{"id":"@pyreon/compiler","moduleParts":{},"imported":[],"importedBy":[{"uid":"8d2ce63a-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,8 @@ 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
|
|
42
|
-
|
|
43
|
-
preact: "@pyreon/preact-compat",
|
|
44
|
-
vue: "@pyreon/vue-compat",
|
|
45
|
-
solid: "@pyreon/solid-compat"
|
|
46
|
-
};
|
|
41
|
+
const ISLANDS_REGISTRY_ID = "\0pyreon/islands-registry";
|
|
42
|
+
const ISLANDS_REGISTRY_IMPORT = "virtual:pyreon/islands-registry";
|
|
47
43
|
const COMPAT_ALIASES = {
|
|
48
44
|
react: {
|
|
49
45
|
react: "@pyreon/react-compat",
|
|
@@ -71,6 +67,59 @@ const COMPAT_ALIASES = {
|
|
|
71
67
|
}
|
|
72
68
|
};
|
|
73
69
|
/**
|
|
70
|
+
* Detect whether a file id resolves to a `@pyreon/*` framework-package source
|
|
71
|
+
* (i.e. a published Pyreon package whose .tsx is being pulled in via the
|
|
72
|
+
* `bun` condition workspace-link, NOT user code, NOT an example app).
|
|
73
|
+
*
|
|
74
|
+
* Why this exists: in compat mode, OXC's per-project `importSource` is set
|
|
75
|
+
* to `@pyreon/core` and the resolveId hook redirects `@pyreon/core/jsx-runtime`
|
|
76
|
+
* to the compat package. That's correct for user code (the whole point of
|
|
77
|
+
* compat mode) but WRONG for framework-internal sources like
|
|
78
|
+
* `@pyreon/zero/src/link.tsx`, which need the real `@pyreon/core` runtime.
|
|
79
|
+
* The fix skips the redirect when the importer is a `@pyreon/*` framework
|
|
80
|
+
* file. Result: published-package consumers (where `@pyreon/zero` resolves
|
|
81
|
+
* to its pre-built `lib/`) and workspace-dev consumers (where it resolves
|
|
82
|
+
* to source) both get correct JSX runtime resolution.
|
|
83
|
+
*
|
|
84
|
+
* Detection heuristic: walk to nearest `package.json`, require BOTH:
|
|
85
|
+
* 1. `name` starts with `@pyreon/` (workspace member of the @pyreon scope)
|
|
86
|
+
* 2. file path contains `/packages/` AND NOT `/examples/`
|
|
87
|
+
*
|
|
88
|
+
* Step 2 excludes the existing `@pyreon/example-{react,vue,solid,preact}-compat`
|
|
89
|
+
* apps under `examples/`. Without it, user code in those apps would skip the
|
|
90
|
+
* compat-mode JSX-runtime redirect and import `@pyreon/core/jsx-runtime`
|
|
91
|
+
* directly — breaking the React/Vue/Solid/Preact compat layer's contract.
|
|
92
|
+
*
|
|
93
|
+
* Result cached per directory. The `/packages/` + `/examples/` check is a
|
|
94
|
+
* structural property of the monorepo (workspace layout), not the package
|
|
95
|
+
* name — so it's robust against renames.
|
|
96
|
+
*/
|
|
97
|
+
function isPyreonWorkspaceFile(id, cache) {
|
|
98
|
+
const queryIdx = id.indexOf("?");
|
|
99
|
+
const filePath = queryIdx === -1 ? id : id.slice(0, queryIdx);
|
|
100
|
+
if (!filePath || filePath[0] === "\0") return false;
|
|
101
|
+
if (!filePath.includes("/packages/") || filePath.includes("/examples/")) return false;
|
|
102
|
+
let dir = dirname(filePath);
|
|
103
|
+
for (let i = 0; i < 12; i++) {
|
|
104
|
+
const cached = cache.get(dir);
|
|
105
|
+
if (cached !== void 0) return cached;
|
|
106
|
+
const pkgPath = join(dir, "package.json");
|
|
107
|
+
if (existsSync(pkgPath)) {
|
|
108
|
+
let isPyreon = false;
|
|
109
|
+
try {
|
|
110
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
111
|
+
isPyreon = typeof pkg.name === "string" && pkg.name.startsWith("@pyreon/");
|
|
112
|
+
} catch {}
|
|
113
|
+
cache.set(dir, isPyreon);
|
|
114
|
+
return isPyreon;
|
|
115
|
+
}
|
|
116
|
+
const parent = dirname(dir);
|
|
117
|
+
if (parent === dir) break;
|
|
118
|
+
dir = parent;
|
|
119
|
+
}
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
74
123
|
* Return the Pyreon compat target for an import specifier, or undefined if
|
|
75
124
|
* the import should not be redirected.
|
|
76
125
|
*/
|
|
@@ -85,27 +134,56 @@ function getCompatTarget(compat, id) {
|
|
|
85
134
|
if (compat === "solid") return "@pyreon/solid-compat/jsx-runtime";
|
|
86
135
|
}
|
|
87
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Scan the consumer's package.json for `@pyreon/*` deps. Result is the
|
|
139
|
+
* list of names to exclude from Vite's deps optimizer (avoids
|
|
140
|
+
* `.vite/deps/@pyreon_*.js: File does not exist` runtime errors caused
|
|
141
|
+
* by esbuild trying to pre-bundle TypeScript source files exposed via
|
|
142
|
+
* the `bun` resolve condition).
|
|
143
|
+
*
|
|
144
|
+
* Reads dependencies + devDependencies + peerDependencies. Best-effort:
|
|
145
|
+
* missing/malformed package.json returns an empty list so a typo in
|
|
146
|
+
* the consumer's manifest doesn't break the build.
|
|
147
|
+
*/
|
|
148
|
+
function scanPyreonDeps(root) {
|
|
149
|
+
const pkgPath = join(root, "package.json");
|
|
150
|
+
if (!existsSync(pkgPath)) return [];
|
|
151
|
+
try {
|
|
152
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
153
|
+
const all = {
|
|
154
|
+
...pkg.dependencies,
|
|
155
|
+
...pkg.devDependencies,
|
|
156
|
+
...pkg.peerDependencies
|
|
157
|
+
};
|
|
158
|
+
return Object.keys(all).filter((name) => name.startsWith("@pyreon/"));
|
|
159
|
+
} catch {
|
|
160
|
+
return [];
|
|
161
|
+
}
|
|
162
|
+
}
|
|
88
163
|
function pyreonPlugin(options) {
|
|
89
164
|
const ssrConfig = options?.ssr;
|
|
90
165
|
const compat = options?.compat;
|
|
166
|
+
const islandsEnabled = options?.islands !== false;
|
|
91
167
|
let isBuild = false;
|
|
92
168
|
let projectRoot = "";
|
|
93
169
|
const signalExportRegistry = /* @__PURE__ */ new Map();
|
|
94
170
|
const resolveCache = /* @__PURE__ */ new Map();
|
|
171
|
+
const pyreonWorkspaceDirCache = /* @__PURE__ */ new Map();
|
|
172
|
+
const islandRegistry = /* @__PURE__ */ new Map();
|
|
95
173
|
return {
|
|
96
174
|
name: "pyreon",
|
|
97
175
|
enforce: "pre",
|
|
98
176
|
config(userConfig, env) {
|
|
99
177
|
isBuild = env.command === "build";
|
|
100
178
|
projectRoot = userConfig.root ?? process.cwd();
|
|
101
|
-
const
|
|
102
|
-
const
|
|
179
|
+
const compatExclude = compat ? Object.keys(COMPAT_ALIASES[compat]) : [];
|
|
180
|
+
const pyreonExclude = scanPyreonDeps(projectRoot);
|
|
103
181
|
return {
|
|
104
182
|
resolve: { conditions: ["bun"] },
|
|
105
|
-
optimizeDeps: { exclude:
|
|
183
|
+
optimizeDeps: { exclude: Array.from(new Set([...compatExclude, ...pyreonExclude])) },
|
|
106
184
|
oxc: { jsx: {
|
|
107
185
|
runtime: "automatic",
|
|
108
|
-
importSource:
|
|
186
|
+
importSource: "@pyreon/core"
|
|
109
187
|
} },
|
|
110
188
|
...env.isSsrBuild && ssrConfig ? { build: {
|
|
111
189
|
ssr: true,
|
|
@@ -115,15 +193,19 @@ function pyreonPlugin(options) {
|
|
|
115
193
|
},
|
|
116
194
|
async buildStart() {
|
|
117
195
|
await prescanSignalExports(projectRoot, signalExportRegistry);
|
|
196
|
+
if (islandsEnabled) await prescanIslandDeclarations(projectRoot, islandRegistry);
|
|
118
197
|
},
|
|
119
198
|
async resolveId(id, importer) {
|
|
120
199
|
if (id === HMR_RUNTIME_IMPORT) return HMR_RUNTIME_ID;
|
|
200
|
+
if (id === ISLANDS_REGISTRY_IMPORT) return ISLANDS_REGISTRY_ID;
|
|
201
|
+
if (compat && (id === "@pyreon/core/jsx-runtime" || id === "@pyreon/core/jsx-dev-runtime") && importer && isPyreonWorkspaceFile(importer, pyreonWorkspaceDirCache)) return;
|
|
121
202
|
const target = getCompatTarget(compat, id);
|
|
122
203
|
if (!target) return;
|
|
123
204
|
return (await this.resolve(target, importer, { skipSelf: true }))?.id;
|
|
124
205
|
},
|
|
125
206
|
load(id) {
|
|
126
207
|
if (id === HMR_RUNTIME_ID) return HMR_RUNTIME_SOURCE;
|
|
208
|
+
if (id === ISLANDS_REGISTRY_ID) return renderIslandsRegistry(islandRegistry, islandsEnabled);
|
|
127
209
|
},
|
|
128
210
|
async transform(code, id, transformOptions) {
|
|
129
211
|
const ext = getExt(id);
|
|
@@ -139,6 +221,7 @@ function pyreonPlugin(options) {
|
|
|
139
221
|
return;
|
|
140
222
|
}
|
|
141
223
|
scanSignalExports(code, normalizeModuleId(id), signalExportRegistry);
|
|
224
|
+
if (islandsEnabled) scanIslandDeclarations(code, id, islandRegistry);
|
|
142
225
|
const knownSignals = await resolveImportedSignals(code, id, signalExportRegistry, this, resolveCache);
|
|
143
226
|
const result = transformJSX(code, id, {
|
|
144
227
|
ssr: transformOptions?.ssr === true,
|
|
@@ -222,8 +305,18 @@ function generateProjectContext(root) {
|
|
|
222
305
|
* The arguments are extracted via balanced-paren matching in `injectHmr`.
|
|
223
306
|
* A brace-depth check filters out matches inside functions/blocks — only
|
|
224
307
|
* module-scope (depth 0) signals are rewritten for HMR state preservation.
|
|
308
|
+
*
|
|
309
|
+
* The optional `<...>` group accepts a TypeScript type parameter so that
|
|
310
|
+
* `signal<T>(initial)` declarations are also rewritten — without it, any
|
|
311
|
+
* generic-typed module-scope signal silently skipped HMR preservation.
|
|
312
|
+
*
|
|
313
|
+
* The inner `(?:[^<>]|<[^<>]*>)*` permits one level of generic nesting
|
|
314
|
+
* (e.g. `signal<Array<Row>>([])`, `signal<Map<string, number>>(m)`).
|
|
315
|
+
* Deeper nesting (`signal<Array<{ id: T<U> }>>(...)`) falls back to
|
|
316
|
+
* not-rewritten — tracked as a follow-up if real consumers need it,
|
|
317
|
+
* but unlikely at module scope where generics are usually shallow.
|
|
225
318
|
*/
|
|
226
|
-
const SIGNAL_PREFIX_RE = /^((?:export\s+)?(?:const|let)\s+(\w+)\s*=\s*)signal
|
|
319
|
+
const SIGNAL_PREFIX_RE = /^((?:export\s+)?(?:const|let)\s+(\w+)\s*=\s*)signal(?:<(?:[^<>]|<[^<>]*>)*>)?\(/gm;
|
|
227
320
|
/**
|
|
228
321
|
* Detect whether the module exports any component-like functions
|
|
229
322
|
* (uppercase first letter — standard convention for JSX components).
|
|
@@ -385,6 +478,129 @@ function normalizeModuleId(id) {
|
|
|
385
478
|
return queryIndex >= 0 ? id.slice(0, queryIndex) : id;
|
|
386
479
|
}
|
|
387
480
|
/**
|
|
481
|
+
* Pre-scan all source files in the project for `island()` declarations.
|
|
482
|
+
*
|
|
483
|
+
* Called from `buildStart` (when `islands: true`) so the registry is fully
|
|
484
|
+
* populated before any transforms run. Mirrors `prescanSignalExports` shape;
|
|
485
|
+
* the per-file regex pattern matches:
|
|
486
|
+
*
|
|
487
|
+
* island(() => import('PATH'), { name: 'NAME', hydrate: 'STRATEGY' })
|
|
488
|
+
*
|
|
489
|
+
* Edge cases the regex deliberately doesn't cover (user falls back to manual
|
|
490
|
+
* `hydrateIslands({ ... })`):
|
|
491
|
+
* - Loader is a variable, not an inline arrow: `island(myLoader, { name })`
|
|
492
|
+
* - Name is a variable: `island(() => import('./X'), { name: NAME_CONST })`
|
|
493
|
+
* - Options come from a spread: `island(loader, { ...opts })`
|
|
494
|
+
*/
|
|
495
|
+
async function prescanIslandDeclarations(root, registry) {
|
|
496
|
+
const files = [];
|
|
497
|
+
function walk(dir) {
|
|
498
|
+
try {
|
|
499
|
+
for (const entry of readdirSync(dir)) {
|
|
500
|
+
if (entry.startsWith(".") || entry === "node_modules" || entry === "dist" || entry === "lib" || entry === "build") continue;
|
|
501
|
+
const full = join(dir, entry);
|
|
502
|
+
try {
|
|
503
|
+
if (statSync(full).isDirectory()) walk(full);
|
|
504
|
+
else if (/\.(ts|tsx|js|jsx)$/.test(entry)) files.push(full);
|
|
505
|
+
} catch {}
|
|
506
|
+
}
|
|
507
|
+
} catch {}
|
|
508
|
+
}
|
|
509
|
+
walk(root);
|
|
510
|
+
for (const file of files) try {
|
|
511
|
+
scanIslandDeclarations(readFileSync(file, "utf-8"), file, registry);
|
|
512
|
+
} catch {}
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Scan a single source file for `island()` declarations and record them.
|
|
516
|
+
*
|
|
517
|
+
* The regex captures:
|
|
518
|
+
* - Group 1: dynamic-import path (`./components/Counter`)
|
|
519
|
+
* - Group 2: options block contents
|
|
520
|
+
*
|
|
521
|
+
* Then a follow-up regex pulls `name: 'X'` and `hydrate: 'Y'` from the
|
|
522
|
+
* options block. Single-line and multi-line forms both work.
|
|
523
|
+
*
|
|
524
|
+
* Resolves the loader path relative to the file where the call lives so
|
|
525
|
+
* the emitted virtual-module registry gets an absolute path Vite's resolver
|
|
526
|
+
* can find.
|
|
527
|
+
*/
|
|
528
|
+
function scanIslandDeclarations(code, filePath, registry) {
|
|
529
|
+
const ISLAND_CALL_RE = /island\s*\(\s*\(\s*\)\s*=>\s*import\s*\(\s*['"]([^'"]+)['"]\s*\)\s*,\s*\{([\s\S]*?)\}\s*\)/g;
|
|
530
|
+
const decls = [];
|
|
531
|
+
let match;
|
|
532
|
+
while ((match = ISLAND_CALL_RE.exec(code)) !== null) {
|
|
533
|
+
const importPath = match[1];
|
|
534
|
+
const optsBlock = match[2];
|
|
535
|
+
const nameMatch = /(?:^|[\s,{])name\s*:\s*['"]([^'"]+)['"]/.exec(optsBlock);
|
|
536
|
+
if (!nameMatch) continue;
|
|
537
|
+
const hydrateMatch = /(?:^|[\s,{])hydrate\s*:\s*['"]([^'"]+)['"]/.exec(optsBlock);
|
|
538
|
+
const hydrate = hydrateMatch ? hydrateMatch[1] : "load";
|
|
539
|
+
const loaderAbsPath = importPath.startsWith(".") ? resolveRelative(filePath, importPath) : importPath;
|
|
540
|
+
decls.push({
|
|
541
|
+
name: nameMatch[1],
|
|
542
|
+
hydrate,
|
|
543
|
+
loaderAbsPath
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
if (decls.length > 0) registry.set(normalizeModuleId(filePath), decls);
|
|
547
|
+
else registry.delete(normalizeModuleId(filePath));
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Resolve a dynamic-import specifier to an absolute path, mirroring how Node
|
|
551
|
+
* / Vite resolve `import('./X')` from the source file's directory.
|
|
552
|
+
*/
|
|
553
|
+
function resolveRelative(fromFile, relPath) {
|
|
554
|
+
return join(dirname(fromFile), relPath);
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Render the auto-generated `virtual:pyreon/islands-registry` source. Emits:
|
|
558
|
+
*
|
|
559
|
+
* export const __pyreonIslandRegistry = {
|
|
560
|
+
* Counter: () => import('/abs/path/to/components/Counter'),
|
|
561
|
+
* IdleClock: () => import('/abs/path/to/components/IdleClock'),
|
|
562
|
+
* // never-strategy islands deliberately omitted
|
|
563
|
+
* }
|
|
564
|
+
*
|
|
565
|
+
* `hydrate: 'never'` islands are skipped — registering a loader for them
|
|
566
|
+
* would defeat the strategy by pulling the component module into the
|
|
567
|
+
* client bundle graph. `hydrateIslandsAuto()` short-circuits never-islands
|
|
568
|
+
* at runtime regardless; emitting here would still create the dynamic-
|
|
569
|
+
* import chunk.
|
|
570
|
+
*
|
|
571
|
+
* Duplicate `name` across declarations: the LAST one wins. Documented as
|
|
572
|
+
* an anti-pattern (caught by the planned `pyreon doctor --check-islands`).
|
|
573
|
+
*/
|
|
574
|
+
function renderIslandsRegistry(registry, enabled) {
|
|
575
|
+
if (!enabled) return [
|
|
576
|
+
`// pyreon plugin: islands feature is disabled (pyreon({ islands: false })).`,
|
|
577
|
+
`// hydrateIslandsAuto() will throw at runtime — re-enable via vite.config.ts`,
|
|
578
|
+
`// or use manual hydrateIslands({ ... }) instead.`,
|
|
579
|
+
`export const __pyreonIslandRegistry = {};`,
|
|
580
|
+
`export const __pyreonIslandsEnabled = false;`
|
|
581
|
+
].join("\n");
|
|
582
|
+
const entries = [];
|
|
583
|
+
const seen = /* @__PURE__ */ new Set();
|
|
584
|
+
const all = Array.from(registry.values()).flat();
|
|
585
|
+
all.sort((a, b) => a.name.localeCompare(b.name));
|
|
586
|
+
for (const { name, hydrate, loaderAbsPath } of all) {
|
|
587
|
+
if (hydrate === "never") continue;
|
|
588
|
+
if (seen.has(name)) continue;
|
|
589
|
+
seen.add(name);
|
|
590
|
+
entries.push(` ${JSON.stringify(name)}: () => import(${JSON.stringify(loaderAbsPath)}),`);
|
|
591
|
+
}
|
|
592
|
+
return [
|
|
593
|
+
`// Auto-generated by @pyreon/vite-plugin (islands: true). Do not edit.`,
|
|
594
|
+
`// Sourced from island() declarations in your project. Never-strategy`,
|
|
595
|
+
`// islands are intentionally omitted — registering a loader for them`,
|
|
596
|
+
`// would defeat the zero-JS contract.`,
|
|
597
|
+
`export const __pyreonIslandRegistry = {`,
|
|
598
|
+
...entries,
|
|
599
|
+
`};`,
|
|
600
|
+
`export const __pyreonIslandsEnabled = true;`
|
|
601
|
+
].join("\n");
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
388
604
|
* Pre-scan all source files in the project for signal exports.
|
|
389
605
|
*
|
|
390
606
|
* Called from `buildStart` so the registry is fully populated before any
|
package/lib/types/index.d.ts
CHANGED
|
@@ -29,6 +29,32 @@ interface PyreonPluginOptions {
|
|
|
29
29
|
ssr?: {
|
|
30
30
|
/** Server entry file path (e.g. "./src/entry-server.ts") */entry: string;
|
|
31
31
|
};
|
|
32
|
+
/**
|
|
33
|
+
* Auto-discover `island()` declarations and expose them as
|
|
34
|
+
* `virtual:pyreon/islands-registry` for `hydrateIslandsAuto()` in
|
|
35
|
+
* `@pyreon/server/client`.
|
|
36
|
+
*
|
|
37
|
+
* Eliminates the manual sync between `island()` declarations and the
|
|
38
|
+
* client-side `hydrateIslands({ ... })` registry — typo / forgotten entry /
|
|
39
|
+
* registry drift is the #1 author foot-gun for islands.
|
|
40
|
+
*
|
|
41
|
+
* Defaults to `true`. The prescan is cheap (regex over the same files
|
|
42
|
+
* already walked by `prescanSignalExports`); set to `false` only if you
|
|
43
|
+
* have a reason not to support `hydrateIslandsAuto()`.
|
|
44
|
+
*
|
|
45
|
+
* `hydrate: 'never'` islands are deliberately OMITTED from the auto-
|
|
46
|
+
* registry — the whole point of the strategy is shipping zero client JS,
|
|
47
|
+
* so registering a loader (which would pull the component module into the
|
|
48
|
+
* client bundle graph) defeats it.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* pyreon({ islands: true })
|
|
52
|
+
*
|
|
53
|
+
* // src/entry-client.ts
|
|
54
|
+
* import { hydrateIslandsAuto } from '@pyreon/server/client'
|
|
55
|
+
* hydrateIslandsAuto()
|
|
56
|
+
*/
|
|
57
|
+
islands?: boolean;
|
|
32
58
|
}
|
|
33
59
|
declare function pyreonPlugin(options?: PyreonPluginOptions): Plugin;
|
|
34
60
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/vite-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.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.16.0"
|
|
46
47
|
},
|
|
47
48
|
"devDependencies": {
|
|
48
49
|
"vite": "^8.0.0"
|