@content-collections/core 0.9.1 → 0.11.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/dist/index.d.ts CHANGED
@@ -60,7 +60,7 @@ declare const literalSchema: z__default.ZodUnion<[z__default.ZodString, z__defau
60
60
  type Literal = z__default.infer<typeof literalSchema>;
61
61
  type SchemaType = Literal | {
62
62
  [key: string]: SchemaType;
63
- } | SchemaType[];
63
+ } | ReadonlyArray<SchemaType>;
64
64
  type NotSerializableError = `The return type of the transform function must be an object serializable object.
65
65
  See https://www.content-collections.dev/docs/serialization for more information.
66
66
 
@@ -95,6 +95,11 @@ type Prettify<T> = {
95
95
  [K in keyof T]: T[K];
96
96
  } & {};
97
97
  type GetSchema<TCollection extends AnyCollection> = TCollection extends Collection<any, any, any, infer TSchema, any, any> ? Prettify<TSchema> : never;
98
+ declare const skippedSymbol: unique symbol;
99
+ type SkippedSignal = {
100
+ [skippedSymbol]: true;
101
+ reason?: string;
102
+ };
98
103
  type Context<TSchema = unknown> = {
99
104
  documents<TCollection extends AnyCollection>(collection: TCollection): Array<GetSchema<TCollection>>;
100
105
  cache: CacheFn;
@@ -103,6 +108,7 @@ type Context<TSchema = unknown> = {
103
108
  directory: string;
104
109
  documents: () => Promise<Array<TSchema>>;
105
110
  };
111
+ skip: (reason?: string) => SkippedSignal;
106
112
  };
107
113
  type Z = typeof z$1;
108
114
  type CollectionRequest<TName extends string, TShape extends TSchemaProp, TParser, TSchema, TTransformResult, TDocument> = {
@@ -130,7 +136,7 @@ type InvalidReturnType<TMessage extends string, TObject> = {
130
136
  type ResolveImports<TTransformResult> = TTransformResult extends Import<any> ? GetTypeOfImport<TTransformResult> : TTransformResult extends Array<infer U> ? Array<ResolveImports<U>> : TTransformResult extends (...args: any[]) => any ? TTransformResult : TTransformResult extends object ? {
131
137
  [K in keyof TTransformResult]: ResolveImports<TTransformResult[K]>;
132
138
  } : TTransformResult;
133
- declare function defineCollection<TName extends string, TShape extends TSchemaProp, TParser extends ConfiguredParser = "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, ResolveImports<TDocument>> : InvalidReturnType<NotSerializableError, TDocument>>(collection: CollectionRequest<TName, TShape, TParser, TSchema, TTransformResult, TDocument>): TResult;
139
+ declare function defineCollection<TName extends string, TShape extends TSchemaProp, TParser extends ConfiguredParser = "frontmatter", TSchema = Schema<TParser, TShape>, TTransformResult = never, TDocument = [TTransformResult] extends [never] ? Schema<TParser, TShape> : Exclude<Awaited<TTransformResult>, SkippedSignal>, TResult = TDocument extends Serializable ? Collection<TName, TShape, TParser, TSchema, TTransformResult, ResolveImports<TDocument>> : InvalidReturnType<NotSerializableError, TDocument>>(collection: CollectionRequest<TName, TShape, TParser, TSchema, TTransformResult, TDocument>): TResult;
134
140
  type Cache = "memory" | "file" | "none";
135
141
  type Configuration<TCollections extends Array<AnyCollection>> = {
136
142
  collections: TCollections;
@@ -187,6 +193,11 @@ type TransformerEvents = {
187
193
  collection: AnyCollection;
188
194
  error: TransformError;
189
195
  };
196
+ "transformer:document-skipped": {
197
+ collection: AnyCollection;
198
+ filePath: string;
199
+ reason?: string;
200
+ };
190
201
  };
191
202
  type ErrorType$1 = "Validation" | "Configuration" | "Transform" | "Result";
192
203
  declare class TransformError extends Error {
@@ -299,7 +310,7 @@ declare function createBuilder(configurationPath: string, options?: Options, emi
299
310
  unsubscribe: () => Promise<void>;
300
311
  }>;
301
312
  on: {
302
- <TKey extends "builder:start" | "builder:end" | "builder:created" | "watcher:file-changed" | "watcher:config-changed" | "watcher:config-reload-error" | "collector:read-error" | "collector:parse-error" | "transformer:validation-error" | "transformer:result-error" | "transformer:error" | "watcher:subscribe-error" | "watcher:subscribed" | "watcher:unsubscribed">(key: TKey, listener: (event: Events[TKey]) => void): void;
313
+ <TKey extends "builder:start" | "builder:end" | "builder:created" | "watcher:file-changed" | "watcher:config-changed" | "watcher:config-reload-error" | "collector:read-error" | "collector:parse-error" | "transformer:validation-error" | "transformer:result-error" | "transformer:error" | "transformer:document-skipped" | "watcher:subscribe-error" | "watcher:subscribed" | "watcher:unsubscribed">(key: TKey, listener: (event: Events[TKey]) => void): void;
303
314
  <TKey extends "_error" | "_all">(key: TKey, listener: (event: SystemEvents[TKey]) => void): void;
304
315
  };
305
316
  }>;
@@ -311,4 +322,4 @@ declare const deprecations: {
311
322
  type Deprecation = keyof typeof deprecations;
312
323
  declare function suppressDeprecatedWarnings(...deprecations: Array<Deprecation | "all">): void;
313
324
 
314
- export { type AnyCollection, type AnyConfiguration, type Builder, type BuilderEvents, CollectError, type Collection, type CollectionRequest, type Configuration, ConfigurationError, ConfigurationReloadError, type Context, type Document, type GetTypeByName, type Meta, type Modification, type Schema, TransformError, type Watcher, createBuilder, createDefaultImport, createNamedImport, defineCollection, defineConfig, defineParser, suppressDeprecatedWarnings };
325
+ export { type AnyCollection, type AnyConfiguration, type Builder, type BuilderEvents, CollectError, type Collection, type CollectionRequest, type Configuration, ConfigurationError, ConfigurationReloadError, type Context, type Document, type GetTypeByName, type Meta, type Modification, type Schema, type SkippedSignal, TransformError, type Watcher, createBuilder, createDefaultImport, createNamedImport, defineCollection, defineConfig, defineParser, skippedSymbol, suppressDeprecatedWarnings };
package/dist/index.js CHANGED
@@ -115,7 +115,7 @@ import { existsSync as existsSync2 } from "node:fs";
115
115
  import fs2 from "node:fs/promises";
116
116
  import path3 from "node:path";
117
117
 
118
- // ../../node_modules/.pnpm/bundle-require@5.0.0_esbuild@0.25.0/node_modules/bundle-require/dist/index.js
118
+ // ../../node_modules/.pnpm/bundle-require@5.0.0_esbuild@0.25.6/node_modules/bundle-require/dist/index.js
119
119
  import {
120
120
  build,
121
121
  context
@@ -292,7 +292,7 @@ var loadTsConfigInternal = (dir = process.cwd(), name = "tsconfig.json", isExten
292
292
  };
293
293
  var loadTsConfig = (dir, name) => loadTsConfigInternal(dir, name);
294
294
 
295
- // ../../node_modules/.pnpm/bundle-require@5.0.0_esbuild@0.25.0/node_modules/bundle-require/dist/index.js
295
+ // ../../node_modules/.pnpm/bundle-require@5.0.0_esbuild@0.25.6/node_modules/bundle-require/dist/index.js
296
296
  var tsconfigPathsToRegExp = (paths) => {
297
297
  return Object.keys(paths || {}).map((key) => {
298
298
  return new RegExp(`^${key.replace(/\*/, ".*")}$`);
@@ -519,6 +519,9 @@ function posixToNativePath(pathName) {
519
519
  }
520
520
  return pathName;
521
521
  }
522
+ function toError(error) {
523
+ return error instanceof Error ? error : new Error(String(error));
524
+ }
522
525
 
523
526
  // src/collector.ts
524
527
  var CollectError = class extends Error {
@@ -636,6 +639,8 @@ function createSynchronizer(readCollectionFile, collections, baseDirectory = "."
636
639
  };
637
640
  }).filter(({ collection, relativePath }) => {
638
641
  return picomatch.isMatch(relativePath, collection.include, {
642
+ // @see https://github.com/sdorra/content-collections/issues/602
643
+ windows: process.platform === "win32",
639
644
  ignore: collection.exclude
640
645
  });
641
646
  });
@@ -688,12 +693,70 @@ function createSynchronizer(readCollectionFile, collections, baseDirectory = "."
688
693
 
689
694
  // src/transformer.ts
690
695
  import os from "node:os";
691
- import { basename, dirname as dirname2, extname } from "node:path";
696
+ import { basename, dirname as dirname2, extname, join as join3 } from "node:path";
692
697
  import pLimit from "p-limit";
693
698
 
699
+ // src/config.ts
700
+ import { z } from "zod";
701
+
702
+ // src/warn.ts
703
+ var deprecations = {
704
+ legacySchema: `The use of a function as a schema is deprecated.
705
+ Please use a StandardSchema compliant library directly.
706
+ For more information, see:
707
+ https://content-collections.dev/docs/deprecations/schema-as-function`
708
+ };
709
+ var _suppressDeprecatedWarnings = [];
710
+ function suppressDeprecatedWarnings(...deprecations2) {
711
+ for (const deprecation of deprecations2) {
712
+ if (deprecation === "all") {
713
+ _suppressDeprecatedWarnings.push(
714
+ ...Object.keys(deprecations2)
715
+ );
716
+ return;
717
+ } else {
718
+ _suppressDeprecatedWarnings.push(deprecation);
719
+ }
720
+ }
721
+ }
722
+ function warnDeprecated(deprecation, logger = console.warn) {
723
+ if (_suppressDeprecatedWarnings.includes(deprecation)) {
724
+ return;
725
+ }
726
+ logger(`[CC DEPRECATED]: ${deprecations[deprecation]}`);
727
+ }
728
+
729
+ // src/config.ts
730
+ var skippedSymbol = Symbol("skipped");
731
+ var InvalidReturnTypeSymbol = Symbol(`InvalidReturnType`);
732
+ function defineCollection(collection) {
733
+ let typeName = collection.typeName;
734
+ if (!typeName) {
735
+ typeName = generateTypeName(collection.name);
736
+ }
737
+ let parser = collection.parser;
738
+ if (!parser) {
739
+ parser = "frontmatter";
740
+ }
741
+ let schema2 = collection.schema;
742
+ if (!schema2["~standard"]) {
743
+ warnDeprecated("legacySchema");
744
+ schema2 = z.object(schema2(z));
745
+ }
746
+ return {
747
+ ...collection,
748
+ typeName,
749
+ parser,
750
+ schema: schema2
751
+ };
752
+ }
753
+ function defineConfig(config) {
754
+ return config;
755
+ }
756
+
694
757
  // src/serializer.ts
695
758
  import serializeJs from "serialize-javascript";
696
- import z from "zod";
759
+ import z2 from "zod";
697
760
 
698
761
  // src/import.ts
699
762
  var importSymbol = Symbol("import");
@@ -715,24 +778,24 @@ function createNamedImport(name, path10) {
715
778
  }
716
779
 
717
780
  // src/serializer.ts
718
- var literalSchema = z.union([
781
+ var literalSchema = z2.union([
719
782
  // json
720
- z.string(),
721
- z.number(),
722
- z.boolean(),
723
- z.null(),
783
+ z2.string(),
784
+ z2.number(),
785
+ z2.boolean(),
786
+ z2.null(),
724
787
  // serializable-javascript
725
- z.undefined(),
726
- z.date(),
727
- z.map(z.unknown(), z.unknown()),
728
- z.set(z.unknown()),
729
- z.bigint()
788
+ z2.undefined(),
789
+ z2.date(),
790
+ z2.map(z2.unknown(), z2.unknown()),
791
+ z2.set(z2.unknown()),
792
+ z2.bigint()
730
793
  ]);
731
- var schema = z.lazy(
732
- () => z.union([literalSchema, z.array(schema), z.record(schema)])
794
+ var schema = z2.lazy(
795
+ () => z2.union([literalSchema, z2.array(schema), z2.record(schema)])
733
796
  );
734
797
  var extension = "js";
735
- var serializableSchema = z.record(schema);
798
+ var serializableSchema = z2.record(schema);
736
799
  function createImport(imp, variableName) {
737
800
  const variableDeclaration = imp.name ? `{ ${imp.name} as ${variableName} }` : variableName;
738
801
  return `import ${variableDeclaration} from "${imp.path}";
@@ -776,6 +839,9 @@ var TransformError = class extends Error {
776
839
  this.type = type;
777
840
  }
778
841
  };
842
+ function isSkippedSignal(signal) {
843
+ return signal[skippedSymbol] === true;
844
+ }
779
845
  function createPath(path10, ext) {
780
846
  let p = path10.slice(0, -ext.length);
781
847
  if (p.endsWith("/index")) {
@@ -867,7 +933,11 @@ function createTransformer(emitter, cacheManager) {
867
933
  return collection.documents.map((doc) => doc.document);
868
934
  }
869
935
  },
870
- cache: cache.cacheFn
936
+ cache: cache.cacheFn,
937
+ skip: (reason) => ({
938
+ [skippedSymbol]: true,
939
+ reason
940
+ })
871
941
  };
872
942
  }
873
943
  async function transformDocument(collections, collection, transform, doc) {
@@ -876,10 +946,18 @@ function createTransformer(emitter, cacheManager) {
876
946
  try {
877
947
  const document = await transform(doc.document, context2);
878
948
  await cache.tidyUp();
879
- return {
880
- ...doc,
881
- document
882
- };
949
+ if (isSkippedSignal(document)) {
950
+ emitter.emit("transformer:document-skipped", {
951
+ collection,
952
+ filePath: join3(collection.directory, doc.document._meta.filePath),
953
+ reason: document.reason
954
+ });
955
+ } else {
956
+ return {
957
+ ...doc,
958
+ document
959
+ };
960
+ }
883
961
  } catch (error) {
884
962
  if (error instanceof TransformError) {
885
963
  emitter.emit("transformer:error", {
@@ -1113,41 +1191,58 @@ function createEmitter() {
1113
1191
  }
1114
1192
 
1115
1193
  // src/watcher.ts
1116
- import * as watcher from "@parcel/watcher";
1194
+ import chokidar from "chokidar";
1117
1195
  import path8, { dirname as dirname3, resolve } from "node:path";
1118
1196
  async function createWatcher(emitter, baseDirectory, configuration, sync) {
1119
- const onChange = async (error, events) => {
1120
- if (error) {
1197
+ const paths = removeChildPaths([
1198
+ ...configuration.collections.map((collection) => path8.join(baseDirectory, collection.directory)).map((p) => resolve(p)),
1199
+ ...configuration.inputPaths.map((p) => dirname3(p))
1200
+ ]);
1201
+ const watcher = chokidar.watch(paths, {
1202
+ ignored: [
1203
+ /(^|[\/\\])\../,
1204
+ // ignore dotfiles
1205
+ /(^|[\/\\])node_modules([\/\\]|$)/
1206
+ // ignore node_modules
1207
+ ],
1208
+ persistent: true,
1209
+ ignoreInitial: true
1210
+ // ignore initial add events
1211
+ });
1212
+ const handleEvent = async (modification, filePath) => {
1213
+ try {
1214
+ await sync(modification, filePath);
1215
+ } catch (error) {
1121
1216
  emitter.emit("watcher:subscribe-error", {
1122
1217
  paths,
1123
- error
1218
+ error: toError(error)
1124
1219
  });
1125
- return;
1126
- }
1127
- for (const event of events) {
1128
- await sync(event.type, event.path);
1129
1220
  }
1130
1221
  };
1131
- const paths = removeChildPaths([
1132
- ...configuration.collections.map((collection) => path8.join(baseDirectory, collection.directory)).map((p) => resolve(p)),
1133
- ...configuration.inputPaths.map((p) => dirname3(p))
1134
- ]);
1135
- const subscriptions = (await Promise.all(paths.map((path10) => watcher.subscribe(path10, onChange)))).filter(isDefined);
1136
- emitter.emit("watcher:subscribed", {
1137
- paths
1222
+ watcher.on("add", (filePath) => handleEvent("create", filePath));
1223
+ watcher.on("change", (filePath) => handleEvent("update", filePath));
1224
+ watcher.on("unlink", (filePath) => handleEvent("delete", filePath));
1225
+ watcher.on("error", (error) => {
1226
+ emitter.emit("watcher:subscribe-error", {
1227
+ paths,
1228
+ error: toError(error)
1229
+ });
1230
+ });
1231
+ await new Promise((resolve2, reject) => {
1232
+ watcher.on("ready", () => {
1233
+ emitter.emit("watcher:subscribed", {
1234
+ paths
1235
+ });
1236
+ resolve2();
1237
+ });
1238
+ watcher.on("error", reject);
1138
1239
  });
1139
1240
  return {
1140
1241
  unsubscribe: async () => {
1141
- if (!subscriptions || subscriptions.length === 0) {
1142
- return;
1143
- }
1144
- await Promise.all(
1145
- subscriptions.map((subscription) => subscription.unsubscribe())
1146
- );
1242
+ await watcher.close();
1147
1243
  emitter.emit("watcher:unsubscribed", {
1148
1244
  paths
1149
1245
  });
1150
- return;
1151
1246
  }
1152
1247
  };
1153
1248
  }
@@ -1176,7 +1271,7 @@ async function createBuilder(configurationPath, options = {
1176
1271
  outputDirectory
1177
1272
  });
1178
1273
  let configuration = await readConfiguration(configurationPath, options);
1179
- let watcher2 = null;
1274
+ let watcher = null;
1180
1275
  let context2 = await createBuildContext({
1181
1276
  emitter,
1182
1277
  baseDirectory,
@@ -1217,8 +1312,8 @@ async function createBuilder(configurationPath, options = {
1217
1312
  });
1218
1313
  return false;
1219
1314
  }
1220
- if (watcher2) {
1221
- await watcher2.unsubscribe();
1315
+ if (watcher) {
1316
+ await watcher.unsubscribe();
1222
1317
  }
1223
1318
  context2 = await createBuildContext({
1224
1319
  emitter,
@@ -1226,8 +1321,8 @@ async function createBuilder(configurationPath, options = {
1226
1321
  outputDirectory,
1227
1322
  configuration
1228
1323
  });
1229
- if (watcher2) {
1230
- watcher2 = await createWatcher(
1324
+ if (watcher) {
1325
+ watcher = await createWatcher(
1231
1326
  emitter,
1232
1327
  baseDirectory,
1233
1328
  configuration,
@@ -1245,11 +1340,11 @@ async function createBuilder(configurationPath, options = {
1245
1340
  }
1246
1341
  }
1247
1342
  async function watch() {
1248
- watcher2 = await createWatcher(emitter, baseDirectory, configuration, sync);
1343
+ watcher = await createWatcher(emitter, baseDirectory, configuration, sync);
1249
1344
  return {
1250
1345
  unsubscribe: async () => {
1251
- if (watcher2) {
1252
- await watcher2.unsubscribe();
1346
+ if (watcher) {
1347
+ await watcher.unsubscribe();
1253
1348
  }
1254
1349
  }
1255
1350
  };
@@ -1261,63 +1356,6 @@ async function createBuilder(configurationPath, options = {
1261
1356
  on: emitter.on
1262
1357
  };
1263
1358
  }
1264
-
1265
- // src/config.ts
1266
- import { z as z2 } from "zod";
1267
-
1268
- // src/warn.ts
1269
- var deprecations = {
1270
- legacySchema: `The use of a function as a schema is deprecated.
1271
- Please use a StandardSchema compliant library directly.
1272
- For more information, see:
1273
- https://content-collections.dev/docs/deprecations/schema-as-function`
1274
- };
1275
- var _suppressDeprecatedWarnings = [];
1276
- function suppressDeprecatedWarnings(...deprecations2) {
1277
- for (const deprecation of deprecations2) {
1278
- if (deprecation === "all") {
1279
- _suppressDeprecatedWarnings.push(
1280
- ...Object.keys(deprecations2)
1281
- );
1282
- return;
1283
- } else {
1284
- _suppressDeprecatedWarnings.push(deprecation);
1285
- }
1286
- }
1287
- }
1288
- function warnDeprecated(deprecation, logger = console.warn) {
1289
- if (_suppressDeprecatedWarnings.includes(deprecation)) {
1290
- return;
1291
- }
1292
- logger(`[CC DEPRECATED]: ${deprecations[deprecation]}`);
1293
- }
1294
-
1295
- // src/config.ts
1296
- var InvalidReturnTypeSymbol = Symbol(`InvalidReturnType`);
1297
- function defineCollection(collection) {
1298
- let typeName = collection.typeName;
1299
- if (!typeName) {
1300
- typeName = generateTypeName(collection.name);
1301
- }
1302
- let parser = collection.parser;
1303
- if (!parser) {
1304
- parser = "frontmatter";
1305
- }
1306
- let schema2 = collection.schema;
1307
- if (!schema2["~standard"]) {
1308
- warnDeprecated("legacySchema");
1309
- schema2 = z2.object(schema2(z2));
1310
- }
1311
- return {
1312
- ...collection,
1313
- typeName,
1314
- parser,
1315
- schema: schema2
1316
- };
1317
- }
1318
- function defineConfig(config) {
1319
- return config;
1320
- }
1321
1359
  export {
1322
1360
  CollectError,
1323
1361
  ConfigurationError,
@@ -1329,5 +1367,6 @@ export {
1329
1367
  defineCollection,
1330
1368
  defineConfig,
1331
1369
  defineParser,
1370
+ skippedSymbol,
1332
1371
  suppressDeprecatedWarnings
1333
1372
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@content-collections/core",
3
3
  "type": "module",
4
- "version": "0.9.1",
4
+ "version": "0.11.0",
5
5
  "description": "Core of Content Collections",
6
6
  "author": "Sebastian Sdorra <s.sdorra@gmail.com>",
7
7
  "license": "MIT",
@@ -33,17 +33,17 @@
33
33
  "@types/picomatch": "^3.0.1",
34
34
  "@types/pluralize": "^0.0.33",
35
35
  "@types/serialize-javascript": "^5.0.4",
36
- "@vitest/coverage-v8": "^3.1.3",
36
+ "@vitest/coverage-v8": "^3.2.4",
37
37
  "bundle-require": "^5.0.0",
38
38
  "tsup": "^8.2.4",
39
39
  "tsx": "^4.19.2",
40
40
  "typescript": "^5.5.4",
41
- "vitest": "^3.1.3"
41
+ "vitest": "^3.2.4"
42
42
  },
43
43
  "dependencies": {
44
- "@parcel/watcher": "^2.4.1",
45
44
  "@standard-schema/spec": "^1.0.0",
46
45
  "camelcase": "^8.0.0",
46
+ "chokidar": "^4.0.3",
47
47
  "esbuild": "^0.25.0",
48
48
  "gray-matter": "^4.0.3",
49
49
  "p-limit": "^6.1.0",
@@ -57,6 +57,6 @@
57
57
  "scripts": {
58
58
  "build": "tsup src/index.ts --format esm --dts -d dist",
59
59
  "typecheck": "tsc",
60
- "test": "vitest --run --coverage"
60
+ "test": "vitest --run --coverage --typecheck"
61
61
  }
62
62
  }