@abreen/tada 1.13.4 → 1.13.9
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/build/bundle.ts +17 -2
- package/build/generate-content-assets.ts +38 -175
- package/build/pipeline.ts +1 -1
- package/build/source-model.ts +2 -14
- package/build/types.d.ts +1 -12
- package/build/utils/derive-theme.ts +10 -0
- package/build/utils/render.ts +1 -0
- package/build/utils/trace-core.ts +75 -22
- package/build/utils/trace.ts +27 -10
- package/build/watch/assets.ts +2 -2
- package/build/watch/build-full.ts +26 -52
- package/build/watch/build-helpers.ts +66 -0
- package/build/watch/build-incremental.ts +32 -60
- package/build/watch/build-result.ts +19 -0
- package/build/watch/compiler-types.d.ts +1 -0
- package/build/watch/compiler.ts +25 -61
- package/build/watch/config-paths.ts +56 -0
- package/build/watch/engine.ts +199 -0
- package/{watch → build/watch}/fs-commit.ts +1 -3
- package/build/watch/index.ts +1 -1
- package/build/watch/mutations.ts +23 -25
- package/build/watch/planner.ts +14 -78
- package/build/watch/runtime.ts +26 -38
- package/build/watch/snapshot.ts +26 -47
- package/build/watch/types.d.ts +84 -0
- package/build/watch/validation.ts +1 -1
- package/package.json +1 -2
- package/src/_base.scss +6 -5
- package/src/slides/index.ts +34 -0
- package/src/style.scss +1 -1
- package/src/trace/index.ts +14 -7
- package/src/trace/style.scss +11 -10
- package/templates/_theme.scss +2 -0
- package/templates/default.html +1 -1
- package/build/watch-reachability-state.ts +0 -305
- package/watch/engine.ts +0 -296
- package/watch/events.ts +0 -10
- package/watch/types.d.ts +0 -103
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import chokidar from 'chokidar';
|
|
2
|
+
import { applyCommitPlan } from './fs-commit';
|
|
3
|
+
import type {
|
|
4
|
+
ChangeBatch,
|
|
5
|
+
CompilerBuildResult,
|
|
6
|
+
WatchEngineOptions,
|
|
7
|
+
WatchEventKind,
|
|
8
|
+
WatchLifecycleEvent,
|
|
9
|
+
} from './types';
|
|
10
|
+
import type { TadaSnapshot } from './snapshot';
|
|
11
|
+
|
|
12
|
+
function normalizeBatch(changes: Map<string, WatchEventKind>): ChangeBatch {
|
|
13
|
+
return {
|
|
14
|
+
changes: [...changes.entries()]
|
|
15
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
16
|
+
.map(([path, kind]) => ({ path, kind })),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function mapBatch(batch: ChangeBatch): Map<string, WatchEventKind> {
|
|
21
|
+
return new Map(batch.changes.map(change => [change.path, change.kind]));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function mergeChangeKind(
|
|
25
|
+
previous: WatchEventKind | undefined,
|
|
26
|
+
next: WatchEventKind,
|
|
27
|
+
): WatchEventKind {
|
|
28
|
+
if (!previous) {
|
|
29
|
+
return next;
|
|
30
|
+
}
|
|
31
|
+
if (previous === 'add' && next === 'unlink') {
|
|
32
|
+
return 'change';
|
|
33
|
+
}
|
|
34
|
+
if (previous === 'unlink' && next === 'add') {
|
|
35
|
+
return 'change';
|
|
36
|
+
}
|
|
37
|
+
if (previous === 'add' || next === 'add') {
|
|
38
|
+
return 'add';
|
|
39
|
+
}
|
|
40
|
+
if (previous === 'unlink' || next === 'unlink') {
|
|
41
|
+
return 'unlink';
|
|
42
|
+
}
|
|
43
|
+
return 'change';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function toDiagnostics(error: unknown): { message: string }[] {
|
|
47
|
+
if (error instanceof Error) {
|
|
48
|
+
return [{ message: error.message }];
|
|
49
|
+
}
|
|
50
|
+
return [{ message: String(error) }];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function runWatchEngine(
|
|
54
|
+
options: WatchEngineOptions,
|
|
55
|
+
): Promise<void> {
|
|
56
|
+
const debounceMs = options.debounceMs ?? 300;
|
|
57
|
+
let snapshot: TadaSnapshot | undefined;
|
|
58
|
+
let uncommitted = new Map<string, WatchEventKind>();
|
|
59
|
+
let pending = new Map<string, WatchEventKind>();
|
|
60
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
61
|
+
let running = false;
|
|
62
|
+
|
|
63
|
+
async function emit(event: WatchLifecycleEvent): Promise<void> {
|
|
64
|
+
await options.onEvent?.(event);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function waitForQuietPeriod(): Promise<void> {
|
|
68
|
+
await new Promise(resolve => setTimeout(resolve, debounceMs));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function runBuild(batch?: ChangeBatch): Promise<CompilerBuildResult> {
|
|
72
|
+
await emit({ kind: 'build-started', batch });
|
|
73
|
+
try {
|
|
74
|
+
return await options.compiler.build(snapshot, batch);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
return { ok: false, diagnostics: toDiagnostics(error) };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function flush(): Promise<void> {
|
|
81
|
+
if (running) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
running = true;
|
|
85
|
+
try {
|
|
86
|
+
let finalSuccess:
|
|
87
|
+
| {
|
|
88
|
+
batch?: ChangeBatch;
|
|
89
|
+
result: Extract<CompilerBuildResult, { ok: true }>;
|
|
90
|
+
}
|
|
91
|
+
| undefined;
|
|
92
|
+
|
|
93
|
+
while (pending.size > 0) {
|
|
94
|
+
const changes = new Map(uncommitted);
|
|
95
|
+
for (const [filePath, kind] of pending) {
|
|
96
|
+
changes.set(filePath, mergeChangeKind(changes.get(filePath), kind));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const batch = normalizeBatch(changes);
|
|
100
|
+
pending = new Map();
|
|
101
|
+
const outcome = await runBuild(batch);
|
|
102
|
+
|
|
103
|
+
if (!outcome.ok) {
|
|
104
|
+
uncommitted = mapBatch(batch);
|
|
105
|
+
await emit({
|
|
106
|
+
kind: 'build-failed',
|
|
107
|
+
batch,
|
|
108
|
+
diagnostics: outcome.diagnostics,
|
|
109
|
+
});
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
applyCommitPlan(outcome.commit);
|
|
114
|
+
snapshot = outcome.snapshot;
|
|
115
|
+
uncommitted = new Map();
|
|
116
|
+
finalSuccess = { batch, result: outcome };
|
|
117
|
+
|
|
118
|
+
if (pending.size === 0) {
|
|
119
|
+
await waitForQuietPeriod();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (finalSuccess) {
|
|
124
|
+
await emit({
|
|
125
|
+
kind: 'build-succeeded',
|
|
126
|
+
batch: finalSuccess.batch,
|
|
127
|
+
meta: finalSuccess.result.meta,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
} finally {
|
|
131
|
+
running = false;
|
|
132
|
+
if (pending.size > 0) {
|
|
133
|
+
schedule();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function schedule(): void {
|
|
139
|
+
if (timer) {
|
|
140
|
+
clearTimeout(timer);
|
|
141
|
+
}
|
|
142
|
+
timer = setTimeout(() => {
|
|
143
|
+
timer = null;
|
|
144
|
+
void flush();
|
|
145
|
+
}, debounceMs);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function onFileChange(filePath: string, kind: WatchEventKind): void {
|
|
149
|
+
pending.set(filePath, mergeChangeKind(pending.get(filePath), kind));
|
|
150
|
+
schedule();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const startupOutcome = await runBuild();
|
|
154
|
+
if (!startupOutcome.ok) {
|
|
155
|
+
await emit({
|
|
156
|
+
kind: 'build-failed',
|
|
157
|
+
diagnostics: startupOutcome.diagnostics,
|
|
158
|
+
});
|
|
159
|
+
} else {
|
|
160
|
+
applyCommitPlan(startupOutcome.commit);
|
|
161
|
+
snapshot = startupOutcome.snapshot;
|
|
162
|
+
await emit({ kind: 'build-succeeded', meta: startupOutcome.meta });
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const watcherEntries = options.compiler
|
|
166
|
+
.getWatchTargets()
|
|
167
|
+
.map(target => ({
|
|
168
|
+
target,
|
|
169
|
+
watcher: chokidar.watch(target.path, {
|
|
170
|
+
ignoreInitial: true,
|
|
171
|
+
atomic: true,
|
|
172
|
+
awaitWriteFinish: { stabilityThreshold: 100 },
|
|
173
|
+
...target.chokidar,
|
|
174
|
+
}),
|
|
175
|
+
}));
|
|
176
|
+
|
|
177
|
+
let readyCount = 0;
|
|
178
|
+
const readyGoal = watcherEntries.length;
|
|
179
|
+
const onReady = async () => {
|
|
180
|
+
readyCount += 1;
|
|
181
|
+
if (readyCount === readyGoal) {
|
|
182
|
+
await emit({ kind: 'watching' });
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
for (const { watcher, target } of watcherEntries) {
|
|
187
|
+
const emitIfIncluded = (filePath: string, kind: WatchEventKind) => {
|
|
188
|
+
if (!target.filter || target.filter(filePath)) {
|
|
189
|
+
onFileChange(filePath, kind);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
watcher.on('add', filePath => emitIfIncluded(filePath, 'add'));
|
|
193
|
+
watcher.on('change', filePath => emitIfIncluded(filePath, 'change'));
|
|
194
|
+
watcher.on('unlink', filePath => emitIfIncluded(filePath, 'unlink'));
|
|
195
|
+
watcher.on('ready', () => void onReady());
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return new Promise(() => {});
|
|
199
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { globals, type Globals } from '../
|
|
3
|
+
import { globals, type Globals } from '../globals';
|
|
4
4
|
import type {
|
|
5
5
|
ApplyMutationsCommitPlan,
|
|
6
6
|
CommitPlan,
|
|
@@ -87,8 +87,6 @@ function applyFileMutation(
|
|
|
87
87
|
const stagedPath = path.join(stagedRoot, mutation.path);
|
|
88
88
|
|
|
89
89
|
if (mutation.kind === 'write') {
|
|
90
|
-
ensureParentDir(stagedPath);
|
|
91
|
-
fs.writeFileSync(stagedPath, mutation.content);
|
|
92
90
|
ensureParentDir(targetPath);
|
|
93
91
|
renameWithRetry(stagedPath, targetPath, globals);
|
|
94
92
|
return;
|
package/build/watch/index.ts
CHANGED
package/build/watch/mutations.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FileMutation } from './types';
|
|
2
|
+
import type { TadaOutputOwner, TadaSnapshot } from './snapshot';
|
|
2
3
|
|
|
3
4
|
function outputsEqual(
|
|
4
5
|
left: string | Buffer | undefined,
|
|
@@ -15,16 +16,25 @@ function outputsEqual(
|
|
|
15
16
|
return Buffer.compare(leftBuffer, rightBuffer) === 0;
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
function getOutputContent(
|
|
20
|
+
snapshot: TadaSnapshot,
|
|
21
|
+
owner: TadaOutputOwner | undefined,
|
|
22
|
+
outputPath: string,
|
|
23
|
+
): string | Buffer | undefined {
|
|
24
|
+
if (!owner) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
const records =
|
|
28
|
+
owner.kind === 'content' ? snapshot.contentRecords : snapshot.publicRecords;
|
|
29
|
+
return records.get(owner.sourcePath)?.outputs.get(outputPath);
|
|
30
|
+
}
|
|
31
|
+
|
|
18
32
|
export function computeMutations(
|
|
19
33
|
previous: TadaSnapshot,
|
|
20
34
|
next: TadaSnapshot,
|
|
21
35
|
forceSourcePaths: Set<string> = new Set(),
|
|
22
|
-
):
|
|
23
|
-
const mutations:
|
|
24
|
-
path: string;
|
|
25
|
-
kind: 'write' | 'delete';
|
|
26
|
-
content?: string | Buffer;
|
|
27
|
-
}[] = [];
|
|
36
|
+
): FileMutation[] {
|
|
37
|
+
const mutations: FileMutation[] = [];
|
|
28
38
|
const allPaths = new Set([
|
|
29
39
|
...previous.outputOwners.keys(),
|
|
30
40
|
...next.outputOwners.keys(),
|
|
@@ -34,24 +44,12 @@ export function computeMutations(
|
|
|
34
44
|
const previousOwner = previous.outputOwners.get(outputPath);
|
|
35
45
|
const nextOwner = next.outputOwners.get(outputPath);
|
|
36
46
|
|
|
37
|
-
const previousContent =
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
? previous.publicRecords
|
|
44
|
-
.get(previousOwner.sourcePath)
|
|
45
|
-
?.outputs.get(outputPath)
|
|
46
|
-
: undefined;
|
|
47
|
-
const nextContent =
|
|
48
|
-
nextOwner?.kind === 'content'
|
|
49
|
-
? next.contentRecords.get(nextOwner.sourcePath)?.outputs.get(outputPath)
|
|
50
|
-
: nextOwner
|
|
51
|
-
? next.publicRecords
|
|
52
|
-
.get(nextOwner.sourcePath)
|
|
53
|
-
?.outputs.get(outputPath)
|
|
54
|
-
: undefined;
|
|
47
|
+
const previousContent = getOutputContent(
|
|
48
|
+
previous,
|
|
49
|
+
previousOwner,
|
|
50
|
+
outputPath,
|
|
51
|
+
);
|
|
52
|
+
const nextContent = getOutputContent(next, nextOwner, outputPath);
|
|
55
53
|
|
|
56
54
|
if (!nextOwner) {
|
|
57
55
|
mutations.push({ path: outputPath, kind: 'delete' });
|
package/build/watch/planner.ts
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
import {
|
|
3
|
-
getProjectConfigBaseName,
|
|
4
|
-
getSiteConfigBaseName,
|
|
5
|
-
getSupportedConfigFilePaths,
|
|
6
|
-
} from '../config-files';
|
|
7
2
|
import { loadProjectConfig } from '../config-loader';
|
|
8
3
|
import { getProjectConfigDir } from '../templates';
|
|
9
|
-
import type { ChangeBatch } from '
|
|
4
|
+
import type { ChangeBatch } from './types';
|
|
10
5
|
import type { TadaProjectScan, TadaSnapshot } from './snapshot';
|
|
6
|
+
import { classifyWatchConfigPath } from './config-paths';
|
|
11
7
|
|
|
12
|
-
export interface
|
|
13
|
-
kind: '
|
|
8
|
+
export interface TadaIncrementalWatchPlan {
|
|
9
|
+
kind: 'incremental';
|
|
14
10
|
scan: TadaProjectScan;
|
|
15
11
|
contentToRender: Set<string>;
|
|
16
12
|
publicToRender: Set<string>;
|
|
@@ -18,6 +14,8 @@ export interface TadaWatchPlan {
|
|
|
18
14
|
publicToRemove: Set<string>;
|
|
19
15
|
}
|
|
20
16
|
|
|
17
|
+
export type TadaWatchPlan = { kind: 'full' } | TadaIncrementalWatchPlan;
|
|
18
|
+
|
|
21
19
|
export function diffAuthorKeys(previous: unknown, next: unknown): Set<string> {
|
|
22
20
|
const previousMap =
|
|
23
21
|
previous && typeof previous === 'object'
|
|
@@ -60,7 +58,7 @@ export function createTadaWatchPlan({
|
|
|
60
58
|
batch,
|
|
61
59
|
scan,
|
|
62
60
|
}: {
|
|
63
|
-
snapshot: TadaSnapshot
|
|
61
|
+
snapshot: TadaSnapshot;
|
|
64
62
|
batch: ChangeBatch;
|
|
65
63
|
scan: TadaProjectScan;
|
|
66
64
|
}): TadaWatchPlan {
|
|
@@ -69,66 +67,24 @@ export function createTadaWatchPlan({
|
|
|
69
67
|
const contentToRemove = new Set<string>();
|
|
70
68
|
const publicToRemove = new Set<string>();
|
|
71
69
|
const projectConfigDir = getProjectConfigDir();
|
|
72
|
-
const siteConfigPaths = new Set(
|
|
73
|
-
getSupportedConfigFilePaths('.', getSiteConfigBaseName('dev')).map(
|
|
74
|
-
filePath => path.resolve(filePath),
|
|
75
|
-
),
|
|
76
|
-
);
|
|
77
|
-
const navPaths = new Set(
|
|
78
|
-
getSupportedConfigFilePaths(
|
|
79
|
-
projectConfigDir,
|
|
80
|
-
getProjectConfigBaseName('nav'),
|
|
81
|
-
).map(filePath => path.resolve(filePath)),
|
|
82
|
-
);
|
|
83
|
-
const authorsPaths = new Set(
|
|
84
|
-
getSupportedConfigFilePaths(
|
|
85
|
-
projectConfigDir,
|
|
86
|
-
getProjectConfigBaseName('authors'),
|
|
87
|
-
).map(filePath => path.resolve(filePath)),
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
if (!snapshot) {
|
|
91
|
-
return {
|
|
92
|
-
kind: 'full',
|
|
93
|
-
scan,
|
|
94
|
-
contentToRender,
|
|
95
|
-
publicToRender,
|
|
96
|
-
contentToRemove,
|
|
97
|
-
publicToRemove,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
70
|
|
|
101
71
|
for (const change of batch.changes) {
|
|
102
|
-
const
|
|
103
|
-
if (
|
|
104
|
-
return {
|
|
105
|
-
kind: 'full',
|
|
106
|
-
scan,
|
|
107
|
-
contentToRender,
|
|
108
|
-
publicToRender,
|
|
109
|
-
contentToRemove,
|
|
110
|
-
publicToRemove,
|
|
111
|
-
};
|
|
72
|
+
const configKind = classifyWatchConfigPath(change.path);
|
|
73
|
+
if (configKind === 'site' || configKind === 'nav') {
|
|
74
|
+
return { kind: 'full' };
|
|
112
75
|
}
|
|
113
76
|
}
|
|
114
77
|
|
|
115
78
|
for (const change of batch.changes) {
|
|
116
79
|
const resolvedPath = path.resolve(change.path);
|
|
117
|
-
if (
|
|
80
|
+
if (classifyWatchConfigPath(resolvedPath) === 'authors') {
|
|
118
81
|
const nextAuthorsData = loadProjectConfig(
|
|
119
82
|
projectConfigDir,
|
|
120
83
|
'authors',
|
|
121
84
|
snapshot.siteVariables,
|
|
122
85
|
)?.value;
|
|
123
86
|
if (snapshot.authorsData === undefined || nextAuthorsData === undefined) {
|
|
124
|
-
return {
|
|
125
|
-
kind: 'full',
|
|
126
|
-
scan,
|
|
127
|
-
contentToRender,
|
|
128
|
-
publicToRender,
|
|
129
|
-
contentToRemove,
|
|
130
|
-
publicToRemove,
|
|
131
|
-
};
|
|
87
|
+
return { kind: 'full' };
|
|
132
88
|
}
|
|
133
89
|
const changedAuthorKeys = diffAuthorKeys(
|
|
134
90
|
snapshot.authorsData,
|
|
@@ -144,17 +100,6 @@ export function createTadaWatchPlan({
|
|
|
144
100
|
contentToRender.add(resolvedPath);
|
|
145
101
|
} else if (snapshot.contentRecords.has(resolvedPath)) {
|
|
146
102
|
contentToRemove.add(resolvedPath);
|
|
147
|
-
const previousRecord = snapshot.contentRecords.get(resolvedPath)!;
|
|
148
|
-
for (const partialPath of previousRecord.partialDeps) {
|
|
149
|
-
addDependents(
|
|
150
|
-
contentToRender,
|
|
151
|
-
snapshot.reversePartialDeps,
|
|
152
|
-
partialPath,
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
for (const tracePath of previousRecord.traceDeps) {
|
|
156
|
-
addDependents(contentToRender, snapshot.reverseTraceDeps, tracePath);
|
|
157
|
-
}
|
|
158
103
|
}
|
|
159
104
|
|
|
160
105
|
if (scan.publicFiles.has(resolvedPath)) {
|
|
@@ -168,15 +113,13 @@ export function createTadaWatchPlan({
|
|
|
168
113
|
}
|
|
169
114
|
|
|
170
115
|
const changedTargets = new Set<string>();
|
|
171
|
-
const
|
|
172
|
-
for (const target of snapshot.validTargets) {
|
|
116
|
+
for (const target of snapshot.scan.validTargets) {
|
|
173
117
|
if (!scan.validTargets.has(target)) {
|
|
174
118
|
changedTargets.add(target);
|
|
175
|
-
removedTargets.add(target);
|
|
176
119
|
}
|
|
177
120
|
}
|
|
178
121
|
for (const target of scan.validTargets) {
|
|
179
|
-
if (!snapshot.validTargets.has(target)) {
|
|
122
|
+
if (!snapshot.scan.validTargets.has(target)) {
|
|
180
123
|
changedTargets.add(target);
|
|
181
124
|
}
|
|
182
125
|
}
|
|
@@ -184,13 +127,6 @@ export function createTadaWatchPlan({
|
|
|
184
127
|
addDependents(contentToRender, snapshot.reverseInternalTargetDeps, target);
|
|
185
128
|
}
|
|
186
129
|
|
|
187
|
-
if (removedTargets.size > 0 && contentToRender.size === 0) {
|
|
188
|
-
const rootOwner = snapshot.outputOwners.get('index.html');
|
|
189
|
-
if (rootOwner?.kind === 'content') {
|
|
190
|
-
contentToRender.add(rootOwner.sourcePath);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
130
|
for (const [outputPath, owner] of snapshot.outputOwners) {
|
|
195
131
|
const nextContentOwner = scan.contentOwners.get(outputPath);
|
|
196
132
|
const nextPublicOwner = scan.publicOwners.get(outputPath);
|
package/build/watch/runtime.ts
CHANGED
|
@@ -3,9 +3,7 @@ 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 type {
|
|
7
|
-
import type { TadaSnapshot } from './snapshot';
|
|
8
|
-
import type { WatchLifecycleEvent } from '../../watch/types';
|
|
6
|
+
import type { WatchLifecycleEvent } from './types';
|
|
9
7
|
import {
|
|
10
8
|
WATCH_RELOAD_MESSAGE_REBUILDING,
|
|
11
9
|
WATCH_RELOAD_MESSAGE_RELOAD,
|
|
@@ -30,7 +28,6 @@ export class TadaWatchRuntime {
|
|
|
30
28
|
private httpPort: number | undefined;
|
|
31
29
|
private distDir: string;
|
|
32
30
|
private server: Bun.Server<undefined> | null;
|
|
33
|
-
private lastBuildFailed: boolean;
|
|
34
31
|
private pagefindRunner: WatchPagefindRunner | undefined;
|
|
35
32
|
private pagefindSiteVariablesKey: string | undefined;
|
|
36
33
|
|
|
@@ -38,7 +35,6 @@ export class TadaWatchRuntime {
|
|
|
38
35
|
this.httpPort = httpPort;
|
|
39
36
|
this.distDir = distDir;
|
|
40
37
|
this.server = null;
|
|
41
|
-
this.lastBuildFailed = false;
|
|
42
38
|
this.pagefindSiteVariablesKey = undefined;
|
|
43
39
|
}
|
|
44
40
|
|
|
@@ -68,51 +64,45 @@ export class TadaWatchRuntime {
|
|
|
68
64
|
wslog.debug`WebSocket server listening at ws://localhost:${this.server.port}${WATCH_RELOAD_PATH}`;
|
|
69
65
|
}
|
|
70
66
|
|
|
71
|
-
async onEvent(
|
|
72
|
-
event: WatchLifecycleEvent<TadaSnapshot, TadaBuildMeta>,
|
|
73
|
-
): Promise<void> {
|
|
67
|
+
async onEvent(event: WatchLifecycleEvent): Promise<void> {
|
|
74
68
|
switch (event.kind) {
|
|
75
69
|
case 'build-started':
|
|
76
|
-
if (!event.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
70
|
+
if (!event.batch) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
for (const change of event.batch.changes) {
|
|
74
|
+
log.event`${B`${path.basename(change.path)}`} ${describeChange(change.kind)}`;
|
|
81
75
|
}
|
|
76
|
+
this.broadcast(WATCH_RELOAD_MESSAGE_REBUILDING);
|
|
82
77
|
return;
|
|
83
78
|
case 'build-succeeded': {
|
|
84
|
-
const recoveredFromFailure = this.lastBuildFailed;
|
|
85
|
-
this.lastBuildFailed = false;
|
|
86
79
|
printFlair();
|
|
87
80
|
this.ensureServerStarted();
|
|
88
|
-
if (
|
|
81
|
+
if (event.batch) {
|
|
89
82
|
this.broadcast(WATCH_RELOAD_MESSAGE_RELOAD);
|
|
90
83
|
}
|
|
91
|
-
if (event.meta
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
this.pagefindSiteVariablesKey = siteVariablesKey;
|
|
100
|
-
}
|
|
101
|
-
this.pagefindRunner.update(
|
|
102
|
-
this.distDir,
|
|
103
|
-
event.meta.htmlAssetsByPath,
|
|
104
|
-
event.meta.htmlAnalysisByPath,
|
|
105
|
-
);
|
|
106
|
-
setImmediate(() => this.pagefindRunner!.run());
|
|
107
|
-
} else {
|
|
108
|
-
this.pagefindRunner = undefined;
|
|
109
|
-
this.pagefindSiteVariablesKey = undefined;
|
|
84
|
+
if (event.meta.siteVariables.features.search !== false) {
|
|
85
|
+
const siteVariablesKey = JSON.stringify(event.meta.siteVariables);
|
|
86
|
+
if (
|
|
87
|
+
!this.pagefindRunner ||
|
|
88
|
+
this.pagefindSiteVariablesKey !== siteVariablesKey
|
|
89
|
+
) {
|
|
90
|
+
this.pagefindRunner = new WatchPagefindRunner();
|
|
91
|
+
this.pagefindSiteVariablesKey = siteVariablesKey;
|
|
110
92
|
}
|
|
93
|
+
this.pagefindRunner.update(
|
|
94
|
+
this.distDir,
|
|
95
|
+
event.meta.htmlAssetsByPath,
|
|
96
|
+
event.meta.htmlAnalysisByPath,
|
|
97
|
+
);
|
|
98
|
+
setImmediate(() => this.pagefindRunner!.run());
|
|
99
|
+
} else {
|
|
100
|
+
this.pagefindRunner = undefined;
|
|
101
|
+
this.pagefindSiteVariablesKey = undefined;
|
|
111
102
|
}
|
|
112
103
|
return;
|
|
113
104
|
}
|
|
114
105
|
case 'build-failed':
|
|
115
|
-
this.lastBuildFailed = true;
|
|
116
106
|
for (const diagnostic of event.diagnostics) {
|
|
117
107
|
log.error`${diagnostic.message}`;
|
|
118
108
|
}
|
|
@@ -120,8 +110,6 @@ export class TadaWatchRuntime {
|
|
|
120
110
|
case 'watching':
|
|
121
111
|
log.info`Watching for changes...`;
|
|
122
112
|
return;
|
|
123
|
-
case 'build-skipped':
|
|
124
|
-
return;
|
|
125
113
|
}
|
|
126
114
|
}
|
|
127
115
|
}
|