@content-collections/core 0.13.1 → 0.14.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
@@ -52,7 +52,7 @@ If your framework is not listed, you can still use Content Collections by using
52
52
  });
53
53
 
54
54
  export default defineConfig({
55
- collections: [posts],
55
+ content: [posts],
56
56
  });
57
57
  ```
58
58
 
package/dist/index.d.ts CHANGED
@@ -1,5 +1,45 @@
1
1
  import { StandardSchemaV1 } from "@standard-schema/spec";
2
2
 
3
+ //#region src/parser.d.ts
4
+ type ParseFn = (content: string) => Record<string, unknown> | Promise<Record<string, unknown>>;
5
+ type Parser = {
6
+ hasContent: boolean;
7
+ parse: ParseFn;
8
+ };
9
+ type PredefinedParsers = typeof parsers;
10
+ type PredefinedParser = keyof typeof parsers;
11
+ type ConfiguredParser = PredefinedParser | Parser;
12
+ declare function parseYaml(content: string): any;
13
+ declare function frontmatterParser(fileContent: string): {
14
+ content: string;
15
+ };
16
+ declare function frontmatterOnlyParser(fileContent: string): {
17
+ [key: string]: any;
18
+ };
19
+ declare const parsers: {
20
+ frontmatter: {
21
+ hasContent: true;
22
+ parse: typeof frontmatterParser;
23
+ };
24
+ "frontmatter-only": {
25
+ hasContent: false;
26
+ parse: typeof frontmatterOnlyParser;
27
+ };
28
+ json: {
29
+ hasContent: false;
30
+ parse: (text: string, reviver?: (this: any, key: string, value: any) => any) => any;
31
+ };
32
+ yaml: {
33
+ hasContent: false;
34
+ parse: typeof parseYaml;
35
+ };
36
+ };
37
+ type DefineParserResult<TArgument extends Parser | ParseFn> = TArgument extends Function ? {
38
+ hasContent: false;
39
+ parse: ParseFn;
40
+ } : TArgument extends infer Parser ? Parser : never;
41
+ declare function defineParser<TArgument extends Parser | ParseFn>(parser: TArgument): DefineParserResult<TArgument>;
42
+ //#endregion
3
43
  //#region src/cache.d.ts
4
44
  type Options$2 = {
5
45
  key: string;
@@ -673,46 +713,6 @@ type GetTypeOfImport<T$1> = T$1 extends Import<infer U> ? U : never;
673
713
  declare function createDefaultImport<T$1>(path: string): Import<T$1>;
674
714
  declare function createNamedImport<T$1>(name: string, path: string): Import<T$1>;
675
715
  //#endregion
676
- //#region src/parser.d.ts
677
- type ParseFn = (content: string) => Record<string, unknown> | Promise<Record<string, unknown>>;
678
- type Parser = {
679
- hasContent: boolean;
680
- parse: ParseFn;
681
- };
682
- type PredefinedParsers = typeof parsers;
683
- type PredefinedParser = keyof typeof parsers;
684
- type ConfiguredParser = PredefinedParser | Parser;
685
- declare function parseYaml(content: string): any;
686
- declare function frontmatterParser(fileContent: string): {
687
- content: string;
688
- };
689
- declare function frontmatterOnlyParser(fileContent: string): {
690
- [key: string]: any;
691
- };
692
- declare const parsers: {
693
- frontmatter: {
694
- hasContent: true;
695
- parse: typeof frontmatterParser;
696
- };
697
- "frontmatter-only": {
698
- hasContent: false;
699
- parse: typeof frontmatterOnlyParser;
700
- };
701
- json: {
702
- hasContent: false;
703
- parse: (text: string, reviver?: (this: any, key: string, value: any) => any) => any;
704
- };
705
- yaml: {
706
- hasContent: false;
707
- parse: typeof parseYaml;
708
- };
709
- };
710
- type DefineParserResult<TArgument extends Parser | ParseFn> = TArgument extends Function ? {
711
- hasContent: false;
712
- parse: ParseFn;
713
- } : TArgument extends infer Parser ? Parser : never;
714
- declare function defineParser<TArgument extends Parser | ParseFn>(parser: TArgument): DefineParserResult<TArgument>;
715
- //#endregion
716
716
  //#region src/serializer.d.ts
717
717
  declare const literalSchema: ZodMiniUnion<readonly [ZodMiniString<string>, ZodMiniNumber<number>, ZodMiniBoolean<boolean>, ZodMiniNull, ZodMiniUndefined, ZodMiniDate<Date>, ZodMiniMap<ZodMiniUnknown, ZodMiniUnknown>, ZodMiniSet<ZodMiniUnknown>, ZodMiniBigInt<bigint>, ZodMiniObject<{
718
718
  __cc_import: ZodMiniLiteral<true>;
@@ -753,15 +753,18 @@ type Schema<TParser extends ConfiguredParser, TShape extends TSchemaProp> = GetO
753
753
  _meta: Meta;
754
754
  };
755
755
  type Prettify<T$1> = { [K in keyof T$1]: T$1[K] } & {};
756
- type GetSchema<TCollection$1 extends AnyCollection> = TCollection$1 extends Collection<any, any, any, infer TSchema, any, any> ? Prettify<TSchema> : never;
756
+ type GetSchema<TSource extends AnyContent> = TSource extends Collection<any, any, any, infer TSchema, any, any> ? Prettify<TSchema> : TSource extends Singleton<any, any, any, infer TSchema, any, any> ? Prettify<TSchema> : never;
757
757
  declare const skippedSymbol: unique symbol;
758
758
  type SkippedSignal = {
759
759
  [skippedSymbol]: true;
760
760
  reason?: string;
761
761
  };
762
762
  type Context<TSchema$1 = unknown> = {
763
- documents<TCollection$1 extends AnyCollection>(collection: TCollection$1): Array<GetSchema<TCollection$1>>;
763
+ documents<TSource extends AnyContent>(source: TSource): Array<GetSchema<TSource>>;
764
764
  cache: CacheFn;
765
+ /**
766
+ * @deprecated Use `CollectionContext["collection"]` in collection transforms.
767
+ */
765
768
  collection: {
766
769
  name: string;
767
770
  directory: string;
@@ -769,23 +772,59 @@ type Context<TSchema$1 = unknown> = {
769
772
  };
770
773
  skip: (reason?: string) => SkippedSignal;
771
774
  };
775
+ type CollectionContext<TSchema$1 = unknown> = Context<TSchema$1> & {
776
+ collection: {
777
+ name: string;
778
+ directory: string;
779
+ documents: () => Promise<Array<TSchema$1>>;
780
+ };
781
+ };
782
+ type SingletonContext<TSchema$1 = unknown> = Context<TSchema$1> & {
783
+ singleton: {
784
+ name: string;
785
+ filePath: string;
786
+ directory: string;
787
+ document: () => Promise<TSchema$1 | undefined>;
788
+ };
789
+ };
790
+ type ContentType = "collection" | "singleton";
772
791
  type CollectionRequest<TName extends string, TShape extends TSchemaProp, TParser, TSchema$1, TTransformResult, TDocument$1> = {
773
792
  name: TName;
774
793
  parser?: TParser;
775
794
  typeName?: string;
776
795
  schema: TShape;
777
- transform?: (data: TSchema$1, context: Context<TSchema$1>) => TTransformResult;
796
+ transform?: (data: TSchema$1, context: CollectionContext<TSchema$1>) => TTransformResult;
778
797
  directory: string;
779
798
  include: string | string[];
780
799
  exclude?: string | string[];
781
800
  onSuccess?: (documents: Array<TDocument$1>) => void | Promise<void>;
782
801
  };
783
802
  type Collection<TName extends string, TShape extends TSchemaProp, TParser extends ConfiguredParser, TSchema$1, TTransformResult, TDocument$1> = Omit<CollectionRequest<TName, TShape, TParser, TSchema$1, TTransformResult, TDocument$1>, "schema"> & {
803
+ type: "collection";
784
804
  typeName: string;
785
805
  schema: StandardSchemaV1;
786
806
  parser: TParser;
787
807
  };
788
808
  type AnyCollection = Collection<any, TSchemaProp, ConfiguredParser, any, any, any>;
809
+ type SingletonRequest<TName extends string, TShape extends TSchemaProp, TParser, TSchema$1, TTransformResult, TDocument$1> = {
810
+ name: TName;
811
+ filePath: string;
812
+ parser?: TParser;
813
+ typeName?: string;
814
+ schema: TShape;
815
+ optional?: boolean;
816
+ transform?: (data: TSchema$1, context: SingletonContext<TSchema$1>) => TTransformResult;
817
+ onSuccess?: (document: TDocument$1 | undefined) => void | Promise<void>;
818
+ };
819
+ type Singleton<TName extends string, TShape extends TSchemaProp, TParser extends ConfiguredParser, TSchema$1, TTransformResult, TDocument$1> = Omit<SingletonRequest<TName, TShape, TParser, TSchema$1, TTransformResult, TDocument$1>, "schema"> & {
820
+ type: "singleton";
821
+ typeName: string;
822
+ schema: StandardSchemaV1;
823
+ parser: TParser;
824
+ };
825
+ type AnySingleton = Singleton<any, TSchemaProp, ConfiguredParser, any, any, any>;
826
+ type AnyContent = AnyCollection | AnySingleton;
827
+ declare function isSingleton(source: AnyContent): source is AnySingleton;
789
828
  declare const InvalidReturnTypeSymbol: unique symbol;
790
829
  type InvalidReturnType<TMessage extends string, TObject> = {
791
830
  [InvalidReturnTypeSymbol]: TMessage;
@@ -793,13 +832,37 @@ type InvalidReturnType<TMessage extends string, TObject> = {
793
832
  };
794
833
  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 ? { [K in keyof TTransformResult]: ResolveImports<TTransformResult[K]> } : TTransformResult;
795
834
  declare function defineCollection<TName extends string, TShape extends TSchemaProp, TParser extends ConfiguredParser = "frontmatter", TSchema$1 = Schema<TParser, TShape>, TTransformResult = never, TDocument$1 = ([TTransformResult] extends [never] ? Schema<TParser, TShape> : Exclude<Awaited<TTransformResult>, SkippedSignal>), TResult = (TDocument$1 extends Serializable ? Collection<TName, TShape, TParser, TSchema$1, TTransformResult, ResolveImports<TDocument$1>> : InvalidReturnType<NotSerializableError, TDocument$1>)>(collection: CollectionRequest<TName, TShape, TParser, TSchema$1, TTransformResult, TDocument$1>): TResult;
835
+ declare function defineSingleton<TName extends string, TShape extends TSchemaProp, TParser extends ConfiguredParser = "frontmatter", TSchema$1 = Schema<TParser, TShape>, TTransformResult = never, TDocument$1 = ([TTransformResult] extends [never] ? Schema<TParser, TShape> : Exclude<Awaited<TTransformResult>, SkippedSignal>), TResult = (TDocument$1 extends Serializable ? Singleton<TName, TShape, TParser, TSchema$1, TTransformResult, ResolveImports<TDocument$1>> : InvalidReturnType<NotSerializableError, TDocument$1>)>(singleton: SingletonRequest<TName, TShape, TParser, TSchema$1, TTransformResult, TDocument$1>): TResult;
796
836
  type Cache = "memory" | "file" | "none";
797
- type Configuration<TCollections extends Array<AnyCollection>> = {
837
+ type ConfigurationWithContent<TCollections extends Array<AnyContent>> = {
838
+ content: TCollections;
839
+ /**
840
+ * @deprecated Use `content` instead.
841
+ */
842
+ collections?: TCollections;
843
+ cache?: Cache;
844
+ };
845
+ type ConfigurationWithCollections<TCollections extends Array<AnyContent>> = {
846
+ /**
847
+ * @deprecated Use `content` instead.
848
+ */
798
849
  collections: TCollections;
850
+ content?: TCollections;
799
851
  cache?: Cache;
800
852
  };
801
- type AnyConfiguration = Configuration<Array<AnyCollection>>;
802
- declare function defineConfig<TConfig extends AnyConfiguration>(config: TConfig): TConfig;
853
+ type Configuration<TCollections extends Array<AnyContent>> = ConfigurationWithContent<TCollections> | ConfigurationWithCollections<TCollections>;
854
+ type AnyConfiguration = Configuration<Array<AnyContent>>;
855
+ declare function defineConfig<TCollections extends Array<AnyContent>>(config: {
856
+ content: TCollections;
857
+ cache?: Cache;
858
+ }): ConfigurationWithContent<TCollections>;
859
+ declare function defineConfig<TCollections extends Array<AnyContent>>(config: {
860
+ /**
861
+ * @deprecated Use `content` instead.
862
+ */
863
+ collections: TCollections;
864
+ cache?: Cache;
865
+ }): ConfigurationWithCollections<TCollections>;
803
866
  //#endregion
804
867
  //#region src/types.d.ts
805
868
  type Modification = "create" | "update" | "delete";
@@ -813,9 +876,14 @@ type CollectionFile = {
813
876
  };
814
877
  path: string;
815
878
  };
816
- type CollectionByName<TConfiguration extends AnyConfiguration> = { [TCollection in TConfiguration["collections"][number] as TCollection["name"]]: TCollection };
817
- type GetDocument<TCollection$1 extends AnyCollection> = TCollection$1 extends Collection<any, any, any, any, any, infer TDocument> ? TDocument : never;
818
- type GetTypeByName<TConfiguration extends AnyConfiguration, TName extends keyof CollectionByName<TConfiguration>, TCollection$1 = CollectionByName<TConfiguration>[TName]> = TCollection$1 extends AnyCollection ? GetDocument<TCollection$1> : never;
879
+ type ConfigurationSources<TConfiguration extends AnyConfiguration> = TConfiguration extends {
880
+ content: infer TSources extends Array<AnyContent>;
881
+ } ? TSources : TConfiguration extends {
882
+ collections: infer TSources extends Array<AnyContent>;
883
+ } ? TSources : never;
884
+ type CollectionByName<TConfiguration extends AnyConfiguration> = { [TCollection in ConfigurationSources<TConfiguration>[number] as TCollection["name"]]: TCollection };
885
+ type GetDocument<TSource extends AnyContent> = TSource extends Collection<any, any, any, any, any, infer TDocument> ? TDocument : TSource extends Singleton<any, any, any, any, any, infer TDocument> ? TDocument : never;
886
+ type GetTypeByName<TConfiguration extends AnyConfiguration, TName extends keyof CollectionByName<TConfiguration>, TCollection$1 = CollectionByName<TConfiguration>[TName]> = TCollection$1 extends AnyContent ? GetDocument<TCollection$1> : never;
819
887
  //#endregion
820
888
  //#region src/collector.d.ts
821
889
  type CollectorEvents = {
@@ -837,21 +905,24 @@ declare class CollectError extends Error {
837
905
  //#region src/transformer.d.ts
838
906
  type TransformerEvents = {
839
907
  "transformer:validation-error": {
840
- collection: AnyCollection;
908
+ collection: AnyContent;
841
909
  file: CollectionFile;
842
910
  error: TransformError;
843
911
  };
912
+ "transformer:singleton-warning": {
913
+ collection: AnyContent;
914
+ };
844
915
  "transformer:result-error": {
845
- collection: AnyCollection;
916
+ collection: AnyContent;
846
917
  document: any;
847
918
  error: TransformError;
848
919
  };
849
920
  "transformer:error": {
850
- collection: AnyCollection;
921
+ collection: AnyContent;
851
922
  error: TransformError;
852
923
  };
853
924
  "transformer:document-skipped": {
854
- collection: AnyCollection;
925
+ collection: AnyContent;
855
926
  filePath: string;
856
927
  reason?: string;
857
928
  };
@@ -881,7 +952,9 @@ type WatchableCollection = {
881
952
  };
882
953
  type WatcherConfiguration = {
883
954
  inputPaths: Array<string>;
884
- collections: Array<WatchableCollection>;
955
+ collections: Array<WatchableCollection | {
956
+ filePath: string;
957
+ }>;
885
958
  };
886
959
  declare function createWatcher(emitter: Emitter, baseDirectory: string, configuration: WatcherConfiguration, sync: SyncFn): Promise<{
887
960
  unsubscribe: () => Promise<void>;
@@ -920,7 +993,7 @@ declare class ConfigurationError extends Error {
920
993
  constructor(type: ErrorType, message: string);
921
994
  }
922
995
  type InternalConfiguration = {
923
- collections: Array<AnyCollection>;
996
+ collections: Array<AnyContent>;
924
997
  path: string;
925
998
  inputPaths: Array<string>;
926
999
  checksum: string;
@@ -979,7 +1052,7 @@ declare function createBuilder(configurationPath: string, options?: Options$1, e
979
1052
  unsubscribe: () => Promise<void>;
980
1053
  }>;
981
1054
  on: {
982
- <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;
1055
+ <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:singleton-warning" | "transformer:result-error" | "transformer:error" | "transformer:document-skipped" | "watcher:subscribe-error" | "watcher:subscribed" | "watcher:unsubscribed">(key: TKey, listener: (event: Events[TKey]) => void): void;
983
1056
  <TKey extends "_error" | "_all">(key: TKey, listener: (event: SystemEvents[TKey]) => void): void;
984
1057
  };
985
1058
  }>;
@@ -990,7 +1063,7 @@ declare function createInternalBuilder(initialConfiguration: InternalConfigurati
990
1063
  unsubscribe: () => Promise<void>;
991
1064
  }>;
992
1065
  on: {
993
- <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;
1066
+ <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:singleton-warning" | "transformer:result-error" | "transformer:error" | "transformer:document-skipped" | "watcher:subscribe-error" | "watcher:subscribed" | "watcher:unsubscribed">(key: TKey, listener: (event: Events[TKey]) => void): void;
994
1067
  <TKey extends "_error" | "_all">(key: TKey, listener: (event: SystemEvents[TKey]) => void): void;
995
1068
  };
996
1069
  }>;
@@ -999,8 +1072,9 @@ type Builder = Awaited<ReturnType<typeof createBuilder>>;
999
1072
  //#region src/features.d.ts
1000
1073
  declare const deprecations: {
1001
1074
  implicitContentProperty: string;
1075
+ collectionsConfigProperty: string;
1002
1076
  };
1003
1077
  type Deprecation = keyof typeof deprecations;
1004
1078
  declare function suppressDeprecatedWarnings(...suppresses: Array<Deprecation | "all">): void;
1005
1079
  //#endregion
1006
- export { AnyCollection, AnyConfiguration, Builder, BuilderEvents, CollectError, Collection, CollectionRequest, Configuration, ConfigurationError, ConfigurationReloadError, Context, type Document, type GetTypeByName, Meta, type Modification, Schema, SkippedSignal, TransformError, type Watcher, createBuilder, createDefaultImport, createInternalBuilder, createNamedImport, defineCollection, defineConfig, defineParser, skippedSymbol, suppressDeprecatedWarnings };
1080
+ export { AnyCollection, AnyConfiguration, AnyContent, AnySingleton, Builder, BuilderEvents, CollectError, Collection, CollectionContext, CollectionRequest, Configuration, ConfigurationError, ConfigurationReloadError, ConfigurationWithCollections, ConfigurationWithContent, ContentType, Context, type Document, type GetTypeByName, Meta, type Modification, Schema, Singleton, SingletonContext, SingletonRequest, SkippedSignal, TransformError, type Watcher, createBuilder, createDefaultImport, createInternalBuilder, createNamedImport, defineCollection, defineConfig, defineParser, defineSingleton, isSingleton, skippedSymbol, suppressDeprecatedWarnings };
package/dist/index.js CHANGED
@@ -4,6 +4,10 @@ import { existsSync } from "node:fs";
4
4
  import fs, { mkdir, readFile, unlink, writeFile } from "node:fs/promises";
5
5
  import { readFile as readFile$1 } from "fs/promises";
6
6
  import { glob } from "tinyglobby";
7
+ import fs2 from "fs";
8
+ import path2 from "path";
9
+ import { build } from "esbuild";
10
+ import { createRequire } from "module";
7
11
  import matter from "gray-matter";
8
12
  import { parse, stringify } from "yaml";
9
13
  import camelcase from "camelcase";
@@ -11,10 +15,6 @@ import pluralize from "pluralize";
11
15
  import picomatch from "picomatch";
12
16
  import os from "node:os";
13
17
  import pLimit from "p-limit";
14
- import fs2 from "fs";
15
- import path2 from "path";
16
- import { build } from "esbuild";
17
- import { createRequire } from "module";
18
18
  import serializeJs from "serialize-javascript";
19
19
  import { EventEmitter } from "node:events";
20
20
  import chokidar from "chokidar";
@@ -23,10 +23,8 @@ import chokidar from "chokidar";
23
23
  function createKey(config$1, input, key) {
24
24
  return createHash("sha256").update(config$1).update(JSON.stringify(input)).update(key).digest("hex");
25
25
  }
26
- async function createCacheDirectory(directory) {
27
- const cacheDirectory = path.join(directory, ".content-collections", "cache");
26
+ async function createCacheDirectory(cacheDirectory) {
28
27
  if (!existsSync(cacheDirectory)) await mkdir(cacheDirectory, { recursive: true });
29
- return cacheDirectory;
30
28
  }
31
29
  function fileName(input) {
32
30
  return input.replace(/[^a-z0-9]/gi, "_").toLowerCase();
@@ -39,8 +37,8 @@ async function readMapping(mappingPath) {
39
37
  }
40
38
  return {};
41
39
  }
42
- async function createCacheManager(baseDirectory, configChecksum) {
43
- const cacheDirectory = await createCacheDirectory(baseDirectory);
40
+ async function createCacheManager(cacheDirectory, configChecksum) {
41
+ await createCacheDirectory(cacheDirectory);
44
42
  const mappingPath = join(cacheDirectory, "mapping.json");
45
43
  const mapping = await readMapping(mappingPath);
46
44
  async function flush() {
@@ -94,215 +92,6 @@ async function createCacheManager(baseDirectory, configChecksum) {
94
92
  };
95
93
  }
96
94
 
97
- //#endregion
98
- //#region src/parser.ts
99
- function parseYaml(content) {
100
- return parse(content.trim());
101
- }
102
- function frontmatter(fileContent) {
103
- return matter(fileContent, { engines: { yaml: {
104
- parse: parseYaml,
105
- stringify
106
- } } });
107
- }
108
- function frontmatterParser(fileContent) {
109
- const { data, content } = frontmatter(fileContent);
110
- return {
111
- ...data,
112
- content: content.trim()
113
- };
114
- }
115
- function frontmatterOnlyParser(fileContent) {
116
- const { data } = frontmatter(fileContent);
117
- return data;
118
- }
119
- const parsers = {
120
- frontmatter: {
121
- hasContent: true,
122
- parse: frontmatterParser
123
- },
124
- ["frontmatter-only"]: {
125
- hasContent: false,
126
- parse: frontmatterOnlyParser
127
- },
128
- json: {
129
- hasContent: false,
130
- parse: JSON.parse
131
- },
132
- yaml: {
133
- hasContent: false,
134
- parse: parseYaml
135
- }
136
- };
137
- function getParser(configuredParser) {
138
- if (typeof configuredParser === "string") return parsers[configuredParser];
139
- return configuredParser;
140
- }
141
- function defineParser(parser) {
142
- if (typeof parser === "function") return {
143
- hasContent: false,
144
- parse: parser
145
- };
146
- return parser;
147
- }
148
- function isValidParser(parser) {
149
- if (typeof parser === "string") return parser in parsers;
150
- return "hasContent" in parser && typeof parser.parse === "function";
151
- }
152
-
153
- //#endregion
154
- //#region src/utils.ts
155
- function generateTypeName(name) {
156
- return camelcase(pluralize.singular(name), { pascalCase: true });
157
- }
158
- function isDefined(value) {
159
- return value !== void 0 && value !== null;
160
- }
161
- function orderByPath(a, b) {
162
- return a.path.localeCompare(b.path);
163
- }
164
- function removeChildPaths(paths) {
165
- return Array.from(new Set(paths.filter((path$1) => {
166
- return !paths.some((otherPath) => {
167
- if (path$1 === otherPath) return false;
168
- return path$1.startsWith(otherPath);
169
- });
170
- })));
171
- }
172
- function posixToNativePath(pathName) {
173
- if (path.sep !== path.posix.sep) return pathName.replaceAll(path.posix.sep, path.sep);
174
- return pathName;
175
- }
176
- function toError(error) {
177
- return error instanceof Error ? error : new Error(String(error));
178
- }
179
-
180
- //#endregion
181
- //#region src/collector.ts
182
- var CollectError = class extends Error {
183
- type;
184
- constructor(type, message) {
185
- super(message);
186
- this.type = type;
187
- }
188
- };
189
- function createCollector(emitter, baseDirectory = ".") {
190
- async function read(filePath) {
191
- try {
192
- return await readFile$1(filePath, "utf-8");
193
- } catch (error) {
194
- emitter.emit("collector:read-error", {
195
- filePath,
196
- error: new CollectError("Read", String(error))
197
- });
198
- return null;
199
- }
200
- }
201
- async function collectFile(collection, filePath) {
202
- const file = await read(path.join(baseDirectory, collection.directory, filePath));
203
- if (!file) return null;
204
- try {
205
- return {
206
- data: await getParser(collection.parser).parse(file),
207
- path: filePath
208
- };
209
- } catch (error) {
210
- emitter.emit("collector:parse-error", {
211
- filePath: path.join(collection.directory, filePath),
212
- error: new CollectError("Parse", String(error))
213
- });
214
- return null;
215
- }
216
- }
217
- function createIgnorePattern(collection) {
218
- if (collection.exclude) if (Array.isArray(collection.exclude)) return collection.exclude;
219
- else return [collection.exclude];
220
- }
221
- async function resolveCollection(collection) {
222
- const collectionDirectory = path.join(baseDirectory, collection.directory);
223
- const promises = (await glob(Array.isArray(collection.include) ? collection.include : [collection.include], {
224
- cwd: collectionDirectory,
225
- onlyFiles: true,
226
- absolute: false,
227
- ignore: createIgnorePattern(collection)
228
- })).map((filePath) => collectFile(collection, posixToNativePath(filePath)));
229
- const files = await Promise.all(promises);
230
- return {
231
- ...collection,
232
- files: files.filter(isDefined).sort(orderByPath)
233
- };
234
- }
235
- async function collect(unresolvedCollections) {
236
- const promises = unresolvedCollections.map((collection) => resolveCollection(collection));
237
- return await Promise.all(promises);
238
- }
239
- return {
240
- collect,
241
- collectFile
242
- };
243
- }
244
-
245
- //#endregion
246
- //#region src/synchronizer.ts
247
- function createSynchronizer(readCollectionFile, collections, baseDirectory = ".") {
248
- function findCollections(filePath) {
249
- const resolvedFilePath = path.resolve(filePath);
250
- return collections.filter((collection) => {
251
- return resolvedFilePath.startsWith(path.resolve(baseDirectory, collection.directory));
252
- });
253
- }
254
- function createRelativePath(collectionPath, filePath) {
255
- const resolvedCollectionPath = path.resolve(baseDirectory, collectionPath);
256
- let relativePath = path.resolve(filePath).slice(resolvedCollectionPath.length);
257
- if (relativePath.startsWith(path.sep)) relativePath = relativePath.slice(path.sep.length);
258
- return relativePath;
259
- }
260
- function resolve$1(filePath) {
261
- return findCollections(filePath).map((collection) => {
262
- return {
263
- collection,
264
- relativePath: createRelativePath(collection.directory, filePath)
265
- };
266
- }).filter(({ collection, relativePath }) => {
267
- return picomatch.isMatch(relativePath, collection.include, {
268
- windows: process.platform === "win32",
269
- ignore: collection.exclude
270
- });
271
- });
272
- }
273
- function deleted(filePath) {
274
- const resolvedCollections = resolve$1(filePath);
275
- if (resolvedCollections.length === 0) return false;
276
- let changed$1 = false;
277
- for (const { collection, relativePath } of resolvedCollections) {
278
- const index = collection.files.findIndex((file) => file.path === relativePath);
279
- if (collection.files.splice(index, 1).length > 0) changed$1 = true;
280
- }
281
- return changed$1;
282
- }
283
- async function changed(filePath) {
284
- const resolvedCollections = resolve$1(filePath);
285
- if (resolvedCollections.length === 0) return false;
286
- let changed$1 = false;
287
- for (const { collection, relativePath } of resolvedCollections) {
288
- const index = collection.files.findIndex((file$1) => file$1.path === relativePath);
289
- const file = await readCollectionFile(collection, relativePath);
290
- if (file) {
291
- changed$1 = true;
292
- if (index === -1) {
293
- collection.files.push(file);
294
- collection.files.sort(orderByPath);
295
- } else collection.files[index] = file;
296
- }
297
- }
298
- return changed$1;
299
- }
300
- return {
301
- deleted,
302
- changed
303
- };
304
- }
305
-
306
95
  //#endregion
307
96
  //#region ../../node_modules/.pnpm/load-tsconfig@0.2.5/node_modules/load-tsconfig/dist/index.js
308
97
  var singleComment = Symbol("singleComment");
@@ -522,6 +311,46 @@ async function compile(configurationPath, outfile) {
522
311
  return Object.keys(result.metafile.inputs);
523
312
  }
524
313
 
314
+ //#endregion
315
+ //#region src/features.ts
316
+ const deprecations = {
317
+ implicitContentProperty: `The implicit addition of a content property to schemas is deprecated.
318
+ Please add an explicit content property to your schema.
319
+ For more information, see:
320
+ https://content-collections.dev/docs/deprecations/implicit-content-property`,
321
+ collectionsConfigProperty: `The configuration property "collections" is deprecated.
322
+ Please use "content" instead.
323
+ For more information, see:
324
+ https://content-collections.dev/docs/deprecations/config-collections-property`
325
+ };
326
+ const _suppressDeprecatedWarnings = [];
327
+ function suppressDeprecatedWarnings(...suppresses) {
328
+ for (const deprecation of suppresses) if (deprecation === "all") {
329
+ _suppressDeprecatedWarnings.push(...Object.keys(deprecations));
330
+ return;
331
+ } else _suppressDeprecatedWarnings.push(deprecation);
332
+ }
333
+ function deprecated(deprecation, logger = console.warn) {
334
+ if (_suppressDeprecatedWarnings.includes(deprecation)) return;
335
+ logger(`[CC DEPRECATED]: ${deprecations[deprecation]}`);
336
+ }
337
+ const retiredFeatures = { legacySchema: `The use of a function as a schema is retired.
338
+ Please use a StandardSchema compliant library directly.
339
+ For more information, see:
340
+ https://content-collections.dev/docs/deprecations/schema-as-function` };
341
+ var RetiredFeatureError = class RetiredFeatureError extends Error {
342
+ feature;
343
+ constructor(feature) {
344
+ super(`This feature has been removed:\n${retiredFeatures[feature]}`);
345
+ this.feature = feature;
346
+ this.name = "RetiredFeatureError";
347
+ Object.setPrototypeOf(this, RetiredFeatureError.prototype);
348
+ }
349
+ };
350
+ function retired(feature) {
351
+ throw new RetiredFeatureError(feature);
352
+ }
353
+
525
354
  //#endregion
526
355
  //#region src/configurationReader.ts
527
356
  var ConfigurationError = class extends Error {
@@ -531,83 +360,160 @@ var ConfigurationError = class extends Error {
531
360
  this.type = type;
532
361
  }
533
362
  };
534
- const defaultConfigName = "content-collection-config.mjs";
535
- function resolveCacheDir(config$1, options) {
536
- if (options.cacheDir) return options.cacheDir;
537
- return path.join(path.dirname(config$1), ".content-collections", "cache");
363
+ const defaultConfigName = "content-collection-config.mjs";
364
+ function resolveCacheDir$1(config$1, options) {
365
+ if (options.cacheDir) return options.cacheDir;
366
+ return path.join(path.dirname(config$1), ".content-collections", "cache");
367
+ }
368
+ function createConfigurationReader() {
369
+ return async (configurationPath, options = { configName: defaultConfigName }) => {
370
+ if (!existsSync(configurationPath)) throw new ConfigurationError("Read", `configuration file ${configurationPath} does not exist`);
371
+ const cacheDir = resolveCacheDir$1(configurationPath, options);
372
+ await fs.mkdir(cacheDir, { recursive: true });
373
+ const outfile = path.join(cacheDir, options.configName);
374
+ try {
375
+ const configurationPaths = await compile(configurationPath, outfile);
376
+ const { content, collections,...rest } = (await import(`file://${path.resolve(outfile)}?x=${Date.now()}`)).default;
377
+ if (!content && collections) deprecated("collectionsConfigProperty");
378
+ const hash = createHash("sha256");
379
+ hash.update(await fs.readFile(outfile, "utf-8"));
380
+ const checksum = hash.digest("hex");
381
+ return {
382
+ ...rest,
383
+ collections: content ?? collections ?? [],
384
+ path: configurationPath,
385
+ inputPaths: configurationPaths.map((p) => path.resolve(p)),
386
+ generateTypes: true,
387
+ checksum
388
+ };
389
+ } catch (error) {
390
+ throw new ConfigurationError("Compile", `configuration file ${configurationPath} is invalid: ${error}`);
391
+ }
392
+ };
393
+ }
394
+
395
+ //#endregion
396
+ //#region src/parser.ts
397
+ function parseYaml(content) {
398
+ return parse(content.trim());
399
+ }
400
+ function frontmatter(fileContent) {
401
+ return matter(fileContent, { engines: { yaml: {
402
+ parse: parseYaml,
403
+ stringify
404
+ } } });
405
+ }
406
+ function frontmatterParser(fileContent) {
407
+ const { data, content } = frontmatter(fileContent);
408
+ return {
409
+ ...data,
410
+ content: content.trim()
411
+ };
412
+ }
413
+ function frontmatterOnlyParser(fileContent) {
414
+ const { data } = frontmatter(fileContent);
415
+ return data;
416
+ }
417
+ const parsers = {
418
+ frontmatter: {
419
+ hasContent: true,
420
+ parse: frontmatterParser
421
+ },
422
+ ["frontmatter-only"]: {
423
+ hasContent: false,
424
+ parse: frontmatterOnlyParser
425
+ },
426
+ json: {
427
+ hasContent: false,
428
+ parse: JSON.parse
429
+ },
430
+ yaml: {
431
+ hasContent: false,
432
+ parse: parseYaml
433
+ }
434
+ };
435
+ function getParser(configuredParser) {
436
+ if (typeof configuredParser === "string") return parsers[configuredParser];
437
+ return configuredParser;
538
438
  }
539
- function createConfigurationReader() {
540
- return async (configurationPath, options = { configName: defaultConfigName }) => {
541
- if (!existsSync(configurationPath)) throw new ConfigurationError("Read", `configuration file ${configurationPath} does not exist`);
542
- const cacheDir = resolveCacheDir(configurationPath, options);
543
- await fs.mkdir(cacheDir, { recursive: true });
544
- const outfile = path.join(cacheDir, options.configName);
545
- try {
546
- const configurationPaths = await compile(configurationPath, outfile);
547
- const module = await import(`file://${path.resolve(outfile)}?x=${Date.now()}`);
548
- const hash = createHash("sha256");
549
- hash.update(await fs.readFile(outfile, "utf-8"));
550
- const checksum = hash.digest("hex");
551
- return {
552
- ...module.default,
553
- path: configurationPath,
554
- inputPaths: configurationPaths.map((p) => path.resolve(p)),
555
- generateTypes: true,
556
- checksum
557
- };
558
- } catch (error) {
559
- throw new ConfigurationError("Compile", `configuration file ${configurationPath} is invalid: ${error}`);
560
- }
439
+ function defineParser(parser) {
440
+ if (typeof parser === "function") return {
441
+ hasContent: false,
442
+ parse: parser
561
443
  };
444
+ return parser;
445
+ }
446
+ function isValidParser(parser) {
447
+ if (typeof parser === "string") return parser in parsers;
448
+ return "hasContent" in parser && typeof parser.parse === "function";
562
449
  }
563
450
 
564
451
  //#endregion
565
- //#region src/features.ts
566
- const deprecations = { implicitContentProperty: `The implicit addition of a content property to schemas is deprecated.
567
- Please add an explicit content property to your schema.
568
- For more information, see:
569
- https://content-collections.dev/docs/deprecations/implicit-content-property` };
570
- const _suppressDeprecatedWarnings = [];
571
- function suppressDeprecatedWarnings(...suppresses) {
572
- for (const deprecation of suppresses) if (deprecation === "all") {
573
- _suppressDeprecatedWarnings.push(...Object.keys(deprecations));
574
- return;
575
- } else _suppressDeprecatedWarnings.push(deprecation);
452
+ //#region src/utils.ts
453
+ function generateTypeName(name) {
454
+ return camelcase(pluralize.singular(name), { pascalCase: true });
576
455
  }
577
- function deprecated(deprecation, logger = console.warn) {
578
- if (_suppressDeprecatedWarnings.includes(deprecation)) return;
579
- logger(`[CC DEPRECATED]: ${deprecations[deprecation]}`);
456
+ function generateArrayConstName(name) {
457
+ return "all" + pluralize(name.charAt(0).toUpperCase() + name.slice(1));
580
458
  }
581
- const retiredFeatures = { legacySchema: `The use of a function as a schema is retired.
582
- Please use a StandardSchema compliant library directly.
583
- For more information, see:
584
- https://content-collections.dev/docs/deprecations/schema-as-function` };
585
- var RetiredFeatureError = class RetiredFeatureError extends Error {
586
- feature;
587
- constructor(feature) {
588
- super(`This feature has been removed:\n${retiredFeatures[feature]}`);
589
- this.feature = feature;
590
- this.name = "RetiredFeatureError";
591
- Object.setPrototypeOf(this, RetiredFeatureError.prototype);
592
- }
593
- };
594
- function retired(feature) {
595
- throw new RetiredFeatureError(feature);
459
+ function generateSingletonConstName(typeName) {
460
+ return camelcase(typeName, { pascalCase: false });
461
+ }
462
+ function isDefined(value) {
463
+ return value !== void 0 && value !== null;
464
+ }
465
+ function orderByPath(a, b) {
466
+ return a.path.localeCompare(b.path);
467
+ }
468
+ function removeChildPaths(paths) {
469
+ return Array.from(new Set(paths.filter((path$1) => {
470
+ return !paths.some((otherPath) => {
471
+ if (path$1 === otherPath) return false;
472
+ return path$1.startsWith(otherPath);
473
+ });
474
+ })));
475
+ }
476
+ function posixToNativePath(pathName) {
477
+ if (path.sep !== path.posix.sep) return pathName.replaceAll(path.posix.sep, path.sep);
478
+ return pathName;
479
+ }
480
+ function toError(error) {
481
+ return error instanceof Error ? error : new Error(String(error));
596
482
  }
597
483
 
598
484
  //#endregion
599
485
  //#region src/config.ts
600
486
  const skippedSymbol = Symbol("skipped");
487
+ function isSingleton(source) {
488
+ return source.type === "singleton";
489
+ }
601
490
  function defineCollection(collection) {
602
491
  let typeName = collection.typeName;
603
492
  if (!typeName) typeName = generateTypeName(collection.name);
604
493
  let parser = collection.parser;
605
494
  if (!parser) parser = "frontmatter";
606
- else if (!isValidParser(parser)) throw new ConfigurationError("Read", `Parser ${parser} is not valid a parser`);
495
+ else if (!isValidParser(parser)) throw new ConfigurationError("Read", `Parser ${parser} is not a valid parser`);
607
496
  let schema$1 = collection.schema;
608
497
  if (!schema$1["~standard"]) retired("legacySchema");
609
498
  return {
610
499
  ...collection,
500
+ type: "collection",
501
+ typeName,
502
+ parser,
503
+ schema: schema$1
504
+ };
505
+ }
506
+ function defineSingleton(singleton) {
507
+ let typeName = singleton.typeName;
508
+ if (!typeName) typeName = generateTypeName(singleton.name);
509
+ let parser = singleton.parser;
510
+ if (!parser) parser = "frontmatter";
511
+ else if (!isValidParser(parser)) throw new ConfigurationError("Read", `Parser ${parser} is not a valid parser`);
512
+ let schema$1 = singleton.schema;
513
+ if (!schema$1["~standard"]) retired("legacySchema");
514
+ return {
515
+ ...singleton,
516
+ type: "singleton",
611
517
  typeName,
612
518
  parser,
613
519
  schema: schema$1
@@ -617,6 +523,175 @@ function defineConfig(config$1) {
617
523
  return config$1;
618
524
  }
619
525
 
526
+ //#endregion
527
+ //#region src/collector.ts
528
+ var CollectError = class extends Error {
529
+ type;
530
+ constructor(type, message) {
531
+ super(message);
532
+ this.type = type;
533
+ }
534
+ };
535
+ function createCollector(emitter, baseDirectory = ".") {
536
+ async function read(filePath) {
537
+ try {
538
+ return await readFile$1(filePath, "utf-8");
539
+ } catch (error) {
540
+ emitter.emit("collector:read-error", {
541
+ filePath,
542
+ error: new CollectError("Read", String(error))
543
+ });
544
+ return null;
545
+ }
546
+ }
547
+ async function collectCollectionFile(collection, filePath) {
548
+ const file = await read(path.join(baseDirectory, collection.directory, filePath));
549
+ if (!file) return null;
550
+ try {
551
+ return {
552
+ data: await getParser(collection.parser).parse(file),
553
+ path: filePath
554
+ };
555
+ } catch (error) {
556
+ emitter.emit("collector:parse-error", {
557
+ filePath: path.join(collection.directory, filePath),
558
+ error: new CollectError("Parse", String(error))
559
+ });
560
+ return null;
561
+ }
562
+ }
563
+ async function collectSingletonFile(singleton) {
564
+ const file = await read(path.join(baseDirectory, singleton.filePath));
565
+ if (!file) {
566
+ if (singleton.optional) return null;
567
+ throw new CollectError("Read", `Singleton file not found at path: ${singleton.filePath}`);
568
+ }
569
+ try {
570
+ return {
571
+ data: await getParser(singleton.parser).parse(file),
572
+ path: path.basename(singleton.filePath)
573
+ };
574
+ } catch (error) {
575
+ emitter.emit("collector:parse-error", {
576
+ filePath: singleton.filePath,
577
+ error: new CollectError("Parse", String(error))
578
+ });
579
+ return null;
580
+ }
581
+ }
582
+ async function collectFile(source, filePath) {
583
+ if (isSingleton(source)) return collectSingletonFile(source);
584
+ return collectCollectionFile(source, filePath);
585
+ }
586
+ function createIgnorePattern(collection) {
587
+ if (collection.exclude) if (Array.isArray(collection.exclude)) return collection.exclude;
588
+ else return [collection.exclude];
589
+ }
590
+ async function resolveCollection(collection) {
591
+ const collectionDirectory = path.join(baseDirectory, collection.directory);
592
+ const promises = (await glob(Array.isArray(collection.include) ? collection.include : [collection.include], {
593
+ cwd: collectionDirectory,
594
+ onlyFiles: true,
595
+ absolute: false,
596
+ ignore: createIgnorePattern(collection)
597
+ })).map((filePath) => collectCollectionFile(collection, posixToNativePath(filePath)));
598
+ const files = await Promise.all(promises);
599
+ return {
600
+ ...collection,
601
+ files: files.filter(isDefined).sort(orderByPath)
602
+ };
603
+ }
604
+ async function resolveSingleton(singleton) {
605
+ const file = await collectSingletonFile(singleton);
606
+ return {
607
+ ...singleton,
608
+ files: file ? [file] : []
609
+ };
610
+ }
611
+ async function collect(unresolvedCollections) {
612
+ const promises = unresolvedCollections.map((collection) => {
613
+ if (isSingleton(collection)) return resolveSingleton(collection);
614
+ return resolveCollection(collection);
615
+ });
616
+ return await Promise.all(promises);
617
+ }
618
+ return {
619
+ collect,
620
+ collectFile
621
+ };
622
+ }
623
+
624
+ //#endregion
625
+ //#region src/synchronizer.ts
626
+ function createSynchronizer(readCollectionFile, collections, baseDirectory = ".") {
627
+ function findCollections(filePath) {
628
+ const resolvedFilePath = path.resolve(filePath);
629
+ return collections.filter((collection) => {
630
+ if (isSingleton(collection)) return resolvedFilePath === path.resolve(baseDirectory, collection.filePath);
631
+ return resolvedFilePath.startsWith(path.resolve(baseDirectory, collection.directory));
632
+ });
633
+ }
634
+ function createRelativePath(collectionPath, filePath) {
635
+ const resolvedCollectionPath = path.resolve(baseDirectory, collectionPath);
636
+ let relativePath = path.resolve(filePath).slice(resolvedCollectionPath.length);
637
+ if (relativePath.startsWith(path.sep)) relativePath = relativePath.slice(path.sep.length);
638
+ return relativePath;
639
+ }
640
+ function resolve$1(filePath) {
641
+ return findCollections(filePath).map((collection) => {
642
+ return {
643
+ collection,
644
+ relativePath: isSingleton(collection) ? path.basename(collection.filePath) : createRelativePath(collection.directory, filePath)
645
+ };
646
+ }).filter(({ collection, relativePath }) => {
647
+ if (isSingleton(collection)) return true;
648
+ return picomatch.isMatch(relativePath, collection.include, {
649
+ windows: process.platform === "win32",
650
+ ignore: collection.exclude
651
+ });
652
+ });
653
+ }
654
+ function deleted(filePath) {
655
+ const resolvedCollections = resolve$1(filePath);
656
+ if (resolvedCollections.length === 0) return false;
657
+ let changed$1 = false;
658
+ for (const { collection, relativePath } of resolvedCollections) {
659
+ if (isSingleton(collection)) {
660
+ if (collection.files.length > 0) {
661
+ collection.files = [];
662
+ changed$1 = true;
663
+ }
664
+ continue;
665
+ }
666
+ const index = collection.files.findIndex((file) => file.path === relativePath);
667
+ if (collection.files.splice(index, 1).length > 0) changed$1 = true;
668
+ }
669
+ return changed$1;
670
+ }
671
+ async function changed(filePath) {
672
+ const resolvedCollections = resolve$1(filePath);
673
+ if (resolvedCollections.length === 0) return false;
674
+ let changed$1 = false;
675
+ for (const { collection, relativePath } of resolvedCollections) {
676
+ const index = collection.files.findIndex((file$1) => file$1.path === relativePath);
677
+ const file = await readCollectionFile(collection, relativePath);
678
+ if (file) {
679
+ changed$1 = true;
680
+ if (isSingleton(collection)) collection.files = [file];
681
+ else if (index === -1) {
682
+ collection.files.push(file);
683
+ collection.files.sort(orderByPath);
684
+ } else collection.files[index] = file;
685
+ }
686
+ }
687
+ return changed$1;
688
+ }
689
+ return {
690
+ deleted,
691
+ changed
692
+ };
693
+ }
694
+
620
695
  //#endregion
621
696
  //#region ../../node_modules/.pnpm/zod@4.1.11/node_modules/zod/v4/core/core.js
622
697
  /** A special constant with type `never` */
@@ -1856,17 +1931,24 @@ function createImport(imp, variableName) {
1856
1931
  function serialize(value) {
1857
1932
  let serializedValue = "";
1858
1933
  let counter = 0;
1859
- function handleImports(item) {
1860
- if (item instanceof Object) Object.entries(item).forEach(([key, value$1]) => {
1861
- if (isImport(value$1)) {
1862
- counter++;
1863
- const variableName = `__v_${counter}`;
1864
- serializedValue += createImport(value$1, variableName);
1865
- item[key] = variableName;
1866
- } else if (value$1 instanceof Object) handleImports(value$1);
1934
+ function handleImports(item, parent, key) {
1935
+ if (!item || typeof item !== "object") return;
1936
+ if (isImport(item)) {
1937
+ counter++;
1938
+ const variableName = `__v_${counter}`;
1939
+ serializedValue += createImport(item, variableName);
1940
+ if (parent !== void 0 && key !== void 0) parent[key] = variableName;
1941
+ return;
1942
+ }
1943
+ if (Array.isArray(item)) {
1944
+ item.forEach((entry, index) => handleImports(entry, item, index));
1945
+ return;
1946
+ }
1947
+ Object.entries(item).forEach(([entryKey, entryValue]) => {
1948
+ handleImports(entryValue, item, entryKey);
1867
1949
  });
1868
1950
  }
1869
- value.forEach(handleImports);
1951
+ handleImports(value);
1870
1952
  serializedValue += "\n";
1871
1953
  const js = serializeJs(value, {
1872
1954
  space: 2,
@@ -1959,35 +2041,46 @@ function createTransformer(emitter, cacheManager) {
1959
2041
  };
1960
2042
  }
1961
2043
  function createContext(collections, collection, cache) {
1962
- return {
1963
- documents: (collection$1) => {
1964
- const resolved = collections.find((c) => c.name === collection$1.name);
1965
- if (!resolved) throw new TransformError("Configuration", `Collection ${collection$1.name} not found, do you have registered it in your configuration?`);
2044
+ const sourceDirectory = isSingleton(collection) ? dirname(collection.filePath) : collection.directory;
2045
+ const base = {
2046
+ documents: (source) => {
2047
+ const resolved = collections.find((c) => c.name === source.name);
2048
+ if (!resolved) throw new TransformError("Configuration", `Collection ${source.name} not found, do you have registered it in your configuration?`);
1966
2049
  return resolved.documents.map((doc) => doc.document);
1967
2050
  },
2051
+ cache: cache.cacheFn,
1968
2052
  collection: {
1969
2053
  name: collection.name,
1970
- directory: collection.directory,
1971
- documents: async () => {
1972
- return collection.documents.map((doc) => doc.document);
1973
- }
2054
+ directory: sourceDirectory,
2055
+ documents: async () => collection.documents.map((doc) => doc.document)
1974
2056
  },
1975
- cache: cache.cacheFn,
1976
2057
  skip: (reason) => ({
1977
2058
  [skippedSymbol]: true,
1978
2059
  reason
1979
2060
  })
1980
2061
  };
2062
+ if (isSingleton(collection)) return {
2063
+ ...base,
2064
+ singleton: {
2065
+ name: collection.name,
2066
+ filePath: collection.filePath,
2067
+ directory: sourceDirectory,
2068
+ document: async () => {
2069
+ return collection.documents[0]?.document;
2070
+ }
2071
+ }
2072
+ };
2073
+ return base;
1981
2074
  }
1982
2075
  async function transformDocument(collections, collection, transform, doc) {
1983
2076
  const cache = cacheManager.cache(collection.name, doc.document._meta.path);
1984
2077
  const context = createContext(collections, collection, cache);
1985
2078
  try {
1986
- const document = await transform(doc.document, context);
2079
+ const document = isSingleton(collection) ? await transform(doc.document, context) : await transform(doc.document, context);
1987
2080
  await cache.tidyUp();
1988
2081
  if (isSkippedSignal(document)) emitter.emit("transformer:document-skipped", {
1989
2082
  collection,
1990
- filePath: join(collection.directory, doc.document._meta.filePath),
2083
+ filePath: join(isSingleton(collection) ? dirname(collection.filePath) : collection.directory, doc.document._meta.filePath),
1991
2084
  reason: document.reason
1992
2085
  });
1993
2086
  else return {
@@ -2009,7 +2102,8 @@ function createTransformer(emitter, cacheManager) {
2009
2102
  const transform = collection.transform;
2010
2103
  if (transform) {
2011
2104
  const limit = pLimit(os.cpus().length);
2012
- const docs = collection.documents.map((doc) => limit(() => transformDocument(collections, collection, transform, doc)));
2105
+ const typedTransform = transform;
2106
+ const docs = collection.documents.map((doc) => limit(() => transformDocument(collections, collection, typedTransform, doc)));
2013
2107
  const transformed = await Promise.all(docs);
2014
2108
  await cacheManager.flush();
2015
2109
  return transformed.filter(isDefined);
@@ -2032,29 +2126,33 @@ function createTransformer(emitter, cacheManager) {
2032
2126
  return async (untransformedCollections) => {
2033
2127
  const promises = untransformedCollections.map((collection) => parseCollection(collection));
2034
2128
  const collections = await Promise.all(promises);
2035
- for (const collection of collections) collection.documents = await validateDocuments(collection, await transformCollection(collections, collection));
2129
+ for (const collection of collections) {
2130
+ collection.documents = await validateDocuments(collection, await transformCollection(collections, collection));
2131
+ if (isSingleton(collection)) {
2132
+ if (collection.documents.length === 0) emitter.emit("transformer:singleton-warning", { collection });
2133
+ }
2134
+ }
2036
2135
  return collections;
2037
2136
  };
2038
2137
  }
2039
2138
 
2040
2139
  //#endregion
2041
2140
  //#region src/writer.ts
2042
- function createArrayConstName(name) {
2043
- return "all" + pluralize(name.charAt(0).toUpperCase() + name.slice(1));
2044
- }
2045
2141
  async function createDataFile(directory, collection) {
2046
- const dataPath = path.join(directory, `${createArrayConstName(collection.name)}.${extension}`);
2047
- await fs.writeFile(dataPath, serialize(collection.documents.map((doc) => doc.document)));
2142
+ const constName = isSingleton(collection) ? generateSingletonConstName(collection.typeName) : generateArrayConstName(collection.name);
2143
+ const dataPath = path.join(directory, `${constName}.${extension}`);
2144
+ const value = isSingleton(collection) ? collection.documents[0]?.document : collection.documents.map((doc) => doc.document);
2145
+ await fs.writeFile(dataPath, serialize(value));
2048
2146
  }
2049
2147
  function createDataFiles(directory, collections) {
2050
2148
  return Promise.all(collections.map((collection) => createDataFile(directory, collection)));
2051
2149
  }
2052
2150
  async function createJavaScriptFile(directory, configuration) {
2053
- const collections = configuration.collections.map(({ name }) => createArrayConstName(name));
2151
+ const exports = configuration.collections.map((collection) => isSingleton(collection) ? generateSingletonConstName(collection.typeName) : generateArrayConstName(collection.name));
2054
2152
  let content = `// generated by content-collections at ${/* @__PURE__ */ new Date()}\n\n`;
2055
- for (const name of collections) content += `import ${name} from "./${name}.${extension}";\n`;
2153
+ for (const name of exports) content += `import ${name} from "./${name}.${extension}";\n`;
2056
2154
  content += "\n";
2057
- content += "export { " + collections.join(", ") + " };\n";
2155
+ content += "export { " + exports.join(", ") + " };\n";
2058
2156
  await fs.writeFile(path.join(directory, "index.js"), content, "utf-8");
2059
2157
  }
2060
2158
  function createImportPath(directory, target) {
@@ -2071,7 +2169,8 @@ import { GetTypeByName } from "@content-collections/core";
2071
2169
  for (const collection of collections) {
2072
2170
  content += `\n`;
2073
2171
  content += `export type ${collection.typeName} = GetTypeByName<typeof configuration, "${collection.name}">;\n`;
2074
- content += `export declare const ${createArrayConstName(collection.name)}: Array<${collection.typeName}>;\n`;
2172
+ if (isSingleton(collection)) content += `export declare const ${generateSingletonConstName(collection.typeName)}: ${collection.typeName}${collection.optional ? " | undefined" : ""};\n`;
2173
+ else content += `export declare const ${generateArrayConstName(collection.name)}: Array<${collection.typeName}>;\n`;
2075
2174
  }
2076
2175
  content += "\n";
2077
2176
  content += "export {};\n";
@@ -2088,12 +2187,12 @@ async function createWriter(directory) {
2088
2187
 
2089
2188
  //#endregion
2090
2189
  //#region src/build.ts
2091
- async function createBuildContext({ emitter, outputDirectory, baseDirectory, configuration }) {
2190
+ async function createBuildContext({ emitter, outputDirectory, cacheDirectory, baseDirectory, configuration }) {
2092
2191
  const collector = createCollector(emitter, baseDirectory);
2093
2192
  const [writer, resolved, cacheManager] = await Promise.all([
2094
2193
  createWriter(outputDirectory),
2095
2194
  collector.collect(configuration.collections),
2096
- createCacheManager(baseDirectory, configuration.checksum)
2195
+ createCacheManager(cacheDirectory, configuration.checksum)
2097
2196
  ]);
2098
2197
  return {
2099
2198
  resolved,
@@ -2114,7 +2213,7 @@ async function build$1({ emitter, transform, resolved, writer, configuration })
2114
2213
  writer.createTypeDefinitionFile(configuration),
2115
2214
  writer.createJavaScriptFile(configuration)
2116
2215
  ]);
2117
- const pendingOnSuccess = collections.filter((collection) => Boolean(collection.onSuccess)).map((collection) => collection.onSuccess?.(collection.documents.map((doc) => doc.document)));
2216
+ const pendingOnSuccess = collections.filter((collection) => Boolean(collection.onSuccess)).map((collection) => isSingleton(collection) ? collection.onSuccess?.(collection.documents[0]?.document) : collection.onSuccess?.(collection.documents.map((doc) => doc.document)));
2118
2217
  await Promise.all(pendingOnSuccess.filter(isDefined));
2119
2218
  const stats = collections.reduce((acc, collection) => {
2120
2219
  acc.collections++;
@@ -2161,7 +2260,10 @@ function createEmitter() {
2161
2260
  //#endregion
2162
2261
  //#region src/watcher.ts
2163
2262
  async function createWatcher(emitter, baseDirectory, configuration, sync) {
2164
- const paths = removeChildPaths([...configuration.collections.map((collection) => path.join(baseDirectory, collection.directory)).map((p) => resolve(p)), ...configuration.inputPaths.map((p) => dirname(p))]);
2263
+ const paths = removeChildPaths([...configuration.collections.map((collection) => {
2264
+ const directory = isSingleton(collection) ? path.dirname(collection.filePath) : collection.directory;
2265
+ return path.join(baseDirectory, directory);
2266
+ }).map((p) => resolve(p)), ...configuration.inputPaths.map((p) => dirname(p))]);
2165
2267
  const watcher = chokidar.watch(paths, {
2166
2268
  ignored: [/(^|[\/\\])\../, /(^|[\/\\])node_modules([\/\\]|$)/],
2167
2269
  persistent: true,
@@ -2205,6 +2307,10 @@ function resolveOutputDir(baseDirectory, options) {
2205
2307
  if (options.outputDir) return options.outputDir;
2206
2308
  return path.join(baseDirectory, ".content-collections", "generated");
2207
2309
  }
2310
+ function resolveCacheDir(baseDirectory, options) {
2311
+ if (options.cacheDir) return options.cacheDir;
2312
+ return path.join(baseDirectory, ".content-collections", "cache");
2313
+ }
2208
2314
  var ConfigurationReloadError = class extends Error {
2209
2315
  constructor(message) {
2210
2316
  super(message);
@@ -2219,6 +2325,7 @@ async function createInternalBuilder(initialConfiguration, baseDirectory, option
2219
2325
  const readConfiguration = createConfigurationReader();
2220
2326
  const configurationPath = initialConfiguration.path;
2221
2327
  const outputDirectory = resolveOutputDir(baseDirectory, options);
2328
+ const cacheDirectory = resolveCacheDir(baseDirectory, options);
2222
2329
  emitter.emit("builder:created", {
2223
2330
  createdAt: Date.now(),
2224
2331
  configurationPath,
@@ -2230,6 +2337,7 @@ async function createInternalBuilder(initialConfiguration, baseDirectory, option
2230
2337
  emitter,
2231
2338
  baseDirectory,
2232
2339
  outputDirectory,
2340
+ cacheDirectory,
2233
2341
  configuration
2234
2342
  });
2235
2343
  async function sync(modification, filePath) {
@@ -2267,6 +2375,7 @@ async function createInternalBuilder(initialConfiguration, baseDirectory, option
2267
2375
  emitter,
2268
2376
  baseDirectory,
2269
2377
  outputDirectory,
2378
+ cacheDirectory,
2270
2379
  configuration
2271
2380
  });
2272
2381
  if (watcher) watcher = await createWatcher(emitter, baseDirectory, configuration, sync);
@@ -2292,4 +2401,4 @@ async function createInternalBuilder(initialConfiguration, baseDirectory, option
2292
2401
  }
2293
2402
 
2294
2403
  //#endregion
2295
- export { CollectError, ConfigurationError, ConfigurationReloadError, TransformError, createBuilder, createDefaultImport, createInternalBuilder, createNamedImport, defineCollection, defineConfig, defineParser, skippedSymbol, suppressDeprecatedWarnings };
2404
+ export { CollectError, ConfigurationError, ConfigurationReloadError, TransformError, createBuilder, createDefaultImport, createInternalBuilder, createNamedImport, defineCollection, defineConfig, defineParser, defineSingleton, isSingleton, skippedSymbol, suppressDeprecatedWarnings };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@content-collections/core",
3
3
  "type": "module",
4
- "version": "0.13.1",
4
+ "version": "0.14.1",
5
5
  "description": "Core of Content Collections",
6
6
  "author": "Sebastian Sdorra <s.sdorra@gmail.com>",
7
7
  "license": "MIT",