@refastdev/create-refast-app
Version:
create-refast-app
264 lines (256 loc) • 8.88 kB
JavaScript
;
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();