@pracht/vite-plugin 0.3.2 → 0.4.1
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/README.md +22 -0
- package/dist/index.d.mts +4 -2
- package/dist/index.mjs +79 -54
- package/dist/pages-router.mjs +30 -10
- package/package.json +16 -3
package/README.md
CHANGED
|
@@ -27,6 +27,28 @@ export default defineConfig({
|
|
|
27
27
|
- Pre-renders SSG and ISG routes at build time (`prerenderConcurrency` controls parallelism)
|
|
28
28
|
- Provides HMR during development
|
|
29
29
|
|
|
30
|
+
## TSRX (`.tsrx`) Support
|
|
31
|
+
|
|
32
|
+
`.tsrx` modules — TSRX/Ripple-flavoured Preact components — are supported out
|
|
33
|
+
of the box. Bring your own
|
|
34
|
+
[`@tsrx/vite-plugin-preact`](https://github.com/Ripple-TS/ripple) and add it to
|
|
35
|
+
your `plugins` array alongside `pracht()`:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
// vite.config.ts
|
|
39
|
+
import { defineConfig } from "vite";
|
|
40
|
+
import { pracht } from "@pracht/vite-plugin";
|
|
41
|
+
import { tsrxPreact } from "@tsrx/vite-plugin-preact";
|
|
42
|
+
|
|
43
|
+
export default defineConfig({
|
|
44
|
+
plugins: [tsrxPreact(), pracht()],
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The pracht plugin globs `.tsrx` files alongside `.tsx` for routes and shells
|
|
49
|
+
(both manifest- and pages-router modes), and its server-only export stripping
|
|
50
|
+
pass treats them the same way — no separate pracht option is required.
|
|
51
|
+
|
|
30
52
|
## Peer Dependencies
|
|
31
53
|
|
|
32
54
|
- `vite@^8.0.0`
|
package/dist/index.d.mts
CHANGED
|
@@ -30,7 +30,7 @@ interface PrachtAdapter {
|
|
|
30
30
|
* Additional Vite plugins the adapter needs (e.g. `@cloudflare/vite-plugin`).
|
|
31
31
|
* Returned plugins are appended to the plugin array returned by `pracht()`.
|
|
32
32
|
*/
|
|
33
|
-
vitePlugins?(): Plugin[]
|
|
33
|
+
vitePlugins?(): Plugin[];
|
|
34
34
|
/**
|
|
35
35
|
* If true, the adapter owns dev-server request handling and the vite-plugin
|
|
36
36
|
* will not install its own SSR middleware. Used when the adapter contributes
|
|
@@ -62,6 +62,8 @@ interface PrachtPluginOptions {
|
|
|
62
62
|
pagesDefaultRender?: RenderMode$1;
|
|
63
63
|
/** Maximum number of SSG/ISG pages rendered concurrently during `pracht build`. */
|
|
64
64
|
prerenderConcurrency?: number;
|
|
65
|
+
/** Maximum request body size (bytes) accepted by the dev SSR middleware. Defaults to 1 MiB. */
|
|
66
|
+
maxBodySize?: number;
|
|
65
67
|
}
|
|
66
68
|
//#endregion
|
|
67
69
|
//#region src/plugin-codegen.d.ts
|
|
@@ -75,6 +77,6 @@ declare function createPrachtServerModuleSource(options?: PrachtPluginOptions, b
|
|
|
75
77
|
declare function createPrachtRegistryModuleSource(options?: PrachtPluginOptions): string;
|
|
76
78
|
//#endregion
|
|
77
79
|
//#region src/index.d.ts
|
|
78
|
-
declare function pracht(options?: PrachtPluginOptions):
|
|
80
|
+
declare function pracht(options?: PrachtPluginOptions): Plugin[];
|
|
79
81
|
//#endregion
|
|
80
82
|
export { PRACHT_CLIENT_MODULE_ID, PRACHT_SERVER_MODULE_ID, type PrachtAdapter, type PrachtPluginOptions, type RenderMode, createPrachtClientModuleSource, createPrachtRegistryModuleSource, createPrachtServerModuleSource, pracht };
|
package/dist/index.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import preact from "@preact/preset-vite";
|
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { parseAst } from "vite";
|
|
5
5
|
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
+
import { createNodeServerEntryModule } from "@pracht/adapter-node";
|
|
6
7
|
//#region src/client-module-query.ts
|
|
7
8
|
const CLIENT_MODULE_QUERY = "pracht-client";
|
|
8
9
|
const PRACHT_CLIENT_MODULE_QUERY = `?${CLIENT_MODULE_QUERY}`;
|
|
@@ -21,6 +22,7 @@ function stripPrachtClientModuleQuery(id) {
|
|
|
21
22
|
function getRolldownLang(id) {
|
|
22
23
|
const path = stripPrachtClientModuleQuery(id).split("?")[0];
|
|
23
24
|
if (/\.(c|m)?tsx$/i.test(path)) return "tsx";
|
|
25
|
+
if (/\.tsrx$/i.test(path)) return "tsx";
|
|
24
26
|
if (/\.(c|m)?ts$/i.test(path)) return "ts";
|
|
25
27
|
if (/\.(c|m)?jsx$/i.test(path)) return "jsx";
|
|
26
28
|
if (/\.mdx?$/i.test(path)) return "jsx";
|
|
@@ -945,41 +947,7 @@ function createDefaultNodeAdapter() {
|
|
|
945
947
|
id: "node",
|
|
946
948
|
serverImports: "import { resolveApp, resolveApiRoutes } from \"@pracht/core\";",
|
|
947
949
|
createServerEntryModule() {
|
|
948
|
-
return
|
|
949
|
-
"import { existsSync, readFileSync } from \"node:fs\";",
|
|
950
|
-
"import { createServer } from \"node:http\";",
|
|
951
|
-
"import { dirname, resolve } from \"node:path\";",
|
|
952
|
-
"import { fileURLToPath, pathToFileURL } from \"node:url\";",
|
|
953
|
-
"import { createNodeRequestHandler } from \"@pracht/adapter-node\";",
|
|
954
|
-
"",
|
|
955
|
-
"const serverDir = dirname(fileURLToPath(import.meta.url));",
|
|
956
|
-
"const staticDir = resolve(serverDir, \"../client\");",
|
|
957
|
-
"const isgManifestPath = resolve(serverDir, \"isg-manifest.json\");",
|
|
958
|
-
"const isgManifest = existsSync(isgManifestPath)",
|
|
959
|
-
" ? JSON.parse(readFileSync(isgManifestPath, \"utf-8\"))",
|
|
960
|
-
" : {};",
|
|
961
|
-
"",
|
|
962
|
-
"export const handler = createNodeRequestHandler({",
|
|
963
|
-
" app: resolvedApp,",
|
|
964
|
-
" registry,",
|
|
965
|
-
" staticDir,",
|
|
966
|
-
" isgManifest,",
|
|
967
|
-
" apiRoutes,",
|
|
968
|
-
" clientEntryUrl: clientEntryUrl ?? undefined,",
|
|
969
|
-
" cssManifest,",
|
|
970
|
-
" jsManifest,",
|
|
971
|
-
"});",
|
|
972
|
-
"",
|
|
973
|
-
"const entryHref = process.argv[1] ? pathToFileURL(process.argv[1]).href : null;",
|
|
974
|
-
"if (entryHref && import.meta.url === entryHref) {",
|
|
975
|
-
" const server = createServer(handler);",
|
|
976
|
-
" const port = Number(process.env.PORT ?? 3000);",
|
|
977
|
-
" server.listen(port, () => {",
|
|
978
|
-
" console.log(`pracht node server listening on http://localhost:${port}`);",
|
|
979
|
-
" });",
|
|
980
|
-
"}",
|
|
981
|
-
""
|
|
982
|
-
].join("\n");
|
|
950
|
+
return createNodeServerEntryModule();
|
|
983
951
|
}
|
|
984
952
|
};
|
|
985
953
|
}
|
|
@@ -995,7 +963,8 @@ const DEFAULTS = {
|
|
|
995
963
|
adapter: createDefaultNodeAdapter(),
|
|
996
964
|
pagesDir: "",
|
|
997
965
|
pagesDefaultRender: "ssr",
|
|
998
|
-
prerenderConcurrency: 10
|
|
966
|
+
prerenderConcurrency: 10,
|
|
967
|
+
maxBodySize: 1024 * 1024
|
|
999
968
|
};
|
|
1000
969
|
function resolveOptions(options) {
|
|
1001
970
|
const resolved = {
|
|
@@ -1003,6 +972,7 @@ function resolveOptions(options) {
|
|
|
1003
972
|
...options
|
|
1004
973
|
};
|
|
1005
974
|
if (!Number.isInteger(resolved.prerenderConcurrency) || resolved.prerenderConcurrency <= 0) throw new Error("pracht({ prerenderConcurrency }) expects a positive integer.");
|
|
975
|
+
if (!Number.isInteger(resolved.maxBodySize) || resolved.maxBodySize <= 0) throw new Error("pracht({ maxBodySize }) expects a positive integer number of bytes.");
|
|
1006
976
|
return resolved;
|
|
1007
977
|
}
|
|
1008
978
|
//#endregion
|
|
@@ -1011,14 +981,23 @@ function createPrachtClientModuleSource(options = {}, buildOptions = {}) {
|
|
|
1011
981
|
const resolved = resolveOptions(options);
|
|
1012
982
|
const isPagesMode = !!resolved.pagesDir;
|
|
1013
983
|
const appImport = isPagesMode ? generatePagesAppInlineSource(resolved, buildOptions.root) : `import { app } from ${JSON.stringify(resolved.appFile)};`;
|
|
1014
|
-
const
|
|
984
|
+
const dirPrefix = isPagesMode ? resolved.pagesDir : resolved.routesDir;
|
|
985
|
+
const routeGlob = `${dirPrefix}/**/*.{ts,tsx,js,jsx,md,mdx}`;
|
|
986
|
+
const routeTsrxGlob = `${dirPrefix}/**/*.tsrx`;
|
|
1015
987
|
const shellGlob = isPagesMode ? `${resolved.pagesDir}/**/_app.{ts,tsx,js,jsx}` : `${resolved.shellsDir}/**/*.{ts,tsx,js,jsx,md,mdx}`;
|
|
988
|
+
const shellTsrxGlob = isPagesMode ? `${resolved.pagesDir}/**/_app.tsrx` : `${resolved.shellsDir}/**/*.tsrx`;
|
|
1016
989
|
return [
|
|
1017
990
|
"import { resolveApp, initClientRouter, readHydrationState } from \"@pracht/core\";",
|
|
1018
991
|
appImport,
|
|
1019
992
|
"",
|
|
1020
|
-
`const routeModules =
|
|
1021
|
-
`
|
|
993
|
+
`const routeModules = {`,
|
|
994
|
+
` ...import.meta.glob(${JSON.stringify(routeGlob)}, { query: ${JSON.stringify(PRACHT_CLIENT_MODULE_QUERY)} }),`,
|
|
995
|
+
` ...import.meta.glob(${JSON.stringify(routeTsrxGlob)}),`,
|
|
996
|
+
`};`,
|
|
997
|
+
`const shellModules = {`,
|
|
998
|
+
` ...import.meta.glob(${JSON.stringify(shellGlob)}, { query: ${JSON.stringify(PRACHT_CLIENT_MODULE_QUERY)} }),`,
|
|
999
|
+
` ...import.meta.glob(${JSON.stringify(shellTsrxGlob)}),`,
|
|
1000
|
+
`};`,
|
|
1022
1001
|
"",
|
|
1023
1002
|
"const resolvedApp = resolveApp(app);",
|
|
1024
1003
|
"",
|
|
@@ -1097,10 +1076,18 @@ function createPrachtRegistryModuleSource(options = {}) {
|
|
|
1097
1076
|
const resolved = resolveOptions(options);
|
|
1098
1077
|
const isPagesMode = !!resolved.pagesDir;
|
|
1099
1078
|
const routeGlob = isPagesMode ? `${resolved.pagesDir}/**/*.{ts,tsx,js,jsx,md,mdx}` : `${resolved.routesDir}/**/*.{ts,tsx,js,jsx,md,mdx}`;
|
|
1079
|
+
const routeTsrxGlob = isPagesMode ? `${resolved.pagesDir}/**/*.tsrx` : `${resolved.routesDir}/**/*.tsrx`;
|
|
1100
1080
|
const shellGlob = isPagesMode ? `${resolved.pagesDir}/**/_app.{ts,tsx,js,jsx}` : `${resolved.shellsDir}/**/*.{ts,tsx,js,jsx,md,mdx}`;
|
|
1081
|
+
const shellTsrxGlob = isPagesMode ? `${resolved.pagesDir}/**/_app.tsrx` : `${resolved.shellsDir}/**/*.tsrx`;
|
|
1101
1082
|
return [
|
|
1102
|
-
`export const routeModules =
|
|
1103
|
-
`
|
|
1083
|
+
`export const routeModules = {`,
|
|
1084
|
+
` ...import.meta.glob(${JSON.stringify(routeGlob)}),`,
|
|
1085
|
+
` ...import.meta.glob(${JSON.stringify(routeTsrxGlob)}),`,
|
|
1086
|
+
`};`,
|
|
1087
|
+
`export const shellModules = {`,
|
|
1088
|
+
` ...import.meta.glob(${JSON.stringify(shellGlob)}),`,
|
|
1089
|
+
` ...import.meta.glob(${JSON.stringify(shellTsrxGlob)}),`,
|
|
1090
|
+
`};`,
|
|
1104
1091
|
`export const middlewareModules = import.meta.glob(${JSON.stringify(`${resolved.middlewareDir}/**/*.{ts,tsx,js,jsx}`)});`,
|
|
1105
1092
|
`export const apiModules = import.meta.glob(${JSON.stringify(`${resolved.apiDir}/**/*.{ts,js,tsx,jsx}`)});`,
|
|
1106
1093
|
`export const dataModules = import.meta.glob(${JSON.stringify(`${resolved.serverDir}/**/*.{ts,js,tsx,jsx}`)});`,
|
|
@@ -1138,8 +1125,9 @@ function generatePagesAppInlineSource(options, root = process.cwd()) {
|
|
|
1138
1125
|
//#endregion
|
|
1139
1126
|
//#region src/plugin-dev-ssr.ts
|
|
1140
1127
|
const BODYLESS_METHODS = new Set(["GET", "HEAD"]);
|
|
1141
|
-
const
|
|
1142
|
-
function createDevSSRMiddleware(server) {
|
|
1128
|
+
const DEFAULT_MAX_BODY_SIZE = 1024 * 1024;
|
|
1129
|
+
function createDevSSRMiddleware(server, options = {}) {
|
|
1130
|
+
const maxBodySize = options.maxBodySize ?? DEFAULT_MAX_BODY_SIZE;
|
|
1143
1131
|
return async (req, res, next) => {
|
|
1144
1132
|
const url = req.url ?? "/";
|
|
1145
1133
|
const pathname = new URL(url, "http://localhost").pathname;
|
|
@@ -1148,7 +1136,7 @@ function createDevSSRMiddleware(server) {
|
|
|
1148
1136
|
const [framework, serverMod] = await Promise.all([server.ssrLoadModule("@pracht/core"), server.ssrLoadModule(PRACHT_SERVER_MODULE_ID)]);
|
|
1149
1137
|
let webRequest;
|
|
1150
1138
|
try {
|
|
1151
|
-
webRequest = await nodeToWebRequest(req);
|
|
1139
|
+
webRequest = await nodeToWebRequest(req, maxBodySize);
|
|
1152
1140
|
} catch (err) {
|
|
1153
1141
|
if (err instanceof Error && err.message === "Request body too large") {
|
|
1154
1142
|
res.statusCode = 413;
|
|
@@ -1192,7 +1180,7 @@ async function handleDevError(server, req, res, next, url, error) {
|
|
|
1192
1180
|
return;
|
|
1193
1181
|
}
|
|
1194
1182
|
try {
|
|
1195
|
-
const { buildErrorOverlayHtml } = await server.ssrLoadModule("pracht/error-overlay");
|
|
1183
|
+
const { buildErrorOverlayHtml } = await server.ssrLoadModule("@pracht/core/error-overlay");
|
|
1196
1184
|
let html = buildErrorOverlayHtml({
|
|
1197
1185
|
message: error instanceof Error ? error.message : String(error),
|
|
1198
1186
|
stack: error instanceof Error ? error.stack : void 0
|
|
@@ -1205,7 +1193,7 @@ async function handleDevError(server, req, res, next, url, error) {
|
|
|
1205
1193
|
next(error);
|
|
1206
1194
|
}
|
|
1207
1195
|
}
|
|
1208
|
-
async function nodeToWebRequest(req) {
|
|
1196
|
+
async function nodeToWebRequest(req, maxBodySize) {
|
|
1209
1197
|
const protocol = "http";
|
|
1210
1198
|
const host = req.headers.host ?? "localhost";
|
|
1211
1199
|
const url = new URL(req.url ?? "/", `${protocol}://${host}`);
|
|
@@ -1226,10 +1214,7 @@ async function nodeToWebRequest(req) {
|
|
|
1226
1214
|
for await (const chunk of req) {
|
|
1227
1215
|
const buf = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
1228
1216
|
totalSize += buf.byteLength;
|
|
1229
|
-
if (totalSize >
|
|
1230
|
-
req.destroy();
|
|
1231
|
-
throw new Error("Request body too large");
|
|
1232
|
-
}
|
|
1217
|
+
if (totalSize > maxBodySize) throw new Error("Request body too large");
|
|
1233
1218
|
chunks.push(buf);
|
|
1234
1219
|
}
|
|
1235
1220
|
const body = Buffer.concat(chunks);
|
|
@@ -1239,7 +1224,7 @@ async function nodeToWebRequest(req) {
|
|
|
1239
1224
|
}
|
|
1240
1225
|
//#endregion
|
|
1241
1226
|
//#region src/index.ts
|
|
1242
|
-
|
|
1227
|
+
function pracht(options = {}) {
|
|
1243
1228
|
const resolved = resolveOptions(options);
|
|
1244
1229
|
const isPagesMode = !!resolved.pagesDir;
|
|
1245
1230
|
let root = process.cwd();
|
|
@@ -1292,7 +1277,7 @@ async function pracht(options = {}) {
|
|
|
1292
1277
|
if (isPagesMode) watchPagesDirectory(server, resolved, root);
|
|
1293
1278
|
if (resolved.adapter.ownsDevServer) return;
|
|
1294
1279
|
return () => {
|
|
1295
|
-
server.middlewares.use(createDevSSRMiddleware(server));
|
|
1280
|
+
server.middlewares.use(createDevSSRMiddleware(server, { maxBodySize: resolved.maxBodySize }));
|
|
1296
1281
|
};
|
|
1297
1282
|
},
|
|
1298
1283
|
handleHotUpdate({ file, server }) {
|
|
@@ -1333,15 +1318,54 @@ async function pracht(options = {}) {
|
|
|
1333
1318
|
};
|
|
1334
1319
|
}
|
|
1335
1320
|
};
|
|
1321
|
+
const optimizeDepsEntriesPlugin = {
|
|
1322
|
+
name: "pracht:optimize-deps-entries",
|
|
1323
|
+
enforce: "post",
|
|
1324
|
+
config(config) {
|
|
1325
|
+
return withPrachtOptimizeDepsEntries(config, createPrachtOptimizeDepsEntries(resolved));
|
|
1326
|
+
}
|
|
1327
|
+
};
|
|
1336
1328
|
const plugins = [
|
|
1337
1329
|
...preact(),
|
|
1338
1330
|
prachtPlugin,
|
|
1339
1331
|
clientModuleTransformPlugin
|
|
1340
1332
|
];
|
|
1341
|
-
const adapterPlugins =
|
|
1333
|
+
const adapterPlugins = resolved.adapter.vitePlugins?.();
|
|
1342
1334
|
if (adapterPlugins?.length) plugins.push(...adapterPlugins);
|
|
1335
|
+
plugins.push(optimizeDepsEntriesPlugin);
|
|
1343
1336
|
return plugins;
|
|
1344
1337
|
}
|
|
1338
|
+
function withPrachtOptimizeDepsEntries(config, prachtEntries) {
|
|
1339
|
+
const environments = Object.fromEntries(Object.entries(config.environments ?? {}).map(([name, environment]) => [name, { optimizeDeps: { entries: mergeOptimizeDepsEntries(environment.optimizeDeps?.entries, prachtEntries) } }]));
|
|
1340
|
+
return {
|
|
1341
|
+
optimizeDeps: { entries: mergeOptimizeDepsEntries(config.optimizeDeps?.entries, prachtEntries) },
|
|
1342
|
+
...Object.keys(environments).length > 0 ? { environments } : {}
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
function createPrachtOptimizeDepsEntries(resolved) {
|
|
1346
|
+
const scriptExtensions = "{ts,tsx,js,jsx}";
|
|
1347
|
+
const routeExtensions = "{ts,tsx,js,jsx,md,mdx,tsrx}";
|
|
1348
|
+
const entries = resolved.pagesDir ? [
|
|
1349
|
+
`${toOptimizeDepsEntry(resolved.pagesDir)}/**/*.${routeExtensions}`,
|
|
1350
|
+
`${toOptimizeDepsEntry(resolved.middlewareDir)}/**/*.${scriptExtensions}`,
|
|
1351
|
+
`${toOptimizeDepsEntry(resolved.apiDir)}/**/*.{ts,js,tsx,jsx}`,
|
|
1352
|
+
`${toOptimizeDepsEntry(resolved.serverDir)}/**/*.{ts,js,tsx,jsx}`
|
|
1353
|
+
] : [
|
|
1354
|
+
toOptimizeDepsEntry(resolved.appFile),
|
|
1355
|
+
`${toOptimizeDepsEntry(resolved.routesDir)}/**/*.${routeExtensions}`,
|
|
1356
|
+
`${toOptimizeDepsEntry(resolved.shellsDir)}/**/*.${routeExtensions}`,
|
|
1357
|
+
`${toOptimizeDepsEntry(resolved.middlewareDir)}/**/*.${scriptExtensions}`,
|
|
1358
|
+
`${toOptimizeDepsEntry(resolved.apiDir)}/**/*.{ts,js,tsx,jsx}`,
|
|
1359
|
+
`${toOptimizeDepsEntry(resolved.serverDir)}/**/*.{ts,js,tsx,jsx}`
|
|
1360
|
+
];
|
|
1361
|
+
return [...new Set(entries.filter(Boolean))];
|
|
1362
|
+
}
|
|
1363
|
+
function mergeOptimizeDepsEntries(userEntries, prachtEntries) {
|
|
1364
|
+
return [...new Set([...Array.isArray(userEntries) ? userEntries : userEntries ? [userEntries] : [], ...prachtEntries])];
|
|
1365
|
+
}
|
|
1366
|
+
function toOptimizeDepsEntry(path) {
|
|
1367
|
+
return toPosixPath(path).replace(/^\.\//, "").replace(/^\//, "").replace(/\/$/, "");
|
|
1368
|
+
}
|
|
1345
1369
|
function watchPagesDirectory(server, resolved, root) {
|
|
1346
1370
|
const abs = resolveConfigPath(root, resolved.pagesDir);
|
|
1347
1371
|
server.watcher.on("add", (f) => {
|
|
@@ -1369,7 +1393,8 @@ const ROUTE_FILE_EXTENSIONS = new Set([
|
|
|
1369
1393
|
".js",
|
|
1370
1394
|
".jsx",
|
|
1371
1395
|
".md",
|
|
1372
|
-
".mdx"
|
|
1396
|
+
".mdx",
|
|
1397
|
+
".tsrx"
|
|
1373
1398
|
]);
|
|
1374
1399
|
function computeRouteFileDirs(root, resolved) {
|
|
1375
1400
|
return (resolved.pagesDir ? [resolved.pagesDir] : [resolved.routesDir, resolved.shellsDir]).map((dir) => resolveConfigPath(root, dir)).map(withTrailingSep);
|
package/dist/pages-router.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
|
3
3
|
//#region src/pages-router.ts
|
|
4
4
|
const PAGE_EXTENSIONS = new Set([
|
|
5
5
|
".tsx",
|
|
6
|
+
".tsrx",
|
|
6
7
|
".ts",
|
|
7
8
|
".jsx",
|
|
8
9
|
".js",
|
|
@@ -11,6 +12,7 @@ const PAGE_EXTENSIONS = new Set([
|
|
|
11
12
|
]);
|
|
12
13
|
const SHELL_EXTENSIONS = new Set([
|
|
13
14
|
".tsx",
|
|
15
|
+
".tsrx",
|
|
14
16
|
".ts",
|
|
15
17
|
".jsx",
|
|
16
18
|
".js"
|
|
@@ -45,14 +47,14 @@ function scan(dir, root, pages) {
|
|
|
45
47
|
relativePath: rel,
|
|
46
48
|
routePath,
|
|
47
49
|
isIndex: name === "index",
|
|
48
|
-
isCatchAll:
|
|
49
|
-
isDynamic:
|
|
50
|
+
isCatchAll: routePath.split("/").includes("*"),
|
|
51
|
+
isDynamic: routePath.split("/").some((segment) => segment.startsWith(":")),
|
|
50
52
|
renderMode
|
|
51
53
|
});
|
|
52
54
|
}
|
|
53
55
|
}
|
|
54
56
|
function filePathToRoutePath(relativePath) {
|
|
55
|
-
let route = relativePath.replace(/\.(tsx?|jsx?|mdx?)$/, "");
|
|
57
|
+
let route = relativePath.replace(/\.(tsx?|tsrx|jsx?|mdx?)$/, "");
|
|
56
58
|
route = route.replace(/\\/g, "/");
|
|
57
59
|
if (route === "_app" || route.endsWith("/_app")) return "__shell__";
|
|
58
60
|
if (route === "index") return "/";
|
|
@@ -62,13 +64,31 @@ function filePathToRoutePath(relativePath) {
|
|
|
62
64
|
return `/${route}`;
|
|
63
65
|
}
|
|
64
66
|
function sortRoutes(pages) {
|
|
65
|
-
return [...pages].filter((p) => p.routePath !== "__shell__").sort(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
return [...pages].filter((p) => p.routePath !== "__shell__").sort(comparePagesBySpecificity);
|
|
68
|
+
}
|
|
69
|
+
function comparePagesBySpecificity(left, right) {
|
|
70
|
+
const leftSegments = splitRoutePath(left.routePath);
|
|
71
|
+
const rightSegments = splitRoutePath(right.routePath);
|
|
72
|
+
const length = Math.max(leftSegments.length, rightSegments.length);
|
|
73
|
+
for (let index = 0; index < length; index += 1) {
|
|
74
|
+
const leftSegment = leftSegments[index];
|
|
75
|
+
const rightSegment = rightSegments[index];
|
|
76
|
+
if (!leftSegment) return -1;
|
|
77
|
+
if (!rightSegment) return 1;
|
|
78
|
+
const leftScore = getRouteSegmentSpecificity(leftSegment);
|
|
79
|
+
const rightScore = getRouteSegmentSpecificity(rightSegment);
|
|
80
|
+
if (leftScore !== rightScore) return rightScore - leftScore;
|
|
81
|
+
if (leftScore === 3 && leftSegment !== rightSegment) return leftSegment.localeCompare(rightSegment);
|
|
82
|
+
}
|
|
83
|
+
return left.routePath.localeCompare(right.routePath);
|
|
84
|
+
}
|
|
85
|
+
function splitRoutePath(routePath) {
|
|
86
|
+
return routePath.split("/").filter(Boolean);
|
|
87
|
+
}
|
|
88
|
+
function getRouteSegmentSpecificity(segment) {
|
|
89
|
+
if (segment === "*") return 1;
|
|
90
|
+
if (segment.startsWith(":")) return 2;
|
|
91
|
+
return 3;
|
|
72
92
|
}
|
|
73
93
|
const RENDER_MODE_RE = /export\s+const\s+RENDER_MODE\s*=\s*["'](\w+)["']/;
|
|
74
94
|
function extractRenderMode(source) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pracht/vite-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
|
+
"description": "Vite plugin for Pracht apps with virtual modules, dev SSR, prerendering, route inspection, and multi-adapter builds.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pracht",
|
|
7
|
+
"preact",
|
|
8
|
+
"vite",
|
|
9
|
+
"vite-plugin",
|
|
10
|
+
"ssr",
|
|
11
|
+
"ssg",
|
|
12
|
+
"prerender",
|
|
13
|
+
"routing",
|
|
14
|
+
"cloudflare",
|
|
15
|
+
"vercel"
|
|
16
|
+
],
|
|
4
17
|
"license": "MIT",
|
|
5
18
|
"homepage": "https://github.com/JoviDeCroock/pracht/tree/main/packages/vite-plugin",
|
|
6
19
|
"bugs": {
|
|
@@ -31,8 +44,8 @@
|
|
|
31
44
|
"dependencies": {
|
|
32
45
|
"@preact/preset-vite": "^2.10.5",
|
|
33
46
|
"@prefresh/vite": "^2.0.0",
|
|
34
|
-
"@pracht/adapter-node": "0.1
|
|
35
|
-
"@pracht/core": "0.
|
|
47
|
+
"@pracht/adapter-node": "0.2.1",
|
|
48
|
+
"@pracht/core": "0.6.1"
|
|
36
49
|
},
|
|
37
50
|
"peerDependencies": {
|
|
38
51
|
"vite": "^8.0.0"
|