pagegraph 0.11.0 → 0.13.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 +403 -148
- package/dist/audit.d.ts +2 -2
- package/dist/audit.js +4 -4
- package/dist/build-only/audit.d.ts +1 -0
- package/dist/build-only/audit.js +6 -0
- package/dist/build-only/audit.js.map +1 -0
- package/dist/build-only/claims.d.ts +1 -0
- package/dist/build-only/claims.js +6 -0
- package/dist/build-only/claims.js.map +1 -0
- package/dist/build-only/config.d.ts +1 -0
- package/dist/build-only/config.js +6 -0
- package/dist/build-only/config.js.map +1 -0
- package/dist/build-only/oxlint.d.ts +1 -0
- package/dist/build-only/oxlint.js +6 -0
- package/dist/build-only/oxlint.js.map +1 -0
- package/dist/build-only/tanstack-start.d.ts +1 -0
- package/dist/build-only/tanstack-start.js +6 -0
- package/dist/build-only/tanstack-start.js.map +1 -0
- package/dist/build-only/vite.d.ts +1 -0
- package/dist/build-only/vite.js +6 -0
- package/dist/build-only/vite.js.map +1 -0
- package/dist/{checks-nk8ZRRIA.js → checks-BS-aDXB_.js} +4 -142
- package/dist/checks-BS-aDXB_.js.map +1 -0
- package/dist/claims-Bch7Lxlo.d.ts +92 -0
- package/dist/claims-Bv7voeEV.js +431 -0
- package/dist/claims-Bv7voeEV.js.map +1 -0
- package/dist/claims.d.ts +94 -0
- package/dist/claims.js +2 -0
- package/dist/cli.js +10368 -8485
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +4 -83
- package/dist/config.js +3 -122
- package/dist/config.js.map +1 -1
- package/dist/{content-CB_kHmJ3.js → content-CHd0X4s6.js} +3 -2
- package/dist/{content-CB_kHmJ3.js.map → content-CHd0X4s6.js.map} +1 -1
- package/dist/{content-DVL2iyGU.d.ts → content-DgNr7apS.d.ts} +5 -130
- package/dist/declare-D5o7hwmv.js +9 -0
- package/dist/declare-D5o7hwmv.js.map +1 -0
- package/dist/document-CztEu3Vd.d.ts +109 -0
- package/dist/document-Dvemcv7V.js +379 -0
- package/dist/document-Dvemcv7V.js.map +1 -0
- package/dist/facts-1OKclPOk.js +115 -0
- package/dist/facts-1OKclPOk.js.map +1 -0
- package/dist/graph-D1PIndsC.d.ts +12238 -0
- package/dist/graph-DMNOLXLu.js +172 -0
- package/dist/graph-DMNOLXLu.js.map +1 -0
- package/dist/index-C5w9ripz.d.ts +204 -0
- package/dist/index.d.ts +6 -196
- package/dist/index.js +55 -134
- package/dist/index.js.map +1 -1
- package/dist/inline-BbS93GAT.js +682 -0
- package/dist/inline-BbS93GAT.js.map +1 -0
- package/dist/llms-Dhr_5u2O.js +19 -0
- package/dist/llms-Dhr_5u2O.js.map +1 -0
- package/dist/markdown-C6dRE9zD.js +168 -0
- package/dist/markdown-C6dRE9zD.js.map +1 -0
- package/dist/markdown-path-lJB9BAVZ.js +6 -0
- package/dist/markdown-path-lJB9BAVZ.js.map +1 -0
- package/dist/message-CuBjgZ74.js +12 -0
- package/dist/message-CuBjgZ74.js.map +1 -0
- package/dist/oxlint.d.ts +58 -0
- package/dist/oxlint.js +202 -0
- package/dist/oxlint.js.map +1 -0
- package/dist/page-heads-BfjWQvau.d.ts +108 -0
- package/dist/page-heads-BgbaIjj9.js +25 -0
- package/dist/page-heads-BgbaIjj9.js.map +1 -0
- package/dist/projections-Bk-qOOsR.d.ts +78 -0
- package/dist/projections-D0oOFKiY.js +142 -0
- package/dist/projections-D0oOFKiY.js.map +1 -0
- package/dist/react.d.ts +27 -12051
- package/dist/react.js +14 -4
- package/dist/react.js.map +1 -1
- package/dist/route-config-35ZEzkNC.js +308 -0
- package/dist/route-config-35ZEzkNC.js.map +1 -0
- package/dist/route-config-wneg6ktn.d.ts +29 -0
- package/dist/tanstack-start/index.d.ts +56 -0
- package/dist/tanstack-start/index.js +717 -0
- package/dist/tanstack-start/index.js.map +1 -0
- package/dist/tanstack-start/markdown.d.ts +12 -0
- package/dist/tanstack-start/markdown.js +149 -0
- package/dist/tanstack-start/markdown.js.map +1 -0
- package/dist/tanstack-start/prerender-worker.d.ts +2 -0
- package/dist/tanstack-start/prerender-worker.js +7 -0
- package/dist/tanstack-start/prerender-worker.js.map +1 -0
- package/dist/tanstack-start/react.d.ts +7 -0
- package/dist/tanstack-start/react.js +12 -0
- package/dist/tanstack-start/react.js.map +1 -0
- package/dist/tanstack-start/server.d.ts +20 -0
- package/dist/tanstack-start/server.js +77 -0
- package/dist/tanstack-start/server.js.map +1 -0
- package/dist/vite-graph-loader-CsGiYNWE.js +105 -0
- package/dist/vite-graph-loader-CsGiYNWE.js.map +1 -0
- package/dist/vite-graph-loader-DZCwejuJ.d.ts +133 -0
- package/dist/vite.d.ts +2 -28
- package/dist/vite.js +1 -307
- package/dist/wire-B4RQMc4e.js +26 -0
- package/dist/wire-B4RQMc4e.js.map +1 -0
- package/package.json +70 -7
- package/dist/checks-nk8ZRRIA.js.map +0 -1
- package/dist/vite.js.map +0 -1
|
@@ -0,0 +1,717 @@
|
|
|
1
|
+
import { t as buildSeoGraph } from "../graph-DMNOLXLu.js";
|
|
2
|
+
import { n as graphToJson } from "../wire-B4RQMc4e.js";
|
|
3
|
+
import { a as globToRegExp } from "../checks-BS-aDXB_.js";
|
|
4
|
+
import { o as hashDocument } from "../inline-BbS93GAT.js";
|
|
5
|
+
import { t as decodeFacts } from "../facts-1OKclPOk.js";
|
|
6
|
+
import { n as documentMarkdown } from "../markdown-C6dRE9zD.js";
|
|
7
|
+
import { i as seoRouteConfig, n as inMemoryWriteFs, r as resolveOptions, t as deriveRouteConfig } from "../route-config-35ZEzkNC.js";
|
|
8
|
+
import { n as withCleanStdout } from "../vite-graph-loader-CsGiYNWE.js";
|
|
9
|
+
import { r as replayClaims } from "../claims-Bv7voeEV.js";
|
|
10
|
+
import { t as MARKDOWN_CAPTURE_PATH } from "../markdown-path-lJB9BAVZ.js";
|
|
11
|
+
import { existsSync } from "node:fs";
|
|
12
|
+
import { mkdir, readFile, readdir, unlink, writeFile } from "node:fs/promises";
|
|
13
|
+
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
14
|
+
import { Generator, getConfig } from "@tanstack/router-generator";
|
|
15
|
+
import { createRunnableDevEnvironment, parseAst, resolveConfig } from "vite";
|
|
16
|
+
import * as Effect from "effect/Effect";
|
|
17
|
+
import * as Schema from "effect/Schema";
|
|
18
|
+
import { pathToFileURL } from "node:url";
|
|
19
|
+
//#region src/tanstack-start/routes.ts
|
|
20
|
+
/**
|
|
21
|
+
* Route discovery for the graph: the router's own generator parses the routes
|
|
22
|
+
* directory (the same parse `routeTree.gen.ts` comes from), into memory only.
|
|
23
|
+
*/
|
|
24
|
+
async function scanRoutes(root, routesDirectory) {
|
|
25
|
+
const cacheDirectory = resolve(root, "node_modules/.cache/pagegraph");
|
|
26
|
+
let scanned;
|
|
27
|
+
const config = getConfig({
|
|
28
|
+
routesDirectory: resolve(root, routesDirectory),
|
|
29
|
+
generatedRouteTree: resolve(cacheDirectory, "route-tree.gen.ts"),
|
|
30
|
+
tmpDir: resolve(cacheDirectory, "tmp"),
|
|
31
|
+
disableTypes: true,
|
|
32
|
+
enableRouteTreeFormatting: false,
|
|
33
|
+
disableLogging: true,
|
|
34
|
+
plugins: [{
|
|
35
|
+
name: "pagegraph:scan",
|
|
36
|
+
onRouteTreeChanged({ routeTree, routeNodes, rootRouteNode }) {
|
|
37
|
+
scanned = {
|
|
38
|
+
root: rootRouteNode,
|
|
39
|
+
tree: routeTree,
|
|
40
|
+
nodes: routeNodes
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}]
|
|
44
|
+
}, root);
|
|
45
|
+
const memory = inMemoryWriteFs(config.generatedRouteTree);
|
|
46
|
+
await new Generator({
|
|
47
|
+
config,
|
|
48
|
+
root,
|
|
49
|
+
fs: memory.fs
|
|
50
|
+
}).run();
|
|
51
|
+
if (scanned === void 0) throw new Error(`pagegraph found no route tree in ${resolve(root, routesDirectory)}`);
|
|
52
|
+
return scanned;
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/tanstack-start/graph.ts
|
|
56
|
+
/**
|
|
57
|
+
* Build the graph from the app's own modules, evaluated in the `pagegraph`
|
|
58
|
+
* Vite environment: one module per route file that declares `staticData`, plus
|
|
59
|
+
* the app's collections module. Never the route tree, which reaches every
|
|
60
|
+
* route — including app routes that only resolve inside the server runtime.
|
|
61
|
+
*/
|
|
62
|
+
const GRAPH_ROUTE_TYPES = /* @__PURE__ */ new Set([
|
|
63
|
+
"__root",
|
|
64
|
+
"static",
|
|
65
|
+
"layout",
|
|
66
|
+
"pathless_layout"
|
|
67
|
+
]);
|
|
68
|
+
const messageOf = (cause) => cause instanceof Error ? cause.message : String(cause);
|
|
69
|
+
const routeStaticData = (module, file) => {
|
|
70
|
+
const route = module["Route"];
|
|
71
|
+
if (route?.options === void 0) throw new Error(`${file} has no \`Route\` export`);
|
|
72
|
+
const data = route.options.staticData;
|
|
73
|
+
return data === void 0 ? void 0 : {
|
|
74
|
+
seo: data.seo,
|
|
75
|
+
markdown: data.markdown,
|
|
76
|
+
llms: data.llms
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
const loadCollections = async (runner, root, fullPath) => {
|
|
80
|
+
const path = relative(root, fullPath);
|
|
81
|
+
const exported = (await runner.import(fullPath).catch((cause) => {
|
|
82
|
+
throw new Error(`pagegraph could not evaluate ${path}: ${messageOf(cause)}`, { cause });
|
|
83
|
+
}))["collections"];
|
|
84
|
+
if (exported === void 0) throw new Error(`${path} has no \`collections\` export`);
|
|
85
|
+
const collections = typeof exported === "function" ? await exported() : exported;
|
|
86
|
+
if (!Array.isArray(collections)) throw new Error(`${path} \`collections\` must be an array or return one`);
|
|
87
|
+
return collections;
|
|
88
|
+
};
|
|
89
|
+
/** Scan the routes directory and pick the modules an evaluation loads. */
|
|
90
|
+
async function planGraph(root, options) {
|
|
91
|
+
const scan = await scanRoutes(root, options.routesDirectory ?? "src/routes");
|
|
92
|
+
const excluded = (options.exclude ?? []).map(globToRegExp);
|
|
93
|
+
const participates = (node) => GRAPH_ROUTE_TYPES.has(node._fsRouteType) && (node._fsRouteType === "__root" || node.createFileRouteProps?.has("staticData") === true) && !excluded.some((pattern) => pattern.test(node.filePath));
|
|
94
|
+
return {
|
|
95
|
+
root,
|
|
96
|
+
options,
|
|
97
|
+
scan,
|
|
98
|
+
routeFiles: [scan.root, ...scan.nodes].filter(participates).map((node) => node.fullPath),
|
|
99
|
+
collectionsFile: options.collections === void 0 ? void 0 : resolve(root, options.collections),
|
|
100
|
+
factsFile: options.facts === void 0 ? void 0 : resolve(root, options.facts)
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* A route node's path relative to its parent, as the router matches it: route
|
|
105
|
+
* groups, pathless layouts, and trailing underscores contribute no segment.
|
|
106
|
+
*/
|
|
107
|
+
const publicSegment = (node) => node.isNonPath === true || node.cleanedPath === void 0 || node.cleanedPath === "" ? void 0 : node.cleanedPath;
|
|
108
|
+
/** Evaluate a plan's modules through `runner`, then build the graph and the site runtime. */
|
|
109
|
+
async function evaluateGraph(runner, plan) {
|
|
110
|
+
const { root, options, scan } = plan;
|
|
111
|
+
const staticData = new Map(await Promise.all(plan.routeFiles.map(async (fullPath) => {
|
|
112
|
+
const file = relative(root, fullPath);
|
|
113
|
+
const routeFile = relative(resolve(root, options.routesDirectory ?? "src/routes"), fullPath);
|
|
114
|
+
const module = await runner.import(fullPath).catch((cause) => {
|
|
115
|
+
throw new Error(`pagegraph could not evaluate ${file} in the graph environment: ${messageOf(cause)}\nIf it is an app surface rather than a public page, add "${routeFile}" to pagegraph({ exclude }).`, { cause });
|
|
116
|
+
});
|
|
117
|
+
return [fullPath, routeStaticData(module, file)];
|
|
118
|
+
})));
|
|
119
|
+
const walkable = (node) => ({
|
|
120
|
+
options: {
|
|
121
|
+
path: publicSegment(node),
|
|
122
|
+
staticData: staticData.get(node.fullPath)
|
|
123
|
+
},
|
|
124
|
+
children: (node.children ?? []).map(walkable)
|
|
125
|
+
});
|
|
126
|
+
const routeTree = {
|
|
127
|
+
options: { staticData: staticData.get(scan.root.fullPath) },
|
|
128
|
+
children: scan.tree.map(walkable)
|
|
129
|
+
};
|
|
130
|
+
const collections = plan.collectionsFile === void 0 ? [] : await loadCollections(runner, root, plan.collectionsFile);
|
|
131
|
+
const graph = buildSeoGraph({
|
|
132
|
+
routeTree,
|
|
133
|
+
collections
|
|
134
|
+
});
|
|
135
|
+
const exclusions = options.routeConfig === void 0 ? [] : deriveRouteConfig(scan.nodes, resolveOptions({
|
|
136
|
+
...options.routeConfig,
|
|
137
|
+
routesDirectory: options.routesDirectory
|
|
138
|
+
})).robotsExclusions;
|
|
139
|
+
const site = {
|
|
140
|
+
origin: options.origin,
|
|
141
|
+
indexable: options.indexable ?? true,
|
|
142
|
+
robots: {
|
|
143
|
+
disallow: [.../* @__PURE__ */ new Set([...exclusions, ...options.robots?.disallow ?? []])].sort(),
|
|
144
|
+
contentSignal: options.robots?.contentSignal,
|
|
145
|
+
directives: options.robots?.directives
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
let facts;
|
|
149
|
+
if (plan.factsFile !== void 0) {
|
|
150
|
+
const path = relative(root, plan.factsFile);
|
|
151
|
+
try {
|
|
152
|
+
const module = await runner.import(plan.factsFile);
|
|
153
|
+
if (module["facts"] === void 0) throw new Error(`${path} has no \`facts\` export`);
|
|
154
|
+
facts = decodeFacts(module["facts"]);
|
|
155
|
+
} catch (cause) {
|
|
156
|
+
throw new Error(`pagegraph could not evaluate ${path}: ${messageOf(cause)}`, { cause });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
graph,
|
|
161
|
+
site,
|
|
162
|
+
markdown: options.markdown === void 0 ? null : { origin: options.markdown.origin },
|
|
163
|
+
facts
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region src/markdown/schema.ts
|
|
168
|
+
const InlineNode = Schema.suspend(() => Schema.Union([
|
|
169
|
+
Schema.String,
|
|
170
|
+
Schema.Struct({
|
|
171
|
+
fact: Schema.String,
|
|
172
|
+
text: Schema.String
|
|
173
|
+
}),
|
|
174
|
+
Schema.Struct({
|
|
175
|
+
tag: Schema.Literals([
|
|
176
|
+
"em",
|
|
177
|
+
"strong",
|
|
178
|
+
"a",
|
|
179
|
+
"code",
|
|
180
|
+
"br"
|
|
181
|
+
]),
|
|
182
|
+
href: Schema.optionalKey(Schema.String),
|
|
183
|
+
children: Schema.Array(InlineNode)
|
|
184
|
+
})
|
|
185
|
+
]));
|
|
186
|
+
const Message = Schema.Struct({
|
|
187
|
+
hash: Schema.String,
|
|
188
|
+
tree: Schema.Array(InlineNode),
|
|
189
|
+
text: Schema.String,
|
|
190
|
+
source: Schema.String,
|
|
191
|
+
facts: Schema.Array(Schema.String),
|
|
192
|
+
audience: Schema.Literals([
|
|
193
|
+
"all",
|
|
194
|
+
"agents",
|
|
195
|
+
"humans"
|
|
196
|
+
])
|
|
197
|
+
});
|
|
198
|
+
const BodyEntry = Schema.Union([Schema.Struct({
|
|
199
|
+
type: Schema.Literal("message"),
|
|
200
|
+
message: Message
|
|
201
|
+
}), Schema.Struct({
|
|
202
|
+
type: Schema.Literal("visual"),
|
|
203
|
+
text: Schema.String,
|
|
204
|
+
source: Schema.String,
|
|
205
|
+
audience: Schema.Literals([
|
|
206
|
+
"all",
|
|
207
|
+
"agents",
|
|
208
|
+
"humans"
|
|
209
|
+
])
|
|
210
|
+
})]);
|
|
211
|
+
const PageDocument = Schema.Struct({
|
|
212
|
+
path: Schema.String,
|
|
213
|
+
hash: Schema.String,
|
|
214
|
+
title: Schema.String,
|
|
215
|
+
description: Schema.String,
|
|
216
|
+
sections: Schema.Array(Schema.Struct({
|
|
217
|
+
claimsMarkdown: Schema.optionalKey(Schema.String),
|
|
218
|
+
evidence: Schema.optionalKey(Schema.Boolean),
|
|
219
|
+
kind: Schema.String,
|
|
220
|
+
id: Schema.String,
|
|
221
|
+
title: Schema.optionalKey(Message),
|
|
222
|
+
language: Schema.optionalKey(Schema.String),
|
|
223
|
+
body: Schema.Array(BodyEntry),
|
|
224
|
+
items: Schema.Array(Schema.Struct({
|
|
225
|
+
title: Schema.optionalKey(Message),
|
|
226
|
+
href: Schema.optionalKey(Schema.String),
|
|
227
|
+
body: Schema.Array(BodyEntry),
|
|
228
|
+
cells: Schema.Array(Message),
|
|
229
|
+
source: Schema.String,
|
|
230
|
+
audience: Schema.Literals([
|
|
231
|
+
"all",
|
|
232
|
+
"agents",
|
|
233
|
+
"humans"
|
|
234
|
+
])
|
|
235
|
+
})),
|
|
236
|
+
source: Schema.String,
|
|
237
|
+
audience: Schema.Literals([
|
|
238
|
+
"all",
|
|
239
|
+
"agents",
|
|
240
|
+
"humans"
|
|
241
|
+
])
|
|
242
|
+
})),
|
|
243
|
+
messages: Schema.Array(Message),
|
|
244
|
+
facts: Schema.Array(Schema.String)
|
|
245
|
+
});
|
|
246
|
+
const CaptureBundle = Schema.Struct({
|
|
247
|
+
origin: Schema.String,
|
|
248
|
+
documents: Schema.Array(PageDocument),
|
|
249
|
+
heads: Schema.Array(Schema.Struct({
|
|
250
|
+
path: Schema.String,
|
|
251
|
+
title: Schema.String,
|
|
252
|
+
description: Schema.String
|
|
253
|
+
}))
|
|
254
|
+
});
|
|
255
|
+
//#endregion
|
|
256
|
+
//#region src/tanstack-start/persist-markdown.ts
|
|
257
|
+
/** File names must remain inside the package's capture and the client's output trees. */
|
|
258
|
+
function documentName(path) {
|
|
259
|
+
if (!path.startsWith("/") || path.startsWith("//") || /[?#\\$\u0000-\u001f\u007f]/.test(path) || path !== "/" && path.endsWith("/") || path.slice(1).split("/").some((part) => part === "." || part === ".." || part === "*" || part === "" && path !== "/")) throw new Error(`Invalid captured document path "${path}"`);
|
|
260
|
+
return path === "/" ? "index" : path.slice(1);
|
|
261
|
+
}
|
|
262
|
+
function verifyDocument(document) {
|
|
263
|
+
const name = documentName(document.path);
|
|
264
|
+
const { hash, ...content } = document;
|
|
265
|
+
if (!/^[0-9a-f]{64}$/.test(hash) || hashDocument(content) !== hash) throw new Error(`Invalid captured document hash for "${document.path}"`);
|
|
266
|
+
return name;
|
|
267
|
+
}
|
|
268
|
+
/** Only valid captures at their derived paths belong to pagegraph; preserve unrelated files. */
|
|
269
|
+
async function pruneDocuments(directory, paths) {
|
|
270
|
+
const visit = async (folder) => {
|
|
271
|
+
const entries = await readdir(folder, { withFileTypes: true });
|
|
272
|
+
for (const entry of entries) {
|
|
273
|
+
const file = resolve(folder, entry.name);
|
|
274
|
+
if (entry.isDirectory()) {
|
|
275
|
+
await visit(file);
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
279
|
+
const contents = await readFile(file, "utf8");
|
|
280
|
+
let document;
|
|
281
|
+
let name;
|
|
282
|
+
try {
|
|
283
|
+
document = Schema.decodeUnknownSync(Schema.fromJsonString(PageDocument))(contents, { onExcessProperty: "error" });
|
|
284
|
+
name = verifyDocument(document);
|
|
285
|
+
} catch {
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
if (file === resolve(directory, `${name}.json`) && !paths.has(document.path)) await unlink(file);
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
await visit(directory);
|
|
292
|
+
}
|
|
293
|
+
/** Documents, twins, and graph heads are persisted before prerender onSuccess returns. */
|
|
294
|
+
async function persistMarkdownCapture(html, root, clientOutDir, claims, facts = {}) {
|
|
295
|
+
const bundle = Schema.decodeUnknownSync(Schema.fromJsonString(CaptureBundle))(html, { onExcessProperty: "error" });
|
|
296
|
+
const origin = new URL(bundle.origin);
|
|
297
|
+
if (origin.protocol !== "https:" && origin.protocol !== "http:" || origin.origin !== bundle.origin) throw new Error(`Invalid captured markdown origin "${bundle.origin}"`);
|
|
298
|
+
const active = /* @__PURE__ */ new Set();
|
|
299
|
+
const destinations = /* @__PURE__ */ new Set();
|
|
300
|
+
const names = bundle.documents.map((document) => {
|
|
301
|
+
if (active.has(document.path)) throw new Error(`Duplicate captured document path "${document.path}"`);
|
|
302
|
+
active.add(document.path);
|
|
303
|
+
const name = verifyDocument(document);
|
|
304
|
+
if (destinations.has(name)) throw new Error(`Captured document paths share the output name "${name}"`);
|
|
305
|
+
destinations.add(name);
|
|
306
|
+
return name;
|
|
307
|
+
});
|
|
308
|
+
await unlink(resolve(clientOutDir, MARKDOWN_CAPTURE_PATH.slice(1)));
|
|
309
|
+
const directory = resolve(root, ".pagegraph/documents");
|
|
310
|
+
await mkdir(directory, { recursive: true });
|
|
311
|
+
for (const [index, document] of bundle.documents.entries()) {
|
|
312
|
+
const name = names[index];
|
|
313
|
+
const destination = resolve(directory, `${name}.json`);
|
|
314
|
+
const twin = resolve(clientOutDir, `${name}.md`);
|
|
315
|
+
await mkdir(dirname(destination), { recursive: true });
|
|
316
|
+
await writeFile(destination, JSON.stringify(document));
|
|
317
|
+
await mkdir(dirname(twin), { recursive: true });
|
|
318
|
+
await writeFile(twin, documentMarkdown(document, bundle.origin));
|
|
319
|
+
}
|
|
320
|
+
await pruneDocuments(directory, active);
|
|
321
|
+
await writeFile(resolve(root, ".pagegraph/heads.json"), `${JSON.stringify(bundle.heads)}\n`);
|
|
322
|
+
if (claims !== void 0) await Effect.runPromise(replayClaims(root, bundle.documents, bundle.heads, facts, claims));
|
|
323
|
+
}
|
|
324
|
+
//#endregion
|
|
325
|
+
//#region src/tanstack-start/source.ts
|
|
326
|
+
var DocumentSourceError = class extends Schema.TaggedError()("DocumentSourceError", { message: Schema.String }) {};
|
|
327
|
+
const SOURCE_COMPONENTS = /* @__PURE__ */ new Set([
|
|
328
|
+
"T",
|
|
329
|
+
"Title",
|
|
330
|
+
"Section",
|
|
331
|
+
"Visual",
|
|
332
|
+
"ForAgents",
|
|
333
|
+
"ForHumans"
|
|
334
|
+
]);
|
|
335
|
+
const parse = Effect.fn("DocumentSource.parse")(function* (code, id) {
|
|
336
|
+
return yield* Effect.try({
|
|
337
|
+
try: () => parseAst(code, { lang: id.endsWith(".tsx") ? "tsx" : id.endsWith(".ts") ? "ts" : id.endsWith(".jsx") ? "jsx" : "js" }, id),
|
|
338
|
+
catch: (cause) => new DocumentSourceError({ message: `Cannot parse document source ${id}: ${cause instanceof Error ? cause.message : String(cause)}` })
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
function componentNames(program) {
|
|
342
|
+
const named = /* @__PURE__ */ new Set();
|
|
343
|
+
const sections = /* @__PURE__ */ new Set();
|
|
344
|
+
const namespaces = /* @__PURE__ */ new Set();
|
|
345
|
+
for (const node of program.body) {
|
|
346
|
+
if (node.type !== "ImportDeclaration" || node.source.value !== "pagegraph/react" || node.importKind === "type") continue;
|
|
347
|
+
for (const specifier of node.specifiers) if (specifier.type === "ImportSpecifier") {
|
|
348
|
+
if (specifier.importKind === "type") continue;
|
|
349
|
+
const imported = specifier.imported.type === "Identifier" ? specifier.imported.name : specifier.imported.value;
|
|
350
|
+
if (SOURCE_COMPONENTS.has(imported)) named.add(specifier.local.name);
|
|
351
|
+
if (imported === "Section") sections.add(specifier.local.name);
|
|
352
|
+
} else if (specifier.type === "ImportNamespaceSpecifier") namespaces.add(specifier.local.name);
|
|
353
|
+
}
|
|
354
|
+
return {
|
|
355
|
+
named,
|
|
356
|
+
sections,
|
|
357
|
+
namespaces
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
function memberParts(name) {
|
|
361
|
+
if (name.type === "JSXIdentifier") return [name.name];
|
|
362
|
+
if (name.type !== "JSXMemberExpression") return [];
|
|
363
|
+
return [...memberParts(name.object), name.property.name];
|
|
364
|
+
}
|
|
365
|
+
function isSourceComponent(name, names) {
|
|
366
|
+
const [base, ...members] = memberParts(name);
|
|
367
|
+
if (base === void 0) return false;
|
|
368
|
+
if (members.length === 0) return names.named.has(base);
|
|
369
|
+
if (names.namespaces.has(base)) return members.length === 1 && members[0] !== void 0 && SOURCE_COMPONENTS.has(members[0]) || members.length === 2 && members[0] === "Section" && members[1] === "Item";
|
|
370
|
+
return names.sections.has(base) && members.length === 1 && members[0] === "Item";
|
|
371
|
+
}
|
|
372
|
+
function walk(value, visit) {
|
|
373
|
+
if (Array.isArray(value)) for (const child of value) walk(child, visit);
|
|
374
|
+
else if (typeof value === "object" && value !== null) {
|
|
375
|
+
if (isOpeningElement(value)) visit(value);
|
|
376
|
+
for (const child of Object.values(value)) walk(child, visit);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
function isOpeningElement(node) {
|
|
380
|
+
return "type" in node && node.type === "JSXOpeningElement";
|
|
381
|
+
}
|
|
382
|
+
function transformDocumentSource(code, id, root) {
|
|
383
|
+
const program = Effect.runSync(parse(code, id));
|
|
384
|
+
const names = componentNames(program);
|
|
385
|
+
const edits = [];
|
|
386
|
+
const file = relative(resolve(root), resolve(id)).split(sep).join("/");
|
|
387
|
+
walk(program, (node) => {
|
|
388
|
+
if (!isSourceComponent(node.name, names)) return;
|
|
389
|
+
if (node.attributes.some((attribute) => attribute.type === "JSXAttribute" && attribute.name.type === "JSXIdentifier" && attribute.name.name === "source")) return;
|
|
390
|
+
const line = code.slice(0, node.start).split("\n").length;
|
|
391
|
+
edits.push({
|
|
392
|
+
start: node.name.end,
|
|
393
|
+
end: node.name.end,
|
|
394
|
+
value: ` source=${JSON.stringify(`${file}:${line}`)}`
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
if (edits.length === 0) return void 0;
|
|
398
|
+
edits.sort((a, b) => b.start - a.start);
|
|
399
|
+
let transformed = code;
|
|
400
|
+
for (const edit of edits) transformed = transformed.slice(0, edit.start) + edit.value + transformed.slice(edit.end);
|
|
401
|
+
return { code: transformed };
|
|
402
|
+
}
|
|
403
|
+
//#endregion
|
|
404
|
+
//#region src/tanstack-start/plugin.ts
|
|
405
|
+
/**
|
|
406
|
+
* The `pagegraph()` Vite plugin for TanStack Start.
|
|
407
|
+
*
|
|
408
|
+
* The graph is built once per build (and on change in dev) inside the app's own
|
|
409
|
+
* Vite pipeline, then shipped to the server runtime as `virtual:pagegraph/runtime`.
|
|
410
|
+
* The Worker never rebuilds it and never imports build code.
|
|
411
|
+
*
|
|
412
|
+
* Route modules and content collections are evaluated in a dedicated Node
|
|
413
|
+
* environment, `pagegraph`, that shares the app's plugins, aliases, and
|
|
414
|
+
* `define`s. Two settings make that safe:
|
|
415
|
+
* - its `resolve.noExternal` is cleared, so TanStack Start's packages load from
|
|
416
|
+
* node_modules, where their virtual router entry is an inert stub. Bundled (as
|
|
417
|
+
* the app's server bundles them), they would pull in the whole route tree.
|
|
418
|
+
* - only route files that declare `staticData` are evaluated, one by one, never
|
|
419
|
+
* `routeTree.gen.ts`.
|
|
420
|
+
*
|
|
421
|
+
* Dev imports those modules through the environment's module runner and
|
|
422
|
+
* re-evaluates after a change reaches it. A build builds the environment first
|
|
423
|
+
* (a `buildApp` hook that runs before the framework's) with the build's own
|
|
424
|
+
* config and plugins, then imports the output. The CLI and `loadPageHeads` use
|
|
425
|
+
* {@link evaluateAppGraph}, which runs the dev path without a server.
|
|
426
|
+
*/
|
|
427
|
+
const GRAPH_ENVIRONMENT$1 = "pagegraph";
|
|
428
|
+
const PLUGIN_NAME = "pagegraph";
|
|
429
|
+
const RESOLVED_RUNTIME_ID = `\0virtual:pagegraph/runtime`;
|
|
430
|
+
const ENTRY_ID = "virtual:pagegraph/entry";
|
|
431
|
+
const RESOLVED_ENTRY_ID = `\0${ENTRY_ID}`;
|
|
432
|
+
/** The runtime entry imports the virtual module, so dep pre-bundling must leave it to this plugin. */
|
|
433
|
+
const RUNTIME_ENTRIES = [
|
|
434
|
+
"pagegraph/tanstack-start/server",
|
|
435
|
+
"pagegraph/tanstack-start/markdown",
|
|
436
|
+
"pagegraph/tanstack-start/react"
|
|
437
|
+
];
|
|
438
|
+
/** Where a build writes the graph environment's output, relative to the Vite root. */
|
|
439
|
+
const BUILD_DIRECTORY = "node_modules/.cache/pagegraph/build";
|
|
440
|
+
const runtimeModulesPlugin = {
|
|
441
|
+
name: "pagegraph:runtime-modules",
|
|
442
|
+
enforce: "post",
|
|
443
|
+
applyToEnvironment: (environment) => environment.name === GRAPH_ENVIRONMENT$1,
|
|
444
|
+
resolveId: {
|
|
445
|
+
filter: { id: /^(?!node:|data:|file:|virtual:)[a-z][a-z0-9+.-]*:/ },
|
|
446
|
+
handler: (id) => ({
|
|
447
|
+
id,
|
|
448
|
+
external: true
|
|
449
|
+
})
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
const runtimeModule = ({ graph, site, markdown, facts }) => `export const graph = ${JSON.stringify(graphToJson(graph))};\nexport const site = ${JSON.stringify(site)};\nexport const markdown = ${JSON.stringify(markdown)};\nexport const facts = ${JSON.stringify(facts) ?? "undefined"};\n`;
|
|
453
|
+
/** The graph environment's build entry: a loader per module the plan evaluates. */
|
|
454
|
+
const entryModule = (plan) => {
|
|
455
|
+
return `export const modules = {\n${[
|
|
456
|
+
...plan.routeFiles,
|
|
457
|
+
...plan.collectionsFile === void 0 ? [] : [plan.collectionsFile],
|
|
458
|
+
...plan.factsFile === void 0 ? [] : [plan.factsFile]
|
|
459
|
+
].map((file) => ` ${JSON.stringify(file)}: () => import(${JSON.stringify(file)}),`).join("\n")}\n};\n`;
|
|
460
|
+
};
|
|
461
|
+
/** Build the graph environment with the build's config, import its output, and evaluate. */
|
|
462
|
+
const buildGraph = async (builder, environment, plan) => {
|
|
463
|
+
await builder.build(environment);
|
|
464
|
+
const entry = join(resolve(environment.config.root, environment.config.build.outDir), "entry.mjs");
|
|
465
|
+
const { modules } = await import(pathToFileURL(entry).href);
|
|
466
|
+
return evaluateGraph({ import: (file) => {
|
|
467
|
+
const load = modules[file];
|
|
468
|
+
if (load === void 0) throw new Error(`${file} is not in the graph build`);
|
|
469
|
+
return load();
|
|
470
|
+
} }, plan);
|
|
471
|
+
};
|
|
472
|
+
function pagegraph(options) {
|
|
473
|
+
if (options.claims !== void 0 && options.markdown === void 0) throw new Error("Claims require pagegraph({ markdown: { origin }, claims: { rules, model } }) so the build captures documents.");
|
|
474
|
+
const api = { options };
|
|
475
|
+
let root = process.cwd();
|
|
476
|
+
let clientOutDir = resolve(root, "dist/client");
|
|
477
|
+
let server;
|
|
478
|
+
let devEnvironment;
|
|
479
|
+
/** The plan the graph environment's build entry is generated from. */
|
|
480
|
+
let buildPlan;
|
|
481
|
+
let current;
|
|
482
|
+
/** Bumped by every change that reaches the graph; an evaluation that spans one is discarded. */
|
|
483
|
+
let generation = 0;
|
|
484
|
+
const evaluateInDev = async (environment) => {
|
|
485
|
+
for (;;) {
|
|
486
|
+
const started = generation;
|
|
487
|
+
environment.runner.clearCache();
|
|
488
|
+
const result = await evaluateGraph(environment.runner, await planGraph(root, options));
|
|
489
|
+
if (started === generation) return result;
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
return Object.assign([
|
|
493
|
+
{
|
|
494
|
+
name: PLUGIN_NAME,
|
|
495
|
+
api,
|
|
496
|
+
sharedDuringBuild: true,
|
|
497
|
+
config() {
|
|
498
|
+
return { environments: { [GRAPH_ENVIRONMENT$1]: {
|
|
499
|
+
consumer: "server",
|
|
500
|
+
dev: { createEnvironment: (name, environmentConfig) => createRunnableDevEnvironment(name, environmentConfig) },
|
|
501
|
+
build: {
|
|
502
|
+
outDir: BUILD_DIRECTORY,
|
|
503
|
+
emptyOutDir: true,
|
|
504
|
+
copyPublicDir: false,
|
|
505
|
+
minify: false,
|
|
506
|
+
rolldownOptions: {
|
|
507
|
+
input: { entry: ENTRY_ID },
|
|
508
|
+
output: {
|
|
509
|
+
entryFileNames: "[name].mjs",
|
|
510
|
+
chunkFileNames: "chunks/[name]-[hash].mjs"
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
} } };
|
|
515
|
+
},
|
|
516
|
+
configEnvironment(name, environmentConfig) {
|
|
517
|
+
if (name === "pagegraph") {
|
|
518
|
+
environmentConfig.resolve ??= {};
|
|
519
|
+
environmentConfig.resolve.noExternal = [];
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
environmentConfig.optimizeDeps ??= {};
|
|
523
|
+
environmentConfig.optimizeDeps.exclude = [...environmentConfig.optimizeDeps.exclude ?? [], ...RUNTIME_ENTRIES];
|
|
524
|
+
},
|
|
525
|
+
configResolved(resolved) {
|
|
526
|
+
root = resolved.root;
|
|
527
|
+
clientOutDir = resolve(root, resolved.environments.client?.build.outDir ?? "dist/client");
|
|
528
|
+
},
|
|
529
|
+
configureServer: {
|
|
530
|
+
order: "pre",
|
|
531
|
+
handler(devServer) {
|
|
532
|
+
server = devServer;
|
|
533
|
+
devEnvironment = devServer.environments[GRAPH_ENVIRONMENT$1];
|
|
534
|
+
}
|
|
535
|
+
},
|
|
536
|
+
hotUpdate({ file, modules }) {
|
|
537
|
+
if (this.environment.name !== "pagegraph") return;
|
|
538
|
+
const routesDirectory = resolve(root, options.routesDirectory ?? "src/routes");
|
|
539
|
+
if (modules.length === 0 && !file.startsWith(`${routesDirectory}/`)) return;
|
|
540
|
+
generation += 1;
|
|
541
|
+
current = void 0;
|
|
542
|
+
for (const other of Object.values(server?.environments ?? {})) {
|
|
543
|
+
if (other.name === "pagegraph") continue;
|
|
544
|
+
const runtime = other.moduleGraph.getModuleById(RESOLVED_RUNTIME_ID);
|
|
545
|
+
if (runtime !== void 0) other.reloadModule(runtime);
|
|
546
|
+
}
|
|
547
|
+
},
|
|
548
|
+
buildApp: {
|
|
549
|
+
order: "pre",
|
|
550
|
+
async handler(builder) {
|
|
551
|
+
const environment = builder.environments[GRAPH_ENVIRONMENT$1];
|
|
552
|
+
if (environment === void 0) return;
|
|
553
|
+
buildPlan = await planGraph(environment.config.root, options);
|
|
554
|
+
const built = buildGraph(builder, environment, buildPlan);
|
|
555
|
+
current = built;
|
|
556
|
+
await built;
|
|
557
|
+
environment.isBuilt = false;
|
|
558
|
+
}
|
|
559
|
+
},
|
|
560
|
+
resolveId: {
|
|
561
|
+
filter: { id: /^virtual:pagegraph\/(runtime|entry)$/ },
|
|
562
|
+
handler: (id) => `\0${id}`
|
|
563
|
+
},
|
|
564
|
+
load: {
|
|
565
|
+
filter: { id: /^\0virtual:pagegraph\/(runtime|entry)$/ },
|
|
566
|
+
async handler(id) {
|
|
567
|
+
if (id === RESOLVED_ENTRY_ID) {
|
|
568
|
+
if (buildPlan === void 0) throw new Error(`[${PLUGIN_NAME}] ${ENTRY_ID} is only built by the pagegraph buildApp hook`);
|
|
569
|
+
return entryModule(buildPlan);
|
|
570
|
+
}
|
|
571
|
+
if (this.environment.name === "pagegraph") return "export const graph = null;\nexport const site = null;\nexport const markdown = null;\nexport const facts = undefined;\n";
|
|
572
|
+
if (current === void 0) {
|
|
573
|
+
if (devEnvironment === void 0) throw new Error(`[${PLUGIN_NAME}] the graph was not built before ${this.environment.name} imported it; build with Vite's app builder (\`vite build\`).`);
|
|
574
|
+
current = evaluateInDev(devEnvironment);
|
|
575
|
+
}
|
|
576
|
+
return runtimeModule(await current);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
name: "pagegraph:markdown",
|
|
582
|
+
enforce: "pre",
|
|
583
|
+
applyToEnvironment: (environment) => environment.name !== GRAPH_ENVIRONMENT$1,
|
|
584
|
+
resolveId: {
|
|
585
|
+
filter: { id: /^virtual:pagegraph\/prerender-server$/ },
|
|
586
|
+
handler: (id) => `\0${id}`
|
|
587
|
+
},
|
|
588
|
+
load: {
|
|
589
|
+
filter: { id: /^\0virtual:pagegraph\/prerender-server$/ },
|
|
590
|
+
handler: () => `export { default } from ${JSON.stringify(resolve(root, options.markdown?.serverEntry ?? "dist/server/index.js"))};`
|
|
591
|
+
},
|
|
592
|
+
transform: {
|
|
593
|
+
filter: {
|
|
594
|
+
id: /\.[jt]sx?(\?|$)/,
|
|
595
|
+
code: /pagegraph\/react/
|
|
596
|
+
},
|
|
597
|
+
handler(code, id) {
|
|
598
|
+
const file = id.split("?")[0];
|
|
599
|
+
if (file === void 0 || file.includes("/node_modules/") || file.endsWith(".gen.ts") || !file.startsWith(`${root}/`)) return;
|
|
600
|
+
return transformDocumentSource(code, file, root);
|
|
601
|
+
}
|
|
602
|
+
},
|
|
603
|
+
configurePreviewServer(previewServer) {
|
|
604
|
+
previewServer.middlewares.use((_request, response, next) => {
|
|
605
|
+
response.on("finish", () => {
|
|
606
|
+
const error = response.getHeader("x-pagegraph-error");
|
|
607
|
+
if (typeof error === "string") previewServer.config.logger.error(decodeURIComponent(error));
|
|
608
|
+
});
|
|
609
|
+
next();
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
},
|
|
613
|
+
runtimeModulesPlugin,
|
|
614
|
+
...options.routeConfig === void 0 ? [] : [seoRouteConfig({
|
|
615
|
+
...options.routeConfig,
|
|
616
|
+
routesDirectory: options.routesDirectory
|
|
617
|
+
})]
|
|
618
|
+
], { prerenderPages: options.markdown === void 0 ? [] : [{
|
|
619
|
+
path: MARKDOWN_CAPTURE_PATH,
|
|
620
|
+
sitemap: { exclude: true },
|
|
621
|
+
prerender: { onSuccess: async ({ html }) => {
|
|
622
|
+
if (current === void 0) throw new Error("The pagegraph graph must be evaluated before Markdown persistence.");
|
|
623
|
+
const app = await current;
|
|
624
|
+
await persistMarkdownCapture(html, root, clientOutDir, options.claims, app.facts);
|
|
625
|
+
} }
|
|
626
|
+
}] });
|
|
627
|
+
}
|
|
628
|
+
//#endregion
|
|
629
|
+
//#region src/tanstack-start/load.ts
|
|
630
|
+
/**
|
|
631
|
+
* Evaluate an app's graph outside a running dev server — for a build, the
|
|
632
|
+
* CLI (`pagegraph.config.ts`), and build-time head loading — through the same
|
|
633
|
+
* `pagegraph` environment the dev server uses.
|
|
634
|
+
*/
|
|
635
|
+
const GRAPH_ENVIRONMENT = "pagegraph";
|
|
636
|
+
const cliEnvironmentOverride = (command) => command === "build" ? { [GRAPH_ENVIRONMENT]: { isBundled: false } } : void 0;
|
|
637
|
+
/** Locate the config without overriding its authored root with an inline root. */
|
|
638
|
+
const appConfig = (options) => ({
|
|
639
|
+
configFile: options.configFile ?? [
|
|
640
|
+
"js",
|
|
641
|
+
"mjs",
|
|
642
|
+
"ts",
|
|
643
|
+
"cjs",
|
|
644
|
+
"mts",
|
|
645
|
+
"cts"
|
|
646
|
+
].map((extension) => resolve(options.root, `vite.config.${extension}`)).find(existsSync) ?? false,
|
|
647
|
+
logLevel: "error",
|
|
648
|
+
plugins: [{
|
|
649
|
+
name: "pagegraph:loader-root",
|
|
650
|
+
enforce: "pre",
|
|
651
|
+
config(config) {
|
|
652
|
+
config.root ??= options.root;
|
|
653
|
+
}
|
|
654
|
+
}]
|
|
655
|
+
});
|
|
656
|
+
const pluginApi = (plugins, configFile) => {
|
|
657
|
+
const plugin = plugins.find((candidate) => candidate.name === "pagegraph");
|
|
658
|
+
if (plugin?.api === void 0) throw new Error(`${configFile ?? "The Vite config"} does not register pagegraph() from "pagegraph/tanstack-start".`);
|
|
659
|
+
return plugin.api;
|
|
660
|
+
};
|
|
661
|
+
/**
|
|
662
|
+
* Resolve the app's Vite config, evaluate the graph in its `pagegraph`
|
|
663
|
+
* environment, and release it. The command defaults to `serve`; callers can
|
|
664
|
+
* request `build` to match the deployed site configuration.
|
|
665
|
+
*/
|
|
666
|
+
async function evaluateAppGraph(options) {
|
|
667
|
+
const command = options.command ?? "serve";
|
|
668
|
+
const config = await resolveConfig({
|
|
669
|
+
...appConfig(options),
|
|
670
|
+
mode: options.mode,
|
|
671
|
+
environments: cliEnvironmentOverride(command)
|
|
672
|
+
}, command);
|
|
673
|
+
const api = pluginApi(config.plugins, config.configFile);
|
|
674
|
+
const environment = createRunnableDevEnvironment(GRAPH_ENVIRONMENT, config, { hot: false });
|
|
675
|
+
await environment.init();
|
|
676
|
+
try {
|
|
677
|
+
await environment.pluginContainer.buildStart({});
|
|
678
|
+
const app = await evaluateGraph(environment.runner, await planGraph(config.root, api.options));
|
|
679
|
+
return {
|
|
680
|
+
...app,
|
|
681
|
+
pagegraph: {
|
|
682
|
+
root: config.root,
|
|
683
|
+
options: api.options,
|
|
684
|
+
facts: app.facts ?? {}
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
} finally {
|
|
688
|
+
await environment.close();
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* A `pagegraph.config.ts` graph loader for a TanStack Start app. It uses the
|
|
693
|
+
* built site's identity by default; pass `command: "serve"` for a dev host.
|
|
694
|
+
*
|
|
695
|
+
* ```ts
|
|
696
|
+
* export default defineSeoConfig({
|
|
697
|
+
* loadGraph: tanstackStartGraph({ root: import.meta.dirname }),
|
|
698
|
+
* });
|
|
699
|
+
* ```
|
|
700
|
+
*/
|
|
701
|
+
const tanstackStartGraph = (options) => async (_input, environment) => {
|
|
702
|
+
const { graph, site, pagegraph } = await withCleanStdout(() => evaluateAppGraph({
|
|
703
|
+
...options,
|
|
704
|
+
command: environment?.command ?? options.command ?? "build",
|
|
705
|
+
mode: environment?.mode ?? options.mode ?? "production"
|
|
706
|
+
}));
|
|
707
|
+
return {
|
|
708
|
+
graph,
|
|
709
|
+
site,
|
|
710
|
+
pagegraph,
|
|
711
|
+
dispose: async () => {}
|
|
712
|
+
};
|
|
713
|
+
};
|
|
714
|
+
//#endregion
|
|
715
|
+
export { evaluateAppGraph, pagegraph, tanstackStartGraph };
|
|
716
|
+
|
|
717
|
+
//# sourceMappingURL=index.js.map
|