orchetera
Version:
Welcome to **Orchetera** — your orchestration tool to kickstart Firebase-ready projects with ease!
164 lines (137 loc) • 4.91 kB
JavaScript
const { execSync } = require("child_process");
const fs = require("fs");
const path = require("path");
const fse = require("fs-extra");
// Get app name
const appName = process.argv[2] || ".";
const isCurrentDir = appName === ".";
const skipStart = process.argv.includes("--no-start");
// Path to installation directory
const appPath = path.resolve(process.cwd(), appName);
const firebasePath = path.join(appPath, "../firebaseConfig.js");
// if firebaseConfig.js doesn't exist
if (!fs.existsSync(firebasePath)) {
console.log("[INFO] Creating firebaseConfig...");
const firebaseTemplateContent = `const firebaseConfig = {
apiKey: "",
authDomain: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: "",
measurementId: ""
};`;
fs.mkdirSync(path.dirname(firebasePath), { recursive: true });
fs.writeFileSync(firebasePath, firebaseTemplateContent);
console.warn(
`[!] firebaseConfig.js not exist. Generated at: ${firebasePath}. Please open and complete it.`
);
process.exit(1);
}
// if firebaseConfig is exist
// mapping firebaseConfig to object form
const firebaseRaw = fs.readFileSync(firebasePath, "utf8");
let configMatch = firebaseRaw.match(/firebaseConfig\s*=\s*{([^}]*)}/s);
if (!configMatch) {
console.error("[x] firebaseConfig not found in firebaseConfig.js");
process.exit(1);
}
if (!firebaseRaw.includes("const firebaseConfig")) {
console.error("[x] firebaseConfig is not properly declared.");
process.exit(1);
}
const configBlock = configMatch[1];
const configObj = {};
configBlock.split("\n").forEach((line) => {
const match = line.trim().match(/^(\w+):\s*["'](.*?)["'],?$/);
if (match) {
const [, key, value] = match;
configObj[key] = value;
}
});
const emptyFields = Object.entries(configObj).filter(([_, v]) => !v);
if (emptyFields.length) {
console.error("[!] Some firebaseConfig values are still empty:");
emptyFields.forEach(([k]) => console.warn(`- ${k}`));
process.exit(1);
}
// Run CRA with local template
console.log("[INFO] Creating react app...");
if (!isCurrentDir && fs.existsSync(appPath)) {
console.error(`[x] Directory "${appName}" already exists. Aborting.`);
process.exit(1);
}
execSync(`npx create-react-app ${appName} --template file:${__dirname}`, {
stdio: "inherit",
});
// Generate PORT
console.log("[INFO] Generating Port...");
const randomPort = Math.floor(1000 + Math.random() * 9000);
// Path setup
const envPath = path.join(appPath, ".env");
const firebasercPath = path.join(appPath, ".firebaserc");
// Prepare .env content
console.log("[INFO] Generating .env and .firebaserc...");
let envContent = `PORT=${randomPort}\n`;
let firebasercContent = "";
let projectId = "";
// Inject firebase config
for (const [key, val] of Object.entries(configObj)) {
const upperKey = key.toUpperCase();
envContent += `REACT_APP_FIREBASE_${upperKey}=${val}\n`;
if (upperKey === "PROJECTID") {
projectId = val;
}
}
// Overwrite warning
if (fs.existsSync(envPath)) {
console.warn("[!] .env file exists. Will be overwritten.");
}
fs.writeFileSync(envPath, envContent);
console.log(`[INFO] .env file created at: ${envPath}`);
// Write .firebaserc if projectId found
if (projectId) {
firebasercContent = JSON.stringify(
{ projects: { default: projectId } },
null,
2 // prettier format
);
if (fs.existsSync(firebasercPath)) {
console.warn("[!] .firebaserc file exists. Will be overwritten.");
}
fs.writeFileSync(firebasercPath, firebasercContent);
console.log(`[INFO] .firebaserc file created at: ${firebasercPath}`);
} else {
console.warn(
"[!] projectId not found in firebase config. .firebaserc not created."
);
}
console.log(`[INFO] Encrypting .env with dechipera...`);
execSync("npx dechipera --no-exit", { cwd: appPath, stdio: "inherit" });
console.log("[INFO] Removing firebaseConfig...");
fse.removeSync(firebasePath);
console.log("[INFO] Change application title...");
// Replace <title> in public/index.html with app name
const htmlPath = path.join(appPath, "public", "index.html");
let html = fs.readFileSync(htmlPath, "utf-8");
const titleName = isCurrentDir ? path.basename(process.cwd()) : appName;
html = html.replace(/<title>.*<\/title>/, `<title>${titleName}</title>`);
fs.writeFileSync(htmlPath, html);
console.log(
`[OK] Orchetera successfully generated and run on PORT ${randomPort}, and will be running soon...`
);
console.log("[INFO] Opening Visual Studio Code...");
execSync(`code .`, { cwd: appPath, stdio: "inherit" });
// Run npm start in generated folder
console.log("[INFO] Running app on PORT " + randomPort + "...");
try {
if (!skipStart) {
execSync(`npm start`, { cwd: appPath, stdio: "inherit" });
} else {
console.log("[INFO] run the app using `npm start`");
}
} catch (e) {
console.error("[x] Failed to run app:", e.message);
process.exit(1);
}