@content-collections/core 0.3.1 → 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 CHANGED
@@ -110,6 +110,18 @@ Transform your content into type-safe data collections. Eliminate the need for m
110
110
 
111
111
  For a more detailed guide, please refer to the [documentation](https://content-collections.dev/docs/guides/getting-started).
112
112
 
113
+ ## Sponsors
114
+
115
+ <a href="https://supastarter.dev">
116
+ <picture>
117
+ <source media="(prefers-color-scheme: dark)" srcset="./assets/sponsors/supastarter/dark.svg">
118
+ <source media="(prefers-color-scheme: light)" srcset="./assets/sponsors/supastarter/light.svg">
119
+ <img alt="supastarter" src="./assets/sponsors/supastarter/light.svg" height="64">
120
+ </picture>
121
+ </a>
122
+
123
+ ### [Become a sponsor](https://github.com/sponsors/sdorra)
124
+
113
125
  ## License
114
126
 
115
127
  Content Collections is licensed under the [MIT License](./LICENSE).
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ZodRawShape, z, ZodObject, ZodTypeAny, ZodString } from 'zod';
1
+ import z, { ZodRawShape, z as z$1, ZodObject, ZodTypeAny, ZodString } from 'zod';
2
2
  import { parse } from 'yaml';
3
3
 
4
4
  type Parsers = typeof parsers;
@@ -23,6 +23,14 @@ declare const parsers: {
23
23
 
24
24
  type CacheFn = <TInput, TOutput>(input: TInput, compute: (input: TInput) => Promise<TOutput> | TOutput) => Promise<TOutput>;
25
25
 
26
+ declare const literalSchema: z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull, z.ZodUndefined]>;
27
+ type Literal = z.infer<typeof literalSchema>;
28
+ type Json = Literal | {
29
+ [key: string]: Json;
30
+ } | Json[];
31
+ declare const jsonObjectScheme: z.ZodRecord<z.ZodString, z.ZodType<Json, z.ZodTypeDef, Json>>;
32
+ type JSONObject = z.infer<typeof jsonObjectScheme>;
33
+
26
34
  type Meta = {
27
35
  filePath: string;
28
36
  fileName: string;
@@ -38,14 +46,14 @@ type AddContent<TShape extends ZodRawShape> = TShape extends {
38
46
  } ? TShape : TShape & WithContent;
39
47
  type GetParsedShape<TParser extends Parser, TShape extends ZodRawShape> = Parsers[TParser]["hasContent"] extends true ? AddContent<TShape> : TShape;
40
48
  type GetShape<TParser extends Parser | undefined, TShape extends ZodRawShape> = TParser extends Parser ? GetParsedShape<TParser, TShape> : AddContent<TShape>;
41
- type Schema<TParser extends Parser | undefined, TShape extends ZodRawShape> = z.infer<ZodObject<GetShape<TParser, TShape>>> & {
49
+ type Schema<TParser extends Parser | undefined, TShape extends ZodRawShape> = z$1.infer<ZodObject<GetShape<TParser, TShape>>> & {
42
50
  _meta: Meta;
43
51
  };
44
52
  type Context = {
45
53
  documents<TCollection extends AnyCollection>(collection: TCollection): Array<Schema<TCollection["parser"], TCollection["schema"]>>;
46
54
  cache: CacheFn;
47
55
  };
48
- type Z = typeof z;
56
+ type Z = typeof z$1;
49
57
  type CollectionRequest<TName extends string, TShape extends ZodRawShape, TParser, TSchema, TTransformResult, TDocument> = {
50
58
  name: TName;
51
59
  parser?: TParser;
@@ -62,7 +70,13 @@ type Collection<TName extends string, TShape extends ZodRawShape, TParser extend
62
70
  parser: TParser;
63
71
  };
64
72
  type AnyCollection = Collection<any, ZodRawShape, Parser, any, any, any>;
65
- declare function defineCollection<TName extends string, TShape extends ZodRawShape, TParser extends Parser = "frontmatter", TSchema = Schema<TParser, TShape>, TTransformResult = never, TDocument = [TTransformResult] extends [never] ? Schema<TParser, TShape> : Awaited<TTransformResult>>(collection: CollectionRequest<TName, TShape, TParser, TSchema, TTransformResult, TDocument>): Collection<TName, TShape, TParser, TSchema, TTransformResult, TDocument>;
73
+ type NonJSONObjectError = "The return type of the transform function must be an valid JSONObject, the following type is not valid:";
74
+ declare const InvalidReturnTypeSymbol: unique symbol;
75
+ type InvalidReturnType<TMessage extends string, TObject> = {
76
+ [InvalidReturnTypeSymbol]: TMessage;
77
+ object: TObject;
78
+ };
79
+ declare function defineCollection<TName extends string, TShape extends ZodRawShape, TParser extends Parser = "frontmatter", TSchema = Schema<TParser, TShape>, TTransformResult = never, TDocument = [TTransformResult] extends [never] ? Schema<TParser, TShape> : Awaited<TTransformResult>, TResult = TDocument extends JSONObject ? Collection<TName, TShape, TParser, TSchema, TTransformResult, TDocument> : InvalidReturnType<NonJSONObjectError, TDocument>>(collection: CollectionRequest<TName, TShape, TParser, TSchema, TTransformResult, TDocument>): TResult;
66
80
  type Cache = "memory" | "file" | "none";
67
81
  type Configuration<TCollections extends Array<AnyCollection>> = {
68
82
  collections: TCollections;
@@ -107,12 +121,17 @@ type TransformerEvents = {
107
121
  file: CollectionFile;
108
122
  error: TransformError;
109
123
  };
124
+ "transformer:result-error": {
125
+ collection: AnyCollection;
126
+ document: any;
127
+ error: TransformError;
128
+ };
110
129
  "transformer:error": {
111
130
  collection: AnyCollection;
112
131
  error: TransformError;
113
132
  };
114
133
  };
115
- type ErrorType$1 = "Validation" | "Configuration" | "Transform";
134
+ type ErrorType$1 = "Validation" | "Configuration" | "Transform" | "Result";
116
135
  declare class TransformError extends Error {
117
136
  type: ErrorType$1;
118
137
  constructor(type: ErrorType$1, message: string);
@@ -167,7 +186,7 @@ declare function createBuilder(configurationPath: string, options?: Options): Pr
167
186
  unsubscribe: () => Promise<void>;
168
187
  }>;
169
188
  on: {
170
- <TKey extends "builder:start" | "builder:end" | "collector:read-error" | "collector:parse-error" | "transformer:validation-error" | "transformer:error" | "watcher:file-changed">(key: TKey, listener: (event: Events[TKey]) => void): void;
189
+ <TKey extends "builder:start" | "builder:end" | "collector:read-error" | "collector:parse-error" | "transformer:validation-error" | "transformer:result-error" | "transformer:error" | "watcher:file-changed">(key: TKey, listener: (event: Events[TKey]) => void): void;
171
190
  <TKey_1 extends "_error" | "_all">(key: TKey_1, listener: (event: SystemEvents[TKey_1]) => void): void;
172
191
  };
173
192
  }>;
package/dist/index.js CHANGED
@@ -16,6 +16,7 @@ function orderByPath(a, b) {
16
16
  }
17
17
 
18
18
  // src/config.ts
19
+ var InvalidReturnTypeSymbol = Symbol(`InvalidReturnType`);
19
20
  function defineCollection(collection) {
20
21
  let typeName = collection.typeName;
21
22
  if (!typeName) {
@@ -44,7 +45,7 @@ import path from "path";
44
45
  // package.json
45
46
  var package_default = {
46
47
  name: "@content-collections/core",
47
- version: "0.3.1",
48
+ version: "0.4.1",
48
49
  type: "module",
49
50
  main: "dist/index.js",
50
51
  types: "./dist/index.d.ts",
@@ -314,11 +315,18 @@ async function createJavaScriptFile(directory, configuration) {
314
315
  content += "export { " + collections.join(", ") + " };\n";
315
316
  await fs2.writeFile(path3.join(directory, "index.js"), content, "utf-8");
316
317
  }
318
+ function createImportPath(directory, target) {
319
+ let importPath = path3.posix.join(...path3.relative(directory, target).split(path3.sep));
320
+ if (!importPath.startsWith(".")) {
321
+ importPath = "./" + importPath;
322
+ }
323
+ return importPath;
324
+ }
317
325
  async function createTypeDefinitionFile(directory, configuration) {
318
326
  if (!configuration.generateTypes) {
319
327
  return;
320
328
  }
321
- const importPath = path3.relative(directory, configuration.path);
329
+ const importPath = createImportPath(directory, configuration.path);
322
330
  let content = `import configuration from "${importPath}";
323
331
  import { GetTypeByName } from "@content-collections/core";
324
332
  `;
@@ -348,7 +356,23 @@ async function createWriter(directory) {
348
356
 
349
357
  // src/transformer.ts
350
358
  import { basename, dirname, extname } from "path";
351
- import { z as z2 } from "zod";
359
+ import { z as z3 } from "zod";
360
+
361
+ // src/json.ts
362
+ import z2 from "zod";
363
+ var literalSchema = z2.union([
364
+ z2.string(),
365
+ z2.number(),
366
+ z2.boolean(),
367
+ z2.null(),
368
+ z2.undefined()
369
+ ]);
370
+ var jsonSchema = z2.lazy(
371
+ () => z2.union([literalSchema, z2.array(jsonSchema), z2.record(jsonSchema)])
372
+ );
373
+ var jsonObjectScheme = z2.record(jsonSchema);
374
+
375
+ // src/transformer.ts
352
376
  var TransformError = class extends Error {
353
377
  type;
354
378
  constructor(type, message) {
@@ -367,10 +391,10 @@ function createTransformer(emitter, cacheManager) {
367
391
  function createSchema(parserName, schema) {
368
392
  const parser = parsers[parserName];
369
393
  if (!parser.hasContent) {
370
- return z2.object(schema);
394
+ return z3.object(schema);
371
395
  }
372
- return z2.object({
373
- content: z2.string(),
396
+ return z3.object({
397
+ content: z3.string(),
374
398
  ...schema
375
399
  });
376
400
  }
@@ -433,12 +457,16 @@ function createTransformer(emitter, cacheManager) {
433
457
  if (collection.transform) {
434
458
  const docs = [];
435
459
  for (const doc of collection.documents) {
436
- const cache = cacheManager.cache(collection.name, doc.document._meta.path);
460
+ const cache = cacheManager.cache(
461
+ collection.name,
462
+ doc.document._meta.path
463
+ );
437
464
  const context = createContext(collections, cache);
438
465
  try {
466
+ const document = await collection.transform(doc.document, context);
439
467
  docs.push({
440
468
  ...doc,
441
- document: await collection.transform(doc.document, context)
469
+ document
442
470
  });
443
471
  await cache.tidyUp();
444
472
  } catch (error) {
@@ -460,13 +488,30 @@ function createTransformer(emitter, cacheManager) {
460
488
  }
461
489
  return collection.documents;
462
490
  }
491
+ async function validateDocuments(collection, documents) {
492
+ const docs = [];
493
+ for (const doc of documents) {
494
+ let parsedData = await jsonObjectScheme.safeParseAsync(doc.document);
495
+ if (parsedData.success) {
496
+ docs.push(doc);
497
+ } else {
498
+ emitter.emit("transformer:result-error", {
499
+ collection,
500
+ document: doc.document,
501
+ error: new TransformError("Result", parsedData.error.message)
502
+ });
503
+ }
504
+ }
505
+ return docs;
506
+ }
463
507
  return async (untransformedCollections) => {
464
508
  const promises = untransformedCollections.map(
465
509
  (collection) => parseCollection(collection)
466
510
  );
467
511
  const collections = await Promise.all(promises);
468
512
  for (const collection of collections) {
469
- collection.documents = await transformCollection(collections, collection);
513
+ const documents = await transformCollection(collections, collection);
514
+ collection.documents = await validateDocuments(collection, documents);
470
515
  }
471
516
  return collections;
472
517
  };
@@ -476,9 +521,9 @@ function createTransformer(emitter, cacheManager) {
476
521
  import micromatch from "micromatch";
477
522
  import path4 from "path";
478
523
  function createSynchronizer(readCollectionFile, collections, baseDirectory = ".") {
479
- function findCollection(filePath) {
524
+ function findCollections(filePath) {
480
525
  const resolvedFilePath = path4.resolve(filePath);
481
- return collections.find((collection) => {
526
+ return collections.filter((collection) => {
482
527
  return resolvedFilePath.startsWith(
483
528
  path4.resolve(baseDirectory, collection.directory)
484
529
  );
@@ -494,51 +539,56 @@ function createSynchronizer(readCollectionFile, collections, baseDirectory = "."
494
539
  return relativePath;
495
540
  }
496
541
  function resolve(filePath) {
497
- const collection = findCollection(filePath);
498
- if (!collection) {
499
- return null;
500
- }
501
- const relativePath = createRelativePath(collection.directory, filePath);
502
- if (!micromatch.isMatch(relativePath, collection.include)) {
503
- return null;
504
- }
505
- return {
506
- collection,
507
- relativePath
508
- };
542
+ const collections2 = findCollections(filePath);
543
+ return collections2.map((collection) => {
544
+ const relativePath = createRelativePath(collection.directory, filePath);
545
+ return {
546
+ collection,
547
+ relativePath
548
+ };
549
+ }).filter(({ collection, relativePath }) => {
550
+ return micromatch.isMatch(relativePath, collection.include);
551
+ });
509
552
  }
510
553
  function deleted(filePath) {
511
- const resolved = resolve(filePath);
512
- if (!resolved) {
554
+ const resolvedCollections = resolve(filePath);
555
+ if (resolvedCollections.length === 0) {
513
556
  return false;
514
557
  }
515
- const { collection, relativePath } = resolved;
516
- const index = collection.files.findIndex(
517
- (file) => file.path === relativePath
518
- );
519
- const deleted2 = collection.files.splice(index, 1);
520
- return deleted2.length > 0;
558
+ let changed2 = false;
559
+ for (const { collection, relativePath } of resolvedCollections) {
560
+ const index = collection.files.findIndex(
561
+ (file) => file.path === relativePath
562
+ );
563
+ const deleted2 = collection.files.splice(index, 1);
564
+ if (deleted2.length > 0) {
565
+ changed2 = true;
566
+ }
567
+ }
568
+ return changed2;
521
569
  }
522
570
  async function changed(filePath) {
523
- const resolved = resolve(filePath);
524
- if (!resolved) {
571
+ const resolvedCollections = resolve(filePath);
572
+ if (resolvedCollections.length === 0) {
525
573
  return false;
526
574
  }
527
- const { collection, relativePath } = resolved;
528
- const index = collection.files.findIndex(
529
- (file2) => file2.path === relativePath
530
- );
531
- const file = await readCollectionFile(collection, relativePath);
532
- if (!file) {
533
- return false;
534
- }
535
- if (index === -1) {
536
- collection.files.push(file);
537
- collection.files.sort(orderByPath);
538
- } else {
539
- collection.files[index] = file;
575
+ let changed2 = false;
576
+ for (const { collection, relativePath } of resolvedCollections) {
577
+ const index = collection.files.findIndex(
578
+ (file2) => file2.path === relativePath
579
+ );
580
+ const file = await readCollectionFile(collection, relativePath);
581
+ if (file) {
582
+ changed2 = true;
583
+ if (index === -1) {
584
+ collection.files.push(file);
585
+ collection.files.sort(orderByPath);
586
+ } else {
587
+ collection.files[index] = file;
588
+ }
589
+ }
540
590
  }
541
- return true;
591
+ return changed2;
542
592
  }
543
593
  return {
544
594
  deleted,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@content-collections/core",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "./dist/index.d.ts",