@abreen/tada 1.11.3 → 1.13.2

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 (79) hide show
  1. package/README.md +65 -64
  2. package/bin/tada.ts +61 -66
  3. package/build/build-manifest.ts +14 -3
  4. package/build/bundle.ts +0 -1
  5. package/build/config-files.ts +183 -0
  6. package/build/config-loader.ts +159 -0
  7. package/build/copy.ts +0 -83
  8. package/build/generate-content-assets.ts +75 -160
  9. package/build/globals.ts +48 -89
  10. package/build/log.ts +27 -18
  11. package/build/pagefind.ts +33 -26
  12. package/build/pipeline.ts +98 -79
  13. package/build/reachability.ts +51 -248
  14. package/build/serve.ts +35 -6
  15. package/build/site-variables.ts +20 -11
  16. package/build/source-model.ts +400 -0
  17. package/build/{watch/records.ts → source-records.ts} +111 -49
  18. package/build/template-globals.ts +91 -0
  19. package/build/templates.ts +50 -40
  20. package/build/test-helpers.ts +15 -0
  21. package/build/toc-plugin.ts +5 -1
  22. package/build/types.d.ts +9 -1
  23. package/build/util.ts +8 -0
  24. package/build/utils/code.ts +6 -16
  25. package/build/utils/content-files.ts +57 -126
  26. package/build/utils/final-html.ts +239 -0
  27. package/build/utils/markdown.ts +237 -17
  28. package/build/utils/paths.ts +2 -1
  29. package/build/utils/render.ts +108 -99
  30. package/build/validate-config-links.ts +91 -14
  31. package/build/watch/assets.ts +0 -2
  32. package/build/watch/build-full.ts +14 -15
  33. package/build/watch/build-incremental.ts +15 -8
  34. package/build/watch/compiler-types.d.ts +6 -1
  35. package/build/watch/compiler.ts +27 -15
  36. package/build/watch/index.ts +2 -7
  37. package/build/watch/planner.ts +32 -16
  38. package/build/watch/reload.ts +4 -0
  39. package/build/watch/runtime.ts +29 -45
  40. package/build/watch/snapshot.ts +50 -402
  41. package/build/watch/validation.ts +6 -3
  42. package/build/watch-reachability-state.ts +16 -23
  43. package/build/watch-reload-client.ts +11 -4
  44. package/init/content/index.md +1 -1
  45. package/init/content/labs/02/index.md +61 -0
  46. package/init/content/labs/02/selection_sort.py +23 -0
  47. package/init/content/labs/index.md +4 -2
  48. package/init/content/lectures/01/index.md +1 -1
  49. package/init/content/markdown.md +1 -1
  50. package/package.json +3 -4
  51. package/src/code/index.ts +46 -18
  52. package/src/code.scss +2 -1
  53. package/src/globals.ts +47 -0
  54. package/src/index.ts +1 -0
  55. package/src/navigate/index.ts +2 -5
  56. package/src/navigate/lifecycle.ts +3 -1
  57. package/src/navigate/runtime.ts +6 -5
  58. package/src/page-update/index.ts +20 -16
  59. package/src/search/index.ts +53 -25
  60. package/src/slides/index.ts +467 -0
  61. package/src/slides/style.scss +104 -0
  62. package/src/style.scss +8 -3
  63. package/src/timezone/index.ts +16 -20
  64. package/src/top/index.ts +2 -4
  65. package/src/trace/index.ts +91 -7
  66. package/src/trace/style.scss +1 -1
  67. package/src/util.ts +6 -2
  68. package/templates/_author.html +2 -2
  69. package/templates/_heading.html +1 -1
  70. package/templates/_nav.html +2 -2
  71. package/templates/_theme.scss +3 -3
  72. package/templates/_top.html +14 -14
  73. package/templates/default.html +8 -0
  74. package/types/dev.d.ts +1 -1
  75. package/types/site-variables.d.ts +4 -5
  76. package/watch/fs-commit.ts +63 -10
  77. package/build/apply-base-path-plugin.ts +0 -186
  78. package/build/content-watch.ts +0 -104
  79. package/build/validate-internal-links-plugin.ts +0 -207
@@ -1,27 +1,19 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
+ import { normalizeOutputPath } from '../util';
3
4
  import {
4
- getContentDir,
5
- getDistDir,
6
- getPublicDir,
7
- normalizeOutputPath,
8
- shouldSkipContentFile,
9
- toPosix,
10
- } from '../util';
11
- import { getExtensionToShikiLanguage } from '../site-variables';
12
- import {
13
- extensionIsMarkdown,
14
- getProcessedExtensions,
15
- isLiterateJava,
16
- isPartial,
17
- } from '../utils/file-types';
18
- import type { ChangeBatch } from '../../watch/types';
19
- import type { Asset, SiteVariables } from '../types';
5
+ type TadaProjectScan,
6
+ assertNoOutputPathConflicts,
7
+ scanProject,
8
+ updateProjectScan,
9
+ } from '../source-model';
10
+ import type { Asset, HtmlOutputAnalysis, SiteVariables } from '../types';
20
11
 
21
12
  export interface TadaSourceRecord {
22
13
  sourcePath: string;
23
14
  kind: 'content' | 'public';
24
15
  outputs: Map<string, string | Buffer>;
16
+ htmlAnalysisByOutputPath?: Map<string, HtmlOutputAnalysis>;
25
17
  partialDeps: Set<string>;
26
18
  traceDeps: Set<string>;
27
19
  internalTargets: Set<string>;
@@ -53,39 +45,6 @@ export interface TadaSnapshot {
53
45
  validTargets: Set<string>;
54
46
  }
55
47
 
56
- export interface TadaProjectScan {
57
- contentDir: string;
58
- publicDir: string;
59
- distDir: string;
60
- contentFiles: Set<string>;
61
- buildContentFiles: Set<string>;
62
- publicFiles: Set<string>;
63
- validTargets: Set<string>;
64
- literateJavaOutputPaths: Set<string>;
65
- processedExts: Set<string>;
66
- contentOwners: Map<string, string>;
67
- publicOwners: Map<string, string>;
68
- sourceOutputPaths: Map<string, Set<string>>;
69
- sourceTargetPaths: Map<string, Set<string>>;
70
- }
71
-
72
- function walkFiles(dir: string): string[] {
73
- if (!fs.existsSync(dir)) {
74
- return [];
75
- }
76
-
77
- return fs.readdirSync(dir, { withFileTypes: true }).flatMap(entry => {
78
- const fullPath = path.join(dir, entry.name);
79
- if (entry.isDirectory()) {
80
- return walkFiles(fullPath);
81
- }
82
- if (entry.isFile()) {
83
- return [fullPath];
84
- }
85
- return [];
86
- });
87
- }
88
-
89
48
  function buildReverseMap(
90
49
  records: Iterable<TadaSourceRecord>,
91
50
  selector: (record: TadaSourceRecord) => Iterable<string>,
@@ -104,139 +63,6 @@ function buildReverseMap(
104
63
  return reverse;
105
64
  }
106
65
 
107
- function getProcessedExts(siteVariables: SiteVariables): Set<string> {
108
- return new Set(
109
- getProcessedExtensions(
110
- Object.keys(getExtensionToShikiLanguage(siteVariables)),
111
- ).map(ext => ext.toLowerCase()),
112
- );
113
- }
114
-
115
- function isBuildContentSource(
116
- filePath: string,
117
- processedExts: Set<string>,
118
- ): boolean {
119
- const ext = path.extname(filePath).slice(1).toLowerCase();
120
- if (!processedExts.has(ext) || isPartial(filePath)) {
121
- return false;
122
- }
123
- return !shouldSkipContentFile(filePath);
124
- }
125
-
126
- function addGeneratedRouteAliases(
127
- targets: Set<string>,
128
- outputPath: string,
129
- ): void {
130
- const normalizedPath = normalizeOutputPath(outputPath);
131
- targets.add(normalizedPath);
132
- if (!normalizedPath.endsWith('/index.html')) {
133
- return;
134
- }
135
- const base = normalizedPath.slice(0, -'index.html'.length);
136
- targets.add(base);
137
- if (base.endsWith('/') && base.length > 1) {
138
- targets.add(base.slice(0, -1));
139
- }
140
- }
141
-
142
- function getContentOutputPathsForSource({
143
- contentDir,
144
- filePath,
145
- processedExts,
146
- buildContent,
147
- }: {
148
- contentDir: string;
149
- filePath: string;
150
- processedExts: Set<string>;
151
- buildContent: boolean;
152
- }): Set<string> {
153
- const relPath = toPosix(path.relative(contentDir, filePath));
154
- const parsed = path.parse(relPath);
155
- const ext = path.extname(filePath).slice(1).toLowerCase();
156
- const outputs = new Set<string>();
157
-
158
- if (!processedExts.has(ext)) {
159
- outputs.add(relPath);
160
- return outputs;
161
- }
162
-
163
- if (!buildContent) {
164
- return outputs;
165
- }
166
-
167
- if (isLiterateJava(filePath)) {
168
- outputs.add(toPosix(path.join(parsed.dir, `${parsed.name}.html`)));
169
- outputs.add(toPosix(path.join(parsed.dir, parsed.name)));
170
- return outputs;
171
- }
172
-
173
- if (
174
- extensionIsMarkdown(parsed.ext.toLowerCase()) ||
175
- parsed.ext.toLowerCase() === '.html'
176
- ) {
177
- outputs.add(toPosix(path.join(parsed.dir, `${parsed.name}.html`)));
178
- return outputs;
179
- }
180
-
181
- outputs.add(relPath);
182
- outputs.add(`${relPath}.html`);
183
- return outputs;
184
- }
185
-
186
- function getTargetPathsForSource({
187
- kind,
188
- rootDir,
189
- filePath,
190
- processedExts,
191
- buildContent,
192
- }: {
193
- kind: 'content' | 'public';
194
- rootDir: string;
195
- filePath: string;
196
- processedExts: Set<string>;
197
- buildContent: boolean;
198
- }): Set<string> {
199
- const targets = new Set<string>();
200
- const relPath = toPosix(path.relative(rootDir, filePath));
201
-
202
- if (kind === 'public') {
203
- targets.add(normalizeOutputPath(`/${relPath}`));
204
- return targets;
205
- }
206
-
207
- const ext = path.extname(filePath).slice(1).toLowerCase();
208
- if (!processedExts.has(ext)) {
209
- targets.add(normalizeOutputPath(`/${relPath}`));
210
- return targets;
211
- }
212
-
213
- if (!buildContent || isPartial(filePath)) {
214
- return targets;
215
- }
216
-
217
- const parsed = path.parse(relPath);
218
- const subPath = toPosix(path.join(parsed.dir, parsed.name));
219
-
220
- if (isLiterateJava(filePath)) {
221
- const javaSubPath = toPosix(path.join(parsed.dir, parsed.name));
222
- addGeneratedRouteAliases(targets, `/${javaSubPath}.html`);
223
- targets.add(normalizeOutputPath(`/${javaSubPath}`));
224
- return targets;
225
- }
226
-
227
- if (
228
- extensionIsMarkdown(parsed.ext.toLowerCase()) ||
229
- parsed.ext.toLowerCase() === '.html'
230
- ) {
231
- addGeneratedRouteAliases(targets, `/${subPath}.html`);
232
- return targets;
233
- }
234
-
235
- addGeneratedRouteAliases(targets, `/${relPath}.html`);
236
- targets.add(normalizeOutputPath(`/${relPath}`));
237
- return targets;
238
- }
239
-
240
66
  function cloneSetMap(
241
67
  source: Map<string, Set<string>>,
242
68
  ): Map<string, Set<string>> {
@@ -245,219 +71,6 @@ function cloneSetMap(
245
71
  );
246
72
  }
247
73
 
248
- export function scanProject(siteVariables: SiteVariables): TadaProjectScan {
249
- const contentDir = getContentDir();
250
- const publicDir = getPublicDir();
251
- const distDir = getDistDir();
252
- const processedExts = getProcessedExts(siteVariables);
253
- const contentFiles = new Set(walkFiles(contentDir).sort());
254
- const publicFiles = new Set(walkFiles(publicDir).sort());
255
- const buildContentFiles = new Set<string>();
256
- const literateJavaOutputPaths = new Set<string>();
257
- const contentOwners = new Map<string, string>();
258
- const publicOwners = new Map<string, string>();
259
- const sourceOutputPaths = new Map<string, Set<string>>();
260
- const sourceTargetPaths = new Map<string, Set<string>>();
261
- const validTargets = new Set<string>();
262
-
263
- for (const filePath of contentFiles) {
264
- const buildContent = isBuildContentSource(filePath, processedExts);
265
- if (buildContent) {
266
- buildContentFiles.add(filePath);
267
- }
268
- if (buildContent && isLiterateJava(filePath)) {
269
- const parsed = path.parse(path.relative(contentDir, filePath));
270
- literateJavaOutputPaths.add(
271
- `/${toPosix(path.join(parsed.dir, parsed.name))}`,
272
- );
273
- }
274
-
275
- const outputs = getContentOutputPathsForSource({
276
- contentDir,
277
- filePath,
278
- processedExts,
279
- buildContent,
280
- });
281
- sourceOutputPaths.set(filePath, outputs);
282
- for (const outputPath of outputs) {
283
- contentOwners.set(outputPath, filePath);
284
- }
285
-
286
- const targets = getTargetPathsForSource({
287
- kind: 'content',
288
- rootDir: contentDir,
289
- filePath,
290
- processedExts,
291
- buildContent,
292
- });
293
- sourceTargetPaths.set(filePath, targets);
294
- for (const target of targets) {
295
- validTargets.add(target);
296
- }
297
- }
298
-
299
- for (const filePath of publicFiles) {
300
- const relPath = toPosix(path.relative(publicDir, filePath));
301
- publicOwners.set(relPath, filePath);
302
- sourceOutputPaths.set(filePath, new Set([relPath]));
303
- const targets = getTargetPathsForSource({
304
- kind: 'public',
305
- rootDir: publicDir,
306
- filePath,
307
- processedExts,
308
- buildContent: false,
309
- });
310
- sourceTargetPaths.set(filePath, targets);
311
- for (const target of targets) {
312
- validTargets.add(target);
313
- }
314
- }
315
-
316
- return {
317
- contentDir,
318
- publicDir,
319
- distDir,
320
- contentFiles,
321
- buildContentFiles,
322
- publicFiles,
323
- validTargets,
324
- literateJavaOutputPaths,
325
- processedExts,
326
- contentOwners,
327
- publicOwners,
328
- sourceOutputPaths,
329
- sourceTargetPaths,
330
- };
331
- }
332
-
333
- export function updateProjectScan(
334
- snapshot: TadaSnapshot,
335
- batch: ChangeBatch,
336
- ): TadaProjectScan {
337
- const contentDir = getContentDir();
338
- const publicDir = getPublicDir();
339
- const distDir = getDistDir();
340
- const contentFiles = new Set(snapshot.contentFiles);
341
- const buildContentFiles = new Set(snapshot.buildContentFiles);
342
- const publicFiles = new Set(snapshot.publicFiles);
343
- const literateJavaOutputPaths = new Set(snapshot.literateJavaOutputPaths);
344
- const contentOwners = new Map(snapshot.contentOwners);
345
- const publicOwners = new Map(snapshot.publicOwners);
346
- const sourceOutputPaths = cloneSetMap(snapshot.sourceOutputPaths);
347
- const sourceTargetPaths = cloneSetMap(snapshot.sourceTargetPaths);
348
-
349
- for (const change of batch.changes) {
350
- const sourcePath = path.resolve(change.path);
351
- const inContent = sourcePath.startsWith(`${contentDir}${path.sep}`);
352
- const inPublic = sourcePath.startsWith(`${publicDir}${path.sep}`);
353
- if (!inContent && !inPublic) {
354
- continue;
355
- }
356
-
357
- for (const outputPath of sourceOutputPaths.get(sourcePath) || []) {
358
- if (inContent) {
359
- contentOwners.delete(outputPath);
360
- } else {
361
- publicOwners.delete(outputPath);
362
- }
363
- }
364
- sourceOutputPaths.delete(sourcePath);
365
- sourceTargetPaths.delete(sourcePath);
366
- contentFiles.delete(sourcePath);
367
- buildContentFiles.delete(sourcePath);
368
- publicFiles.delete(sourcePath);
369
-
370
- if (inContent && isLiterateJava(sourcePath)) {
371
- const parsed = path.parse(path.relative(contentDir, sourcePath));
372
- literateJavaOutputPaths.delete(
373
- `/${toPosix(path.join(parsed.dir, parsed.name))}`,
374
- );
375
- }
376
-
377
- if (!fs.existsSync(sourcePath) || !fs.statSync(sourcePath).isFile()) {
378
- continue;
379
- }
380
-
381
- if (inContent) {
382
- const buildContent = isBuildContentSource(
383
- sourcePath,
384
- snapshot.processedExts,
385
- );
386
- contentFiles.add(sourcePath);
387
- if (buildContent) {
388
- buildContentFiles.add(sourcePath);
389
- }
390
- if (buildContent && isLiterateJava(sourcePath)) {
391
- const parsed = path.parse(path.relative(contentDir, sourcePath));
392
- literateJavaOutputPaths.add(
393
- `/${toPosix(path.join(parsed.dir, parsed.name))}`,
394
- );
395
- }
396
-
397
- const outputs = getContentOutputPathsForSource({
398
- contentDir,
399
- filePath: sourcePath,
400
- processedExts: snapshot.processedExts,
401
- buildContent,
402
- });
403
- sourceOutputPaths.set(sourcePath, outputs);
404
- for (const outputPath of outputs) {
405
- contentOwners.set(outputPath, sourcePath);
406
- }
407
-
408
- sourceTargetPaths.set(
409
- sourcePath,
410
- getTargetPathsForSource({
411
- kind: 'content',
412
- rootDir: contentDir,
413
- filePath: sourcePath,
414
- processedExts: snapshot.processedExts,
415
- buildContent,
416
- }),
417
- );
418
- continue;
419
- }
420
-
421
- const relPath = toPosix(path.relative(publicDir, sourcePath));
422
- publicFiles.add(sourcePath);
423
- publicOwners.set(relPath, sourcePath);
424
- sourceOutputPaths.set(sourcePath, new Set([relPath]));
425
- sourceTargetPaths.set(
426
- sourcePath,
427
- getTargetPathsForSource({
428
- kind: 'public',
429
- rootDir: publicDir,
430
- filePath: sourcePath,
431
- processedExts: snapshot.processedExts,
432
- buildContent: false,
433
- }),
434
- );
435
- }
436
-
437
- const validTargets = new Set<string>();
438
- for (const targets of sourceTargetPaths.values()) {
439
- for (const target of targets) {
440
- validTargets.add(target);
441
- }
442
- }
443
-
444
- return {
445
- contentDir,
446
- publicDir,
447
- distDir,
448
- contentFiles,
449
- buildContentFiles,
450
- publicFiles,
451
- validTargets,
452
- literateJavaOutputPaths,
453
- processedExts: snapshot.processedExts,
454
- contentOwners,
455
- publicOwners,
456
- sourceOutputPaths,
457
- sourceTargetPaths,
458
- };
459
- }
460
-
461
74
  export function collectOutputOwners(snapshot: {
462
75
  contentRecords: Map<string, TadaSourceRecord>;
463
76
  publicRecords: Map<string, TadaSourceRecord>;
@@ -545,13 +158,6 @@ export function createSnapshot({
545
158
  };
546
159
  }
547
160
 
548
- export function assertNoOutputPathConflicts(scan: TadaProjectScan): string[] {
549
- const conflicts = [...scan.contentOwners.keys()].filter(relPath =>
550
- scan.publicOwners.has(relPath),
551
- );
552
- return conflicts.sort();
553
- }
554
-
555
161
  export function collectSourceOutputs(
556
162
  assets: Asset[],
557
163
  generatedOutputPaths: Set<string>,
@@ -567,6 +173,28 @@ export function collectSourceOutputs(
567
173
  return outputs;
568
174
  }
569
175
 
176
+ function cloneHtmlOutputAnalysis(
177
+ analysis: HtmlOutputAnalysis,
178
+ ): HtmlOutputAnalysis {
179
+ return { outgoingTargets: new Set(analysis.outgoingTargets) };
180
+ }
181
+
182
+ export function collectSourceHtmlAnalysis(
183
+ assets: Asset[],
184
+ ): Map<string, HtmlOutputAnalysis> {
185
+ const htmlAnalysisByOutputPath = new Map<string, HtmlOutputAnalysis>();
186
+ for (const asset of assets) {
187
+ if (!asset.assetPath.endsWith('.html') || !asset.htmlAnalysis) {
188
+ continue;
189
+ }
190
+ htmlAnalysisByOutputPath.set(
191
+ asset.assetPath,
192
+ cloneHtmlOutputAnalysis(asset.htmlAnalysis),
193
+ );
194
+ }
195
+ return htmlAnalysisByOutputPath;
196
+ }
197
+
570
198
  export function collectHtmlAssetsByPath(
571
199
  contentRecords: Map<string, TadaSourceRecord>,
572
200
  ): Map<string, string> {
@@ -581,6 +209,26 @@ export function collectHtmlAssetsByPath(
581
209
  return htmlAssetsByPath;
582
210
  }
583
211
 
212
+ export function collectHtmlAnalysisByPath(
213
+ contentRecords: Map<string, TadaSourceRecord>,
214
+ ): Map<string, HtmlOutputAnalysis> {
215
+ const htmlAnalysisByPath = new Map<string, HtmlOutputAnalysis>();
216
+ for (const record of contentRecords.values()) {
217
+ for (const [outputPath, analysis] of record.htmlAnalysisByOutputPath ||
218
+ new Map<string, HtmlOutputAnalysis>()) {
219
+ htmlAnalysisByPath.set(outputPath, cloneHtmlOutputAnalysis(analysis));
220
+ }
221
+ }
222
+ return htmlAnalysisByPath;
223
+ }
224
+
584
225
  export function normalizeInternalTarget(target: string): string {
585
226
  return normalizeOutputPath(target);
586
227
  }
228
+
229
+ export {
230
+ assertNoOutputPathConflicts,
231
+ scanProject,
232
+ updateProjectScan,
233
+ type TadaProjectScan,
234
+ };
@@ -1,6 +1,6 @@
1
1
  import { B } from '../colors';
2
2
  import { makeLogger } from '../log';
3
- import { json } from '../templates';
3
+ import { config, getConfigFileName } from '../templates';
4
4
  import { validateConfigLinks } from '../validate-config-links';
5
5
  import type { WatchDiagnostic } from '../../watch/types';
6
6
  import { assertNoOutputPathConflicts, type TadaProjectScan } from './snapshot';
@@ -27,10 +27,13 @@ export function validateConfig(scan: TadaProjectScan): WatchDiagnostic[] {
27
27
  ];
28
28
  }
29
29
 
30
- export function validateJsonLinks(
30
+ export function validateProjectConfigLinks(
31
31
  validTargets: Set<string>,
32
32
  ): WatchDiagnostic[] {
33
33
  return diagnosticsFromMessages(
34
- validateConfigLinks(validTargets, json('nav.json'), json('authors.json')),
34
+ validateConfigLinks(validTargets, config('nav'), config('authors'), {
35
+ navFileName: getConfigFileName('nav'),
36
+ authorsFileName: getConfigFileName('authors'),
37
+ }),
35
38
  );
36
39
  }
@@ -1,6 +1,7 @@
1
- import { collectDirectSiteAssetLinks } from './reachability';
1
+ import { collectOutgoingHtmlAssetPaths } from './reachability';
2
+ import type { HtmlOutputAnalysis } from './types';
2
3
 
3
- type ReadHtmlForAsset = (assetPath: string) => string;
4
+ type ReadAnalysisForAsset = (assetPath: string) => HtmlOutputAnalysis;
4
5
 
5
6
  function cloneSet(values: Iterable<string> | undefined): Set<string> {
6
7
  return new Set(values || []);
@@ -9,24 +10,19 @@ function cloneSet(values: Iterable<string> | undefined): Set<string> {
9
10
  interface IncrementalOptions {
10
11
  changedAssetPaths?: Iterable<string>;
11
12
  removedAssetPaths?: Iterable<string>;
12
- readHtmlForAsset: ReadHtmlForAsset;
13
+ readAnalysisForAsset: ReadAnalysisForAsset;
13
14
  }
14
15
 
15
16
  class WatchReachabilityState {
16
17
  private rootPath: string;
17
- private basePath: string;
18
18
  private knownAssets: Set<string>;
19
19
  private outgoingBySource: Map<string, Set<string>>;
20
20
  private incomingByTarget: Map<string, Set<string>>;
21
21
  private reachable: Set<string>;
22
22
  private initialized: boolean;
23
23
 
24
- constructor({
25
- rootPath = 'index.html',
26
- basePath = '/',
27
- }: { rootPath?: string; basePath?: string } = {}) {
24
+ constructor({ rootPath = 'index.html' }: { rootPath?: string } = {}) {
28
25
  this.rootPath = rootPath;
29
- this.basePath = basePath;
30
26
  this.knownAssets = new Set();
31
27
  this.outgoingBySource = new Map();
32
28
  this.incomingByTarget = new Map();
@@ -66,7 +62,7 @@ class WatchReachabilityState {
66
62
  this.rebuildIncomingByTarget();
67
63
  }
68
64
 
69
- rebuild(readHtmlForAsset: ReadHtmlForAsset): void {
65
+ rebuild(readAnalysisForAsset: ReadAnalysisForAsset): void {
70
66
  if (!this.knownAssets.has(this.rootPath)) {
71
67
  throw new Error(`Pagefind reachability root not found: ${this.rootPath}`);
72
68
  }
@@ -79,7 +75,7 @@ class WatchReachabilityState {
79
75
  for (const sourcePath of knownAssetPaths) {
80
76
  const outgoingPaths = this.readOutgoingPaths(
81
77
  sourcePath,
82
- readHtmlForAsset,
78
+ readAnalysisForAsset,
83
79
  );
84
80
  this.outgoingBySource.set(sourcePath, outgoingPaths);
85
81
  this.addIncomingEdges(sourcePath, outgoingPaths);
@@ -92,10 +88,10 @@ class WatchReachabilityState {
92
88
  applyIncremental({
93
89
  changedAssetPaths,
94
90
  removedAssetPaths,
95
- readHtmlForAsset,
91
+ readAnalysisForAsset,
96
92
  }: IncrementalOptions): void {
97
93
  if (!this.initialized) {
98
- this.rebuild(readHtmlForAsset);
94
+ this.rebuild(readAnalysisForAsset);
99
95
  return;
100
96
  }
101
97
 
@@ -136,7 +132,10 @@ class WatchReachabilityState {
136
132
 
137
133
  const changedAssetList = [...normalizedChanged].sort();
138
134
  for (const assetPath of changedAssetList) {
139
- const outgoingPaths = this.readOutgoingPaths(assetPath, readHtmlForAsset);
135
+ const outgoingPaths = this.readOutgoingPaths(
136
+ assetPath,
137
+ readAnalysisForAsset,
138
+ );
140
139
  this.replaceOutgoing(assetPath, outgoingPaths);
141
140
  }
142
141
 
@@ -196,20 +195,14 @@ class WatchReachabilityState {
196
195
 
197
196
  private readOutgoingPaths(
198
197
  sourcePath: string,
199
- readHtmlForAsset: ReadHtmlForAsset,
198
+ readAnalysisForAsset: ReadAnalysisForAsset,
200
199
  ): Set<string> {
201
200
  if (!this.knownAssets.has(sourcePath)) {
202
201
  return new Set();
203
202
  }
204
203
 
205
- const html = readHtmlForAsset(sourcePath);
206
- const { htmlAssetPaths } = collectDirectSiteAssetLinks({
207
- html,
208
- fromAssetPath: sourcePath,
209
- knownAssets: this.knownAssets,
210
- basePath: this.basePath,
211
- });
212
- return new Set(htmlAssetPaths);
204
+ const analysis = readAnalysisForAsset(sourcePath);
205
+ return new Set(collectOutgoingHtmlAssetPaths(analysis, this.knownAssets));
213
206
  }
214
207
 
215
208
  private addIncomingEdges(
@@ -1,4 +1,8 @@
1
- declare const __WEBSOCKET_PORT__: number;
1
+ import {
2
+ WATCH_RELOAD_MESSAGE_REBUILDING,
3
+ WATCH_RELOAD_MESSAGE_RELOAD,
4
+ WATCH_RELOAD_PATH,
5
+ } from './watch/reload';
2
6
 
3
7
  (function () {
4
8
  function setLoading(loading: boolean): void {
@@ -16,17 +20,20 @@ body.loading-cursor * {
16
20
  }`;
17
21
  document.head.appendChild(style);
18
22
 
19
- const ws = new WebSocket(`ws://localhost:${__WEBSOCKET_PORT__}`);
23
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
24
+ const ws = new WebSocket(
25
+ `${protocol}//${window.location.host}${WATCH_RELOAD_PATH}`,
26
+ );
20
27
 
21
28
  ws.onopen = () => {
22
29
  console.log('[watch-reload] connected to watcher');
23
30
  };
24
31
 
25
32
  ws.onmessage = event => {
26
- if (event.data === 'rebuilding') {
33
+ if (event.data === WATCH_RELOAD_MESSAGE_REBUILDING) {
27
34
  console.log('[watch-reload] Rebuilding...');
28
35
  setLoading(true);
29
- } else if (event.data === 'reload') {
36
+ } else if (event.data === WATCH_RELOAD_MESSAGE_RELOAD) {
30
37
  console.log('[watch-reload] Reloading page...');
31
38
  setLoading(false);
32
39
  window.history.scrollRestoration = 'auto';
@@ -14,7 +14,7 @@ This is an example site built with <%= page.toolName %>.
14
14
  ## Getting started
15
15
 
16
16
  Edit the files in `content/` to add your own pages. To configure the nav,
17
- update `nav.json`. Update `authors.json` with your information, or delete it
17
+ update `nav.yaml`. Update `authors.yaml` with your information, or delete it
18
18
  if you won't use the `author` front matter field.
19
19
 
20
20
  See the [Markdown examples page](/markdown.html) for syntax examples.