@criticalmanufacturing/node-package-bundler
Version:
Connect IoT Package Bundler
363 lines • 20.3 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PackagePacker = void 0;
/* eslint-disable no-useless-escape */
const inversify_config_1 = require("./inversify.config");
const io = require("fs-extra");
const path = require("path");
const os = require("node:os");
const unzipper = require("unzipper");
const configuration_1 = require("./models/configuration");
// eslint-disable-next-line @typescript-eslint/no-var-requires
const ncc = require("@vercel/ncc");
const types_1 = require("./types");
class PackagePacker {
go(options) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b;
const source = options.i || options.input || process.cwd();
const destination = options.o || options.output || "";
let temp = options.t || options.temp || path.join(source, "__TEMP__");
const configurationFile = options.c || options.config || path.join(source, "packConfig.json");
let addons = options.a || options.addons;
const version = options.v || options.version || "";
const debug = options.d || options.debug || false;
this._logger = inversify_config_1.container.get(types_1.TYPES.Logger);
this._operations = inversify_config_1.container.get(types_1.TYPES.Operations);
const packageFile = path.join(path.dirname(__filename), "..", "package.json");
const packageContent = io.readJSONSync(packageFile);
this._logger.notice(`** Using Package Bundler version '${packageContent.version}' **`);
this._logger.notice(``);
this._logger.debug(`Using the following settings:`);
this._logger.debug(` Source : ${source}`);
this._logger.debug(` Destination : ${destination}`);
this._logger.debug(` Temporary : ${temp}`);
this._logger.debug(` Configuration : ${configurationFile}`);
this._logger.debug(` Addons : ${addons}`);
this._logger.debug(` Version : ${version}`);
this._logger.debug(` Debug Mode : ${debug}`);
this._logger.debug("");
// Sanity checks
if (!io.existsSync(source)) {
this._logger.error(`Source directory '${source}' doesn't exist!`);
process.exit(1);
}
if (!io.existsSync(configurationFile)) {
this._logger.error(`Configuration file '${configurationFile}' doesn't exist!`);
this._logger.error(`This package will not packed!`);
process.exit(2);
}
const configuration = JSON.parse(io.readFileSync(configurationFile, "utf8"));
// Only process addons if there are addons to process and if there is an arg for addons
if (configuration.addons != null && addons != null && addons !== "") {
addons = yield this.processAddOns(addons, source, configuration);
if (!io.existsSync(addons)) {
this._logger.error(`Addons location '${addons}' doesn't exist!`);
process.exit(1);
}
}
const paths = inversify_config_1.container.get(types_1.TYPES.Paths);
paths.setup(source, destination, temp, addons);
// Prepare temp Directory
if (configuration.type === configuration_1.ComponentType.TasksPackage) {
temp = io.readJSONSync(path.join(source, "ng-package.json")).dest;
if (!io.existsSync(temp)) {
this._logger.error(`'${temp}' doesn't exist! Did you forget to run 'ng build'?`);
process.exit(1);
}
this._logger.warn(`Temporary directory changed to '${temp}'`);
}
else {
this._operations.deleteDirectory(temp);
this._operations.createDirectory(temp);
}
const main = (_a = io.readJSONSync(path.join(source, "package.json"))) === null || _a === void 0 ? void 0 : _a.main;
const mainSplitted = main === null || main === void 0 ? void 0 : main.split("/");
const index = mainSplitted === null || mainSplitted === void 0 ? void 0 : mainSplitted[mainSplitted.length - 1];
mainSplitted === null || mainSplitted === void 0 ? void 0 : mainSplitted.pop();
const srcDirectory = (_b = mainSplitted === null || mainSplitted === void 0 ? void 0 : mainSplitted.join("/")) !== null && _b !== void 0 ? _b : "src";
// Pack package
const packs = configuration.packs || [];
if (packs.length === 0) {
switch (configuration.type) {
case configuration_1.ComponentType.TasksPackage:
packs.push({
directory: srcDirectory,
source: "public-api-runtime.js",
destination: index,
});
break;
case configuration_1.ComponentType.BusinessScenario:
break;
default:
packs.push({
directory: srcDirectory,
source: index,
destination: index,
});
}
}
for (const pack of packs) {
yield this.packPackage(path.join(source, pack.directory), pack.source || "index.js", temp, pack.destination || "index.js");
}
if (configuration.type !== configuration_1.ComponentType.TasksPackage) {
// Copy necessary files to generate package
if (!io.existsSync("npm-shrinkwrap.json")) {
this._logger.warn("npm-shrinkwrap.json file not found. Trying to generate it...");
inversify_config_1.container.get(types_1.TYPES.Processors.ShrinkwrapGenerator).process(source, "npm-shrinkwrap.json");
}
this._operations.copyFile("npm-shrinkwrap.json", source, temp, true);
this._operations.copyFile(".npmignore", source, temp, true);
this._operations.copyFile(".npmrc", source, temp, true);
this._operations.copyFile("README.md", source, temp);
this._operations.copyFile("package.json", source, temp);
// normalize package.json main
const packageJSONTemp = io.readJSONSync(path.join(temp, "package.json"));
packageJSONTemp.main = "src/index.js";
io.writeJSONSync(path.join(temp, "package.json"), packageJSONTemp);
}
if (configuration.type === configuration_1.ComponentType.TasksPackage) {
this._operations.deleteFile(path.join(source, "src", "index.js"));
}
this._operations.setPackageJsonAsPacked(path.join(temp, "package.json"));
if (version != null && version !== "") {
this._operations.changePackageJsonVersion(path.join(temp, "package.json"), version);
}
// TasksPackages must have the dependencies for the GUI. All others, clear them
if (configuration.type !== configuration_1.ComponentType.TasksPackage) {
this._operations.removeDependenciesFromPackageJson(path.join(temp, "package.json"));
}
// Copy .node files (Addons)
(configuration.addons || []).forEach((addon) => {
const sourceAddonDir = path.join(/*mappedAddons ||*/ addons, addon.name, addon.version);
const destinationAddonDir = path.join(temp, "addons", addon.name);
this._operations.createDirectory(path.join(temp, "addons"));
this._operations.createDirectory(destinationAddonDir);
for (const addonFile of this.findByExtension(sourceAddonDir, addon.fileMask)) {
this._operations.copyFile(addonFile, sourceAddonDir, destinationAddonDir);
}
});
// Process any template action
if (configuration.templates != null) {
const destinationTemplates = path.join(temp, "package.json");
switch (configuration.type) {
case configuration_1.ComponentType.Component:
inversify_config_1.container.get(types_1.TYPES.Processors.DriverTemplates).process(configuration.templates, destinationTemplates);
break;
case configuration_1.ComponentType.TasksPackage:
case configuration_1.ComponentType.TasksLibrary:
yield inversify_config_1.container.get(types_1.TYPES.Processors.LibraryTemplates).process(configuration.templates, destinationTemplates);
break;
}
}
// Process any business Scenarios
if (configuration.businessScenarios != null) {
const destinationBusinessScenarios = path.join(temp, "package.json");
switch (configuration.type) {
case configuration_1.ComponentType.TasksPackage:
case configuration_1.ComponentType.TasksLibrary:
case configuration_1.ComponentType.BusinessScenario:
yield inversify_config_1.container.get(types_1.TYPES.Processors.LibraryBusinessScenarios).process(configuration.businessScenarios, destinationBusinessScenarios);
break;
}
}
// Process any font action
if (configuration.font != null) {
const destinationFont = path.join(temp, "package.json");
if (configuration.type === configuration_1.ComponentType.TasksLibrary || configuration.type === configuration_1.ComponentType.TasksPackage) {
inversify_config_1.container.get(types_1.TYPES.Processors.LibraryFontProcessor).process(configuration.font, destinationFont);
}
}
// process Post actions
(configuration.postActions || []).forEach((action) => {
const actionSource = (action.source || "")
.replace("${Source}", source)
.replace("${Temp}", temp)
.replace("${Destination}", destination)
.replace("${Addons}", addons);
const actionDestination = (action.destination || "")
.replace("${Source}", source)
.replace("${Temp}", temp)
.replace("${Destination}", destination)
.replace("${Addons}", addons);
switch (action.type) {
case configuration_1.ActionType.DeleteFile:
this._operations.deleteFile(actionSource);
break;
case configuration_1.ActionType.DeleteDirectory:
this._operations.deleteDirectory(actionSource);
break;
case configuration_1.ActionType.CopyDirectory:
this._operations.copyDirectory(actionSource, actionDestination);
break;
case configuration_1.ActionType.CopyFile:
this._operations.copyFile(action.file || "", actionSource, actionDestination);
break;
case configuration_1.ActionType.MoveFile:
this._operations.moveFile(action.file || "", actionSource, actionDestination);
break;
case configuration_1.ActionType.RenameFile:
this._operations.renameFile(actionSource, actionDestination);
break;
case configuration_1.ActionType.ReplaceText:
this._operations.replaceTextInFile(actionSource, action.search || "", action.replace || "", action.isRegularExpression || false);
break;
}
});
// Place index.js into src directory
this._operations.createDirectory(path.join(temp, "src"));
if (configuration.type === configuration_1.ComponentType.TasksPackage) {
this._operations.moveFile("index.js", temp, path.join(temp, "src"));
}
else {
for (const pack of packs) {
// Fix issue with nconf
// https://github.com/zeit/ncc/issues/451
["argv", "env", "file", "literal", "memory"].forEach((toFix) => {
this._operations.replaceTextInFile(path.join(temp, pack.destination || "index.js"), `arg === \"${toFix}.js\"`, `arg === \"${toFix}\"`, false);
});
this._operations.moveFile(pack.destination || "index.js", temp, path.join(temp, "src"));
}
}
// Create Package and place it in the destination
if (destination !== "") {
this._operations.createDirectory(destination);
if (os.platform() === "win32") {
this._operations.run("npm.cmd", ["pack"], temp);
}
else {
this._operations.run("npm", ["pack"], temp);
}
for (const packedPackage of this.findByExtension(temp, "*.tgz")) {
this._operations.moveFile(packedPackage, temp, destination);
}
}
// Delete temp
if (debug === false) {
this._operations.deleteDirectory(temp);
}
else {
this._logger.warn(`Directory '${temp}' was *NOT* deleted`);
}
this._logger.info("");
this._logger.success("** Finished **");
});
}
/**
* Retrieves a list of files that math a glob
* @param searchPath Full path to search
* @param extension Glob expression
*/
findByExtension(searchPath, extension, basePath) {
if (extension.startsWith("*.")) {
extension = extension.substring(1);
}
basePath = basePath !== null && basePath !== void 0 ? basePath : "";
const result = [];
const files = io.readdirSync(searchPath);
for (const file of files) {
const filename = path.join(searchPath, file);
const stat = io.lstatSync(filename);
if (stat.isDirectory()) {
result.push(...this.findByExtension(filename, extension, basePath + `/${path.basename(filename)}`));
}
else if (filename.endsWith(extension) || extension === "*") {
result.push(path.join(basePath, path.basename(filename)));
}
}
return (result);
}
/**
* Execute ncc over a selected file to bundle it
* @param SourceDirectory Directory where the file is
* @param sourceFile name of the file to bundle
* @param destinationDirectory Destination where to save the result
* @param destinationFile Name of the final bundled file
*/
packPackage(SourceDirectory, sourceFile, destinationDirectory, destinationFile) {
return __awaiter(this, void 0, void 0, function* () {
const nccInput = path.join(SourceDirectory, sourceFile);
const nccOptions = { minify: false, sourceMap: false, sourceMapRegister: false, quiet: true };
const { code, assets } = yield ncc(nccInput, nccOptions);
for (const [assetName, assetCode] of Object.entries(assets)) {
const assetSize = Math.round(Buffer.byteLength(assetCode.source, "utf8") / 1024);
this._operations.createFile(path.join(destinationDirectory, assetName), assetCode.source);
this._logger.notice(`[PACKED ASSET] ${assetSize}Kb \t ${assetName} `);
}
// await this.sleep(5000);
const codeSize = Math.round(Buffer.byteLength(code, "utf8") / 1024);
// const mapSize = map ? Math.round(Buffer.byteLength(map, "utf8") / 1024) : 0;
this._operations.createFile(path.join(destinationDirectory, destinationFile), code);
this._logger.notice(`[PACK RESULT] ${codeSize}Kb \t ${destinationFile}`);
});
}
/**
* Sleep for a moment before continuing to the next instruction
* @param ms Number of milliseconds to sleep
*/
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* Treat addons entry. To validate, download, unzip and reajust addons location.
* @param addons Addons location
* @param source Where is the source location
* @param configuration Configurations parsed from the packConfig
*/
processAddOns(addons, source, configuration) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
if (this._operations.isValidUrl(addons)) {
const url = new URL(addons);
const localAddons = path.join(source, "__ADDONS__");
let success = 0;
if (configuration.addons != null && configuration.addons.length > 0) {
this._operations.deleteDirectory(localAddons);
this._operations.createDirectory(localAddons);
}
for (const addon of configuration.addons || []) {
const packageName = `${addon.name}-${addon.version}.zip`;
const destination = path.join(localAddons, `${addon.name}/${addon.version}/`);
const downloadUrl = `${url.href}/${addon.name}/${packageName}`;
this._logger.debug(`Downloading package: ${addon.name}-${addon.version} to ${destination} from ${downloadUrl}`);
// Download the package
const response = yield fetch(downloadUrl, {
method: "GET",
});
if (!response.ok || (response === null || response === void 0 ? void 0 : response.body) === null) {
this._logger.error(`Failed to download package: ${response.statusText}`);
process.exit(1);
}
// Convert the ReadableStream into a Stream
const nodeStream = yield this._operations.readStreamToBuffer(response.body);
// Unzip to the destination folder
yield unzipper.Open.buffer(nodeStream)
.then(d => d.extract({ path: destination, concurrency: 5 }));
success += 1;
}
;
if (success === ((_a = configuration.addons) === null || _a === void 0 ? void 0 : _a.length)) {
addons = localAddons;
this._logger.debug(`Changed the default addon location to: ${addons}`);
}
else {
this._operations.deleteDirectory(localAddons);
this._logger.error(`Failed to download all the required addons from the nexus.`);
process.exit(1);
}
}
return addons;
});
}
}
exports.PackagePacker = PackagePacker;
//# sourceMappingURL=packagePacker.js.map