@plebeiantech/lat.md 0.12.3-fork.14 → 0.12.3-fork.16
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/src/cli/checklist-menu.d.ts +2 -1
- package/dist/src/cli/checklist-menu.js +3 -2
- package/dist/src/cli/index.js +47 -10
- package/dist/src/cli/init-preferences.d.ts +4 -0
- package/dist/src/cli/init-preferences.js +35 -0
- package/dist/src/cli/init.js +7 -1
- package/dist/src/cli/search.js +2 -7
- package/dist/src/cli/ui-build-server.d.ts +12 -0
- package/dist/src/cli/ui-build-server.js +26 -0
- package/dist/src/cli/ui-build.js +1 -1
- package/dist/src/cli/ui.js +1 -1
- package/dist/src/search/query.d.ts +5 -2
- package/dist/src/search/query.js +25 -21
- package/dist/src/source-formats.d.ts +1 -1
- package/dist/src/source-formats.js +2 -0
- package/dist/src/source-parser.js +200 -0
- package/dist/src/view/client/assets/GraphView-BuZg_wrj.js +456 -0
- package/dist/src/view/client/assets/{MarkdownEditor-B7quqPb8.js → MarkdownEditor-BKqJtZYv.js} +8 -8
- package/dist/src/view/client/assets/_commonjsHelpers-CqkleIqs.js +1 -0
- package/dist/src/view/client/assets/abnfDiagram-VCTEODGH-CAu3KwfO.js +1 -0
- package/dist/src/view/client/assets/{arc-BnwR8Bu7.js → arc-CFnPZhKM.js} +1 -1
- package/dist/src/view/client/assets/architectureDiagram-5GKGNRK7-Cf9Kd0OA.js +36 -0
- package/dist/src/view/client/assets/{blockDiagram-I7D4REHJ-DETl8e_H.js → blockDiagram-I7D4REHJ-dPn7g_vm.js} +2 -2
- package/dist/src/view/client/assets/{c4Diagram-7LVT6UL2-DJRAT1Ve.js → c4Diagram-7LVT6UL2-DdZrwaPd.js} +2 -2
- package/dist/src/view/client/assets/channel-C-byJRR7.js +1 -0
- package/dist/src/view/client/assets/{chunk-2Q5K7J3B-C0TmOC1Q.js → chunk-2Q5K7J3B-C42JuteH.js} +1 -1
- package/dist/src/view/client/assets/{chunk-5VM5RSS4-dfBOrYE6.js → chunk-5VM5RSS4-BKK3z8Zy.js} +1 -1
- package/dist/src/view/client/assets/{chunk-F27PBJKO-DAnks99K.js → chunk-F27PBJKO-qZl_uKcW.js} +1 -1
- package/dist/src/view/client/assets/{chunk-IMKFNOWR-CPjB07if.js → chunk-IMKFNOWR-Bs4Rnoj_.js} +1 -1
- package/dist/src/view/client/assets/{chunk-JWPE2WC7-VvU_gZI6.js → chunk-JWPE2WC7-BpxgV2rA.js} +1 -1
- package/dist/src/view/client/assets/{chunk-POPQ4Y6H-DDUbVF55.js → chunk-POPQ4Y6H-hIynofdE.js} +1 -1
- package/dist/src/view/client/assets/{chunk-SVP7TREG-BHROkr6D.js → chunk-SVP7TREG-t3gKMqeu.js} +1 -1
- package/dist/src/view/client/assets/{chunk-TICWLB2K-95PzGIOg.js → chunk-TICWLB2K-B2x394mh.js} +1 -1
- package/dist/src/view/client/assets/{chunk-XXDRQBXY-MdmuLhIs.js → chunk-XXDRQBXY-CHaVKDsH.js} +1 -1
- package/dist/src/view/client/assets/classDiagram-ZZMXUADV-FlqGrEwv.js +1 -0
- package/dist/src/view/client/assets/classDiagram-v2-VYDZK3BY-FlqGrEwv.js +1 -0
- package/dist/src/view/client/assets/cose-bilkent-JH36ORCC-DE2-Jgqu.js +1 -0
- package/dist/src/view/client/assets/{cynefin-OW5HDTMX-CeA8IKWo.js → cynefin-OW5HDTMX-BsEtOvRy.js} +1 -1
- package/dist/src/view/client/assets/cynefinDiagram-5FMLGOSQ-BKYG1Xs8.js +62 -0
- package/dist/src/view/client/assets/{dagre-GXQ25YYZ-DLQwTa3H.js → dagre-GXQ25YYZ-hgrdNjl7.js} +2 -2
- package/dist/src/view/client/assets/{diagram-S7CK7UJ4-kbKdIyXG.js → diagram-S7CK7UJ4-VIx8zAxf.js} +2 -2
- package/dist/src/view/client/assets/diagram-UQ7AKVKN-yfloehRm.js +41 -0
- package/dist/src/view/client/assets/{diagram-VSXAHHWV-EXJTU8qq.js → diagram-VSXAHHWV-BT_dA4Q9.js} +2 -2
- package/dist/src/view/client/assets/diagram-VX7I27RA-Gfmwxx6u.js +24 -0
- package/dist/src/view/client/assets/diagram-Z3DM3KII-C2yM7EOo.js +24 -0
- package/dist/src/view/client/assets/ebnfDiagram-PWID7BFC-Bkb_Lj59.js +1 -0
- package/dist/src/view/client/assets/{erDiagram-RLTQ6QDP-C925J5A3.js → erDiagram-RLTQ6QDP-C7wQHPye.js} +2 -2
- package/dist/src/view/client/assets/{flowDiagram-HODETNUW-DzoCa-Nq.js → flowDiagram-HODETNUW-CXoKr3oI.js} +1 -1
- package/dist/src/view/client/assets/ganttDiagram-EL5Y4UJY-DqqXKgLa.js +292 -0
- package/dist/src/view/client/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dist/src/view/client/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dist/src/view/client/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dist/src/view/client/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/src/view/client/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
- package/dist/src/view/client/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
- package/dist/src/view/client/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
- package/dist/src/view/client/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/dist/src/view/client/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
- package/dist/src/view/client/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
- package/dist/src/view/client/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dist/src/view/client/assets/gitGraphDiagram-WWUBYQGX-D_DAhSgZ.js +106 -0
- package/dist/src/view/client/assets/index-DAxFk0q-.css +1 -0
- package/dist/src/view/client/assets/index-DPuOWFj2.js +10 -0
- package/dist/src/view/client/assets/infoDiagram-27XIBGKW-B50EOats.js +2 -0
- package/dist/src/view/client/assets/{ishikawaDiagram-5VMMS53U-CW4mto2d.js → ishikawaDiagram-5VMMS53U-D1rny9gj.js} +2 -2
- package/dist/src/view/client/assets/{journeyDiagram-3NMN7TZE-8R4eacFW.js → journeyDiagram-3NMN7TZE-B5UD2Xo4.js} +2 -2
- package/dist/src/view/client/assets/{kanban-definition-UXKFOSKX-Cy9ioXEJ.js → kanban-definition-UXKFOSKX-CTgey9zw.js} +6 -6
- package/dist/src/view/client/assets/{layout-C0MFuhth.js → layout-4X2n4os-.js} +1 -1
- package/dist/src/view/client/assets/{linear-Dv3N4dQR.js → linear-D0RIBXsp.js} +1 -1
- package/dist/src/view/client/assets/{maplibre-gl-DnqZgfUS.js → maplibre-gl-CDX6Zuow.js} +1 -1
- package/dist/src/view/client/assets/{mermaid.core-bds9A7e2.js → mermaid.core-BAnH8y-D.js} +11 -11
- package/dist/src/view/client/assets/{mindmap-definition-YA3MSWOX-DjmGW355.js → mindmap-definition-YA3MSWOX-WiFsh3Jg.js} +2 -2
- package/dist/src/view/client/assets/pegDiagram-XKGWAZYB-DCJOrckP.js +1 -0
- package/dist/src/view/client/assets/{pieDiagram-E7YTZNPT-CCH2gGR-.js → pieDiagram-E7YTZNPT-C78IkUE2.js} +2 -2
- package/dist/src/view/client/assets/{quadrantDiagram-AXDQQJYC-CjocPJFz.js → quadrantDiagram-AXDQQJYC-xZ6guleB.js} +4 -4
- package/dist/src/view/client/assets/railroadDiagram-O6MQD6OU-D3PwmgX0.js +1 -0
- package/dist/src/view/client/assets/{requirementDiagram-BXWQKSXE-Ct6AoXRr.js → requirementDiagram-BXWQKSXE-v-I8wLGH.js} +2 -2
- package/dist/src/view/client/assets/{sankeyDiagram-P5KCCOFB-CIpgGN_w.js → sankeyDiagram-P5KCCOFB-DBY-sdO7.js} +2 -2
- package/dist/src/view/client/assets/{sequenceDiagram-WJ2MYXX4-DrmLFj6w.js → sequenceDiagram-WJ2MYXX4-D9HApBFi.js} +2 -2
- package/dist/src/view/client/assets/sizeCapture-INFHLROL-ERAytLnQ.js +1 -0
- package/dist/src/view/client/assets/stateDiagram-D77RDMKH-BKef_Qwv.js +1 -0
- package/dist/src/view/client/assets/stateDiagram-v2-MP3YSRHH-BePfayWI.js +1 -0
- package/dist/src/view/client/assets/swimlanes-42K2YHIH-D7dS_tnF.js +1 -0
- package/dist/src/view/client/assets/swimlanesDiagram-VR7AAH4N-DaqCEguT.js +8 -0
- package/dist/src/view/client/assets/{timeline-definition-24CTP7MA-BExPu6X5.js → timeline-definition-24CTP7MA-Di1UCseG.js} +2 -2
- package/dist/src/view/client/assets/{vennDiagram-4TSXK5OY-PC1MU3si.js → vennDiagram-4TSXK5OY-g7Je3H2H.js} +2 -2
- package/dist/src/view/client/assets/{wardleyDiagram-VM6X3IG4-DNWqU3fa.js → wardleyDiagram-VM6X3IG4-C_diPEka.js} +2 -2
- package/dist/src/view/client/assets/{xychartDiagram-S5SC5T6Z-E1uA28_D.js → xychartDiagram-S5SC5T6Z-C8uIQy_I.js} +2 -2
- package/dist/src/view/client/index.html +2 -2
- package/dist/src/view/document-route.d.ts +2 -0
- package/dist/src/view/document-route.js +40 -7
- package/dist/src/view/graph.d.ts +2 -2
- package/dist/src/view/graph.js +7 -3
- package/dist/src/view/highlight.js +205 -203
- package/dist/src/view/markdown.js +4 -0
- package/dist/src/view/preindexed-search.d.ts +15 -0
- package/dist/src/view/preindexed-search.js +25 -0
- package/dist/src/view/protocol.d.ts +3 -0
- package/dist/src/view/references.js +3 -4
- package/dist/src/view/search-result.d.ts +4 -0
- package/dist/src/view/search-result.js +20 -0
- package/dist/src/view/search.d.ts +7 -2
- package/dist/src/view/search.js +23 -30
- package/dist/src/view/server-build.d.ts +21 -0
- package/dist/src/view/server-build.js +145 -0
- package/dist/src/view/server-deployment.d.ts +32 -0
- package/dist/src/view/server-deployment.js +133 -0
- package/dist/src/view/server-index-worker.d.ts +6 -0
- package/dist/src/view/server-index-worker.js +14 -0
- package/dist/src/view/server.d.ts +1 -1
- package/dist/src/view/server.js +26 -8
- package/dist/src/view/static-build.d.ts +16 -1
- package/dist/src/view/static-build.js +187 -89
- package/dist/src/view/static-protocol.d.ts +7 -0
- package/dist/src/view/static-protocol.js +5 -0
- package/dist/src/view/store.js +14 -4
- package/dist/src/view/vercel-build.d.ts +24 -0
- package/dist/src/view/vercel-build.js +133 -0
- package/dist/src/view/vercel-server-build.d.ts +10 -0
- package/dist/src/view/vercel-server-build.js +61 -0
- package/dist/vendor/lat.md-server/cli.d.ts +2 -0
- package/dist/vendor/lat.md-server/cli.js +12 -0
- package/dist/vendor/lat.md-server/index.d.ts +38 -0
- package/dist/vendor/lat.md-server/index.js +128 -0
- package/package.json +19 -5
- package/dist/src/view/client/assets/abnfDiagram-VCTEODGH-V7-T6qsl.js +0 -1
- package/dist/src/view/client/assets/architectureDiagram-5GKGNRK7-u97vDna9.js +0 -36
- package/dist/src/view/client/assets/channel-BwQbnWCN.js +0 -1
- package/dist/src/view/client/assets/classDiagram-ZZMXUADV-HvQKkIS-.js +0 -1
- package/dist/src/view/client/assets/classDiagram-v2-VYDZK3BY-HvQKkIS-.js +0 -1
- package/dist/src/view/client/assets/cose-bilkent-JH36ORCC-CauUf_rN.js +0 -1
- package/dist/src/view/client/assets/cynefinDiagram-5FMLGOSQ-L1rtcT8l.js +0 -62
- package/dist/src/view/client/assets/diagram-UQ7AKVKN-kmzp92RC.js +0 -41
- package/dist/src/view/client/assets/diagram-VX7I27RA-DAiIyMCG.js +0 -24
- package/dist/src/view/client/assets/diagram-Z3DM3KII-G9wz50ZV.js +0 -24
- package/dist/src/view/client/assets/ebnfDiagram-PWID7BFC-D2tVRCNA.js +0 -1
- package/dist/src/view/client/assets/ganttDiagram-EL5Y4UJY-D2ebbg4p.js +0 -292
- package/dist/src/view/client/assets/gitGraphDiagram-WWUBYQGX-B4QratYy.js +0 -106
- package/dist/src/view/client/assets/index-D0ajr_Ja.css +0 -1
- package/dist/src/view/client/assets/index-KZcnpXJH.js +0 -321
- package/dist/src/view/client/assets/infoDiagram-27XIBGKW-DL4mtHkt.js +0 -2
- package/dist/src/view/client/assets/pegDiagram-XKGWAZYB-BWsZp0ab.js +0 -1
- package/dist/src/view/client/assets/railroadDiagram-O6MQD6OU-BjLeb2KF.js +0 -1
- package/dist/src/view/client/assets/sizeCapture-INFHLROL-BdFMe2Lr.js +0 -1
- package/dist/src/view/client/assets/stateDiagram-D77RDMKH-Zd9F1goW.js +0 -1
- package/dist/src/view/client/assets/stateDiagram-v2-MP3YSRHH-ByIKCftl.js +0 -1
- package/dist/src/view/client/assets/swimlanes-42K2YHIH-a7VFwi00.js +0 -1
- package/dist/src/view/client/assets/swimlanesDiagram-VR7AAH4N-CzxaUHB-.js +0 -8
- /package/dist/src/view/client/assets/{logo-small-RR_KBOAT.svg → logo-RR_KBOAT.svg} +0 -0
package/dist/src/view/server.js
CHANGED
|
@@ -2,7 +2,7 @@ import { readFile } from 'node:fs/promises';
|
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
3
|
import { extname, join, relative, resolve, sep } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import { createLatServerApp, listenLatServer, } from '
|
|
5
|
+
import { createLatServerApp, listenLatServer, } from '../../vendor/lat.md-server/index.js';
|
|
6
6
|
import { plainStyler } from '../context.js';
|
|
7
7
|
import { sectionCommand } from '../cli/section.js';
|
|
8
8
|
import { DEFAULT_VIEW_LOGO_TEXT, } from './protocol.js';
|
|
@@ -11,7 +11,7 @@ import { ViewExternalNotFoundError, ViewDocumentNotFoundError, ViewSourceNotFoun
|
|
|
11
11
|
import { createViewSearch } from './search.js';
|
|
12
12
|
import { createViewStore } from './store.js';
|
|
13
13
|
import { rewriteClientAssetUrls } from './client-shell.js';
|
|
14
|
-
import { documentResourcePath,
|
|
14
|
+
import { documentResourcePath, documentPath, rawDocumentPath, validateDocumentRoutes, } from './document-route.js';
|
|
15
15
|
const DEFAULT_HOST = '127.0.0.1';
|
|
16
16
|
export const DEFAULT_VIEW_PORT = 4242;
|
|
17
17
|
const MAX_DOCUMENT_EDIT_BODY_BYTES = 16 * 1024 * 1024;
|
|
@@ -98,9 +98,13 @@ async function sendClientFile(res, path, headOnly, immutable = false) {
|
|
|
98
98
|
send(res, 404, 'text/plain; charset=utf-8', 'Not found', headOnly);
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
|
-
async function sendClientShell(res, path, headOnly) {
|
|
101
|
+
async function sendClientShell(res, path, headOnly, entry) {
|
|
102
102
|
try {
|
|
103
|
-
const
|
|
103
|
+
const html = rewriteClientAssetUrls(await readFile(path, 'utf8'), '/');
|
|
104
|
+
const config = `<meta name="lat-live-entry" content="${encodeURIComponent(entry)}">`;
|
|
105
|
+
const body = html.includes('</head>')
|
|
106
|
+
? html.replace('</head>', `${config}</head>`)
|
|
107
|
+
: `${config}${html}`;
|
|
104
108
|
res.setHeader('Cache-Control', 'no-cache');
|
|
105
109
|
send(res, 200, 'text/html; charset=utf-8', body, headOnly);
|
|
106
110
|
}
|
|
@@ -121,6 +125,13 @@ export async function createViewApp(ctx, options = {}) {
|
|
|
121
125
|
watch: options.watch,
|
|
122
126
|
externalCa: options.externalCa,
|
|
123
127
|
});
|
|
128
|
+
try {
|
|
129
|
+
validateDocumentRoutes(store.getIndex().files);
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
await store.close();
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
124
135
|
const search = options.search ??
|
|
125
136
|
createViewSearch(ctx.latDir, undefined, () => store.markdownGeneration);
|
|
126
137
|
const eventClients = new Set();
|
|
@@ -153,8 +164,15 @@ export async function createViewApp(ctx, options = {}) {
|
|
|
153
164
|
send(res, 404, 'text/plain; charset=utf-8', 'No Markdown files found');
|
|
154
165
|
return;
|
|
155
166
|
}
|
|
156
|
-
res.
|
|
157
|
-
|
|
167
|
+
await sendClientShell(res, join(clientDir, 'index.html'), headOnly, entry);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const index = store.getIndex();
|
|
171
|
+
const routePath = url.pathname.replace(/\/$/, '');
|
|
172
|
+
const markdownPath = documentPath(routePath);
|
|
173
|
+
if (markdownPath === index.entry) {
|
|
174
|
+
res.statusCode = 308;
|
|
175
|
+
res.setHeader('Location', `/${url.search}`);
|
|
158
176
|
res.end();
|
|
159
177
|
return;
|
|
160
178
|
}
|
|
@@ -351,10 +369,10 @@ export async function createViewApp(ctx, options = {}) {
|
|
|
351
369
|
}
|
|
352
370
|
if (url.pathname === '/search' ||
|
|
353
371
|
url.pathname === '/graph' ||
|
|
354
|
-
|
|
372
|
+
(markdownPath !== null && index.files.includes(markdownPath)) ||
|
|
355
373
|
url.pathname.startsWith('/code/') ||
|
|
356
374
|
url.pathname.startsWith('/external/')) {
|
|
357
|
-
await sendClientShell(res, join(clientDir, 'index.html'), headOnly);
|
|
375
|
+
await sendClientShell(res, join(clientDir, 'index.html'), headOnly, index.entry);
|
|
358
376
|
return;
|
|
359
377
|
}
|
|
360
378
|
send(res, 404, 'text/plain; charset=utf-8', 'Not found', headOnly);
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import { rename } from 'node:fs/promises';
|
|
1
2
|
import type { CmdContext } from '../context.js';
|
|
2
3
|
export type StaticViewBuildOptions = {
|
|
3
4
|
basePath?: string;
|
|
4
5
|
clientDir?: string;
|
|
6
|
+
codeExcludePaths?: string[];
|
|
7
|
+
force?: boolean;
|
|
5
8
|
logoText?: string;
|
|
6
9
|
externalCa?: string | Buffer;
|
|
10
|
+
searchApi?: string;
|
|
7
11
|
};
|
|
8
12
|
export type StaticViewBuildResult = {
|
|
9
13
|
documents: number;
|
|
@@ -12,6 +16,17 @@ export type StaticViewBuildResult = {
|
|
|
12
16
|
};
|
|
13
17
|
export declare function normalizeStaticViewBasePath(value: string): string;
|
|
14
18
|
/** Convert a live view route to its physical static-directory URL. */
|
|
15
|
-
export declare function staticViewUrl(value: string, basePath: string): string;
|
|
19
|
+
export declare function staticViewUrl(value: string, basePath: string, entryPath?: string): string;
|
|
20
|
+
/** Refuse build targets that exist or could replace the project itself. */
|
|
21
|
+
export declare function validateViewBuildOutput(outputDir: string, projectRoot: string, kind?: string, force?: boolean): Promise<void>;
|
|
22
|
+
type ViewBuildMoveDependencies = {
|
|
23
|
+
move: typeof rename;
|
|
24
|
+
wait: (milliseconds: number) => Promise<void>;
|
|
25
|
+
};
|
|
26
|
+
/** Commit a staged build after transient filesystem locks have cleared. */
|
|
27
|
+
export declare function moveViewBuildOutput(stagingDir: string, outputDir: string, dependencies?: ViewBuildMoveDependencies): Promise<void>;
|
|
28
|
+
/** Remove abandoned staging after transient filesystem locks have cleared. */
|
|
29
|
+
export declare function removeViewBuildStaging(stagingDir: string): Promise<void>;
|
|
16
30
|
/** Build a serverless snapshot of the current read-only Lat UI. */
|
|
17
31
|
export declare function buildStaticView(ctx: CmdContext, requestedOutput: string, options?: StaticViewBuildOptions): Promise<StaticViewBuildResult>;
|
|
32
|
+
export {};
|
|
@@ -5,21 +5,24 @@ import { fileURLToPath } from 'node:url';
|
|
|
5
5
|
import { isDocumentPath } from '../document-formats.js';
|
|
6
6
|
import { DEFAULT_VIEW_LOGO_TEXT } from './protocol.js';
|
|
7
7
|
import { documentTreeUrls, rewriteDocumentTreeUrls } from './document-tree.js';
|
|
8
|
-
import { viewStaticSourceKey, } from './static-protocol.js';
|
|
8
|
+
import { VIEW_STATIC_BOOTSTRAP_ID, viewStaticDocumentRequest, viewStaticSourceKey, } from './static-protocol.js';
|
|
9
9
|
import { createViewStore } from './store.js';
|
|
10
10
|
import { rewriteClientAssetUrls } from './client-shell.js';
|
|
11
|
-
import { documentResourcePath, documentPath, documentUrl, rawDocumentPath, } from './document-route.js';
|
|
11
|
+
import { documentResourcePath, documentPath, documentUrl, rawDocumentPath, validateDocumentRoutes, } from './document-route.js';
|
|
12
12
|
const defaultClientDir = fileURLToPath(new URL('./client/', import.meta.url));
|
|
13
|
+
const VIEW_BUILD_MOVE_MAX_RETRIES = 10;
|
|
14
|
+
const VIEW_BUILD_MOVE_RETRY_DELAY_MS = 150;
|
|
13
15
|
function isInside(parent, child) {
|
|
14
16
|
const rel = relative(parent, child);
|
|
15
17
|
return rel === '' || (rel !== '..' && !rel.startsWith(`..${sep}`));
|
|
16
18
|
}
|
|
17
19
|
export function normalizeStaticViewBasePath(value) {
|
|
18
|
-
const
|
|
20
|
+
const path = value.startsWith('/') ? value : `/${value}`;
|
|
21
|
+
const parsed = new URL(path, 'http://lat.local');
|
|
19
22
|
if (parsed.origin !== 'http://lat.local' ||
|
|
20
23
|
parsed.search ||
|
|
21
24
|
parsed.hash ||
|
|
22
|
-
|
|
25
|
+
value.includes('://')) {
|
|
23
26
|
throw new Error('Static UI base path must be an absolute URL path');
|
|
24
27
|
}
|
|
25
28
|
return parsed.pathname.endsWith('/')
|
|
@@ -47,8 +50,8 @@ function decodeHtmlUrlAttribute(value) {
|
|
|
47
50
|
return value.replace(/&(amp|#38|#x26);/gi, '&');
|
|
48
51
|
}
|
|
49
52
|
/** Convert a live view route to its physical static-directory URL. */
|
|
50
|
-
export function staticViewUrl(value, basePath) {
|
|
51
|
-
if (!value || value.startsWith('
|
|
53
|
+
export function staticViewUrl(value, basePath, entryPath) {
|
|
54
|
+
if (!value || !value.startsWith('/') || value.startsWith('//'))
|
|
52
55
|
return value;
|
|
53
56
|
let url;
|
|
54
57
|
try {
|
|
@@ -59,9 +62,13 @@ export function staticViewUrl(value, basePath) {
|
|
|
59
62
|
}
|
|
60
63
|
if (url.origin !== 'http://lat.local')
|
|
61
64
|
return value;
|
|
62
|
-
|
|
65
|
+
const markdownPath = documentPath(url.pathname.replace(/\/$/, ''));
|
|
66
|
+
if (markdownPath || rawDocumentPath(url.pathname)) {
|
|
63
67
|
const route = url.pathname.slice(1).replace(/\/+$/, '');
|
|
64
68
|
const suffix = `${url.search}${url.hash}`;
|
|
69
|
+
if (entryPath && markdownPath === entryPath) {
|
|
70
|
+
return `${basePath}${suffix}`;
|
|
71
|
+
}
|
|
65
72
|
return `${basePath}${route}${suffix}`;
|
|
66
73
|
}
|
|
67
74
|
if (url.pathname.startsWith('/resources/')) {
|
|
@@ -77,13 +84,16 @@ export function staticViewUrl(value, basePath) {
|
|
|
77
84
|
if (url.pathname === '/graph') {
|
|
78
85
|
return `${basePath}graph/${url.search}${url.hash}`;
|
|
79
86
|
}
|
|
87
|
+
if (url.pathname === '/' || url.pathname === '/search') {
|
|
88
|
+
return `${basePath}${url.pathname.slice(1)}${url.search}${url.hash}`;
|
|
89
|
+
}
|
|
80
90
|
return value;
|
|
81
91
|
}
|
|
82
92
|
function documentPathFromUrl(value) {
|
|
83
93
|
return documentPath(value.pathname) ?? rawDocumentPath(value.pathname);
|
|
84
94
|
}
|
|
85
|
-
function rewriteHtmlLink(value, basePath, sourcePath, documentPaths) {
|
|
86
|
-
const direct = staticViewUrl(value, basePath);
|
|
95
|
+
function rewriteHtmlLink(value, basePath, entryPath, sourcePath, documentPaths) {
|
|
96
|
+
const direct = staticViewUrl(value, basePath, entryPath);
|
|
87
97
|
if (direct !== value || !sourcePath || value.startsWith('#'))
|
|
88
98
|
return direct;
|
|
89
99
|
let resolved;
|
|
@@ -103,64 +113,67 @@ function rewriteHtmlLink(value, basePath, sourcePath, documentPaths) {
|
|
|
103
113
|
}
|
|
104
114
|
const documentPath = documentPathFromUrl(resolved);
|
|
105
115
|
if (resolved.pathname.startsWith('/external/')) {
|
|
106
|
-
return staticViewUrl(`${resolved.pathname}${resolved.search}${resolved.hash}`, basePath);
|
|
116
|
+
return staticViewUrl(`${resolved.pathname}${resolved.search}${resolved.hash}`, basePath, entryPath);
|
|
107
117
|
}
|
|
108
118
|
if (resolved.origin !== 'http://lat.local' ||
|
|
109
119
|
!documentPath ||
|
|
110
120
|
!documentPaths.has(documentPath)) {
|
|
111
121
|
return value;
|
|
112
122
|
}
|
|
113
|
-
return staticViewUrl(`${documentUrl(documentPath)}${resolved.search}${resolved.hash}`, basePath);
|
|
123
|
+
return staticViewUrl(`${documentUrl(documentPath)}${resolved.search}${resolved.hash}`, basePath, entryPath);
|
|
114
124
|
}
|
|
115
|
-
function rewriteDocumentLinks(tree, basePath, sourcePath, documentPaths) {
|
|
116
|
-
return rewriteDocumentTreeUrls(tree, (value) => rewriteHtmlLink(value, basePath, sourcePath, documentPaths));
|
|
125
|
+
function rewriteDocumentLinks(tree, basePath, entryPath, sourcePath, documentPaths) {
|
|
126
|
+
return rewriteDocumentTreeUrls(tree, (value) => rewriteHtmlLink(value, basePath, entryPath, sourcePath, documentPaths));
|
|
117
127
|
}
|
|
118
|
-
function rewriteMarkdownReference(reference, basePath, sourcePath, documentPaths) {
|
|
128
|
+
function rewriteMarkdownReference(reference, basePath, entryPath, sourcePath, documentPaths) {
|
|
119
129
|
return {
|
|
120
130
|
...reference,
|
|
121
|
-
paragraphTree: rewriteDocumentLinks(reference.paragraphTree, basePath, sourcePath, documentPaths),
|
|
122
|
-
url: staticViewUrl(reference.url, basePath),
|
|
131
|
+
paragraphTree: rewriteDocumentLinks(reference.paragraphTree, basePath, entryPath, sourcePath, documentPaths),
|
|
132
|
+
url: staticViewUrl(reference.url, basePath, entryPath),
|
|
123
133
|
};
|
|
124
134
|
}
|
|
125
|
-
function rewriteBackReference(reference, basePath, sectionPaths, documentPaths) {
|
|
135
|
+
function rewriteBackReference(reference, basePath, entryPath, sectionPaths, documentPaths) {
|
|
126
136
|
return reference.kind === 'markdown'
|
|
127
|
-
? rewriteMarkdownReference(reference, basePath, sectionPaths.get(reference.sectionId.split('#', 1)[0]) ?? null, documentPaths)
|
|
128
|
-
: {
|
|
137
|
+
? rewriteMarkdownReference(reference, basePath, entryPath, sectionPaths.get(reference.sectionId.split('#', 1)[0]) ?? null, documentPaths)
|
|
138
|
+
: {
|
|
139
|
+
...reference,
|
|
140
|
+
url: staticViewUrl(reference.url, basePath, entryPath),
|
|
141
|
+
};
|
|
129
142
|
}
|
|
130
|
-
function rewriteDocument(document, basePath, sectionPaths, documentPaths) {
|
|
143
|
+
function rewriteDocument(document, basePath, entryPath, sectionPaths, documentPaths) {
|
|
131
144
|
return {
|
|
132
145
|
...document,
|
|
133
|
-
tree: rewriteDocumentLinks(document.tree, basePath, document.path, documentPaths),
|
|
146
|
+
tree: rewriteDocumentLinks(document.tree, basePath, entryPath, document.path, documentPaths),
|
|
134
147
|
gitTree: null,
|
|
135
148
|
backReferences: document.backReferences.map((section) => ({
|
|
136
149
|
...section,
|
|
137
|
-
references: section.references.map((reference) => rewriteBackReference(reference, basePath, sectionPaths, documentPaths)),
|
|
150
|
+
references: section.references.map((reference) => rewriteBackReference(reference, basePath, entryPath, sectionPaths, documentPaths)),
|
|
138
151
|
})),
|
|
139
152
|
};
|
|
140
153
|
}
|
|
141
|
-
function rewriteSourceReference(reference, basePath, sectionPaths, documentPaths) {
|
|
154
|
+
function rewriteSourceReference(reference, basePath, entryPath, sectionPaths, documentPaths) {
|
|
142
155
|
const sectionPath = reference.sectionId.split('#', 1)[0];
|
|
143
156
|
return {
|
|
144
157
|
...reference,
|
|
145
|
-
paragraphTree: rewriteDocumentLinks(reference.paragraphTree, basePath, sectionPaths.get(sectionPath) ?? null, documentPaths),
|
|
146
|
-
url: staticViewUrl(reference.url, basePath),
|
|
158
|
+
paragraphTree: rewriteDocumentLinks(reference.paragraphTree, basePath, entryPath, sectionPaths.get(sectionPath) ?? null, documentPaths),
|
|
159
|
+
url: staticViewUrl(reference.url, basePath, entryPath),
|
|
147
160
|
};
|
|
148
161
|
}
|
|
149
|
-
function rewriteSource(source, basePath, sectionPaths, documentPaths) {
|
|
162
|
+
function rewriteSource(source, basePath, entryPath, sectionPaths, documentPaths) {
|
|
150
163
|
return {
|
|
151
164
|
...source,
|
|
152
165
|
context: source.context
|
|
153
|
-
? rewriteSourceReference(source.context, basePath, sectionPaths, documentPaths)
|
|
166
|
+
? rewriteSourceReference(source.context, basePath, entryPath, sectionPaths, documentPaths)
|
|
154
167
|
: null,
|
|
155
|
-
otherReferences: source.otherReferences.map((reference) => rewriteSourceReference(reference, basePath, sectionPaths, documentPaths)),
|
|
168
|
+
otherReferences: source.otherReferences.map((reference) => rewriteSourceReference(reference, basePath, entryPath, sectionPaths, documentPaths)),
|
|
156
169
|
};
|
|
157
170
|
}
|
|
158
|
-
function rewriteGraph(graph, basePath) {
|
|
171
|
+
function rewriteGraph(graph, basePath, entryPath) {
|
|
159
172
|
return {
|
|
160
173
|
...graph,
|
|
161
174
|
nodes: graph.nodes.map(({ gitStatus: _gitStatus, ...node }) => ({
|
|
162
175
|
...node,
|
|
163
|
-
url: staticViewUrl(node.url, basePath),
|
|
176
|
+
url: staticViewUrl(node.url, basePath, entryPath),
|
|
164
177
|
})),
|
|
165
178
|
};
|
|
166
179
|
}
|
|
@@ -307,13 +320,26 @@ function sourceRequestsFromSource(source, requests) {
|
|
|
307
320
|
}
|
|
308
321
|
}
|
|
309
322
|
}
|
|
310
|
-
function
|
|
311
|
-
|
|
323
|
+
function serializeJson(value) {
|
|
324
|
+
return `${JSON.stringify(value)}\n`;
|
|
325
|
+
}
|
|
326
|
+
function dataFile(kind, content) {
|
|
327
|
+
const digest = createHash('sha256')
|
|
328
|
+
.update(content)
|
|
329
|
+
.digest('hex')
|
|
330
|
+
.slice(0, 20);
|
|
312
331
|
return `data/${kind}/${digest}.json`;
|
|
313
332
|
}
|
|
314
333
|
async function writeJson(path, value) {
|
|
315
334
|
await mkdir(dirname(path), { recursive: true });
|
|
316
|
-
await writeFile(path,
|
|
335
|
+
await writeFile(path, serializeJson(value));
|
|
336
|
+
}
|
|
337
|
+
async function writeDataJson(outputDir, kind, value) {
|
|
338
|
+
const content = serializeJson(value);
|
|
339
|
+
const path = dataFile(kind, content);
|
|
340
|
+
await mkdir(dirname(join(outputDir, path)), { recursive: true });
|
|
341
|
+
await writeFile(join(outputDir, path), content);
|
|
342
|
+
return path;
|
|
317
343
|
}
|
|
318
344
|
async function outputExists(path) {
|
|
319
345
|
try {
|
|
@@ -326,16 +352,47 @@ async function outputExists(path) {
|
|
|
326
352
|
throw error;
|
|
327
353
|
}
|
|
328
354
|
}
|
|
329
|
-
|
|
355
|
+
/** Refuse build targets that exist or could replace the project itself. */
|
|
356
|
+
export async function validateViewBuildOutput(outputDir, projectRoot, kind = 'Static', force = false) {
|
|
330
357
|
if (outputDir === parse(outputDir).root ||
|
|
331
358
|
outputDir === projectRoot ||
|
|
332
359
|
isInside(outputDir, projectRoot)) {
|
|
333
|
-
throw new Error(
|
|
360
|
+
throw new Error(`${kind} UI output must not contain the project root`);
|
|
334
361
|
}
|
|
335
|
-
if (await outputExists(outputDir)) {
|
|
336
|
-
throw new Error(
|
|
362
|
+
if (!force && (await outputExists(outputDir))) {
|
|
363
|
+
throw new Error(`${kind} UI output already exists: ${outputDir}. Use --force to replace it.`);
|
|
337
364
|
}
|
|
338
365
|
}
|
|
366
|
+
const defaultViewBuildMoveDependencies = {
|
|
367
|
+
move: rename,
|
|
368
|
+
wait: (milliseconds) => new Promise((resolveWait) => setTimeout(resolveWait, milliseconds)),
|
|
369
|
+
};
|
|
370
|
+
/** Commit a staged build after transient filesystem locks have cleared. */
|
|
371
|
+
export async function moveViewBuildOutput(stagingDir, outputDir, dependencies = defaultViewBuildMoveDependencies) {
|
|
372
|
+
for (let retries = 0;; retries += 1) {
|
|
373
|
+
try {
|
|
374
|
+
await dependencies.move(stagingDir, outputDir);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
catch (error) {
|
|
378
|
+
const code = error.code;
|
|
379
|
+
if (retries >= VIEW_BUILD_MOVE_MAX_RETRIES ||
|
|
380
|
+
(code !== 'EBUSY' && code !== 'EPERM' && code !== 'ENOTEMPTY')) {
|
|
381
|
+
throw error;
|
|
382
|
+
}
|
|
383
|
+
await dependencies.wait(VIEW_BUILD_MOVE_RETRY_DELAY_MS);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
/** Remove abandoned staging after transient filesystem locks have cleared. */
|
|
388
|
+
export async function removeViewBuildStaging(stagingDir) {
|
|
389
|
+
await rm(stagingDir, {
|
|
390
|
+
recursive: true,
|
|
391
|
+
force: true,
|
|
392
|
+
maxRetries: VIEW_BUILD_MOVE_MAX_RETRIES,
|
|
393
|
+
retryDelay: VIEW_BUILD_MOVE_RETRY_DELAY_MS,
|
|
394
|
+
});
|
|
395
|
+
}
|
|
339
396
|
function sectionDocumentPaths(documents) {
|
|
340
397
|
const result = new Map();
|
|
341
398
|
for (const path of documents.keys()) {
|
|
@@ -343,25 +400,44 @@ function sectionDocumentPaths(documents) {
|
|
|
343
400
|
}
|
|
344
401
|
return result;
|
|
345
402
|
}
|
|
346
|
-
function clientShell(html, basePath) {
|
|
403
|
+
function clientShell(html, basePath, entry, searchApi) {
|
|
347
404
|
const assets = rewriteClientAssetUrls(html, basePath);
|
|
348
|
-
const configValue = JSON.stringify({ basePath
|
|
349
|
-
const config = `<
|
|
405
|
+
const configValue = encodeURIComponent(JSON.stringify({ basePath, entry, searchApi }));
|
|
406
|
+
const config = `<meta name="lat-static-view" content="${configValue}">`;
|
|
350
407
|
return assets.includes('</head>')
|
|
351
408
|
? assets.replace('</head>', ` ${config}\n </head>`)
|
|
352
409
|
: `${config}\n${assets}`;
|
|
353
410
|
}
|
|
354
|
-
function
|
|
411
|
+
function bootstrapShell(shell, manifest, responses = {}) {
|
|
412
|
+
const bootstrap = { manifest, responses };
|
|
413
|
+
const value = JSON.stringify(bootstrap).replaceAll('<', '\\u003c');
|
|
414
|
+
const data = `<script id="${VIEW_STATIC_BOOTSTRAP_ID}" type="application/json">${value}</script>`;
|
|
415
|
+
return shell.includes('</head>')
|
|
416
|
+
? shell.replace('</head>', ` ${data}\n </head>`)
|
|
417
|
+
: `${data}\n${shell}`;
|
|
418
|
+
}
|
|
419
|
+
const REDIRECT_SCRIPT_PATH = 'data/redirect.js';
|
|
420
|
+
const REDIRECT_SCRIPT = `const meta = document.querySelector('meta[name="lat-redirect"]');
|
|
421
|
+
if (meta) {
|
|
422
|
+
try {
|
|
423
|
+
window.location.replace(decodeURIComponent(meta.content) + window.location.search + window.location.hash);
|
|
424
|
+
} catch {}
|
|
425
|
+
}
|
|
426
|
+
`;
|
|
427
|
+
function redirectShell(target, basePath) {
|
|
355
428
|
const escaped = target.replaceAll('&', '&').replaceAll('"', '"');
|
|
356
|
-
const
|
|
429
|
+
const scriptUrl = `${basePath}${REDIRECT_SCRIPT_PATH}`
|
|
430
|
+
.replaceAll('&', '&')
|
|
431
|
+
.replaceAll('"', '"');
|
|
357
432
|
return `<!doctype html>
|
|
358
433
|
<html lang="en">
|
|
359
434
|
<head>
|
|
360
435
|
<meta charset="UTF-8" />
|
|
361
|
-
<meta
|
|
436
|
+
<meta name="lat-redirect" content="${encodeURIComponent(target)}" />
|
|
362
437
|
<title>lat.md</title>
|
|
438
|
+
<script src="${scriptUrl}" defer></script>
|
|
363
439
|
</head>
|
|
364
|
-
<body><
|
|
440
|
+
<body><a href="${escaped}">Open lat.md</a></body>
|
|
365
441
|
</html>
|
|
366
442
|
`;
|
|
367
443
|
}
|
|
@@ -376,11 +452,11 @@ export async function buildStaticView(ctx, requestedOutput, options = {}) {
|
|
|
376
452
|
const basePath = normalizeStaticViewBasePath(options.basePath ?? '/');
|
|
377
453
|
const clientDir = options.clientDir ?? defaultClientDir;
|
|
378
454
|
const logoText = options.logoText ?? DEFAULT_VIEW_LOGO_TEXT;
|
|
379
|
-
await
|
|
455
|
+
await validateViewBuildOutput(outputDir, ctx.projectRoot, 'Static', options.force);
|
|
380
456
|
await mkdir(dirname(outputDir), { recursive: true });
|
|
381
|
-
const stagingDir = await mkdtemp(join(dirname(outputDir), '.lat-
|
|
457
|
+
const stagingDir = await mkdtemp(join(dirname(outputDir), '.lat-static-staging-'));
|
|
382
458
|
const store = await createViewStore(ctx.latDir, ctx.projectRoot, {
|
|
383
|
-
codeExcludePaths: [outputDir],
|
|
459
|
+
codeExcludePaths: [outputDir, ...(options.codeExcludePaths ?? [])],
|
|
384
460
|
git: false,
|
|
385
461
|
watch: false,
|
|
386
462
|
externalIgnoreLocal: true,
|
|
@@ -391,8 +467,9 @@ export async function buildStaticView(ctx, requestedOutput, options = {}) {
|
|
|
391
467
|
const payloadDir = staticViewPayloadDir(stagingDir, basePath);
|
|
392
468
|
await mkdir(payloadDir, { recursive: true });
|
|
393
469
|
await cp(clientDir, payloadDir, { recursive: true });
|
|
394
|
-
const shell = clientShell(clientHtml, basePath);
|
|
395
470
|
const index = { ...store.getIndex(), git: null, logoText };
|
|
471
|
+
validateDocumentRoutes(index.files);
|
|
472
|
+
const shell = clientShell(clientHtml, basePath, index.entry, options.searchApi);
|
|
396
473
|
const documents = new Map();
|
|
397
474
|
const documentResources = new Set();
|
|
398
475
|
const sourceRequests = new Map();
|
|
@@ -489,25 +566,28 @@ export async function buildStaticView(ctx, requestedOutput, options = {}) {
|
|
|
489
566
|
}
|
|
490
567
|
}
|
|
491
568
|
}
|
|
569
|
+
const rewrittenGraph = rewriteGraph(graph, basePath, index.entry);
|
|
492
570
|
const manifest = {
|
|
493
571
|
version: 1,
|
|
494
572
|
index,
|
|
495
|
-
graph: '
|
|
573
|
+
graph: await writeDataJson(payloadDir, 'graphs', rewrittenGraph),
|
|
496
574
|
documents: {},
|
|
497
575
|
sources: {},
|
|
498
576
|
externals: {},
|
|
499
577
|
};
|
|
500
|
-
|
|
578
|
+
const documentBootstraps = new Map();
|
|
501
579
|
for (const [path, document] of documents) {
|
|
502
|
-
const
|
|
503
|
-
manifest.documents[path] =
|
|
504
|
-
await writeJson(join(payloadDir, dataPath), rewriteDocument(document, basePath, sectionPaths, documentPaths));
|
|
580
|
+
const rewritten = rewriteDocument(document, basePath, index.entry, sectionPaths, documentPaths);
|
|
581
|
+
manifest.documents[path] = await writeDataJson(payloadDir, 'documents', rewritten);
|
|
505
582
|
const source = await store.getDocumentSource(path);
|
|
506
|
-
const rawPath = join(payloadDir,
|
|
583
|
+
const rawPath = join(payloadDir, ...path.split('/'));
|
|
507
584
|
await mkdir(dirname(rawPath), { recursive: true });
|
|
508
585
|
await writeFile(rawPath, source.content);
|
|
509
|
-
const route =
|
|
510
|
-
|
|
586
|
+
const route = path.slice(0, -'.md'.length);
|
|
587
|
+
documentBootstraps.set(route, {
|
|
588
|
+
request: viewStaticDocumentRequest(path),
|
|
589
|
+
document: rewritten,
|
|
590
|
+
});
|
|
511
591
|
}
|
|
512
592
|
for (const path of [...documentResources].sort()) {
|
|
513
593
|
let content;
|
|
@@ -523,78 +603,96 @@ export async function buildStaticView(ctx, requestedOutput, options = {}) {
|
|
|
523
603
|
}
|
|
524
604
|
const sourceFiles = new Map();
|
|
525
605
|
for (const [key, source] of sources) {
|
|
526
|
-
const rewritten = rewriteSource(source, basePath, sectionPaths, documentPaths);
|
|
606
|
+
const rewritten = rewriteSource(source, basePath, index.entry, sectionPaths, documentPaths);
|
|
527
607
|
const { path, content, highlightedLines, ...view } = rewritten;
|
|
528
608
|
let fileDataPath = sourceFiles.get(path);
|
|
529
609
|
if (!fileDataPath) {
|
|
530
|
-
fileDataPath =
|
|
531
|
-
sourceFiles.set(path, fileDataPath);
|
|
532
|
-
await writeJson(join(payloadDir, fileDataPath), {
|
|
610
|
+
fileDataPath = await writeDataJson(payloadDir, 'source-files', {
|
|
533
611
|
path,
|
|
534
612
|
content,
|
|
535
613
|
highlightedLines,
|
|
536
614
|
});
|
|
615
|
+
sourceFiles.set(path, fileDataPath);
|
|
537
616
|
}
|
|
538
|
-
const viewDataPath =
|
|
617
|
+
const viewDataPath = await writeDataJson(payloadDir, 'source-views', view);
|
|
539
618
|
manifest.sources[key] = { file: fileDataPath, view: viewDataPath };
|
|
540
|
-
await writeJson(join(payloadDir, viewDataPath), view);
|
|
541
|
-
}
|
|
542
|
-
for (const path of sourceFiles.keys()) {
|
|
543
|
-
const route = `code/${path}`;
|
|
544
|
-
await writeRouteShell(payloadDir, route, shell);
|
|
545
619
|
}
|
|
546
620
|
const externalSourceFiles = new Map();
|
|
547
621
|
const externalRoutes = new Set();
|
|
548
622
|
for (const [target, external] of externals) {
|
|
549
623
|
if (external.kind === 'markdown') {
|
|
550
|
-
const dataPath =
|
|
551
|
-
manifest.externals[target] = { kind: 'markdown', document: dataPath };
|
|
552
|
-
await writeJson(join(payloadDir, dataPath), {
|
|
624
|
+
const dataPath = await writeDataJson(payloadDir, 'external-documents', {
|
|
553
625
|
...external,
|
|
554
|
-
document: rewriteDocument(external.document, basePath, sectionPaths, documentPaths),
|
|
626
|
+
document: rewriteDocument(external.document, basePath, index.entry, sectionPaths, documentPaths),
|
|
555
627
|
});
|
|
628
|
+
manifest.externals[target] = { kind: 'markdown', document: dataPath };
|
|
556
629
|
const colon = external.document.path.indexOf(':');
|
|
557
630
|
externalRoutes.add(`external/${external.document.path.slice(0, colon)}/${external.document.path.slice(colon + 1)}`);
|
|
558
631
|
continue;
|
|
559
632
|
}
|
|
560
|
-
const rewritten = rewriteSource(external.source, basePath, sectionPaths, documentPaths);
|
|
633
|
+
const rewritten = rewriteSource(external.source, basePath, index.entry, sectionPaths, documentPaths);
|
|
561
634
|
const { path, content, highlightedLines, ...sourceView } = rewritten;
|
|
562
635
|
let fileDataPath = externalSourceFiles.get(path);
|
|
563
636
|
if (!fileDataPath) {
|
|
564
|
-
fileDataPath =
|
|
565
|
-
externalSourceFiles.set(path, fileDataPath);
|
|
566
|
-
await writeJson(join(payloadDir, fileDataPath), {
|
|
637
|
+
fileDataPath = await writeDataJson(payloadDir, 'external-source-files', {
|
|
567
638
|
path,
|
|
568
639
|
content,
|
|
569
640
|
highlightedLines,
|
|
570
641
|
});
|
|
642
|
+
externalSourceFiles.set(path, fileDataPath);
|
|
571
643
|
}
|
|
572
|
-
const viewDataPath = dataFile('external-source-views', target);
|
|
573
|
-
manifest.externals[target] = {
|
|
574
|
-
kind: 'source',
|
|
575
|
-
file: fileDataPath,
|
|
576
|
-
view: viewDataPath,
|
|
577
|
-
};
|
|
578
644
|
const view = {
|
|
579
645
|
kind: 'source',
|
|
580
646
|
target: external.target,
|
|
581
647
|
source: sourceView,
|
|
582
648
|
};
|
|
583
|
-
await
|
|
649
|
+
const viewDataPath = await writeDataJson(payloadDir, 'external-source-views', view);
|
|
650
|
+
manifest.externals[target] = {
|
|
651
|
+
kind: 'source',
|
|
652
|
+
file: fileDataPath,
|
|
653
|
+
view: viewDataPath,
|
|
654
|
+
};
|
|
584
655
|
const colon = path.indexOf(':');
|
|
585
656
|
externalRoutes.add(`external/${path.slice(0, colon)}/${path.slice(colon + 1)}`);
|
|
586
657
|
}
|
|
658
|
+
await writeJson(join(payloadDir, 'data/manifest.json'), manifest);
|
|
659
|
+
const entryRoute = index.entry.slice(0, -'.md'.length);
|
|
660
|
+
const entryBootstrap = documentBootstraps.get(entryRoute);
|
|
661
|
+
if (!entryBootstrap) {
|
|
662
|
+
throw new Error(`Static UI entry document is missing: ${index.entry}`);
|
|
663
|
+
}
|
|
664
|
+
for (const [route, initial] of documentBootstraps) {
|
|
665
|
+
if (route === entryRoute)
|
|
666
|
+
continue;
|
|
667
|
+
await writeRouteShell(payloadDir, route, bootstrapShell(shell, manifest, {
|
|
668
|
+
[initial.request]: initial.document,
|
|
669
|
+
}));
|
|
670
|
+
}
|
|
671
|
+
const genericShell = bootstrapShell(shell, manifest);
|
|
672
|
+
for (const path of sourceFiles.keys()) {
|
|
673
|
+
await writeRouteShell(payloadDir, `code/${path}`, genericShell);
|
|
674
|
+
}
|
|
587
675
|
for (const route of externalRoutes) {
|
|
588
|
-
await writeRouteShell(payloadDir, route,
|
|
676
|
+
await writeRouteShell(payloadDir, route, genericShell);
|
|
589
677
|
}
|
|
590
|
-
await writeRouteShell(payloadDir, 'graph', shell);
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
678
|
+
await writeRouteShell(payloadDir, 'graph', bootstrapShell(shell, manifest, { '/api/graph': rewrittenGraph }));
|
|
679
|
+
if (options.searchApi) {
|
|
680
|
+
await writeRouteShell(payloadDir, 'search', genericShell);
|
|
681
|
+
}
|
|
682
|
+
const redirectScriptPath = join(payloadDir, REDIRECT_SCRIPT_PATH);
|
|
683
|
+
await mkdir(dirname(redirectScriptPath), { recursive: true });
|
|
684
|
+
await writeFile(redirectScriptPath, REDIRECT_SCRIPT);
|
|
685
|
+
await writeFile(join(payloadDir, 'index.html'), bootstrapShell(shell, manifest, {
|
|
686
|
+
[entryBootstrap.request]: entryBootstrap.document,
|
|
687
|
+
}));
|
|
688
|
+
await writeRouteShell(payloadDir, entryRoute, redirectShell(basePath, basePath));
|
|
594
689
|
if (payloadDir !== stagingDir) {
|
|
595
|
-
await writeFile(join(stagingDir, 'index.html'),
|
|
690
|
+
await writeFile(join(stagingDir, 'index.html'), redirectShell(basePath, basePath));
|
|
691
|
+
}
|
|
692
|
+
if (options.force) {
|
|
693
|
+
await rm(outputDir, { recursive: true, force: true });
|
|
596
694
|
}
|
|
597
|
-
await
|
|
695
|
+
await moveViewBuildOutput(stagingDir, outputDir);
|
|
598
696
|
return {
|
|
599
697
|
documents: documents.size,
|
|
600
698
|
outputDir,
|
|
@@ -602,7 +700,7 @@ export async function buildStaticView(ctx, requestedOutput, options = {}) {
|
|
|
602
700
|
};
|
|
603
701
|
}
|
|
604
702
|
catch (error) {
|
|
605
|
-
await
|
|
703
|
+
await removeViewBuildStaging(stagingDir);
|
|
606
704
|
throw error;
|
|
607
705
|
}
|
|
608
706
|
finally {
|
|
@@ -36,5 +36,12 @@ export type ViewStaticManifest = {
|
|
|
36
36
|
sources: Record<string, ViewStaticSourceEntry>;
|
|
37
37
|
externals: Record<string, ViewStaticExternalEntry>;
|
|
38
38
|
};
|
|
39
|
+
export type ViewStaticBootstrap = {
|
|
40
|
+
manifest: ViewStaticManifest;
|
|
41
|
+
responses: Record<string, object>;
|
|
42
|
+
};
|
|
43
|
+
export declare const VIEW_STATIC_BOOTSTRAP_ID = "lat-static-bootstrap";
|
|
44
|
+
/** Match the live document endpoint key used by the static browser adapter. */
|
|
45
|
+
export declare function viewStaticDocumentRequest(path: string): string;
|
|
39
46
|
/** Stable lookup key shared by the static exporter and browser adapter. */
|
|
40
47
|
export declare function viewStaticSourceKey(request: ViewStaticSourceRequest): string;
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
export const VIEW_STATIC_BOOTSTRAP_ID = 'lat-static-bootstrap';
|
|
2
|
+
/** Match the live document endpoint key used by the static browser adapter. */
|
|
3
|
+
export function viewStaticDocumentRequest(path) {
|
|
4
|
+
return `/api/document?path=${encodeURIComponent(path)}`;
|
|
5
|
+
}
|
|
1
6
|
/** Stable lookup key shared by the static exporter and browser adapter. */
|
|
2
7
|
export function viewStaticSourceKey(request) {
|
|
3
8
|
return JSON.stringify([
|