UNPKG

@refastdev/create-refast-app

Version:
264 lines (256 loc) 8.88 kB
#!/usr/bin/env node "use strict"; const prompts = require("@inquirer/prompts"); const fs = require("fs"); const path = require("path"); const tsDeepmerge = require("ts-deepmerge"); const isbinaryfile = require("isbinaryfile"); const fileForeach = (dirPath, callback, rootPath) => { if (rootPath == null) { rootPath = ""; } const files = fs.readdirSync(dirPath); for (let i = 0; i < files.length; i++) { const name = files[i]; if (name == null) continue; const fullPath = path.join(dirPath, name); const relativePath = path.join(rootPath, name).replace(/\\/g, "/"); const stat = fs.lstatSync(fullPath); if (stat.isDirectory()) { fileForeach(fullPath, callback, relativePath); } else if (stat.isFile()) { callback(relativePath, stat); } } }; const dirCopy = (sourcePath, targetPath, writeFile) => { fileForeach(sourcePath, (relativePath) => { const filePath = path.join(sourcePath, relativePath); const targetFilePath = path.join(targetPath, relativePath); if (!writeFile(filePath, targetFilePath, relativePath)) { const dir = path.dirname(targetFilePath); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } fs.copyFileSync(filePath, targetFilePath); } }); }; const isBinaryFile = (filePath) => { return isbinaryfile.isBinaryFileSync(filePath); }; const webDirMap = { electron: { webDir: "src/renderer" } }; const REPLACE_REG = /{{PROJECT_NAME}}/g; const mergeJson = (srcPath, toPath) => { const stringify = require("json-stable-stringify"); const newJson = JSON.parse(fs.readFileSync(srcPath, { encoding: "utf8" })); const baseJson = JSON.parse(fs.readFileSync(toPath, { encoding: "utf8" })); const newObject = tsDeepmerge.merge(baseJson, newJson); const newObjectJson = `${stringify(newObject, { cmp: (a, b) => { const aType = typeof a.value; const bType = typeof b.value; if (aType !== "object" && bType === "object") { return -1; } if (aType === "object" && bType !== "object") { return 1; } return a.key.localeCompare(b.key); }, space: 2 })} `; fs.writeFileSync(toPath, newObjectJson, { encoding: "utf8" }); }; const createOtherFile = async (projectPath) => { const gitignore = `logs *.log* node_modules .eslintcache # OSX .DS_Store dist out build app .idea package-lock.json pnpm-lock.yaml pnpm-lock.json *.tsbuildinfo `; const gitignorePath = path.join(projectPath, ".gitignore"); fs.writeFileSync(gitignorePath, gitignore, { encoding: "utf8" }); }; const mergeCopy = async (templatePath, projectPath, webProjectPath) => { dirCopy(templatePath, projectPath, (srcPath, toPath, relativePath) => { if (srcPath.endsWith("package.json") || srcPath.endsWith("tsconfig.json") || srcPath.endsWith("tsconfig.web.json") || srcPath.endsWith("tsconfig.node.json")) { const dir = path.dirname(toPath); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } if (fs.existsSync(toPath)) { mergeJson(srcPath, toPath); } return true; } if (srcPath.replace(/\\/g, "/").startsWith(`${templatePath.replace(/\\/g, "/")}/src/`)) { toPath = path.join(webProjectPath, relativePath); const dir = path.dirname(toPath); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } fs.copyFileSync(srcPath, toPath); return true; } return false; }); }; const replaceCopy = async (srcDir, targetDir, reg, replaceName) => { dirCopy(srcDir, targetDir, (srcPath, toPath) => { if (!isBinaryFile(srcPath)) { const dir = path.dirname(toPath); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } const data = fs.readFileSync(srcPath, { encoding: "utf8" }); const newData = data.replace(reg, replaceName); fs.writeFileSync(toPath, newData, { encoding: "utf8" }); return true; } return false; }); }; const mergeWebProject = async (templatePath, projectPath, webTargetProjectPath, options) => { const frameworkPath = path.resolve( templatePath, `diff-merge/${options.framework}-${options.script}` ); await mergeCopy(frameworkPath, projectPath, webTargetProjectPath); if (options.framework === "preact") { const preactVitePath = path.resolve( templatePath, `diff-merge/preact-vite-${options.appType}-${options.script}` ); await mergeCopy(preactVitePath, projectPath, webTargetProjectPath); } if (options.tailwindcss) { const tailwindcssPath = path.resolve(templatePath, "diff-merge/tailwindcss"); await mergeCopy(tailwindcssPath, projectPath, webTargetProjectPath); } if (options.ui !== void 0 && options.ui !== "") { const uiPath = path.resolve(templatePath, `diff-merge/${options.ui}-${options.script}`); await mergeCopy(uiPath, projectPath, webTargetProjectPath); const frameworkUIPath = path.resolve( templatePath, `diff-merge/${options.framework}-${options.ui}-${options.script}` ); if (fs.existsSync(frameworkUIPath)) { await mergeCopy(frameworkUIPath, projectPath, webTargetProjectPath); } } for (let i = 0; i < options.components.length; i++) { const component = options.components[i]; const componentPath = path.resolve(templatePath, `diff-merge/${component}`); const componentScriptPath = path.resolve( templatePath, `diff-merge/${component}-${options.script}` ); if (fs.existsSync(componentPath)) { await mergeCopy(componentPath, projectPath, webTargetProjectPath); } if (fs.existsSync(componentScriptPath)) { await mergeCopy(componentScriptPath, projectPath, webTargetProjectPath); } } }; const create = async (options) => { const currentPath = process.cwd(); const targetProjectPath = path.resolve(currentPath, options.projectName); if (fs.existsSync(targetProjectPath)) { throw new Error(`folder: '${options.projectName}' already exists`); } const templatePath = path.resolve(__dirname, "../template"); const webSourcePath = path.join(templatePath, `web-${options.script}`); if (!fs.existsSync(webSourcePath)) { throw new Error(`template missing: ${webSourcePath}`); } let webTargetProjectPath = targetProjectPath; if (options.appType === "web") { replaceCopy(webSourcePath, targetProjectPath, REPLACE_REG, options.projectName); } else { const sourcePath = path.join(templatePath, `${options.appType}-${options.script}`); await replaceCopy(sourcePath, targetProjectPath, REPLACE_REG, options.projectName); const opt = webDirMap[options.appType]; const webTargetPath = opt == null ? void 0 : opt.webDir; if (webTargetPath !== void 0) { webTargetProjectPath = path.join(targetProjectPath, webTargetPath); if (!fs.existsSync(webTargetProjectPath)) { fs.mkdirSync(webTargetProjectPath, { recursive: true }); } } } await mergeWebProject(templatePath, targetProjectPath, webTargetProjectPath, options); await createOtherFile(targetProjectPath); }; const main = async () => { const projectName = await prompts.input({ message: "Enter Your Project Name", default: "example-refast-project" }); const appType = await prompts.select({ message: "Choose App Type", choices: [{ name: "web (default)", value: "web" }, { value: "tauri" }, { value: "electron" }] }); const script = await prompts.select({ message: "Choose Language", choices: [{ name: "typescript (default)", value: "typescript" }, { value: "javascript" }] }); const framework = await prompts.select({ message: "Choose Framework", choices: [{ name: "react (default)", value: "react" }, { value: "preact" }] }); const uiChoices = [ { name: "daisyui (default)", value: "daisyui" }, { name: "nonuse", value: "" } ]; if (framework === "react") { uiChoices.push({ name: "antd", value: "antd" }); } const ui = await prompts.select({ message: "Choose UI", choices: uiChoices }); const choices = [ { name: "prettier", value: "prettier", checked: true }, { name: "husky", value: "husky", checked: true } ]; if (appType === "web" || appType === "tauri") { choices.push({ name: "eslint", value: "eslint", checked: true }); } if (appType === "web") { choices.push({ name: "vitest", value: "vitest", checked: true }); } const components = await prompts.checkbox({ message: "Select Components", choices }); let tailwindcss = false; if (ui === "daisyui") { tailwindcss = true; } try { console.log("please wait..."); await create({ projectName, appType, script, framework, ui, tailwindcss, components }); console.log(`✅ create done! ${projectName}`); } catch (e) { console.error(e); } }; main();