wxt
Version:
⚡ Next-gen Web Extension Framework
126 lines (125 loc) • 4.48 kB
JavaScript
import { pathExists } from "./utils/fs.mjs";
import { readdir, rename } from "node:fs/promises";
import path from "node:path";
import { consola as consola$1 } from "consola";
import { styleText } from "node:util";
import prompts from "prompts";
import { downloadTemplate } from "giget";
//#region src/core/initialize.ts
async function initialize(options) {
consola$1.info("Initializing new project");
const templates = await listTemplates();
const defaultTemplate = templates.find((template) => template.name === options.template?.toLowerCase().trim());
const input = await prompts([
{
name: "directory",
type: () => options.directory == null ? "text" : void 0,
message: "Project Directory",
initial: options.directory
},
{
name: "template",
type: () => defaultTemplate == null ? "select" : void 0,
message: "Choose a template",
choices: templates.map((template) => ({
title: TEMPLATE_COLORS[template.name] ? styleText(TEMPLATE_COLORS[template.name], template.name) : template.name,
value: template
}))
},
{
name: "packageManager",
type: () => options.packageManager == null ? "select" : void 0,
message: "Package Manager",
choices: [
{
title: styleText("magenta", "bun"),
value: "bun"
},
{
title: styleText("red", "npm"),
value: "npm"
},
{
title: styleText("yellow", "pnpm"),
value: "pnpm"
},
{
title: styleText("cyan", "yarn"),
value: "yarn"
}
]
}
], { onCancel: () => process.exit(1) });
input.directory ||= options.directory || ".";
input.template ??= defaultTemplate;
input.packageManager ??= options.packageManager;
if (await pathExists(input.directory)) {
if (!((await readdir(input.directory)).filter((dir) => dir !== ".git").length === 0)) {
consola$1.error(`The directory ${path.resolve(input.directory)} is not empty. Aborted.`);
process.exit(1);
}
}
await cloneProject(input);
const cdPath = path.relative(process.cwd(), path.resolve(input.directory));
console.log();
consola$1.log(`✨ WXT project created with the ${TEMPLATE_COLORS[input.template.name] ? styleText(TEMPLATE_COLORS[input.template.name], input.template.name) : input.template.name} template.`);
console.log();
consola$1.log("Next steps:");
let step = 0;
if (cdPath !== "") consola$1.log(` ${++step}.`, styleText("cyan", `cd ${cdPath}`));
consola$1.log(` ${++step}.`, styleText("cyan", `${input.packageManager} install`));
console.log();
}
async function listTemplates() {
return (await listTemplatesUngh().catch((err) => {
consola$1.debug("Failed to load templates via ungh:", err);
return listTemplatesGithub();
})).sort((l, r) => {
const diff = (TEMPLATE_SORT_WEIGHT[l.name] ?? Number.MAX_SAFE_INTEGER) - (TEMPLATE_SORT_WEIGHT[r.name] ?? Number.MAX_SAFE_INTEGER);
if (diff !== 0) return diff;
return l.name.localeCompare(r.name);
});
}
async function listTemplatesUngh() {
const res = await fetch("https://ungh.cc/repos/wxt-dev/wxt/files/main");
if (res.status !== 200) throw Error(`Request failed with status ${res.status} ${res.statusText}: ${await res.text()}`);
return (await res.json()).files.map((item) => item.path.match(/templates\/(.+)\/package\.json/)?.[1]).filter((name) => name != null).map((name) => ({
name,
path: `templates/${name}`
}));
}
async function listTemplatesGithub() {
const res = await fetch(`https://api.github.com/repos/${REPO}/contents/templates`, { headers: { Accept: "application/vnd.github+json" } });
if (res.status !== 200) throw Error(`Request failed with status ${res.status} ${res.statusText}: ${await res.text()}`);
return await res.json();
}
async function cloneProject({ directory, template }) {
const { createSpinner } = await import("nanospinner");
const spinner = createSpinner("Downloading template").start();
try {
await downloadTemplate(`gh:${REPO}/${template.path}`, {
dir: directory,
force: true
});
await rename(path.join(directory, "_gitignore"), path.join(directory, ".gitignore")).catch((err) => consola$1.warn("Failed to move _gitignore to .gitignore:", err));
spinner.success();
} catch (err) {
spinner.error();
throw Error(`Failed to setup new project: ${JSON.stringify(err, null, 2)}`);
}
}
const TEMPLATE_COLORS = {
vanilla: "blue",
vue: "green",
react: "cyan",
svelte: "red",
solid: "blue"
};
const TEMPLATE_SORT_WEIGHT = {
vanilla: 0,
vue: 1,
react: 2
};
const REPO = "wxt-dev/wxt";
//#endregion
export { initialize };