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.
@@ -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=
@@ -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
- contentCollections({ configPath: "content-collections.ts" }),
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidml0ZS5jb25maWcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zb3VyY2Uvdml0ZS5jb25maWcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxrQkFBa0IsTUFBTSwyQkFBMkIsQ0FBQTtBQUMxRCxPQUFPLFFBQVEsTUFBTSxtQkFBbUIsQ0FBQTtBQUN4QyxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0seUJBQXlCLENBQUE7QUFDbEQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLG1DQUFtQyxDQUFBO0FBQ2pFLE9BQU8sS0FBSyxNQUFNLHNCQUFzQixDQUFBO0FBQ3hDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxNQUFNLENBQUE7QUFDbkMsT0FBTyxJQUFJLE1BQU0sa0JBQWtCLENBQUE7QUFDbkMsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLDBCQUEwQixDQUFBO0FBQ3JELE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQTtBQUNqRCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sNEJBQTRCLENBQUE7QUFDckQsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDBCQUEwQixDQUFBO0FBRWpELE1BQU0sTUFBTSxHQUFHLE1BQU0sVUFBVSxFQUFFLENBQUE7QUFFakMsdUVBQXVFO0FBQ3ZFLHdFQUF3RTtBQUN4RSx5RUFBeUU7QUFDekUsbURBQW1EO0FBQ25ELE1BQU0sYUFBYSxHQUFHLE1BQU0sQ0FBQyxJQUFJO0lBQy9CLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxRQUFRLElBQUksRUFBRSxDQUFDO1NBQ3BCLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLEtBQUssTUFBTSxDQUFDO1NBQzlCLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDVCxJQUFJLEVBQUUsR0FBRyxNQUFNLENBQUMsSUFBSSxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUMsTUFBTSxTQUFTO1FBQzlDLE9BQU8sRUFBRSxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUU7S0FDM0IsQ0FBQyxDQUFDO0lBQ1AsQ0FBQyxDQUFDLEVBQUUsQ0FBQTtBQUVOLGVBQWUsWUFBWSxDQUFDO0lBQzFCLE9BQU8sRUFBRTtRQUNQLFFBQVEsQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDO1FBQ3BCLE1BQU0sQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDO1FBQ2xCLE1BQU0sQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDO1FBQ2xCLG9GQUFvRjtRQUNwRiw0Q0FBNEM7UUFDNUMsUUFBUSxDQUFDLEVBQUUsYUFBYSxFQUFFLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxFQUFFLENBQUM7UUFDL0MsUUFBUSxFQUFFO1FBQ1Ysa0JBQWtCLENBQUMsRUFBRSxVQUFVLEVBQUUsd0JBQXdCLEVBQUUsQ0FBQztRQUM1RCxhQUFhLENBQUM7WUFDWixZQUFZLEVBQUUsR0FBRztZQUNqQixpRUFBaUU7WUFDakUsZ0VBQWdFO1lBQ2hFLHlEQUF5RDtZQUN6RCxLQUFLLEVBQUUsQ0FBQyxFQUFFLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxJQUFJLEdBQUcsQ0FBQyxDQUFDLENBQUMsR0FBRyxFQUFFLEVBQUUsR0FBRyxhQUFhLENBQUM7WUFDMUUsZ0VBQWdFO1lBQ2hFLG9FQUFvRTtZQUNwRSxrRUFBa0U7WUFDbEUsNkJBQTZCO1lBQzdCLFNBQVMsRUFBRTtnQkFDVCxPQUFPLEVBQUUsSUFBSTtnQkFDYixVQUFVLEVBQUUsSUFBSTtnQkFDaEIsd0JBQXdCLEVBQUUsS0FBSzthQUNoQztZQUNELE9BQU8sRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFO1lBQ2hGLE1BQU0sRUFBRTtnQkFDTixrQkFBa0IsRUFBRSxrQkFBa0I7Z0JBQ3RDLFFBQVEsRUFBRSxNQUFNLENBQUMsSUFBSTthQUN0QjtTQUNGLENBQUM7UUFDRixLQUFLLEVBQUU7UUFDUCxJQUFJLEVBQUU7S0FDUDtDQUNGLENBQUMsQ0FBQSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBjb250ZW50Q29sbGVjdGlvbnMgZnJvbSBcIkBjb250ZW50LWNvbGxlY3Rpb25zL3ZpdGVcIlxuaW1wb3J0IHRhaWx3aW5kIGZyb20gXCJAdGFpbHdpbmRjc3Mvdml0ZVwiXG5pbXBvcnQgeyBkZXZ0b29scyB9IGZyb20gXCJAdGFuc3RhY2svZGV2dG9vbHMtdml0ZVwiXG5pbXBvcnQgeyB0YW5zdGFja1N0YXJ0IH0gZnJvbSBcIkB0YW5zdGFjay9yZWFjdC1zdGFydC9wbHVnaW4vdml0ZVwiXG5pbXBvcnQgcmVhY3QgZnJvbSBcIkB2aXRlanMvcGx1Z2luLXJlYWN0XCJcbmltcG9ydCB7IGRlZmluZUNvbmZpZyB9IGZyb20gXCJ2aXRlXCJcbmltcG9ydCBzdmdyIGZyb20gXCJ2aXRlLXBsdWdpbi1zdmdyXCJcbmltcG9ydCB7IGxvYWRDb25maWcgfSBmcm9tIFwiLi9hY3Rpb25zL2NvbmZpZy9sb2FkLnRzXCJcbmltcG9ydCB7IGFnZW50cyB9IGZyb20gXCIuL3BsdWdpbnMvdml0ZS1hZ2VudHMudHNcIlxuaW1wb3J0IHsgbGl2ZW1hcmsgfSBmcm9tIFwiLi9wbHVnaW5zL3ZpdGUtbGl2ZW1hcmsudHNcIlxuaW1wb3J0IHsgcm9ib3RzIH0gZnJvbSBcIi4vcGx1Z2lucy92aXRlLXJvYm90cy50c1wiXG5cbmNvbnN0IGNvbmZpZyA9IGF3YWl0IGxvYWRDb25maWcoKVxuXG4vLyBQZXItYmxvZy1zZWN0aW9uIFJTUyBmZWVkIHBhdGhzIHRvIHByZXJlbmRlci4gR2F0ZWQgb24gYGNvbmZpZy5zaXRlYFxuLy8gYmVjYXVzZSBmZWVkIFVSTHMgYXJlIGFic29sdXRlIChtYXRjaGluZyB0aGUgc2l0ZW1hcC9yb2JvdHMudHh0IGdhdGUpXG4vLyBhbmQgdGhlIHJvdXRlIHJldHVybnMgNDA0IHdpdGhvdXQgaXQgYW55d2F5LiBFeGNsdWRlZCBmcm9tIHRoZSBzaXRlbWFwXG4vLyBzaW5jZSBgLnhtbGAgcGF5bG9hZHMgZG9uJ3QgYmVsb25nIGluIGEgc2l0ZW1hcC5cbmNvbnN0IGJsb2dGZWVkUGFnZXMgPSBjb25maWcuc2l0ZVxuICA/IChjb25maWcuc2VjdGlvbnMgPz8gW10pXG4gICAgICAuZmlsdGVyKHMgPT4gcy50eXBlID09PSBcImJsb2dcIilcbiAgICAgIC5tYXAocyA9PiAoe1xuICAgICAgICBwYXRoOiBgJHtjb25maWcuYmFzZSA/PyBcIlwifSR7cy5wcmVmaXh9cnNzLnhtbGAsXG4gICAgICAgIHNpdGVtYXA6IHsgZXhjbHVkZTogdHJ1ZSB9LFxuICAgICAgfSkpXG4gIDogW11cblxuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcbiAgcGx1Z2luczogW1xuICAgIGxpdmVtYXJrKHsgY29uZmlnIH0pLFxuICAgIHJvYm90cyh7IGNvbmZpZyB9KSxcbiAgICBhZ2VudHMoeyBjb25maWcgfSksXG4gICAgLy8gSXRzIHNlcnZlci10by1icm93c2VyIGNvbnNvbGUgcGlwaW5nIGFuZCBWaXRlJ3MgYnJvd3Nlci10by1zZXJ2ZXIgZm9yd2FyZGluZyBlY2hvXG4gICAgLy8gYW55IGJyb3dzZXIgZXJyb3IgYmFjayBhbmQgZm9ydGggZm9yZXZlci5cbiAgICBkZXZ0b29scyh7IGNvbnNvbGVQaXBpbmc6IHsgZW5hYmxlZDogZmFsc2UgfSB9KSxcbiAgICB0YWlsd2luZCgpLFxuICAgIGNvbnRlbnRDb2xsZWN0aW9ucyh7IGNvbmZpZ1BhdGg6IFwiY29udGVudC1jb2xsZWN0aW9ucy50c1wiIH0pLFxuICAgIHRhbnN0YWNrU3RhcnQoe1xuICAgICAgc3JjRGlyZWN0b3J5OiBcIi5cIixcbiAgICAgIC8vIFNlZWQgcGF0aCBtdXN0IG1hdGNoIHdoYXQgdGhlIGNyYXdsZXIgcHJvZHVjZXMgKHJvdXRlci1lbWl0dGVkXG4gICAgICAvLyBocmVmcyBpbmNsdWRlIHRoZSBiYXNlcGF0aCksIG90aGVyd2lzZSBwcmVyZW5kZXIgcmVjb3JkcyBib3RoXG4gICAgICAvLyBzaGFwZXMgYW5kIHRoZSBzaXRlbWFwIGVuZHMgdXAgd2l0aCBkdXBsaWNhdGUgZW50cmllcy5cbiAgICAgIHBhZ2VzOiBbeyBwYXRoOiBjb25maWcuYmFzZSA/IGAke2NvbmZpZy5iYXNlfS9gIDogXCIvXCIgfSwgLi4uYmxvZ0ZlZWRQYWdlc10sXG4gICAgICAvLyBEaXNhYmxlIHJvdXRlLXRyZWUgYXV0by1kaXNjb3Zlcnk6IGl0IHByb2R1Y2VzIGJhc2VwYXRoLW5haXZlXG4gICAgICAvLyBwYXRocyAoZS5nLiBgL2ApIHRoYXQgZHVwbGljYXRlIHRoZSBjcmF3bC1kZXJpdmVkIHByZWZpeGVkIHBhdGhzLFxuICAgICAgLy8gYmxvYXRpbmcgcGFnZXMuanNvbiBhbmQgc2l0ZW1hcC4gQ3Jhd2xpbmcgZnJvbSB0aGUgc2VlZCBhbHJlYWR5XG4gICAgICAvLyByZWFjaGVzIGV2ZXJ5IGxpbmtlZCBwYWdlLlxuICAgICAgcHJlcmVuZGVyOiB7XG4gICAgICAgIGVuYWJsZWQ6IHRydWUsXG4gICAgICAgIGNyYXdsTGlua3M6IHRydWUsXG4gICAgICAgIGF1dG9TdGF0aWNQYXRoc0Rpc2NvdmVyeTogZmFsc2UsXG4gICAgICB9LFxuICAgICAgc2l0ZW1hcDogY29uZmlnLnNpdGUgPyB7IGVuYWJsZWQ6IHRydWUsIGhvc3Q6IGNvbmZpZy5zaXRlIH0gOiB7IGVuYWJsZWQ6IGZhbHNlIH0sXG4gICAgICByb3V0ZXI6IHtcbiAgICAgICAgZ2VuZXJhdGVkUm91dGVUcmVlOiBcInJvdXRlVHJlZS5nZW4udHNcIixcbiAgICAgICAgYmFzZXBhdGg6IGNvbmZpZy5iYXNlLFxuICAgICAgfSxcbiAgICB9KSxcbiAgICByZWFjdCgpLFxuICAgIHN2Z3IoKSxcbiAgXSxcbn0pXG4iXX0=
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",
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
+ }
@@ -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
- contentCollections({ configPath: "content-collections.ts" }),
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