@pixso/create-plugin
Version:
plugin template
325 lines (293 loc) • 8.6 kB
JavaScript
const fs = require("fs");
const path = require("path");
const prompts = require("prompts");
const fse = require("fs-extra");
const { blue, green, yellow, cyan, magenta, red, reset } = require("kolorist");
const argv = require("minimist")(process.argv.slice(2), {
string: ["_"],
boolean: ["help", "host", "force"],
alias: { h: "help", t: "template" }
});
if (argv.help) {
console.log(`
Usage:
npx @pixso/create-plugin <project-name> [options]
Options:
-t, --template <name> Specify framework variant (e.g., vue, vue-ts, react, react-ts, preact, preact-ts, svelte, svelte-ts)
--host Enable host script support (Only for private deployment clients/hwdc)
--force Overwrite target directory if it exists
-h, --help Display this help message
`);
process.exit(0);
}
const cwd = process.cwd();
const FRAMEWORKS = [
{
name: "vue",
display: "Vue",
color: green,
variants: [
{
name: "vue-ts",
display: "TypeScript",
color: blue,
},
{
name: "vue",
display: "JavaScript",
color: yellow,
},
],
},
{
name: "react",
display: "React",
color: cyan,
variants: [
{
name: "react-ts",
display: "TypeScript",
color: blue,
},
{
name: "react",
display: "JavaScript",
color: yellow,
},
],
},
{
name: "preact",
display: "Preact",
color: magenta,
variants: [
{
name: "preact-ts",
display: "TypeScript",
color: blue,
},
{
name: "preact",
display: "JavaScript",
color: yellow,
},
],
},
{
name: "svelte",
display: "Svelte",
color: red,
variants: [
{
name: "svelte-ts",
display: "TypeScript",
color: blue,
},
{
name: "svelte",
display: "JavaScript",
color: yellow,
},
],
},
];
const TEMPLATES = FRAMEWORKS.map(
(f) => (f.variants && f.variants.map((v) => v.name)) || [f.name]
).reduce((a, b) => a.concat(b), []);
function isEmpty(path) {
return fs.readdirSync(path).length === 0;
}
function isValidPackageName(projectName) {
return /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(
projectName
);
}
function toValidPackageName(projectName) {
return projectName
.trim()
.toLowerCase()
.replace(/\s+/g, "-")
.replace(/^[._]/, "")
.replace(/[^a-z\d\-~]+/g, "-");
}
function pkgFromUserAgent(userAgent) {
if (!userAgent) return undefined;
const pkgSpec = userAgent.split(" ")[0];
const pkgSpecArr = pkgSpec.split("/");
return {
name: pkgSpecArr[0],
version: pkgSpecArr[1],
};
}
async function init() {
let targetDir = argv._[0];
let argTemplate = argv.template || argv.t;
const defaultProjectName = !targetDir ? "pixso-plugin-project" : targetDir;
let result;
try {
result = await prompts(
[
{
type: targetDir ? null : "text",
name: "projectName",
message: "Project name:",
initial: defaultProjectName,
onState: (state) =>
(targetDir = state.value.trim() || defaultProjectName),
},
{
type: () =>
argv.force || !fs.existsSync(targetDir) || isEmpty(targetDir) ? null : "confirm",
name: "overwrite",
message: () =>
(targetDir === "."
? "Current directory"
: `Target directory "${targetDir}"`) +
` is not empty. Remove existing files and continue?`,
},
{
type: (_, { overwrite } = {}) => {
if (overwrite === false) {
throw new Error(red("✖") + " Operation cancelled");
}
return null;
},
name: "overwriteChecker",
},
{
type: () => (isValidPackageName(targetDir) ? null : "text"),
name: "packageName",
message: "Package name:",
initial: () => toValidPackageName(targetDir),
validate: (dir) =>
isValidPackageName(dir) || "Invalid package.json name",
},
{
type:
argTemplate && TEMPLATES.includes(argTemplate) ? null : "select",
name: "framework",
message:
typeof argTemplate === "string" && !TEMPLATES.includes(argTemplate)
? reset(
`"${argTemplate}" isn't a valid template. Please choose from below: `
)
: reset("Select a framework:"),
initial: 0,
choices: FRAMEWORKS.map((framework) => {
const frameworkColor = framework.color;
return {
title: frameworkColor(framework.display || framework.name),
value: framework,
};
}),
},
{
type: (framework) =>
framework && framework.variants ? "select" : null,
name: "variant",
message: reset("Select a variant:"),
choices: (framework) =>
framework.variants.map((variant) => {
const variantColor = variant.color;
return {
title: variantColor(variant.display || variant.name),
value: variant.name,
};
}),
},
],
{
onCancel: () => {
throw new Error(red("✖") + " Operation cancelled");
},
}
);
} catch (cancelled) {
console.log(cancelled.message);
return;
}
const overwrite = argv.force || result.overwrite;
const { packageName, variant } = result;
const containsHostScript = argv.host || false;
const root = path.join(cwd, targetDir);
if (overwrite) {
fse.emptyDirSync(root);
} else if (!fs.existsSync(root)) {
fs.mkdirSync(root);
}
argTemplate = variant || argTemplate;
const templateDir = path.join(__dirname, `template-${argTemplate}`);
fse.copySync(templateDir, root);
const pkg = require(path.join(root, "package.json"));
pkg.name = packageName || targetDir;
if (containsHostScript) {
// Update manifest.json to include host entry
const manifest = require(path.join(root, "manifest.json"));
manifest.main = {
sandbox: manifest.main,
host: "dist/host.js",
};
fs.writeFileSync(
path.join(root, "manifest.json"),
JSON.stringify(manifest, null, 2)
);
// Dynamically generate host script file
const hostExt = argTemplate.endsWith("-ts") ? "ts" : "js";
const hostContent = [
"/**",
" * Host script - Only available for private deployment clients.",
" * For more information: https://pixso.cn/developer/en/",
" */",
`hostApi.onMounted(() => {`,
` console.log("Host script has been mounted");`,
`});`,
"",
].join("\n");
fs.writeFileSync(path.join(root, `host.${hostExt}`), hostContent);
// Generate plugin.config to enable host compilation
const pluginConfigContent =
hostExt === "ts"
? [
"import { defineConfig } from '@pixso/plugin-cli';",
"",
"export default defineConfig({",
" host: './host.ts',",
"});",
"",
].join("\n")
: [
"const { defineConfig } = require('@pixso/plugin-cli');",
"",
"module.exports = defineConfig({",
" host: './host.js',",
"});",
"",
].join("\n");
fs.writeFileSync(
path.join(root, `plugin.config.${hostExt}`),
pluginConfigContent
);
}
fs.writeFileSync(
path.join(root, "package.json"),
JSON.stringify(pkg, null, 2)
);
const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent);
const pkgManager = pkgInfo ? pkgInfo.name : "npm";
console.log(`\nDone. Now run:\n`);
if (root !== cwd) {
console.log(` cd ${path.relative(cwd, root)}`);
}
switch (pkgManager) {
case "yarn":
console.log(" yarn");
console.log(" yarn dev");
break;
default:
console.log(` ${pkgManager} install`);
console.log(` ${pkgManager} run dev`);
break;
}
}
init();