astro-sveltia-cms-loader
Version:
Astro loader for loading content from Sveltia CMS.
312 lines (306 loc) • 9.18 kB
JavaScript
import { writeFileSync, readFileSync } from 'node:fs';
import yaml from 'yaml';
import { z } from 'astro/zod';
import { glob } from 'astro/loaders';
import { parse, resolve } from 'node:path';
// src/integration.ts
var DEFAULT_CONFIG_PATH = "./public/admin/config.yaml";
var createPlugin = (globalOptions = {}) => ({
name: "astro-sveltia-cms-loader",
hooks: {
"astro:config:setup": ({ config, createCodegenDir }) => {
const codegenDir = createCodegenDir();
const globalOptionsFile = new URL("global-options.json", codegenDir);
const newGlobalOptions = {
configPath: DEFAULT_CONFIG_PATH,
repositoryRoot: config.root,
...globalOptions
};
writeFileSync(globalOptionsFile, JSON.stringify(newGlobalOptions), "utf-8");
}
}
});
var integration_default = createPlugin;
var getGlobalOptions = () => {
try {
const globalOptionsFile = resolve(
process.cwd(),
".astro/integrations/astro-sveltia-cms-loader/global-options.json"
);
const globalOptions = JSON.parse(readFileSync(globalOptionsFile, "utf-8"));
return globalOptions;
} catch (error) {
throw new Error(`Failed to load global options file: ${error}`);
}
};
var getCollectionGlobLoad = async (collection, context) => {
const globalOptions = getGlobalOptions();
if (collection.files) {
const filePaths = collection.files.map(({ file }) => file);
const pattern = filePaths.length > 1 ? `{${filePaths.join(",")}}` : filePaths[0];
const base = new URL(collection.folder ?? ".", globalOptions.repositoryRoot);
const generateId = ({ entry }) => parse(entry).name;
return glob({
pattern,
base,
generateId
}).load(context);
}
if (collection.folder) {
const extension = collection.extension ?? "md";
const wildcardPath = (collection.path ?? "{{slug}}").replace(/\{\{[^}]+\}\}/g, "*");
const pattern = `${wildcardPath}.${extension}`;
const base = new URL(collection.folder, globalOptions.repositoryRoot);
return glob({ pattern, base }).load(context);
}
throw new Error(`Collection "${collection.name}" is missing "files" or "folder" property.`);
};
var generateCodePropSchema = (field) => {
let schema = z.string();
if (field.required !== false) {
schema = schema.min(1, { message: "This field is required" });
return schema;
}
if (field.required === false) {
return schema.optional();
}
return schema;
};
var codeWidget = (field) => {
if (field.output_code_only) return generateCodePropSchema(field);
return z.object({
code: generateCodePropSchema(field),
lang: z.string()
});
};
var colorWidget = (field) => {
let schema = z.string();
if (field.required !== false) {
schema = schema.min(1, { message: "This field is required" });
return schema;
}
if (field.required === false) {
return schema.optional();
}
return schema;
};
var dateTimeWidget = (field) => {
const schema = z.coerce.string();
if (field.required === false) {
return schema.optional();
}
return schema;
};
var fileWidget = (field) => {
let schema = z.string();
if (field.required !== false) {
schema = schema.min(1, { message: "This field is required" });
return schema;
}
if (field.required === false) {
return schema.optional();
}
return schema;
};
var imageWidget = (field) => {
let schema = z.string();
if (field.required !== false) {
schema = schema.min(1, { message: "This field is required" });
return schema;
}
if (field.required === false) {
return schema.optional();
}
return schema;
};
var listWidget = (field) => {
let schema = z.string().array();
if (field.field) schema = generateFieldSchema(field.field).array();
if (field.fields) schema = generateSchema(field.fields).array();
if (field.min) schema = schema.min(field.min);
if (field.max) schema = schema.max(field.max);
return field.required === false ? schema.optional() : schema.nonempty();
};
var numberWidget = (field) => {
let schema = z.number();
if (field.value_type === "int") schema = schema.int();
if (field.min) schema = schema.min(field.min);
if (field.max) schema = schema.max(field.max);
if (field.step) schema = schema.step(field.step);
if (field.required === false) {
return schema.optional().nullable();
}
return schema;
};
// src/factory/widgets/object.ts
var objectWidget = (field) => {
const schema = generateSchema(field.fields);
if (field.required === false) {
return schema.optional().nullable();
}
return schema;
};
var selectWidget = (field) => {
let schema = field.multiple ? z.array(z.string()) : z.string();
if (field.required !== false) {
schema = schema.min(1, { message: "This field is required" });
return schema;
}
if (field.required === false) {
return schema.optional();
}
return schema;
};
var stringWidget = (field) => {
let schema = z.string();
if (field.type === "email") schema = schema.email();
if (field.type === "url") schema = schema.url();
if (field.pattern) {
const [regex, message] = field.pattern;
schema = schema.regex(new RegExp(regex), { message });
}
if (field.minlength) schema = schema.min(field.minlength);
if (field.maxlength) schema = schema.max(field.maxlength);
if (field.required !== false) {
schema = schema.min(1, { message: "This field is required" });
return schema;
}
if (field.required === false) {
return schema.optional();
}
return schema;
};
var textWidget = (field) => {
let schema = z.string();
if (field.pattern) {
const [regex, message] = field.pattern;
schema = schema.regex(new RegExp(regex), { message });
}
if (field.minlength) schema = schema.min(field.minlength);
if (field.maxlength) schema = schema.max(field.maxlength);
if (field.required !== false) {
schema = schema.min(1, { message: "This field is required" });
return schema;
}
if (field.required === false) {
return schema.optional();
}
return schema;
};
// src/factory/index.ts
var generateFieldSchema = (field) => {
let schema = z.any();
switch (field.widget) {
case "boolean": {
schema = z.boolean();
break;
}
case "code": {
schema = codeWidget(field);
break;
}
case "color": {
schema = colorWidget(field);
break;
}
case "datetime": {
schema = dateTimeWidget(field);
break;
}
case "file": {
schema = fileWidget(field);
break;
}
case "image": {
schema = imageWidget(field);
break;
}
case "list": {
schema = listWidget(field);
break;
}
case "number": {
schema = numberWidget(field);
break;
}
case "object": {
schema = objectWidget(field);
break;
}
case "select": {
schema = selectWidget(field);
break;
}
case "string": {
schema = stringWidget(field);
break;
}
case "text": {
schema = textWidget(field);
break;
}
}
return schema;
};
var generateSchema = (fields) => {
const schemaEntries = [];
for (const field of fields) {
if (field.name === "_discriminator") continue;
schemaEntries.push([field.name, generateFieldSchema(field)]);
}
return z.object(Object.fromEntries(schemaEntries));
};
// src/loader/index.ts
var getCollection = (options) => {
const globalOptions = getGlobalOptions();
const configPath = options.overrides?.configPath || globalOptions?.configPath;
const configFile = readFileSync(configPath, "utf-8");
const { collections: parsedCollections } = yaml.parse(configFile);
const collection = parsedCollections.find(({ name }) => name === options.collectionName);
if (!collection) {
throw new Error(`Collection "${options.collectionName}" not found in CMS config`);
}
return collection;
};
var collectionLoader = (options) => {
const collection = getCollection(options);
return {
name: "sveltia-cms-collection-loader",
load: async (context) => {
try {
const globLoad = options.overrides?.glob ? glob({ ...options.overrides.glob }).load(context) : await getCollectionGlobLoad(collection, context);
return globLoad;
} catch (error) {
const errorMessage = error.message;
context.logger.error(errorMessage);
}
},
schema: () => {
const { folder, fields, files } = collection;
let schema = void 0;
if (folder) {
schema = generateSchema(fields);
}
if (files) {
const filesUnion = files.map((file) => {
return z.object({
_discriminator: z.literal(file.name),
...generateSchema(file.fields).shape
});
});
if (filesUnion.length === 0) throw new Error("No files found in collection");
if (filesUnion.length > 1) {
schema = z.discriminatedUnion(
"_discriminator",
filesUnion
);
} else {
schema = filesUnion[0];
}
}
if (!schema) throw new Error("No schema generated");
return schema;
}
};
};
export { collectionLoader, integration_default as default };