ng-postbuild
Version:
A post-build CLI utility for Angular projects to flatten and cleanup the dist output.
192 lines (164 loc) • 5.54 kB
JavaScript
import fs from "fs";
import path from "path";
import * as tar from "tar";
// CLI args parsing
const args = process.argv.slice(2);
// Show help and exit if --help is passed
if (args.includes("--help")) {
console.log(`
🛠️ ng-postbuild CLI Help
Usage:
ng-postbuild [options]
Options:
--out <filename> Output tar file name (default: <project>.tar)
--rename <foldername> Rename project folder inside the archive
--no-compress Skip tar compression
--delete-server Delete the "server" folder after build
--help Show this help message
Examples:
ng-postbuild
ng-postbuild --out build.tar
ng-postbuild --rename my-app
ng-postbuild --no-compress
ng-postbuild --rename my-app --out app
`);
process.exit(0);
}
const outFlagIndex = args.indexOf("--out");
const renameFlagIndex = args.indexOf("--rename");
const shouldCompress = !args.includes("--no-compress");
const outFileName =
outFlagIndex !== -1 && args[outFlagIndex + 1] ? args[outFlagIndex + 1] : null;
const renamedFolder =
renameFlagIndex !== -1 && args[renameFlagIndex + 1]
? args[renameFlagIndex + 1]
: null;
const deleteServer = args.includes("--delete-server");
// Log CLI options
console.log("🛠️ CLI Options:");
console.log(
` ➤ Compression: ${
shouldCompress ? "Enabled" : "Disabled"
} (--no-compress to disable)`
);
console.log(
` ➤ Output Tar Name: ${
outFileName || "[default: <project>.tar]"
} (--out <filename>)`
);
console.log(
` ➤ Rename Folder To: ${
renamedFolder || "[unchanged]"
} (--rename <foldername>)`
);
console.log(
` ➤ Delete Server: ${deleteServer ? "Yes" : "No"} (--delete-server)`
);
console.log("");
console.log("✅ Post-build script started");
const projectRoot = process.cwd();
const angularJsonPath = path.join(projectRoot, "angular.json");
if (!fs.existsSync(angularJsonPath)) {
console.error("❌ angular.json not found in current directory.");
process.exit(1);
}
let angularConfig;
try {
angularConfig = JSON.parse(fs.readFileSync(angularJsonPath, "utf-8"));
console.log("angular.json parsed successfully");
} catch (err) {
console.error("❌ Failed to parse angular.json:", err.message);
process.exit(1);
}
const defaultProject =
angularConfig.defaultProject || Object.keys(angularConfig.projects)[0];
const outputPath =
angularConfig.projects?.[defaultProject]?.architect?.build?.options
?.outputPath;
if (!outputPath) {
console.error("❌ Could not determine outputPath from angular.json.");
process.exit(1);
}
const distRoot = path.join(projectRoot, outputPath);
const browserPath = path.join(distRoot, "browser");
if (!fs.existsSync(browserPath)) {
console.error(`❌ Browser folder not found: ${browserPath}`);
process.exit(1);
}
// Move files from /browser to dist root
fs.readdirSync(browserPath).forEach((file) => {
const src = path.join(browserPath, file);
const destFileName = file === "index.csr.html" ? "index.html" : file;
const dest = path.join(distRoot, destFileName);
try {
fs.renameSync(src, dest);
console.log(`Moved ${file} → ${destFileName}`);
} catch (err) {
console.error(
`\x1b[31m❌ Failed to move file "${file}":\x1b[0m`,
`\x1b[31m${err.message}\x1b[0m`
);
console.error(`\x1b[31m❌ Please check and test your build folder\x1b[0m`);
process.exit(1); // Exit the process on error
}
});
// Remove browser folder
try {
fs.rmSync(browserPath, { recursive: true, force: true });
console.log(`✅ Removed browser folder`);
} catch (err) {
console.error(`❌ Failed to remove browser folder:`, err.message);
}
if (deleteServer) {
const serverPath = path.join(distRoot, "server");
if (fs.existsSync(serverPath)) {
try {
fs.rmSync(serverPath, { recursive: true, force: true });
console.log(`✅ Deleted server folder`);
} catch (err) {
console.error(`❌ Failed to delete server folder:`, err.message);
}
} else {
console.log(`⚠️ Server folder not found, skipping delete`);
}
}
if (shouldCompress) {
const archiveName = `${outFileName || defaultProject}.tar`;
const archivePath = path.join(projectRoot, archiveName);
const distFolderName = path.basename(distRoot);
console.log(`📦 Creating archive: ${archivePath}`);
// Temporarily copy to a virtual structure like dist/renamedFolder/
const sourcePath = path.join("dist", distFolderName); // relative
tar
.c(
{
gzip: false,
file: archivePath,
cwd: projectRoot,
portable: true,
noMtime: true,
// Rename dist/project to dist/renamed if needed
transform: (entry) => {
if (renamedFolder) {
const updatedPath = entry.path.replace(
new RegExp(`^dist/${distFolderName}`),
`dist/${renamedFolder}`
);
entry.path = updatedPath;
}
return entry;
},
},
[sourcePath]
)
.then(() => {
console.log(`✅ Archive created: ${archivePath}`);
})
.catch((err) => {
console.error(`❌ Failed to create archive:`, err.message);
});
} else {
console.log("⚠️ Compression skipped (use --no-compress to disable)");
}
console.log("✅ Post-build script finished");