mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-11 23:04:52 +09:00
These packages are obsoleted by the promises API to the built-in Node fs module. The fs-extra package is also causing problems with broken APIs that don't behave the same as Node's fs module, and throwing errors without code properties on them.
160 lines
4.5 KiB
JavaScript
160 lines
4.5 KiB
JavaScript
/* eslint-disable no-console */
|
|
"use strict";
|
|
const fs = require("node:fs/promises");
|
|
const JSZip = require("jszip");
|
|
const path = require("path");
|
|
const stream = require("stream");
|
|
const util = require("util");
|
|
const yargs = require("yargs");
|
|
|
|
const finished = util.promisify(stream.finished);
|
|
|
|
|
|
async function buildMod(args, info, modName) {
|
|
if (args.build) {
|
|
await fs.mkdir(args.outputDir, { recursive: true });
|
|
modName = args.modName ?? `${info.name}_${info.version}`;
|
|
|
|
if (args.pack) {
|
|
let zip = new JSZip();
|
|
for await (const dirent of await fs.opendir(args.sourceDir, { recursive: true })) {
|
|
if (dirent.isFile()) {
|
|
const itemPath = path.join(dirent.parentPath, dirent.name);
|
|
// On Windows the path created uses backslashes as the directory sepparator
|
|
// but the zip file needs to use forward slashes. We can't use the posix
|
|
// version of relative here as it doesn't work with Windows style paths.
|
|
let basePath = path.relative(args.sourceDir, itemPath).replace(/\\/g, "/");
|
|
// eslint-disable-next-line max-depth
|
|
if (basePath === "info.json") {
|
|
// The info.json file is overridden later.
|
|
continue;
|
|
}
|
|
zip.file(path.posix.join(modName, basePath), (await fs.open(itemPath)).createReadStream());
|
|
}
|
|
}
|
|
|
|
for (let [fileName, pathParts] of Object.entries(info.additional_files || {})) {
|
|
let filePath = path.join(args.sourceDir, ...pathParts);
|
|
let fileStream;
|
|
try {
|
|
fileStream = (await fs.open(filePath)).createReadStream();
|
|
} catch (err) {
|
|
throw new Error(`Error reading additional file ${filePath}`, { cause: err });
|
|
}
|
|
zip.file(path.posix.join(modName, fileName), fileStream);
|
|
}
|
|
delete info.additional_files;
|
|
|
|
zip.file(path.posix.join(modName, "info.json"), JSON.stringify(info, null, "\t"));
|
|
|
|
let modPath = path.join(args.outputDir, `${modName}.zip`);
|
|
console.log(`Writing ${modPath}`);
|
|
let writeStream = zip.generateNodeStream().pipe(
|
|
(await fs.open(modPath, "w")).createWriteStream()
|
|
);
|
|
await finished(writeStream);
|
|
|
|
} else {
|
|
let modDir = path.join(args.outputDir, modName);
|
|
console.log(`Removing potentially existing build ${modDir}`);
|
|
await fs.rm(modDir, { force: true, recursive: true, maxRetries: 10 });
|
|
console.log(`Building ${modDir}`);
|
|
await fs.cp(args.sourceDir, modDir, {
|
|
errorOnExists: true,
|
|
mode: fs.constants.COPYFILE_FICLONE,
|
|
recursive: true,
|
|
});
|
|
for (let [fileName, pathParts] of Object.entries(info.additional_files) || []) {
|
|
let filePath = path.join(...pathParts);
|
|
await fs.copyFile(
|
|
filePath,
|
|
path.join(modDir, fileName),
|
|
fs.constants.COPYFILE_EXCL | fs.constants.COPYFILE_FICLONE,
|
|
);
|
|
}
|
|
delete info.additional_files;
|
|
|
|
await fs.writeFile(path.join(modDir, "info.json"), JSON.stringify(info, null, "\t"));
|
|
}
|
|
}
|
|
}
|
|
|
|
async function build(args) {
|
|
let info = JSON.parse(await fs.readFile(path.join(args.sourceDir, "info.json")));
|
|
|
|
if (args.factorioVersion) {
|
|
info.factorio_version = args.factorioVersion;
|
|
}
|
|
|
|
if (args.dependencies) {
|
|
info.dependencies = args.dependencies;
|
|
}
|
|
|
|
if (args.clean) {
|
|
let splitter = /^(.*)_(\d+\.\d+\.\d+)(\.zip)?$/;
|
|
for (let entry of await fs.readdir(args.outputDir)) {
|
|
let match = splitter.exec(entry);
|
|
if (match) {
|
|
let name = match[1];
|
|
if (name === info.name) {
|
|
let modPath = path.join(args.outputDir, entry);
|
|
console.log(`Removing ${modPath}`);
|
|
await fs.rm(modPath, { force: true, recursive: true, maxRetries: 10 });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (info.variants) {
|
|
for (let variantOverrides of info.variants) {
|
|
let variantInfo = {
|
|
...info,
|
|
...variantOverrides,
|
|
};
|
|
delete variantInfo.variants;
|
|
await buildMod(args, variantInfo);
|
|
}
|
|
} else {
|
|
await buildMod(args, info);
|
|
}
|
|
}
|
|
|
|
|
|
async function main() {
|
|
const args = yargs
|
|
.scriptName("build")
|
|
.options({
|
|
"clean": { describe: "Remove previous builds", type: "boolean", default: false },
|
|
"build": { describe: "Build mod(s)", type: "boolean", default: true },
|
|
"pack": { describe: "Pack into zip file", type: "boolean", default: true },
|
|
"factorio-version": { describe: "Override factorio_version", type: "string" },
|
|
"source-dir": {
|
|
describe: "Path to mod source files",
|
|
type: "string",
|
|
nargs: 1,
|
|
normalize: true,
|
|
demandOption: true,
|
|
},
|
|
"output-dir": {
|
|
describe: "Path to output built mod(s)",
|
|
type: "string",
|
|
nargs: 1,
|
|
normalize: true,
|
|
default: "dist/factorio",
|
|
},
|
|
})
|
|
.strict()
|
|
.argv
|
|
;
|
|
|
|
await build(args);
|
|
}
|
|
|
|
if (module === require.main) {
|
|
main().catch(err => { console.log(err); });
|
|
}
|
|
|
|
module.exports = {
|
|
build,
|
|
};
|