UNPKG

create-fff

Version:

Create FFF-related configuration files.

399 lines (386 loc) 9.65 kB
#!/usr/bin/env node var __defProp = Object.defineProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; // src/index.ts import { cancel as cancel3, intro, isCancel as isCancel3, outro as outro2, select } from "@clack/prompts"; import minimist from "minimist"; import color2 from "picocolors"; // src/netlify-cms.ts import { access, constants, mkdir, writeFile } from "node:fs/promises"; import { parse } from "node:path"; import { cancel as cancel2, confirm as confirm2, isCancel as isCancel2, note as note2, outro, text as text2 } from "@clack/prompts"; import color from "picocolors"; import { stringify } from "yaml"; // package.json var version = "1.0.0"; // src/lib/netlify-cms/config.ts import { exec } from "node:child_process"; import { promisify } from "node:util"; // src/lib/netlify-cms/collections.ts var collections_exports = {}; __export(collections_exports, { article: () => article, note: () => note, photo: () => photo }); // src/lib/netlify-cms/fields.ts var type = (type2) => ({ default: type2, label: "Post Type", name: "type", widget: "hidden" }); var common = [ { label: "Created on", name: "created", widget: "datetime" }, { label: "Updated on", name: "updated", widget: "datetime" }, { label: "Published on", name: "published", widget: "datetime" }, { label: "Tags", name: "tags", widget: "list" }, { label: "Flags", name: "flags", widget: "list" } ]; var title = { label: "Title", name: "title", widget: "string" }; var summary = { label: "Summary", name: "summary", widget: "text" }; var image = ({ featured, object_media }) => ({ label: featured ? "Featured Image" : "Photo", name: "image", ...object_media ? { fields: [ { allow_multiple: false, label: "Source", name: "src", widget: "image" }, { label: "Alternate Text", name: "alt", widget: "string" }, { label: "Figure Caption", name: "figcaption", widget: "string" } ], max: 1, widget: "list" } : { allow_multiple: false, widget: "image" } }); var content = { label: "Content", name: "body", widget: "markdown" }; // src/lib/netlify-cms/collections.ts var common2 = (options) => ({ create: true, extension: "md", // TODO: media_folder // TODO: public_folder filter: options.filter ? { field: "type", value: options.type } : void 0, folder: options.folder, format: "yaml-frontmatter", path: options.path.replace("{{type}}", options.type), slug: options.slug.replace("{{type}}", options.type) }); var article = (options) => ({ label: "Articles", label_singular: "Article", name: "article", ...common2({ ...options, type: "article" }), fields: [ ...common, type("article"), title, summary, image({ featured: true, object_media: options.object_media }), content ] }); var note = (options) => ({ label: "Notes", label_singular: "Note", name: "note", ...common2({ ...options, type: "note" }), fields: [ ...common, type("note"), summary, content ] }); var photo = (options) => ({ label: "Photos", label_singular: "Photo", name: "photo", ...common2({ ...options, type: "photo" }), fields: [ ...common, type("photo"), summary, image({ featured: false, object_media: options.object_media }), content ] }); // src/lib/netlify-cms/config.ts var config = async (options) => ({ backend: { branch: await promisify(exec)("git branch --show-current").then((response) => response.stdout.trim()) ?? "main", name: "git-gateway" }, collections: options.collections.map( (collection) => collections_exports[collection](options) ), media_folder: options.media_folder, public_folder: options.public_folder }); // src/lib/netlify-cms/options.ts import { cancel, confirm, isCancel, multiselect, text } from "@clack/prompts"; var fillOptions = async (options) => { if (!options.collections) { options.collections = await multiselect({ cursorAt: "note", initialValues: ["article"], message: "Choose the type of post you want:", options: [ { label: "Article", value: "article" }, { label: "Note", value: "note" }, { label: "Photo", value: "photo" } ], required: true }); } if (isCancel(options.collections)) { cancel("Operation cancelled"); return process.exit(0); } if (!options.object_media) { options.object_media = await confirm({ initialValue: false, message: "Do you want to use Object Media?" }); } if (isCancel(options.object_media)) { cancel("Operation cancelled"); return process.exit(0); } if (!options.media_folder) { options.media_folder = await text({ message: "media_folder", placeholder: "public/uploads", validate: (value) => !value && "Please enter a path." }); } if (isCancel(options.media_folder)) { cancel("Operation cancelled"); return process.exit(0); } if (!options.public_folder) { options.public_folder = await text({ message: "public_folder", placeholder: "/uploads", validate: (value) => !value && "Please enter a path." }); } if (isCancel(options.public_folder)) { cancel("Operation cancelled"); return process.exit(0); } if (!options.folder) { options.folder = await text({ message: "Where do you want to use as the posts root?", placeholder: "src/posts", validate: (value) => !value && "Please enter a path." }); } if (isCancel(options.folder)) { cancel("Operation cancelled"); return process.exit(0); } if (!options.path) { options.path = await text({ initialValue: "{{type}}/{{slug}}/index", message: "path", placeholder: "{{type}}/{{slug}}/index", validate: (value) => !value && "Please enter a path." }); } if (isCancel(options.path)) { cancel("Operation cancelled"); return process.exit(0); } if (!options.slug) { options.slug = await text({ initialValue: "{{type}}/{{slug}}", message: "slug", placeholder: "{{type}}/{{slug}}", validate: (value) => !value && "Please enter a path." }); } if (isCancel(options.slug)) { cancel("Operation cancelled"); return process.exit(0); } return options; }; // src/lib/netlify-cms/presets.ts var presets_exports = {}; __export(presets_exports, { urara: () => urara }); var urara = { argv: { "config-path": "./urara/admin/config.yml" }, options: { filter: true, folder: "urara", media_folder: "urara/uploads", path: "{{slug}}/+page", public_folder: "/uploads", slug: "{{slug}}" } }; // src/netlify-cms.ts var netlifyCMS = async (argv) => { argv = { ...argv, ...presets_exports[argv.preset]?.argv }; const path = argv["config-path"] ?? await text2({ message: "Where should we create your Netlify CMS config?", placeholder: "./public/admin/config.yml", validate: (value) => { if (!value) return "Please enter a path."; if (value[0] !== ".") return "Please enter a relative path."; } }); if (isCancel2(path)) { cancel2("Operation cancelled"); return process.exit(0); } await access(path, constants.F_OK).then(async () => { const check = await confirm2({ message: color.yellow( `${path} already has files. Do you want to continue?` ) }); if (isCancel2(check) || check === false) { cancel2("Operation cancelled"); return process.exit(0); } }).catch(console.error); const options = await fillOptions(presets_exports[argv.preset]?.options); if (options.path.includes("{{type}}")) { options.filter = false; } else { const filter = await confirm2({ message: "Do you want to enable post type filtering?" // '\nWhen not using the {{type}} folder,\nit will correctly categorize posts;\nhowever, it will cause posts that do not have a type value set to not be displayed.\nhttps://fff.js.org' }); if (isCancel2(filter)) { cancel2("Operation cancelled"); return process.exit(0); } options.filter = filter; } await mkdir(parse(path).dir, { recursive: true }).then( async () => await writeFile( path, /** @see {@link https://github.com/decaporg/decap-cms/issues/1342} */ stringify(await config(options), { aliasDuplicateObjects: false }) ) ).catch(console.error); note2(`FFF Flavored Frontmatter Version ${version}`); outro("You're all set! "); }; // src/index.ts var main = async () => { console.clear(); intro(color2.black(color2.bgCyan(" create-fff "))); const argv = minimist(process.argv.slice(2)); const type2 = argv._[0] ?? await select({ message: "Choose what you need to create:", options: [ { hint: "config.yml", label: "Netlify CMS Config (WIP)", value: "netlify-cms" }, { hint: "contentlayer.config.ts", label: "Contentlayer Schema (TODO)", value: "contentlayer" } ] }); if (isCancel3(type2)) { cancel3("Operation cancelled"); return process.exit(0); } switch (type2) { case "netlify-cms": case "decap-cms": { await netlifyCMS(argv).catch(console.error); process.exit(0); } default: { outro2(type2); process.exit(0); } } }; main().catch(console.error);