@content-collections/core 0.13.1 → 0.14.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 +1 -1
- package/dist/index.d.ts +132 -58
- package/dist/index.js +373 -271
- package/package.json +1 -1
package/README.md
CHANGED
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<
|
|
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<
|
|
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:
|
|
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
|
|
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
|
|
802
|
-
|
|
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
|
|
817
|
-
|
|
818
|
-
|
|
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:
|
|
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:
|
|
916
|
+
collection: AnyContent;
|
|
846
917
|
document: any;
|
|
847
918
|
error: TransformError;
|
|
848
919
|
};
|
|
849
920
|
"transformer:error": {
|
|
850
|
-
collection:
|
|
921
|
+
collection: AnyContent;
|
|
851
922
|
error: TransformError;
|
|
852
923
|
};
|
|
853
924
|
"transformer:document-skipped": {
|
|
854
|
-
collection:
|
|
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<
|
|
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";
|
|
@@ -94,215 +94,6 @@ async function createCacheManager(baseDirectory, configChecksum) {
|
|
|
94
94
|
};
|
|
95
95
|
}
|
|
96
96
|
|
|
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
97
|
//#endregion
|
|
307
98
|
//#region ../../node_modules/.pnpm/load-tsconfig@0.2.5/node_modules/load-tsconfig/dist/index.js
|
|
308
99
|
var singleComment = Symbol("singleComment");
|
|
@@ -522,6 +313,46 @@ async function compile(configurationPath, outfile) {
|
|
|
522
313
|
return Object.keys(result.metafile.inputs);
|
|
523
314
|
}
|
|
524
315
|
|
|
316
|
+
//#endregion
|
|
317
|
+
//#region src/features.ts
|
|
318
|
+
const deprecations = {
|
|
319
|
+
implicitContentProperty: `The implicit addition of a content property to schemas is deprecated.
|
|
320
|
+
Please add an explicit content property to your schema.
|
|
321
|
+
For more information, see:
|
|
322
|
+
https://content-collections.dev/docs/deprecations/implicit-content-property`,
|
|
323
|
+
collectionsConfigProperty: `The configuration property "collections" is deprecated.
|
|
324
|
+
Please use "content" instead.
|
|
325
|
+
For more information, see:
|
|
326
|
+
https://content-collections.dev/docs/deprecations/config-collections-property`
|
|
327
|
+
};
|
|
328
|
+
const _suppressDeprecatedWarnings = [];
|
|
329
|
+
function suppressDeprecatedWarnings(...suppresses) {
|
|
330
|
+
for (const deprecation of suppresses) if (deprecation === "all") {
|
|
331
|
+
_suppressDeprecatedWarnings.push(...Object.keys(deprecations));
|
|
332
|
+
return;
|
|
333
|
+
} else _suppressDeprecatedWarnings.push(deprecation);
|
|
334
|
+
}
|
|
335
|
+
function deprecated(deprecation, logger = console.warn) {
|
|
336
|
+
if (_suppressDeprecatedWarnings.includes(deprecation)) return;
|
|
337
|
+
logger(`[CC DEPRECATED]: ${deprecations[deprecation]}`);
|
|
338
|
+
}
|
|
339
|
+
const retiredFeatures = { legacySchema: `The use of a function as a schema is retired.
|
|
340
|
+
Please use a StandardSchema compliant library directly.
|
|
341
|
+
For more information, see:
|
|
342
|
+
https://content-collections.dev/docs/deprecations/schema-as-function` };
|
|
343
|
+
var RetiredFeatureError = class RetiredFeatureError extends Error {
|
|
344
|
+
feature;
|
|
345
|
+
constructor(feature) {
|
|
346
|
+
super(`This feature has been removed:\n${retiredFeatures[feature]}`);
|
|
347
|
+
this.feature = feature;
|
|
348
|
+
this.name = "RetiredFeatureError";
|
|
349
|
+
Object.setPrototypeOf(this, RetiredFeatureError.prototype);
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
function retired(feature) {
|
|
353
|
+
throw new RetiredFeatureError(feature);
|
|
354
|
+
}
|
|
355
|
+
|
|
525
356
|
//#endregion
|
|
526
357
|
//#region src/configurationReader.ts
|
|
527
358
|
var ConfigurationError = class extends Error {
|
|
@@ -544,12 +375,14 @@ function createConfigurationReader() {
|
|
|
544
375
|
const outfile = path.join(cacheDir, options.configName);
|
|
545
376
|
try {
|
|
546
377
|
const configurationPaths = await compile(configurationPath, outfile);
|
|
547
|
-
const
|
|
378
|
+
const { content, collections,...rest } = (await import(`file://${path.resolve(outfile)}?x=${Date.now()}`)).default;
|
|
379
|
+
if (!content && collections) deprecated("collectionsConfigProperty");
|
|
548
380
|
const hash = createHash("sha256");
|
|
549
381
|
hash.update(await fs.readFile(outfile, "utf-8"));
|
|
550
382
|
const checksum = hash.digest("hex");
|
|
551
383
|
return {
|
|
552
|
-
...
|
|
384
|
+
...rest,
|
|
385
|
+
collections: content ?? collections ?? [],
|
|
553
386
|
path: configurationPath,
|
|
554
387
|
inputPaths: configurationPaths.map((p) => path.resolve(p)),
|
|
555
388
|
generateTypes: true,
|
|
@@ -562,52 +395,127 @@ function createConfigurationReader() {
|
|
|
562
395
|
}
|
|
563
396
|
|
|
564
397
|
//#endregion
|
|
565
|
-
//#region src/
|
|
566
|
-
|
|
567
|
-
|
|
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);
|
|
398
|
+
//#region src/parser.ts
|
|
399
|
+
function parseYaml(content) {
|
|
400
|
+
return parse(content.trim());
|
|
576
401
|
}
|
|
577
|
-
function
|
|
578
|
-
|
|
579
|
-
|
|
402
|
+
function frontmatter(fileContent) {
|
|
403
|
+
return matter(fileContent, { engines: { yaml: {
|
|
404
|
+
parse: parseYaml,
|
|
405
|
+
stringify
|
|
406
|
+
} } });
|
|
580
407
|
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
408
|
+
function frontmatterParser(fileContent) {
|
|
409
|
+
const { data, content } = frontmatter(fileContent);
|
|
410
|
+
return {
|
|
411
|
+
...data,
|
|
412
|
+
content: content.trim()
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
function frontmatterOnlyParser(fileContent) {
|
|
416
|
+
const { data } = frontmatter(fileContent);
|
|
417
|
+
return data;
|
|
418
|
+
}
|
|
419
|
+
const parsers = {
|
|
420
|
+
frontmatter: {
|
|
421
|
+
hasContent: true,
|
|
422
|
+
parse: frontmatterParser
|
|
423
|
+
},
|
|
424
|
+
["frontmatter-only"]: {
|
|
425
|
+
hasContent: false,
|
|
426
|
+
parse: frontmatterOnlyParser
|
|
427
|
+
},
|
|
428
|
+
json: {
|
|
429
|
+
hasContent: false,
|
|
430
|
+
parse: JSON.parse
|
|
431
|
+
},
|
|
432
|
+
yaml: {
|
|
433
|
+
hasContent: false,
|
|
434
|
+
parse: parseYaml
|
|
592
435
|
}
|
|
593
436
|
};
|
|
594
|
-
function
|
|
595
|
-
|
|
437
|
+
function getParser(configuredParser) {
|
|
438
|
+
if (typeof configuredParser === "string") return parsers[configuredParser];
|
|
439
|
+
return configuredParser;
|
|
440
|
+
}
|
|
441
|
+
function defineParser(parser) {
|
|
442
|
+
if (typeof parser === "function") return {
|
|
443
|
+
hasContent: false,
|
|
444
|
+
parse: parser
|
|
445
|
+
};
|
|
446
|
+
return parser;
|
|
447
|
+
}
|
|
448
|
+
function isValidParser(parser) {
|
|
449
|
+
if (typeof parser === "string") return parser in parsers;
|
|
450
|
+
return "hasContent" in parser && typeof parser.parse === "function";
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
//#endregion
|
|
454
|
+
//#region src/utils.ts
|
|
455
|
+
function generateTypeName(name) {
|
|
456
|
+
return camelcase(pluralize.singular(name), { pascalCase: true });
|
|
457
|
+
}
|
|
458
|
+
function generateArrayConstName(name) {
|
|
459
|
+
return "all" + pluralize(name.charAt(0).toUpperCase() + name.slice(1));
|
|
460
|
+
}
|
|
461
|
+
function generateSingletonConstName(typeName) {
|
|
462
|
+
return camelcase(typeName, { pascalCase: false });
|
|
463
|
+
}
|
|
464
|
+
function isDefined(value) {
|
|
465
|
+
return value !== void 0 && value !== null;
|
|
466
|
+
}
|
|
467
|
+
function orderByPath(a, b) {
|
|
468
|
+
return a.path.localeCompare(b.path);
|
|
469
|
+
}
|
|
470
|
+
function removeChildPaths(paths) {
|
|
471
|
+
return Array.from(new Set(paths.filter((path$1) => {
|
|
472
|
+
return !paths.some((otherPath) => {
|
|
473
|
+
if (path$1 === otherPath) return false;
|
|
474
|
+
return path$1.startsWith(otherPath);
|
|
475
|
+
});
|
|
476
|
+
})));
|
|
477
|
+
}
|
|
478
|
+
function posixToNativePath(pathName) {
|
|
479
|
+
if (path.sep !== path.posix.sep) return pathName.replaceAll(path.posix.sep, path.sep);
|
|
480
|
+
return pathName;
|
|
481
|
+
}
|
|
482
|
+
function toError(error) {
|
|
483
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
596
484
|
}
|
|
597
485
|
|
|
598
486
|
//#endregion
|
|
599
487
|
//#region src/config.ts
|
|
600
488
|
const skippedSymbol = Symbol("skipped");
|
|
489
|
+
function isSingleton(source) {
|
|
490
|
+
return source.type === "singleton";
|
|
491
|
+
}
|
|
601
492
|
function defineCollection(collection) {
|
|
602
493
|
let typeName = collection.typeName;
|
|
603
494
|
if (!typeName) typeName = generateTypeName(collection.name);
|
|
604
495
|
let parser = collection.parser;
|
|
605
496
|
if (!parser) parser = "frontmatter";
|
|
606
|
-
else if (!isValidParser(parser)) throw new ConfigurationError("Read", `Parser ${parser} is not valid
|
|
497
|
+
else if (!isValidParser(parser)) throw new ConfigurationError("Read", `Parser ${parser} is not a valid parser`);
|
|
607
498
|
let schema$1 = collection.schema;
|
|
608
499
|
if (!schema$1["~standard"]) retired("legacySchema");
|
|
609
500
|
return {
|
|
610
501
|
...collection,
|
|
502
|
+
type: "collection",
|
|
503
|
+
typeName,
|
|
504
|
+
parser,
|
|
505
|
+
schema: schema$1
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
function defineSingleton(singleton) {
|
|
509
|
+
let typeName = singleton.typeName;
|
|
510
|
+
if (!typeName) typeName = generateTypeName(singleton.name);
|
|
511
|
+
let parser = singleton.parser;
|
|
512
|
+
if (!parser) parser = "frontmatter";
|
|
513
|
+
else if (!isValidParser(parser)) throw new ConfigurationError("Read", `Parser ${parser} is not a valid parser`);
|
|
514
|
+
let schema$1 = singleton.schema;
|
|
515
|
+
if (!schema$1["~standard"]) retired("legacySchema");
|
|
516
|
+
return {
|
|
517
|
+
...singleton,
|
|
518
|
+
type: "singleton",
|
|
611
519
|
typeName,
|
|
612
520
|
parser,
|
|
613
521
|
schema: schema$1
|
|
@@ -617,6 +525,175 @@ function defineConfig(config$1) {
|
|
|
617
525
|
return config$1;
|
|
618
526
|
}
|
|
619
527
|
|
|
528
|
+
//#endregion
|
|
529
|
+
//#region src/collector.ts
|
|
530
|
+
var CollectError = class extends Error {
|
|
531
|
+
type;
|
|
532
|
+
constructor(type, message) {
|
|
533
|
+
super(message);
|
|
534
|
+
this.type = type;
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
function createCollector(emitter, baseDirectory = ".") {
|
|
538
|
+
async function read(filePath) {
|
|
539
|
+
try {
|
|
540
|
+
return await readFile$1(filePath, "utf-8");
|
|
541
|
+
} catch (error) {
|
|
542
|
+
emitter.emit("collector:read-error", {
|
|
543
|
+
filePath,
|
|
544
|
+
error: new CollectError("Read", String(error))
|
|
545
|
+
});
|
|
546
|
+
return null;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
async function collectCollectionFile(collection, filePath) {
|
|
550
|
+
const file = await read(path.join(baseDirectory, collection.directory, filePath));
|
|
551
|
+
if (!file) return null;
|
|
552
|
+
try {
|
|
553
|
+
return {
|
|
554
|
+
data: await getParser(collection.parser).parse(file),
|
|
555
|
+
path: filePath
|
|
556
|
+
};
|
|
557
|
+
} catch (error) {
|
|
558
|
+
emitter.emit("collector:parse-error", {
|
|
559
|
+
filePath: path.join(collection.directory, filePath),
|
|
560
|
+
error: new CollectError("Parse", String(error))
|
|
561
|
+
});
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
async function collectSingletonFile(singleton) {
|
|
566
|
+
const file = await read(path.join(baseDirectory, singleton.filePath));
|
|
567
|
+
if (!file) {
|
|
568
|
+
if (singleton.optional) return null;
|
|
569
|
+
throw new CollectError("Read", `Singleton file not found at path: ${singleton.filePath}`);
|
|
570
|
+
}
|
|
571
|
+
try {
|
|
572
|
+
return {
|
|
573
|
+
data: await getParser(singleton.parser).parse(file),
|
|
574
|
+
path: path.basename(singleton.filePath)
|
|
575
|
+
};
|
|
576
|
+
} catch (error) {
|
|
577
|
+
emitter.emit("collector:parse-error", {
|
|
578
|
+
filePath: singleton.filePath,
|
|
579
|
+
error: new CollectError("Parse", String(error))
|
|
580
|
+
});
|
|
581
|
+
return null;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
async function collectFile(source, filePath) {
|
|
585
|
+
if (isSingleton(source)) return collectSingletonFile(source);
|
|
586
|
+
return collectCollectionFile(source, filePath);
|
|
587
|
+
}
|
|
588
|
+
function createIgnorePattern(collection) {
|
|
589
|
+
if (collection.exclude) if (Array.isArray(collection.exclude)) return collection.exclude;
|
|
590
|
+
else return [collection.exclude];
|
|
591
|
+
}
|
|
592
|
+
async function resolveCollection(collection) {
|
|
593
|
+
const collectionDirectory = path.join(baseDirectory, collection.directory);
|
|
594
|
+
const promises = (await glob(Array.isArray(collection.include) ? collection.include : [collection.include], {
|
|
595
|
+
cwd: collectionDirectory,
|
|
596
|
+
onlyFiles: true,
|
|
597
|
+
absolute: false,
|
|
598
|
+
ignore: createIgnorePattern(collection)
|
|
599
|
+
})).map((filePath) => collectCollectionFile(collection, posixToNativePath(filePath)));
|
|
600
|
+
const files = await Promise.all(promises);
|
|
601
|
+
return {
|
|
602
|
+
...collection,
|
|
603
|
+
files: files.filter(isDefined).sort(orderByPath)
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
async function resolveSingleton(singleton) {
|
|
607
|
+
const file = await collectSingletonFile(singleton);
|
|
608
|
+
return {
|
|
609
|
+
...singleton,
|
|
610
|
+
files: file ? [file] : []
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
async function collect(unresolvedCollections) {
|
|
614
|
+
const promises = unresolvedCollections.map((collection) => {
|
|
615
|
+
if (isSingleton(collection)) return resolveSingleton(collection);
|
|
616
|
+
return resolveCollection(collection);
|
|
617
|
+
});
|
|
618
|
+
return await Promise.all(promises);
|
|
619
|
+
}
|
|
620
|
+
return {
|
|
621
|
+
collect,
|
|
622
|
+
collectFile
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
//#endregion
|
|
627
|
+
//#region src/synchronizer.ts
|
|
628
|
+
function createSynchronizer(readCollectionFile, collections, baseDirectory = ".") {
|
|
629
|
+
function findCollections(filePath) {
|
|
630
|
+
const resolvedFilePath = path.resolve(filePath);
|
|
631
|
+
return collections.filter((collection) => {
|
|
632
|
+
if (isSingleton(collection)) return resolvedFilePath === path.resolve(baseDirectory, collection.filePath);
|
|
633
|
+
return resolvedFilePath.startsWith(path.resolve(baseDirectory, collection.directory));
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
function createRelativePath(collectionPath, filePath) {
|
|
637
|
+
const resolvedCollectionPath = path.resolve(baseDirectory, collectionPath);
|
|
638
|
+
let relativePath = path.resolve(filePath).slice(resolvedCollectionPath.length);
|
|
639
|
+
if (relativePath.startsWith(path.sep)) relativePath = relativePath.slice(path.sep.length);
|
|
640
|
+
return relativePath;
|
|
641
|
+
}
|
|
642
|
+
function resolve$1(filePath) {
|
|
643
|
+
return findCollections(filePath).map((collection) => {
|
|
644
|
+
return {
|
|
645
|
+
collection,
|
|
646
|
+
relativePath: isSingleton(collection) ? path.basename(collection.filePath) : createRelativePath(collection.directory, filePath)
|
|
647
|
+
};
|
|
648
|
+
}).filter(({ collection, relativePath }) => {
|
|
649
|
+
if (isSingleton(collection)) return true;
|
|
650
|
+
return picomatch.isMatch(relativePath, collection.include, {
|
|
651
|
+
windows: process.platform === "win32",
|
|
652
|
+
ignore: collection.exclude
|
|
653
|
+
});
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
function deleted(filePath) {
|
|
657
|
+
const resolvedCollections = resolve$1(filePath);
|
|
658
|
+
if (resolvedCollections.length === 0) return false;
|
|
659
|
+
let changed$1 = false;
|
|
660
|
+
for (const { collection, relativePath } of resolvedCollections) {
|
|
661
|
+
if (isSingleton(collection)) {
|
|
662
|
+
if (collection.files.length > 0) {
|
|
663
|
+
collection.files = [];
|
|
664
|
+
changed$1 = true;
|
|
665
|
+
}
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
668
|
+
const index = collection.files.findIndex((file) => file.path === relativePath);
|
|
669
|
+
if (collection.files.splice(index, 1).length > 0) changed$1 = true;
|
|
670
|
+
}
|
|
671
|
+
return changed$1;
|
|
672
|
+
}
|
|
673
|
+
async function changed(filePath) {
|
|
674
|
+
const resolvedCollections = resolve$1(filePath);
|
|
675
|
+
if (resolvedCollections.length === 0) return false;
|
|
676
|
+
let changed$1 = false;
|
|
677
|
+
for (const { collection, relativePath } of resolvedCollections) {
|
|
678
|
+
const index = collection.files.findIndex((file$1) => file$1.path === relativePath);
|
|
679
|
+
const file = await readCollectionFile(collection, relativePath);
|
|
680
|
+
if (file) {
|
|
681
|
+
changed$1 = true;
|
|
682
|
+
if (isSingleton(collection)) collection.files = [file];
|
|
683
|
+
else if (index === -1) {
|
|
684
|
+
collection.files.push(file);
|
|
685
|
+
collection.files.sort(orderByPath);
|
|
686
|
+
} else collection.files[index] = file;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return changed$1;
|
|
690
|
+
}
|
|
691
|
+
return {
|
|
692
|
+
deleted,
|
|
693
|
+
changed
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
|
|
620
697
|
//#endregion
|
|
621
698
|
//#region ../../node_modules/.pnpm/zod@4.1.11/node_modules/zod/v4/core/core.js
|
|
622
699
|
/** A special constant with type `never` */
|
|
@@ -1857,16 +1934,21 @@ function serialize(value) {
|
|
|
1857
1934
|
let serializedValue = "";
|
|
1858
1935
|
let counter = 0;
|
|
1859
1936
|
function handleImports(item) {
|
|
1860
|
-
if (item
|
|
1937
|
+
if (!item || typeof item !== "object") return;
|
|
1938
|
+
if (Array.isArray(item)) {
|
|
1939
|
+
item.forEach(handleImports);
|
|
1940
|
+
return;
|
|
1941
|
+
}
|
|
1942
|
+
Object.entries(item).forEach(([key, value$1]) => {
|
|
1861
1943
|
if (isImport(value$1)) {
|
|
1862
1944
|
counter++;
|
|
1863
1945
|
const variableName = `__v_${counter}`;
|
|
1864
1946
|
serializedValue += createImport(value$1, variableName);
|
|
1865
1947
|
item[key] = variableName;
|
|
1866
|
-
} else
|
|
1948
|
+
} else handleImports(value$1);
|
|
1867
1949
|
});
|
|
1868
1950
|
}
|
|
1869
|
-
value
|
|
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
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
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:
|
|
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
|
|
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)
|
|
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
|
|
2047
|
-
|
|
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
|
|
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
|
|
2153
|
+
for (const name of exports) content += `import ${name} from "./${name}.${extension}";\n`;
|
|
2056
2154
|
content += "\n";
|
|
2057
|
-
content += "export { " +
|
|
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 ${
|
|
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";
|
|
@@ -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) =>
|
|
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,
|
|
@@ -2292,4 +2394,4 @@ async function createInternalBuilder(initialConfiguration, baseDirectory, option
|
|
|
2292
2394
|
}
|
|
2293
2395
|
|
|
2294
2396
|
//#endregion
|
|
2295
|
-
export { CollectError, ConfigurationError, ConfigurationReloadError, TransformError, createBuilder, createDefaultImport, createInternalBuilder, createNamedImport, defineCollection, defineConfig, defineParser, skippedSymbol, suppressDeprecatedWarnings };
|
|
2397
|
+
export { CollectError, ConfigurationError, ConfigurationReloadError, TransformError, createBuilder, createDefaultImport, createInternalBuilder, createNamedImport, defineCollection, defineConfig, defineParser, defineSingleton, isSingleton, skippedSymbol, suppressDeprecatedWarnings };
|