///
import {
defineCollection as defineNative,
type BaseSchema,
type SchemaContext,
} from 'astro:content';
import { z, type ZodIntersection } from 'astro/zod';
export type { BaseSchema };
// Copied from Starlight because Astro core doesn't export their definitions :(
// https://github.com/withastro/starlight/blob/95ace6d51c7bf631863b06ee46bbc9b6dff6828a/packages/starlight/schema.ts#L116-L123
/** Type that extends a collection's default schema with an optional, user-defined schema. */
export type ExtendedSchema = [
T,
] extends [never]
? S
: T extends BaseSchema
? ZodIntersection
: S;
type CollectionConfig = ReturnType>;
export interface CollectionExtensionOptions {
extends?: E | ((context: SchemaContext) => E);
}
export type ExtendedCollection =
ExtendedSchema extends BaseSchema
? CollectionConfig>
: CollectionConfig;
export type FancyCollection = (
options?: CollectionExtensionOptions
) => ExtendedCollection;
/**
* Define a collection from an integration that can be extended by users of the integration.
*
* Also known as a FancyCollection 💅
*/
export function defineCollection(
config: CollectionConfig
): FancyCollection {
const definedConfig = defineNative(config);
const fn = (
options?: CollectionExtensionOptions
): ExtendedCollection => {
const fancyMarker = {
[FANCY_COLLECTION_MARKER]: fn,
};
if (options?.extends === undefined)
return Object.assign(definedConfig as ExtendedCollection, fancyMarker);
// Make TS not forget about type narrowing;
const { extends: extendSchema } = options;
const config = {
...definedConfig,
schema: (context) => {
const userSchema =
typeof extendSchema === 'function' ? extendSchema(context) : extendSchema;
const baseSchema =
typeof definedConfig.schema === 'function'
? definedConfig.schema(context)
: definedConfig.schema;
return baseSchema === undefined ? userSchema : z.intersection(baseSchema, userSchema);
},
} as ExtendedCollection;
return Object.assign(config, fancyMarker);
};
return Object.assign(fn, {
[FANCY_COLLECTION_MARKER]: true,
});
}
const FANCY_COLLECTION_MARKER = Symbol('@inox-tools/content-utils/fancyCollection');
type DerivedCollection = {
[FANCY_COLLECTION_MARKER]: FancyCollection;
};
function isDerivedCollection(something: any): something is DerivedCollection {
return typeof something[FANCY_COLLECTION_MARKER] === 'function';
}
/**
* Guard checking that a value is a FancyCollection.
*/
export function isFancyCollection(something: any): something is FancyCollection {
return something[FANCY_COLLECTION_MARKER] === true;
}
/**
* Extract the original FancyCollection from a value, if it was derived from one.
*
* If the value was not derived from a FancyCollection, return null;
*/
export function tryGetOriginalFancyCollection(something: any): FancyCollection | null {
return isDerivedCollection(something) ? something[FANCY_COLLECTION_MARKER] : null;
}