@pagesmith/core 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -4
- package/REFERENCE.md +163 -94
- package/dist/ai/index.d.mts +5 -3
- package/dist/ai/index.d.mts.map +1 -1
- package/dist/ai/index.mjs +300 -206
- package/dist/ai/index.mjs.map +1 -1
- package/dist/assets/index.d.mts +10 -1
- package/dist/assets/index.d.mts.map +1 -1
- package/dist/assets/index.mjs +2 -2
- package/dist/{assets-DXiWF_KI.mjs → assets-CAPOqQ_P.mjs} +42 -5
- package/dist/assets-CAPOqQ_P.mjs.map +1 -0
- package/dist/{content-config-Bfe4W9us.d.mts → content-config-Bu2HH0Yx.d.mts} +49 -17
- package/dist/{content-config-Bfe4W9us.d.mts.map → content-config-Bu2HH0Yx.d.mts.map} +1 -1
- package/dist/{content-layer-DPK1EmfY.mjs → content-layer-CJRrNpZ_.mjs} +192 -36
- package/dist/content-layer-CJRrNpZ_.mjs.map +1 -0
- package/dist/content-layer-Ckt08g2i.d.mts +122 -0
- package/dist/content-layer-Ckt08g2i.d.mts.map +1 -0
- package/dist/create/index.d.mts.map +1 -1
- package/dist/create/index.mjs +31 -30
- package/dist/create/index.mjs.map +1 -1
- package/dist/css/index.d.mts +1 -1
- package/dist/css/index.mjs +1 -1
- package/dist/css-CO3CBqxx.mjs +24 -0
- package/dist/css-CO3CBqxx.mjs.map +1 -0
- package/dist/heading-D4X2L4vd.d.mts +12 -0
- package/dist/heading-D4X2L4vd.d.mts.map +1 -0
- package/dist/{index-BBYkDxwI.d.mts → index-B4YZRIzb.d.mts} +1 -1
- package/dist/{index-BBYkDxwI.d.mts.map → index-B4YZRIzb.d.mts.map} +1 -1
- package/dist/{index-Bg9srb5U.d.mts → index-B7NRZAxd.d.mts} +1 -1
- package/dist/{index-Bg9srb5U.d.mts.map → index-B7NRZAxd.d.mts.map} +1 -1
- package/dist/{index-YXQxMV6J.d.mts → index-CryArLlX.d.mts} +2 -2
- package/dist/{index-YXQxMV6J.d.mts.map → index-CryArLlX.d.mts.map} +1 -1
- package/dist/{index-CbOKbkjJ.d.mts → index-D44syBt-.d.mts} +3 -2
- package/dist/index-D44syBt-.d.mts.map +1 -0
- package/dist/index.d.mts +16 -99
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +15 -11
- package/dist/index.mjs.map +1 -1
- package/dist/loaders/index.d.mts +2 -2
- package/dist/loaders/index.mjs +1 -1
- package/dist/{loaders-Cf-BXf2L.mjs → loaders-DnyWfANR.mjs} +3 -3
- package/dist/loaders-DnyWfANR.mjs.map +1 -0
- package/dist/markdown/index.d.mts +2 -2
- package/dist/markdown/index.mjs +1 -1
- package/dist/{markdown-CyrHoDhP.mjs → markdown-DMHd400a.mjs} +29 -2
- package/dist/markdown-DMHd400a.mjs.map +1 -0
- package/dist/{heading-BpDXnl-7.d.mts → markdown-config-CDvh5aJ-.d.mts} +2 -10
- package/dist/markdown-config-CDvh5aJ-.d.mts.map +1 -0
- package/dist/mcp/index.d.mts +23 -0
- package/dist/mcp/index.d.mts.map +1 -0
- package/dist/mcp/index.mjs +2 -0
- package/dist/mcp/server.d.mts +13 -0
- package/dist/mcp/server.d.mts.map +1 -0
- package/dist/mcp/server.mjs +2 -0
- package/dist/runtime/index.d.mts.map +1 -1
- package/dist/runtime/index.mjs +4 -9
- package/dist/runtime/index.mjs.map +1 -1
- package/dist/schemas/index.d.mts +4 -3
- package/dist/server-BZA_iSen.mjs +203 -0
- package/dist/server-BZA_iSen.mjs.map +1 -0
- package/dist/ssg-utils/index.d.mts +51 -0
- package/dist/ssg-utils/index.d.mts.map +1 -0
- package/dist/ssg-utils/index.mjs +119 -0
- package/dist/ssg-utils/index.mjs.map +1 -0
- package/dist/{types-Cn52sdoq.d.mts → types-B-V5qemH.d.mts} +1 -1
- package/dist/{types-Cn52sdoq.d.mts.map → types-B-V5qemH.d.mts.map} +1 -1
- package/dist/vite/index.d.mts +69 -34
- package/dist/vite/index.d.mts.map +1 -1
- package/dist/vite/index.mjs +296 -228
- package/dist/vite/index.mjs.map +1 -1
- package/docs/agents/AGENTS.md.template +29 -0
- package/docs/agents/changelog-notes.md +15 -0
- package/docs/agents/errors.md +150 -0
- package/docs/agents/migration.md +25 -0
- package/docs/agents/recipes.md +50 -0
- package/docs/agents/usage.md +119 -0
- package/docs/llms-full.txt +111 -0
- package/docs/llms.txt +68 -0
- package/package.json +57 -4
- package/dist/assets-DXiWF_KI.mjs.map +0 -1
- package/dist/content-layer-DPK1EmfY.mjs.map +0 -1
- package/dist/css-BneO430t.mjs +0 -20
- package/dist/css-BneO430t.mjs.map +0 -1
- package/dist/heading-BpDXnl-7.d.mts.map +0 -1
- package/dist/index-CbOKbkjJ.d.mts.map +0 -1
- package/dist/loaders-Cf-BXf2L.mjs.map +0 -1
- package/dist/markdown-CyrHoDhP.mjs.map +0 -1
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { t as processMarkdown } from "./markdown-
|
|
2
|
-
import { r as resolveLoader, t as defaultIncludePatterns } from "./loaders-
|
|
1
|
+
import { t as processMarkdown } from "./markdown-DMHd400a.mjs";
|
|
2
|
+
import { r as resolveLoader, t as defaultIncludePatterns } from "./loaders-DnyWfANR.mjs";
|
|
3
|
+
import { existsSync, watch } from "fs";
|
|
4
|
+
import { dirname, extname, relative, resolve } from "path";
|
|
3
5
|
import remarkParse from "remark-parse";
|
|
4
6
|
import { unified } from "unified";
|
|
5
|
-
import {
|
|
7
|
+
import { availableParallelism } from "os";
|
|
6
8
|
import fg from "fast-glob";
|
|
7
|
-
import { existsSync } from "fs";
|
|
8
9
|
//#region src/convert.ts
|
|
9
10
|
async function convert(input, options = {}) {
|
|
10
11
|
const result = await processMarkdown(input, options.markdown || {});
|
|
@@ -92,7 +93,11 @@ function collectRehypePlugins(plugins) {
|
|
|
92
93
|
/** Run all plugin validators against an entry. */
|
|
93
94
|
function runPluginValidators(plugins, entry) {
|
|
94
95
|
const issues = [];
|
|
95
|
-
for (const plugin of plugins) if (plugin.validate)
|
|
96
|
+
for (const plugin of plugins) if (plugin.validate) try {
|
|
97
|
+
issues.push(...plugin.validate(entry));
|
|
98
|
+
} catch (err) {
|
|
99
|
+
issues.push(`[${plugin.name}] Validator threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
100
|
+
}
|
|
96
101
|
return issues;
|
|
97
102
|
}
|
|
98
103
|
//#endregion
|
|
@@ -162,7 +167,8 @@ function validateSchema(data, schema) {
|
|
|
162
167
|
issues: result.error.issues.map((issue) => ({
|
|
163
168
|
field: issue.path.length > 0 ? formatPath(issue.path) : void 0,
|
|
164
169
|
message: issue.message,
|
|
165
|
-
severity: "error"
|
|
170
|
+
severity: "error",
|
|
171
|
+
source: "schema"
|
|
166
172
|
})),
|
|
167
173
|
validatedData: data
|
|
168
174
|
};
|
|
@@ -199,7 +205,7 @@ function collectCodeBlocks(node) {
|
|
|
199
205
|
const codeBlockValidator = {
|
|
200
206
|
name: "code-blocks",
|
|
201
207
|
validate(ctx) {
|
|
202
|
-
if (!ctx.rawContent
|
|
208
|
+
if (!ctx.rawContent) return [];
|
|
203
209
|
const issues = [];
|
|
204
210
|
const tree = ctx.mdast;
|
|
205
211
|
const codeBlocks = collectCodeBlocks(tree);
|
|
@@ -227,9 +233,9 @@ const codeBlockValidator = {
|
|
|
227
233
|
//#endregion
|
|
228
234
|
//#region src/validation/heading-validator.ts
|
|
229
235
|
/** Extract plain text from a heading node's children. */
|
|
230
|
-
function getTextContent(node) {
|
|
236
|
+
function getTextContent$1(node) {
|
|
231
237
|
if (node.type === "text") return node.value ?? "";
|
|
232
|
-
if (node.children) return node.children.map(getTextContent).join("");
|
|
238
|
+
if (node.children) return node.children.map(getTextContent$1).join("");
|
|
233
239
|
return "";
|
|
234
240
|
}
|
|
235
241
|
/** Collect all heading nodes from MDAST. */
|
|
@@ -237,7 +243,7 @@ function collectHeadings(node) {
|
|
|
237
243
|
const headings = [];
|
|
238
244
|
if (node.type === "heading" && node.depth) headings.push({
|
|
239
245
|
depth: node.depth,
|
|
240
|
-
text: getTextContent(node),
|
|
246
|
+
text: getTextContent$1(node),
|
|
241
247
|
line: node.position?.start.line
|
|
242
248
|
});
|
|
243
249
|
if (node.children) for (const child of node.children) headings.push(...collectHeadings(child));
|
|
@@ -246,7 +252,7 @@ function collectHeadings(node) {
|
|
|
246
252
|
const headingValidator = {
|
|
247
253
|
name: "headings",
|
|
248
254
|
validate(ctx) {
|
|
249
|
-
if (!ctx.rawContent
|
|
255
|
+
if (!ctx.rawContent) return [];
|
|
250
256
|
const issues = [];
|
|
251
257
|
const tree = ctx.mdast;
|
|
252
258
|
const headings = collectHeadings(tree);
|
|
@@ -257,6 +263,14 @@ const headingValidator = {
|
|
|
257
263
|
});
|
|
258
264
|
return issues;
|
|
259
265
|
}
|
|
266
|
+
for (const h of headings) if (!h.text.trim()) {
|
|
267
|
+
const lineInfo = h.line ? ` (line ${h.line})` : "";
|
|
268
|
+
issues.push({
|
|
269
|
+
field: `headings${lineInfo}`,
|
|
270
|
+
message: `Empty heading at level h${h.depth}`,
|
|
271
|
+
severity: "warn"
|
|
272
|
+
});
|
|
273
|
+
}
|
|
260
274
|
const h1s = headings.filter((h) => h.depth === 1);
|
|
261
275
|
if (h1s.length > 1) for (const h of h1s.slice(1)) {
|
|
262
276
|
const lineInfo = h.line ? ` (line ${h.line})` : "";
|
|
@@ -289,11 +303,18 @@ const headingValidator = {
|
|
|
289
303
|
* Walks the shared MDAST for link/image nodes. Internal links are checked for file existence;
|
|
290
304
|
* external links are checked for well-formed URL format.
|
|
291
305
|
*/
|
|
306
|
+
/** Extract plain text from a node's children. */
|
|
307
|
+
function getTextContent(node) {
|
|
308
|
+
if (node.type === "text") return node.value ?? "";
|
|
309
|
+
if (node.children) return node.children.map(getTextContent).join("");
|
|
310
|
+
return "";
|
|
311
|
+
}
|
|
292
312
|
/** Walk MDAST tree, collecting link and image nodes. */
|
|
293
313
|
function collectLinks(node) {
|
|
294
314
|
const links = [];
|
|
295
315
|
if ((node.type === "link" || node.type === "image") && node.url) links.push({
|
|
296
316
|
url: node.url,
|
|
317
|
+
text: node.type === "link" ? getTextContent(node) : "",
|
|
297
318
|
line: node.position?.start.line
|
|
298
319
|
});
|
|
299
320
|
if (node.children) for (const child of node.children) links.push(...collectLinks(child));
|
|
@@ -305,6 +326,7 @@ function isInternalLink(url) {
|
|
|
305
326
|
if (url.startsWith("//")) return false;
|
|
306
327
|
if (url.startsWith("mailto:")) return false;
|
|
307
328
|
if (url.startsWith("tel:")) return false;
|
|
329
|
+
if (url.startsWith("data:")) return false;
|
|
308
330
|
return true;
|
|
309
331
|
}
|
|
310
332
|
function isWellFormedUrl(url) {
|
|
@@ -326,13 +348,18 @@ function createLinkValidator(options) {
|
|
|
326
348
|
return {
|
|
327
349
|
name: "links",
|
|
328
350
|
validate(ctx) {
|
|
329
|
-
if (!ctx.rawContent
|
|
351
|
+
if (!ctx.rawContent) return [];
|
|
330
352
|
const issues = [];
|
|
331
353
|
const tree = ctx.mdast;
|
|
332
354
|
const links = collectLinks(tree);
|
|
333
355
|
const fileDir = dirname(ctx.filePath);
|
|
334
356
|
for (const link of links) {
|
|
335
357
|
const lineInfo = link.line ? ` (line ${link.line})` : "";
|
|
358
|
+
if (link.text !== void 0 && !link.text.trim()) issues.push({
|
|
359
|
+
field: `links${lineInfo}`,
|
|
360
|
+
message: `Link has no visible text: ${link.url}`,
|
|
361
|
+
severity: "warn"
|
|
362
|
+
});
|
|
336
363
|
if (link.url.startsWith("http://") || link.url.startsWith("https://")) {
|
|
337
364
|
if (!isWellFormedUrl(link.url)) issues.push({
|
|
338
365
|
field: `links${lineInfo}`,
|
|
@@ -359,30 +386,36 @@ function createLinkValidator(options) {
|
|
|
359
386
|
const linkValidator = createLinkValidator();
|
|
360
387
|
//#endregion
|
|
361
388
|
//#region src/validation/runner.ts
|
|
362
|
-
/**
|
|
363
|
-
* Validation runner — executes a list of content validators on an entry.
|
|
364
|
-
*
|
|
365
|
-
* Validators that throw are caught and converted to error-severity issues
|
|
366
|
-
* so one failing validator does not block the rest.
|
|
367
|
-
*/
|
|
368
389
|
/** The built-in validators for markdown content. */
|
|
369
390
|
const builtinMarkdownValidators = [
|
|
370
391
|
linkValidator,
|
|
371
392
|
codeBlockValidator,
|
|
372
393
|
headingValidator
|
|
373
394
|
];
|
|
395
|
+
/** Parse raw markdown to MDAST once, reusing any pre-parsed tree. */
|
|
396
|
+
function resolveContext(ctx) {
|
|
397
|
+
const mdast = ctx.mdast ?? unified().use(remarkParse).parse(ctx.rawContent ?? "");
|
|
398
|
+
return {
|
|
399
|
+
...ctx,
|
|
400
|
+
mdast
|
|
401
|
+
};
|
|
402
|
+
}
|
|
374
403
|
/** Run all validators on a single content entry. */
|
|
375
404
|
async function runValidators(ctx, validators) {
|
|
376
|
-
|
|
405
|
+
const resolved = resolveContext(ctx);
|
|
377
406
|
const issues = [];
|
|
378
407
|
for (const validator of validators) try {
|
|
379
|
-
const result = await validator.validate(
|
|
380
|
-
issues.push(...result)
|
|
408
|
+
const result = await validator.validate(resolved);
|
|
409
|
+
issues.push(...result.map((issue) => ({
|
|
410
|
+
...issue,
|
|
411
|
+
source: "content"
|
|
412
|
+
})));
|
|
381
413
|
} catch (err) {
|
|
382
414
|
const message = err instanceof Error ? err.message : String(err);
|
|
383
415
|
issues.push({
|
|
384
416
|
message: `Validator "${validator.name}" threw: ${message}`,
|
|
385
|
-
severity: "error"
|
|
417
|
+
severity: "error",
|
|
418
|
+
source: "content"
|
|
386
419
|
});
|
|
387
420
|
}
|
|
388
421
|
return issues;
|
|
@@ -395,9 +428,24 @@ async function runValidators(ctx, validators) {
|
|
|
395
428
|
* Handles file discovery, loading, validation, and caching.
|
|
396
429
|
* Not exported directly — used internally by ContentLayer.
|
|
397
430
|
*/
|
|
431
|
+
const MAX_CONCURRENCY = availableParallelism() * 2;
|
|
432
|
+
async function mapWithConcurrency(items, concurrency, fn) {
|
|
433
|
+
const results = Array.from({ length: items.length });
|
|
434
|
+
let index = 0;
|
|
435
|
+
async function worker() {
|
|
436
|
+
while (index < items.length) {
|
|
437
|
+
const i = index++;
|
|
438
|
+
results[i] = await fn(items[i]);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
const workers = Array.from({ length: Math.min(concurrency, items.length) }, () => worker());
|
|
442
|
+
await Promise.all(workers);
|
|
443
|
+
return results;
|
|
444
|
+
}
|
|
398
445
|
var ContentStore = class {
|
|
399
446
|
cache = /* @__PURE__ */ new Map();
|
|
400
447
|
loaded = /* @__PURE__ */ new Set();
|
|
448
|
+
loading = /* @__PURE__ */ new Map();
|
|
401
449
|
config;
|
|
402
450
|
rootDir;
|
|
403
451
|
markdownConfig;
|
|
@@ -412,6 +460,17 @@ var ContentStore = class {
|
|
|
412
460
|
const cached = this.cache.get(name);
|
|
413
461
|
return cached ? Array.from(cached.values()).map((c) => c.entry) : [];
|
|
414
462
|
}
|
|
463
|
+
const existing = this.loading.get(name);
|
|
464
|
+
if (existing) return existing;
|
|
465
|
+
const promise = this._doLoadCollection(name, def);
|
|
466
|
+
this.loading.set(name, promise);
|
|
467
|
+
try {
|
|
468
|
+
return await promise;
|
|
469
|
+
} finally {
|
|
470
|
+
this.loading.delete(name);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
async _doLoadCollection(name, def) {
|
|
415
474
|
const loader = resolveLoader(def.loader);
|
|
416
475
|
const directory = resolve(this.rootDir, def.directory);
|
|
417
476
|
const files = await discoverFiles({
|
|
@@ -420,23 +479,25 @@ var ContentStore = class {
|
|
|
420
479
|
exclude: def.exclude
|
|
421
480
|
});
|
|
422
481
|
const entries = /* @__PURE__ */ new Map();
|
|
423
|
-
const
|
|
482
|
+
const strict = this.config.strict ?? false;
|
|
483
|
+
const results = await mapWithConcurrency(files, MAX_CONCURRENCY, async (filePath) => {
|
|
424
484
|
try {
|
|
425
485
|
return await this.loadEntry(name, filePath, directory, loader, def);
|
|
426
486
|
} catch (err) {
|
|
427
487
|
const message = err instanceof Error ? err.message : String(err);
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
console.warn(loadError.message);
|
|
488
|
+
const loadError = new Error(`[${name}] Failed to load ${filePath}: ${message}`, { cause: err });
|
|
489
|
+
if (strict) throw loadError;
|
|
490
|
+
console.warn(`[pagesmith] ${loadError.message}`);
|
|
431
491
|
return {
|
|
432
|
-
entry: new ContentEntry(
|
|
492
|
+
entry: new ContentEntry(def.slugify ? def.slugify(filePath, directory) : toSlug(filePath, directory), name, filePath, {}, void 0, this.markdownConfig),
|
|
433
493
|
issues: [{
|
|
434
494
|
message: loadError.message,
|
|
435
|
-
severity: "error"
|
|
495
|
+
severity: "error",
|
|
496
|
+
source: "schema"
|
|
436
497
|
}]
|
|
437
498
|
};
|
|
438
499
|
}
|
|
439
|
-
})
|
|
500
|
+
});
|
|
440
501
|
for (const result of results) if (result) entries.set(result.entry.slug, result);
|
|
441
502
|
this.cache.set(name, entries);
|
|
442
503
|
this.loaded.add(name);
|
|
@@ -453,14 +514,21 @@ var ContentStore = class {
|
|
|
453
514
|
slug
|
|
454
515
|
};
|
|
455
516
|
if (def.transform) raw = await def.transform(raw);
|
|
456
|
-
if (def.computed)
|
|
517
|
+
if (def.computed) {
|
|
518
|
+
raw = {
|
|
519
|
+
...raw,
|
|
520
|
+
data: { ...raw.data }
|
|
521
|
+
};
|
|
522
|
+
for (const [key, fn] of Object.entries(def.computed)) raw.data[key] = await fn(raw);
|
|
523
|
+
}
|
|
457
524
|
if (def.filter && !def.filter(raw)) return null;
|
|
458
525
|
const { issues, validatedData } = validateSchema(raw.data, def.schema);
|
|
459
526
|
if (def.validate) {
|
|
460
527
|
const customError = def.validate(raw);
|
|
461
528
|
if (customError) issues.push({
|
|
462
529
|
message: customError,
|
|
463
|
-
severity: "error"
|
|
530
|
+
severity: "error",
|
|
531
|
+
source: "custom"
|
|
464
532
|
});
|
|
465
533
|
}
|
|
466
534
|
if (raw.content !== void 0) {
|
|
@@ -471,7 +539,15 @@ var ContentStore = class {
|
|
|
471
539
|
slug,
|
|
472
540
|
collection: collectionName,
|
|
473
541
|
rawContent: raw.content,
|
|
474
|
-
data: raw.data
|
|
542
|
+
data: raw.data,
|
|
543
|
+
getEntry: (col, s) => {
|
|
544
|
+
const cached = this.cache.get(col)?.get(s);
|
|
545
|
+
if (!cached) return void 0;
|
|
546
|
+
return {
|
|
547
|
+
slug: cached.entry.slug,
|
|
548
|
+
data: cached.entry.data
|
|
549
|
+
};
|
|
550
|
+
}
|
|
475
551
|
}, validators);
|
|
476
552
|
issues.push(...contentIssues);
|
|
477
553
|
}
|
|
@@ -483,7 +559,8 @@ var ContentStore = class {
|
|
|
483
559
|
});
|
|
484
560
|
for (const message of pluginIssues) issues.push({
|
|
485
561
|
message,
|
|
486
|
-
severity: "error"
|
|
562
|
+
severity: "error",
|
|
563
|
+
source: "plugin"
|
|
487
564
|
});
|
|
488
565
|
}
|
|
489
566
|
return {
|
|
@@ -495,6 +572,9 @@ var ContentStore = class {
|
|
|
495
572
|
getEntry(collection, slug) {
|
|
496
573
|
return this.cache.get(collection)?.get(slug)?.entry;
|
|
497
574
|
}
|
|
575
|
+
isCollectionLoaded(collection) {
|
|
576
|
+
return this.loaded.has(collection);
|
|
577
|
+
}
|
|
498
578
|
/** Get validation issues for a collection. */
|
|
499
579
|
getIssues(collection) {
|
|
500
580
|
const result = /* @__PURE__ */ new Map();
|
|
@@ -531,6 +611,42 @@ var ContentStore = class {
|
|
|
531
611
|
this.cache.clear();
|
|
532
612
|
this.loaded.clear();
|
|
533
613
|
}
|
|
614
|
+
/** Get cache statistics for debugging and monitoring. */
|
|
615
|
+
getCacheStats() {
|
|
616
|
+
const entries = {};
|
|
617
|
+
let totalEntries = 0;
|
|
618
|
+
for (const [name, cache] of this.cache) {
|
|
619
|
+
entries[name] = cache.size;
|
|
620
|
+
totalEntries += cache.size;
|
|
621
|
+
}
|
|
622
|
+
return {
|
|
623
|
+
collections: this.loaded.size,
|
|
624
|
+
entries,
|
|
625
|
+
totalEntries
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
/** Invalidate entries matching a predicate without reloading the entire collection. */
|
|
629
|
+
async invalidateWhere(collection, predicate) {
|
|
630
|
+
const collectionCache = this.cache.get(collection);
|
|
631
|
+
if (!collectionCache) return 0;
|
|
632
|
+
const def = this.config.collections[collection];
|
|
633
|
+
if (!def) return 0;
|
|
634
|
+
const loader = resolveLoader(def.loader);
|
|
635
|
+
const directory = resolve(this.rootDir, def.directory);
|
|
636
|
+
let count = 0;
|
|
637
|
+
for (const [slug, cached] of collectionCache) {
|
|
638
|
+
if (!predicate(cached.entry)) continue;
|
|
639
|
+
count++;
|
|
640
|
+
try {
|
|
641
|
+
const result = await this.loadEntry(collection, cached.entry.filePath, directory, loader, def);
|
|
642
|
+
if (result) collectionCache.set(slug, result);
|
|
643
|
+
else collectionCache.delete(slug);
|
|
644
|
+
} catch {
|
|
645
|
+
collectionCache.delete(slug);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
return count;
|
|
649
|
+
}
|
|
534
650
|
/** Resolve the list of content validators for a collection. */
|
|
535
651
|
resolveValidators(def) {
|
|
536
652
|
const builtin = def.disableBuiltinValidators ? [] : builtinMarkdownValidators;
|
|
@@ -577,6 +693,9 @@ var ContentLayerImpl = class {
|
|
|
577
693
|
return this.store.loadCollection(name, def);
|
|
578
694
|
}
|
|
579
695
|
async getEntry(collection, slug) {
|
|
696
|
+
const cached = this.store.getEntry(collection, slug);
|
|
697
|
+
if (cached) return cached;
|
|
698
|
+
if (this.store.isCollectionLoaded(collection)) return void 0;
|
|
580
699
|
await this.getCollection(collection);
|
|
581
700
|
return this.store.getEntry(collection, slug);
|
|
582
701
|
}
|
|
@@ -592,6 +711,9 @@ var ContentLayerImpl = class {
|
|
|
592
711
|
invalidateAll() {
|
|
593
712
|
this.store.invalidateAll();
|
|
594
713
|
}
|
|
714
|
+
async invalidateWhere(collection, predicate) {
|
|
715
|
+
return this.store.invalidateWhere(collection, predicate);
|
|
716
|
+
}
|
|
595
717
|
async validate(collection) {
|
|
596
718
|
const names = collection ? [collection] : Object.keys(this.config.collections);
|
|
597
719
|
const results = [];
|
|
@@ -627,12 +749,46 @@ var ContentLayerImpl = class {
|
|
|
627
749
|
getCollections() {
|
|
628
750
|
return { ...this.config.collections };
|
|
629
751
|
}
|
|
752
|
+
watch(callback) {
|
|
753
|
+
const watchers = [];
|
|
754
|
+
const root = this.config.root ?? process.cwd();
|
|
755
|
+
const timers = /* @__PURE__ */ new Map();
|
|
756
|
+
for (const [name, def] of Object.entries(this.config.collections)) {
|
|
757
|
+
const dir = resolve(root, def.directory);
|
|
758
|
+
try {
|
|
759
|
+
const watcher = watch(dir, { recursive: true }, (event, filename) => {
|
|
760
|
+
const existing = timers.get(name);
|
|
761
|
+
if (existing) clearTimeout(existing);
|
|
762
|
+
timers.set(name, setTimeout(() => {
|
|
763
|
+
timers.delete(name);
|
|
764
|
+
this.store.invalidateCollection(name);
|
|
765
|
+
callback({
|
|
766
|
+
collection: name,
|
|
767
|
+
event,
|
|
768
|
+
filename
|
|
769
|
+
});
|
|
770
|
+
}, 100));
|
|
771
|
+
});
|
|
772
|
+
watcher.on("error", (err) => {
|
|
773
|
+
console.warn(`Content watcher error for "${name}":`, err.message);
|
|
774
|
+
});
|
|
775
|
+
watchers.push(watcher);
|
|
776
|
+
} catch {}
|
|
777
|
+
}
|
|
778
|
+
return { close() {
|
|
779
|
+
for (const w of watchers) w.close();
|
|
780
|
+
for (const t of timers.values()) clearTimeout(t);
|
|
781
|
+
timers.clear();
|
|
782
|
+
} };
|
|
783
|
+
}
|
|
784
|
+
getCacheStats() {
|
|
785
|
+
return this.store.getCacheStats();
|
|
786
|
+
}
|
|
630
787
|
};
|
|
631
|
-
/** Create a new content layer from a configuration. */
|
|
632
788
|
function createContentLayer(config) {
|
|
633
789
|
return new ContentLayerImpl(config);
|
|
634
790
|
}
|
|
635
791
|
//#endregion
|
|
636
|
-
export { linkValidator as a,
|
|
792
|
+
export { linkValidator as a, validateSchema as c, convert as d, createLinkValidator as i, toSlug as l, builtinMarkdownValidators as n, headingValidator as o, runValidators as r, codeBlockValidator as s, createContentLayer as t, ContentEntry as u };
|
|
637
793
|
|
|
638
|
-
//# sourceMappingURL=content-layer-
|
|
794
|
+
//# sourceMappingURL=content-layer-CJRrNpZ_.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-layer-CJRrNpZ_.mjs","names":["getTextContent","coreConvert","pathResolve","fsWatch"],"sources":["../src/convert.ts","../src/utils/read-time.ts","../src/entry.ts","../src/plugins/index.ts","../src/utils/glob.ts","../src/utils/slug.ts","../src/validation/schema-validator.ts","../src/validation/code-block-validator.ts","../src/validation/heading-validator.ts","../src/validation/link-validator.ts","../src/validation/runner.ts","../src/store.ts","../src/content-layer.ts"],"sourcesContent":["import { processMarkdown } from './markdown'\nimport type { Heading } from './schemas/heading'\nimport type { MarkdownConfig } from './schemas/markdown-config'\n\nexport type ConvertOptions = {\n markdown?: MarkdownConfig\n}\n\nexport type ConvertResult = {\n html: string\n toc: Heading[]\n frontmatter: Record<string, unknown>\n}\n\nexport async function convert(input: string, options: ConvertOptions = {}): Promise<ConvertResult> {\n const result = await processMarkdown(input, options.markdown || {})\n return { html: result.html, toc: result.headings, frontmatter: result.frontmatter }\n}\n","/**\n * Read time estimation.\n *\n * Computes estimated reading time from markdown source (~200 words per minute).\n */\n\n/** Compute read time in minutes from raw markdown. */\nexport function computeReadTime(rawMarkdown: string): number {\n const plainText = rawMarkdown\n .replace(/```[\\s\\S]*?```/g, ' ')\n .replace(/^( {4}|\\t).+$/gm, ' ')\n .replace(/^---[\\s\\S]*?---/m, ' ')\n .replace(/<[^>]+>/g, ' ')\n .replace(/!?\\[([^\\]]*)\\]\\([^)]*\\)/g, '$1')\n .replace(/[#*_~`>]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n const wordCount = plainText.split(' ').filter(Boolean).length\n return Math.max(1, Math.ceil(wordCount / 200))\n}\n","/**\n * ContentEntry — represents a single content entry in a collection.\n *\n * Supports lazy rendering: data is available immediately after loading,\n * but HTML rendering is deferred until render() is called.\n */\n\nimport type { Heading } from './schemas/heading'\nimport type { MarkdownConfig } from './schemas/markdown-config'\nimport { processMarkdown } from './markdown'\nimport { computeReadTime } from './utils/read-time'\n\nexport type RenderedContent = {\n /** Processed HTML */\n html: string\n /** Extracted headings for TOC */\n headings: Heading[]\n /** Estimated read time in minutes */\n readTime: number\n}\n\nexport class ContentEntry<T = Record<string, any>> {\n /** URL-friendly identifier */\n readonly slug: string\n /** Collection this entry belongs to */\n readonly collection: string\n /** Absolute path to source file */\n readonly filePath: string\n /** Validated data (frontmatter or parsed data) */\n readonly data: T\n /** Raw body content (markdown only) */\n readonly rawContent?: string\n\n /** Cached render result */\n private _rendered?: RenderedContent\n /** Markdown config for rendering */\n private _markdownConfig: MarkdownConfig\n\n constructor(\n slug: string,\n collection: string,\n filePath: string,\n data: T,\n rawContent: string | undefined,\n markdownConfig: MarkdownConfig,\n ) {\n this.slug = slug\n this.collection = collection\n this.filePath = filePath\n this.data = data\n this.rawContent = rawContent\n this._markdownConfig = markdownConfig\n }\n\n /** Render the entry content to HTML. Cached after first call. */\n async render(options?: { force?: boolean }): Promise<RenderedContent> {\n if (this._rendered && !options?.force) {\n return this._rendered\n }\n\n if (!this.rawContent) {\n // Non-markdown entries have no renderable content\n this._rendered = { html: '', headings: [], readTime: 0 }\n return this._rendered\n }\n\n const result = await processMarkdown(this.rawContent, this._markdownConfig, {\n content: this.rawContent,\n frontmatter: this.data as Record<string, unknown>,\n })\n const readTime = computeReadTime(this.rawContent)\n\n this._rendered = {\n html: result.html,\n headings: result.headings,\n readTime,\n }\n\n return this._rendered\n }\n\n /** Clear cached render result. */\n clearRenderCache(): void {\n this._rendered = undefined\n }\n}\n","/**\n * Plugin registration and execution.\n */\n\nimport type { ContentPlugin } from '../schemas/content-config'\n\n/** Collect all remark plugins from content plugins. */\nexport function collectRemarkPlugins(plugins: ContentPlugin[]): any[] {\n return plugins.filter((p) => p.remarkPlugin).map((p) => p.remarkPlugin!)\n}\n\n/** Collect all rehype plugins from content plugins. */\nexport function collectRehypePlugins(plugins: ContentPlugin[]): any[] {\n return plugins.filter((p) => p.rehypePlugin).map((p) => p.rehypePlugin!)\n}\n\n/** Run all plugin validators against an entry. */\nexport function runPluginValidators(\n plugins: ContentPlugin[],\n entry: { data: Record<string, any>; content?: string },\n): string[] {\n const issues: string[] = []\n for (const plugin of plugins) {\n if (plugin.validate) {\n try {\n issues.push(...plugin.validate(entry))\n } catch (err) {\n issues.push(\n `[${plugin.name}] Validator threw: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n }\n }\n return issues\n}\n\nexport type { ContentPlugin } from '../schemas/content-config'\n","/**\n * File discovery via glob patterns.\n */\n\nimport fg from 'fast-glob'\nimport { resolve } from 'path'\n\nexport interface DiscoverOptions {\n /** Directory to search in (absolute path) */\n directory: string\n /** Glob patterns to include */\n include: string[]\n /** Glob patterns to exclude */\n exclude?: string[]\n}\n\n/** Discover files matching glob patterns in a directory. */\nexport async function discoverFiles(options: DiscoverOptions): Promise<string[]> {\n const { directory, include, exclude = [] } = options\n\n const files = await fg(include, {\n cwd: directory,\n absolute: true,\n ignore: ['**/node_modules/**', '**/dist/**', '**/dev/**', ...exclude],\n })\n\n return files.map((p) => resolve(p))\n}\n","/**\n * Path-to-slug conversion.\n *\n * Generalized from packages/core/src/content/collector.ts.\n */\n\nimport { extname, relative } from 'path'\n\n/**\n * Convert a content file path to a URL-friendly slug.\n *\n * Examples:\n * content/posts/hello-world/README.md -> 'hello-world'\n * content/posts/hello-world/index.md -> 'hello-world'\n * content/posts/hello-world.md -> 'hello-world'\n * content/authors/john.json -> 'john'\n * content/posts/nested/deep/post.md -> 'nested/deep/post'\n */\nexport function toSlug(filePath: string, directory: string): string {\n const ext = extname(filePath)\n let slug = relative(directory, filePath).replace(/\\\\/g, '/')\n\n // Remove file extension\n if (ext) {\n slug = slug.slice(0, -ext.length)\n }\n\n // Strip README / index suffixes\n if (slug === 'README' || slug === 'index') return '/'\n if (slug.endsWith('/README')) slug = slug.slice(0, -7)\n if (slug.endsWith('/index')) slug = slug.slice(0, -6)\n\n return slug\n}\n","/**\n * Schema validation with rich error reporting.\n *\n * Wraps Zod's safeParse to produce human-readable validation issues.\n */\n\nimport type { ZodError, ZodType } from 'zod'\n\nexport type ValidationIssue = {\n /** Field path (e.g. 'tags[0]') */\n field?: string\n /** Human-readable error message */\n message: string\n /** Error severity */\n severity: 'error' | 'warn'\n /** Origin of this issue — helps consumers distinguish validation phases. */\n source?: 'schema' | 'content' | 'plugin' | 'custom'\n}\n\nexport type ValidationEntryResult = {\n slug: string\n filePath: string\n issues: ValidationIssue[]\n}\n\nexport type ValidationResult = {\n collection: string\n entries: ValidationEntryResult[]\n errors: number\n warnings: number\n}\n\n/** Format a Zod error path into a human-readable field path. */\nexport function formatPath(path: PropertyKey[]): string {\n return path\n .map((segment, i) => {\n if (typeof segment === 'number') return `[${segment}]`\n if (typeof segment === 'symbol') return `[${String(segment)}]`\n if (i === 0) return segment\n return `.${segment}`\n })\n .join('')\n}\n\n/** Validate data against a Zod schema and return structured issues. */\nexport function validateSchema(\n data: Record<string, any>,\n schema: ZodType,\n): {\n issues: ValidationIssue[]\n validatedData: any\n} {\n const result = schema.safeParse(data)\n if (result.success) {\n return {\n issues: [],\n validatedData: result.data,\n }\n }\n\n const issues = (result.error as ZodError).issues.map((issue) => ({\n field: issue.path.length > 0 ? formatPath(issue.path) : undefined,\n message: issue.message,\n severity: 'error' as const,\n source: 'schema' as const,\n }))\n\n // On failure, return raw data so the pipeline can continue in non-strict mode.\n // The issues array signals that validation did not pass.\n return {\n issues,\n validatedData: data,\n }\n}\n","/**\n * Code block validator — checks fenced code block meta syntax.\n *\n * Walks the shared MDAST for `code` nodes. Validates known meta properties\n * and language identifiers. Meta syntax follows Expressive Code conventions.\n */\n\nimport type { ValidationIssue } from './schema-validator'\nimport type { ContentValidator, MdastNode, ResolvedValidatorContext } from './types'\n\n/** Known meta properties accepted by Expressive Code and its plugins. */\nconst KNOWN_META_PROPS = new Set([\n 'title',\n 'showLineNumbers',\n 'startLineNumber',\n 'wrap',\n 'frame',\n 'collapse',\n 'mark',\n 'ins',\n 'del',\n])\n\n/** Extract the property name portion of a meta token (before `=` or `{`). */\nfunction extractMetaPropNames(meta: string): string[] {\n const props: string[] = []\n\n // Match key=value, key={...}, or bare flags\n const tokenRegex = /(\\w+)(?:=(?:\\{[^}]*\\}|\"[^\"]*\"|'[^']*'|\\S+))?/g\n let match: RegExpExecArray | null\n while ((match = tokenRegex.exec(meta)) !== null) {\n props.push(match[1]!)\n }\n\n return props\n}\n\n/** Collect all `code` nodes from MDAST. */\nfunction collectCodeBlocks(node: MdastNode): MdastNode[] {\n const blocks: MdastNode[] = []\n\n if (node.type === 'code') {\n blocks.push(node)\n }\n\n if (node.children) {\n for (const child of node.children) {\n blocks.push(...collectCodeBlocks(child))\n }\n }\n\n return blocks\n}\n\nexport const codeBlockValidator: ContentValidator = {\n name: 'code-blocks',\n\n validate(ctx: ResolvedValidatorContext): ValidationIssue[] {\n if (!ctx.rawContent) return []\n\n const issues: ValidationIssue[] = []\n const tree = ctx.mdast as MdastNode\n\n const codeBlocks = collectCodeBlocks(tree)\n\n for (const block of codeBlocks) {\n const line = block.position?.start.line\n const lineInfo = line ? ` (line ${line})` : ''\n const meta = block.meta ?? ''\n const hasMeta = meta.trim().length > 0\n\n // Language required when using syntax features\n if (hasMeta && !block.lang) {\n issues.push({\n field: `code-block${lineInfo}`,\n message: 'Code block has meta properties but no language identifier',\n severity: 'warn',\n })\n }\n\n if (!hasMeta) continue\n\n // Check for unknown meta properties\n const propNames = extractMetaPropNames(meta)\n for (const prop of propNames) {\n if (!KNOWN_META_PROPS.has(prop)) {\n issues.push({\n field: `code-block${lineInfo}`,\n message: `Unknown code block meta property: \"${prop}\"`,\n severity: 'warn',\n })\n }\n }\n }\n\n return issues\n },\n}\n","/**\n * Heading validator — checks heading structure in markdown.\n *\n * Walks the shared MDAST for heading nodes. Validates level ordering and h1 uniqueness.\n */\n\nimport type { ValidationIssue } from './schema-validator'\nimport type { ContentValidator, MdastNode, ResolvedValidatorContext } from './types'\n\n/** Extract plain text from a heading node's children. */\nfunction getTextContent(node: MdastNode): string {\n if (node.type === 'text') return node.value ?? ''\n if (node.children) return node.children.map(getTextContent).join('')\n return ''\n}\n\n/** Collect all heading nodes from MDAST. */\nfunction collectHeadings(node: MdastNode): Array<{ depth: number; text: string; line?: number }> {\n const headings: Array<{ depth: number; text: string; line?: number }> = []\n\n if (node.type === 'heading' && node.depth) {\n headings.push({\n depth: node.depth,\n text: getTextContent(node),\n line: node.position?.start.line,\n })\n }\n\n if (node.children) {\n for (const child of node.children) {\n headings.push(...collectHeadings(child))\n }\n }\n\n return headings\n}\n\nexport const headingValidator: ContentValidator = {\n name: 'headings',\n\n validate(ctx: ResolvedValidatorContext): ValidationIssue[] {\n if (!ctx.rawContent) return []\n\n const issues: ValidationIssue[] = []\n const tree = ctx.mdast as MdastNode\n\n const headings = collectHeadings(tree)\n\n // No headings in a document with content is worth noting\n if (headings.length === 0) {\n const hasContent = ctx.rawContent.trim().length > 0\n if (hasContent) {\n issues.push({\n message: 'Document has content but no headings',\n severity: 'warn',\n })\n }\n return issues\n }\n\n // Empty heading text\n for (const h of headings) {\n if (!h.text.trim()) {\n const lineInfo = h.line ? ` (line ${h.line})` : ''\n issues.push({\n field: `headings${lineInfo}`,\n message: `Empty heading at level h${h.depth}`,\n severity: 'warn',\n })\n }\n }\n\n // At most one h1\n const h1s = headings.filter((h) => h.depth === 1)\n if (h1s.length > 1) {\n for (const h of h1s.slice(1)) {\n const lineInfo = h.line ? ` (line ${h.line})` : ''\n issues.push({\n field: `headings${lineInfo}`,\n message: `Multiple h1 headings found: \"${h.text}\"`,\n severity: 'warn',\n })\n }\n }\n\n // No skipped levels (only flag when going deeper)\n for (let i = 1; i < headings.length; i++) {\n const prev = headings[i - 1]!\n const curr = headings[i]!\n if (curr.depth > prev.depth + 1) {\n const lineInfo = curr.line ? ` (line ${curr.line})` : ''\n issues.push({\n field: `headings${lineInfo}`,\n message: `Heading level skip: h${prev.depth} -> h${curr.depth} (\"${curr.text}\")`,\n severity: 'warn',\n })\n }\n }\n\n return issues\n },\n}\n","/**\n * Link validator — checks links in markdown content.\n *\n * Walks the shared MDAST for link/image nodes. Internal links are checked for file existence;\n * external links are checked for well-formed URL format.\n */\n\nimport { existsSync } from 'fs'\nimport { dirname, resolve } from 'path'\nimport type { ValidationIssue } from './schema-validator'\nimport type { ContentValidator, MdastNode, ResolvedValidatorContext } from './types'\n\n/** Extract plain text from a node's children. */\nfunction getTextContent(node: MdastNode): string {\n if (node.type === 'text') return node.value ?? ''\n if (node.children) return node.children.map(getTextContent).join('')\n return ''\n}\n\n/** Walk MDAST tree, collecting link and image nodes. */\nfunction collectLinks(node: MdastNode): Array<{ url: string; text: string; line?: number }> {\n const links: Array<{ url: string; text: string; line?: number }> = []\n\n if ((node.type === 'link' || node.type === 'image') && node.url) {\n links.push({\n url: node.url,\n text: node.type === 'link' ? getTextContent(node) : '',\n line: node.position?.start.line,\n })\n }\n\n if (node.children) {\n for (const child of node.children) {\n links.push(...collectLinks(child))\n }\n }\n\n return links\n}\n\nfunction isInternalLink(url: string): boolean {\n if (url.startsWith('#')) return false\n if (url.startsWith('http://') || url.startsWith('https://')) return false\n if (url.startsWith('//')) return false\n if (url.startsWith('mailto:')) return false\n if (url.startsWith('tel:')) return false\n if (url.startsWith('data:')) return false\n return true\n}\n\nfunction isWellFormedUrl(url: string): boolean {\n try {\n new URL(url)\n return true\n } catch {\n return false\n }\n}\n\nexport type LinkValidatorOptions = {\n /** Glob patterns for internal links to skip file-existence checks on. */\n skipPatterns?: string[]\n}\n\nexport function createLinkValidator(options?: LinkValidatorOptions): ContentValidator {\n const skipPatterns = options?.skipPatterns ?? []\n\n function shouldSkip(url: string): boolean {\n return skipPatterns.some((pattern) => {\n if (pattern.includes('*')) {\n const regex = new RegExp('^' + pattern.replace(/\\*/g, '.*') + '$')\n return regex.test(url)\n }\n return url.startsWith(pattern)\n })\n }\n\n return {\n name: 'links',\n\n validate(ctx: ResolvedValidatorContext): ValidationIssue[] {\n if (!ctx.rawContent) return []\n\n const issues: ValidationIssue[] = []\n const tree = ctx.mdast as MdastNode\n\n const links = collectLinks(tree)\n const fileDir = dirname(ctx.filePath)\n\n for (const link of links) {\n const lineInfo = link.line ? ` (line ${link.line})` : ''\n\n // Empty link text — bad accessibility\n if (link.text !== undefined && !link.text.trim()) {\n issues.push({\n field: `links${lineInfo}`,\n message: `Link has no visible text: ${link.url}`,\n severity: 'warn',\n })\n }\n\n // External links — check URL format\n if (link.url.startsWith('http://') || link.url.startsWith('https://')) {\n if (!isWellFormedUrl(link.url)) {\n issues.push({\n field: `links${lineInfo}`,\n message: `Malformed external URL: ${link.url}`,\n severity: 'warn',\n })\n }\n continue\n }\n\n // Internal links — check file exists\n if (isInternalLink(link.url)) {\n if (shouldSkip(link.url)) continue\n\n // Strip fragment and query\n const urlPath = link.url.split('#')[0]!.split('?')[0]!\n if (!urlPath) continue // pure fragment link\n\n const resolved = resolve(fileDir, urlPath)\n if (!existsSync(resolved)) {\n issues.push({\n field: `links${lineInfo}`,\n message: `Broken internal link: ${link.url}`,\n severity: 'error',\n })\n }\n }\n }\n\n return issues\n },\n }\n}\n\nexport const linkValidator: ContentValidator = createLinkValidator()\n","/**\n * Validation runner — executes a list of content validators on an entry.\n *\n * Validators that throw are caught and converted to error-severity issues\n * so one failing validator does not block the rest.\n */\n\nimport type { Root } from 'mdast'\nimport remarkParse from 'remark-parse'\nimport { unified } from 'unified'\nimport type { ValidationIssue } from './schema-validator'\nimport type { ContentValidator, ResolvedValidatorContext, ValidatorContext } from './types'\n\nimport { codeBlockValidator } from './code-block-validator'\nimport { headingValidator } from './heading-validator'\nimport { linkValidator } from './link-validator'\n\n/** The built-in validators for markdown content. */\nexport const builtinMarkdownValidators: ContentValidator[] = [\n linkValidator,\n codeBlockValidator,\n headingValidator,\n]\n\n/** Parse raw markdown to MDAST once, reusing any pre-parsed tree. */\nfunction resolveContext(ctx: ValidatorContext): ResolvedValidatorContext {\n const mdast: Root =\n ctx.mdast ??\n unified()\n .use(remarkParse)\n .parse(ctx.rawContent ?? '')\n return { ...ctx, mdast }\n}\n\n/** Run all validators on a single content entry. */\nexport async function runValidators(\n ctx: ValidatorContext,\n validators: ContentValidator[],\n): Promise<ValidationIssue[]> {\n // Parse the MDAST once before the loop so all validators share the same tree.\n const resolved = resolveContext(ctx)\n\n const issues: ValidationIssue[] = []\n\n for (const validator of validators) {\n try {\n const result = await validator.validate(resolved)\n issues.push(...result.map((issue) => ({ ...issue, source: 'content' as const })))\n } catch (err) {\n // Convert thrown errors into issues so one bad validator doesn't abort all\n const message = err instanceof Error ? err.message : String(err)\n issues.push({\n message: `Validator \"${validator.name}\" threw: ${message}`,\n severity: 'error',\n source: 'content',\n })\n }\n }\n\n return issues\n}\n","/**\n * ContentStore — in-memory cache for loaded collections.\n *\n * Handles file discovery, loading, validation, and caching.\n * Not exported directly — used internally by ContentLayer.\n */\n\nimport { availableParallelism } from 'os'\nimport { resolve } from 'path'\nimport type { MarkdownConfig } from './schemas/markdown-config'\nimport type { ZodType } from 'zod'\nimport { ContentEntry } from './entry'\nimport { defaultIncludePatterns, resolveLoader } from './loaders'\nimport type { Loader } from './loaders/types'\nimport { collectRehypePlugins, collectRemarkPlugins, runPluginValidators } from './plugins'\nimport type { CollectionDef, RawEntry } from './schemas/collection'\nimport type { ContentLayerConfig } from './schemas/content-config'\nimport { discoverFiles } from './utils/glob'\nimport { toSlug } from './utils/slug'\nimport { validateSchema, type ValidationIssue } from './validation'\nimport { builtinMarkdownValidators, runValidators } from './validation/runner'\nimport type { ContentValidator } from './validation/types'\n\nconst MAX_CONCURRENCY = availableParallelism() * 2\n\nasync function mapWithConcurrency<T, R>(\n items: T[],\n concurrency: number,\n fn: (item: T) => Promise<R>,\n): Promise<R[]> {\n const results: R[] = Array.from({ length: items.length })\n let index = 0\n\n async function worker(): Promise<void> {\n while (index < items.length) {\n const i = index++\n results[i] = await fn(items[i])\n }\n }\n\n const workers = Array.from({ length: Math.min(concurrency, items.length) }, () => worker())\n await Promise.all(workers)\n return results\n}\n\ntype CacheEntry = {\n entry: ContentEntry\n issues: ValidationIssue[]\n}\n\nexport class ContentStore {\n private cache = new Map<string, Map<string, CacheEntry>>()\n private loaded = new Set<string>()\n private loading = new Map<string, Promise<ContentEntry[]>>()\n private config: ContentLayerConfig\n private rootDir: string\n private markdownConfig: MarkdownConfig\n\n constructor(config: ContentLayerConfig) {\n this.config = config\n this.rootDir = config.root ? resolve(config.root) : process.cwd()\n this.markdownConfig = this.createMarkdownConfig()\n }\n\n /** Load a collection (if not already loaded) and return entries. */\n async loadCollection<S extends ZodType>(\n name: string,\n def: CollectionDef<S>,\n ): Promise<ContentEntry[]> {\n if (this.loaded.has(name)) {\n const cached = this.cache.get(name)\n return cached ? Array.from(cached.values()).map((c) => c.entry) : []\n }\n\n const existing = this.loading.get(name)\n if (existing) return existing\n\n const promise = this._doLoadCollection(name, def)\n this.loading.set(name, promise)\n try {\n return await promise\n } finally {\n this.loading.delete(name)\n }\n }\n\n private async _doLoadCollection<S extends ZodType>(\n name: string,\n def: CollectionDef<S>,\n ): Promise<ContentEntry[]> {\n const loader = resolveLoader(def.loader)\n const directory = resolve(this.rootDir, def.directory)\n const include = def.include ?? defaultIncludePatterns(loader)\n\n const files = await discoverFiles({\n directory,\n include,\n exclude: def.exclude,\n })\n\n const entries = new Map<string, CacheEntry>()\n const strict = this.config.strict ?? false\n const results = await mapWithConcurrency(files, MAX_CONCURRENCY, async (filePath) => {\n try {\n return await this.loadEntry(name, filePath, directory, loader, def)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n const loadError = new Error(`[${name}] Failed to load ${filePath}: ${message}`, {\n cause: err,\n })\n\n if (strict) {\n throw loadError\n }\n\n console.warn(`[pagesmith] ${loadError.message}`)\n const slug = def.slugify ? def.slugify(filePath, directory) : toSlug(filePath, directory)\n return {\n entry: new ContentEntry(slug, name, filePath, {}, undefined, this.markdownConfig),\n issues: [\n { message: loadError.message, severity: 'error' as const, source: 'schema' as const },\n ],\n }\n }\n })\n\n for (const result of results) {\n if (result) {\n entries.set(result.entry.slug, result)\n }\n }\n\n this.cache.set(name, entries)\n this.loaded.add(name)\n\n return Array.from(entries.values()).map((c) => c.entry)\n }\n\n /** Load a single entry from a file. */\n private async loadEntry(\n collectionName: string,\n filePath: string,\n directory: string,\n loader: Loader,\n def: CollectionDef<any>,\n ): Promise<CacheEntry | null> {\n const loaded = await loader.load(filePath)\n const slug = def.slugify ? def.slugify(filePath, directory) : toSlug(filePath, directory)\n\n let raw: RawEntry = {\n data: loaded.data,\n content: loaded.content,\n filePath,\n slug,\n }\n\n // Apply transform\n if (def.transform) {\n raw = await def.transform(raw)\n }\n\n // Apply computed fields (clone data to avoid mutating loader output)\n if (def.computed) {\n raw = { ...raw, data: { ...raw.data } }\n for (const [key, fn] of Object.entries(def.computed) as Array<\n [string, (entry: RawEntry) => any]\n >) {\n raw.data[key] = await fn(raw)\n }\n }\n\n // Apply filter\n if (def.filter && !def.filter(raw)) {\n return null\n }\n\n // Validate schema once to collect issues and transformed data.\n const { issues, validatedData } = validateSchema(raw.data, def.schema)\n\n // Custom validation\n if (def.validate) {\n const customError = def.validate(raw)\n if (customError) {\n issues.push({ message: customError, severity: 'error', source: 'custom' })\n }\n }\n\n // Run content validators on markdown entries\n const isMarkdownEntry = raw.content !== undefined\n if (isMarkdownEntry) {\n const validators = this.resolveValidators(def)\n if (validators.length > 0) {\n const contentIssues = await runValidators(\n {\n filePath,\n slug,\n collection: collectionName,\n rawContent: raw.content,\n data: raw.data,\n getEntry: (col, s) => {\n const cached = this.cache.get(col)?.get(s)\n if (!cached) return undefined\n return { slug: cached.entry.slug, data: cached.entry.data }\n },\n },\n validators,\n )\n issues.push(...contentIssues)\n }\n }\n\n if (this.config.plugins?.length) {\n const pluginIssues = runPluginValidators(this.config.plugins, {\n data: raw.data,\n content: raw.content,\n })\n for (const message of pluginIssues) {\n issues.push({ message, severity: 'error', source: 'plugin' })\n }\n }\n\n const entry = new ContentEntry(\n slug,\n collectionName,\n filePath,\n validatedData,\n raw.content,\n this.markdownConfig,\n )\n\n return { entry, issues }\n }\n\n /** Get a single entry by slug. */\n getEntry(collection: string, slug: string): ContentEntry | undefined {\n return this.cache.get(collection)?.get(slug)?.entry\n }\n\n isCollectionLoaded(collection: string): boolean {\n return this.loaded.has(collection)\n }\n\n /** Get validation issues for a collection. */\n getIssues(collection: string): Map<string, ValidationIssue[]> {\n const result = new Map<string, ValidationIssue[]>()\n const entries = this.cache.get(collection)\n if (!entries) return result\n for (const [slug, cached] of entries) {\n if (cached.issues.length > 0) {\n result.set(slug, cached.issues)\n }\n }\n return result\n }\n\n /** Invalidate a single entry and reload it without reloading the entire collection. */\n async invalidate(collection: string, slug: string): Promise<void> {\n const def = this.config.collections[collection]\n if (!def) return\n\n const collectionCache = this.cache.get(collection)\n if (!collectionCache) return\n\n const existing = collectionCache.get(slug)\n if (!existing) return\n\n const loader = resolveLoader(def.loader)\n const directory = resolve(this.rootDir, def.directory)\n\n try {\n const result = await this.loadEntry(\n collection,\n existing.entry.filePath,\n directory,\n loader,\n def,\n )\n if (result) {\n collectionCache.set(slug, result)\n } else {\n // Entry was filtered out after reload\n collectionCache.delete(slug)\n }\n } catch {\n // File may have been deleted; remove from cache\n collectionCache.delete(slug)\n }\n }\n\n /** Invalidate an entire collection. */\n async invalidateCollection(collection: string): Promise<void> {\n this.cache.delete(collection)\n this.loaded.delete(collection)\n }\n\n /** Invalidate all collections. */\n invalidateAll(): void {\n this.cache.clear()\n this.loaded.clear()\n }\n\n /** Get cache statistics for debugging and monitoring. */\n getCacheStats(): { collections: number; entries: Record<string, number>; totalEntries: number } {\n const entries: Record<string, number> = {}\n let totalEntries = 0\n for (const [name, cache] of this.cache) {\n entries[name] = cache.size\n totalEntries += cache.size\n }\n return { collections: this.loaded.size, entries, totalEntries }\n }\n\n /** Invalidate entries matching a predicate without reloading the entire collection. */\n async invalidateWhere(\n collection: string,\n predicate: (entry: ContentEntry) => boolean,\n ): Promise<number> {\n const collectionCache = this.cache.get(collection)\n if (!collectionCache) return 0\n\n const def = this.config.collections[collection]\n if (!def) return 0\n\n const loader = resolveLoader(def.loader)\n const directory = resolve(this.rootDir, def.directory)\n let count = 0\n\n for (const [slug, cached] of collectionCache) {\n if (!predicate(cached.entry)) continue\n count++\n try {\n const result = await this.loadEntry(\n collection,\n cached.entry.filePath,\n directory,\n loader,\n def,\n )\n if (result) {\n collectionCache.set(slug, result)\n } else {\n collectionCache.delete(slug)\n }\n } catch {\n collectionCache.delete(slug)\n }\n }\n\n return count\n }\n\n /** Resolve the list of content validators for a collection. */\n private resolveValidators(def: CollectionDef<any>): ContentValidator[] {\n const builtin = def.disableBuiltinValidators ? [] : builtinMarkdownValidators\n const custom = def.validators ?? []\n return [...builtin, ...custom]\n }\n\n private createMarkdownConfig(): MarkdownConfig {\n const base = this.config.markdown ?? {}\n const remarkPlugins = [...(base.remarkPlugins ?? [])]\n const rehypePlugins = [...(base.rehypePlugins ?? [])]\n const plugins = this.config.plugins ?? []\n\n if (plugins.length > 0) {\n remarkPlugins.push(...collectRemarkPlugins(plugins))\n rehypePlugins.push(...collectRehypePlugins(plugins))\n }\n\n return {\n ...base,\n ...(remarkPlugins.length > 0 ? { remarkPlugins } : {}),\n ...(rehypePlugins.length > 0 ? { rehypePlugins } : {}),\n }\n }\n}\n","/**\n * ContentLayer — the main API for working with content collections.\n *\n * Created via createContentLayer(config). Provides methods to:\n * - Load and query collections (getCollection, getEntry)\n * - Convert markdown directly (convert)\n * - Invalidate cache (invalidate, invalidateCollection, invalidateAll)\n * - Validate all entries (validate)\n */\n\nimport { watch as fsWatch, type FSWatcher } from 'fs'\nimport { resolve as pathResolve } from 'path'\n\nimport { convert as coreConvert } from './convert'\nimport type { ConvertResult } from './convert'\nimport type { MarkdownConfig } from './schemas/markdown-config'\nimport type { ContentEntry } from './entry'\nimport type { CollectionDef, InferCollectionData } from './schemas/collection'\nimport type { ContentLayerConfig } from './schemas/content-config'\nimport { ContentStore } from './store'\nimport type { ValidationResult } from './validation'\n\nexport type WatchEvent = {\n collection: string\n event: string\n filename: string | null\n}\n\nexport type WatchHandle = {\n close(): void\n}\n\nexport type WatchCallback = (event: WatchEvent) => void\n\n/** Typed content layer that preserves collection schema types. */\nexport type TypedContentLayer<T extends Record<string, CollectionDef>> = ContentLayer & {\n getCollection<K extends keyof T & string>(\n name: K,\n ): Promise<ContentEntry<InferCollectionData<T[K]>>[]>\n getEntry<K extends keyof T & string>(\n collection: K,\n slug: string,\n ): Promise<ContentEntry<InferCollectionData<T[K]>> | undefined>\n}\n\nexport interface ContentLayer {\n /** Get all entries in a collection. */\n getCollection(name: string): Promise<ContentEntry[]>\n\n /** Get a single entry by collection name and slug. */\n getEntry(collection: string, slug: string): Promise<ContentEntry | undefined>\n\n /** Convert raw markdown to HTML (no collection, no validation). */\n convert(markdown: string, options?: LayerConvertOptions): Promise<ConvertResult>\n\n /** Invalidate a single entry's cache. */\n invalidate(collection: string, slug: string): Promise<void>\n\n /** Invalidate an entire collection's cache. */\n invalidateCollection(collection: string): Promise<void>\n\n /** Invalidate all cached data. */\n invalidateAll(): void\n\n /** Validate all entries in a collection (or all collections). */\n validate(collection?: string): Promise<ValidationResult[]>\n\n /** Get the names of all configured collections. */\n getCollectionNames(): string[]\n\n /** Get the definition of a collection. */\n getCollectionDef(name: string): CollectionDef | undefined\n\n /** Get all collection definitions. */\n getCollections(): Record<string, CollectionDef>\n\n /** Invalidate entries in a collection that match a predicate. Returns count of invalidated entries. */\n invalidateWhere(collection: string, predicate: (entry: ContentEntry) => boolean): Promise<number>\n\n /** Watch collection directories for changes. Returns a handle to stop watching. */\n watch(callback: WatchCallback): WatchHandle\n\n /** Get cache statistics for debugging and monitoring. */\n getCacheStats(): { collections: number; entries: Record<string, number>; totalEntries: number }\n}\n\nexport type LayerConvertOptions = {\n markdown?: MarkdownConfig\n}\n\nclass ContentLayerImpl implements ContentLayer {\n private store: ContentStore\n private config: ContentLayerConfig\n\n constructor(config: ContentLayerConfig) {\n this.config = config\n this.store = new ContentStore(config)\n }\n\n async getCollection(name: string): Promise<ContentEntry[]> {\n const def = this.config.collections[name]\n if (!def) {\n throw new Error(\n `Collection \"${name}\" not found. Available: ${Object.keys(this.config.collections).join(\n ', ',\n )}`,\n )\n }\n return this.store.loadCollection(name, def)\n }\n\n async getEntry(collection: string, slug: string): Promise<ContentEntry | undefined> {\n const cached = this.store.getEntry(collection, slug)\n if (cached) return cached\n\n // If the collection was already loaded and this slug wasn't found, short-circuit.\n if (this.store.isCollectionLoaded(collection)) return undefined\n\n // The first getEntry call loads the full collection and then serves from cache.\n // Single-entry loading would skip collection-level transforms and validation context.\n await this.getCollection(collection)\n return this.store.getEntry(collection, slug)\n }\n\n async convert(markdown: string, options?: LayerConvertOptions): Promise<ConvertResult> {\n return coreConvert(markdown, {\n markdown: options?.markdown ?? this.config.markdown,\n })\n }\n\n async invalidate(collection: string, slug: string): Promise<void> {\n await this.store.invalidate(collection, slug)\n }\n\n async invalidateCollection(collection: string): Promise<void> {\n await this.store.invalidateCollection(collection)\n }\n\n invalidateAll(): void {\n this.store.invalidateAll()\n }\n\n async invalidateWhere(\n collection: string,\n predicate: (entry: ContentEntry) => boolean,\n ): Promise<number> {\n return this.store.invalidateWhere(collection, predicate)\n }\n\n async validate(collection?: string): Promise<ValidationResult[]> {\n const names = collection ? [collection] : Object.keys(this.config.collections)\n const results: ValidationResult[] = []\n\n for (const name of names) {\n // Ensure loaded\n await this.getCollection(name)\n\n const issues = this.store.getIssues(name)\n const entries = Array.from(issues.entries()).map(([slug, entryIssues]) => {\n const entry = this.store.getEntry(name, slug)\n return {\n slug,\n filePath: entry?.filePath ?? '',\n issues: entryIssues,\n }\n })\n\n let errors = 0\n let warnings = 0\n for (const entry of entries) {\n for (const issue of entry.issues) {\n if (issue.severity === 'error') errors++\n else warnings++\n }\n }\n\n results.push({ collection: name, entries, errors, warnings })\n }\n\n return results\n }\n\n getCollectionNames(): string[] {\n return Object.keys(this.config.collections)\n }\n\n getCollectionDef(name: string): CollectionDef | undefined {\n return this.config.collections[name]\n }\n\n getCollections(): Record<string, CollectionDef> {\n return { ...this.config.collections }\n }\n\n watch(callback: WatchCallback): WatchHandle {\n const watchers: FSWatcher[] = []\n const root = this.config.root ?? process.cwd()\n const timers = new Map<string, ReturnType<typeof setTimeout>>()\n\n for (const [name, def] of Object.entries(this.config.collections)) {\n const dir = pathResolve(root, def.directory)\n try {\n const watcher = fsWatch(dir, { recursive: true }, (event, filename) => {\n // Debounce: coalesce rapid changes per collection within 100ms\n const existing = timers.get(name)\n if (existing) clearTimeout(existing)\n timers.set(\n name,\n setTimeout(() => {\n timers.delete(name)\n void this.store.invalidateCollection(name)\n callback({ collection: name, event, filename })\n }, 100),\n )\n })\n watcher.on('error', (err) => {\n console.warn(`Content watcher error for \"${name}\":`, err.message)\n })\n watchers.push(watcher)\n } catch {\n // Directory may not exist yet — skip silently\n }\n }\n\n return {\n close() {\n for (const w of watchers) w.close()\n for (const t of timers.values()) clearTimeout(t)\n timers.clear()\n },\n }\n }\n\n getCacheStats() {\n return this.store.getCacheStats()\n }\n}\n\n/** Create a new content layer from a configuration. */\nexport function createContentLayer<T extends Record<string, CollectionDef>>(\n config: ContentLayerConfig & { collections: T },\n): TypedContentLayer<T>\nexport function createContentLayer(config: ContentLayerConfig): ContentLayer\nexport function createContentLayer(config: ContentLayerConfig): ContentLayer {\n return new ContentLayerImpl(config)\n}\n"],"mappings":"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"}
|