@abreen/tada 1.17.0 → 1.18.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.
Files changed (107) hide show
  1. package/README.md +41 -7
  2. package/bin/tada.ts +23 -9
  3. package/bin/validators.ts +34 -1
  4. package/build/branding.ts +41 -0
  5. package/build/build-manifest.ts +10 -5
  6. package/build/{watch/compiler-types.d.ts → build-types.d.ts} +8 -5
  7. package/build/build-validation.ts +176 -0
  8. package/build/bundle.ts +75 -65
  9. package/build/columns-plugin.ts +27 -1
  10. package/build/config-loader.ts +3 -3
  11. package/build/custom-fonts.ts +8 -5
  12. package/build/deflist-id-plugin.ts +2 -2
  13. package/build/generate-favicon.ts +7 -6
  14. package/build/generate-fonts.ts +8 -15
  15. package/build/generate-katex-assets.ts +19 -17
  16. package/build/generate-web-app-manifest.ts +3 -11
  17. package/build/lodash-template.ts +14 -0
  18. package/build/output-publication.ts +238 -0
  19. package/build/pagefind.ts +49 -21
  20. package/build/pdf-text.ts +41 -1
  21. package/build/pipeline.ts +95 -101
  22. package/build/site-assets.ts +46 -0
  23. package/build/site-build.ts +267 -0
  24. package/build/source-model.ts +223 -229
  25. package/build/source-records.ts +69 -81
  26. package/build/template-globals.ts +3 -0
  27. package/build/templates.ts +12 -8
  28. package/build/toc-plugin.ts +21 -21
  29. package/build/types.d.ts +38 -38
  30. package/build/util.ts +0 -4
  31. package/build/utils/code.ts +51 -117
  32. package/build/utils/final-html.ts +171 -47
  33. package/build/utils/jdi-runner/LiterateRunner.java +9 -1
  34. package/build/utils/literate-java.ts +11 -7
  35. package/build/utils/markdown-partials.ts +4 -3
  36. package/build/utils/markdown.ts +6 -17
  37. package/build/utils/paths.ts +3 -0
  38. package/build/utils/plain-text.ts +18 -0
  39. package/build/utils/render.ts +197 -154
  40. package/build/utils/shiki-highlighter.ts +11 -0
  41. package/build/utils/source-template.ts +2 -2
  42. package/build/utils/trace-core.ts +8 -83
  43. package/build/utils/trace.ts +38 -95
  44. package/build/validate-config-links.ts +39 -6
  45. package/build/watch/build-incremental.ts +46 -103
  46. package/build/watch/compiler.ts +158 -37
  47. package/build/watch/engine.ts +207 -162
  48. package/build/watch/index.ts +9 -2
  49. package/build/watch/planner.ts +88 -144
  50. package/build/watch/runtime.ts +21 -34
  51. package/build/watch/types.d.ts +32 -64
  52. package/package.json +4 -5
  53. package/schema/site.schema.json +18 -2
  54. package/src/_content.scss +447 -0
  55. package/src/_fonts.scss +90 -0
  56. package/src/_layout.scss +50 -35
  57. package/src/appearance-picker/{style.scss → _index.scss} +1 -1
  58. package/src/code/{style.scss → _index.scss} +61 -0
  59. package/src/header/{_base.scss → _index.scss} +98 -6
  60. package/src/index.ts +1 -17
  61. package/src/navigate/{style.scss → _index.scss} +20 -0
  62. package/src/navigate/eligible.ts +0 -40
  63. package/src/navigate/index.ts +50 -5
  64. package/src/navigate/lifecycle.ts +19 -2
  65. package/src/navigate/runtime.ts +20 -4
  66. package/src/page-update/index.ts +33 -20
  67. package/src/print/{style.scss → _index.scss} +3 -1
  68. package/src/search/index.ts +30 -2
  69. package/src/slides/{style.scss → _index.scss} +5 -0
  70. package/src/style.scss +24 -536
  71. package/src/timezone/index.ts +29 -3
  72. package/src/timezone/time-format.ts +4 -0
  73. package/src/toc/index.ts +6 -5
  74. package/src/trace/index.ts +11 -1
  75. package/templates/_author.html +2 -2
  76. package/templates/_download.html +1 -1
  77. package/templates/_footer.html +1 -1
  78. package/templates/_heading.html +3 -3
  79. package/templates/_nav.html +2 -2
  80. package/templates/_page-bottom.html +6 -0
  81. package/templates/_top.html +18 -14
  82. package/templates/code.html +22 -21
  83. package/templates/default.html +12 -11
  84. package/templates/literate.html +6 -5
  85. package/build/copy.ts +0 -82
  86. package/build/generate-content-assets.ts +0 -121
  87. package/build/utils/content-files.ts +0 -170
  88. package/build/watch/assets.ts +0 -103
  89. package/build/watch/build-full.ts +0 -97
  90. package/build/watch/build-helpers.ts +0 -66
  91. package/build/watch/build-result.ts +0 -19
  92. package/build/watch/fs-commit.ts +0 -153
  93. package/build/watch/mutations.ts +0 -73
  94. package/build/watch/snapshot.ts +0 -201
  95. package/build/watch/validation.ts +0 -50
  96. package/src/code.scss +0 -60
  97. package/src/critical.scss +0 -5
  98. package/src/header/style.scss +0 -91
  99. /package/src/{literate.scss → _literate.scss} +0 -0
  100. /package/src/{material-symbols.scss → _material-symbols.scss} +0 -0
  101. /package/src/anchor/{style.scss → _index.scss} +0 -0
  102. /package/src/page-update/{style.scss → _index.scss} +0 -0
  103. /package/src/question/{style.scss → _index.scss} +0 -0
  104. /package/src/search/{style.scss → _index.scss} +0 -0
  105. /package/src/timezone/{style.scss → _index.scss} +0 -0
  106. /package/src/toc/{style.scss → _index.scss} +0 -0
  107. /package/src/trace/{style.scss → _index.scss} +0 -0
@@ -1,4 +1,3 @@
1
- import fs from 'fs';
2
1
  import path from 'path';
3
2
  import { createHash } from 'crypto';
4
3
  import { renderCodeSegment } from './code';
@@ -7,6 +6,7 @@ import { normalizeOutputPath } from './paths';
7
6
  import { computeLayout } from './trace-layout';
8
7
  import { filterStep, generateStepSvg } from './trace-svg';
9
8
  import type {
9
+ TraceArtifactFile,
10
10
  TraceChunkEntry,
11
11
  TraceHeapObject,
12
12
  TraceManifest,
@@ -24,14 +24,15 @@ export interface ChunkTraceOutputOptions {
24
24
  export interface ChunkTraceOutputResult {
25
25
  manifest: TraceManifest;
26
26
  artifactId: string;
27
- outputPaths: string[];
27
+ /** `manifest.json` followed by the chunk files */
28
+ files: TraceArtifactFile[];
28
29
  }
29
30
 
30
31
  function stepOutputEvents(step: TraceStep): TraceOutputEvent[] {
31
32
  return step.output ?? [];
32
33
  }
33
34
 
34
- function hashTraceFiles(files: { name: string; content: string }[]): string {
35
+ function hashTraceFiles(files: TraceArtifactFile[]): string {
35
36
  const hasher = createHash('sha256');
36
37
  for (const file of files) {
37
38
  hasher.update(file.name, 'utf8');
@@ -46,7 +47,8 @@ function safeSvgIdPart(value: string): string {
46
47
  return value.replace(/[^A-Za-z0-9_-]+/g, '-').replace(/^-+|-+$/g, '');
47
48
  }
48
49
 
49
- function buildTraceOutputPath(
50
+ /** Output path of a trace artifact file, relative to the output directory */
51
+ export function buildTraceOutputPath(
50
52
  relDir: string,
51
53
  traceName: string,
52
54
  artifactId: string,
@@ -147,8 +149,6 @@ export function bridgeConstructorReturnValues(steps: TraceStep[]): TraceStep[] {
147
149
 
148
150
  export function chunkTraceOutput(
149
151
  output: string,
150
- traceOutputDir: string,
151
- relDir: string,
152
152
  traceName: string,
153
153
  primaryFile: string,
154
154
  sources: { file: string; source: string }[],
@@ -189,7 +189,7 @@ export function chunkTraceOutput(
189
189
 
190
190
  let chunkEntries: TraceChunkEntry[] = [];
191
191
  let chunkIndex = 0;
192
- const chunkFiles: { name: string; content: string }[] = [];
192
+ const chunkFiles: TraceArtifactFile[] = [];
193
193
 
194
194
  for (const [stepIndex, step] of allSteps.entries()) {
195
195
  const svg = generateStepSvg(
@@ -237,21 +237,7 @@ export function chunkTraceOutput(
237
237
  content: JSON.stringify(manifest),
238
238
  };
239
239
  const files = [manifestFile, ...chunkFiles];
240
- const artifactId = hashTraceFiles(files);
241
- const artifactDir = path.join(traceOutputDir, artifactId);
242
-
243
- fs.mkdirSync(artifactDir, { recursive: true });
244
- for (const file of files) {
245
- fs.writeFileSync(path.join(artifactDir, file.name), file.content);
246
- }
247
-
248
- return {
249
- manifest,
250
- artifactId,
251
- outputPaths: files.map(file =>
252
- buildTraceOutputPath(relDir, traceName, artifactId, file.name),
253
- ),
254
- };
240
+ return { manifest, artifactId: hashTraceFiles(files), files };
255
241
  }
256
242
 
257
243
  export function highlightTraceSource(
@@ -263,67 +249,6 @@ export function highlightTraceSource(
263
249
  });
264
250
  }
265
251
 
266
- export function getTraceOutputPaths(
267
- relDir: string,
268
- traceName: string,
269
- artifactId: string,
270
- totalSteps: number,
271
- ): string[] {
272
- const outputPaths = [
273
- buildTraceOutputPath(relDir, traceName, artifactId, 'manifest.json'),
274
- ];
275
- for (
276
- let chunkIndex = 0;
277
- chunkIndex * DEFAULT_CHUNK_SIZE < totalSteps;
278
- chunkIndex++
279
- ) {
280
- outputPaths.push(
281
- buildTraceOutputPath(
282
- relDir,
283
- traceName,
284
- artifactId,
285
- `chunk-${chunkIndex}.json`,
286
- ),
287
- );
288
- }
289
- return outputPaths;
290
- }
291
-
292
- function linkOrCopyFile(sourcePath: string, targetPath: string): void {
293
- try {
294
- fs.linkSync(sourcePath, targetPath);
295
- } catch {
296
- fs.copyFileSync(sourcePath, targetPath);
297
- }
298
- }
299
-
300
- export function materializeTraceOutputs({
301
- outputPaths,
302
- targetDistDir,
303
- sourceDistDir,
304
- }: {
305
- outputPaths: string[];
306
- targetDistDir: string;
307
- sourceDistDir: string;
308
- }): boolean {
309
- for (const relPath of outputPaths) {
310
- const targetPath = path.join(targetDistDir, relPath);
311
- if (fs.existsSync(targetPath)) {
312
- continue;
313
- }
314
-
315
- const sourcePath = path.join(sourceDistDir, relPath);
316
- if (!fs.existsSync(sourcePath)) {
317
- return false;
318
- }
319
-
320
- fs.mkdirSync(path.dirname(targetPath), { recursive: true });
321
- linkOrCopyFile(sourcePath, targetPath);
322
- }
323
-
324
- return true;
325
- }
326
-
327
252
  export function buildManifestUrl({
328
253
  relDir,
329
254
  traceName,
@@ -7,11 +7,10 @@ import { toPosix } from './paths';
7
7
  import { checkJavac } from './literate-java';
8
8
  import {
9
9
  buildManifestUrl,
10
+ buildTraceOutputPath,
10
11
  chunkTraceOutput,
11
12
  DEFAULT_CHUNK_SIZE,
12
- getTraceOutputPaths,
13
13
  highlightTraceSource,
14
- materializeTraceOutputs,
15
14
  renderTraceWidgetHtml,
16
15
  } from './trace-core';
17
16
  import {
@@ -25,6 +24,7 @@ import type {
25
24
  RenderDependencyCollector,
26
25
  TraceToolAvailability,
27
26
  } from '../types';
27
+ import type { TraceCache, TraceCacheEntry } from '../build-types';
28
28
 
29
29
  const log = makeLogger(import.meta.url);
30
30
 
@@ -33,20 +33,10 @@ export const TRACEABLE_EXTENSIONS = new Set(['.java', '.py']);
33
33
  export interface TraceContext {
34
34
  filePath: string;
35
35
  contentDir: string;
36
- distDir: string;
37
36
  applyBasePath: (subPath: string) => string;
38
- cache: Map<string, TraceResult>;
37
+ cache: TraceCache;
39
38
  toolAvailability?: TraceToolAvailability;
40
39
  dependencyCollector?: RenderDependencyCollector;
41
- cachedTraceSourceDir?: string;
42
- }
43
-
44
- export interface TraceResult {
45
- manifestUrl: string;
46
- artifactId: string;
47
- highlightedSources: { file: string; highlightedSource: string }[];
48
- totalSteps: number;
49
- sourceMtims: Record<string, number>;
50
40
  }
51
41
 
52
42
  export function isTraceSourceFile(filePath: string): boolean {
@@ -132,12 +122,10 @@ export function createTraceHelpers(context: TraceContext): {
132
122
  const {
133
123
  filePath,
134
124
  contentDir,
135
- distDir,
136
125
  applyBasePath,
137
126
  cache,
138
127
  toolAvailability,
139
128
  dependencyCollector,
140
- cachedTraceSourceDir,
141
129
  } = context;
142
130
  const pageDir = path.dirname(filePath);
143
131
 
@@ -202,7 +190,7 @@ export function createTraceHelpers(context: TraceContext): {
202
190
 
203
191
  function cacheIsFresh(
204
192
  sources: TraceSourceFile[],
205
- cached: TraceResult,
193
+ cached: TraceCacheEntry,
206
194
  ): boolean {
207
195
  return sources.every(source => {
208
196
  const mtime = fs.statSync(source.absolutePath, {
@@ -229,45 +217,13 @@ export function createTraceHelpers(context: TraceContext): {
229
217
 
230
218
  function getOrRunTrace(
231
219
  sourceFile: string,
232
- companionFiles?: string[],
233
- ): TraceResult {
234
- const sources = resolveTraceSources(sourceFile, companionFiles);
235
- for (const source of sources) {
236
- dependencyCollector?.traceFiles?.add(source.absolutePath);
237
- }
238
- const sourceFilePath = sources[0].absolutePath;
239
- const traceName = path.parse(sourceFile).name;
240
- const relDir = toPosix(path.relative(contentDir, pageDir));
220
+ sources: TraceSourceFile[],
221
+ traceName: string,
222
+ ): TraceCacheEntry {
241
223
  const cacheKey = cacheKeyForSources(sources);
242
-
243
224
  const cached = cache.get(cacheKey);
244
225
  if (cached && cacheIsFresh(sources, cached)) {
245
- const outputPaths = getTraceOutputPaths(
246
- relDir,
247
- traceName,
248
- cached.artifactId,
249
- cached.totalSteps,
250
- );
251
- if (
252
- materializeTraceOutputs({
253
- outputPaths,
254
- targetDistDir: distDir,
255
- sourceDistDir: cachedTraceSourceDir || distDir,
256
- })
257
- ) {
258
- for (const outputPath of outputPaths) {
259
- dependencyCollector?.generatedOutputPaths?.add(outputPath);
260
- }
261
- return {
262
- ...cached,
263
- manifestUrl: buildManifestUrl({
264
- relDir,
265
- traceName,
266
- artifactId: cached.artifactId,
267
- applyBasePath,
268
- }),
269
- };
270
- }
226
+ return cached;
271
227
  }
272
228
 
273
229
  log.info`Tracing ${B`${sourceFile}`}`;
@@ -288,49 +244,26 @@ export function createTraceHelpers(context: TraceContext): {
288
244
  fs.rmSync(workspaceDir, { recursive: true, force: true });
289
245
  }
290
246
 
291
- const language = traceLanguageForFile(sourceFilePath);
292
- const highlightedSources = sources.map(source => ({
293
- file: source.file,
294
- highlightedSource: highlightTraceSource(source.source, language),
295
- }));
296
- const traceOutputDir = path.join(distDir, relDir, '_traces', traceName);
297
-
298
- const traceOutput = chunkTraceOutput(
247
+ const language = traceLanguageForFile(sources[0].absolutePath);
248
+ const { manifest, artifactId, files } = chunkTraceOutput(
299
249
  output,
300
- traceOutputDir,
301
- relDir,
302
250
  traceName,
303
251
  sources[0].file,
304
252
  sources.map(source => ({ file: source.file, source: source.source })),
305
253
  { chunkSize: DEFAULT_CHUNK_SIZE, ignoreFields },
306
254
  );
307
- for (const outputPath of traceOutput.outputPaths) {
308
- dependencyCollector?.generatedOutputPaths?.add(outputPath);
309
- }
310
-
311
- const manifestUrl = buildManifestUrl({
312
- relDir,
313
- traceName,
314
- artifactId: traceOutput.artifactId,
315
- applyBasePath,
316
- });
317
-
318
- const totalSteps = traceOutput.manifest.totalSteps;
319
- const sourceMtims = getSourceMtims(sources);
320
- cache.set(cacheKey, {
321
- manifestUrl,
322
- artifactId: traceOutput.artifactId,
323
- highlightedSources,
324
- totalSteps,
325
- sourceMtims,
326
- });
327
- return {
328
- manifestUrl,
329
- artifactId: traceOutput.artifactId,
330
- highlightedSources,
331
- totalSteps,
332
- sourceMtims,
255
+ const entry: TraceCacheEntry = {
256
+ artifactId,
257
+ files,
258
+ highlightedSources: sources.map(source => ({
259
+ file: source.file,
260
+ highlightedSource: highlightTraceSource(source.source, language),
261
+ })),
262
+ totalSteps: manifest.totalSteps,
263
+ sourceMtims: getSourceMtims(sources),
333
264
  };
265
+ cache.set(cacheKey, entry);
266
+ return entry;
334
267
  }
335
268
 
336
269
  return {
@@ -352,14 +285,24 @@ export function createTraceHelpers(context: TraceContext): {
352
285
  });
353
286
  }
354
287
 
355
- const { manifestUrl, highlightedSources, totalSteps } = getOrRunTrace(
356
- sourceFile,
357
- companionFiles,
358
- );
288
+ const traceName = path.parse(sourceFile).name;
289
+ const relDir = toPosix(path.relative(contentDir, pageDir));
290
+ const trace = getOrRunTrace(sourceFile, sources, traceName);
291
+ for (const file of trace.files) {
292
+ dependencyCollector?.traceOutputs?.set(
293
+ buildTraceOutputPath(relDir, traceName, trace.artifactId, file.name),
294
+ file.content,
295
+ );
296
+ }
359
297
  return renderTraceWidgetHtml({
360
- highlightedSources,
361
- manifestUrl,
362
- totalSteps,
298
+ highlightedSources: trace.highlightedSources,
299
+ manifestUrl: buildManifestUrl({
300
+ relDir,
301
+ traceName,
302
+ artifactId: trace.artifactId,
303
+ applyBasePath,
304
+ }),
305
+ totalSteps: trace.totalSteps,
363
306
  });
364
307
  },
365
308
  };
@@ -70,9 +70,24 @@ function getInternalHrefTarget(href: string): string {
70
70
  }
71
71
  }
72
72
 
73
+ function validateDirectoryLink(
74
+ href: string,
75
+ resolvedTarget: string,
76
+ validTargets: ReadonlySet<string>,
77
+ context: string,
78
+ ): string | null {
79
+ const indexPath = normalizeOutputPath(
80
+ path.posix.join(resolvedTarget, 'index.html'),
81
+ );
82
+ if (validTargets.has(indexPath)) {
83
+ return `${context}: directory link must reference index.html explicitly: "${href}" (resolved to "${resolvedTarget}", expected "${indexPath}")`;
84
+ }
85
+ return null;
86
+ }
87
+
73
88
  export function validateNavLinks(
74
89
  navData: unknown,
75
- validTargets: Set<string>,
90
+ validTargets: ReadonlySet<string>,
76
91
  fileName: string = 'nav.yaml',
77
92
  ): string[] {
78
93
  if (!Array.isArray(navData)) {
@@ -96,8 +111,16 @@ export function validateNavLinks(
96
111
  continue;
97
112
  }
98
113
 
99
- const normalized = normalizeOutputPath(link.internal);
100
- if (!validTargets.has(normalized)) {
114
+ const normalized = getInternalHrefTarget(link.internal);
115
+ const directoryError = validateDirectoryLink(
116
+ link.internal,
117
+ normalized,
118
+ validTargets,
119
+ `${fileName}: internal link in section "${section.title}"`,
120
+ );
121
+ if (directoryError) {
122
+ errors.push(directoryError);
123
+ } else if (!validTargets.has(normalized)) {
101
124
  errors.push(
102
125
  `${fileName}: broken internal link in section "${section.title}": "${link.internal}"`,
103
126
  );
@@ -116,7 +139,7 @@ interface AuthorEntry {
116
139
 
117
140
  export function validateAuthorLinks(
118
141
  authorsData: unknown,
119
- validTargets: Set<string>,
142
+ validTargets: ReadonlySet<string>,
120
143
  fileName: string = 'authors.yaml',
121
144
  ): string[] {
122
145
  if (!authorsData || typeof authorsData !== 'object') {
@@ -166,7 +189,7 @@ export function validateAuthorLinks(
166
189
  }
167
190
 
168
191
  export function validateConfigLinks(
169
- validTargets: Set<string>,
192
+ validTargets: ReadonlySet<string>,
170
193
  navData: unknown,
171
194
  authorsData: unknown,
172
195
  {
@@ -188,7 +211,7 @@ function splitHref(href: string): { pathname: string; suffix: string } {
188
211
  export function validateParentLink(
189
212
  parent: unknown,
190
213
  filePath: string,
191
- validTargets: Set<string>,
214
+ validTargets: ReadonlySet<string>,
192
215
  sourceUrlPath: string,
193
216
  ): string | null {
194
217
  if (typeof parent === 'string' && splitHref(parent).pathname === '') {
@@ -200,6 +223,16 @@ export function validateParentLink(
200
223
  return null;
201
224
  }
202
225
 
226
+ const directoryError = validateDirectoryLink(
227
+ parent as string,
228
+ resolvedTarget,
229
+ validTargets,
230
+ `${filePath}: parent link`,
231
+ );
232
+ if (directoryError) {
233
+ return directoryError;
234
+ }
235
+
203
236
  if (!validTargets.has(resolvedTarget)) {
204
237
  return `${filePath}: broken parent link: "${parent}"`;
205
238
  }
@@ -1,118 +1,61 @@
1
- import { compileTemplates, config } from '../templates';
2
- import { getDistDir } from '../util';
3
- import type { CompilerBuildResult } from './types';
4
- import type { TraceCache, WatchTraceOptions } from './compiler-types';
5
- import type { TadaIncrementalWatchPlan } from './planner';
6
- import { createSnapshot, type TadaSnapshot } from './snapshot';
1
+ import { compileTemplates } from '../templates';
2
+ import { validateConfig } from '../build-validation';
7
3
  import {
8
- copyExistingBuildAssets,
9
4
  ensureHighlighter,
10
- makeTempBuildDir,
11
- removeDirIfExists,
12
- } from './assets';
13
- import { computeMutations } from './mutations';
14
- import { validateConfig, validateProjectConfigLinks } from './validation';
15
- import { buildFailedFromError, buildSucceeded } from './build-result';
16
- import {
17
- buildFailedWithDiagnostics,
18
- renderContentRecord,
19
- renderPublicRecord,
20
- } from './build-helpers';
5
+ finishBuild,
6
+ renderSources,
7
+ type TadaSnapshot,
8
+ } from '../site-build';
9
+ import type { TraceCache } from '../build-types';
10
+ import type { TraceToolAvailability } from '../types';
11
+ import type { TadaIncrementalWatchPlan } from './planner';
12
+ import type { CompileOutcome } from './types';
21
13
 
22
14
  export async function buildIncremental({
23
15
  plan,
24
16
  snapshot,
25
17
  traceCache,
26
- traceOptions,
18
+ traceToolAvailability,
27
19
  }: {
28
20
  plan: TadaIncrementalWatchPlan;
29
21
  snapshot: TadaSnapshot;
30
22
  traceCache: TraceCache;
31
- traceOptions: WatchTraceOptions;
32
- }): Promise<CompilerBuildResult> {
33
- const distDir = getDistDir();
34
- const outputDir = makeTempBuildDir(distDir);
35
- try {
36
- const siteVariables = snapshot.siteVariables;
37
- const configDiagnostics = validateConfig(plan.scan, siteVariables);
38
- if (configDiagnostics.length > 0) {
39
- return buildFailedWithDiagnostics(outputDir, configDiagnostics);
40
- }
41
-
42
- compileTemplates(siteVariables);
43
- await ensureHighlighter(siteVariables);
44
- copyExistingBuildAssets(distDir, outputDir, snapshot.assetFiles);
45
-
46
- const nextContentRecords = new Map(snapshot.contentRecords);
47
- const nextPublicRecords = new Map(snapshot.publicRecords);
48
-
49
- for (const sourcePath of plan.contentToRemove) {
50
- nextContentRecords.delete(sourcePath);
51
- }
52
- for (const sourcePath of plan.publicToRemove) {
53
- nextPublicRecords.delete(sourcePath);
54
- }
55
-
56
- for (const sourcePath of plan.contentToRender) {
57
- nextContentRecords.delete(sourcePath);
58
- const record = renderContentRecord({
59
- filePath: sourcePath,
60
- siteVariables,
61
- scan: plan.scan,
62
- assetFiles: snapshot.assetFiles,
63
- outputDir,
64
- traceCache,
65
- traceOptions,
66
- cachedTraceSourceDir: distDir,
67
- });
68
- if (record) {
69
- nextContentRecords.set(sourcePath, record);
70
- }
71
- }
72
-
73
- for (const sourcePath of plan.publicToRender) {
74
- nextPublicRecords.delete(sourcePath);
75
- const record = renderPublicRecord({
76
- filePath: sourcePath,
77
- publicDir: plan.scan.publicDir,
78
- });
79
- nextPublicRecords.set(sourcePath, record);
80
- }
81
-
82
- const nextSnapshot = createSnapshot({
83
- siteVariables,
84
- assetFiles: snapshot.assetFiles,
85
- navData: config('nav'),
86
- authorsData: config('authors'),
87
- scan: plan.scan,
88
- contentRecords: nextContentRecords,
89
- publicRecords: nextPublicRecords,
90
- });
23
+ traceToolAvailability: TraceToolAvailability;
24
+ }): Promise<CompileOutcome> {
25
+ const { siteVariables } = snapshot;
26
+ const configDiagnostics = validateConfig(plan.scan, siteVariables);
27
+ if (configDiagnostics.length > 0) {
28
+ return { ok: false, diagnostics: configDiagnostics };
29
+ }
91
30
 
92
- const linkDiagnostics = validateProjectConfigLinks(
93
- nextSnapshot.scan.validTargets,
94
- );
95
- if (linkDiagnostics.length > 0) {
96
- return buildFailedWithDiagnostics(outputDir, linkDiagnostics);
97
- }
31
+ compileTemplates(siteVariables);
32
+ await ensureHighlighter(siteVariables);
98
33
 
99
- const forceSourcePaths = new Set([
100
- ...plan.contentToRender,
101
- ...plan.publicToRender,
102
- ]);
103
- const mutations = computeMutations(
104
- snapshot,
105
- nextSnapshot,
106
- forceSourcePaths,
107
- );
108
- removeDirIfExists(outputDir);
109
- return buildSucceeded(nextSnapshot, {
110
- kind: 'apply-mutations',
111
- rootDir: distDir,
112
- mutations,
113
- });
114
- } catch (error) {
115
- removeDirIfExists(outputDir);
116
- return buildFailedFromError(error);
34
+ const records = new Map(snapshot.records);
35
+ for (const source of [...plan.removeSources, ...plan.renderSources]) {
36
+ records.delete(source);
37
+ }
38
+ const rendered = renderSources(plan.renderSources, {
39
+ siteVariables,
40
+ scan: plan.scan,
41
+ assetFiles: snapshot.assetFiles,
42
+ isWatchMode: true,
43
+ traceCache,
44
+ traceToolAvailability,
45
+ });
46
+ for (const [source, record] of rendered.records) {
47
+ records.set(source, record);
117
48
  }
49
+
50
+ const result = finishBuild({
51
+ siteVariables,
52
+ scan: plan.scan,
53
+ assetFiles: snapshot.assetFiles,
54
+ generatedOutputs: snapshot.generatedOutputs,
55
+ records,
56
+ diagnostics: rendered.diagnostics,
57
+ });
58
+ return result.ok
59
+ ? { ...result, full: false, forceSourcePaths: plan.renderSources }
60
+ : result;
118
61
  }