@pyreon/vite-plugin 0.5.5 → 0.5.7
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/types/index.d.ts +32 -305
- package/lib/types/index.d.ts.map +1 -1
- package/package.json +2 -2
package/lib/types/index.d.ts
CHANGED
|
@@ -1,309 +1,36 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { join, resolve } from "node:path";
|
|
3
|
-
import { generateContext, transformJSX } from "@pyreon/compiler";
|
|
1
|
+
import { Plugin } from "vite";
|
|
4
2
|
|
|
5
|
-
//#region src/index.ts
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
* 3. Returns the SSR'd HTML for every non-asset request
|
|
34
|
-
*
|
|
35
|
-
* For production, build separately:
|
|
36
|
-
* vite build # client bundle
|
|
37
|
-
* vite build --ssr src/entry-server.ts --outDir dist/server # server bundle
|
|
38
|
-
*/
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Resolve a package specifier to an absolute source path, respecting the "bun"
|
|
42
|
-
* export condition. Falls back to the "import" condition.
|
|
43
|
-
*
|
|
44
|
-
* This is needed because Vite 8's resolve pipeline doesn't consistently apply
|
|
45
|
-
* custom conditions from `resolve.conditions` during the `vite:import-analysis`
|
|
46
|
-
* phase for aliased workspace packages.
|
|
47
|
-
*/
|
|
48
|
-
function resolveWithBunCondition(specifier, projectRoot) {
|
|
49
|
-
const pkgName = (specifier.startsWith("@") ? specifier.split("/").slice(0, 2) : specifier.split("/").slice(0, 1)).join("/");
|
|
50
|
-
const subpath = specifier.slice(pkgName.length) || ".";
|
|
51
|
-
const exportKey = subpath === "." ? "." : `.${subpath}`;
|
|
52
|
-
try {
|
|
53
|
-
const pkgDir = resolve(projectRoot, "node_modules", pkgName);
|
|
54
|
-
const pkgJsonPath = resolve(pkgDir, "package.json");
|
|
55
|
-
const exp = JSON.parse(readFileSync(pkgJsonPath, "utf-8")).exports?.[exportKey];
|
|
56
|
-
if (!exp) return void 0;
|
|
57
|
-
if (typeof exp === "string") return resolve(pkgDir, exp);
|
|
58
|
-
const target = exp.bun ?? exp.import ?? exp.default;
|
|
59
|
-
return target ? resolve(pkgDir, target) : void 0;
|
|
60
|
-
} catch {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Return the Pyreon compat target for an import specifier, or undefined if
|
|
66
|
-
* the import should not be redirected.
|
|
67
|
-
*/
|
|
68
|
-
function getCompatTarget(compat, id) {
|
|
69
|
-
if (!compat) return void 0;
|
|
70
|
-
const aliased = COMPAT_ALIASES[compat][id];
|
|
71
|
-
if (aliased) return aliased;
|
|
72
|
-
if (id === "@pyreon/core/jsx-runtime" || id === "@pyreon/core/jsx-dev-runtime") {
|
|
73
|
-
if (compat === "react") return "@pyreon/react-compat/jsx-runtime";
|
|
74
|
-
if (compat === "preact") return "@pyreon/preact-compat/jsx-runtime";
|
|
75
|
-
if (compat === "vue") return "@pyreon/vue-compat/jsx-runtime";
|
|
76
|
-
if (compat === "solid") return "@pyreon/solid-compat/jsx-runtime";
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
function pyreonPlugin(options) {
|
|
80
|
-
const ssrConfig = options?.ssr;
|
|
81
|
-
const compat = options?.compat;
|
|
82
|
-
let isBuild = false;
|
|
83
|
-
let projectRoot = "";
|
|
84
|
-
const resolvedAliases = /* @__PURE__ */new Map();
|
|
85
|
-
return {
|
|
86
|
-
name: "pyreon",
|
|
87
|
-
enforce: "pre",
|
|
88
|
-
config(userConfig, env) {
|
|
89
|
-
isBuild = env.command === "build";
|
|
90
|
-
projectRoot = userConfig.root ?? process.cwd();
|
|
91
|
-
return {
|
|
92
|
-
resolve: {
|
|
93
|
-
conditions: ["bun"]
|
|
94
|
-
},
|
|
95
|
-
optimizeDeps: {
|
|
96
|
-
exclude: compat ? Object.keys(COMPAT_ALIASES[compat]) : []
|
|
97
|
-
},
|
|
98
|
-
oxc: {
|
|
99
|
-
jsx: {
|
|
100
|
-
runtime: "automatic",
|
|
101
|
-
importSource: compat ? COMPAT_JSX_SOURCE[compat] : "@pyreon/core"
|
|
102
|
-
}
|
|
103
|
-
},
|
|
104
|
-
...(env.isSsrBuild && ssrConfig ? {
|
|
105
|
-
build: {
|
|
106
|
-
ssr: true,
|
|
107
|
-
rollupOptions: {
|
|
108
|
-
input: ssrConfig.entry
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
} : {})
|
|
112
|
-
};
|
|
113
|
-
},
|
|
114
|
-
resolveId(id) {
|
|
115
|
-
if (id === HMR_RUNTIME_IMPORT) return HMR_RUNTIME_ID;
|
|
116
|
-
const target = getCompatTarget(compat, id);
|
|
117
|
-
if (!target) return;
|
|
118
|
-
let resolved = resolvedAliases.get(target);
|
|
119
|
-
if (!resolved) {
|
|
120
|
-
resolved = resolveWithBunCondition(target, projectRoot);
|
|
121
|
-
if (resolved) resolvedAliases.set(target, resolved);
|
|
122
|
-
}
|
|
123
|
-
return resolved;
|
|
124
|
-
},
|
|
125
|
-
load(id) {
|
|
126
|
-
if (id === HMR_RUNTIME_ID) return HMR_RUNTIME_SOURCE;
|
|
127
|
-
},
|
|
128
|
-
transform(code, id) {
|
|
129
|
-
const ext = getExt(id);
|
|
130
|
-
if (ext !== ".tsx" && ext !== ".jsx" && ext !== ".pyreon") return;
|
|
131
|
-
if (compat === "react" || compat === "preact" || compat === "vue" || compat === "solid") return;
|
|
132
|
-
const result = transformJSX(code, id);
|
|
133
|
-
for (const w of result.warnings) this.warn(`${w.message} (${id}:${w.line}:${w.column})`);
|
|
134
|
-
let output = result.code;
|
|
135
|
-
if (!isBuild) output = injectHmr(output, id);
|
|
136
|
-
return {
|
|
137
|
-
code: output,
|
|
138
|
-
map: null
|
|
139
|
-
};
|
|
140
|
-
},
|
|
141
|
-
configureServer(server) {
|
|
142
|
-
generateProjectContext(projectRoot);
|
|
143
|
-
let contextTimer = null;
|
|
144
|
-
server.watcher.on("change", file => {
|
|
145
|
-
if (/\.(tsx|jsx|ts|js)$/.test(file) && !file.includes("node_modules")) {
|
|
146
|
-
if (contextTimer) clearTimeout(contextTimer);
|
|
147
|
-
contextTimer = setTimeout(() => generateProjectContext(projectRoot), 500);
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
if (!ssrConfig) return;
|
|
151
|
-
return () => {
|
|
152
|
-
server.middlewares.use(async (req, res, next) => {
|
|
153
|
-
if (req.method !== "GET") return next();
|
|
154
|
-
const url = req.url ?? "/";
|
|
155
|
-
if (isAssetRequest(url)) return next();
|
|
156
|
-
try {
|
|
157
|
-
await handleSsrRequest(server, ssrConfig.entry, url, req, res, next);
|
|
158
|
-
} catch (err) {
|
|
159
|
-
server.ssrFixStacktrace(err);
|
|
160
|
-
next(err);
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
};
|
|
164
|
-
}
|
|
3
|
+
//#region src/index.d.ts
|
|
4
|
+
type CompatFramework = "react" | "preact" | "vue" | "solid";
|
|
5
|
+
interface PyreonPluginOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Alias imports from an existing framework to Pyreon's compat layer.
|
|
8
|
+
*
|
|
9
|
+
* This lets you drop Pyreon into an existing project with zero code changes —
|
|
10
|
+
* `import { useState } from "react"` will resolve to `@pyreon/react-compat`.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* pyreon({ compat: "react" }) // react + react-dom → @pyreon/react-compat
|
|
14
|
+
* pyreon({ compat: "vue" }) // vue → @pyreon/vue-compat
|
|
15
|
+
* pyreon({ compat: "solid" }) // solid-js → @pyreon/solid-compat
|
|
16
|
+
* pyreon({ compat: "preact" }) // preact + hooks + signals → @pyreon/preact-compat
|
|
17
|
+
*/
|
|
18
|
+
compat?: CompatFramework;
|
|
19
|
+
/**
|
|
20
|
+
* Enable SSR dev middleware.
|
|
21
|
+
*
|
|
22
|
+
* Pass an object with `entry` pointing to your server entry file.
|
|
23
|
+
* The entry must export a `handler` function: `(req: Request) => Promise<Response>`
|
|
24
|
+
* or a default export of the same type.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* pyreonPlugin({ ssr: { entry: "./src/entry-server.ts" } })
|
|
28
|
+
*/
|
|
29
|
+
ssr?: {
|
|
30
|
+
/** Server entry file path (e.g. "./src/entry-server.ts") */entry: string;
|
|
165
31
|
};
|
|
166
32
|
}
|
|
167
|
-
|
|
168
|
-
const mod = await server.ssrLoadModule(entry);
|
|
169
|
-
const handler = mod.handler ?? mod.default;
|
|
170
|
-
if (typeof handler !== "function") {
|
|
171
|
-
next();
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
const origin = `http://${req.headers.host ?? "localhost"}`;
|
|
175
|
-
const fullUrl = new URL(url, origin);
|
|
176
|
-
const response = await handler(new Request(fullUrl.href, {
|
|
177
|
-
method: req.method ?? "GET",
|
|
178
|
-
headers: Object.entries(req.headers).reduce((h, [k, v]) => {
|
|
179
|
-
if (v) h.set(k, Array.isArray(v) ? v.join(", ") : v);
|
|
180
|
-
return h;
|
|
181
|
-
}, new Headers())
|
|
182
|
-
}));
|
|
183
|
-
let html = await response.text();
|
|
184
|
-
html = await server.transformIndexHtml(url, html);
|
|
185
|
-
res.statusCode = response.status;
|
|
186
|
-
response.headers.forEach((v, k) => {
|
|
187
|
-
res.setHeader(k, v);
|
|
188
|
-
});
|
|
189
|
-
res.end(html);
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* Generate .pyreon/context.json — project map for AI coding assistants.
|
|
193
|
-
* Delegates to @pyreon/compiler's unified project scanner.
|
|
194
|
-
*/
|
|
195
|
-
function generateProjectContext(root) {
|
|
196
|
-
try {
|
|
197
|
-
const context = generateContext(root);
|
|
198
|
-
const outDir = join(root, ".pyreon");
|
|
199
|
-
if (!existsSync(outDir)) mkdirSync(outDir, {
|
|
200
|
-
recursive: true
|
|
201
|
-
});
|
|
202
|
-
writeFileSync(join(outDir, "context.json"), JSON.stringify(context, null, 2), "utf-8");
|
|
203
|
-
} catch {}
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* Regex that detects signal declarations (prefix + variable name).
|
|
207
|
-
* The arguments are extracted via balanced-paren matching in `injectHmr`.
|
|
208
|
-
* A brace-depth check filters out matches inside functions/blocks — only
|
|
209
|
-
* module-scope (depth 0) signals are rewritten for HMR state preservation.
|
|
210
|
-
*/
|
|
211
|
-
|
|
212
|
-
function skipStringLiteral(code, start, quote) {
|
|
213
|
-
let j = start + 1;
|
|
214
|
-
while (j < code.length) {
|
|
215
|
-
if (code[j] === "\\") {
|
|
216
|
-
j += 2;
|
|
217
|
-
continue;
|
|
218
|
-
}
|
|
219
|
-
if (code[j] === quote) break;
|
|
220
|
-
j++;
|
|
221
|
-
}
|
|
222
|
-
return j;
|
|
223
|
-
}
|
|
224
|
-
function extractBalancedArgs(code, start) {
|
|
225
|
-
let depth = 1;
|
|
226
|
-
for (let i = start; i < code.length; i++) {
|
|
227
|
-
const ch = code[i];
|
|
228
|
-
if (ch === "(") depth++;else if (ch === ")") {
|
|
229
|
-
depth--;
|
|
230
|
-
if (depth === 0) return code.slice(start, i);
|
|
231
|
-
} else if (ch === "\"" || ch === "'" || ch === "`") i = skipStringLiteral(code, i, ch);
|
|
232
|
-
}
|
|
233
|
-
return null;
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* Compute brace depth at position `pos` — returns 0 for module scope.
|
|
237
|
-
* Skips string literals to avoid counting braces inside strings.
|
|
238
|
-
*/
|
|
239
|
-
function braceDepthAt(code, pos) {
|
|
240
|
-
let depth = 0;
|
|
241
|
-
for (let i = 0; i < pos; i++) {
|
|
242
|
-
const ch = code[i];
|
|
243
|
-
if (ch === "{") depth++;else if (ch === "}") depth--;else if (ch === "\"" || ch === "'" || ch === "`") i = skipStringLiteral(code, i, ch);
|
|
244
|
-
}
|
|
245
|
-
return depth;
|
|
246
|
-
}
|
|
247
|
-
/** Rewrite module-scope `signal()` calls to `__hmr_signal()` for state preservation. */
|
|
248
|
-
function rewriteSignals(code, moduleId) {
|
|
249
|
-
const escapedId = JSON.stringify(moduleId);
|
|
250
|
-
const matches = [];
|
|
251
|
-
let m = SIGNAL_PREFIX_RE.exec(code);
|
|
252
|
-
while (m !== null) {
|
|
253
|
-
const argsStart = m.index + m[0].length;
|
|
254
|
-
const args = extractBalancedArgs(code, argsStart);
|
|
255
|
-
if (args === null) {
|
|
256
|
-
m = SIGNAL_PREFIX_RE.exec(code);
|
|
257
|
-
continue;
|
|
258
|
-
}
|
|
259
|
-
if (braceDepthAt(code, m.index) === 0) matches.push({
|
|
260
|
-
start: m.index,
|
|
261
|
-
end: argsStart + args.length + 1,
|
|
262
|
-
prefix: m[1] ?? "",
|
|
263
|
-
name: m[2] ?? "",
|
|
264
|
-
args
|
|
265
|
-
});
|
|
266
|
-
m = SIGNAL_PREFIX_RE.exec(code);
|
|
267
|
-
}
|
|
268
|
-
SIGNAL_PREFIX_RE.lastIndex = 0;
|
|
269
|
-
let output = code;
|
|
270
|
-
for (let i = matches.length - 1; i >= 0; i--) {
|
|
271
|
-
const {
|
|
272
|
-
start,
|
|
273
|
-
end,
|
|
274
|
-
prefix,
|
|
275
|
-
name,
|
|
276
|
-
args
|
|
277
|
-
} = matches[i];
|
|
278
|
-
const replacement = `${prefix}__hmr_signal(${escapedId}, ${JSON.stringify(name)}, signal, ${args})`;
|
|
279
|
-
output = output.slice(0, start) + replacement + output.slice(end);
|
|
280
|
-
}
|
|
281
|
-
return output;
|
|
282
|
-
}
|
|
283
|
-
function injectHmr(code, moduleId) {
|
|
284
|
-
const hasSignals = SIGNAL_PREFIX_RE.test(code);
|
|
285
|
-
SIGNAL_PREFIX_RE.lastIndex = 0;
|
|
286
|
-
if (!EXPORT_COMPONENT_RE.test(code) && !hasSignals) return code;
|
|
287
|
-
let output = hasSignals ? rewriteSignals(code, moduleId) : code;
|
|
288
|
-
const escapedId = JSON.stringify(moduleId);
|
|
289
|
-
const lines = [];
|
|
290
|
-
if (hasSignals) lines.push(`import { __hmr_signal, __hmr_dispose } from "${HMR_RUNTIME_IMPORT}";`);
|
|
291
|
-
lines.push(`if (import.meta.hot) {`);
|
|
292
|
-
if (hasSignals) lines.push(` import.meta.hot.dispose(() => __hmr_dispose(${escapedId}));`);
|
|
293
|
-
lines.push(` import.meta.hot.accept();`);
|
|
294
|
-
lines.push(`}`);
|
|
295
|
-
output = `${output}\n\n${lines.join("\n")}\n`;
|
|
296
|
-
return output;
|
|
297
|
-
}
|
|
298
|
-
function getExt(id) {
|
|
299
|
-
const clean = id.split("?")[0] ?? id;
|
|
300
|
-
const dot = clean.lastIndexOf(".");
|
|
301
|
-
return dot >= 0 ? clean.slice(dot) : "";
|
|
302
|
-
}
|
|
303
|
-
/** Skip Vite-handled asset requests (CSS, images, HMR, etc.) */
|
|
304
|
-
function isAssetRequest(url) {
|
|
305
|
-
return url.startsWith("/@") || url.startsWith("/__") || url.includes("/node_modules/") || /\.(css|js|ts|tsx|jsx|json|ico|png|jpg|jpeg|gif|svg|woff2?|ttf|eot|map)(\?|$)/.test(url);
|
|
306
|
-
}
|
|
33
|
+
declare function pyreonPlugin(options?: PyreonPluginOptions): Plugin;
|
|
307
34
|
//#endregion
|
|
308
|
-
export { pyreonPlugin as default };
|
|
309
|
-
//# sourceMappingURL=
|
|
35
|
+
export { CompatFramework, PyreonPluginOptions, pyreonPlugin as default };
|
|
36
|
+
//# sourceMappingURL=index2.d.ts.map
|
package/lib/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"
|
|
1
|
+
{"version":3,"file":"index2.d.ts","names":[],"sources":["../../../src/index.ts"],"mappings":";;;KA2CY,eAAA;AAAA,UAEK,mBAAA;;;;;;;;;;;;;EAaf,MAAA,GAAS,eAAA;;;;;;;;;;;EAYT,GAAA;gEAEE,KAAA;EAAA;AAAA;AAAA,iBAkGoB,YAAA,CAAa,OAAA,GAAU,mBAAA,GAAsB,MAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/vite-plugin",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.7",
|
|
4
4
|
"description": "Vite plugin for Pyreon — .pyreon SFC support, HMR, compiler integration",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"prepublishOnly": "bun run build"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@pyreon/compiler": "^0.5.
|
|
42
|
+
"@pyreon/compiler": "^0.5.7"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"vite": ">=8.0.0"
|