@content-collections/core 0.4.1 → 0.6.0

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
@@ -22,35 +22,16 @@ Transform your content into type-safe data collections. Eliminate the need for m
22
22
 
23
23
  ## Installation
24
24
 
25
- 1. Install required packages:
25
+ Content Collections offers a variety of adapters that seamlessly integrate with popular web frameworks. The installation process depends on the chosen adapter:
26
26
 
27
- ```bash
28
- pnpm add -D @content-collections/core @content-collections/cli concurrently
29
- ```
30
-
31
- 1. Add path alias to your `tsconfig.json`:
32
-
33
- ```json
34
- {
35
- "compilerOptions": {
36
- // ...
37
- "paths": {
38
- "content-collections": ["./.content-collections/generated"]
39
- }
40
- }
41
- }
42
- ```
27
+ - [Next.js](https://www.content-collections.dev/docs/quickstart/next)
28
+ - [Qwik](https://www.content-collections.dev/docs/quickstart/qwik)
29
+ - [Remix (Vite)](https://www.content-collections.dev/docs/quickstart/remix-vite)
30
+ - [Solid Start](https://www.content-collections.dev/docs/quickstart/solid)
31
+ - [Svelte Kit](https://www.content-collections.dev/docs/quickstart/svelte-kit)
32
+ - [Vite](https://www.content-collections.dev/docs/quickstart/vite)
43
33
 
44
- 1. Update your scripts in `package.json`:
45
-
46
- ```json
47
- {
48
- "scripts": {
49
- "dev": "concurrently 'content-collections watch' 'build-scripts dev'",
50
- "build": "content-collections build && build-scripts build"
51
- }
52
- }
53
- ```
34
+ If your framework is not listed, you can still use Content Collections by using the [CLI](https://www.content-collections.dev/docs/quickstart/cli). Please open a ticket if you want to see your framework listed.
54
35
 
55
36
  ## Usage
56
37
 
@@ -108,7 +89,14 @@ Transform your content into type-safe data collections. Eliminate the need for m
108
89
  }
109
90
  ```
110
91
 
111
- For a more detailed guide, please refer to the [documentation](https://content-collections.dev/docs/guides/getting-started).
92
+ Please note that the example above shows only the very basics of Content Collections and it does not cover content transformation.
93
+ Content Collections does not transform content like markdown or mdx by default.
94
+ But it has packages which can do that for you:
95
+
96
+ - [@content-collections/markdown](https://www.content-collections.dev/docs/samples/markdown)
97
+ - [@content-collections/mdx](https://www.content-collections.dev/docs/samples/mdx)
98
+
99
+ If you want to see more examples and use cases, please refer to the [documentation](https://content-collections.dev/docs).
112
100
 
113
101
  ## Sponsors
114
102
 
@@ -119,6 +107,10 @@ For a more detailed guide, please refer to the [documentation](https://content-c
119
107
  <img alt="supastarter" src="./assets/sponsors/supastarter/light.svg" height="64">
120
108
  </picture>
121
109
  </a>
110
+ &nbsp;&nbsp;
111
+ <a href="https://cloudogu.com">
112
+ <img src="./assets/sponsors/cloudogu.png" alt="Cloudogu GmbH" height="64">
113
+ </a>
122
114
 
123
115
  ### [Become a sponsor](https://github.com/sponsors/sdorra)
124
116
 
package/dist/index.d.ts CHANGED
@@ -23,13 +23,17 @@ 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]>;
26
+ declare const literalSchema: z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull, z.ZodUndefined, z.ZodDate, z.ZodMap<z.ZodUnknown, z.ZodUnknown>, z.ZodSet<z.ZodUnknown>, z.ZodBigInt]>;
27
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>;
28
+ type SchemaType = Literal | {
29
+ [key: string]: SchemaType;
30
+ } | SchemaType[];
31
+ type NotSerializableError = `The return type of the transform function must be an object serializable object.
32
+ See https://www.content-collections.dev/docs/serialization for more information.
33
+
34
+ The following type is not valid:`;
35
+ declare const serializableSchema: z.ZodRecord<z.ZodString, z.ZodType<SchemaType, z.ZodTypeDef, SchemaType>>;
36
+ type Serializable = z.infer<typeof serializableSchema>;
33
37
 
34
38
  type Meta = {
35
39
  filePath: string;
@@ -52,6 +56,10 @@ type Schema<TParser extends Parser | undefined, TShape extends ZodRawShape> = z$
52
56
  type Context = {
53
57
  documents<TCollection extends AnyCollection>(collection: TCollection): Array<Schema<TCollection["parser"], TCollection["schema"]>>;
54
58
  cache: CacheFn;
59
+ collection: {
60
+ name: string;
61
+ directory: string;
62
+ };
55
63
  };
56
64
  type Z = typeof z$1;
57
65
  type CollectionRequest<TName extends string, TShape extends ZodRawShape, TParser, TSchema, TTransformResult, TDocument> = {
@@ -62,6 +70,7 @@ type CollectionRequest<TName extends string, TShape extends ZodRawShape, TParser
62
70
  transform?: (data: TSchema, context: Context) => TTransformResult;
63
71
  directory: string;
64
72
  include: string | string[];
73
+ exclude?: string | string[];
65
74
  onSuccess?: (documents: Array<TDocument>) => void | Promise<void>;
66
75
  };
67
76
  type Collection<TName extends string, TShape extends ZodRawShape, TParser extends Parser, TSchema, TTransformResult, TDocument> = Omit<CollectionRequest<TName, TShape, TParser, TSchema, TTransformResult, TDocument>, "schema"> & {
@@ -70,13 +79,12 @@ type Collection<TName extends string, TShape extends ZodRawShape, TParser extend
70
79
  parser: TParser;
71
80
  };
72
81
  type AnyCollection = Collection<any, ZodRawShape, Parser, any, any, any>;
73
- type NonJSONObjectError = "The return type of the transform function must be an valid JSONObject, the following type is not valid:";
74
82
  declare const InvalidReturnTypeSymbol: unique symbol;
75
83
  type InvalidReturnType<TMessage extends string, TObject> = {
76
84
  [InvalidReturnTypeSymbol]: TMessage;
77
85
  object: TObject;
78
86
  };
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;
87
+ 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 Serializable ? Collection<TName, TShape, TParser, TSchema, TTransformResult, TDocument> : InvalidReturnType<NotSerializableError, TDocument>>(collection: CollectionRequest<TName, TShape, TParser, TSchema, TTransformResult, TDocument>): TResult;
80
88
  type Cache = "memory" | "file" | "none";
81
89
  type Configuration<TCollections extends Array<AnyCollection>> = {
82
90
  collections: TCollections;
@@ -86,6 +94,9 @@ type AnyConfiguration = Configuration<Array<AnyCollection>>;
86
94
  declare function defineConfig<TConfig extends AnyConfiguration>(config: TConfig): TConfig;
87
95
 
88
96
  type Modification = "create" | "update" | "delete";
97
+ type Document = {
98
+ _meta: Meta;
99
+ };
89
100
  type CollectionFile = {
90
101
  data: {
91
102
  content?: string;
@@ -192,4 +203,4 @@ declare function createBuilder(configurationPath: string, options?: Options): Pr
192
203
  }>;
193
204
  type Builder = Awaited<ReturnType<typeof createBuilder>>;
194
205
 
195
- export { AnyCollection, AnyConfiguration, Builder, BuilderEvents, CollectError, Collection, CollectionRequest, Configuration, ConfigurationError, Context, GetTypeByName, Meta, Modification, Schema, TransformError, createBuilder, defineCollection, defineConfig };
206
+ export { type AnyCollection, type AnyConfiguration, type Builder, type BuilderEvents, CollectError, type Collection, type CollectionRequest, type Configuration, ConfigurationError, type Context, type Document, type GetTypeByName, type Meta, type Modification, type Schema, TransformError, createBuilder, defineCollection, defineConfig };
package/dist/index.js CHANGED
@@ -45,7 +45,7 @@ import path from "path";
45
45
  // package.json
46
46
  var package_default = {
47
47
  name: "@content-collections/core",
48
- version: "0.4.1",
48
+ version: "0.6.0",
49
49
  type: "module",
50
50
  main: "dist/index.js",
51
51
  types: "./dist/index.d.ts",
@@ -67,26 +67,31 @@ var package_default = {
67
67
  prepack: "cp ../../README.md ./README.md",
68
68
  postpack: "rm -f ./README.md"
69
69
  },
70
+ peerDependencies: {
71
+ typescript: "^5.0.2"
72
+ },
70
73
  devDependencies: {
71
- "@types/micromatch": "^4.0.6",
72
- "@types/node": "^20.9.0",
74
+ "@types/micromatch": "^4.0.7",
75
+ "@types/node": "^20.14.2",
73
76
  "@types/pluralize": "^0.0.33",
74
- "@vitest/coverage-v8": "^1.1.0",
75
- tsup: "^7.2.0",
77
+ "@types/serialize-javascript": "^5.0.4",
78
+ "@vitest/coverage-v8": "^1.5.0",
79
+ tsup: "^8.0.2",
76
80
  tsx: "^4.1.1",
77
- typescript: "^5.3.3",
78
- vitest: "^1.1.0"
81
+ typescript: "^5.4.5",
82
+ vitest: "^1.5.0"
79
83
  },
80
84
  dependencies: {
81
- "@parcel/watcher": "^2.3.0",
85
+ "@parcel/watcher": "^2.4.1",
82
86
  camelcase: "^8.0.0",
83
- esbuild: "^0.19.5",
87
+ esbuild: "^0.21.4",
84
88
  "fast-glob": "^3.3.2",
85
89
  "gray-matter": "^4.0.3",
86
90
  micromatch: "^4.0.5",
87
91
  pluralize: "^8.0.0",
88
- yaml: "^2.3.4",
89
- zod: "^3.22.4"
92
+ "serialize-javascript": "^6.0.2",
93
+ yaml: "^2.4.1",
94
+ zod: "^3.22.5"
90
95
  }
91
96
  };
92
97
 
@@ -245,12 +250,23 @@ function createCollector(emitter, baseDirectory = ".") {
245
250
  return null;
246
251
  }
247
252
  }
253
+ function createIgnorePattern(collection) {
254
+ if (collection.exclude) {
255
+ if (Array.isArray(collection.exclude)) {
256
+ return collection.exclude;
257
+ } else {
258
+ return [collection.exclude];
259
+ }
260
+ }
261
+ return void 0;
262
+ }
248
263
  async function resolveCollection(collection) {
249
264
  const collectionDirectory = path2.join(baseDirectory, collection.directory);
250
265
  const filePaths = await fg(collection.include, {
251
266
  cwd: collectionDirectory,
252
267
  onlyFiles: true,
253
- absolute: false
268
+ absolute: false,
269
+ ignore: createIgnorePattern(collection)
254
270
  });
255
271
  const promises = filePaths.map(
256
272
  (filePath) => collectFile(collection, filePath)
@@ -277,6 +293,38 @@ function createCollector(emitter, baseDirectory = ".") {
277
293
  import fs2 from "fs/promises";
278
294
  import path3 from "path";
279
295
  import pluralize2 from "pluralize";
296
+
297
+ // src/serializer.ts
298
+ import z2 from "zod";
299
+ import serializeJs from "serialize-javascript";
300
+ var literalSchema = z2.union([
301
+ // json
302
+ z2.string(),
303
+ z2.number(),
304
+ z2.boolean(),
305
+ z2.null(),
306
+ // serializable-javascript
307
+ z2.undefined(),
308
+ z2.date(),
309
+ z2.map(z2.unknown(), z2.unknown()),
310
+ z2.set(z2.unknown()),
311
+ z2.bigint()
312
+ ]);
313
+ var schema = z2.lazy(
314
+ () => z2.union([literalSchema, z2.array(schema), z2.record(schema)])
315
+ );
316
+ var extension = "js";
317
+ var serializableSchema = z2.record(schema);
318
+ function serialize(value) {
319
+ const serializedValue = serializeJs(value, {
320
+ space: 2,
321
+ unsafe: true,
322
+ ignoreFunction: true
323
+ });
324
+ return `export default ${serializedValue};`;
325
+ }
326
+
327
+ // src/writer.ts
280
328
  function createArrayConstName(name) {
281
329
  let suffix = name.charAt(0).toUpperCase() + name.slice(1);
282
330
  return "all" + pluralize2(suffix);
@@ -284,15 +332,11 @@ function createArrayConstName(name) {
284
332
  async function createDataFile(directory, collection) {
285
333
  const dataPath = path3.join(
286
334
  directory,
287
- `${createArrayConstName(collection.name)}.json`
335
+ `${createArrayConstName(collection.name)}.${extension}`
288
336
  );
289
337
  await fs2.writeFile(
290
338
  dataPath,
291
- JSON.stringify(
292
- collection.documents.map((doc) => doc.document),
293
- null,
294
- 2
295
- )
339
+ serialize(collection.documents.map((doc) => doc.document))
296
340
  );
297
341
  }
298
342
  function createDataFiles(directory, collections) {
@@ -308,7 +352,7 @@ async function createJavaScriptFile(directory, configuration) {
308
352
 
309
353
  `;
310
354
  for (const name of collections) {
311
- content += `import ${name} from "./${name}.json";
355
+ content += `import ${name} from "./${name}.${extension}";
312
356
  `;
313
357
  }
314
358
  content += "\n";
@@ -316,7 +360,9 @@ async function createJavaScriptFile(directory, configuration) {
316
360
  await fs2.writeFile(path3.join(directory, "index.js"), content, "utf-8");
317
361
  }
318
362
  function createImportPath(directory, target) {
319
- let importPath = path3.posix.join(...path3.relative(directory, target).split(path3.sep));
363
+ let importPath = path3.posix.join(
364
+ ...path3.relative(directory, target).split(path3.sep)
365
+ );
320
366
  if (!importPath.startsWith(".")) {
321
367
  importPath = "./" + importPath;
322
368
  }
@@ -357,22 +403,6 @@ async function createWriter(directory) {
357
403
  // src/transformer.ts
358
404
  import { basename, dirname, extname } from "path";
359
405
  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
376
406
  var TransformError = class extends Error {
377
407
  type;
378
408
  constructor(type, message) {
@@ -388,20 +418,20 @@ function createPath(path7, ext) {
388
418
  return p;
389
419
  }
390
420
  function createTransformer(emitter, cacheManager) {
391
- function createSchema(parserName, schema) {
421
+ function createSchema(parserName, schema2) {
392
422
  const parser = parsers[parserName];
393
423
  if (!parser.hasContent) {
394
- return z3.object(schema);
424
+ return z3.object(schema2);
395
425
  }
396
426
  return z3.object({
397
427
  content: z3.string(),
398
- ...schema
428
+ ...schema2
399
429
  });
400
430
  }
401
431
  async function parseFile(collection, file) {
402
432
  const { data, path: path7 } = file;
403
- const schema = createSchema(collection.parser, collection.schema);
404
- let parsedData = await schema.safeParseAsync(data);
433
+ const schema2 = createSchema(collection.parser, collection.schema);
434
+ let parsedData = await schema2.safeParseAsync(data);
405
435
  if (!parsedData.success) {
406
436
  emitter.emit("transformer:validation-error", {
407
437
  collection,
@@ -411,9 +441,9 @@ function createTransformer(emitter, cacheManager) {
411
441
  return null;
412
442
  }
413
443
  const ext = extname(path7);
414
- let extension = ext;
415
- if (extension.startsWith(".")) {
416
- extension = extension.slice(1);
444
+ let extension2 = ext;
445
+ if (extension2.startsWith(".")) {
446
+ extension2 = extension2.slice(1);
417
447
  }
418
448
  const document = {
419
449
  ...parsedData.data,
@@ -421,7 +451,7 @@ function createTransformer(emitter, cacheManager) {
421
451
  filePath: path7,
422
452
  fileName: basename(path7),
423
453
  directory: dirname(path7),
424
- extension,
454
+ extension: extension2,
425
455
  path: createPath(path7, ext)
426
456
  }
427
457
  };
@@ -438,18 +468,22 @@ function createTransformer(emitter, cacheManager) {
438
468
  documents: (await Promise.all(promises)).filter(isDefined)
439
469
  };
440
470
  }
441
- function createContext(collections, cache) {
471
+ function createContext(collections, collection, cache) {
442
472
  return {
443
- documents: (collection) => {
444
- const resolved = collections.find((c) => c.name === collection.name);
473
+ documents: (collection2) => {
474
+ const resolved = collections.find((c) => c.name === collection2.name);
445
475
  if (!resolved) {
446
476
  throw new TransformError(
447
477
  "Configuration",
448
- `Collection ${collection.name} not found, do you have registered it in your configuration?`
478
+ `Collection ${collection2.name} not found, do you have registered it in your configuration?`
449
479
  );
450
480
  }
451
481
  return resolved.documents.map((doc) => doc.document);
452
482
  },
483
+ collection: {
484
+ name: collection.name,
485
+ directory: collection.directory
486
+ },
453
487
  cache: cache.cacheFn
454
488
  };
455
489
  }
@@ -461,7 +495,7 @@ function createTransformer(emitter, cacheManager) {
461
495
  collection.name,
462
496
  doc.document._meta.path
463
497
  );
464
- const context = createContext(collections, cache);
498
+ const context = createContext(collections, collection, cache);
465
499
  try {
466
500
  const document = await collection.transform(doc.document, context);
467
501
  docs.push({
@@ -491,7 +525,7 @@ function createTransformer(emitter, cacheManager) {
491
525
  async function validateDocuments(collection, documents) {
492
526
  const docs = [];
493
527
  for (const doc of documents) {
494
- let parsedData = await jsonObjectScheme.safeParseAsync(doc.document);
528
+ let parsedData = await serializableSchema.safeParseAsync(doc.document);
495
529
  if (parsedData.success) {
496
530
  docs.push(doc);
497
531
  } else {
@@ -547,7 +581,9 @@ function createSynchronizer(readCollectionFile, collections, baseDirectory = "."
547
581
  relativePath
548
582
  };
549
583
  }).filter(({ collection, relativePath }) => {
550
- return micromatch.isMatch(relativePath, collection.include);
584
+ return micromatch.isMatch(relativePath, collection.include, {
585
+ ignore: collection.exclude
586
+ });
551
587
  });
552
588
  }
553
589
  function deleted(filePath) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@content-collections/core",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -15,26 +15,31 @@
15
15
  "dist",
16
16
  "README.md"
17
17
  ],
18
+ "peerDependencies": {
19
+ "typescript": "^5.0.2"
20
+ },
18
21
  "devDependencies": {
19
- "@types/micromatch": "^4.0.6",
20
- "@types/node": "^20.9.0",
22
+ "@types/micromatch": "^4.0.7",
23
+ "@types/node": "^20.14.2",
21
24
  "@types/pluralize": "^0.0.33",
22
- "@vitest/coverage-v8": "^1.1.0",
23
- "tsup": "^7.2.0",
25
+ "@types/serialize-javascript": "^5.0.4",
26
+ "@vitest/coverage-v8": "^1.5.0",
27
+ "tsup": "^8.0.2",
24
28
  "tsx": "^4.1.1",
25
- "typescript": "^5.3.3",
26
- "vitest": "^1.1.0"
29
+ "typescript": "^5.4.5",
30
+ "vitest": "^1.5.0"
27
31
  },
28
32
  "dependencies": {
29
- "@parcel/watcher": "^2.3.0",
33
+ "@parcel/watcher": "^2.4.1",
30
34
  "camelcase": "^8.0.0",
31
- "esbuild": "^0.19.5",
35
+ "esbuild": "^0.21.4",
32
36
  "fast-glob": "^3.3.2",
33
37
  "gray-matter": "^4.0.3",
34
38
  "micromatch": "^4.0.5",
35
39
  "pluralize": "^8.0.0",
36
- "yaml": "^2.3.4",
37
- "zod": "^3.22.4"
40
+ "serialize-javascript": "^6.0.2",
41
+ "yaml": "^2.4.1",
42
+ "zod": "^3.22.5"
38
43
  },
39
44
  "scripts": {
40
45
  "build": "tsup src/index.ts --format esm --dts -d dist",