@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/watch/planner.ts
CHANGED
|
@@ -1,182 +1,126 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import type { ChangeBatch } from './types';
|
|
5
|
-
import type { TadaProjectScan, TadaSnapshot } from './snapshot';
|
|
6
|
-
import { classifyWatchConfigPath } from './config-paths';
|
|
2
|
+
import type { SourceEntry, TadaProjectScan } from '../source-model';
|
|
3
|
+
import type { TadaSnapshot } from '../site-build';
|
|
7
4
|
|
|
8
5
|
export interface TadaIncrementalWatchPlan {
|
|
9
6
|
kind: 'incremental';
|
|
10
7
|
scan: TadaProjectScan;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
contentToRemove: Set<string>;
|
|
14
|
-
publicToRemove: Set<string>;
|
|
8
|
+
renderSources: Set<string>;
|
|
9
|
+
removeSources: Set<string>;
|
|
15
10
|
}
|
|
16
11
|
|
|
17
12
|
type TadaWatchPlan = { kind: 'full' } | TadaIncrementalWatchPlan;
|
|
18
13
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
previous && typeof previous === 'object'
|
|
22
|
-
? (previous as Record<string, unknown>)
|
|
23
|
-
: {};
|
|
24
|
-
const nextMap =
|
|
25
|
-
next && typeof next === 'object' ? (next as Record<string, unknown>) : {};
|
|
26
|
-
const keys = new Set([...Object.keys(previousMap), ...Object.keys(nextMap)]);
|
|
27
|
-
const changed = new Set<string>();
|
|
28
|
-
|
|
29
|
-
for (const key of keys) {
|
|
30
|
-
if (JSON.stringify(previousMap[key]) !== JSON.stringify(nextMap[key])) {
|
|
31
|
-
changed.add(key);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return changed;
|
|
14
|
+
function sameItems(a: ReadonlySet<string>, b: ReadonlySet<string>): boolean {
|
|
15
|
+
return a.size === b.size && [...a].every(item => b.has(item));
|
|
36
16
|
}
|
|
37
17
|
|
|
38
|
-
function
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
for (const dependent of dependents) {
|
|
48
|
-
destination.add(dependent);
|
|
49
|
-
}
|
|
18
|
+
function sameSourceEntry(a: SourceEntry, b: SourceEntry | undefined): boolean {
|
|
19
|
+
return (
|
|
20
|
+
b !== undefined &&
|
|
21
|
+
a.kind === b.kind &&
|
|
22
|
+
a.renderKind === b.renderKind &&
|
|
23
|
+
sameItems(a.outputs, b.outputs) &&
|
|
24
|
+
sameItems(a.targets, b.targets)
|
|
25
|
+
);
|
|
50
26
|
}
|
|
51
27
|
|
|
52
|
-
function
|
|
53
|
-
|
|
28
|
+
export function diffAuthorKeys(previous: unknown, next: unknown): Set<string> {
|
|
29
|
+
const asMap = (value: unknown): Record<string, unknown> =>
|
|
30
|
+
value && typeof value === 'object'
|
|
31
|
+
? (value as Record<string, unknown>)
|
|
32
|
+
: {};
|
|
33
|
+
const before = asMap(previous);
|
|
34
|
+
const after = asMap(next);
|
|
35
|
+
return new Set(
|
|
36
|
+
[...Object.keys(before), ...Object.keys(after)].filter(
|
|
37
|
+
key => JSON.stringify(before[key]) !== JSON.stringify(after[key]),
|
|
38
|
+
),
|
|
39
|
+
);
|
|
54
40
|
}
|
|
55
41
|
|
|
56
42
|
export function createTadaWatchPlan({
|
|
57
43
|
snapshot,
|
|
58
|
-
|
|
44
|
+
paths,
|
|
59
45
|
scan,
|
|
46
|
+
full = false,
|
|
47
|
+
changedAuthors = new Set<string>(),
|
|
60
48
|
}: {
|
|
61
49
|
snapshot: TadaSnapshot;
|
|
62
|
-
|
|
50
|
+
paths: ReadonlySet<string>;
|
|
63
51
|
scan: TadaProjectScan;
|
|
52
|
+
full?: boolean;
|
|
53
|
+
changedAuthors?: ReadonlySet<string>;
|
|
64
54
|
}): TadaWatchPlan {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const contentToRemove = new Set<string>();
|
|
68
|
-
const publicToRemove = new Set<string>();
|
|
69
|
-
const projectConfigDir = getProjectConfigDir();
|
|
70
|
-
|
|
71
|
-
for (const change of batch.changes) {
|
|
72
|
-
const configKind = classifyWatchConfigPath(change.path);
|
|
73
|
-
if (configKind === 'site' || configKind === 'nav') {
|
|
74
|
-
return { kind: 'full' };
|
|
75
|
-
}
|
|
55
|
+
if (full) {
|
|
56
|
+
return { kind: 'full' };
|
|
76
57
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return { kind: 'full' };
|
|
88
|
-
}
|
|
89
|
-
const changedAuthorKeys = diffAuthorKeys(
|
|
90
|
-
snapshot.authorsData,
|
|
91
|
-
nextAuthorsData,
|
|
92
|
-
);
|
|
93
|
-
for (const key of changedAuthorKeys) {
|
|
94
|
-
addDependents(contentToRender, snapshot.reverseAuthorDeps, key);
|
|
95
|
-
}
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (scan.contentFiles.has(resolvedPath)) {
|
|
100
|
-
contentToRender.add(resolvedPath);
|
|
101
|
-
} else if (snapshot.contentRecords.has(resolvedPath)) {
|
|
102
|
-
contentToRemove.add(resolvedPath);
|
|
58
|
+
const renderSources = new Set<string>();
|
|
59
|
+
const removeSources = new Set(
|
|
60
|
+
[...snapshot.records.keys()].filter(source => !scan.sources.has(source)),
|
|
61
|
+
);
|
|
62
|
+
const include = (
|
|
63
|
+
index: ReadonlyMap<string, ReadonlySet<string>>,
|
|
64
|
+
key: string,
|
|
65
|
+
) => {
|
|
66
|
+
for (const source of index.get(key) ?? []) {
|
|
67
|
+
renderSources.add(source);
|
|
103
68
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
69
|
+
};
|
|
70
|
+
const changedSources = new Set([...paths, ...removeSources]);
|
|
71
|
+
for (const source of snapshot.scan.sources.keys()) {
|
|
72
|
+
if (!scan.sources.has(source)) {
|
|
73
|
+
changedSources.add(source);
|
|
109
74
|
}
|
|
110
|
-
|
|
111
|
-
addDependents(contentToRender, snapshot.reversePartialDeps, resolvedPath);
|
|
112
|
-
addDependents(contentToRender, snapshot.reverseTraceDeps, resolvedPath);
|
|
113
75
|
}
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
for (const
|
|
117
|
-
|
|
118
|
-
|
|
76
|
+
// A directory event may arrive without events for the files inside it.
|
|
77
|
+
const changedDirPrefixes = [...paths].map(changed => changed + path.sep);
|
|
78
|
+
for (const [source, entry] of scan.sources) {
|
|
79
|
+
const inChangedDirectory = changedDirPrefixes.some(prefix =>
|
|
80
|
+
source.startsWith(prefix),
|
|
81
|
+
);
|
|
82
|
+
if (
|
|
83
|
+
inChangedDirectory ||
|
|
84
|
+
!sameSourceEntry(entry, snapshot.scan.sources.get(source))
|
|
85
|
+
) {
|
|
86
|
+
changedSources.add(source);
|
|
119
87
|
}
|
|
120
88
|
}
|
|
121
|
-
for (const
|
|
122
|
-
if (
|
|
123
|
-
|
|
89
|
+
for (const source of changedSources) {
|
|
90
|
+
if (scan.sources.has(source)) {
|
|
91
|
+
renderSources.add(source);
|
|
124
92
|
}
|
|
93
|
+
include(snapshot.fileDependents, source);
|
|
125
94
|
}
|
|
126
|
-
for (const
|
|
127
|
-
|
|
95
|
+
for (const key of changedAuthors) {
|
|
96
|
+
include(snapshot.authorDependents, key);
|
|
128
97
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
if (owner.kind !== nextKind || owner.sourcePath !== nextSourcePath) {
|
|
144
|
-
if (nextKind === 'content') {
|
|
145
|
-
contentToRender.add(nextSourcePath);
|
|
146
|
-
} else {
|
|
147
|
-
publicToRender.add(nextSourcePath);
|
|
148
|
-
}
|
|
98
|
+
for (const target of new Set([
|
|
99
|
+
...snapshot.scan.validTargets,
|
|
100
|
+
...scan.validTargets,
|
|
101
|
+
...snapshot.scan.generatedPageTargets,
|
|
102
|
+
...scan.generatedPageTargets,
|
|
103
|
+
])) {
|
|
104
|
+
if (
|
|
105
|
+
snapshot.scan.validTargets.has(target) !==
|
|
106
|
+
scan.validTargets.has(target) ||
|
|
107
|
+
snapshot.scan.generatedPageTargets.has(target) !==
|
|
108
|
+
scan.generatedPageTargets.has(target)
|
|
109
|
+
) {
|
|
110
|
+
include(snapshot.targetDependents, target);
|
|
149
111
|
}
|
|
150
112
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
for (const [outputPath, nextSourcePath] of scan.publicOwners) {
|
|
158
|
-
if (!snapshot.outputOwners.has(outputPath)) {
|
|
159
|
-
publicToRender.add(nextSourcePath);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
for (const sourcePath of [...contentToRender]) {
|
|
164
|
-
if (!sourceExists(sourcePath, scan)) {
|
|
165
|
-
contentToRender.delete(sourcePath);
|
|
113
|
+
for (const [output, producers] of scan.outputProducers) {
|
|
114
|
+
for (const source of producers) {
|
|
115
|
+
if (snapshot.outputs.get(output)?.sourcePath !== source) {
|
|
116
|
+
renderSources.add(source);
|
|
117
|
+
}
|
|
166
118
|
}
|
|
167
119
|
}
|
|
168
|
-
for (const
|
|
169
|
-
if (!
|
|
170
|
-
|
|
120
|
+
for (const source of renderSources) {
|
|
121
|
+
if (!scan.sources.has(source)) {
|
|
122
|
+
renderSources.delete(source);
|
|
171
123
|
}
|
|
172
124
|
}
|
|
173
|
-
|
|
174
|
-
return {
|
|
175
|
-
kind: 'incremental',
|
|
176
|
-
scan,
|
|
177
|
-
contentToRender,
|
|
178
|
-
publicToRender,
|
|
179
|
-
contentToRemove,
|
|
180
|
-
publicToRemove,
|
|
181
|
-
};
|
|
125
|
+
return { kind: 'incremental', scan, renderSources, removeSources };
|
|
182
126
|
}
|
package/build/watch/runtime.ts
CHANGED
|
@@ -3,7 +3,10 @@ import { B } from '../colors';
|
|
|
3
3
|
import { makeLogger, printFlair } from '../log';
|
|
4
4
|
import { startServer } from '../serve';
|
|
5
5
|
import { WatchPagefindRunner } from '../pagefind';
|
|
6
|
+
import { isFeatureEnabled } from '../features';
|
|
7
|
+
import { printDiagnostics } from '../build-validation';
|
|
6
8
|
import type { WatchLifecycleEvent } from './types';
|
|
9
|
+
import type { TadaBuildMeta } from '../build-types';
|
|
7
10
|
import {
|
|
8
11
|
WATCH_RELOAD_MESSAGE_REBUILDING,
|
|
9
12
|
WATCH_RELOAD_MESSAGE_RELOAD,
|
|
@@ -14,28 +17,16 @@ import {
|
|
|
14
17
|
const log = makeLogger(import.meta.url);
|
|
15
18
|
const wslog = makeLogger('WebSocket');
|
|
16
19
|
|
|
17
|
-
function describeChange(kind: 'add' | 'change' | 'unlink'): string {
|
|
18
|
-
if (kind === 'add') {
|
|
19
|
-
return 'added, rebuilding';
|
|
20
|
-
}
|
|
21
|
-
if (kind === 'unlink') {
|
|
22
|
-
return 'removed, rebuilding';
|
|
23
|
-
}
|
|
24
|
-
return 'changed, rebuilding';
|
|
25
|
-
}
|
|
26
|
-
|
|
27
20
|
export class TadaWatchRuntime {
|
|
28
21
|
private httpPort: number | undefined;
|
|
29
22
|
private distDir: string;
|
|
30
23
|
private server: Bun.Server<undefined> | null;
|
|
31
24
|
private pagefindRunner: WatchPagefindRunner | undefined;
|
|
32
|
-
private pagefindSiteVariablesKey: string | undefined;
|
|
33
25
|
|
|
34
26
|
constructor({ httpPort, distDir }: { httpPort?: number; distDir: string }) {
|
|
35
27
|
this.httpPort = httpPort;
|
|
36
28
|
this.distDir = distDir;
|
|
37
29
|
this.server = null;
|
|
38
|
-
this.pagefindSiteVariablesKey = undefined;
|
|
39
30
|
}
|
|
40
31
|
|
|
41
32
|
private broadcast(message: string): void {
|
|
@@ -64,48 +55,44 @@ export class TadaWatchRuntime {
|
|
|
64
55
|
wslog.debug`WebSocket server listening at ws://localhost:${this.server.port}${WATCH_RELOAD_PATH}`;
|
|
65
56
|
}
|
|
66
57
|
|
|
67
|
-
|
|
58
|
+
close(): void {
|
|
59
|
+
this.server?.stop(true);
|
|
60
|
+
this.server = null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async onEvent(event: WatchLifecycleEvent<TadaBuildMeta>): Promise<void> {
|
|
68
64
|
switch (event.kind) {
|
|
69
65
|
case 'build-started':
|
|
70
|
-
if (!event.
|
|
66
|
+
if (!event.paths) {
|
|
71
67
|
return;
|
|
72
68
|
}
|
|
73
|
-
for (const
|
|
74
|
-
log.event`${B`${path.basename(
|
|
69
|
+
for (const filePath of event.paths) {
|
|
70
|
+
log.event`${B`${path.basename(filePath)}`} changed, rebuilding`;
|
|
75
71
|
}
|
|
76
72
|
this.broadcast(WATCH_RELOAD_MESSAGE_REBUILDING);
|
|
77
73
|
return;
|
|
78
74
|
case 'build-succeeded': {
|
|
79
75
|
printFlair();
|
|
80
76
|
this.ensureServerStarted();
|
|
81
|
-
if (event.
|
|
77
|
+
if (event.paths) {
|
|
82
78
|
this.broadcast(WATCH_RELOAD_MESSAGE_RELOAD);
|
|
83
79
|
}
|
|
84
|
-
if (event.meta.siteVariables
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
) {
|
|
90
|
-
this.pagefindRunner = new WatchPagefindRunner();
|
|
91
|
-
this.pagefindSiteVariablesKey = siteVariablesKey;
|
|
92
|
-
}
|
|
93
|
-
this.pagefindRunner.update(
|
|
80
|
+
if (isFeatureEnabled(event.meta.siteVariables, 'search')) {
|
|
81
|
+
// One indexer for the whole session runs one index at a time, so
|
|
82
|
+
// two runs never write pagefind/ concurrently.
|
|
83
|
+
const runner = (this.pagefindRunner ??= new WatchPagefindRunner());
|
|
84
|
+
runner.update(
|
|
94
85
|
this.distDir,
|
|
95
86
|
event.meta.htmlAssetsByPath,
|
|
96
87
|
event.meta.htmlAnalysisByPath,
|
|
88
|
+
event.meta.pdfSourceByOutputPath,
|
|
97
89
|
);
|
|
98
|
-
setImmediate(() =>
|
|
99
|
-
} else {
|
|
100
|
-
this.pagefindRunner = undefined;
|
|
101
|
-
this.pagefindSiteVariablesKey = undefined;
|
|
90
|
+
setImmediate(() => runner.run());
|
|
102
91
|
}
|
|
103
92
|
return;
|
|
104
93
|
}
|
|
105
94
|
case 'build-failed':
|
|
106
|
-
|
|
107
|
-
log.error`${diagnostic.message}`;
|
|
108
|
-
}
|
|
95
|
+
printDiagnostics(event.diagnostics);
|
|
109
96
|
return;
|
|
110
97
|
case 'watching':
|
|
111
98
|
log.info`Watching for changes...`;
|
package/build/watch/types.d.ts
CHANGED
|
@@ -1,17 +1,6 @@
|
|
|
1
1
|
import type { ChokidarOptions } from 'chokidar';
|
|
2
|
-
import type {
|
|
3
|
-
import type { TadaSnapshot } from '
|
|
4
|
-
|
|
5
|
-
export type WatchEventKind = 'add' | 'change' | 'unlink';
|
|
6
|
-
|
|
7
|
-
export interface FileChange {
|
|
8
|
-
path: string;
|
|
9
|
-
kind: WatchEventKind;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface ChangeBatch {
|
|
13
|
-
changes: FileChange[];
|
|
14
|
-
}
|
|
2
|
+
import type { BuildDiagnostic } from '../build-types';
|
|
3
|
+
import type { TadaSnapshot } from '../site-build';
|
|
15
4
|
|
|
16
5
|
export interface WatchTarget {
|
|
17
6
|
path: string;
|
|
@@ -19,66 +8,45 @@ export interface WatchTarget {
|
|
|
19
8
|
filter?: (filePath: string) => boolean;
|
|
20
9
|
}
|
|
21
10
|
|
|
22
|
-
export
|
|
23
|
-
|
|
24
|
-
}
|
|
11
|
+
export type WatchBuildResult<Meta> =
|
|
12
|
+
| { ok: true; meta: Meta }
|
|
13
|
+
| { ok: false; diagnostics: BuildDiagnostic[] };
|
|
25
14
|
|
|
26
|
-
export
|
|
27
|
-
kind: '
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
15
|
+
export type WatchLifecycleEvent<Meta> =
|
|
16
|
+
| { kind: 'watching' }
|
|
17
|
+
| { kind: 'build-started'; paths?: ReadonlySet<string> }
|
|
18
|
+
| { kind: 'build-succeeded'; paths?: ReadonlySet<string>; meta: Meta }
|
|
19
|
+
| {
|
|
20
|
+
kind: 'build-failed';
|
|
21
|
+
paths?: ReadonlySet<string>;
|
|
22
|
+
diagnostics: BuildDiagnostic[];
|
|
23
|
+
};
|
|
31
24
|
|
|
32
|
-
export interface
|
|
33
|
-
|
|
34
|
-
|
|
25
|
+
export interface WatchClock {
|
|
26
|
+
setTimeout: typeof setTimeout;
|
|
27
|
+
clearTimeout: typeof clearTimeout;
|
|
35
28
|
}
|
|
36
29
|
|
|
37
|
-
export
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
targetPath: string;
|
|
30
|
+
export interface WatchEngineOptions<Meta> {
|
|
31
|
+
targets: WatchTarget[];
|
|
32
|
+
build: (paths?: ReadonlySet<string>) => Promise<WatchBuildResult<Meta>>;
|
|
33
|
+
debounceMs?: number;
|
|
34
|
+
onEvent?: (event: WatchLifecycleEvent<Meta>) => void | Promise<void>;
|
|
43
35
|
}
|
|
44
36
|
|
|
45
|
-
export interface
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
mutations: FileMutation[];
|
|
37
|
+
export interface WatchHandle {
|
|
38
|
+
done: Promise<void>;
|
|
39
|
+
close(): Promise<void>;
|
|
49
40
|
}
|
|
50
41
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
export type CompilerBuildResult =
|
|
42
|
+
/** The result of compiling one watch build, before it is written */
|
|
43
|
+
export type CompileOutcome =
|
|
54
44
|
| {
|
|
55
45
|
ok: true;
|
|
56
46
|
snapshot: TadaSnapshot;
|
|
57
|
-
|
|
58
|
-
|
|
47
|
+
/** Full builds are written against the files on disk */
|
|
48
|
+
full: boolean;
|
|
49
|
+
/** Sources whose outputs are rewritten even if they look unchanged */
|
|
50
|
+
forceSourcePaths?: ReadonlySet<string>;
|
|
59
51
|
}
|
|
60
|
-
| { ok: false; diagnostics:
|
|
61
|
-
|
|
62
|
-
export interface WatchCompiler {
|
|
63
|
-
getWatchTargets(): WatchTarget[];
|
|
64
|
-
build(
|
|
65
|
-
snapshot: TadaSnapshot | undefined,
|
|
66
|
-
batch?: ChangeBatch,
|
|
67
|
-
): Promise<CompilerBuildResult>;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export type WatchLifecycleEvent =
|
|
71
|
-
| { kind: 'watching' }
|
|
72
|
-
| { kind: 'build-started'; batch?: ChangeBatch }
|
|
73
|
-
| { kind: 'build-succeeded'; batch?: ChangeBatch; meta: TadaBuildMeta }
|
|
74
|
-
| {
|
|
75
|
-
kind: 'build-failed';
|
|
76
|
-
batch?: ChangeBatch;
|
|
77
|
-
diagnostics: WatchDiagnostic[];
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
export interface WatchEngineOptions {
|
|
81
|
-
compiler: WatchCompiler;
|
|
82
|
-
debounceMs?: number;
|
|
83
|
-
onEvent?: (event: WatchLifecycleEvent) => void | Promise<void>;
|
|
84
|
-
}
|
|
52
|
+
| { ok: false; diagnostics: BuildDiagnostic[] };
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
breadcrumbs:
|
|
3
|
+
- label: Labs
|
|
4
|
+
url: /labs/index.html
|
|
5
|
+
- label: Lab 0
|
|
6
|
+
url: /labs/00/index.html
|
|
4
7
|
title: Counting vowels
|
|
5
8
|
author: alex
|
|
6
9
|
description: An example of a literate Java program that takes predefined input.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abreen/tada",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A static site generator",
|
|
6
6
|
"license": "MIT",
|
|
@@ -65,10 +65,10 @@
|
|
|
65
65
|
"chokidar": "^4.0.3",
|
|
66
66
|
"culori": "^4.0.2",
|
|
67
67
|
"d3-flextree": "^2.1.2",
|
|
68
|
+
"entities": "^4.5.0",
|
|
68
69
|
"fontkit": "^2.0.4",
|
|
69
70
|
"front-matter": "^4.0.2",
|
|
70
71
|
"java-parser": "^3.0.1",
|
|
71
|
-
"jsdom": "^26.1.0",
|
|
72
72
|
"katex": "^0.16.45",
|
|
73
73
|
"lodash": "^4.18.1",
|
|
74
74
|
"markdown-it": "^14.1.1",
|
|
@@ -81,8 +81,7 @@
|
|
|
81
81
|
"quote-quote": "^1.2.0",
|
|
82
82
|
"sass": "^1.99.0",
|
|
83
83
|
"sharp": "^0.35.3",
|
|
84
|
-
"shiki": "^4.0.2"
|
|
85
|
-
"string-strip-html": "^8.5.0"
|
|
84
|
+
"shiki": "^4.0.2"
|
|
86
85
|
},
|
|
87
86
|
"lint-staged": {
|
|
88
87
|
"*.{ts,js}": "prettier --write",
|
|
@@ -117,6 +116,7 @@
|
|
|
117
116
|
"istanbul-lib-instrument": "^6.0.3",
|
|
118
117
|
"istanbul-lib-report": "^3.0.1",
|
|
119
118
|
"istanbul-reports": "^3.2.0",
|
|
119
|
+
"jsdom": "^26.1.0",
|
|
120
120
|
"knip": "^6.11.0",
|
|
121
121
|
"lint-staged": "^16.4.0",
|
|
122
122
|
"postcss-scss": "^4.0.9",
|
package/schema/site.schema.json
CHANGED
|
@@ -46,6 +46,8 @@
|
|
|
46
46
|
"maximum": 100,
|
|
47
47
|
"default": 100
|
|
48
48
|
},
|
|
49
|
+
"logo": { "type": "string" },
|
|
50
|
+
"favicon": { "type": "string" },
|
|
49
51
|
"faviconColor": { "type": "string" },
|
|
50
52
|
"faviconSymbol": { "type": "string", "pattern": "^[A-Z0-9\\- ]{1,5}$" },
|
|
51
53
|
"faviconFontWeight": {
|
|
@@ -83,7 +85,14 @@
|
|
|
83
85
|
"$defs": {
|
|
84
86
|
"serifFontOverride": {
|
|
85
87
|
"type": "object",
|
|
86
|
-
"
|
|
88
|
+
"anyOf": [
|
|
89
|
+
{ "required": ["regular"] },
|
|
90
|
+
{
|
|
91
|
+
"required": ["tuning"],
|
|
92
|
+
"properties": { "tuning": {} },
|
|
93
|
+
"additionalProperties": false
|
|
94
|
+
}
|
|
95
|
+
],
|
|
87
96
|
"properties": {
|
|
88
97
|
"regular": { "$ref": "#/$defs/publicWoff2Path" },
|
|
89
98
|
"italic": { "$ref": "#/$defs/publicWoff2Path" },
|
|
@@ -110,7 +119,14 @@
|
|
|
110
119
|
},
|
|
111
120
|
"serifMonoFontOverride": {
|
|
112
121
|
"type": "object",
|
|
113
|
-
"
|
|
122
|
+
"anyOf": [
|
|
123
|
+
{ "required": ["regular"] },
|
|
124
|
+
{
|
|
125
|
+
"required": ["tuning"],
|
|
126
|
+
"properties": { "tuning": {} },
|
|
127
|
+
"additionalProperties": false
|
|
128
|
+
}
|
|
129
|
+
],
|
|
114
130
|
"properties": {
|
|
115
131
|
"regular": { "$ref": "#/$defs/publicWoff2Path" },
|
|
116
132
|
"italic": { "$ref": "#/$defs/publicWoff2Path" },
|