@kevinmarrec/create-cloudstack-app
Version:
CLI that scaffolds an opinionated Bun & Vue fullstack application.
150 lines (141 loc) • 4.56 kB
JavaScript
// src/run.ts
import process from "node:process";
import { parseArgs } from "node:util";
import { cancel, confirm, intro, isCancel, log, note, outro, tasks, text } from "@clack/prompts";
import c from "ansis";
import { resolve as resolve2 } from "pathe";
import { x } from "tinyexec";
// package.json
var version = "1.0.0-rc.8";
// src/scaffold.ts
import { join } from "pathe";
import { glob } from "tinyglobby";
// src/utils/fs.ts
import fs from "node:fs/promises";
import { resolve } from "pathe";
var ignorePredicate = (filename) => [".git"].includes(filename);
async function empty(dir) {
for (const entry of await fs.readdir(dir)) {
if (ignorePredicate(entry)) {
continue;
}
await fs.rm(resolve(dir, entry), { recursive: true });
}
}
async function emptyCheck(path) {
return fs.readdir(path).then((files) => files.every(ignorePredicate)).catch(() => true);
}
async function exists(path) {
return fs.access(path).then(() => true).catch(() => false);
}
var fs_default = {
...fs,
empty,
emptyCheck,
exists
};
// src/scaffold.ts
async function scaffold(root) {
await fs_default.exists(root) ? await fs_default.empty(root) : await fs_default.mkdir(root);
await fs_default.cp(join(import.meta.dirname, "../template"), root, { recursive: true });
await fs_default.rename(join(root, "gitignore"), join(root, ".gitignore"));
const files = await glob("**/*.{json,ts,vue}", { cwd: root, absolute: true });
await Promise.all(files.map(async (file) => {
const content = await fs_default.readFile(file, "utf-8");
if (!content.includes("@kevinmarrec/cloudstack-")) {
return;
}
await fs_default.writeFile(
file,
file.includes("package.json") ? content.replace(/"(@kevinmarrec\/cloudstack-(.*))": "workspace:\*"/g, `"@cloudstack/$2": "npm:$1@^${version}"`) : content.replace(/@kevinmarrec\/cloudstack-(.*)/g, "@cloudstack/$1")
);
}));
}
// src/run.ts
function maybeCancel(value, options) {
if (isCancel(value) || options?.strict && !value) {
cancel("Operation cancelled");
process.exit(1);
}
}
async function run() {
const { values: options, positionals } = parseArgs({
args: process.argv.slice(2),
allowPositionals: true,
options: {
force: { type: "boolean", short: "f" },
help: { type: "boolean", short: "h" },
version: { type: "boolean", short: "v" }
}
});
if (options.help) {
process.stdout.write(`Usage: create-app [OPTIONS...] [DIRECTORY]
Options:
-f, --force Create the project even if the directory is not empty.
-h, --help Display this help message.
--version Display the version number of this CLI.
`);
process.exit(0);
}
if (options.version) {
process.stdout.write(`${version}
`);
process.exit(0);
}
process.stdout.write("\n");
intro(`Cloudstack ${c.dim(`v${version}`)}`);
let projectName = positionals[0] || await text({
message: "Project name",
placeholder: "my-app",
validate: (value) => {
if (!value.trim())
return "Project name cannot be empty";
}
});
maybeCancel(projectName);
projectName = projectName.trim();
const cwd = process.cwd();
const targetDir = resolve2(cwd, projectName);
if (!(await fs_default.emptyCheck(targetDir) || options.force)) {
await log.warn(`${targetDir === cwd ? "Current directory" : `Target directory ${c.blue(targetDir)}`} is not empty`);
const shouldOverwrite = await confirm({
message: "Remove existing files and continue?",
initialValue: true,
active: "Yes",
inactive: "No"
});
maybeCancel(shouldOverwrite, { strict: true });
}
await tasks([{
title: `Scaffolding project in ${c.blue(targetDir)}`,
task: async () => {
await scaffold(targetDir);
return `Scaffolded project in ${c.blue(targetDir)}`;
}
}]);
const shouldInstall = await confirm({
message: "Install dependencies?",
initialValue: true,
active: "Yes",
inactive: "No"
});
maybeCancel(shouldInstall);
await tasks([{
title: "Installing with bun",
enabled: shouldInstall,
task: async () => {
await x("bun", ["install", "--cwd", targetDir, "--force"]);
return "Installed with bun";
}
}]);
await note([
targetDir !== cwd && `cd ${c.reset.blue(projectName)}`,
`bun run dev`
].filter(Boolean).join("\n"), "Next steps");
await outro(`Problems? ${c.cyan("https://github.com/kevinmarrec/cloudstack/issues")}`);
}
// src/index.ts
run().catch((error) => {
console.error(error);
});