@abreen/tada 1.17.1 → 1.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -7
- package/assets/material-symbols/chevron_right_24px.svg +1 -0
- package/bin/tada.ts +16 -7
- package/bin/validators.ts +33 -0
- package/build/branding.ts +41 -0
- package/build/build-manifest.ts +9 -4
- package/build/{watch/compiler-types.d.ts → build-types.d.ts} +8 -5
- package/build/build-validation.ts +176 -0
- package/build/bundle.ts +75 -65
- package/build/config-loader.ts +3 -3
- package/build/custom-fonts.ts +8 -5
- package/build/generate-favicon.ts +7 -6
- package/build/generate-fonts.ts +8 -15
- package/build/generate-katex-assets.ts +19 -17
- package/build/generate-web-app-manifest.ts +3 -11
- package/build/lodash-template.ts +14 -0
- package/build/material-symbols.ts +7 -4
- package/build/output-publication.ts +238 -0
- package/build/pagefind.ts +49 -21
- package/build/pdf-text.ts +41 -1
- package/build/pipeline.ts +95 -101
- package/build/site-assets.ts +46 -0
- package/build/site-build.ts +267 -0
- package/build/source-model.ts +223 -244
- package/build/source-records.ts +69 -81
- package/build/template-globals.ts +3 -0
- package/build/templates.ts +5 -2
- package/build/toc-plugin.ts +21 -21
- package/build/types.d.ts +44 -38
- package/build/util.ts +0 -4
- package/build/utils/code.ts +38 -101
- package/build/utils/final-html.ts +171 -47
- package/build/utils/literate-java.ts +11 -7
- package/build/utils/markdown-partials.ts +4 -3
- package/build/utils/markdown.ts +6 -17
- package/build/utils/paths.ts +3 -0
- package/build/utils/plain-text.ts +18 -0
- package/build/utils/render.ts +227 -165
- package/build/utils/shiki-highlighter.ts +11 -0
- package/build/utils/source-template.ts +2 -2
- package/build/utils/trace-core.ts +8 -83
- package/build/utils/trace.ts +38 -95
- package/build/validate-config-links.ts +60 -20
- package/build/watch/build-incremental.ts +46 -103
- package/build/watch/compiler.ts +158 -37
- package/build/watch/engine.ts +207 -176
- package/build/watch/index.ts +9 -2
- package/build/watch/planner.ts +88 -144
- package/build/watch/runtime.ts +21 -34
- package/build/watch/types.d.ts +32 -64
- package/init/content/labs/00/VowelCounter.java.md +5 -2
- package/init/content/labs/00/index.md +3 -2
- package/init/content/labs/01/index.md +3 -2
- package/init/content/labs/02/index.md +3 -2
- package/init/content/lectures/01/index.md +3 -2
- package/init/content/lectures/02/index.md +3 -2
- package/package.json +4 -4
- package/schema/site.schema.json +18 -2
- package/src/_content.scss +463 -0
- package/src/_fonts.scss +90 -0
- package/src/_layout.scss +50 -35
- package/src/{material-symbols.scss → _material-symbols.scss} +7 -0
- package/src/appearance-picker/{style.scss → _index.scss} +1 -1
- package/src/code/{style.scss → _index.scss} +61 -0
- package/src/header/{_base.scss → _index.scss} +98 -6
- package/src/index.ts +1 -17
- package/src/navigate/{style.scss → _index.scss} +29 -3
- package/src/navigate/breadcrumbs.ts +129 -0
- package/src/navigate/eligible.ts +0 -40
- package/src/navigate/index.ts +38 -4
- package/src/navigate/lifecycle.ts +19 -2
- package/src/navigate/runtime.ts +57 -22
- package/src/page-update/index.ts +33 -20
- package/src/print/{style.scss → _index.scss} +3 -1
- package/src/search/index.ts +7 -0
- package/src/slides/{style.scss → _index.scss} +5 -0
- package/src/style.scss +24 -536
- package/src/toc/index.ts +2 -2
- package/templates/_author.html +2 -2
- package/templates/_download.html +1 -1
- package/templates/_footer.html +1 -1
- package/templates/_heading.html +17 -4
- package/templates/_nav.html +2 -2
- package/templates/_page-bottom.html +6 -0
- package/templates/_top.html +18 -14
- package/templates/code.html +22 -21
- package/templates/default.html +12 -11
- package/templates/literate.html +6 -5
- package/assets/material-symbols/south_east_20px.svg +0 -1
- package/build/copy.ts +0 -82
- package/build/generate-content-assets.ts +0 -121
- package/build/utils/content-files.ts +0 -170
- package/build/watch/assets.ts +0 -103
- package/build/watch/build-full.ts +0 -97
- package/build/watch/build-helpers.ts +0 -66
- package/build/watch/build-result.ts +0 -19
- package/build/watch/fs-commit.ts +0 -218
- package/build/watch/mutations.ts +0 -73
- package/build/watch/snapshot.ts +0 -201
- package/build/watch/validation.ts +0 -57
- package/src/code.scss +0 -60
- package/src/critical.scss +0 -5
- package/src/header/style.scss +0 -91
- /package/src/{literate.scss → _literate.scss} +0 -0
- /package/src/anchor/{style.scss → _index.scss} +0 -0
- /package/src/page-update/{style.scss → _index.scss} +0 -0
- /package/src/question/{style.scss → _index.scss} +0 -0
- /package/src/search/{style.scss → _index.scss} +0 -0
- /package/src/timezone/{style.scss → _index.scss} +0 -0
- /package/src/toc/{style.scss → _index.scss} +0 -0
- /package/src/trace/{style.scss → _index.scss} +0 -0
package/build/utils/render.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import
|
|
3
|
+
import { compileTemplate } from '../lodash-template';
|
|
4
4
|
import type MarkdownIt from 'markdown-it';
|
|
5
|
-
import { stripHtml } from 'string-strip-html';
|
|
6
5
|
import { makeLogger } from '../log';
|
|
7
6
|
import { B } from '../colors';
|
|
8
7
|
import createTemplateGlobals from '../template-globals';
|
|
@@ -16,8 +15,8 @@ import {
|
|
|
16
15
|
getSupportedConfigFileNamesText,
|
|
17
16
|
} from '../config-files';
|
|
18
17
|
import {
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
resolveBreadcrumbLinkTarget,
|
|
19
|
+
validateBreadcrumbs,
|
|
21
20
|
} from '../validate-config-links';
|
|
22
21
|
import {
|
|
23
22
|
extractJavaMethodToc,
|
|
@@ -28,9 +27,11 @@ import {
|
|
|
28
27
|
import { extensionIsMarkdown } from './file-types';
|
|
29
28
|
import { createTraceHelpers } from './trace';
|
|
30
29
|
import { stripHtmlComments } from './html-comments';
|
|
30
|
+
import { htmlToPlainText } from './plain-text';
|
|
31
31
|
import { finalizeHtmlPage } from './final-html';
|
|
32
32
|
import {
|
|
33
33
|
createApplyBasePath,
|
|
34
|
+
getProjectDir,
|
|
34
35
|
normalizeOutputPath,
|
|
35
36
|
toPosix,
|
|
36
37
|
toUrlPath,
|
|
@@ -41,7 +42,7 @@ import { parseFrontMatterAndContent } from './front-matter';
|
|
|
41
42
|
import { createMarkdown } from './markdown';
|
|
42
43
|
import { generateTocHtml, generateCodeTocHtml } from '../toc-plugin';
|
|
43
44
|
import {
|
|
44
|
-
|
|
45
|
+
extractLiterateJavaCode,
|
|
45
46
|
hasMainMethod,
|
|
46
47
|
deriveClassName,
|
|
47
48
|
compileJavaSource,
|
|
@@ -55,7 +56,6 @@ import type {
|
|
|
55
56
|
RenderLiterateJavaOptions,
|
|
56
57
|
RenderCopiedContentOptions,
|
|
57
58
|
RenderDependencyCollector,
|
|
58
|
-
TraceToolAvailability,
|
|
59
59
|
} from '../types';
|
|
60
60
|
|
|
61
61
|
const log = makeLogger(import.meta.url);
|
|
@@ -66,9 +66,20 @@ export { stripHtmlComments } from './html-comments';
|
|
|
66
66
|
|
|
67
67
|
const REQUIRED_FRONT_MATTER_FIELDS = ['title'];
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
// Internal page variables and retired front matter keys. Internal values would
|
|
70
|
+
// be overwritten or, for `template`, select a nonexistent template.
|
|
71
|
+
const RESERVED_FRONT_MATTER_KEYS: readonly string[] = [
|
|
72
|
+
'template',
|
|
73
|
+
'titleHtml',
|
|
74
|
+
'descriptionHtml',
|
|
75
|
+
'tocHtml',
|
|
76
|
+
'tocItems',
|
|
77
|
+
'codeFilePath',
|
|
78
|
+
'downloadName',
|
|
79
|
+
'filePath',
|
|
80
|
+
'parent',
|
|
81
|
+
'parentLabel',
|
|
82
|
+
];
|
|
72
83
|
|
|
73
84
|
function renderInlineField(
|
|
74
85
|
md: MarkdownIt,
|
|
@@ -81,7 +92,7 @@ function renderInlineField(
|
|
|
81
92
|
}
|
|
82
93
|
const html = md.renderInline(raw);
|
|
83
94
|
vars[`${field}Html`] = html;
|
|
84
|
-
vars[field] =
|
|
95
|
+
vars[field] = htmlToPlainText(html);
|
|
85
96
|
}
|
|
86
97
|
|
|
87
98
|
interface TemplateParametersInput {
|
|
@@ -120,6 +131,19 @@ function resolveAuthor(
|
|
|
120
131
|
pageVariables.author = authorEntry;
|
|
121
132
|
}
|
|
122
133
|
|
|
134
|
+
function rejectReservedFrontMatterKeys(
|
|
135
|
+
pageVariables: Record<string, unknown>,
|
|
136
|
+
filePath: string,
|
|
137
|
+
): void {
|
|
138
|
+
const key = RESERVED_FRONT_MATTER_KEYS.find(k =>
|
|
139
|
+
Object.hasOwn(pageVariables, k),
|
|
140
|
+
);
|
|
141
|
+
if (key) {
|
|
142
|
+
const displayPath = toPosix(path.relative(getProjectDir(), filePath));
|
|
143
|
+
throw new Error(`${displayPath}: front matter key "${key}" is reserved`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
123
147
|
function validateFrontMatter(
|
|
124
148
|
pageVariables: Record<string, unknown>,
|
|
125
149
|
filePath: string,
|
|
@@ -166,10 +190,17 @@ function renderSiteBanner(siteVariables: SiteVariables): string {
|
|
|
166
190
|
return createMarkdown(siteVariables).render(siteVariables.banner);
|
|
167
191
|
}
|
|
168
192
|
|
|
193
|
+
// `<meta charset>` is the first element in `<head>` so browsers find it within
|
|
194
|
+
// the first 1024 bytes. Tags injected into the head go right after it.
|
|
195
|
+
const CHARSET_META_PATTERN = /<meta charset="UTF-8"\s*\/?>/;
|
|
196
|
+
|
|
197
|
+
function insertAfterCharsetMeta(html: string, tags: string): string {
|
|
198
|
+
return html.replace(CHARSET_META_PATTERN, charsetMeta => charsetMeta + tags);
|
|
199
|
+
}
|
|
200
|
+
|
|
169
201
|
export function injectAssetTags(
|
|
170
202
|
html: string,
|
|
171
203
|
assetFiles: string[],
|
|
172
|
-
distDir: string,
|
|
173
204
|
siteVariables: SiteVariables,
|
|
174
205
|
): string {
|
|
175
206
|
const jsAssets = assetFiles.filter(f => f.endsWith('.js'));
|
|
@@ -181,15 +212,8 @@ export function injectAssetTags(
|
|
|
181
212
|
`<script defer src="${normalizeOutputPath('/' + asset)}"></script>`,
|
|
182
213
|
)
|
|
183
214
|
.join('');
|
|
184
|
-
|
|
185
|
-
const
|
|
186
|
-
const criticalTags = criticalAssets
|
|
187
|
-
.map(asset => {
|
|
188
|
-
const css = fs.readFileSync(path.join(distDir, asset), 'utf-8');
|
|
189
|
-
return `<style>${css}</style>`;
|
|
190
|
-
})
|
|
191
|
-
.join('');
|
|
192
|
-
const asyncLinkTags = asyncAssets
|
|
215
|
+
// Render-blocking on purpose: pages never paint without the full stylesheet
|
|
216
|
+
const stylesheetTags = cssAssets
|
|
193
217
|
.map(
|
|
194
218
|
asset =>
|
|
195
219
|
`<link href="${normalizeOutputPath('/' + asset)}" rel="stylesheet">`,
|
|
@@ -197,11 +221,6 @@ export function injectAssetTags(
|
|
|
197
221
|
.join('');
|
|
198
222
|
|
|
199
223
|
const fontPreloadTags = getDefaultFontPreloadFiles(siteVariables)
|
|
200
|
-
.filter(
|
|
201
|
-
preload =>
|
|
202
|
-
preload.source === 'public' ||
|
|
203
|
-
fs.existsSync(path.join(distDir, preload.filePath)),
|
|
204
|
-
)
|
|
205
224
|
.map(preload => {
|
|
206
225
|
const urlPath =
|
|
207
226
|
preload.source === 'public'
|
|
@@ -211,31 +230,27 @@ export function injectAssetTags(
|
|
|
211
230
|
})
|
|
212
231
|
.join('');
|
|
213
232
|
|
|
214
|
-
return
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
)
|
|
219
|
-
.replace('</head>', `${scriptTags}</head>`);
|
|
233
|
+
return insertAfterCharsetMeta(
|
|
234
|
+
html,
|
|
235
|
+
`${fontPreloadTags}${stylesheetTags}`,
|
|
236
|
+
).replace('</head>', `${scriptTags}</head>`);
|
|
220
237
|
}
|
|
221
238
|
|
|
222
239
|
export function injectKatexStylesheet(html: string): string {
|
|
223
240
|
const tag = `<link href="/katex/katex.min.css" rel="stylesheet">`;
|
|
224
|
-
return html
|
|
241
|
+
return insertAfterCharsetMeta(html, tag);
|
|
225
242
|
}
|
|
226
243
|
|
|
227
244
|
export function preparePageTemplateHtml({
|
|
228
245
|
templateHtml,
|
|
229
246
|
assetFiles,
|
|
230
|
-
distDir,
|
|
231
247
|
siteVariables,
|
|
232
248
|
}: {
|
|
233
249
|
templateHtml: string;
|
|
234
250
|
assetFiles: string[];
|
|
235
|
-
distDir: string;
|
|
236
251
|
siteVariables: SiteVariables;
|
|
237
252
|
}): string {
|
|
238
|
-
let html = injectAssetTags(templateHtml, assetFiles,
|
|
253
|
+
let html = injectAssetTags(templateHtml, assetFiles, siteVariables);
|
|
239
254
|
if (templateHtml.includes('class="katex"')) {
|
|
240
255
|
html = injectKatexStylesheet(html);
|
|
241
256
|
}
|
|
@@ -249,13 +264,14 @@ function toContentAssetPath(contentDir: string, filePath: string): string {
|
|
|
249
264
|
export function renderPlainTextPageAsset({
|
|
250
265
|
filePath,
|
|
251
266
|
contentDir,
|
|
252
|
-
distDir,
|
|
253
267
|
siteVariables,
|
|
254
268
|
validInternalTargets,
|
|
255
269
|
assetFiles,
|
|
270
|
+
isWatchMode,
|
|
256
271
|
literateJavaOutputPaths,
|
|
272
|
+
generatedPageTargets,
|
|
273
|
+
codePageSourceTargets,
|
|
257
274
|
dependencyCollector,
|
|
258
|
-
cachedTraceSourceDir,
|
|
259
275
|
traceCache,
|
|
260
276
|
traceToolAvailability,
|
|
261
277
|
}: RenderPlainTextOptions): Asset[] {
|
|
@@ -264,34 +280,21 @@ export function renderPlainTextPageAsset({
|
|
|
264
280
|
const sourceUrlPath = normalizeOutputPath(`/${subPath}.html`);
|
|
265
281
|
|
|
266
282
|
log.info`Rendering page ${B`${subPath + ext}`}`;
|
|
267
|
-
const
|
|
268
|
-
const { content, pageVariables, tocItems, alertIds } = renderPlainTextContent(
|
|
283
|
+
const { content, pageVariables, tocItems } = renderPlainTextContent(
|
|
269
284
|
filePath,
|
|
270
285
|
subPath,
|
|
271
286
|
sourceUrlPath,
|
|
272
287
|
siteVariables,
|
|
273
288
|
validInternalTargets,
|
|
274
|
-
|
|
275
|
-
{
|
|
276
|
-
dependencyCollector,
|
|
277
|
-
traceCache,
|
|
278
|
-
contentDir,
|
|
279
|
-
distDir,
|
|
280
|
-
cachedTraceSourceDir,
|
|
281
|
-
traceToolAvailability,
|
|
282
|
-
},
|
|
289
|
+
isWatchMode,
|
|
290
|
+
{ dependencyCollector, traceCache, contentDir, traceToolAvailability },
|
|
283
291
|
);
|
|
284
292
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
if (!pageVariables.template) {
|
|
288
|
-
pageVariables.template = 'default';
|
|
289
|
-
}
|
|
293
|
+
pageVariables.template = 'default';
|
|
290
294
|
|
|
291
295
|
if (pageVariables.toc && tocItems) {
|
|
292
296
|
pageVariables.tocHtml = generateTocHtml(
|
|
293
297
|
tocItems as Parameters<typeof generateTocHtml>[0],
|
|
294
|
-
alertIds,
|
|
295
298
|
);
|
|
296
299
|
}
|
|
297
300
|
|
|
@@ -300,7 +303,7 @@ export function renderPlainTextPageAsset({
|
|
|
300
303
|
siteVariables,
|
|
301
304
|
content,
|
|
302
305
|
subPath,
|
|
303
|
-
isWatchMode
|
|
306
|
+
isWatchMode,
|
|
304
307
|
bannerHtml: renderSiteBanner(siteVariables),
|
|
305
308
|
});
|
|
306
309
|
|
|
@@ -310,16 +313,13 @@ export function renderPlainTextPageAsset({
|
|
|
310
313
|
) as string;
|
|
311
314
|
const finalized = finalizeHtmlPage({
|
|
312
315
|
filePath,
|
|
313
|
-
html: preparePageTemplateHtml({
|
|
314
|
-
templateHtml,
|
|
315
|
-
assetFiles,
|
|
316
|
-
distDir,
|
|
317
|
-
siteVariables,
|
|
318
|
-
}),
|
|
316
|
+
html: preparePageTemplateHtml({ templateHtml, assetFiles, siteVariables }),
|
|
319
317
|
siteVariables,
|
|
320
318
|
sourceUrlPath,
|
|
321
319
|
validInternalTargets,
|
|
322
320
|
literateJavaOutputPaths,
|
|
321
|
+
generatedPageTargets,
|
|
322
|
+
codePageSourceTargets,
|
|
323
323
|
dependencyCollector,
|
|
324
324
|
});
|
|
325
325
|
|
|
@@ -335,11 +335,13 @@ export function renderPlainTextPageAsset({
|
|
|
335
335
|
export function renderCodePageAsset({
|
|
336
336
|
filePath,
|
|
337
337
|
contentDir,
|
|
338
|
-
distDir,
|
|
339
338
|
siteVariables,
|
|
340
339
|
assetFiles,
|
|
340
|
+
isWatchMode,
|
|
341
341
|
validInternalTargets,
|
|
342
342
|
literateJavaOutputPaths,
|
|
343
|
+
generatedPageTargets,
|
|
344
|
+
codePageSourceTargets,
|
|
343
345
|
dependencyCollector,
|
|
344
346
|
}: RenderCodePageOptions): Asset[] {
|
|
345
347
|
const { dir, name, ext } = path.parse(filePath);
|
|
@@ -382,23 +384,20 @@ export function renderCodePageAsset({
|
|
|
382
384
|
siteVariables,
|
|
383
385
|
content,
|
|
384
386
|
subPath,
|
|
385
|
-
isWatchMode
|
|
387
|
+
isWatchMode,
|
|
386
388
|
bannerHtml: renderSiteBanner(siteVariables),
|
|
387
389
|
});
|
|
388
390
|
|
|
389
391
|
const templateHtml = render('code.html', templateParameters) as string;
|
|
390
392
|
const finalized = finalizeHtmlPage({
|
|
391
393
|
filePath,
|
|
392
|
-
html: preparePageTemplateHtml({
|
|
393
|
-
templateHtml,
|
|
394
|
-
assetFiles,
|
|
395
|
-
distDir,
|
|
396
|
-
siteVariables,
|
|
397
|
-
}),
|
|
394
|
+
html: preparePageTemplateHtml({ templateHtml, assetFiles, siteVariables }),
|
|
398
395
|
siteVariables,
|
|
399
396
|
sourceUrlPath,
|
|
400
397
|
validInternalTargets,
|
|
401
398
|
literateJavaOutputPaths,
|
|
399
|
+
generatedPageTargets,
|
|
400
|
+
codePageSourceTargets,
|
|
402
401
|
dependencyCollector,
|
|
403
402
|
});
|
|
404
403
|
|
|
@@ -442,58 +441,42 @@ export function renderCopiedContentAsset({
|
|
|
442
441
|
return [{ assetPath: relPath, content: templated }];
|
|
443
442
|
}
|
|
444
443
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
filePath: string
|
|
448
|
-
subPath: string
|
|
449
|
-
sourceUrlPath: string
|
|
450
|
-
siteVariables: SiteVariables
|
|
451
|
-
validInternalTargets:
|
|
452
|
-
isWatchMode: boolean
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
distDir,
|
|
458
|
-
cachedTraceSourceDir,
|
|
459
|
-
traceToolAvailability,
|
|
460
|
-
}: {
|
|
461
|
-
dependencyCollector?: RenderDependencyCollector;
|
|
462
|
-
traceCache?: Map<
|
|
463
|
-
string,
|
|
464
|
-
{
|
|
465
|
-
manifestUrl: string;
|
|
466
|
-
artifactId: string;
|
|
467
|
-
highlightedSources: { file: string; highlightedSource: string }[];
|
|
468
|
-
totalSteps: number;
|
|
469
|
-
sourceMtims: Record<string, number>;
|
|
470
|
-
}
|
|
471
|
-
>;
|
|
472
|
-
contentDir?: string;
|
|
473
|
-
distDir?: string;
|
|
474
|
-
cachedTraceSourceDir?: string;
|
|
475
|
-
traceToolAvailability?: TraceToolAvailability;
|
|
476
|
-
} = {},
|
|
477
|
-
): {
|
|
478
|
-
content: string | null;
|
|
479
|
-
pageVariables: Record<string, unknown>;
|
|
480
|
-
tocItems: unknown[] | null;
|
|
481
|
-
alertIds: string[];
|
|
482
|
-
} {
|
|
483
|
-
const applyBasePath = createApplyBasePath(siteVariables);
|
|
444
|
+
interface PreparePageVariablesInput {
|
|
445
|
+
rawPageVariables: Record<string, unknown>;
|
|
446
|
+
filePath: string;
|
|
447
|
+
subPath: string;
|
|
448
|
+
sourceUrlPath: string;
|
|
449
|
+
siteVariables: SiteVariables;
|
|
450
|
+
validInternalTargets: ReadonlySet<string>;
|
|
451
|
+
isWatchMode: boolean;
|
|
452
|
+
/** Whether this renderer supports `slides: true` (Markdown pages only) */
|
|
453
|
+
allowSlides: boolean;
|
|
454
|
+
dependencyCollector?: RenderDependencyCollector;
|
|
455
|
+
}
|
|
484
456
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
457
|
+
/**
|
|
458
|
+
* Turns parsed front matter into page variables. Shared by every renderer
|
|
459
|
+
* whose source has front matter, so each one applies the same rules:
|
|
460
|
+
* Lodash processing of string values, inline Markdown for the title and
|
|
461
|
+
* description, required fields, author lookup, and breadcrumb validation.
|
|
462
|
+
*/
|
|
463
|
+
function preparePageVariables({
|
|
464
|
+
rawPageVariables,
|
|
465
|
+
filePath,
|
|
466
|
+
subPath,
|
|
467
|
+
sourceUrlPath,
|
|
468
|
+
siteVariables,
|
|
469
|
+
validInternalTargets,
|
|
470
|
+
isWatchMode,
|
|
471
|
+
allowSlides,
|
|
472
|
+
dependencyCollector,
|
|
473
|
+
}: PreparePageVariablesInput): Record<string, unknown> {
|
|
474
|
+
rejectReservedFrontMatterKeys(rawPageVariables, filePath);
|
|
475
|
+
if (rawPageVariables.slides === true && !allowSlides) {
|
|
489
476
|
throw new Error(
|
|
490
477
|
`${filePath}: slides mode is only supported on Markdown pages`,
|
|
491
478
|
);
|
|
492
479
|
}
|
|
493
|
-
const frontMatterMd = createMarkdown(siteVariables, {
|
|
494
|
-
filePath,
|
|
495
|
-
slides: pageVariables.slides === true,
|
|
496
|
-
});
|
|
497
480
|
|
|
498
481
|
// Handle substitutions inside front matter using siteVariables
|
|
499
482
|
const siteOnlyParams = createTemplateParameters({
|
|
@@ -503,63 +486,129 @@ function renderPlainTextContent(
|
|
|
503
486
|
subPath,
|
|
504
487
|
isWatchMode,
|
|
505
488
|
});
|
|
506
|
-
const
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
489
|
+
const templateString = (value: unknown): unknown =>
|
|
490
|
+
typeof value === 'string' ? compileTemplate(value)(siteOnlyParams) : value;
|
|
491
|
+
const pageVariables: Record<string, unknown> = Object.fromEntries(
|
|
492
|
+
Object.entries(rawPageVariables).map(([key, value]) => [
|
|
493
|
+
key,
|
|
494
|
+
templateString(value),
|
|
510
495
|
]),
|
|
511
496
|
);
|
|
497
|
+
if (Array.isArray(pageVariables.breadcrumbs)) {
|
|
498
|
+
pageVariables.breadcrumbs = pageVariables.breadcrumbs.map(
|
|
499
|
+
(entry: unknown) => {
|
|
500
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
501
|
+
return entry;
|
|
502
|
+
}
|
|
503
|
+
const breadcrumb = entry as Record<string, unknown>;
|
|
504
|
+
return {
|
|
505
|
+
...breadcrumb,
|
|
506
|
+
label: templateString(breadcrumb.label),
|
|
507
|
+
url: templateString(breadcrumb.url),
|
|
508
|
+
};
|
|
509
|
+
},
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
512
|
|
|
513
513
|
// Render title and description as inline Markdown
|
|
514
|
-
|
|
515
|
-
|
|
514
|
+
const frontMatterMd = createMarkdown(siteVariables, {
|
|
515
|
+
filePath,
|
|
516
|
+
slides: pageVariables.slides === true,
|
|
517
|
+
});
|
|
518
|
+
renderInlineField(frontMatterMd, pageVariables, 'title');
|
|
519
|
+
renderInlineField(frontMatterMd, pageVariables, 'description');
|
|
516
520
|
|
|
517
|
-
|
|
521
|
+
validateFrontMatter(pageVariables, filePath);
|
|
522
|
+
resolveAuthor(pageVariables, filePath, dependencyCollector);
|
|
518
523
|
|
|
519
|
-
const
|
|
520
|
-
|
|
524
|
+
const breadcrumbs = pageVariables.breadcrumbs;
|
|
525
|
+
validateBreadcrumbs(
|
|
526
|
+
breadcrumbs,
|
|
521
527
|
filePath,
|
|
522
528
|
validInternalTargets,
|
|
523
529
|
sourceUrlPath,
|
|
524
530
|
);
|
|
525
|
-
|
|
526
|
-
|
|
531
|
+
for (const entry of breadcrumbs ?? []) {
|
|
532
|
+
const resolvedTarget = resolveBreadcrumbLinkTarget(
|
|
533
|
+
entry.url,
|
|
534
|
+
sourceUrlPath,
|
|
535
|
+
);
|
|
536
|
+
if (resolvedTarget) {
|
|
537
|
+
dependencyCollector?.internalTargets?.add(resolvedTarget);
|
|
538
|
+
}
|
|
527
539
|
}
|
|
528
|
-
|
|
529
|
-
|
|
540
|
+
|
|
541
|
+
return pageVariables;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/** Parses the file, renders using template, returns HTML & params used to generate page */
|
|
545
|
+
function renderPlainTextContent(
|
|
546
|
+
filePath: string,
|
|
547
|
+
subPath: string,
|
|
548
|
+
sourceUrlPath: string,
|
|
549
|
+
siteVariables: SiteVariables,
|
|
550
|
+
validInternalTargets: ReadonlySet<string>,
|
|
551
|
+
isWatchMode: boolean,
|
|
552
|
+
{
|
|
553
|
+
dependencyCollector,
|
|
554
|
+
traceCache,
|
|
555
|
+
contentDir,
|
|
556
|
+
traceToolAvailability,
|
|
557
|
+
}: Pick<
|
|
558
|
+
RenderPlainTextOptions,
|
|
559
|
+
| 'dependencyCollector'
|
|
560
|
+
| 'traceCache'
|
|
561
|
+
| 'contentDir'
|
|
562
|
+
| 'traceToolAvailability'
|
|
563
|
+
>,
|
|
564
|
+
): {
|
|
565
|
+
content: string | null;
|
|
566
|
+
pageVariables: Record<string, unknown>;
|
|
567
|
+
tocItems: unknown[] | null;
|
|
568
|
+
} {
|
|
569
|
+
const applyBasePath = createApplyBasePath(siteVariables);
|
|
570
|
+
|
|
571
|
+
const ext = path.extname(filePath);
|
|
572
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
573
|
+
const { pageVariables: rawPageVariables, content } =
|
|
574
|
+
parseFrontMatterAndContent(raw, ext);
|
|
575
|
+
const pageVariables = preparePageVariables({
|
|
576
|
+
rawPageVariables,
|
|
577
|
+
filePath,
|
|
578
|
+
subPath,
|
|
530
579
|
sourceUrlPath,
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
580
|
+
siteVariables,
|
|
581
|
+
validInternalTargets,
|
|
582
|
+
isWatchMode,
|
|
583
|
+
allowSlides: extensionIsMarkdown(ext),
|
|
584
|
+
dependencyCollector,
|
|
585
|
+
});
|
|
535
586
|
|
|
536
587
|
const strippedContent = stripHtmlComments(content);
|
|
537
588
|
|
|
538
589
|
const params = createTemplateParameters({
|
|
539
|
-
pageVariables
|
|
590
|
+
pageVariables,
|
|
540
591
|
siteVariables,
|
|
541
592
|
content: strippedContent,
|
|
542
593
|
subPath,
|
|
543
594
|
isWatchMode,
|
|
544
595
|
});
|
|
545
596
|
|
|
546
|
-
if (traceCache
|
|
597
|
+
if (traceCache) {
|
|
547
598
|
const helpers = createTraceHelpers({
|
|
548
599
|
filePath,
|
|
549
600
|
contentDir,
|
|
550
|
-
distDir,
|
|
551
601
|
applyBasePath,
|
|
552
602
|
cache: traceCache,
|
|
553
603
|
toolAvailability: traceToolAvailability,
|
|
554
604
|
dependencyCollector,
|
|
555
|
-
cachedTraceSourceDir,
|
|
556
605
|
});
|
|
557
606
|
params.renderTrace = helpers.renderTrace;
|
|
558
607
|
}
|
|
559
608
|
|
|
560
609
|
let html: string;
|
|
561
610
|
try {
|
|
562
|
-
html =
|
|
611
|
+
html = compileTemplate(strippedContent)(params);
|
|
563
612
|
} catch (err: unknown) {
|
|
564
613
|
throw new Error(
|
|
565
614
|
`${filePath}: Lodash template error in page or template: ${(err as Error).message}`,
|
|
@@ -568,7 +617,6 @@ function renderPlainTextContent(
|
|
|
568
617
|
}
|
|
569
618
|
|
|
570
619
|
let tocItems: unknown[] | null = null;
|
|
571
|
-
let alertIds: string[] = [];
|
|
572
620
|
if (extensionIsMarkdown(ext)) {
|
|
573
621
|
const md = createMarkdown(siteVariables, {
|
|
574
622
|
filePath,
|
|
@@ -576,29 +624,29 @@ function renderPlainTextContent(
|
|
|
576
624
|
templateParams: params,
|
|
577
625
|
dependencyCollector,
|
|
578
626
|
});
|
|
579
|
-
const env: Record<string, unknown> = {
|
|
627
|
+
const env: Record<string, unknown> = {};
|
|
580
628
|
html = md.render(html!, env);
|
|
581
629
|
tocItems = (env.tocItems as unknown[] | undefined) || null;
|
|
582
|
-
alertIds = env.alertIds as string[];
|
|
583
630
|
}
|
|
584
631
|
|
|
585
632
|
return {
|
|
586
633
|
content: html,
|
|
587
634
|
pageVariables: params.page as Record<string, unknown>,
|
|
588
635
|
tocItems,
|
|
589
|
-
alertIds,
|
|
590
636
|
};
|
|
591
637
|
}
|
|
592
638
|
|
|
593
639
|
export function renderLiterateJavaPageAsset({
|
|
594
640
|
filePath,
|
|
595
641
|
contentDir,
|
|
596
|
-
distDir,
|
|
597
642
|
siteVariables,
|
|
598
643
|
assetFiles,
|
|
644
|
+
isWatchMode,
|
|
599
645
|
skipExecution,
|
|
600
646
|
validInternalTargets,
|
|
601
647
|
literateJavaOutputPaths,
|
|
648
|
+
generatedPageTargets,
|
|
649
|
+
codePageSourceTargets,
|
|
602
650
|
dependencyCollector,
|
|
603
651
|
}: RenderLiterateJavaOptions): Asset[] {
|
|
604
652
|
const { dir, name } = path.parse(filePath);
|
|
@@ -607,16 +655,38 @@ export function renderLiterateJavaPageAsset({
|
|
|
607
655
|
|
|
608
656
|
log.info`Rendering literate Java page ${B`${name}`}`;
|
|
609
657
|
|
|
658
|
+
const sourceUrlPath = `/${subPath}.java.html`;
|
|
610
659
|
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
611
|
-
const {
|
|
660
|
+
const { pageVariables: rawPageVariables, content } =
|
|
661
|
+
parseFrontMatterAndContent(raw, '.md');
|
|
662
|
+
const pageVariables = preparePageVariables({
|
|
663
|
+
rawPageVariables,
|
|
664
|
+
filePath,
|
|
665
|
+
subPath,
|
|
666
|
+
sourceUrlPath,
|
|
667
|
+
siteVariables,
|
|
668
|
+
validInternalTargets,
|
|
669
|
+
isWatchMode,
|
|
670
|
+
allowSlides: false,
|
|
671
|
+
dependencyCollector,
|
|
672
|
+
});
|
|
673
|
+
const params = createTemplateParameters({
|
|
612
674
|
pageVariables,
|
|
675
|
+
siteVariables,
|
|
613
676
|
content,
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
677
|
+
subPath,
|
|
678
|
+
isWatchMode,
|
|
679
|
+
});
|
|
680
|
+
const md = createMarkdown(siteVariables, {
|
|
681
|
+
filePath,
|
|
682
|
+
templateParams: params,
|
|
683
|
+
preserveHtmlComments: true,
|
|
684
|
+
dependencyCollector,
|
|
685
|
+
});
|
|
686
|
+
const env: Record<string, unknown> = {};
|
|
687
|
+
const tokens = md.parse(compileTemplate(content)(params), env);
|
|
688
|
+
const { javaSource, codeBlocks, visibleBlockIndices } =
|
|
689
|
+
extractLiterateJavaCode(tokens);
|
|
620
690
|
|
|
621
691
|
const stdin =
|
|
622
692
|
typeof pageVariables.stdin === 'string' ? pageVariables.stdin : undefined;
|
|
@@ -649,8 +719,6 @@ export function renderLiterateJavaPageAsset({
|
|
|
649
719
|
|
|
650
720
|
// Render full markdown with a custom fence rule that replaces fences
|
|
651
721
|
// with Shiki-highlighted code blocks and optional JDI output columns
|
|
652
|
-
const sourceUrlPath = `/${subPath}.java.html`;
|
|
653
|
-
const md = createMarkdown(siteVariables, { filePath });
|
|
654
722
|
let fenceIndex = 0;
|
|
655
723
|
const defaultFence = md.renderer.rules.fence!;
|
|
656
724
|
|
|
@@ -698,8 +766,9 @@ export function renderLiterateJavaPageAsset({
|
|
|
698
766
|
return codeHtml;
|
|
699
767
|
};
|
|
700
768
|
|
|
701
|
-
const
|
|
702
|
-
|
|
769
|
+
const contentHtml = stripHtmlComments(
|
|
770
|
+
md.renderer.render(tokens, md.options, env),
|
|
771
|
+
);
|
|
703
772
|
|
|
704
773
|
// Build page variables
|
|
705
774
|
const javaFileName = `${className}.java`;
|
|
@@ -707,7 +776,6 @@ export function renderLiterateJavaPageAsset({
|
|
|
707
776
|
`/${toUrlPath(path.relative(contentDir, path.join(dir, javaFileName)))}`,
|
|
708
777
|
);
|
|
709
778
|
|
|
710
|
-
renderInlineField(md, pageVariables, 'title');
|
|
711
779
|
pageVariables.template = 'literate';
|
|
712
780
|
pageVariables.codeFilePath = codeFilePath;
|
|
713
781
|
pageVariables.downloadName = javaFileName;
|
|
@@ -715,34 +783,28 @@ export function renderLiterateJavaPageAsset({
|
|
|
715
783
|
if (pageVariables.toc && env.tocItems) {
|
|
716
784
|
pageVariables.tocHtml = generateTocHtml(
|
|
717
785
|
env.tocItems as Parameters<typeof generateTocHtml>[0],
|
|
718
|
-
env.alertIds as string[],
|
|
719
786
|
);
|
|
720
787
|
}
|
|
721
788
|
|
|
722
|
-
resolveAuthor(pageVariables, filePath, dependencyCollector);
|
|
723
|
-
|
|
724
789
|
const templateParameters = createTemplateParameters({
|
|
725
790
|
pageVariables,
|
|
726
791
|
siteVariables,
|
|
727
792
|
content: contentHtml,
|
|
728
793
|
subPath,
|
|
729
|
-
isWatchMode
|
|
794
|
+
isWatchMode,
|
|
730
795
|
bannerHtml: renderSiteBanner(siteVariables),
|
|
731
796
|
});
|
|
732
797
|
|
|
733
798
|
const templateHtml = render('literate.html', templateParameters) as string;
|
|
734
799
|
const finalized = finalizeHtmlPage({
|
|
735
800
|
filePath,
|
|
736
|
-
html: preparePageTemplateHtml({
|
|
737
|
-
templateHtml,
|
|
738
|
-
assetFiles,
|
|
739
|
-
distDir,
|
|
740
|
-
siteVariables,
|
|
741
|
-
}),
|
|
801
|
+
html: preparePageTemplateHtml({ templateHtml, assetFiles, siteVariables }),
|
|
742
802
|
siteVariables,
|
|
743
803
|
sourceUrlPath,
|
|
744
804
|
validInternalTargets,
|
|
745
805
|
literateJavaOutputPaths,
|
|
806
|
+
generatedPageTargets,
|
|
807
|
+
codePageSourceTargets,
|
|
746
808
|
dependencyCollector,
|
|
747
809
|
});
|
|
748
810
|
|