livemark 0.28.3 → 0.28.4
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/helpers/task-queue.d.ts +7 -0
- package/build/helpers/task-queue.js +28 -0
- package/build/helpers/task-queue.unit.d.ts +1 -0
- package/build/helpers/task-queue.unit.js +56 -0
- package/build/plugins/vite-content.d.ts +9 -0
- package/build/plugins/vite-content.js +60 -0
- package/build/vite.config.js +3 -3
- package/package.json +2 -2
- package/source/helpers/task-queue.ts +28 -0
- package/source/helpers/task-queue.unit.ts +60 -0
- package/source/plugins/vite-content.ts +72 -0
- package/source/vite.config.ts +2 -2
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Run keyed async tasks one at a time; a key enqueued again before its task
|
|
2
|
+
* starts replaces it, so a burst of events for one file costs one run. */
|
|
3
|
+
export declare function createTaskQueue(props: {
|
|
4
|
+
onError: (error: unknown) => void;
|
|
5
|
+
}): {
|
|
6
|
+
enqueue(key: string, task: () => Promise<unknown>): Promise<void>;
|
|
7
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Run keyed async tasks one at a time; a key enqueued again before its task
|
|
2
|
+
* starts replaces it, so a burst of events for one file costs one run. */
|
|
3
|
+
export function createTaskQueue(props) {
|
|
4
|
+
const pending = new Map();
|
|
5
|
+
let draining;
|
|
6
|
+
return {
|
|
7
|
+
enqueue(key, task) {
|
|
8
|
+
pending.set(key, task);
|
|
9
|
+
draining ??= drain();
|
|
10
|
+
return draining;
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
async function drain() {
|
|
14
|
+
// A Map iterator also visits entries set during iteration, which is what
|
|
15
|
+
// picks up keys enqueued while a task runs.
|
|
16
|
+
for (const [key, task] of pending) {
|
|
17
|
+
pending.delete(key);
|
|
18
|
+
try {
|
|
19
|
+
await task();
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
props.onError(error);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
draining = undefined;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFzay1xdWV1ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NvdXJjZS9oZWxwZXJzL3Rhc2stcXVldWUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7MkVBQzJFO0FBQzNFLE1BQU0sVUFBVSxlQUFlLENBQUMsS0FBNEM7SUFDMUUsTUFBTSxPQUFPLEdBQUcsSUFBSSxHQUFHLEVBQWtDLENBQUE7SUFDekQsSUFBSSxRQUFtQyxDQUFBO0lBRXZDLE9BQU87UUFDTCxPQUFPLENBQUMsR0FBVyxFQUFFLElBQTRCO1lBQy9DLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxDQUFBO1lBQ3RCLFFBQVEsS0FBSyxLQUFLLEVBQUUsQ0FBQTtZQUNwQixPQUFPLFFBQVEsQ0FBQTtRQUNqQixDQUFDO0tBQ0YsQ0FBQTtJQUVELEtBQUssVUFBVSxLQUFLO1FBQ2xCLHlFQUF5RTtRQUN6RSw0Q0FBNEM7UUFDNUMsS0FBSyxNQUFNLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxJQUFJLE9BQU8sRUFBRSxDQUFDO1lBQ2xDLE9BQU8sQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUE7WUFDbkIsSUFBSSxDQUFDO2dCQUNILE1BQU0sSUFBSSxFQUFFLENBQUE7WUFDZCxDQUFDO1lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztnQkFDZixLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFBO1lBQ3RCLENBQUM7UUFDSCxDQUFDO1FBQ0QsUUFBUSxHQUFHLFNBQVMsQ0FBQTtJQUN0QixDQUFDO0FBQ0gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKiBSdW4ga2V5ZWQgYXN5bmMgdGFza3Mgb25lIGF0IGEgdGltZTsgYSBrZXkgZW5xdWV1ZWQgYWdhaW4gYmVmb3JlIGl0cyB0YXNrXG4gKiAgc3RhcnRzIHJlcGxhY2VzIGl0LCBzbyBhIGJ1cnN0IG9mIGV2ZW50cyBmb3Igb25lIGZpbGUgY29zdHMgb25lIHJ1bi4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVUYXNrUXVldWUocHJvcHM6IHsgb25FcnJvcjogKGVycm9yOiB1bmtub3duKSA9PiB2b2lkIH0pIHtcbiAgY29uc3QgcGVuZGluZyA9IG5ldyBNYXA8c3RyaW5nLCAoKSA9PiBQcm9taXNlPHVua25vd24+PigpXG4gIGxldCBkcmFpbmluZzogUHJvbWlzZTx2b2lkPiB8IHVuZGVmaW5lZFxuXG4gIHJldHVybiB7XG4gICAgZW5xdWV1ZShrZXk6IHN0cmluZywgdGFzazogKCkgPT4gUHJvbWlzZTx1bmtub3duPikge1xuICAgICAgcGVuZGluZy5zZXQoa2V5LCB0YXNrKVxuICAgICAgZHJhaW5pbmcgPz89IGRyYWluKClcbiAgICAgIHJldHVybiBkcmFpbmluZ1xuICAgIH0sXG4gIH1cblxuICBhc3luYyBmdW5jdGlvbiBkcmFpbigpIHtcbiAgICAvLyBBIE1hcCBpdGVyYXRvciBhbHNvIHZpc2l0cyBlbnRyaWVzIHNldCBkdXJpbmcgaXRlcmF0aW9uLCB3aGljaCBpcyB3aGF0XG4gICAgLy8gcGlja3MgdXAga2V5cyBlbnF1ZXVlZCB3aGlsZSBhIHRhc2sgcnVucy5cbiAgICBmb3IgKGNvbnN0IFtrZXksIHRhc2tdIG9mIHBlbmRpbmcpIHtcbiAgICAgIHBlbmRpbmcuZGVsZXRlKGtleSlcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHRhc2soKVxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgcHJvcHMub25FcnJvcihlcnJvcilcbiAgICAgIH1cbiAgICB9XG4gICAgZHJhaW5pbmcgPSB1bmRlZmluZWRcbiAgfVxufVxuIl19
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { describe, expect, it } from "vite-plus/test";
|
|
2
|
+
import { createTaskQueue } from "./task-queue.js";
|
|
3
|
+
describe("createTaskQueue", () => {
|
|
4
|
+
it("runs one task at a time", async () => {
|
|
5
|
+
const queue = createTaskQueue({ onError: () => { } });
|
|
6
|
+
const log = [];
|
|
7
|
+
const task = (name, ms) => async () => {
|
|
8
|
+
log.push(`start ${name}`);
|
|
9
|
+
await new Promise(resolve => setTimeout(resolve, ms));
|
|
10
|
+
log.push(`end ${name}`);
|
|
11
|
+
};
|
|
12
|
+
queue.enqueue("a", task("a", 30));
|
|
13
|
+
await queue.enqueue("b", task("b", 1));
|
|
14
|
+
expect(log).toEqual(["start a", "end a", "start b", "end b"]);
|
|
15
|
+
});
|
|
16
|
+
it("collapses a key enqueued again before it starts", async () => {
|
|
17
|
+
const queue = createTaskQueue({ onError: () => { } });
|
|
18
|
+
const runs = [];
|
|
19
|
+
queue.enqueue("first", async () => runs.push("first"));
|
|
20
|
+
queue.enqueue("file", async () => runs.push("stale"));
|
|
21
|
+
await queue.enqueue("file", async () => runs.push("latest"));
|
|
22
|
+
expect(runs).toEqual(["first", "latest"]);
|
|
23
|
+
});
|
|
24
|
+
it("reruns a key enqueued while it is running", async () => {
|
|
25
|
+
const queue = createTaskQueue({ onError: () => { } });
|
|
26
|
+
const runs = [];
|
|
27
|
+
let release = () => { };
|
|
28
|
+
queue.enqueue("file", async () => {
|
|
29
|
+
runs.push(1);
|
|
30
|
+
await new Promise(resolve => (release = resolve));
|
|
31
|
+
});
|
|
32
|
+
const done = queue.enqueue("file", async () => runs.push(2));
|
|
33
|
+
release();
|
|
34
|
+
await done;
|
|
35
|
+
expect(runs).toEqual([1, 2]);
|
|
36
|
+
});
|
|
37
|
+
it("reports a failed task and keeps draining", async () => {
|
|
38
|
+
const errors = [];
|
|
39
|
+
const queue = createTaskQueue({ onError: error => errors.push(error) });
|
|
40
|
+
const runs = [];
|
|
41
|
+
queue.enqueue("a", async () => {
|
|
42
|
+
throw new Error("boom");
|
|
43
|
+
});
|
|
44
|
+
await queue.enqueue("b", async () => runs.push("b"));
|
|
45
|
+
expect(errors).toHaveLength(1);
|
|
46
|
+
expect(runs).toEqual(["b"]);
|
|
47
|
+
});
|
|
48
|
+
it("starts a fresh drain after the previous one finished", async () => {
|
|
49
|
+
const queue = createTaskQueue({ onError: () => { } });
|
|
50
|
+
const runs = [];
|
|
51
|
+
await queue.enqueue("a", async () => runs.push("a"));
|
|
52
|
+
await queue.enqueue("a", async () => runs.push("again"));
|
|
53
|
+
expect(runs).toEqual(["a", "again"]);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFzay1xdWV1ZS51bml0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc291cmNlL2hlbHBlcnMvdGFzay1xdWV1ZS51bml0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxFQUFFLEVBQUUsRUFBRSxNQUFNLGdCQUFnQixDQUFBO0FBQ3JELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQTtBQUVqRCxRQUFRLENBQUMsaUJBQWlCLEVBQUUsR0FBRyxFQUFFO0lBQy9CLEVBQUUsQ0FBQyx5QkFBeUIsRUFBRSxLQUFLLElBQUksRUFBRTtRQUN2QyxNQUFNLEtBQUssR0FBRyxlQUFlLENBQUMsRUFBRSxPQUFPLEVBQUUsR0FBRyxFQUFFLEdBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQTtRQUNwRCxNQUFNLEdBQUcsR0FBYSxFQUFFLENBQUE7UUFDeEIsTUFBTSxJQUFJLEdBQUcsQ0FBQyxJQUFZLEVBQUUsRUFBVSxFQUFFLEVBQUUsQ0FBQyxLQUFLLElBQUksRUFBRTtZQUNwRCxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsSUFBSSxFQUFFLENBQUMsQ0FBQTtZQUN6QixNQUFNLElBQUksT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFBO1lBQ3JELEdBQUcsQ0FBQyxJQUFJLENBQUMsT0FBTyxJQUFJLEVBQUUsQ0FBQyxDQUFBO1FBQ3pCLENBQUMsQ0FBQTtRQUNELEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQTtRQUNqQyxNQUFNLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQTtRQUN0QyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsT0FBTyxD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QUFDLENBQUMsQ0FBQTtJQUM3QixDQUFDLENBQUMsQ0FBQTtJQUVGLEVBQUUsQ0FBQyxzREFBc0QsRUFBRSxLQUFLLElBQUksRUFBRTtRQUNwRSxNQUFNLEtBQUssR0FBRyxlQUFlLENBQUMsRUFBRSxPQUFPLEVBQUUsR0FBRyxFQUFFLEdBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQTtRQUNwRCxNQUFNLElBQUksR0FBYSxFQUFFLENBQUE7UUFDekIsTUFBTSxLQUFLLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxLQUFLLElBQUksRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQTtRQUNwRCxNQUFNLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLEtBQUssSUFBSSxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFBO1FBQ3hELE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQTtJQUN0QyxDQUFDLENBQUMsQ0FBQTtBQUNKLENBQUMsQ0FBQyxDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZGVzY3JpYmUsIGV4cGVjdCwgaXQgfSBmcm9tIFwidml0ZS1wbHVzL3Rlc3RcIlxuaW1wb3J0IHsgY3JlYXRlVGFza1F1ZXVlIH0gZnJvbSBcIi4vdGFzay1xdWV1ZS50c1wiXG5cbmRlc2NyaWJlKFwiY3JlYXRlVGFza1F1ZXVlXCIsICgpID0+IHtcbiAgaXQoXCJydW5zIG9uZSB0YXNrIGF0IGEgdGltZVwiLCBhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgcXVldWUgPSBjcmVhdGVUYXNrUXVldWUoeyBvbkVycm9yOiAoKSA9PiB7fSB9KVxuICAgIGNvbnN0IGxvZzogc3RyaW5nW10gPSBbXVxuICAgIGNvbnN0IHRhc2sgPSAobmFtZTogc3RyaW5nLCBtczogbnVtYmVyKSA9PiBhc3luYyAoKSA9PiB7XG4gICAgICBsb2cucHVzaChgc3RhcnQgJHtuYW1lfWApXG4gICAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgbXMpKVxuICAgICAgbG9nLnB1c2goYGVuZCAke25hbWV9YClcbiAgICB9XG4gICAgcXVldWUuZW5xdWV1ZShcImFcIiwgdGFzayhcImFcIiwgMzApKVxuICAgIGF3YWl0IHF1ZXVlLmVucXVldWUoXCJiXCIsIHRhc2soXCJiXCIsIDEpKVxuICAgIGV4cGVjdChsb2cpLnRvRXF1YWwoW1wic3RhcnQgYVwiLCBcImVuZCBhXCIsIFwic3RhcnQgYlwiLCBcImVuZCBiXCJdKVxuICB9KVxuXG4gIGl0KFwiY29sbGFwc2VzIGEga2V5IGVucXVldWVkIGFnYWluIGJlZm9yZSBpdCBzdGFydHNcIiwgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IHF1ZXVlID0gY3JlYXRlVGFza1F1ZXVlKHsgb25FcnJvcjogKCkgPT4ge30gfSlcbiAgICBjb25zdCBydW5zOiBzdHJpbmdbXSA9IFtdXG4gICAgcXVldWUuZW5xdWV1ZShcImZpcnN0XCIsIGFzeW5jICgpID0+IHJ1bnMucHVzaChcImZpcnN0XCIpKVxuICAgIHF1ZXVlLmVucXVldWUoXCJmaWxlXCIsIGFzeW5jICgpID0+IHJ1bnMucHVzaChcInN0YWxlXCIpKVxuICAgIGF3YWl0IHF1ZXVlLmVucXVldWUoXCJmaWxlXCIsIGFzeW5jICgpID0+IHJ1bnMucHVzaChcImxhdGVzdFwiKSlcbiAgICBleHBlY3QocnVucykudG9FcXVhbChbXCJmaXJzdFwiLCBcImxhdGVzdFwiXSlcbiAgfSlcblxuICBpdChcInJlcnVucyBhIGtleSBlbnF1ZXVlZCB3aGlsZSBpdCBpcyBydW5uaW5nXCIsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCBxdWV1ZSA9IGNyZWF0ZVRhc2tRdWV1ZSh7IG9uRXJyb3I6ICgpID0+IHt9IH0pXG4gICAgY29uc3QgcnVuczogbnVtYmVyW10gPSBbXVxuICAgIGxldCByZWxlYXNlID0gKCkgPT4ge31cbiAgICBxdWV1ZS5lbnF1ZXVlKFwiZmlsZVwiLCBhc3luYyAoKSA9PiB7XG4gICAgICBydW5zLnB1c2goMSlcbiAgICAgIGF3YWl0IG5ldyBQcm9taXNlPHZvaWQ+KHJlc29sdmUgPT4gKHJlbGVhc2UgPSByZXNvbHZlKSlcbiAgICB9KVxuICAgIGNvbnN0IGRvbmUgPSBxdWV1ZS5lbnF1ZXVlKFwiZmlsZVwiLCBhc3luYyAoKSA9PiBydW5zLnB1c2goMikpXG4gICAgcmVsZWFzZSgpXG4gICAgYXdhaXQgZG9uZVxuICAgIGV4cGVjdChydW5zKS50b0VxdWFsKFsxLCAyXSlcbiAgfSlcblxuICBpdChcInJlcG9ydHMgYSBmYWlsZWQgdGFzayBhbmQga2VlcHMgZHJhaW5pbmdcIiwgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGVycm9yczogdW5rbm93bltdID0gW11cbiAgICBjb25zdCBxdWV1ZSA9IGNyZWF0ZVRhc2tRdWV1ZSh7IG9uRXJyb3I6IGVycm9yID0+IGVycm9ycy5wdXNoKGVycm9yKSB9KVxuICAgIGNvbnN0IHJ1bnM6IHN0cmluZ1tdID0gW11cbiAgICBxdWV1ZS5lbnF1ZXVlKFwiYVwiLCBhc3luYyAoKSA9PiB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJib29tXCIpXG4gICAgfSlcbiAgICBhd2FpdCBxdWV1ZS5lbnF1ZXVlKFwiYlwiLCBhc3luYyAoKSA9PiBydW5zLnB1c2goXCJiXCIpKVxuICAgIGV4cGVjdChlcnJvcnMpLnRvSGF2ZUxlbmd0aCgxKVxuICAgIGV4cGVjdChydW5zKS50b0VxdWFsKFtcImJcIl0pXG4gIH0pXG5cbiAgaXQoXCJzdGFydHMgYSBmcmVzaCBkcmFpbiBhZnRlciB0aGUgcHJldmlvdXMgb25lIGZpbmlzaGVkXCIsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCBxdWV1ZSA9IGNyZWF0ZVRhc2tRdWV1ZSh7IG9uRXJyb3I6ICgpID0+IHt9IH0pXG4gICAgY29uc3QgcnVuczogc3RyaW5nW10gPSBbXVxuICAgIGF3YWl0IHF1ZXVlLmVucXVldWUoXCJhXCIsIGFzeW5jICgpID0+IHJ1bnMucHVzaChcImFcIikpXG4gICAgYXdhaXQgcXVldWUuZW5xdWV1ZShcImFcIiwgYXN5bmMgKCkgPT4gcnVucy5wdXNoKFwiYWdhaW5cIikpXG4gICAgZXhwZWN0KHJ1bnMpLnRvRXF1YWwoW1wiYVwiLCBcImFnYWluXCJdKVxuICB9KVxufSlcbiJdfQ==
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
export interface ContentOptions {
|
|
3
|
+
configPath: string;
|
|
4
|
+
contentDir: string;
|
|
5
|
+
}
|
|
6
|
+
/** Build the content collections and rebuild them on edits, one build at a time:
|
|
7
|
+
* `@content-collections/vite` builds per file event, a save fires two, and their
|
|
8
|
+
* overlapping writes tore `allArticles.js` so every request failed. */
|
|
9
|
+
export declare function content(opts: ContentOptions): Plugin;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { existsSync, watch } from "node:fs";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { createBuilder } from "@content-collections/core";
|
|
4
|
+
import { configureLogging } from "@content-collections/integrations";
|
|
5
|
+
import { createTaskQueue } from "../helpers/task-queue.js";
|
|
6
|
+
const IGNORED_PATHS = /(^|[/\\])(node_modules|\.[^/\\]+)([/\\]|$)/;
|
|
7
|
+
/** Build the content collections and rebuild them on edits, one build at a time:
|
|
8
|
+
* `@content-collections/vite` builds per file event, a save fires two, and their
|
|
9
|
+
* overlapping writes tore `allArticles.js` so every request failed. */
|
|
10
|
+
export function content(opts) {
|
|
11
|
+
const queue = createTaskQueue({ onError: error => console.error(error) });
|
|
12
|
+
let builder;
|
|
13
|
+
let initialBuild;
|
|
14
|
+
let watcher;
|
|
15
|
+
return {
|
|
16
|
+
name: "livemark:content",
|
|
17
|
+
config(config) {
|
|
18
|
+
const configPath = resolve(config.root ?? process.cwd(), opts.configPath);
|
|
19
|
+
const generated = join(dirname(configPath), ".content-collections", "generated");
|
|
20
|
+
return {
|
|
21
|
+
optimizeDeps: { exclude: ["content-collections"] },
|
|
22
|
+
resolve: { alias: { "content-collections": generated } },
|
|
23
|
+
...((config.server?.fs?.allow?.length ?? 0) > 0 && {
|
|
24
|
+
server: { fs: { allow: [generated] } },
|
|
25
|
+
}),
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
async configResolved(config) {
|
|
29
|
+
builder = await createBuilder(resolve(config.root, opts.configPath));
|
|
30
|
+
configureLogging(builder);
|
|
31
|
+
},
|
|
32
|
+
buildStart() {
|
|
33
|
+
const current = builder;
|
|
34
|
+
if (!current)
|
|
35
|
+
return;
|
|
36
|
+
// Once, not per environment, and through the queue so an edit landing
|
|
37
|
+
// before it cannot overlap it.
|
|
38
|
+
initialBuild ??= new Promise((resolve, reject) => {
|
|
39
|
+
queue.enqueue("build", () => current.build().then(resolve, reject));
|
|
40
|
+
});
|
|
41
|
+
return initialBuild;
|
|
42
|
+
},
|
|
43
|
+
configureServer() {
|
|
44
|
+
const current = builder;
|
|
45
|
+
if (!current)
|
|
46
|
+
return;
|
|
47
|
+
watcher = watch(opts.contentDir, { recursive: true, ignore: IGNORED_PATHS }, (_, filename) => {
|
|
48
|
+
if (!filename)
|
|
49
|
+
return;
|
|
50
|
+
const file = join(opts.contentDir, filename);
|
|
51
|
+
queue.enqueue(file, () => current.sync(existsSync(file) ? "update" : "delete", file));
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
closeBundle() {
|
|
55
|
+
watcher?.close();
|
|
56
|
+
watcher = undefined;
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidml0ZS1jb250ZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc291cmNlL3BsdWdpbnMvdml0ZS1jb250ZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsS0FBSyxFQUFrQixNQUFNLFNBQVMsQ0FBQTtBQUMzRCxPQUFPLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsTUFBTSxXQUFXLENBQUE7QUFDbEQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLDJCQUEyQixDQUFBO0FBQ3pELE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLG1DQUFtQyxDQUFBO0FBRXBFLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQTtBQVMxRCxNQUFNLGFBQWEsR0FBRyw0Q0FBNEMsQ0FBQTtBQUVsRTs7d0VBRXdFO0FBQ3hFLE1BQU0sVUFBVSxPQUFPLENBQUMsSUFBb0I7SUFDMUMsTUFBTSxLQUFLLEdBQUcsZUFBZSxDQUFDLEVBQUUsT0FBTyxFQUFFLEtBQUssQ0FBQyxFQUFFLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUE7SUFDekUsSUFBSSxPQUE0QixDQUFBO0lBQ2hDLElBQUksWUFBdUMsQ0FBQTtJQUMzQyxJQUFJLE9BQThCLENBQUE7SUFDbEMsT0FBTztRQUNMLElBQUksRUFBRSxrQkFBa0I7UUFDeEIsTUFBTSxDQUFDLE1BQU07WUFDWCxNQUFNLFVBQVUsR0FBRyxPQUFPLENBQUMsTUFBTSxDQUFDLElBQUksSUFBSSxPQUFPLENBQUMsR0FBRyxFQUFFLEVBQUUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFBO1lBQ3pFLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLEVBQUUsc0JBQXNCLEVBQUUsV0FBVyxDQUFDLENBQUE7WUFDaEYsT0FBTztnQkFDTCxZQUFZLEVBQUUsRUFBRSxPQUFPLEVBQUUsQ0FBQyxxQkFBcUIsQ0FBQyxFQUFFO2dCQUNsRCxPQUFPLEVBQUUsRUFBRSxLQUFLLEVBQUUsRUFBRSxxQkFBcUIsRUFBRSxTQUFTLEVBQUUsRUFBRTtnQkFDeEQsR0FBRyxDQUFDLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxFQUFFLEVBQUUsS0FBSyxFQUFFLE1BQU0sSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLElBQUk7b0JBQ2pELE1BQU0sRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRSxDQUFDLFNBQVMsQ0FBQyxFQUFFLEVBQUU7aUJBQ3ZDLENBQUM7YUFDSCxDQUFBO1FBQ0gsQ0FBQztRQUNELEtBQUssQ0FBQyxjQUFjLENBQUMsTUFBTTtZQUN6QixPQUFPLEdBQUcsTUFBTSxhQUFhLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUE7WUFDcEUsZ0JBQWdCLENBQUMsT0FBTyxDQUFDLENBQUE7UUFDM0IsQ0FBQztRQUNELFVBQVU7WUFDUixNQUFNLE9BQU8sR0FBRyxPQUFPLENBQUE7WUFDdkIsSUFBSSxDQUFDLE9BQU87Z0JBQUUsT0FBTTtZQUNwQixzRUFBc0U7WUFDdEUsK0JBQStCO1lBQy9CLFlBQVksS0FBSyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRTtnQkFDL0MsS0FBSyxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsR0FBRyxFQUFFLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQTtZQUNyRSxDQUFDLENBQUMsQ0FBQTtZQUNGLE9BQU8sWUFBWSxDQUFBO1FBQ3JCLENBQUM7UUFDRCxlQUFlO1lBQ2IsTUFBTSxPQUFPLEdBQUcsT0FBTyxDQUFBO1lBQ3ZCLElBQUksQ0FBQyxPQUFPO2dCQUFFLE9BQU07WUFDcEIsT0FBTyxHQUFHLEtBQUssQ0FDYixJQUFJLENBQUMsVUFBVSxFQUNmLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsYUFBYSxFQUFFLEVBQzFDLENBQUMsQ0FBQyxFQUFFLFFBQVEsRUFBRSxFQUFFO2dCQUNkLElBQUksQ0FBQyxRQUFRO29CQUFFLE9BQU07Z0JBQ3JCLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLFFBQVEsQ0FBQyxDQUFBO2dCQUM1QyxLQUFLLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxHQUFHLEVBQUUsQ0FDdkIsT0FBTyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxDQUMzRCxDQUFBO1lBQ0gsQ0FBQyxDQUNGLENBQUE7UUFDSCxDQUFDO1FBQ0QsV0FBVztZQUNULE9BQU8sRUFBRSxLQUFLLEVBQUUsQ0FBQTtZQUNoQixPQUFPLEdBQUcsU0FBUyxDQUFBO1FBQ3JCLENBQUM7S0FDRixDQUFBO0FBQ0gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGV4aXN0c1N5bmMsIHdhdGNoLCB0eXBlIEZTV2F0Y2hlciB9IGZyb20gXCJub2RlOmZzXCJcbmltcG9ydCB7IGRpcm5hbWUsIGpvaW4sIHJlc29sdmUgfSBmcm9tIFwibm9kZTpwYXRoXCJcbmltcG9ydCB7IGNyZWF0ZUJ1aWxkZXIgfSBmcm9tIFwiQGNvbnRlbnQtY29sbGVjdGlvbnMvY29yZVwiXG5pbXBvcnQgeyBjb25maWd1cmVMb2dnaW5nIH0gZnJvbSBcIkBjb250ZW50LWNvbGxlY3Rpb25zL2ludGVncmF0aW9uc1wiXG5pbXBvcnQgdHlwZSB7IFBsdWdpbiB9IGZyb20gXCJ2aXRlXCJcbmltcG9ydCB7IGNyZWF0ZVRhc2tRdWV1ZSB9IGZyb20gXCIuLi9oZWxwZXJzL3Rhc2stcXVldWUudHNcIlxuXG5leHBvcnQgaW50ZXJmYWNlIENvbnRlbnRPcHRpb25zIHtcbiAgY29uZmlnUGF0aDogc3RyaW5nXG4gIGNvbnRlbnREaXI6IHN0cmluZ1xufVxuXG50eXBlIEJ1aWxkZXIgPSBBd2FpdGVkPFJldHVyblR5cGU8dHlwZW9mIGNyZWF0ZUJ1aWxkZXI+PlxuXG5jb25zdCBJR05PUkVEX1BBVEhTID0gLyhefFsvXFxcXF0pKG5vZGVfbW9kdWxlc3xcXC5bXi9cXFxcXSspKFsvXFxcXF18JCkvXG5cbi8qKiBCdWlsZCB0aGUgY29udGVudCBjb2xsZWN0aW9ucyBhbmQgcmVidWlsZCB0aGVtIG9uIGVkaXRzLCBvbmUgYnVpbGQgYXQgYSB0aW1lOlxuICogIGBAY29udGVudC1jb2xsZWN0aW9ucy92aXRlYCBidWlsZHMgcGVyIGZpbGUgZXZlbnQsIGEgc2F2ZSBmaXJlcyB0d28sIGFuZCB0aGVpclxuICogIG92ZXJsYXBwaW5nIHdyaXRlcyB0b3JlIGBhbGxBcnRpY2xlcy5qc2Agc28gZXZlcnkgcmVxdWVzdCBmYWlsZWQuICovXG5leHBvcnQgZnVuY3Rpb24gY29udGVudChvcHRzOiBDb250ZW50T3B0aW9ucyk6IFBsdWdpbiB7XG4gIGNvbnN0IHF1ZXVlID0gY3JlYXRlVGFza1F1ZXVlKHsgb25FcnJvcjogZXJyb3IgPT4gY29uc29sZS5lcnJvcihlcnJvcikgfSlcbiAgbGV0IGJ1aWxkZXI6IEJ1aWxkZXIgfCB1bmRlZmluZWRcbiAgbGV0IGluaXRpYWxCdWlsZDogUHJvbWlzZTx2b2lkPiB8IHVuZGVmaW5lZFxuICBsZXQgd2F0Y2hlcjogRlNXYXRjaGVyIHwgdW5kZWZpbmVkXG4gIHJldHVybiB7XG4gICAgbmFtZTogXCJsaXZlbWFyazpjb250ZW50XCIsXG4gICAgY29uZmlnKGNvbmZpZykge1xuICAgICAgY29uc3QgY29uZmlnUGF0aCA9IHJlc29sdmUoY29uZmlnLnJvb3QgPz8gcHJvY2Vzcy5jd2QoKSwgb3B0cy5jb25maWdQYXRoKVxuICAgICAgY29uc3QgZ2VuZXJhdGVkID0gam9pbihkaXJuYW1lKGNvbmZpZ1BhdGgpLCBcIi5jb250ZW50LWNvbGxlY3Rpb25zXCIsIFwiZ2VuZXJhdGVkXCIpXG4gICAgICByZXR1cm4ge1xuICAgICAgICBvcHRpbWl6ZURlcHM6IHsgZXhjbHVkZTogW1wiY29udGVudC1jb2xsZWN0aW9uc1wiXSB9LFxuICAgICAgICByZXNvbHZlOiB7IGFsaWFzOiB7IFwiY29udGVudC1jb2xsZWN0aW9uc1wiOiBnZW5lcmF0ZWQgfSB9LFxuICAgICAgICAuLi4oKGNvbmZpZy5zZXJ2ZXI/LmZzPy5hbGxvdz8ubGVuZ3RoID8/IDApID4gMCAmJiB7XG4gICAgICAgICAgc2VydmVyOiB7IGZzOiB7IGFsbG93OiBbZ2VuZXJhdGVkXSB9IH0sXG4gICAgICAgIH0pLFxuICAgICAgfVxuICAgIH0sXG4gICAgYXN5bmMgY29uZmlnUmVzb2x2ZWQoY29uZmlnKSB7XG4gICAgICBidWlsZGVyID0gYXdhaXQgY3JlYXRlQnVpbGRlcihyZXNvbHZlKGNvbmZpZy5yb290LCBvcHRzLmNvbmZpZ1BhdGgpKVxuICAgICAgY29uZmlndXJlTG9nZ2luZyhidWlsZGVyKVxuICAgIH0sXG4gICAgYnVpbGRTdGFydCgpIHtcbiAgICAgIGNvbnN0IGN1cnJlbnQgPSBidWlsZGVyXG4gICAgICBpZiAoIWN1cnJlbnQpIHJldHVyblxuICAgICAgLy8gT25jZSwgbm90IHBlciBlbnZpcm9ubWVudCwgYW5kIHRocm91Z2ggdGhlIHF1ZXVlIHNvIGFuIGVkaXQgbGFuZGluZ1xuICAgICAgLy8gYmVmb3JlIGl0IGNhbm5vdCBvdmVybGFwIGl0LlxuICAgICAgaW5pdGlhbEJ1aWxkID8/PSBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgIHF1ZXVlLmVucXVldWUoXCJidWlsZFwiLCAoKSA9PiBjdXJyZW50LmJ1aWxkKCkudGhlbihyZXNvbHZlLCByZWplY3QpKVxuICAgICAgfSlcbiAgICAgIHJldHVybiBpbml0aWFsQnVpbGRcbiAgICB9LFxuICAgIGNvbmZpZ3VyZVNlcnZlcigpIHtcbiAgICAgIGNvbnN0IGN1cnJlbnQgPSBidWlsZGVyXG4gICAgICBpZiAoIWN1cnJlbnQpIHJldHVyblxuICAgICAgd2F0Y2hlciA9IHdhdGNoKFxuICAgICAgICBvcHRzLmNvbnRlbnREaXIsXG4gICAgICAgIHsgcmVjdXJzaXZlOiB0cnVlLCBpZ25vcmU6IElHTk9SRURfUEFUSFMgfSxcbiAgICAgICAgKF8sIGZpbGVuYW1lKSA9PiB7XG4gICAgICAgICAgaWYgKCFmaWxlbmFtZSkgcmV0dXJuXG4gICAgICAgICAgY29uc3QgZmlsZSA9IGpvaW4ob3B0cy5jb250ZW50RGlyLCBmaWxlbmFtZSlcbiAgICAgICAgICBxdWV1ZS5lbnF1ZXVlKGZpbGUsICgpID0+XG4gICAgICAgICAgICBjdXJyZW50LnN5bmMoZXhpc3RzU3luYyhmaWxlKSA/IFwidXBkYXRlXCIgOiBcImRlbGV0ZVwiLCBmaWxlKSxcbiAgICAgICAgICApXG4gICAgICAgIH0sXG4gICAgICApXG4gICAgfSxcbiAgICBjbG9zZUJ1bmRsZSgpIHtcbiAgICAgIHdhdGNoZXI/LmNsb3NlKClcbiAgICAgIHdhdGNoZXIgPSB1bmRlZmluZWRcbiAgICB9LFxuICB9XG59XG4iXX0=
|
package/build/vite.config.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import contentCollections from "@content-collections/vite";
|
|
2
1
|
import tailwind from "@tailwindcss/vite";
|
|
3
2
|
import { devtools } from "@tanstack/devtools-vite";
|
|
4
3
|
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
|
|
@@ -7,6 +6,7 @@ import { defineConfig } from "vite";
|
|
|
7
6
|
import svgr from "vite-plugin-svgr";
|
|
8
7
|
import { loadConfig } from "./actions/config/load.js";
|
|
9
8
|
import { agents } from "./plugins/vite-agents.js";
|
|
9
|
+
import { content } from "./plugins/vite-content.js";
|
|
10
10
|
import { livemark } from "./plugins/vite-livemark.js";
|
|
11
11
|
import { robots } from "./plugins/vite-robots.js";
|
|
12
12
|
const config = await loadConfig();
|
|
@@ -31,7 +31,7 @@ export default defineConfig({
|
|
|
31
31
|
// any browser error back and forth forever.
|
|
32
32
|
devtools({ consolePiping: { enabled: false } }),
|
|
33
33
|
tailwind(),
|
|
34
|
-
|
|
34
|
+
content({ configPath: "content-collections.ts", contentDir: config.root }),
|
|
35
35
|
tanstackStart({
|
|
36
36
|
srcDirectory: ".",
|
|
37
37
|
// Seed path must match what the crawler produces (router-emitted
|
|
@@ -57,4 +57,4 @@ export default defineConfig({
|
|
|
57
57
|
svgr(),
|
|
58
58
|
],
|
|
59
59
|
});
|
|
60
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
60
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidml0ZS5jb25maWcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zb3VyY2Uvdml0ZS5jb25maWcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxRQUFRLE1BQU0sbUJBQW1CLENBQUE7QUFDeEMsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLHlCQUF5QixDQUFBO0FBQ2xELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQTtBQUNqRSxPQUFPLEtBQUssTUFBTSxzQkFBc0IsQ0FBQTtBQUN4QyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sTUFBTSxDQUFBO0FBQ25DLE9BQU8sSUFBSSxNQUFNLGtCQUFrQixDQUFBO0FBQ25DLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQTtBQUNyRCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sMEJBQTBCLENBQUE7QUFDakQsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLDJCQUEyQixDQUFBO0FBQ25ELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQTtBQUNyRCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sMEJBQTBCLENBQUE7QUFFakQsTUFBTSxNQUFNLEdBQUcsTUFBTSxVQUFVLEVBQUUsQ0FBQTtBQUVqQyx1RUFBdUU7QUFDdkUsd0VBQXdFO0FBQ3hFLHlFQUF5RTtBQUN6RSxtREFBbUQ7QUFDbkQsTUFBTSxhQUFhLEdBQUcsTUFBTSxDQUFDLElBQUk7SUFDL0IsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLFFBQVEsSUFBSSxFQUFFLENBQUM7U0FDcEIsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksS0FBSyxNQUFNLENBQUM7U0FDOUIsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUNULElBQUksRUFBRSxHQUFHLE1BQU0sQ0FBQyxJQUFJLElBQUksRUFBRSxHQUFHLENBQUMsQ0FBQyxNQUFNLFNBQVM7UUFDOUMsT0FBTyxFQUFFLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRTtLQUMzQixDQUFDLENBQUM7SUFDUCxDQUFDLENBQUMsRUFBRSxDQUFBO0FBRU4sZUFBZSxZQUFZLENBQUM7SUFDMUIsT0FBTyxFQUFFO1FBQ1AsUUFBUSxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUM7UUFDcEIsTUFBTSxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUM7UUFDbEIsTUFBTSxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUM7UUFDbEIsb0ZBQW9GO1FBQ3BGLDRDQUE0QztRQUM1QyxRQUFRLENBQUMsRUFBRSxhQUFhLEVBQUUsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLEVBQUUsQ0FBQztRQUMvQyxRQUFRLEVBQUU7UUFDVixPQUFPLENBQUMsRUFBRSxVQUFVLEVBQUUsd0JBQXdCLEVBQUUsVUFBVSxFQUFFLE1BQU0sQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUMxRSxhQUFhLENBQUM7WUFDWixZQUFZLEVBQUUsR0FBRztZQUNqQixpRUFBaUU7WUFDakUsZ0VBQWdFO1lBQ2hFLHlEQUF5RDtZQUN6RCxLQUFLLEVBQUUsQ0FBQyxFQUFFLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxJQUFJLEdBQUcsQ0FBQyxDQUFDLENBQUMsR0FBRyxFQUFFLEVBQUUsR0FBRyxhQUFhLENBQUM7WUFDMUUsZ0VBQWdFO1lBQ2hFLG9FQUFvRTtZQUNwRSxrRUFBa0U7WUFDbEUsNkJBQTZCO1lBQzdCLFNBQVMsRUFBRTtnQkFDVCxPQUFPLEVBQUUsSUFBSTtnQkFDYixVQUFVLEVBQUUsSUFBSTtnQkFDaEIsd0JBQXdCLEVBQUUsS0FBSzthQUNoQztZQUNELE9BQU8sRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFO1lBQ2hGLE1BQU0sRUFBRTtnQkFDTixrQkFBa0IsRUFBRSxrQkFBa0I7Z0JBQ3RDLFFBQVEsRUFBRSxNQUFNLENBQUMsSUFBSTthQUN0QjtTQUNGLENBQUM7UUFDRixLQUFLLEVBQUU7UUFDUCxJQUFJLEVBQUU7S0FDUDtDQUNGLENBQUMsQ0FBQSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0YWlsd2luZCBmcm9tIFwiQHRhaWx3aW5kY3NzL3ZpdGVcIlxuaW1wb3J0IHsgZGV2dG9vbHMgfSBmcm9tIFwiQHRhbnN0YWNrL2RldnRvb2xzLXZpdGVcIlxuaW1wb3J0IHsgdGFuc3RhY2tTdGFydCB9IGZyb20gXCJAdGFuc3RhY2svcmVhY3Qtc3RhcnQvcGx1Z2luL3ZpdGVcIlxuaW1wb3J0IHJlYWN0IGZyb20gXCJAdml0ZWpzL3BsdWdpbi1yZWFjdFwiXG5pbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tIFwidml0ZVwiXG5pbXBvcnQgc3ZnciBmcm9tIFwidml0ZS1wbHVnaW4tc3ZnclwiXG5pbXBvcnQgeyBsb2FkQ29uZmlnIH0gZnJvbSBcIi4vYWN0aW9ucy9jb25maWcvbG9hZC50c1wiXG5pbXBvcnQgeyBhZ2VudHMgfSBmcm9tIFwiLi9wbHVnaW5zL3ZpdGUtYWdlbnRzLnRzXCJcbmltcG9ydCB7IGNvbnRlbnQgfSBmcm9tIFwiLi9wbHVnaW5zL3ZpdGUtY29udGVudC50c1wiXG5pbXBvcnQgeyBsaXZlbWFyayB9IGZyb20gXCIuL3BsdWdpbnMvdml0ZS1saXZlbWFyay50c1wiXG5pbXBvcnQgeyByb2JvdHMgfSBmcm9tIFwiLi9wbHVnaW5zL3ZpdGUtcm9ib3RzLnRzXCJcblxuY29uc3QgY29uZmlnID0gYXdhaXQgbG9hZENvbmZpZygpXG5cbi8vIFBlci1ibG9nLXNlY3Rpb24gUlNTIGZlZWQgcGF0aHMgdG8gcHJlcmVuZGVyLiBHYXRlZCBvbiBgY29uZmlnLnNpdGVgXG4vLyBiZWNhdXNlIGZlZWQgVVJMcyBhcmUgYWJzb2x1dGUgKG1hdGNoaW5nIHRoZSBzaXRlbWFwL3JvYm90cy50eHQgZ2F0ZSlcbi8vIGFuZCB0aGUgcm91dGUgcmV0dXJucyA0MDQgd2l0aG91dCBpdCBhbnl3YXkuIEV4Y2x1ZGVkIGZyb20gdGhlIHNpdGVtYXBcbi8vIHNpbmNlIGAueG1sYCBwYXlsb2FkcyBkb24ndCBiZWxvbmcgaW4gYSBzaXRlbWFwLlxuY29uc3QgYmxvZ0ZlZWRQYWdlcyA9IGNvbmZpZy5zaXRlXG4gID8gKGNvbmZpZy5zZWN0aW9ucyA/PyBbXSlcbiAgICAgIC5maWx0ZXIocyA9PiBzLnR5cGUgPT09IFwiYmxvZ1wiKVxuICAgICAgLm1hcChzID0+ICh7XG4gICAgICAgIHBhdGg6IGAke2NvbmZpZy5iYXNlID8/IFwiXCJ9JHtzLnByZWZpeH1yc3MueG1sYCxcbiAgICAgICAgc2l0ZW1hcDogeyBleGNsdWRlOiB0cnVlIH0sXG4gICAgICB9KSlcbiAgOiBbXVxuXG5leHBvcnQgZGVmYXVsdCBkZWZpbmVDb25maWcoe1xuICBwbHVnaW5zOiBbXG4gICAgbGl2ZW1hcmsoeyBjb25maWcgfSksXG4gICAgcm9ib3RzKHsgY29uZmlnIH0pLFxuICAgIGFnZW50cyh7IGNvbmZpZyB9KSxcbiAgICAvLyBJdHMgc2VydmVyLXRvLWJyb3dzZXIgY29uc29sZSBwaXBpbmcgYW5kIFZpdGUncyBicm93c2VyLXRvLXNlcnZlciBmb3J3YXJkaW5nIGVjaG9cbiAgICAvLyBhbnkgYnJvd3NlciBlcnJvciBiYWNrIGFuZCBmb3J0aCBmb3JldmVyLlxuICAgIGRldnRvb2xzKHsgY29uc29sZVBpcGluZzogeyBlbmFibGVkOiBmYWxzZSB9IH0pLFxuICAgIHRhaWx3aW5kKCksXG4gICAgY29udGVudCh7IGNvbmZpZ1BhdGg6IFwiY29udGVudC1jb2xsZWN0aW9ucy50c1wiLCBjb250ZW50RGlyOiBjb25maWcucm9vdCB9KSxcbiAgICB0YW5zdGFja1N0YXJ0KHtcbiAgICAgIHNyY0RpcmVjdG9yeTogXCIuXCIsXG4gICAgICAvLyBTZWVkIHBhdGggbXVzdCBtYXRjaCB3aGF0IHRoZSBjcmF3bGVyIHByb2R1Y2VzIChyb3V0ZXItZW1pdHRlZFxuICAgICAgLy8gaHJlZnMgaW5jbHVkZSB0aGUgYmFzZXBhdGgpLCBvdGhlcndpc2UgcHJlcmVuZGVyIHJlY29yZHMgYm90aFxuICAgICAgLy8gc2hhcGVzIGFuZCB0aGUgc2l0ZW1hcCBlbmRzIHVwIHdpdGggZHVwbGljYXRlIGVudHJpZXMuXG4gICAgICBwYWdlczogW3sgcGF0aDogY29uZmlnLmJhc2UgPyBgJHtjb25maWcuYmFzZX0vYCA6IFwiL1wiIH0sIC4uLmJsb2dGZWVkUGFnZXNdLFxuICAgICAgLy8gRGlzYWJsZSByb3V0ZS10cmVlIGF1dG8tZGlzY292ZXJ5OiBpdCBwcm9kdWNlcyBiYXNlcGF0aC1uYWl2ZVxuICAgICAgLy8gcGF0aHMgKGUuZy4gYC9gKSB0aGF0IGR1cGxpY2F0ZSB0aGUgY3Jhd2wtZGVyaXZlZCBwcmVmaXhlZCBwYXRocyxcbiAgICAgIC8vIGJsb2F0aW5nIHBhZ2VzLmpzb24gYW5kIHNpdGVtYXAuIENyYXdsaW5nIGZyb20gdGhlIHNlZWQgYWxyZWFkeVxuICAgICAgLy8gcmVhY2hlcyBldmVyeSBsaW5rZWQgcGFnZS5cbiAgICAgIHByZXJlbmRlcjoge1xuICAgICAgICBlbmFibGVkOiB0cnVlLFxuICAgICAgICBjcmF3bExpbmtzOiB0cnVlLFxuICAgICAgICBhdXRvU3RhdGljUGF0aHNEaXNjb3Zlcnk6IGZhbHNlLFxuICAgICAgfSxcbiAgICAgIHNpdGVtYXA6IGNvbmZpZy5zaXRlID8geyBlbmFibGVkOiB0cnVlLCBob3N0OiBjb25maWcuc2l0ZSB9IDogeyBlbmFibGVkOiBmYWxzZSB9LFxuICAgICAgcm91dGVyOiB7XG4gICAgICAgIGdlbmVyYXRlZFJvdXRlVHJlZTogXCJyb3V0ZVRyZWUuZ2VuLnRzXCIsXG4gICAgICAgIGJhc2VwYXRoOiBjb25maWcuYmFzZSxcbiAgICAgIH0sXG4gICAgfSksXG4gICAgcmVhY3QoKSxcbiAgICBzdmdyKCksXG4gIF0sXG59KVxuIl19
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "livemark",
|
|
3
|
-
"version": "0.28.
|
|
3
|
+
"version": "0.28.4",
|
|
4
4
|
"description": "Livemark description",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Evgeny Karev",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"@base-ui/react": "1.9.0",
|
|
31
31
|
"@clack/prompts": "1.8.1",
|
|
32
32
|
"@content-collections/core": "0.15.3",
|
|
33
|
+
"@content-collections/integrations": "0.5.0",
|
|
33
34
|
"@content-collections/mdx": "0.2.2",
|
|
34
|
-
"@content-collections/vite": "0.3.1",
|
|
35
35
|
"@orama/orama": "^3.1.18",
|
|
36
36
|
"@shikijs/rehype": "4.5.0",
|
|
37
37
|
"@shikijs/twoslash": "^4.5.0",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Run keyed async tasks one at a time; a key enqueued again before its task
|
|
2
|
+
* starts replaces it, so a burst of events for one file costs one run. */
|
|
3
|
+
export function createTaskQueue(props: { onError: (error: unknown) => void }) {
|
|
4
|
+
const pending = new Map<string, () => Promise<unknown>>()
|
|
5
|
+
let draining: Promise<void> | undefined
|
|
6
|
+
|
|
7
|
+
return {
|
|
8
|
+
enqueue(key: string, task: () => Promise<unknown>) {
|
|
9
|
+
pending.set(key, task)
|
|
10
|
+
draining ??= drain()
|
|
11
|
+
return draining
|
|
12
|
+
},
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function drain() {
|
|
16
|
+
// A Map iterator also visits entries set during iteration, which is what
|
|
17
|
+
// picks up keys enqueued while a task runs.
|
|
18
|
+
for (const [key, task] of pending) {
|
|
19
|
+
pending.delete(key)
|
|
20
|
+
try {
|
|
21
|
+
await task()
|
|
22
|
+
} catch (error) {
|
|
23
|
+
props.onError(error)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
draining = undefined
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, expect, it } from "vite-plus/test"
|
|
2
|
+
import { createTaskQueue } from "./task-queue.ts"
|
|
3
|
+
|
|
4
|
+
describe("createTaskQueue", () => {
|
|
5
|
+
it("runs one task at a time", async () => {
|
|
6
|
+
const queue = createTaskQueue({ onError: () => {} })
|
|
7
|
+
const log: string[] = []
|
|
8
|
+
const task = (name: string, ms: number) => async () => {
|
|
9
|
+
log.push(`start ${name}`)
|
|
10
|
+
await new Promise(resolve => setTimeout(resolve, ms))
|
|
11
|
+
log.push(`end ${name}`)
|
|
12
|
+
}
|
|
13
|
+
queue.enqueue("a", task("a", 30))
|
|
14
|
+
await queue.enqueue("b", task("b", 1))
|
|
15
|
+
expect(log).toEqual(["start a", "end a", "start b", "end b"])
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it("collapses a key enqueued again before it starts", async () => {
|
|
19
|
+
const queue = createTaskQueue({ onError: () => {} })
|
|
20
|
+
const runs: string[] = []
|
|
21
|
+
queue.enqueue("first", async () => runs.push("first"))
|
|
22
|
+
queue.enqueue("file", async () => runs.push("stale"))
|
|
23
|
+
await queue.enqueue("file", async () => runs.push("latest"))
|
|
24
|
+
expect(runs).toEqual(["first", "latest"])
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it("reruns a key enqueued while it is running", async () => {
|
|
28
|
+
const queue = createTaskQueue({ onError: () => {} })
|
|
29
|
+
const runs: number[] = []
|
|
30
|
+
let release = () => {}
|
|
31
|
+
queue.enqueue("file", async () => {
|
|
32
|
+
runs.push(1)
|
|
33
|
+
await new Promise<void>(resolve => (release = resolve))
|
|
34
|
+
})
|
|
35
|
+
const done = queue.enqueue("file", async () => runs.push(2))
|
|
36
|
+
release()
|
|
37
|
+
await done
|
|
38
|
+
expect(runs).toEqual([1, 2])
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it("reports a failed task and keeps draining", async () => {
|
|
42
|
+
const errors: unknown[] = []
|
|
43
|
+
const queue = createTaskQueue({ onError: error => errors.push(error) })
|
|
44
|
+
const runs: string[] = []
|
|
45
|
+
queue.enqueue("a", async () => {
|
|
46
|
+
throw new Error("boom")
|
|
47
|
+
})
|
|
48
|
+
await queue.enqueue("b", async () => runs.push("b"))
|
|
49
|
+
expect(errors).toHaveLength(1)
|
|
50
|
+
expect(runs).toEqual(["b"])
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it("starts a fresh drain after the previous one finished", async () => {
|
|
54
|
+
const queue = createTaskQueue({ onError: () => {} })
|
|
55
|
+
const runs: string[] = []
|
|
56
|
+
await queue.enqueue("a", async () => runs.push("a"))
|
|
57
|
+
await queue.enqueue("a", async () => runs.push("again"))
|
|
58
|
+
expect(runs).toEqual(["a", "again"])
|
|
59
|
+
})
|
|
60
|
+
})
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { existsSync, watch, type FSWatcher } from "node:fs"
|
|
2
|
+
import { dirname, join, resolve } from "node:path"
|
|
3
|
+
import { createBuilder } from "@content-collections/core"
|
|
4
|
+
import { configureLogging } from "@content-collections/integrations"
|
|
5
|
+
import type { Plugin } from "vite"
|
|
6
|
+
import { createTaskQueue } from "../helpers/task-queue.ts"
|
|
7
|
+
|
|
8
|
+
export interface ContentOptions {
|
|
9
|
+
configPath: string
|
|
10
|
+
contentDir: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type Builder = Awaited<ReturnType<typeof createBuilder>>
|
|
14
|
+
|
|
15
|
+
const IGNORED_PATHS = /(^|[/\\])(node_modules|\.[^/\\]+)([/\\]|$)/
|
|
16
|
+
|
|
17
|
+
/** Build the content collections and rebuild them on edits, one build at a time:
|
|
18
|
+
* `@content-collections/vite` builds per file event, a save fires two, and their
|
|
19
|
+
* overlapping writes tore `allArticles.js` so every request failed. */
|
|
20
|
+
export function content(opts: ContentOptions): Plugin {
|
|
21
|
+
const queue = createTaskQueue({ onError: error => console.error(error) })
|
|
22
|
+
let builder: Builder | undefined
|
|
23
|
+
let initialBuild: Promise<void> | undefined
|
|
24
|
+
let watcher: FSWatcher | undefined
|
|
25
|
+
return {
|
|
26
|
+
name: "livemark:content",
|
|
27
|
+
config(config) {
|
|
28
|
+
const configPath = resolve(config.root ?? process.cwd(), opts.configPath)
|
|
29
|
+
const generated = join(dirname(configPath), ".content-collections", "generated")
|
|
30
|
+
return {
|
|
31
|
+
optimizeDeps: { exclude: ["content-collections"] },
|
|
32
|
+
resolve: { alias: { "content-collections": generated } },
|
|
33
|
+
...((config.server?.fs?.allow?.length ?? 0) > 0 && {
|
|
34
|
+
server: { fs: { allow: [generated] } },
|
|
35
|
+
}),
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
async configResolved(config) {
|
|
39
|
+
builder = await createBuilder(resolve(config.root, opts.configPath))
|
|
40
|
+
configureLogging(builder)
|
|
41
|
+
},
|
|
42
|
+
buildStart() {
|
|
43
|
+
const current = builder
|
|
44
|
+
if (!current) return
|
|
45
|
+
// Once, not per environment, and through the queue so an edit landing
|
|
46
|
+
// before it cannot overlap it.
|
|
47
|
+
initialBuild ??= new Promise((resolve, reject) => {
|
|
48
|
+
queue.enqueue("build", () => current.build().then(resolve, reject))
|
|
49
|
+
})
|
|
50
|
+
return initialBuild
|
|
51
|
+
},
|
|
52
|
+
configureServer() {
|
|
53
|
+
const current = builder
|
|
54
|
+
if (!current) return
|
|
55
|
+
watcher = watch(
|
|
56
|
+
opts.contentDir,
|
|
57
|
+
{ recursive: true, ignore: IGNORED_PATHS },
|
|
58
|
+
(_, filename) => {
|
|
59
|
+
if (!filename) return
|
|
60
|
+
const file = join(opts.contentDir, filename)
|
|
61
|
+
queue.enqueue(file, () =>
|
|
62
|
+
current.sync(existsSync(file) ? "update" : "delete", file),
|
|
63
|
+
)
|
|
64
|
+
},
|
|
65
|
+
)
|
|
66
|
+
},
|
|
67
|
+
closeBundle() {
|
|
68
|
+
watcher?.close()
|
|
69
|
+
watcher = undefined
|
|
70
|
+
},
|
|
71
|
+
}
|
|
72
|
+
}
|
package/source/vite.config.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import contentCollections from "@content-collections/vite"
|
|
2
1
|
import tailwind from "@tailwindcss/vite"
|
|
3
2
|
import { devtools } from "@tanstack/devtools-vite"
|
|
4
3
|
import { tanstackStart } from "@tanstack/react-start/plugin/vite"
|
|
@@ -7,6 +6,7 @@ import { defineConfig } from "vite"
|
|
|
7
6
|
import svgr from "vite-plugin-svgr"
|
|
8
7
|
import { loadConfig } from "./actions/config/load.ts"
|
|
9
8
|
import { agents } from "./plugins/vite-agents.ts"
|
|
9
|
+
import { content } from "./plugins/vite-content.ts"
|
|
10
10
|
import { livemark } from "./plugins/vite-livemark.ts"
|
|
11
11
|
import { robots } from "./plugins/vite-robots.ts"
|
|
12
12
|
|
|
@@ -34,7 +34,7 @@ export default defineConfig({
|
|
|
34
34
|
// any browser error back and forth forever.
|
|
35
35
|
devtools({ consolePiping: { enabled: false } }),
|
|
36
36
|
tailwind(),
|
|
37
|
-
|
|
37
|
+
content({ configPath: "content-collections.ts", contentDir: config.root }),
|
|
38
38
|
tanstackStart({
|
|
39
39
|
srcDirectory: ".",
|
|
40
40
|
// Seed path must match what the crawler produces (router-emitted
|