@pracht/vite-plugin 0.7.2 → 0.7.4
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/dist/index.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { i as scanPagesDirectory, n as generatePagesManifestSource, o as createRouteLoaderHints } from "./pages-router-
|
|
1
|
+
import { i as scanPagesDirectory, n as generatePagesManifestSource, o as createRouteLoaderHints } from "./pages-router-CStJY2Zp.mjs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { preactSsrPrecompile } from "@pracht/preact-ssr-precompile";
|
|
4
4
|
import preact from "@preact/preset-vite";
|
|
5
|
+
import { existsSync, readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
5
6
|
import { dirname, extname, join, resolve } from "node:path";
|
|
6
7
|
import { loadEnv, parseAst } from "vite";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { evaluateLiteral, extractCapabilityRegistrations, extractDefineCapabilityArgs, scanTopLevelProperties } from "@pracht/capabilities/static";
|
|
8
|
+
import { CAPABILITY_SETTLED_EVENT, CAPABILITY_TRANSPORT_HEADER, CONFIRMATION_HEADER } from "@pracht/capabilities";
|
|
9
|
+
import { extractCapabilityProjection, extractCapabilityRegistrations } from "@pracht/capabilities/static";
|
|
10
10
|
import { createNodeServerEntryModule } from "@pracht/adapter-node";
|
|
11
11
|
import { resolveRegistryModule } from "@pracht/core";
|
|
12
12
|
//#region src/client-module-query.ts
|
|
@@ -1219,6 +1219,35 @@ function createEnvSafetyPlugin(envSafety) {
|
|
|
1219
1219
|
};
|
|
1220
1220
|
}
|
|
1221
1221
|
//#endregion
|
|
1222
|
+
//#region src/hot-update-reload.ts
|
|
1223
|
+
/**
|
|
1224
|
+
* True when `file` participates in server rendering but has no runtime
|
|
1225
|
+
* counterpart in the client module graph, meaning client HMR can never deliver
|
|
1226
|
+
* its change. File-only asset entries created by content scanners are watchers,
|
|
1227
|
+
* not browser modules, so they do not make an update client-reachable.
|
|
1228
|
+
*/
|
|
1229
|
+
function isServerOnlyModuleFile(server, file) {
|
|
1230
|
+
const environments = server.environments;
|
|
1231
|
+
const client = environments?.client;
|
|
1232
|
+
if (!client) return false;
|
|
1233
|
+
if (hasRuntimeModules(client, file)) return false;
|
|
1234
|
+
for (const [name, environment] of Object.entries(environments ?? {})) {
|
|
1235
|
+
if (name === "client" || !environment) continue;
|
|
1236
|
+
if (hasRuntimeModules(environment, file)) return true;
|
|
1237
|
+
}
|
|
1238
|
+
return false;
|
|
1239
|
+
}
|
|
1240
|
+
/** Reload open pages when `file` can only reach them through the server. */
|
|
1241
|
+
function sendServerOnlyFullReload(server, file) {
|
|
1242
|
+
if (!isServerOnlyModuleFile(server, file)) return false;
|
|
1243
|
+
server.environments?.client?.hot?.send({ type: "full-reload" });
|
|
1244
|
+
return true;
|
|
1245
|
+
}
|
|
1246
|
+
function hasRuntimeModules(environment, file) {
|
|
1247
|
+
for (const module of environment.moduleGraph.getModulesByFile(file) ?? []) if (module.type !== "asset") return true;
|
|
1248
|
+
return false;
|
|
1249
|
+
}
|
|
1250
|
+
//#endregion
|
|
1222
1251
|
//#region src/plugin-assets.ts
|
|
1223
1252
|
const PRACHT_CLIENT_MODULE_ID = "virtual:pracht/client";
|
|
1224
1253
|
const PRACHT_SERVER_MODULE_ID = "virtual:pracht/server";
|
|
@@ -1415,57 +1444,37 @@ function extractCapabilities(options = {}, root = process.cwd()) {
|
|
|
1415
1444
|
return extractCapabilityMetadata(name, file, source);
|
|
1416
1445
|
});
|
|
1417
1446
|
}
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
const
|
|
1433
|
-
if (
|
|
1434
|
-
const
|
|
1435
|
-
let
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
if (webmcp && !httpPath) throw new Error(`[pracht] Capability "${name}" (${file}): expose.webmcp requires expose.http.`);
|
|
1441
|
-
let description = "";
|
|
1442
|
-
const descriptionText = properties.get("description");
|
|
1443
|
-
if (descriptionText) {
|
|
1444
|
-
const value = evaluateLiteral(descriptionText);
|
|
1445
|
-
if (typeof value === "string") description = value;
|
|
1446
|
-
}
|
|
1447
|
-
let effect = null;
|
|
1448
|
-
const effectText = properties.get("effect");
|
|
1449
|
-
if (effectText) {
|
|
1450
|
-
const value = evaluateLiteral(effectText);
|
|
1451
|
-
if (typeof value === "string") effect = value;
|
|
1452
|
-
}
|
|
1453
|
-
if (httpPath && effect !== "read" && effect !== "write" && effect !== "destructive") throw new Error(`[pracht] Capability "${name}" (${file}) is exposed via HTTP, but its "effect" could not be extracted at build time. HTTP-exposed capabilities must declare "effect" as an inline "read", "write", or "destructive" string literal.`);
|
|
1454
|
-
let inputSchema = null;
|
|
1455
|
-
if (webmcp) {
|
|
1456
|
-
const inputText = properties.get("input");
|
|
1457
|
-
const value = inputText ? evaluateLiteral(inputText) : void 0;
|
|
1458
|
-
if (!isPlainObject(value)) throw new Error(`[pracht] Capability "${name}" (${file}) is exposed via WebMCP, but its "input" schema could not be extracted at build time. WebMCP-exposed capabilities must declare their input schema as an inline object literal.`);
|
|
1459
|
-
inputSchema = value;
|
|
1447
|
+
/**
|
|
1448
|
+
* Absolute paths of the capability modules the manifest registers.
|
|
1449
|
+
*
|
|
1450
|
+
* The client-import guard uses this rather than a `capabilitiesDir` prefix
|
|
1451
|
+
* test: registration is what makes a module server-only, and the manifest may
|
|
1452
|
+
* point anywhere. A directory test both misses capabilities registered from
|
|
1453
|
+
* elsewhere and wrongly rejects ordinary co-located files (shared constants,
|
|
1454
|
+
* types) that happen to sit in the capability folder.
|
|
1455
|
+
*
|
|
1456
|
+
* Returns an empty list when the manifest cannot be read or parsed — the
|
|
1457
|
+
* virtual-module generation raises its own precise error for those, and
|
|
1458
|
+
* guessing here would turn one clear failure into two confusing ones.
|
|
1459
|
+
*/
|
|
1460
|
+
function resolveCapabilityModulePaths(options = {}, root = process.cwd()) {
|
|
1461
|
+
const resolved = resolveOptions(options);
|
|
1462
|
+
if (resolved.pagesDir) return [];
|
|
1463
|
+
const appFileAbs = resolve(root, resolved.appFile.replace(/^\//, ""));
|
|
1464
|
+
let manifestSource;
|
|
1465
|
+
try {
|
|
1466
|
+
manifestSource = readFileSync(appFileAbs, "utf-8");
|
|
1467
|
+
} catch {
|
|
1468
|
+
return [];
|
|
1460
1469
|
}
|
|
1470
|
+
const appDir = dirname(appFileAbs);
|
|
1471
|
+
return extractCapabilityRegistrations(manifestSource).map(({ file }) => file.startsWith("/") ? resolve(root, file.replace(/^\//, "")) : resolve(appDir, file));
|
|
1472
|
+
}
|
|
1473
|
+
function extractCapabilityMetadata(name, file, source) {
|
|
1461
1474
|
return {
|
|
1462
1475
|
name,
|
|
1463
1476
|
file,
|
|
1464
|
-
|
|
1465
|
-
effect,
|
|
1466
|
-
httpPath,
|
|
1467
|
-
webmcp,
|
|
1468
|
-
inputSchema
|
|
1477
|
+
...extractCapabilityProjection(name, source, (detail) => `[pracht] Capability ${JSON.stringify(name)} (${file}) ${detail}`)
|
|
1469
1478
|
};
|
|
1470
1479
|
}
|
|
1471
1480
|
/**
|
|
@@ -1643,9 +1652,6 @@ function hasWebmcpCapabilities(options = {}, root = process.cwd()) {
|
|
|
1643
1652
|
return true;
|
|
1644
1653
|
}
|
|
1645
1654
|
}
|
|
1646
|
-
function isPlainObject(value) {
|
|
1647
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1648
|
-
}
|
|
1649
1655
|
//#endregion
|
|
1650
1656
|
//#region src/plugin-codegen.ts
|
|
1651
1657
|
const ROUTE_MODULE_EXTENSIONS = new Set([
|
|
@@ -2484,6 +2490,7 @@ function pracht(options = {}) {
|
|
|
2484
2490
|
const isPagesMode = !!resolved.pagesDir;
|
|
2485
2491
|
let root = process.cwd();
|
|
2486
2492
|
let routeFileDirs = [];
|
|
2493
|
+
let capabilityModulePaths = /* @__PURE__ */ new Set();
|
|
2487
2494
|
if (isPagesMode && options.appFile) console.warn("[pracht] Both `pagesDir` and `appFile` are set. `pagesDir` takes precedence — `appFile` will be ignored.");
|
|
2488
2495
|
let isBuild = false;
|
|
2489
2496
|
const prachtPlugin = {
|
|
@@ -2519,6 +2526,7 @@ function pracht(options = {}) {
|
|
|
2519
2526
|
root = config.root;
|
|
2520
2527
|
isBuild = config.command === "build";
|
|
2521
2528
|
routeFileDirs = computeRouteFileDirs(root, resolved);
|
|
2529
|
+
capabilityModulePaths = new Set(resolveCapabilityModulePaths(resolved, root).map(canonicalFilePath));
|
|
2522
2530
|
},
|
|
2523
2531
|
resolveId(id, importer, resolveIdOptions) {
|
|
2524
2532
|
if (isIslandsClientModule(id)) return PRACHT_ISLANDS_CLIENT_MODULE_ID;
|
|
@@ -2543,8 +2551,8 @@ function pracht(options = {}) {
|
|
|
2543
2551
|
return null;
|
|
2544
2552
|
},
|
|
2545
2553
|
transform(code, id) {
|
|
2546
|
-
const appFileAbs = resolveConfigPath(root, resolved.appFile);
|
|
2547
|
-
if (
|
|
2554
|
+
const appFileAbs = canonicalFilePath(resolveConfigPath(root, resolved.appFile));
|
|
2555
|
+
if (canonicalFilePath(id.split("?")[0]) !== appFileAbs) return null;
|
|
2548
2556
|
const transformed = rewriteManifestCoreImports(code.replace(/\(\)\s*=>\s*import\(\s*(['"])([^'"]+)\1\s*\)/g, "$1$2$1"));
|
|
2549
2557
|
if (transformed === code) return null;
|
|
2550
2558
|
return {
|
|
@@ -2580,6 +2588,7 @@ function pracht(options = {}) {
|
|
|
2580
2588
|
if (isPagesMode && relative.startsWith(resolved.pagesDir)) {
|
|
2581
2589
|
clearPagesAppSourceCache();
|
|
2582
2590
|
invalidateVirtualModules(server);
|
|
2591
|
+
sendServerOnlyFullReload(server, file);
|
|
2583
2592
|
return;
|
|
2584
2593
|
}
|
|
2585
2594
|
if (!isPagesMode && relative === resolved.appFile) {
|
|
@@ -2617,12 +2626,14 @@ function pracht(options = {}) {
|
|
|
2617
2626
|
if (capabilityMod) server.moduleGraph.invalidateModule(capabilityMod);
|
|
2618
2627
|
}
|
|
2619
2628
|
}
|
|
2629
|
+
sendServerOnlyFullReload(server, file);
|
|
2620
2630
|
}
|
|
2621
2631
|
};
|
|
2622
2632
|
const clientModuleTransformPlugin = {
|
|
2623
2633
|
name: "pracht:client-module-transform",
|
|
2624
2634
|
enforce: "post",
|
|
2625
2635
|
transform(code, id, transformOptions) {
|
|
2636
|
+
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.`);
|
|
2626
2637
|
if (!(isPrachtClientModuleId(id) || !transformOptions?.ssr && isRouteOrShellFile(id, routeFileDirs))) return null;
|
|
2627
2638
|
const transformed = stripServerOnlyExportsForClient(code, id);
|
|
2628
2639
|
if (transformed === code) return null;
|
|
@@ -2752,7 +2763,34 @@ const ROUTE_FILE_EXTENSIONS = new Set([
|
|
|
2752
2763
|
".tsrx"
|
|
2753
2764
|
]);
|
|
2754
2765
|
function computeRouteFileDirs(root, resolved) {
|
|
2755
|
-
return (resolved.pagesDir ? [resolved.pagesDir] : [resolved.routesDir, resolved.shellsDir]).map((dir) => resolveConfigPath(root, dir)).map(withTrailingSep);
|
|
2766
|
+
return (resolved.pagesDir ? [resolved.pagesDir] : [resolved.routesDir, resolved.shellsDir]).map((dir) => canonicalFilePath(resolveConfigPath(root, dir))).map(withTrailingSep);
|
|
2767
|
+
}
|
|
2768
|
+
/**
|
|
2769
|
+
* Whether `id` is one of the capability modules the manifest registers.
|
|
2770
|
+
* Matching the registered set rather than a directory keeps ordinary files that
|
|
2771
|
+
* merely live beside capabilities importable, and still catches a capability
|
|
2772
|
+
* registered from anywhere else in the project. Extension-agnostic, because the
|
|
2773
|
+
* comparison is against paths the manifest already resolved.
|
|
2774
|
+
*/
|
|
2775
|
+
function isCapabilityModule(id, capabilityModulePaths) {
|
|
2776
|
+
if (capabilityModulePaths.size === 0) return false;
|
|
2777
|
+
const queryStart = id.indexOf("?");
|
|
2778
|
+
const path = queryStart === -1 ? id : id.slice(0, queryStart);
|
|
2779
|
+
if (path.startsWith("\0") || path.startsWith("virtual:")) return false;
|
|
2780
|
+
return capabilityModulePaths.has(canonicalFilePath(path));
|
|
2781
|
+
}
|
|
2782
|
+
/**
|
|
2783
|
+
* Match Vite's canonical module ids even when the manifest path crosses a
|
|
2784
|
+
* symlink (including macOS' /var -> /private/var alias). Missing paths keep
|
|
2785
|
+
* their lexical identity so the projection code can raise its precise missing
|
|
2786
|
+
* capability error later.
|
|
2787
|
+
*/
|
|
2788
|
+
function canonicalFilePath(path) {
|
|
2789
|
+
try {
|
|
2790
|
+
return toPosixPath(realpathSync.native(path));
|
|
2791
|
+
} catch {
|
|
2792
|
+
return toPosixPath(path);
|
|
2793
|
+
}
|
|
2756
2794
|
}
|
|
2757
2795
|
function isRouteOrShellFile(id, dirs) {
|
|
2758
2796
|
if (dirs.length === 0) return false;
|
package/dist/pages-router.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as sortRoutes, i as scanPagesDirectory, n as generatePagesManifestSource, r as generateRoutesFile, t as filePathToRoutePath } from "./pages-router-
|
|
1
|
+
import { a as sortRoutes, i as scanPagesDirectory, n as generatePagesManifestSource, r as generateRoutesFile, t as filePathToRoutePath } from "./pages-router-CStJY2Zp.mjs";
|
|
2
2
|
export { filePathToRoutePath, generatePagesManifestSource, generateRoutesFile, scanPagesDirectory, sortRoutes };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pracht/vite-plugin",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.4",
|
|
4
4
|
"description": "Vite plugin for Pracht apps with virtual modules, dev SSR, prerendering, route inspection, and multi-adapter builds.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pracht",
|
|
@@ -44,10 +44,10 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@preact/preset-vite": "^2.10.5",
|
|
46
46
|
"@prefresh/vite": "^2.0.0",
|
|
47
|
-
"@pracht/
|
|
48
|
-
"@pracht/
|
|
49
|
-
"@pracht/
|
|
50
|
-
"@pracht/
|
|
47
|
+
"@pracht/core": "0.11.3",
|
|
48
|
+
"@pracht/adapter-node": "0.3.6",
|
|
49
|
+
"@pracht/preact-ssr-precompile": "0.1.2",
|
|
50
|
+
"@pracht/capabilities": "0.1.1"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"vite": "^8.0.0"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { basename, extname, join, relative } from "node:path";
|
|
2
1
|
import { readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { basename, extname, join, relative } from "node:path";
|
|
3
3
|
//#region src/route-loader-hints.ts
|
|
4
4
|
const ROUTE_EXTENSIONS = new Set([
|
|
5
5
|
".tsx",
|