mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-07-05 09:21:39 +02:00
221 lines
5.6 KiB
JavaScript
221 lines
5.6 KiB
JavaScript
import { writeFileSync } from "fs";
|
|
import { join } from "path";
|
|
import { createServer } from "vite";
|
|
import zod from "zod";
|
|
|
|
const {
|
|
ZodDefault,
|
|
ZodObject,
|
|
ZodOptional,
|
|
ZodString,
|
|
ZodEffects,
|
|
ZodEnum
|
|
} = zod;
|
|
|
|
/**
|
|
* Process the Zod field
|
|
* @param {ZodTypeAny} field
|
|
* @param {string} defaultValue
|
|
* @returns
|
|
*/
|
|
function getField(field, defaultValue = undefined) {
|
|
let isOptional = false;
|
|
|
|
// Handle various type transformations and assignments
|
|
if (field instanceof ZodOptional) {
|
|
isOptional = true;
|
|
const type = field.unwrap();
|
|
|
|
return getField(type, isOptional, defaultValue)
|
|
}
|
|
|
|
if (field instanceof ZodDefault) {
|
|
// https://github.com/colinhacks/zod/blob/master/README.md#default
|
|
isOptional = true;
|
|
defaultValue = field.parse(undefined);
|
|
// https://github.com/sachinraja/zod-to-ts/blob/main/src/index.ts
|
|
const type = field._def.innerType;
|
|
return getField(type, isOptional, defaultValue)
|
|
}
|
|
|
|
return {
|
|
type: field,
|
|
isOptional,
|
|
defaultValue
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate the field information
|
|
* @param {string} name
|
|
* @param {ZodTypeAny} type
|
|
* @returns
|
|
*/
|
|
function generateFieldInfo(name, type) {
|
|
let description = type.description;
|
|
let defaultValue = undefined;
|
|
|
|
const {
|
|
type: fieldType,
|
|
isOptional: isFieldOptional,
|
|
defaultValue: fieldDefaultValue
|
|
} = getField(type, defaultValue);
|
|
|
|
const fieldInfo = {
|
|
name: name,
|
|
description: description,
|
|
defaultValue: fieldDefaultValue,
|
|
type: fieldType._def.typeName,
|
|
required: !isFieldOptional,
|
|
};
|
|
|
|
if (fieldType instanceof ZodObject) {
|
|
const subFields = extractFieldInfoFromShape(fieldType);
|
|
|
|
fieldInfo.fields = subFields;
|
|
}
|
|
|
|
if (fieldType instanceof ZodEffects) {
|
|
fieldInfo.type = fieldType.sourceType().fmFieldType;
|
|
}
|
|
|
|
if (fieldType instanceof ZodEnum) {
|
|
fieldInfo.options = fieldType.options;
|
|
}
|
|
|
|
if (fieldType instanceof ZodString) {
|
|
// https://github.com/StefanTerdell/zod-to-json-schema/blob/master/src/parsers/string.ts#L45
|
|
if (fieldType._def.checks && fieldType._def.checks.length > 0) {
|
|
const check = fieldType._def.checks.pop();
|
|
fieldInfo.type = check.kind;
|
|
}
|
|
}
|
|
|
|
return fieldInfo;
|
|
}
|
|
|
|
/**
|
|
* Parse the scheme into an array of fields
|
|
* @param {ZodTypeAny} type
|
|
* @returns
|
|
*/
|
|
function extractFieldInfoFromShape(type) {
|
|
if (type instanceof ZodOptional) {
|
|
type = type.unwrap();
|
|
}
|
|
|
|
if (!(type instanceof ZodObject)) {
|
|
// Return an empty array if the type is not of the expected type
|
|
return [];
|
|
}
|
|
|
|
// Iterate through the shape properties
|
|
// https://github.com/sachinraja/zod-to-ts/blob/1389b33557bcca8a02da66cd5c48efbe7579720c/src/index.ts#L134
|
|
const properties = Object.entries(type._def.shape());
|
|
const fieldInfoList = properties.map(([fieldName, fieldShape]) => {
|
|
return generateFieldInfo(fieldName, fieldShape);
|
|
});
|
|
|
|
return fieldInfoList;
|
|
}
|
|
|
|
/**
|
|
* Process each content collection
|
|
* @param {*} collections
|
|
* @returns
|
|
*/
|
|
function processCollection(collections) {
|
|
if (!Array.isArray(collections)) {
|
|
return [];
|
|
}
|
|
|
|
return collections.map(([name, collection]) => {
|
|
const schema = typeof collection.schema === "function" ? collection.schema({
|
|
image() {
|
|
const field = zod.string();
|
|
field.fmFieldType = "image";
|
|
return field;
|
|
}
|
|
}) : collection.schema;
|
|
|
|
return {
|
|
name,
|
|
type: collection.type || "content",
|
|
fields: extractFieldInfoFromShape(schema),
|
|
};
|
|
});
|
|
}
|
|
|
|
/**
|
|
* More info: https://vitejs.dev/guide/api-plugin.html#virtual-modules-convention
|
|
* @returns
|
|
*/
|
|
const astroContentModulePlugin = () => {
|
|
const astroContent = "astro:content";
|
|
const astroContentMarker = `\0${astroContent}`;
|
|
|
|
return {
|
|
name: "astro-content-collections",
|
|
resolveId(importee) {
|
|
if (importee === astroContent) {
|
|
return astroContentMarker;
|
|
}
|
|
},
|
|
load(id) {
|
|
if (id === astroContentMarker) {
|
|
// https://github.com/withastro/astro/blob/defab70cb2a0c67d5e9153542490d2749046b151/packages/astro/content-module.template.mjs#L12
|
|
return `
|
|
export { z } from 'astro/zod';
|
|
// Reference: /node_modules/astro/content-module.template.mjs
|
|
export function defineCollection(config) {
|
|
if (!config.type) config.type = 'content';
|
|
return config;
|
|
};
|
|
`;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Start of the Astro Collections script
|
|
*/
|
|
(async () => {
|
|
const configPath = process.argv[2];
|
|
if (!configPath || typeof configPath !== "string") {
|
|
console.log("No config path provided");
|
|
process.exit(1);
|
|
}
|
|
|
|
// https://vitejs.dev/guide/ssr.html#setting-up-the-dev-server
|
|
// https://github.com/withastro/astro/blob/defab70cb2a0c67d5e9153542490d2749046b151/packages/astro/src/core/config/vite-load.ts#L8
|
|
const server = await createServer({
|
|
server: {
|
|
middlewareMode: true,
|
|
hmr: false
|
|
},
|
|
optimizeDeps: {
|
|
disabled: true,
|
|
},
|
|
appType: "custom",
|
|
plugins: [astroContentModulePlugin()]
|
|
});
|
|
|
|
try {
|
|
// https://github.dev/withastro/astro/blob/defab70cb2a0c67d5e9153542490d2749046b151/packages/astro/src/content/utils.ts#L334
|
|
const contentModule = await server.ssrLoadModule(configPath)
|
|
|
|
const collections = Object.entries(contentModule.collections ?? {});
|
|
const processedCollections = processCollection(collections);
|
|
|
|
writeFileSync(join(process.cwd(), `./.frontmatter/temp/astro.collections.json`), JSON.stringify(processedCollections));
|
|
|
|
console.log("Collections generated successfully");
|
|
process.exit(0);
|
|
} catch (error) {
|
|
console.log(error.message);
|
|
process.exit(1);
|
|
} finally {
|
|
await server.close();
|
|
}
|
|
})(); |