@pracht/vite-plugin 0.8.0 → 0.9.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/README.md +23 -9
- package/dist/index.d.mts +22 -0
- package/dist/index.mjs +356 -90
- package/dist/pages-router-KcCRkhnf.mjs +588 -0
- package/dist/pages-router.d.mts +3 -1
- package/dist/pages-router.mjs +1 -1
- package/package.json +6 -4
- package/dist/pages-router-BQlG21oC.mjs +0 -319
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as scanPagesDirectory, n as generatePagesManifestSource, o as createRouteLoaderHints } from "./pages-router-
|
|
1
|
+
import { c as createRouteStaticPathsHints, d as extensionGlob, f as normalizeAdditionalExtensions, i as scanPagesDirectory, l as DEFAULT_ROUTE_EXTENSIONS, n as generatePagesManifestSource, o as createRouteHeadHints, p as withAdditionalExtensions, s as createRouteLoaderHints, u as LEGACY_BARE_ROUTE_EXTENSIONS } from "./pages-router-KcCRkhnf.mjs";
|
|
2
2
|
import { createRequire, isBuiltin } from "node:module";
|
|
3
3
|
import { preactSsrPrecompile } from "@pracht/preact-ssr-precompile";
|
|
4
4
|
import preact from "@preact/preset-vite";
|
|
@@ -9,6 +9,7 @@ import { PRACHT_GRAPH_ONLY_ENV } from "@pracht/core/server";
|
|
|
9
9
|
import { CAPABILITY_SETTLED_EVENT, CAPABILITY_TRANSPORT_HEADER, CONFIRMATION_HEADER } from "@pracht/capabilities";
|
|
10
10
|
import { extractCapabilityProjection, extractCapabilityRegistrations, extractDefineAppObjectBody, scanTopLevelProperties } from "@pracht/capabilities/static";
|
|
11
11
|
import { createNodeServerEntryModule } from "@pracht/adapter-node";
|
|
12
|
+
import { Readable } from "node:stream";
|
|
12
13
|
import { applyDefaultSecurityHeaders, resolveRegistryModule } from "@pracht/core";
|
|
13
14
|
//#region src/client-module-query.ts
|
|
14
15
|
const CLIENT_MODULE_QUERY = "pracht-client";
|
|
@@ -28,7 +29,6 @@ function stripPrachtClientModuleQuery(id) {
|
|
|
28
29
|
function getRolldownLang(id) {
|
|
29
30
|
const path = stripPrachtClientModuleQuery(id).split("?")[0];
|
|
30
31
|
if (/\.(c|m)?tsx$/i.test(path)) return "tsx";
|
|
31
|
-
if (/\.tsrx$/i.test(path)) return "tsx";
|
|
32
32
|
if (/\.(c|m)?ts$/i.test(path)) return "ts";
|
|
33
33
|
if (/\.(c|m)?jsx$/i.test(path)) return "jsx";
|
|
34
34
|
if (/\.mdx?$/i.test(path)) return "jsx";
|
|
@@ -1258,7 +1258,15 @@ const PRACHT_CAPABILITIES_MODULE_ID = "virtual:pracht/capabilities";
|
|
|
1258
1258
|
const PRACHT_WEBMCP_MODULE_ID = "virtual:pracht/webmcp";
|
|
1259
1259
|
const CLIENT_BROWSER_PATH = "/@pracht/client.js";
|
|
1260
1260
|
const ISLANDS_CLIENT_BROWSER_PATH = "/@pracht/islands.js";
|
|
1261
|
-
|
|
1261
|
+
/**
|
|
1262
|
+
* `base` prefixes every emitted asset URL. Vite normalizes it to leading and
|
|
1263
|
+
* trailing slashes; a CDN base (absolute or protocol-relative) is used as-is,
|
|
1264
|
+
* so the manifest paths still resolve.
|
|
1265
|
+
*/
|
|
1266
|
+
function assetUrl(file, base) {
|
|
1267
|
+
return `${base}${file}`;
|
|
1268
|
+
}
|
|
1269
|
+
function readClientBuildAssets(root = process.cwd(), base = "/") {
|
|
1262
1270
|
const manifestPath = ["dist/client/.vite/manifest.json", "dist/.vite/manifest.json"].map((candidate) => resolve(root, candidate)).find((candidate) => existsSync(candidate));
|
|
1263
1271
|
if (!manifestPath) return {
|
|
1264
1272
|
clientEntryUrl: null,
|
|
@@ -1276,22 +1284,22 @@ function readClientBuildAssets(root = process.cwd()) {
|
|
|
1276
1284
|
if (!entry.src) continue;
|
|
1277
1285
|
const deps = collectTransitiveDeps(manifest, key);
|
|
1278
1286
|
const manifestKey = stripPrachtClientModuleQuery(entry.src);
|
|
1279
|
-
if (deps.css.length > 0) cssManifest[manifestKey] = deps.css.map((f) =>
|
|
1280
|
-
if (deps.js.length > 0) jsManifest[manifestKey] = deps.js.map((f) =>
|
|
1287
|
+
if (deps.css.length > 0) cssManifest[manifestKey] = deps.css.map((f) => assetUrl(f, base));
|
|
1288
|
+
if (deps.js.length > 0) jsManifest[manifestKey] = deps.js.map((f) => assetUrl(f, base));
|
|
1281
1289
|
}
|
|
1282
|
-
addEntryDeps(manifest, jsManifest, PRACHT_CLIENT_MODULE_ID, clientEntry);
|
|
1283
|
-
addEntryDeps(manifest, jsManifest, PRACHT_ISLANDS_CLIENT_MODULE_ID, islandsEntry);
|
|
1290
|
+
addEntryDeps(manifest, jsManifest, PRACHT_CLIENT_MODULE_ID, clientEntry, base);
|
|
1291
|
+
addEntryDeps(manifest, jsManifest, PRACHT_ISLANDS_CLIENT_MODULE_ID, islandsEntry, base);
|
|
1284
1292
|
return {
|
|
1285
|
-
clientEntryUrl: clientEntry ?
|
|
1286
|
-
islandsEntryUrl: islandsEntry ?
|
|
1293
|
+
clientEntryUrl: clientEntry ? assetUrl(clientEntry.file, base) : null,
|
|
1294
|
+
islandsEntryUrl: islandsEntry ? assetUrl(islandsEntry.file, base) : null,
|
|
1287
1295
|
cssManifest,
|
|
1288
1296
|
jsManifest
|
|
1289
1297
|
};
|
|
1290
1298
|
}
|
|
1291
|
-
function addEntryDeps(manifest, jsManifest, entryKey, entry) {
|
|
1299
|
+
function addEntryDeps(manifest, jsManifest, entryKey, entry, base) {
|
|
1292
1300
|
if (!entry) return;
|
|
1293
1301
|
const deps = collectTransitiveDeps(manifest, entryKey).js.filter((file) => file !== entry.file);
|
|
1294
|
-
if (deps.length > 0) jsManifest[entryKey] = deps.map((file) =>
|
|
1302
|
+
if (deps.length > 0) jsManifest[entryKey] = deps.map((file) => assetUrl(file, base));
|
|
1295
1303
|
}
|
|
1296
1304
|
function collectTransitiveDeps(manifest, key) {
|
|
1297
1305
|
const css = /* @__PURE__ */ new Set();
|
|
@@ -1350,6 +1358,7 @@ const DEFAULTS = {
|
|
|
1350
1358
|
shellsDir: "/src/shells",
|
|
1351
1359
|
apiDir: "/src/api",
|
|
1352
1360
|
serverDir: "/src/server",
|
|
1361
|
+
additionalExtensions: [],
|
|
1353
1362
|
islandsDir: "/src/islands",
|
|
1354
1363
|
capabilitiesDir: "/src/capabilities",
|
|
1355
1364
|
adapter: createDefaultNodeAdapter(),
|
|
@@ -1368,6 +1377,7 @@ function resolveOptions(options) {
|
|
|
1368
1377
|
...options
|
|
1369
1378
|
};
|
|
1370
1379
|
if (resolved.llmsTxt === void 0) resolved.llmsTxt = false;
|
|
1380
|
+
resolved.additionalExtensions = normalizeAdditionalExtensions(resolved.additionalExtensions);
|
|
1371
1381
|
if (!new Set([
|
|
1372
1382
|
"spa",
|
|
1373
1383
|
"ssr",
|
|
@@ -1582,7 +1592,7 @@ function createPrachtCapabilitiesClientModuleSource(options = {}, buildOptions =
|
|
|
1582
1592
|
"// Generated by @pracht/vite-plugin from the app manifest capability registrations.",
|
|
1583
1593
|
"// Contains only http-exposed capability names, endpoints, and effects —",
|
|
1584
1594
|
"// capability modules themselves are server-only and never reach the client graph.",
|
|
1585
|
-
"import { createUseCapability } from \"@pracht/core\";",
|
|
1595
|
+
"import { createUseCapability, withBase } from \"@pracht/core\";",
|
|
1586
1596
|
"",
|
|
1587
1597
|
`const endpoints = Object.assign(Object.create(null), JSON.parse(${JSON.stringify(JSON.stringify(endpoints))}));`,
|
|
1588
1598
|
"",
|
|
@@ -1598,7 +1608,7 @@ function createPrachtCapabilitiesClientModuleSource(options = {}, buildOptions =
|
|
|
1598
1608
|
" } else if (opts && opts.confirm) {",
|
|
1599
1609
|
` headers.set(${JSON.stringify(CONFIRMATION_HEADER)}, opts.confirm);`,
|
|
1600
1610
|
" }",
|
|
1601
|
-
" response = await fetch(endpoint.path, {",
|
|
1611
|
+
" response = await fetch(withBase(endpoint.path), {",
|
|
1602
1612
|
" method: endpoint.method,",
|
|
1603
1613
|
" headers,",
|
|
1604
1614
|
" body: JSON.stringify(input === undefined ? {} : input),",
|
|
@@ -1791,15 +1801,6 @@ function hasWebmcpCapabilities(options = {}, root = process.cwd()) {
|
|
|
1791
1801
|
}
|
|
1792
1802
|
//#endregion
|
|
1793
1803
|
//#region src/plugin-codegen.ts
|
|
1794
|
-
const ROUTE_MODULE_EXTENSIONS = new Set([
|
|
1795
|
-
".ts",
|
|
1796
|
-
".tsx",
|
|
1797
|
-
".js",
|
|
1798
|
-
".jsx",
|
|
1799
|
-
".md",
|
|
1800
|
-
".mdx",
|
|
1801
|
-
".tsrx"
|
|
1802
|
-
]);
|
|
1803
1804
|
const NON_FULL_HYDRATION_RE = /hydration\s*:\s*["'](?:islands|none)["']/;
|
|
1804
1805
|
const FULL_HYDRATION_RE = /hydration\s*:\s*["']full["']/;
|
|
1805
1806
|
const PAGES_NON_FULL_HYDRATION_RE = /export\s+const\s+HYDRATION\s*=\s*["'](?:islands|none)["']/;
|
|
@@ -1818,7 +1819,7 @@ function findMatching(source, start, open, close) {
|
|
|
1818
1819
|
}
|
|
1819
1820
|
return -1;
|
|
1820
1821
|
}
|
|
1821
|
-
function scanFiles(dir, files) {
|
|
1822
|
+
function scanFiles(dir, files, extensions) {
|
|
1822
1823
|
let entries;
|
|
1823
1824
|
try {
|
|
1824
1825
|
entries = readdirSync(dir);
|
|
@@ -1833,15 +1834,15 @@ function scanFiles(dir, files) {
|
|
|
1833
1834
|
} catch {
|
|
1834
1835
|
continue;
|
|
1835
1836
|
}
|
|
1836
|
-
if (stat.isDirectory()) scanFiles(abs, files);
|
|
1837
|
-
else if (
|
|
1837
|
+
if (stat.isDirectory()) scanFiles(abs, files, extensions);
|
|
1838
|
+
else if (extensions.has(extname(entry))) files.push(abs);
|
|
1838
1839
|
}
|
|
1839
1840
|
}
|
|
1840
1841
|
function createNonFullHydrationExcludes(resolved, root = process.cwd()) {
|
|
1841
1842
|
const excludes = /* @__PURE__ */ new Set();
|
|
1842
1843
|
if (resolved.pagesDir) {
|
|
1843
1844
|
const files = [];
|
|
1844
|
-
scanFiles(resolve(root, resolved.pagesDir.replace(/^\//, "")), files);
|
|
1845
|
+
scanFiles(resolve(root, resolved.pagesDir.replace(/^\//, "")), files, withAdditionalExtensions(DEFAULT_ROUTE_EXTENSIONS, resolved.additionalExtensions));
|
|
1845
1846
|
for (const file of files) try {
|
|
1846
1847
|
if (PAGES_NON_FULL_HYDRATION_RE.test(readFileSync(file, "utf-8"))) excludes.add(`!/${toPosixPath$1(file).replace(toPosixPath$1(root).replace(/\/$/, "") + "/", "")}`);
|
|
1847
1848
|
} catch {}
|
|
@@ -1891,15 +1892,18 @@ function createPrachtClientModuleSource(options = {}, buildOptions = {}) {
|
|
|
1891
1892
|
const resolved = resolveOptions(options);
|
|
1892
1893
|
const isPagesMode = !!resolved.pagesDir;
|
|
1893
1894
|
const routeLoaderHints = createRouteLoaderHintsForVirtualModules(resolved, buildOptions.root);
|
|
1895
|
+
const routeHeadHints = createRouteHeadHintsForVirtualModules(resolved, buildOptions.root);
|
|
1896
|
+
const routeStaticPathsHints = createRouteStaticPathsHintsForVirtualModules(resolved, buildOptions.root);
|
|
1894
1897
|
const appImport = isPagesMode ? generatePagesAppInlineSource(resolved, buildOptions.root) : `import { app } from ${JSON.stringify(resolved.appFile)};`;
|
|
1898
|
+
const bareRouteExtensions = [...withAdditionalExtensions(LEGACY_BARE_ROUTE_EXTENSIONS, resolved.additionalExtensions)];
|
|
1895
1899
|
const dirPrefix = isPagesMode ? resolved.pagesDir : resolved.routesDir;
|
|
1896
1900
|
const routeGlob = `${dirPrefix}/**/*.{ts,tsx,js,jsx,md,mdx}`;
|
|
1897
|
-
const
|
|
1901
|
+
const additionalRouteGlob = `${dirPrefix}/**/*.${extensionGlob(bareRouteExtensions)}`;
|
|
1898
1902
|
const routeExcludes = createNonFullHydrationExcludes(resolved, buildOptions.root);
|
|
1899
1903
|
const routeGlobPattern = routeExcludes.length > 0 ? [routeGlob, ...routeExcludes] : routeGlob;
|
|
1900
|
-
const
|
|
1904
|
+
const additionalRouteGlobPattern = additionalRouteGlob && routeExcludes.length > 0 ? [additionalRouteGlob, ...routeExcludes] : additionalRouteGlob;
|
|
1901
1905
|
const shellGlob = isPagesMode ? `${resolved.pagesDir}/**/_app.{ts,tsx,js,jsx}` : `${resolved.shellsDir}/**/*.{ts,tsx,js,jsx,md,mdx}`;
|
|
1902
|
-
const
|
|
1906
|
+
const additionalShellGlob = isPagesMode ? `${resolved.pagesDir}/**/_app.${extensionGlob(bareRouteExtensions)}` : `${resolved.shellsDir}/**/*.${extensionGlob(bareRouteExtensions)}`;
|
|
1903
1907
|
const appFilePosix = resolved.appFile.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
1904
1908
|
const appDir = (appFilePosix.startsWith("/") ? appFilePosix : `/${appFilePosix}`).replace(/\/[^/]*$/, "") || "/";
|
|
1905
1909
|
return [
|
|
@@ -1907,17 +1911,19 @@ function createPrachtClientModuleSource(options = {}, buildOptions = {}) {
|
|
|
1907
1911
|
appImport,
|
|
1908
1912
|
"",
|
|
1909
1913
|
`const routeLoaderHints = ${JSON.stringify(routeLoaderHints)};`,
|
|
1914
|
+
`const routeHeadHints = ${JSON.stringify(routeHeadHints)};`,
|
|
1915
|
+
`const routeStaticPathsHints = ${JSON.stringify(routeStaticPathsHints)};`,
|
|
1910
1916
|
`const routeModules = {`,
|
|
1911
1917
|
` ...import.meta.glob(${JSON.stringify(routeGlobPattern)}, { query: ${JSON.stringify(PRACHT_CLIENT_MODULE_QUERY)} }),`,
|
|
1912
|
-
` ...import.meta.glob(${JSON.stringify(
|
|
1918
|
+
` ...import.meta.glob(${JSON.stringify(additionalRouteGlobPattern)}),`,
|
|
1913
1919
|
`};`,
|
|
1914
1920
|
`const shellModules = {`,
|
|
1915
1921
|
` ...import.meta.glob(${JSON.stringify(shellGlob)}, { query: ${JSON.stringify(PRACHT_CLIENT_MODULE_QUERY)} }),`,
|
|
1916
|
-
` ...import.meta.glob(${JSON.stringify(
|
|
1922
|
+
` ...import.meta.glob(${JSON.stringify(additionalShellGlob)}),`,
|
|
1917
1923
|
`};`,
|
|
1918
1924
|
"",
|
|
1919
1925
|
"const resolvedApp = resolveApp(app);",
|
|
1920
|
-
"
|
|
1926
|
+
"applyRouteHints(resolvedApp, routeLoaderHints, routeHeadHints, routeStaticPathsHints);",
|
|
1921
1927
|
"",
|
|
1922
1928
|
...createApplyRouteLoaderHintsSource(),
|
|
1923
1929
|
`const APP_DIR = ${JSON.stringify(appDir)};`,
|
|
@@ -2013,7 +2019,9 @@ function createPrachtServerModuleSource(options = {}, buildOptions = {}) {
|
|
|
2013
2019
|
const isPagesMode = !!resolved.pagesDir;
|
|
2014
2020
|
const registrySource = createPrachtRegistryModuleSource(resolved);
|
|
2015
2021
|
const routeLoaderHints = createRouteLoaderHintsForVirtualModules(resolved, buildOptions.root);
|
|
2016
|
-
const
|
|
2022
|
+
const routeHeadHints = createRouteHeadHintsForVirtualModules(resolved, buildOptions.root);
|
|
2023
|
+
const routeStaticPathsHints = createRouteStaticPathsHintsForVirtualModules(resolved, buildOptions.root);
|
|
2024
|
+
const clientBuild = buildOptions.isBuild ? readClientBuildAssets(buildOptions.root, buildOptions.base ?? "/") : {
|
|
2017
2025
|
clientEntryUrl: null,
|
|
2018
2026
|
islandsEntryUrl: null,
|
|
2019
2027
|
cssManifest: {},
|
|
@@ -2025,7 +2033,10 @@ function createPrachtServerModuleSource(options = {}, buildOptions = {}) {
|
|
|
2025
2033
|
let prachtImports = adapter?.serverImports ? adapter.serverImports + "\nimport { prerenderApp } from \"@pracht/core/server\";" : "import { resolveApp, resolveApiRoutes, prerenderApp } from \"@pracht/core/server\";";
|
|
2026
2034
|
if (llmsTxtConfig) prachtImports += "\nimport { buildLlmsTxt } from \"@pracht/core/server\";";
|
|
2027
2035
|
const appImport = isPagesMode ? generatePagesAppInlineSource(resolved, buildOptions.root) : `import { app } from ${JSON.stringify(resolved.appFile)};`;
|
|
2028
|
-
const
|
|
2036
|
+
const devBase = buildOptions.base ?? "/";
|
|
2037
|
+
const withDevBase = (path) => devBase === "/" ? path : `${devBase}${path.slice(1)}`;
|
|
2038
|
+
const clientEntryUrl = buildOptions.isBuild ? clientBuild.clientEntryUrl : withDevBase(CLIENT_BROWSER_PATH);
|
|
2039
|
+
const islandsEntryUrl = buildOptions.isBuild ? clientBuild.islandsEntryUrl : withDevBase(ISLANDS_CLIENT_BROWSER_PATH);
|
|
2029
2040
|
const islandsGlob = `${resolved.islandsDir}/**/*.{ts,tsx,js,jsx}`;
|
|
2030
2041
|
const source = [
|
|
2031
2042
|
prachtImports,
|
|
@@ -2033,6 +2044,8 @@ function createPrachtServerModuleSource(options = {}, buildOptions = {}) {
|
|
|
2033
2044
|
appImport,
|
|
2034
2045
|
"",
|
|
2035
2046
|
`const routeLoaderHints = ${JSON.stringify(routeLoaderHints)};`,
|
|
2047
|
+
`const routeHeadHints = ${JSON.stringify(routeHeadHints)};`,
|
|
2048
|
+
`const routeStaticPathsHints = ${JSON.stringify(routeStaticPathsHints)};`,
|
|
2036
2049
|
...createApplyRouteLoaderHintsSource(),
|
|
2037
2050
|
registrySource,
|
|
2038
2051
|
"",
|
|
@@ -2044,10 +2057,13 @@ function createPrachtServerModuleSource(options = {}, buildOptions = {}) {
|
|
|
2044
2057
|
"export const islandFiles = Object.keys(islandModules);",
|
|
2045
2058
|
"",
|
|
2046
2059
|
"export const resolvedApp = resolveApp(app);",
|
|
2047
|
-
"
|
|
2060
|
+
"applyRouteHints(resolvedApp, routeLoaderHints, routeHeadHints, routeStaticPathsHints);",
|
|
2048
2061
|
`export const apiRoutes = resolveApiRoutes(Object.keys(apiModules), ${JSON.stringify(resolved.apiDir)});`,
|
|
2049
2062
|
`export const buildTarget = ${JSON.stringify(adapter?.id ?? "node")};`,
|
|
2050
|
-
`export const
|
|
2063
|
+
`export const staticTarget = ${JSON.stringify(adapter?.staticTarget === true)};`,
|
|
2064
|
+
`export const buildBase = ${JSON.stringify(buildOptions.base ?? "/")};`,
|
|
2065
|
+
`export const configuredBase = ${JSON.stringify(buildOptions.configuredBase)};`,
|
|
2066
|
+
`export const clientEntryUrl = ${JSON.stringify(clientEntryUrl ?? "/@pracht/client.js")};`,
|
|
2051
2067
|
`export const islandsEntryUrl = ${JSON.stringify(islandsEntryUrl ?? null)};`,
|
|
2052
2068
|
`export const islandsBootstrapRequired = ${JSON.stringify(islandsBootstrapRequired)};`,
|
|
2053
2069
|
`export const cssManifest = ${JSON.stringify(clientBuild.cssManifest)};`,
|
|
@@ -2075,15 +2091,25 @@ function createPrachtServerModuleSource(options = {}, buildOptions = {}) {
|
|
|
2075
2091
|
*/
|
|
2076
2092
|
function createPrachtDevModuleSource(options = {}, buildOptions = {}) {
|
|
2077
2093
|
const resolved = resolveOptions(options);
|
|
2094
|
+
const routeLoaderHints = createRouteLoaderHintsForVirtualModules(resolved, buildOptions.root);
|
|
2095
|
+
const routeHeadHints = createRouteHeadHintsForVirtualModules(resolved, buildOptions.root);
|
|
2096
|
+
const routeStaticPathsHints = createRouteStaticPathsHintsForVirtualModules(resolved, buildOptions.root);
|
|
2078
2097
|
return [
|
|
2079
2098
|
"import { resolveApp, resolveApiRoutes } from \"@pracht/core/server\";",
|
|
2080
2099
|
resolved.pagesDir ? generatePagesAppInlineSource(resolved, buildOptions.root) : `import { app } from ${JSON.stringify(resolved.appFile)};`,
|
|
2081
2100
|
"",
|
|
2101
|
+
`const routeLoaderHints = ${JSON.stringify(routeLoaderHints)};`,
|
|
2102
|
+
`const routeHeadHints = ${JSON.stringify(routeHeadHints)};`,
|
|
2103
|
+
`const routeStaticPathsHints = ${JSON.stringify(routeStaticPathsHints)};`,
|
|
2104
|
+
...createApplyRouteLoaderHintsSource(),
|
|
2082
2105
|
createPrachtRegistryModuleSource(resolved),
|
|
2083
2106
|
"",
|
|
2084
2107
|
"export const resolvedApp = resolveApp(app);",
|
|
2108
|
+
"applyRouteHints(resolvedApp, routeLoaderHints, routeHeadHints, routeStaticPathsHints);",
|
|
2085
2109
|
`export const apiRoutes = resolveApiRoutes(Object.keys(apiModules), ${JSON.stringify(resolved.apiDir)});`,
|
|
2086
2110
|
`export const buildTarget = ${JSON.stringify(resolved.adapter?.id ?? "node")};`,
|
|
2111
|
+
`export const staticTarget = ${JSON.stringify(resolved.adapter?.staticTarget === true)};`,
|
|
2112
|
+
`export const buildBase = ${JSON.stringify(buildOptions.base ?? "/")};`,
|
|
2087
2113
|
""
|
|
2088
2114
|
].join("\n");
|
|
2089
2115
|
}
|
|
@@ -2108,7 +2134,7 @@ function resolveLlmsTxtConfig(resolved, root = process.cwd()) {
|
|
|
2108
2134
|
}
|
|
2109
2135
|
function createApplyRouteLoaderHintsSource() {
|
|
2110
2136
|
return [
|
|
2111
|
-
"function
|
|
2137
|
+
"function applyRouteHints(resolvedApp, routeLoaderHints, routeHeadHints, routeStaticPathsHints) {",
|
|
2112
2138
|
" for (const route of resolvedApp.routes) {",
|
|
2113
2139
|
" const hint = routeLoaderHints[route.file];",
|
|
2114
2140
|
" if (hint === true) {",
|
|
@@ -2116,14 +2142,52 @@ function createApplyRouteLoaderHintsSource() {
|
|
|
2116
2142
|
" } else if (typeof route.hasLoader === 'undefined' && typeof hint === 'boolean') {",
|
|
2117
2143
|
" route.hasLoader = hint;",
|
|
2118
2144
|
" }",
|
|
2145
|
+
" const routeHeadHint = routeHeadHints[route.file];",
|
|
2146
|
+
" const shellHeadHint = route.shellFile ? routeHeadHints[route.shellFile] : undefined;",
|
|
2147
|
+
" const hasCompleteHeadHints = typeof routeHeadHint === 'boolean' &&",
|
|
2148
|
+
" (!route.shellFile || typeof shellHeadHint === 'boolean');",
|
|
2149
|
+
" if (routeHeadHint === true || shellHeadHint === true) {",
|
|
2150
|
+
" route.hasHead = true;",
|
|
2151
|
+
" } else if (typeof route.hasHead === 'undefined' && hasCompleteHeadHints) {",
|
|
2152
|
+
" route.hasHead = routeHeadHint === true || shellHeadHint === true;",
|
|
2153
|
+
" }",
|
|
2154
|
+
" // Only ever narrows to false, and only when the scan actually saw the",
|
|
2155
|
+
" // module: an unknown route file keeps today's conservative behavior.",
|
|
2156
|
+
" const staticPathsHint = routeStaticPathsHints[route.file];",
|
|
2157
|
+
" if (typeof route.hasStaticPaths === 'undefined' && typeof staticPathsHint === 'boolean') {",
|
|
2158
|
+
" route.hasStaticPaths = staticPathsHint;",
|
|
2159
|
+
" }",
|
|
2119
2160
|
" }",
|
|
2120
2161
|
"}",
|
|
2121
2162
|
""
|
|
2122
2163
|
];
|
|
2123
2164
|
}
|
|
2165
|
+
function createRouteHeadHintsForVirtualModules(options, root = process.cwd()) {
|
|
2166
|
+
const appFileDir = dirname(resolve(root, options.appFile.slice(1)));
|
|
2167
|
+
const directories = options.pagesDir ? [[options.pagesDir, resolve(root, options.pagesDir.slice(1))]] : [[options.routesDir, resolve(root, options.routesDir.slice(1))], [options.shellsDir, resolve(root, options.shellsDir.slice(1))]];
|
|
2168
|
+
return Object.assign({}, ...directories.map(([prefix, directory]) => createRouteHeadHints(directory, {
|
|
2169
|
+
additionalExtensions: options.additionalExtensions,
|
|
2170
|
+
appFileDir,
|
|
2171
|
+
rootRelativePrefix: prefix
|
|
2172
|
+
})));
|
|
2173
|
+
}
|
|
2174
|
+
/**
|
|
2175
|
+
* `getStaticPaths()` presence per route file. Only routes matter — a shell
|
|
2176
|
+
* cannot enumerate paths — so unlike the head hints this skips the shells
|
|
2177
|
+
* directory.
|
|
2178
|
+
*/
|
|
2179
|
+
function createRouteStaticPathsHintsForVirtualModules(options, root = process.cwd()) {
|
|
2180
|
+
const appFileDir = dirname(resolve(root, options.appFile.slice(1)));
|
|
2181
|
+
const routesPrefix = options.pagesDir || options.routesDir;
|
|
2182
|
+
return createRouteStaticPathsHints(resolve(root, routesPrefix.slice(1)), {
|
|
2183
|
+
additionalExtensions: options.additionalExtensions,
|
|
2184
|
+
appFileDir,
|
|
2185
|
+
rootRelativePrefix: routesPrefix
|
|
2186
|
+
});
|
|
2187
|
+
}
|
|
2124
2188
|
function createRouteLoaderHintsForVirtualModules(options, root = process.cwd()) {
|
|
2125
2189
|
if (options.pagesDir) {
|
|
2126
|
-
const pages = scanPagesDirectory(resolve(root, options.pagesDir.slice(1)));
|
|
2190
|
+
const pages = scanPagesDirectory(resolve(root, options.pagesDir.slice(1)), options.additionalExtensions);
|
|
2127
2191
|
const hints = {};
|
|
2128
2192
|
for (const page of pages) {
|
|
2129
2193
|
const key = `${options.pagesDir}/${page.relativePath.replace(/\\/g, "/")}`;
|
|
@@ -2133,6 +2197,7 @@ function createRouteLoaderHintsForVirtualModules(options, root = process.cwd())
|
|
|
2133
2197
|
}
|
|
2134
2198
|
const appFileDir = dirname(resolve(root, options.appFile.slice(1)));
|
|
2135
2199
|
return createRouteLoaderHints(resolve(root, options.routesDir.slice(1)), {
|
|
2200
|
+
additionalExtensions: options.additionalExtensions,
|
|
2136
2201
|
appFileDir,
|
|
2137
2202
|
rootRelativePrefix: options.routesDir
|
|
2138
2203
|
});
|
|
@@ -2141,18 +2206,19 @@ function createPrachtRegistryModuleSource(options = {}) {
|
|
|
2141
2206
|
const resolved = resolveOptions(options);
|
|
2142
2207
|
const apiGlobs = [`${resolved.apiDir}/**/*.{ts,js,tsx,jsx}`, `!${resolved.apiDir}/**/*.d.ts`];
|
|
2143
2208
|
const isPagesMode = !!resolved.pagesDir;
|
|
2209
|
+
const bareRouteExtensions = [...withAdditionalExtensions(LEGACY_BARE_ROUTE_EXTENSIONS, resolved.additionalExtensions)];
|
|
2144
2210
|
const routeGlob = isPagesMode ? `${resolved.pagesDir}/**/*.{ts,tsx,js,jsx,md,mdx}` : `${resolved.routesDir}/**/*.{ts,tsx,js,jsx,md,mdx}`;
|
|
2145
|
-
const
|
|
2211
|
+
const additionalRouteGlob = `${isPagesMode ? resolved.pagesDir : resolved.routesDir}/**/*.${extensionGlob(bareRouteExtensions)}`;
|
|
2146
2212
|
const shellGlob = isPagesMode ? `${resolved.pagesDir}/**/_app.{ts,tsx,js,jsx}` : `${resolved.shellsDir}/**/*.{ts,tsx,js,jsx,md,mdx}`;
|
|
2147
|
-
const
|
|
2213
|
+
const additionalShellGlob = isPagesMode ? `${resolved.pagesDir}/**/_app.${extensionGlob(bareRouteExtensions)}` : `${resolved.shellsDir}/**/*.${extensionGlob(bareRouteExtensions)}`;
|
|
2148
2214
|
return [
|
|
2149
2215
|
`export const routeModules = {`,
|
|
2150
2216
|
` ...import.meta.glob(${JSON.stringify(routeGlob)}),`,
|
|
2151
|
-
` ...import.meta.glob(${JSON.stringify(
|
|
2217
|
+
` ...import.meta.glob(${JSON.stringify(additionalRouteGlob)}),`,
|
|
2152
2218
|
`};`,
|
|
2153
2219
|
`export const shellModules = {`,
|
|
2154
2220
|
` ...import.meta.glob(${JSON.stringify(shellGlob)}),`,
|
|
2155
|
-
` ...import.meta.glob(${JSON.stringify(
|
|
2221
|
+
` ...import.meta.glob(${JSON.stringify(additionalShellGlob)}),`,
|
|
2156
2222
|
`};`,
|
|
2157
2223
|
`export const middlewareModules = import.meta.glob(${JSON.stringify(`${resolved.middlewareDir}/**/*.{ts,tsx,js,jsx}`)});`,
|
|
2158
2224
|
`export const apiModules = import.meta.glob(${JSON.stringify(apiGlobs)});`,
|
|
@@ -2176,13 +2242,15 @@ function clearPagesAppSourceCache() {
|
|
|
2176
2242
|
function generatePagesAppInlineSource(options, root = process.cwd()) {
|
|
2177
2243
|
const absPagesDir = resolve(root, options.pagesDir.slice(1));
|
|
2178
2244
|
const cacheKey = JSON.stringify({
|
|
2245
|
+
additionalExtensions: options.additionalExtensions,
|
|
2179
2246
|
absPagesDir,
|
|
2180
2247
|
pagesDefaultRender: options.pagesDefaultRender,
|
|
2181
2248
|
pagesDirPrefix: options.pagesDir
|
|
2182
2249
|
});
|
|
2183
2250
|
const cached = pagesAppSourceCache.get(cacheKey);
|
|
2184
2251
|
if (cached) return cached;
|
|
2185
|
-
const source = generatePagesManifestSource(scanPagesDirectory(absPagesDir), {
|
|
2252
|
+
const source = generatePagesManifestSource(scanPagesDirectory(absPagesDir, options.additionalExtensions), {
|
|
2253
|
+
additionalExtensions: options.additionalExtensions,
|
|
2186
2254
|
pagesDir: absPagesDir,
|
|
2187
2255
|
pagesDefaultRender: options.pagesDefaultRender,
|
|
2188
2256
|
pagesDirPrefix: options.pagesDir
|
|
@@ -2197,8 +2265,46 @@ const DEFAULT_MAX_BODY_SIZE = 1024 * 1024;
|
|
|
2197
2265
|
const CSS_MODULE_URL_RE = /\.(?:css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\?)/;
|
|
2198
2266
|
const DEVTOOLS_JSON_PATH = "/_pracht.json";
|
|
2199
2267
|
const LLMS_TXT_PATH = "/llms.txt";
|
|
2268
|
+
function isEventStreamContentType(contentType) {
|
|
2269
|
+
return contentType.split(";", 1)[0]?.trim().toLowerCase() === "text/event-stream";
|
|
2270
|
+
}
|
|
2271
|
+
/**
|
|
2272
|
+
* Adapter-owned dev servers can route every browser request through their
|
|
2273
|
+
* platform runtime before Vite's transform middleware gets a chance to serve
|
|
2274
|
+
* Pracht's stable virtual client entries. Serve those two entries at their
|
|
2275
|
+
* public, base-prefixed URLs while leaving every other request to the adapter.
|
|
2276
|
+
*/
|
|
2277
|
+
function createOwnedDevEntryMiddleware(server) {
|
|
2278
|
+
const base = server.config.base || "/";
|
|
2279
|
+
return async (req, res, next) => {
|
|
2280
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
2281
|
+
if (method !== "GET" && method !== "HEAD") return next();
|
|
2282
|
+
const requestUrl = new URL(req.url ?? "/", "http://localhost");
|
|
2283
|
+
const pathname = base === "/" ? requestUrl.pathname : requestUrl.pathname.startsWith(base) ? `/${requestUrl.pathname.slice(base.length)}` : null;
|
|
2284
|
+
if (pathname !== "/@pracht/client.js" && pathname !== "/@pracht/islands.js") return next();
|
|
2285
|
+
try {
|
|
2286
|
+
const result = await server.transformRequest(`${pathname}${requestUrl.search}`);
|
|
2287
|
+
if (!result) return next();
|
|
2288
|
+
if (result.etag && req.headers["if-none-match"] === result.etag) {
|
|
2289
|
+
res.statusCode = 304;
|
|
2290
|
+
res.end();
|
|
2291
|
+
return;
|
|
2292
|
+
}
|
|
2293
|
+
res.statusCode = 200;
|
|
2294
|
+
res.setHeader("content-type", "text/javascript");
|
|
2295
|
+
res.setHeader("cache-control", "no-cache");
|
|
2296
|
+
if (result.etag) res.setHeader("etag", result.etag);
|
|
2297
|
+
for (const [name, value] of Object.entries(server.config.server.headers ?? {})) if (value !== void 0) res.setHeader(name, value);
|
|
2298
|
+
res.end(method === "HEAD" ? void 0 : result.code);
|
|
2299
|
+
} catch (error) {
|
|
2300
|
+
next(error);
|
|
2301
|
+
}
|
|
2302
|
+
};
|
|
2303
|
+
}
|
|
2200
2304
|
function createDevSSRMiddleware(server, options = {}) {
|
|
2201
2305
|
const maxBodySize = options.maxBodySize ?? DEFAULT_MAX_BODY_SIZE;
|
|
2306
|
+
const devBase = server.config.base || "/";
|
|
2307
|
+
const withDevBase = (path) => devBase === "/" || !path.startsWith("/") ? path : `${devBase}${path.slice(1)}`;
|
|
2202
2308
|
let warnedDevtoolsCollision = false;
|
|
2203
2309
|
let warnedLlmsTxtCollision = false;
|
|
2204
2310
|
if (options.llmsTxt && typeof server.config.publicDir === "string") {
|
|
@@ -2223,6 +2329,7 @@ function createDevSSRMiddleware(server, options = {}) {
|
|
|
2223
2329
|
await serveDevtools(server, res, {
|
|
2224
2330
|
apiRoutes: serverMod.apiRoutes ?? [],
|
|
2225
2331
|
app: serverMod.resolvedApp,
|
|
2332
|
+
base: devBase,
|
|
2226
2333
|
url,
|
|
2227
2334
|
wantsJson: requestUrl.pathname === DEVTOOLS_JSON_PATH
|
|
2228
2335
|
});
|
|
@@ -2243,10 +2350,10 @@ function createDevSSRMiddleware(server, options = {}) {
|
|
|
2243
2350
|
return;
|
|
2244
2351
|
}
|
|
2245
2352
|
if (shouldBypassDevSSR(requestUrl, req, routeMatchers)) return next();
|
|
2246
|
-
if (isDevNotFoundRequest(requestUrl, req, routeMatchers)) return serveDevNotFound(server, res, next, url, requestUrl.pathname, routeMatchers);
|
|
2353
|
+
if (isDevNotFoundRequest(requestUrl, req, routeMatchers)) return serveDevNotFound(server, res, next, url, requestUrl.pathname, routeMatchers, devBase);
|
|
2247
2354
|
let webRequest;
|
|
2248
2355
|
try {
|
|
2249
|
-
webRequest = await nodeToWebRequest(req, maxBodySize);
|
|
2356
|
+
webRequest = await nodeToWebRequest(req, maxBodySize, devBase);
|
|
2250
2357
|
} catch (err) {
|
|
2251
2358
|
if (err instanceof Error && err.message === "Request body too large") {
|
|
2252
2359
|
res.statusCode = 413;
|
|
@@ -2261,8 +2368,8 @@ function createDevSSRMiddleware(server, options = {}) {
|
|
|
2261
2368
|
registry: serverMod.registry,
|
|
2262
2369
|
request: webRequest,
|
|
2263
2370
|
debugErrors: true,
|
|
2264
|
-
clientEntryUrl: CLIENT_BROWSER_PATH,
|
|
2265
|
-
islandsEntryUrl: ISLANDS_CLIENT_BROWSER_PATH,
|
|
2371
|
+
clientEntryUrl: withDevBase(CLIENT_BROWSER_PATH),
|
|
2372
|
+
islandsEntryUrl: withDevBase(ISLANDS_CLIENT_BROWSER_PATH),
|
|
2266
2373
|
islandsBootstrapRequired: serverMod.islandsBootstrapRequired === true,
|
|
2267
2374
|
apiRoutes: serverMod.apiRoutes,
|
|
2268
2375
|
timings
|
|
@@ -2270,8 +2377,27 @@ function createDevSSRMiddleware(server, options = {}) {
|
|
|
2270
2377
|
const responseContentType = response.headers.get("content-type") ?? "";
|
|
2271
2378
|
if (response.status === 404 && !responseContentType.includes("application/json") && !routeMatchers.app?.notFound) return next();
|
|
2272
2379
|
const contentType = response.headers.get("content-type") ?? "";
|
|
2380
|
+
if (response.body && isEventStreamContentType(contentType)) {
|
|
2381
|
+
res.statusCode = response.status;
|
|
2382
|
+
response.headers.forEach((value, key) => {
|
|
2383
|
+
res.setHeader(key, value);
|
|
2384
|
+
});
|
|
2385
|
+
const source = Readable.fromWeb(response.body);
|
|
2386
|
+
if (res.destroyed || res.writableEnded) {
|
|
2387
|
+
source.destroy();
|
|
2388
|
+
return;
|
|
2389
|
+
}
|
|
2390
|
+
res.on("close", () => {
|
|
2391
|
+
if (!res.writableFinished) source.destroy();
|
|
2392
|
+
});
|
|
2393
|
+
source.on("error", () => {
|
|
2394
|
+
res.destroy();
|
|
2395
|
+
});
|
|
2396
|
+
source.pipe(res);
|
|
2397
|
+
return;
|
|
2398
|
+
}
|
|
2273
2399
|
let body = await response.text();
|
|
2274
|
-
if (contentType.includes("text/html")) body = await server
|
|
2400
|
+
if (contentType.includes("text/html")) body = await transformDevHtml(server, url, body, devBase);
|
|
2275
2401
|
res.statusCode = response.status;
|
|
2276
2402
|
response.headers.forEach((value, key) => {
|
|
2277
2403
|
res.setHeader(key, value);
|
|
@@ -2280,7 +2406,56 @@ function createDevSSRMiddleware(server, options = {}) {
|
|
|
2280
2406
|
if (serverTiming) res.setHeader("Server-Timing", serverTiming);
|
|
2281
2407
|
res.end(body);
|
|
2282
2408
|
} catch (error) {
|
|
2283
|
-
await handleDevError(server, req, res, next, url, error);
|
|
2409
|
+
await handleDevError(server, req, res, next, url, error, devBase);
|
|
2410
|
+
}
|
|
2411
|
+
};
|
|
2412
|
+
}
|
|
2413
|
+
/**
|
|
2414
|
+
* Vite's HTML transform adds `config.base` to root-absolute asset attributes.
|
|
2415
|
+
* Pracht's runtime has already added it to URLs produced by `withBase()` — the
|
|
2416
|
+
* client entry, route-state preloads, image endpoints, and user-authored asset
|
|
2417
|
+
* URLs — while Vite-owned or module-graph URLs still need the transform. Hide
|
|
2418
|
+
* the already-based strings while the hooks run, then restore them afterward,
|
|
2419
|
+
* so each producer applies the deploy base exactly once.
|
|
2420
|
+
*/
|
|
2421
|
+
async function transformDevHtml(server, url, html, base) {
|
|
2422
|
+
if (base === "/") return server.transformIndexHtml(url, html);
|
|
2423
|
+
const assetUrls = protectRootAbsoluteAssetAttributes(html);
|
|
2424
|
+
let placeholder = "https://pracht.invalid/__PRACHT_DEV_BASE_PLACEHOLDER__/";
|
|
2425
|
+
while (assetUrls.html.includes(placeholder)) placeholder += "_";
|
|
2426
|
+
const protectedHtml = assetUrls.html.replaceAll(base, placeholder);
|
|
2427
|
+
const transformedHtml = await server.transformIndexHtml(url, protectedHtml);
|
|
2428
|
+
return assetUrls.restore(transformedHtml.replaceAll(placeholder, base));
|
|
2429
|
+
}
|
|
2430
|
+
const HTML_ASSET_URL_ATTRIBUTE_RE = /(\s(?:src|href|xlink:href|data|srcset|imagesrcset|poster|content)\s*=\s*)(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+))/gi;
|
|
2431
|
+
/**
|
|
2432
|
+
* Runtime-rendered root-absolute asset URLs have already reached their final
|
|
2433
|
+
* public meaning: raw `/logo.svg` deliberately stays at the origin root,
|
|
2434
|
+
* while `withBase("/logo.svg")` is already under the deploy base. Vite's dev
|
|
2435
|
+
* HTML pass prefixes both, unlike production SSR, so hide complete attribute
|
|
2436
|
+
* values behind inert external URLs until that pass finishes.
|
|
2437
|
+
*/
|
|
2438
|
+
function protectRootAbsoluteAssetAttributes(html) {
|
|
2439
|
+
let markerPrefix = "https://pracht.invalid/__PRACHT_DEV_ASSET_PLACEHOLDER__/";
|
|
2440
|
+
while (html.includes(markerPrefix)) markerPrefix += "_";
|
|
2441
|
+
const replacements = [];
|
|
2442
|
+
return {
|
|
2443
|
+
html: html.replace(HTML_ASSET_URL_ATTRIBUTE_RE, (match, prefix, doubleQuoted, singleQuoted, unquoted) => {
|
|
2444
|
+
const value = doubleQuoted ?? singleQuoted ?? unquoted ?? "";
|
|
2445
|
+
if (!(/(?:srcset)\s*=\s*$/i.test(prefix) ? /(?:^|,)\s*\/(?!\/)/.test(value) : /^\s*\/(?!\/)/.test(value))) return match;
|
|
2446
|
+
const marker = `${markerPrefix}${replacements.length}`;
|
|
2447
|
+
replacements.push({
|
|
2448
|
+
marker,
|
|
2449
|
+
value
|
|
2450
|
+
});
|
|
2451
|
+
if (doubleQuoted !== void 0) return `${prefix}"${marker}"`;
|
|
2452
|
+
if (singleQuoted !== void 0) return `${prefix}'${marker}'`;
|
|
2453
|
+
return `${prefix}${marker}`;
|
|
2454
|
+
}),
|
|
2455
|
+
restore(transformedHtml) {
|
|
2456
|
+
let restoredHtml = transformedHtml;
|
|
2457
|
+
for (const { marker, value } of replacements) restoredHtml = restoredHtml.replaceAll(marker, value);
|
|
2458
|
+
return restoredHtml;
|
|
2284
2459
|
}
|
|
2285
2460
|
};
|
|
2286
2461
|
}
|
|
@@ -2292,7 +2467,7 @@ function createDevSSRMiddleware(server, options = {}) {
|
|
|
2292
2467
|
* avoid a first-paint FOUC.
|
|
2293
2468
|
*/
|
|
2294
2469
|
async function createDevCssManifest(server, options) {
|
|
2295
|
-
const route = options.matchAppRoute(options.app, options.pathname)?.route ?? options.app.notFound;
|
|
2470
|
+
const route = options.pathname === null ? void 0 : options.matchAppRoute(options.app, options.pathname)?.route ?? options.app.notFound;
|
|
2296
2471
|
if (!route) return {};
|
|
2297
2472
|
const manifest = {};
|
|
2298
2473
|
const modules = [...route.shellFile ? [{
|
|
@@ -2341,18 +2516,19 @@ function collectDevCssUrls(entry) {
|
|
|
2341
2516
|
}
|
|
2342
2517
|
return [...urls];
|
|
2343
2518
|
}
|
|
2344
|
-
function injectDevCssLinks(html, manifest) {
|
|
2519
|
+
function injectDevCssLinks(html, manifest, base = "/") {
|
|
2345
2520
|
if (!html.includes("</head>")) return html;
|
|
2346
|
-
const tags = [...new Set(Object.values(manifest).flat())].map((url) => escapeHtmlAttribute(url)).filter((escapedUrl) => !html.includes(`href="${escapedUrl}"`)).map((escapedUrl) => `<link rel="stylesheet" href="${escapedUrl}">`);
|
|
2521
|
+
const tags = [...new Set(Object.values(manifest).flat().map((url) => base === "/" || !url.startsWith("/") ? url : `${base}${url.slice(1)}`))].map((url) => escapeHtmlAttribute(url)).filter((escapedUrl) => !html.includes(`href="${escapedUrl}"`)).map((escapedUrl) => `<link rel="stylesheet" href="${escapedUrl}">`);
|
|
2347
2522
|
if (tags.length === 0) return html;
|
|
2348
2523
|
return html.replace("</head>", ` ${tags.join("\n ")}\n </head>`);
|
|
2349
2524
|
}
|
|
2350
|
-
async function injectDevCssForPath(server, path, html) {
|
|
2351
|
-
return injectDevCssLinks(html, await createDevCssManifest(server, await resolveDevCssContextForPath(server, path)));
|
|
2525
|
+
async function injectDevCssForPath(server, path, html, options = {}) {
|
|
2526
|
+
return injectDevCssLinks(html, await createDevCssManifest(server, await resolveDevCssContextForPath(server, path, options)), server.config.base || "/");
|
|
2352
2527
|
}
|
|
2353
|
-
async function resolveDevCssContextForPath(server, path) {
|
|
2528
|
+
async function resolveDevCssContextForPath(server, path, options = {}) {
|
|
2354
2529
|
const [framework, serverMod] = await Promise.all([server.ssrLoadModule("@pracht/core/server"), server.ssrLoadModule(PRACHT_DEV_MODULE_ID)]);
|
|
2355
|
-
const
|
|
2530
|
+
const publicPathname = new URL(path, "http://localhost").pathname;
|
|
2531
|
+
const pathname = options.basePathRetained ? framework.stripBase(publicPathname) : publicPathname;
|
|
2356
2532
|
return {
|
|
2357
2533
|
app: serverMod.resolvedApp,
|
|
2358
2534
|
matchAppRoute: framework.matchAppRoute,
|
|
@@ -2374,7 +2550,7 @@ function createDevCssInjectionMiddleware(server) {
|
|
|
2374
2550
|
next();
|
|
2375
2551
|
return;
|
|
2376
2552
|
}
|
|
2377
|
-
const contextPromise = resolveDevCssContextForPath(server, req.url ?? "/").catch((error) => {
|
|
2553
|
+
const contextPromise = resolveDevCssContextForPath(server, req.url ?? "/", { basePathRetained: true }).catch((error) => {
|
|
2378
2554
|
if (!warned) {
|
|
2379
2555
|
warned = true;
|
|
2380
2556
|
server.config.logger.warn(`[pracht] Could not discover development stylesheets: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -2405,7 +2581,7 @@ function createDevCssInjectionMiddleware(server) {
|
|
|
2405
2581
|
try {
|
|
2406
2582
|
const context = await contextPromise;
|
|
2407
2583
|
const manifest = context ? await createDevCssManifest(server, context) : null;
|
|
2408
|
-
originalEnd(manifest ? injectDevCssLinks(body.toString("utf-8"), manifest) : body.toString("utf-8"), done);
|
|
2584
|
+
originalEnd(manifest ? injectDevCssLinks(body.toString("utf-8"), manifest, server.config.base || "/") : body.toString("utf-8"), done);
|
|
2409
2585
|
} catch {
|
|
2410
2586
|
originalEnd(body, done);
|
|
2411
2587
|
}
|
|
@@ -2453,13 +2629,13 @@ async function serveDevtools(server, res, options) {
|
|
|
2453
2629
|
res.end(JSON.stringify(graph, null, 2));
|
|
2454
2630
|
return;
|
|
2455
2631
|
}
|
|
2456
|
-
let html = devtools.buildDevtoolsHtml(graph);
|
|
2632
|
+
let html = devtools.buildDevtoolsHtml(graph, { base: options.base });
|
|
2457
2633
|
html = await server.transformIndexHtml(options.url, html);
|
|
2458
2634
|
res.statusCode = 200;
|
|
2459
2635
|
res.setHeader("content-type", "text/html; charset=utf-8");
|
|
2460
2636
|
res.end(html);
|
|
2461
2637
|
}
|
|
2462
|
-
async function handleDevError(server, req, res, next, url, error) {
|
|
2638
|
+
async function handleDevError(server, req, res, next, url, error, base) {
|
|
2463
2639
|
if (error instanceof Error) server.ssrFixStacktrace(error);
|
|
2464
2640
|
if (req.headers["x-pracht-route-state-request"] === "1") {
|
|
2465
2641
|
res.statusCode = 500;
|
|
@@ -2476,7 +2652,8 @@ async function handleDevError(server, req, res, next, url, error) {
|
|
|
2476
2652
|
let html = buildErrorOverlayHtml({
|
|
2477
2653
|
message: error instanceof Error ? error.message : String(error),
|
|
2478
2654
|
stack: error instanceof Error ? error.stack : void 0,
|
|
2479
|
-
root: server.config.root
|
|
2655
|
+
root: server.config.root,
|
|
2656
|
+
base
|
|
2480
2657
|
});
|
|
2481
2658
|
html = await server.transformIndexHtml(url, html);
|
|
2482
2659
|
res.statusCode = 500;
|
|
@@ -2505,11 +2682,12 @@ function isDevNotFoundRequest(requestUrl, req, options = {}) {
|
|
|
2505
2682
|
if (!accept.includes("text/html") && !accept.includes("application/xhtml+xml")) return false;
|
|
2506
2683
|
return !matchesResolvedRoute(url.pathname, options);
|
|
2507
2684
|
}
|
|
2508
|
-
async function serveDevNotFound(server, res, next, url, pathname, options) {
|
|
2685
|
+
async function serveDevNotFound(server, res, next, url, pathname, options, base) {
|
|
2509
2686
|
try {
|
|
2510
2687
|
const { buildDevNotFoundHtml } = await server.ssrLoadModule("@pracht/core/dev-404");
|
|
2511
2688
|
let html = buildDevNotFoundHtml({
|
|
2512
2689
|
apiRoutes: options.apiRoutes.map((route) => ({ path: route.path })),
|
|
2690
|
+
base,
|
|
2513
2691
|
requestedPath: pathname,
|
|
2514
2692
|
routes: options.app.routes.map((route) => ({
|
|
2515
2693
|
path: route.path,
|
|
@@ -2604,10 +2782,11 @@ const DEV_ASSET_EXTENSIONS = new Set([
|
|
|
2604
2782
|
".woff2",
|
|
2605
2783
|
".xml"
|
|
2606
2784
|
]);
|
|
2607
|
-
async function nodeToWebRequest(req, maxBodySize) {
|
|
2785
|
+
async function nodeToWebRequest(req, maxBodySize, base = "/") {
|
|
2608
2786
|
const protocol = "http";
|
|
2609
2787
|
const host = req.headers.host ?? "localhost";
|
|
2610
|
-
const
|
|
2788
|
+
const path = req.url ?? "/";
|
|
2789
|
+
const url = new URL(base === "/" || !path.startsWith("/") ? path : `${base}${path.slice(1)}`, `${protocol}://${host}`);
|
|
2611
2790
|
const method = req.method ?? "GET";
|
|
2612
2791
|
const headers = new Headers();
|
|
2613
2792
|
for (const [key, value] of Object.entries(req.headers)) {
|
|
@@ -2635,14 +2814,34 @@ async function nodeToWebRequest(req, maxBodySize) {
|
|
|
2635
2814
|
}
|
|
2636
2815
|
//#endregion
|
|
2637
2816
|
//#region src/index.ts
|
|
2817
|
+
function reachesHeadBearingModule(modules, serverRoot, headHints) {
|
|
2818
|
+
const pending = [...modules];
|
|
2819
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2820
|
+
while (pending.length > 0) {
|
|
2821
|
+
const module = pending.pop();
|
|
2822
|
+
if (!module || seen.has(module)) continue;
|
|
2823
|
+
seen.add(module);
|
|
2824
|
+
const modulePath = module.file ?? module.id?.split("?", 1)[0];
|
|
2825
|
+
if (modulePath) {
|
|
2826
|
+
const normalizedPath = toPosixPath(modulePath);
|
|
2827
|
+
if (headHints[normalizedPath.startsWith(serverRoot) ? normalizedPath.slice(serverRoot.length) : normalizedPath] === true) return true;
|
|
2828
|
+
}
|
|
2829
|
+
if (module.importers) pending.push(...module.importers);
|
|
2830
|
+
}
|
|
2831
|
+
return false;
|
|
2832
|
+
}
|
|
2638
2833
|
function pracht(options = {}) {
|
|
2639
2834
|
const resolved = resolveOptions(options);
|
|
2640
2835
|
const isPagesMode = !!resolved.pagesDir;
|
|
2641
2836
|
let root = process.cwd();
|
|
2642
2837
|
let routeFileDirs = [];
|
|
2838
|
+
let clientRouteHeadHints = {};
|
|
2839
|
+
const routeFileExtensions = withAdditionalExtensions(DEFAULT_ROUTE_EXTENSIONS, resolved.additionalExtensions);
|
|
2643
2840
|
let capabilityModulePaths = /* @__PURE__ */ new Set();
|
|
2644
2841
|
if (isPagesMode && options.appFile) console.warn("[pracht] Both `pagesDir` and `appFile` are set. `pagesDir` takes precedence — `appFile` will be ignored.");
|
|
2645
2842
|
let isBuild = false;
|
|
2843
|
+
let base = "/";
|
|
2844
|
+
let configuredBase;
|
|
2646
2845
|
const prachtPlugin = {
|
|
2647
2846
|
name: "pracht",
|
|
2648
2847
|
enforce: "pre",
|
|
@@ -2654,13 +2853,15 @@ function pracht(options = {}) {
|
|
|
2654
2853
|
const envDir = _config.envDir ? resolve(configRoot, _config.envDir) : configRoot;
|
|
2655
2854
|
const publicEnvDefine = JSON.stringify(loadEnv(env.mode, envDir, PUBLIC_ENV_PREFIX));
|
|
2656
2855
|
const agentSurfaceDefine = env.command === "build" ? String(hasAgentSurface(resolved, configRoot)) : "true";
|
|
2856
|
+
const staticTargetDefine = String(env.command === "build" && resolved.adapter.staticTarget === true);
|
|
2657
2857
|
return {
|
|
2658
2858
|
appType: "custom",
|
|
2659
2859
|
envPrefix: ["VITE_", PUBLIC_ENV_PREFIX],
|
|
2660
2860
|
resolve: { dedupe: PREACT_DEDUPE },
|
|
2661
2861
|
define: {
|
|
2662
2862
|
__PRACHT_PUBLIC_ENV__: publicEnvDefine,
|
|
2663
|
-
__PRACHT_AGENT_SURFACE__: agentSurfaceDefine
|
|
2863
|
+
__PRACHT_AGENT_SURFACE__: agentSurfaceDefine,
|
|
2864
|
+
__PRACHT_STATIC_TARGET__: staticTargetDefine
|
|
2664
2865
|
},
|
|
2665
2866
|
...isSSRBuild ? {} : { build: { rollupOptions: {
|
|
2666
2867
|
...wantsIslandsEntry ? { input: [PRACHT_ISLANDS_CLIENT_MODULE_ID] } : {},
|
|
@@ -2683,12 +2884,15 @@ function pracht(options = {}) {
|
|
|
2683
2884
|
external: ["node:module"]
|
|
2684
2885
|
} } },
|
|
2685
2886
|
build: { rollupOptions: { external: [/^cloudflare:/] } }
|
|
2686
|
-
} : {}
|
|
2887
|
+
} : {},
|
|
2888
|
+
...!isEdge && isSSRBuild ? { ssr: { noExternal: [PRACHT_SSR_NO_EXTERNAL] } } : {}
|
|
2687
2889
|
};
|
|
2688
2890
|
},
|
|
2689
2891
|
configResolved(config) {
|
|
2892
|
+
assertSafeRootAbsoluteDeployBase(config.base);
|
|
2690
2893
|
root = config.root;
|
|
2691
2894
|
isBuild = config.command === "build";
|
|
2895
|
+
base = config.base;
|
|
2692
2896
|
routeFileDirs = computeRouteFileDirs(root, resolved);
|
|
2693
2897
|
capabilityModulePaths = new Set(resolveCapabilityModulePaths(resolved, root).map(canonicalFilePath));
|
|
2694
2898
|
},
|
|
@@ -2704,11 +2908,19 @@ function pracht(options = {}) {
|
|
|
2704
2908
|
},
|
|
2705
2909
|
load(id) {
|
|
2706
2910
|
if (isIslandsClientModule(id)) return createPrachtIslandsClientModuleSource(resolved, { root });
|
|
2707
|
-
if (isClientModule(id))
|
|
2708
|
-
|
|
2911
|
+
if (isClientModule(id)) {
|
|
2912
|
+
clientRouteHeadHints = createRouteHeadHintsForVirtualModules(resolved, root);
|
|
2913
|
+
return createPrachtClientModuleSource(resolved, { root });
|
|
2914
|
+
}
|
|
2915
|
+
if (isDevModule(id)) return createPrachtDevModuleSource(resolved, {
|
|
2916
|
+
root,
|
|
2917
|
+
base
|
|
2918
|
+
});
|
|
2709
2919
|
if (isServerModule(id)) return createPrachtServerModuleSource(resolved, {
|
|
2710
2920
|
root,
|
|
2711
|
-
isBuild
|
|
2921
|
+
isBuild,
|
|
2922
|
+
base,
|
|
2923
|
+
configuredBase
|
|
2712
2924
|
});
|
|
2713
2925
|
if (isCapabilitiesModule(id)) return createPrachtCapabilitiesClientModuleSource(resolved, { root });
|
|
2714
2926
|
if (isWebmcpModule(id)) return createPrachtWebmcpModuleSource(resolved, { root });
|
|
@@ -2727,6 +2939,7 @@ function pracht(options = {}) {
|
|
|
2727
2939
|
configureServer(server) {
|
|
2728
2940
|
if (isPagesMode) watchPagesDirectory(server, resolved, root);
|
|
2729
2941
|
if (resolved.adapter.ownsDevServer) {
|
|
2942
|
+
server.middlewares.use(createOwnedDevEntryMiddleware(server));
|
|
2730
2943
|
server.middlewares.use(createDevCssInjectionMiddleware(server));
|
|
2731
2944
|
return;
|
|
2732
2945
|
}
|
|
@@ -2745,14 +2958,29 @@ function pracht(options = {}) {
|
|
|
2745
2958
|
return html;
|
|
2746
2959
|
}
|
|
2747
2960
|
},
|
|
2748
|
-
handleHotUpdate({ file, server }) {
|
|
2961
|
+
handleHotUpdate({ file, modules = [], server }) {
|
|
2749
2962
|
const serverRoot = toPosixPath(server.config.root);
|
|
2750
2963
|
const normalizedFile = toPosixPath(file);
|
|
2751
2964
|
const relative = normalizedFile.startsWith(serverRoot) ? normalizedFile.slice(serverRoot.length) : normalizedFile;
|
|
2965
|
+
const changesRouteHeadSource = isPagesMode ? relative.startsWith(resolved.pagesDir) : relative.startsWith(resolved.routesDir) || relative.startsWith(resolved.shellsDir);
|
|
2966
|
+
const changesRouteHeadDependency = reachesHeadBearingModule(modules, serverRoot, clientRouteHeadHints);
|
|
2967
|
+
let shouldReloadClientHead = changesRouteHeadDependency;
|
|
2968
|
+
let clientHeadModule;
|
|
2969
|
+
if (changesRouteHeadSource || changesRouteHeadDependency) clientHeadModule = server.moduleGraph.getModuleById(PRACHT_CLIENT_MODULE_ID);
|
|
2970
|
+
if (changesRouteHeadSource) {
|
|
2971
|
+
const previousHint = clientRouteHeadHints[relative];
|
|
2972
|
+
try {
|
|
2973
|
+
const nextHints = createRouteHeadHintsForVirtualModules(resolved, root);
|
|
2974
|
+
shouldReloadClientHead ||= previousHint === true || nextHints[relative] === true;
|
|
2975
|
+
clientRouteHeadHints = nextHints;
|
|
2976
|
+
} catch {
|
|
2977
|
+
shouldReloadClientHead = true;
|
|
2978
|
+
}
|
|
2979
|
+
} else if (changesRouteHeadDependency && clientHeadModule) server.moduleGraph.invalidateModule(clientHeadModule);
|
|
2752
2980
|
if (isPagesMode && relative.startsWith(resolved.pagesDir)) {
|
|
2753
2981
|
clearPagesAppSourceCache();
|
|
2754
2982
|
invalidateVirtualModules(server);
|
|
2755
|
-
sendServerOnlyFullReload(server, file);
|
|
2983
|
+
if (!sendServerOnlyFullReload(server, file) && shouldReloadClientHead && clientHeadModule) return [...new Set([...modules, clientHeadModule])];
|
|
2756
2984
|
return;
|
|
2757
2985
|
}
|
|
2758
2986
|
if (!isPagesMode && relative === resolved.appFile) {
|
|
@@ -2772,7 +3000,7 @@ function pracht(options = {}) {
|
|
|
2772
3000
|
if (serverMod) server.moduleGraph.invalidateModule(serverMod);
|
|
2773
3001
|
const devMod = server.moduleGraph.getModuleById(PRACHT_DEV_MODULE_ID);
|
|
2774
3002
|
if (devMod) server.moduleGraph.invalidateModule(devMod);
|
|
2775
|
-
if (relative.startsWith(resolved.routesDir)) {
|
|
3003
|
+
if (relative.startsWith(resolved.routesDir) || relative.startsWith(resolved.shellsDir)) {
|
|
2776
3004
|
const clientMod = server.moduleGraph.getModuleById(PRACHT_CLIENT_MODULE_ID);
|
|
2777
3005
|
if (clientMod) server.moduleGraph.invalidateModule(clientMod);
|
|
2778
3006
|
}
|
|
@@ -2790,7 +3018,16 @@ function pracht(options = {}) {
|
|
|
2790
3018
|
if (capabilityMod) server.moduleGraph.invalidateModule(capabilityMod);
|
|
2791
3019
|
}
|
|
2792
3020
|
}
|
|
2793
|
-
sendServerOnlyFullReload(server, file);
|
|
3021
|
+
if (!sendServerOnlyFullReload(server, file) && shouldReloadClientHead && clientHeadModule) return [...new Set([...modules, clientHeadModule])];
|
|
3022
|
+
}
|
|
3023
|
+
};
|
|
3024
|
+
const configuredBasePlugin = {
|
|
3025
|
+
name: "pracht:configured-base",
|
|
3026
|
+
config: {
|
|
3027
|
+
order: "post",
|
|
3028
|
+
handler(config) {
|
|
3029
|
+
configuredBase = typeof config.base === "string" ? config.base : void 0;
|
|
3030
|
+
}
|
|
2794
3031
|
}
|
|
2795
3032
|
};
|
|
2796
3033
|
const clientModuleTransformPlugin = {
|
|
@@ -2798,7 +3035,7 @@ function pracht(options = {}) {
|
|
|
2798
3035
|
enforce: "post",
|
|
2799
3036
|
transform(code, id, transformOptions) {
|
|
2800
3037
|
if (!transformOptions?.ssr && isCapabilityModule(id, capabilityModulePaths)) throw new Error(`[pracht] Capability module ${JSON.stringify(toPosixPath(id))} was imported by client code. Capability modules are server-only — their run() implementation and its imports would be bundled for every visitor. Call the capability instead: \`callCapability\`/\`capabilities\` from "virtual:pracht/capabilities" in the browser, or \`invokeCapability\` from "@pracht/core/server" in loaders, middleware, and API routes.`);
|
|
2801
|
-
if (!(isPrachtClientModuleId(id) || !transformOptions?.ssr && isRouteOrShellFile(id, routeFileDirs))) return null;
|
|
3038
|
+
if (!(isPrachtClientModuleId(id) || !transformOptions?.ssr && isRouteOrShellFile(id, routeFileDirs, routeFileExtensions))) return null;
|
|
2802
3039
|
const transformed = stripServerOnlyExportsForClient(code, id);
|
|
2803
3040
|
if (transformed === code) return null;
|
|
2804
3041
|
return {
|
|
@@ -2812,7 +3049,7 @@ function pracht(options = {}) {
|
|
|
2812
3049
|
name: "pracht:optimize-deps-entries",
|
|
2813
3050
|
enforce: "post",
|
|
2814
3051
|
config(config) {
|
|
2815
|
-
return withPrachtOptimizeDepsEntries(config,
|
|
3052
|
+
return withPrachtOptimizeDepsEntries(config, resolved, createPrachtOptimizeDepsInclude(config.root ?? process.cwd()));
|
|
2816
3053
|
}
|
|
2817
3054
|
};
|
|
2818
3055
|
const precompilePlugin = resolved.precompileSsrJsx ? preactSsrPrecompile({
|
|
@@ -2823,6 +3060,7 @@ function pracht(options = {}) {
|
|
|
2823
3060
|
...precompilePlugin ? [precompilePlugin] : [],
|
|
2824
3061
|
...preact(),
|
|
2825
3062
|
prachtPlugin,
|
|
3063
|
+
configuredBasePlugin,
|
|
2826
3064
|
clientModuleTransformPlugin,
|
|
2827
3065
|
...edgeRuntimeSafetyPlugin ? [edgeRuntimeSafetyPlugin] : [],
|
|
2828
3066
|
createEnvSafetyPlugin(resolved.envSafety)
|
|
@@ -2832,6 +3070,26 @@ function pracht(options = {}) {
|
|
|
2832
3070
|
plugins.push(optimizeDepsEntriesPlugin);
|
|
2833
3071
|
return plugins;
|
|
2834
3072
|
}
|
|
3073
|
+
function assertSafeRootAbsoluteDeployBase(base) {
|
|
3074
|
+
if (typeof base !== "string" || !base.startsWith("/") || base.startsWith("//")) return;
|
|
3075
|
+
let safe = !base.includes("?") && !base.includes("#");
|
|
3076
|
+
if (safe) try {
|
|
3077
|
+
const segments = base.split("/");
|
|
3078
|
+
safe = segments.every((segment, index) => {
|
|
3079
|
+
if (segment === "" && index !== 0 && index !== segments.length - 1) return false;
|
|
3080
|
+
const decoded = decodeURIComponent(segment);
|
|
3081
|
+
if (decoded === "." || decoded === "..") return false;
|
|
3082
|
+
for (const character of decoded) {
|
|
3083
|
+
const codePoint = character.codePointAt(0);
|
|
3084
|
+
if (character === "/" || character === "\\" || codePoint === 0 || codePoint !== void 0 && (codePoint <= 31 || codePoint === 127)) return false;
|
|
3085
|
+
}
|
|
3086
|
+
return true;
|
|
3087
|
+
});
|
|
3088
|
+
} catch {
|
|
3089
|
+
safe = false;
|
|
3090
|
+
}
|
|
3091
|
+
if (!safe) throw new Error(`[pracht] Vite \`base\` is set to ${JSON.stringify(base)}, but root-absolute deploy bases must contain safe URL segments. Repeated slashes, malformed escapes, and segments that decode to a path separator, \`.\`, \`..\`, NUL, or another control character are not allowed.`);
|
|
3092
|
+
}
|
|
2835
3093
|
function isGraphOnlyMode() {
|
|
2836
3094
|
return process.env[PRACHT_GRAPH_ONLY_ENV] === "1";
|
|
2837
3095
|
}
|
|
@@ -2915,6 +3173,7 @@ const PRACHT_OPTIMIZE_DEPS_INCLUDE = [
|
|
|
2915
3173
|
"@pracht/core/manifest"
|
|
2916
3174
|
];
|
|
2917
3175
|
const PREACT_DEDUPE = ["preact", "preact-render-to-string"];
|
|
3176
|
+
const PRACHT_SSR_NO_EXTERNAL = /^@pracht\//;
|
|
2918
3177
|
function createPrachtOptimizeDepsInclude(root) {
|
|
2919
3178
|
try {
|
|
2920
3179
|
if (!toPosixPath(createRequire(join(root, "package.json")).resolve("@pracht/core/package.json")).includes("/node_modules/")) return [];
|
|
@@ -2923,8 +3182,9 @@ function createPrachtOptimizeDepsInclude(root) {
|
|
|
2923
3182
|
return [];
|
|
2924
3183
|
}
|
|
2925
3184
|
}
|
|
2926
|
-
function withPrachtOptimizeDepsEntries(config,
|
|
2927
|
-
const
|
|
3185
|
+
function withPrachtOptimizeDepsEntries(config, resolved, prachtInclude) {
|
|
3186
|
+
const prachtEntries = createPrachtOptimizeDepsEntries(resolved, config.optimizeDeps?.extensions);
|
|
3187
|
+
const environments = Object.fromEntries(Object.entries(config.environments ?? {}).map(([name, environment]) => [name, { optimizeDeps: { entries: mergeOptimizeDepsEntries(environment.optimizeDeps?.entries, createPrachtOptimizeDepsEntries(resolved, environment.optimizeDeps?.extensions ?? config.optimizeDeps?.extensions)) } }]));
|
|
2928
3188
|
return {
|
|
2929
3189
|
optimizeDeps: {
|
|
2930
3190
|
entries: mergeOptimizeDepsEntries(config.optimizeDeps?.entries, prachtEntries),
|
|
@@ -2933,9 +3193,24 @@ function withPrachtOptimizeDepsEntries(config, prachtEntries, prachtInclude) {
|
|
|
2933
3193
|
...Object.keys(environments).length > 0 ? { environments } : {}
|
|
2934
3194
|
};
|
|
2935
3195
|
}
|
|
2936
|
-
|
|
3196
|
+
const VITE_SCANNABLE_ROUTE_EXTENSIONS = new Set([
|
|
3197
|
+
".ts",
|
|
3198
|
+
".tsx",
|
|
3199
|
+
".js",
|
|
3200
|
+
".jsx",
|
|
3201
|
+
".mts",
|
|
3202
|
+
".mjs",
|
|
3203
|
+
".cts",
|
|
3204
|
+
".cjs",
|
|
3205
|
+
".vue",
|
|
3206
|
+
".svelte",
|
|
3207
|
+
".astro",
|
|
3208
|
+
".imba"
|
|
3209
|
+
]);
|
|
3210
|
+
function createPrachtOptimizeDepsEntries(resolved, optimizerExtensions) {
|
|
2937
3211
|
const scriptExtensions = "{ts,tsx,js,jsx}";
|
|
2938
|
-
const
|
|
3212
|
+
const explicitlyScannable = new Set(optimizerExtensions ?? []);
|
|
3213
|
+
const routeExtensions = extensionGlob([...new Set([...DEFAULT_ROUTE_EXTENSIONS, ...resolved.additionalExtensions])].filter((extension) => VITE_SCANNABLE_ROUTE_EXTENSIONS.has(extension) || explicitlyScannable.has(extension)));
|
|
2939
3214
|
const apiDir = toOptimizeDepsEntry(resolved.apiDir);
|
|
2940
3215
|
const apiEntries = [`${apiDir}/**/*.{ts,js,tsx,jsx}`, `!${apiDir}/**/*.d.ts`];
|
|
2941
3216
|
const entries = resolved.pagesDir ? [
|
|
@@ -2985,15 +3260,6 @@ function invalidateVirtualModules(server) {
|
|
|
2985
3260
|
if (serverMod) server.moduleGraph.invalidateModule(serverMod);
|
|
2986
3261
|
if (devMod) server.moduleGraph.invalidateModule(devMod);
|
|
2987
3262
|
}
|
|
2988
|
-
const ROUTE_FILE_EXTENSIONS = new Set([
|
|
2989
|
-
".ts",
|
|
2990
|
-
".tsx",
|
|
2991
|
-
".js",
|
|
2992
|
-
".jsx",
|
|
2993
|
-
".md",
|
|
2994
|
-
".mdx",
|
|
2995
|
-
".tsrx"
|
|
2996
|
-
]);
|
|
2997
3263
|
function computeRouteFileDirs(root, resolved) {
|
|
2998
3264
|
return (resolved.pagesDir ? [resolved.pagesDir] : [resolved.routesDir, resolved.shellsDir]).map((dir) => canonicalFilePath(resolveConfigPath(root, dir))).map(withTrailingSep);
|
|
2999
3265
|
}
|
|
@@ -3024,7 +3290,7 @@ function canonicalFilePath(path) {
|
|
|
3024
3290
|
return toPosixPath(path);
|
|
3025
3291
|
}
|
|
3026
3292
|
}
|
|
3027
|
-
function isRouteOrShellFile(id, dirs) {
|
|
3293
|
+
function isRouteOrShellFile(id, dirs, extensions) {
|
|
3028
3294
|
if (dirs.length === 0) return false;
|
|
3029
3295
|
const queryStart = id.indexOf("?");
|
|
3030
3296
|
const path = queryStart === -1 ? id : id.slice(0, queryStart);
|
|
@@ -3032,7 +3298,7 @@ function isRouteOrShellFile(id, dirs) {
|
|
|
3032
3298
|
const extIndex = path.lastIndexOf(".");
|
|
3033
3299
|
if (extIndex === -1) return false;
|
|
3034
3300
|
const ext = path.slice(extIndex);
|
|
3035
|
-
if (!
|
|
3301
|
+
if (!extensions.has(ext)) return false;
|
|
3036
3302
|
const normalized = toPosixPath(path);
|
|
3037
3303
|
return dirs.some((dir) => normalized.startsWith(dir));
|
|
3038
3304
|
}
|