@napi-rs/cli
Version:
Cli tools for napi-rs
1,326 lines (1,270 loc) • 525 kB
JavaScript
import { createRequire } from "node:module";
import { Cli, Command, Option } from "clipanion";
import path, { dirname, isAbsolute, join, parse, resolve } from "node:path";
import * as colors from "colorette";
import { underline, yellow } from "colorette";
import { createDebug } from "obug";
import { access, copyFile, mkdir, readFile, readdir, rename, stat, unlink, writeFile } from "node:fs/promises";
import { exec, execSync, spawn, spawnSync } from "node:child_process";
import fs, { existsSync, mkdirSync, promises, rmSync, statSync } from "node:fs";
import { isNil, merge, omit, omitBy, pick, sortBy } from "es-toolkit";
import { createHash } from "node:crypto";
import { homedir } from "node:os";
import { parse as parse$1 } from "semver";
import { dump, load } from "js-yaml";
import * as typanion from "typanion";
import { Octokit } from "@octokit/rest";
import { checkbox, confirm, input, select } from "@inquirer/prompts";
//#region src/def/artifacts.ts
var BaseArtifactsCommand = class extends Command {
static paths = [["artifacts"]];
static usage = Command.Usage({ description: "Copy artifacts from Github Actions into npm packages and ready to publish" });
cwd = Option.String("--cwd", process.cwd(), { description: "The working directory of where napi command will be executed in, all other paths options are relative to this path" });
configPath = Option.String("--config-path,-c", { description: "Path to `napi` config json file" });
packageJsonPath = Option.String("--package-json-path", "package.json", { description: "Path to `package.json`" });
outputDir = Option.String("--output-dir,-o,-d", "./artifacts", { description: "Path to the folder where all built `.node` files put, same as `--output-dir` of build command" });
npmDir = Option.String("--npm-dir", "npm", { description: "Path to the folder where the npm packages put" });
buildOutputDir = Option.String("--build-output-dir", { description: "Path to the build output dir, only needed when targets contains `wasm32-wasi-*`" });
getOptions() {
return {
cwd: this.cwd,
configPath: this.configPath,
packageJsonPath: this.packageJsonPath,
outputDir: this.outputDir,
npmDir: this.npmDir,
buildOutputDir: this.buildOutputDir
};
}
};
function applyDefaultArtifactsOptions(options) {
return {
cwd: process.cwd(),
packageJsonPath: "package.json",
outputDir: "./artifacts",
npmDir: "npm",
...options
};
}
//#endregion
//#region src/utils/log.ts
const debugFactory = (namespace) => {
const debug$10 = createDebug(`napi:${namespace}`, { formatters: { i(v) {
return colors.green(v);
} } });
debug$10.info = (...args) => console.error(colors.black(colors.bgGreen(" INFO ")), ...args);
debug$10.warn = (...args) => console.error(colors.black(colors.bgYellow(" WARNING ")), ...args);
debug$10.error = (...args) => console.error(colors.white(colors.bgRed(" ERROR ")), ...args.map((arg) => arg instanceof Error ? arg.stack ?? arg.message : arg));
return debug$10;
};
const debug$9 = debugFactory("utils");
//#endregion
//#region package.json
var version$1 = "3.5.1";
//#endregion
//#region src/utils/misc.ts
const readFileAsync = readFile;
const writeFileAsync = writeFile;
const unlinkAsync = unlink;
const copyFileAsync = copyFile;
const mkdirAsync = mkdir;
const statAsync = stat;
const readdirAsync = readdir;
function fileExists(path$1) {
return access(path$1).then(() => true, () => false);
}
async function dirExistsAsync(path$1) {
try {
return (await statAsync(path$1)).isDirectory();
} catch {
return false;
}
}
function pick$1(o, ...keys) {
return keys.reduce((acc, key) => {
acc[key] = o[key];
return acc;
}, {});
}
async function updatePackageJson(path$1, partial) {
if (!await fileExists(path$1)) {
debug$9(`File not exists ${path$1}`);
return;
}
const old = JSON.parse(await readFileAsync(path$1, "utf8"));
await writeFileAsync(path$1, JSON.stringify({
...old,
...partial
}, null, 2));
}
const CLI_VERSION = version$1;
//#endregion
//#region src/utils/target.ts
const SUB_SYSTEMS = new Set(["android", "ohos"]);
const AVAILABLE_TARGETS = [
"aarch64-apple-darwin",
"aarch64-linux-android",
"aarch64-unknown-linux-gnu",
"aarch64-unknown-linux-musl",
"aarch64-unknown-linux-ohos",
"aarch64-pc-windows-msvc",
"x86_64-apple-darwin",
"x86_64-pc-windows-msvc",
"x86_64-pc-windows-gnu",
"x86_64-unknown-linux-gnu",
"x86_64-unknown-linux-musl",
"x86_64-unknown-linux-ohos",
"x86_64-unknown-freebsd",
"i686-pc-windows-msvc",
"armv7-unknown-linux-gnueabihf",
"armv7-unknown-linux-musleabihf",
"armv7-linux-androideabi",
"universal-apple-darwin",
"loongarch64-unknown-linux-gnu",
"riscv64gc-unknown-linux-gnu",
"powerpc64le-unknown-linux-gnu",
"s390x-unknown-linux-gnu",
"wasm32-wasi-preview1-threads",
"wasm32-wasip1-threads"
];
const DEFAULT_TARGETS = [
"x86_64-apple-darwin",
"aarch64-apple-darwin",
"x86_64-pc-windows-msvc",
"x86_64-unknown-linux-gnu"
];
const TARGET_LINKER = {
"aarch64-unknown-linux-musl": "aarch64-linux-musl-gcc",
"loongarch64-unknown-linux-gnu": "loongarch64-linux-gnu-gcc-13",
"riscv64gc-unknown-linux-gnu": "riscv64-linux-gnu-gcc",
"powerpc64le-unknown-linux-gnu": "powerpc64le-linux-gnu-gcc",
"s390x-unknown-linux-gnu": "s390x-linux-gnu-gcc"
};
const CpuToNodeArch = {
x86_64: "x64",
aarch64: "arm64",
i686: "ia32",
armv7: "arm",
loongarch64: "loong64",
riscv64gc: "riscv64",
powerpc64le: "ppc64"
};
const SysToNodePlatform = {
linux: "linux",
freebsd: "freebsd",
darwin: "darwin",
windows: "win32",
ohos: "openharmony"
};
const UniArchsByPlatform = { darwin: ["x64", "arm64"] };
/**
* A triple is a specific format for specifying a target architecture.
* Triples may be referred to as a target triple which is the architecture for the artifact produced, and the host triple which is the architecture that the compiler is running on.
* The general format of the triple is `<arch><sub>-<vendor>-<sys>-<abi>` where:
* - `arch` = The base CPU architecture, for example `x86_64`, `i686`, `arm`, `thumb`, `mips`, etc.
* - `sub` = The CPU sub-architecture, for example `arm` has `v7`, `v7s`, `v5te`, etc.
* - `vendor` = The vendor, for example `unknown`, `apple`, `pc`, `nvidia`, etc.
* - `sys` = The system name, for example `linux`, `windows`, `darwin`, etc. none is typically used for bare-metal without an OS.
* - `abi` = The ABI, for example `gnu`, `android`, `eabi`, etc.
*/
function parseTriple(rawTriple) {
if (rawTriple === "wasm32-wasi" || rawTriple === "wasm32-wasi-preview1-threads" || rawTriple.startsWith("wasm32-wasip")) return {
triple: rawTriple,
platformArchABI: "wasm32-wasi",
platform: "wasi",
arch: "wasm32",
abi: "wasi"
};
const triples = (rawTriple.endsWith("eabi") ? `${rawTriple.slice(0, -4)}-eabi` : rawTriple).split("-");
let cpu;
let sys;
let abi = null;
if (triples.length === 2) [cpu, sys] = triples;
else [cpu, , sys, abi = null] = triples;
if (abi && SUB_SYSTEMS.has(abi)) {
sys = abi;
abi = null;
}
const platform = SysToNodePlatform[sys] ?? sys;
const arch = CpuToNodeArch[cpu] ?? cpu;
return {
triple: rawTriple,
platformArchABI: abi ? `${platform}-${arch}-${abi}` : `${platform}-${arch}`,
platform,
arch,
abi
};
}
function getSystemDefaultTarget() {
const host = execSync(`rustc -vV`, { env: process.env }).toString("utf8").split("\n").find((line) => line.startsWith("host: "));
const triple = host === null || host === void 0 ? void 0 : host.slice(6);
if (!triple) throw new TypeError(`Can not parse target triple from host`);
return parseTriple(triple);
}
function getTargetLinker(target) {
return TARGET_LINKER[target];
}
function targetToEnvVar(target) {
return target.replace(/-/g, "_").toUpperCase();
}
//#endregion
//#region src/utils/version.ts
let NapiVersion = /* @__PURE__ */ function(NapiVersion$1) {
NapiVersion$1[NapiVersion$1["Napi1"] = 1] = "Napi1";
NapiVersion$1[NapiVersion$1["Napi2"] = 2] = "Napi2";
NapiVersion$1[NapiVersion$1["Napi3"] = 3] = "Napi3";
NapiVersion$1[NapiVersion$1["Napi4"] = 4] = "Napi4";
NapiVersion$1[NapiVersion$1["Napi5"] = 5] = "Napi5";
NapiVersion$1[NapiVersion$1["Napi6"] = 6] = "Napi6";
NapiVersion$1[NapiVersion$1["Napi7"] = 7] = "Napi7";
NapiVersion$1[NapiVersion$1["Napi8"] = 8] = "Napi8";
NapiVersion$1[NapiVersion$1["Napi9"] = 9] = "Napi9";
return NapiVersion$1;
}({});
const NAPI_VERSION_MATRIX = new Map([
[NapiVersion.Napi1, "8.6.0 | 9.0.0 | 10.0.0"],
[NapiVersion.Napi2, "8.10.0 | 9.3.0 | 10.0.0"],
[NapiVersion.Napi3, "6.14.2 | 8.11.2 | 9.11.0 | 10.0.0"],
[NapiVersion.Napi4, "10.16.0 | 11.8.0 | 12.0.0"],
[NapiVersion.Napi5, "10.17.0 | 12.11.0 | 13.0.0"],
[NapiVersion.Napi6, "10.20.0 | 12.17.0 | 14.0.0"],
[NapiVersion.Napi7, "10.23.0 | 12.19.0 | 14.12.0 | 15.0.0"],
[NapiVersion.Napi8, "12.22.0 | 14.17.0 | 15.12.0 | 16.0.0"],
[NapiVersion.Napi9, "18.17.0 | 20.3.0 | 21.1.0"]
]);
function parseNodeVersion(v) {
const matches = v.match(/v?([0-9]+)\.([0-9]+)\.([0-9]+)/i);
if (!matches) throw new Error("Unknown node version number: " + v);
const [, major, minor, patch] = matches;
return {
major: parseInt(major),
minor: parseInt(minor),
patch: parseInt(patch)
};
}
function requiredNodeVersions(napiVersion) {
const requirement = NAPI_VERSION_MATRIX.get(napiVersion);
if (!requirement) return [parseNodeVersion("10.0.0")];
return requirement.split("|").map(parseNodeVersion);
}
function toEngineRequirement(versions) {
const requirements = [];
versions.forEach((v, i) => {
let req = "";
if (i !== 0) {
const lastVersion = versions[i - 1];
req += `< ${lastVersion.major + 1}`;
}
req += `${i === 0 ? "" : " || "}>= ${v.major}.${v.minor}.${v.patch}`;
requirements.push(req);
});
return requirements.join(" ");
}
function napiEngineRequirement(napiVersion) {
return toEngineRequirement(requiredNodeVersions(napiVersion));
}
//#endregion
//#region src/utils/metadata.ts
async function parseMetadata(manifestPath) {
if (!fs.existsSync(manifestPath)) throw new Error(`No crate found in manifest: ${manifestPath}`);
const childProcess = spawn("cargo", [
"metadata",
"--manifest-path",
manifestPath,
"--format-version",
"1"
], { stdio: "pipe" });
let stdout = "";
let stderr = "";
let status = 0;
childProcess.stdout.on("data", (data) => {
stdout += data;
});
childProcess.stderr.on("data", (data) => {
stderr += data;
});
await new Promise((resolve$1) => {
childProcess.on("close", (code) => {
status = code ?? 0;
resolve$1();
});
});
if (status !== 0) {
const simpleMessage = `cargo metadata exited with code ${status}`;
throw new Error(`${simpleMessage} and error message:\n\n${stderr}`, { cause: new Error(simpleMessage) });
}
try {
return JSON.parse(stdout);
} catch (e) {
throw new Error("Failed to parse cargo metadata JSON", { cause: e });
}
}
//#endregion
//#region src/utils/config.ts
async function readNapiConfig(path$1, configPath) {
if (configPath && !await fileExists(configPath)) throw new Error(`NAPI-RS config not found at ${configPath}`);
if (!await fileExists(path$1)) throw new Error(`package.json not found at ${path$1}`);
const content = await readFileAsync(path$1, "utf8");
let pkgJson;
try {
pkgJson = JSON.parse(content);
} catch (e) {
throw new Error(`Failed to parse package.json at ${path$1}`, { cause: e });
}
let separatedConfig;
if (configPath) {
const configContent = await readFileAsync(configPath, "utf8");
try {
separatedConfig = JSON.parse(configContent);
} catch (e) {
throw new Error(`Failed to parse NAPI-RS config at ${configPath}`, { cause: e });
}
}
const userNapiConfig = pkgJson.napi ?? {};
if (pkgJson.napi && separatedConfig) {
const pkgJsonPath = underline(path$1);
const configPathUnderline = underline(configPath);
console.warn(yellow(`Both napi field in ${pkgJsonPath} and [NAPI-RS config](${configPathUnderline}) file are found, the NAPI-RS config file will be used.`));
}
if (separatedConfig) Object.assign(userNapiConfig, separatedConfig);
const napiConfig = merge({
binaryName: "index",
packageName: pkgJson.name,
targets: [],
packageJson: pkgJson,
npmClient: "npm"
}, omit(userNapiConfig, ["targets"]));
let targets = userNapiConfig.targets ?? [];
if (userNapiConfig === null || userNapiConfig === void 0 ? void 0 : userNapiConfig.name) {
console.warn(yellow(`[DEPRECATED] napi.name is deprecated, use napi.binaryName instead.`));
napiConfig.binaryName = userNapiConfig.name;
}
if (!targets.length) {
var _userNapiConfig$tripl, _userNapiConfig$tripl2;
let deprecatedWarned = false;
const warning = yellow(`[DEPRECATED] napi.triples is deprecated, use napi.targets instead.`);
if ((_userNapiConfig$tripl = userNapiConfig.triples) === null || _userNapiConfig$tripl === void 0 ? void 0 : _userNapiConfig$tripl.defaults) {
deprecatedWarned = true;
console.warn(warning);
targets = targets.concat(DEFAULT_TARGETS);
}
if ((_userNapiConfig$tripl2 = userNapiConfig.triples) === null || _userNapiConfig$tripl2 === void 0 || (_userNapiConfig$tripl2 = _userNapiConfig$tripl2.additional) === null || _userNapiConfig$tripl2 === void 0 ? void 0 : _userNapiConfig$tripl2.length) {
targets = targets.concat(userNapiConfig.triples.additional);
if (!deprecatedWarned) console.warn(warning);
}
}
if (new Set(targets).size !== targets.length) {
const duplicateTarget = targets.find((target, index) => targets.indexOf(target) !== index);
throw new Error(`Duplicate targets are not allowed: ${duplicateTarget}`);
}
napiConfig.targets = targets.map(parseTriple);
return napiConfig;
}
//#endregion
//#region src/utils/cargo.ts
function tryInstallCargoBinary(name, bin) {
if (detectCargoBinary(bin)) {
debug$9("Cargo binary already installed: %s", name);
return;
}
try {
debug$9("Installing cargo binary: %s", name);
execSync(`cargo install ${name}`, { stdio: "inherit" });
} catch (e) {
throw new Error(`Failed to install cargo binary: ${name}`, { cause: e });
}
}
function detectCargoBinary(bin) {
debug$9("Detecting cargo binary: %s", bin);
try {
execSync(`cargo help ${bin}`, { stdio: "ignore" });
debug$9("Cargo binary detected: %s", bin);
return true;
} catch {
debug$9("Cargo binary not detected: %s", bin);
return false;
}
}
//#endregion
//#region src/utils/typegen.ts
const TOP_LEVEL_NAMESPACE = "__TOP_LEVEL_MODULE__";
const DEFAULT_TYPE_DEF_HEADER = `/* auto-generated by NAPI-RS */
/* eslint-disable */
`;
var TypeDefKind = /* @__PURE__ */ function(TypeDefKind$1) {
TypeDefKind$1["Const"] = "const";
TypeDefKind$1["Enum"] = "enum";
TypeDefKind$1["StringEnum"] = "string_enum";
TypeDefKind$1["Interface"] = "interface";
TypeDefKind$1["Type"] = "type";
TypeDefKind$1["Fn"] = "fn";
TypeDefKind$1["Struct"] = "struct";
TypeDefKind$1["Extends"] = "extends";
TypeDefKind$1["Impl"] = "impl";
return TypeDefKind$1;
}(TypeDefKind || {});
function prettyPrint(line, constEnum, ident, ambient = false) {
let s = line.js_doc ?? "";
switch (line.kind) {
case TypeDefKind.Interface:
s += `export interface ${line.name} {\n${line.def}\n}`;
break;
case TypeDefKind.Type:
s += `export type ${line.name} = \n${line.def}`;
break;
case TypeDefKind.Enum:
const enumName = constEnum ? "const enum" : "enum";
s += `${exportDeclare(ambient)} ${enumName} ${line.name} {\n${line.def}\n}`;
break;
case TypeDefKind.StringEnum:
if (constEnum) s += `${exportDeclare(ambient)} const enum ${line.name} {\n${line.def}\n}`;
else s += `export type ${line.name} = ${line.def.replaceAll(/.*=/g, "").replaceAll(",", "|")};`;
break;
case TypeDefKind.Struct:
const extendsDef = line.extends ? ` extends ${line.extends}` : "";
if (line.extends) {
const genericMatch = line.extends.match(/Iterator<(.+)>$/);
if (genericMatch) {
const [T, TResult, TNext] = genericMatch[1].split(",").map((p) => p.trim());
line.def = line.def + `\nnext(value?: ${TNext}): IteratorResult<${T}, ${TResult}>`;
}
}
s += `${exportDeclare(ambient)} class ${line.name}${extendsDef} {\n${line.def}\n}`;
if (line.original_name && line.original_name !== line.name) s += `\nexport type ${line.original_name} = ${line.name}`;
break;
case TypeDefKind.Fn:
s += `${exportDeclare(ambient)} ${line.def}`;
break;
default: s += line.def;
}
return correctStringIdent(s, ident);
}
function exportDeclare(ambient) {
if (ambient) return "export";
return "export declare";
}
async function processTypeDef(intermediateTypeFile, constEnum) {
const exports = [];
const groupedDefs = preprocessTypeDef(await readIntermediateTypeFile(intermediateTypeFile));
return {
dts: sortBy(Array.from(groupedDefs), [([namespace]) => namespace]).map(([namespace, defs]) => {
if (namespace === TOP_LEVEL_NAMESPACE) return defs.map((def) => {
switch (def.kind) {
case TypeDefKind.Const:
case TypeDefKind.Enum:
case TypeDefKind.StringEnum:
case TypeDefKind.Fn:
case TypeDefKind.Struct:
exports.push(def.name);
if (def.original_name && def.original_name !== def.name) exports.push(def.original_name);
break;
default: break;
}
return prettyPrint(def, constEnum, 0);
}).join("\n\n");
else {
exports.push(namespace);
let declaration = "";
declaration += `export declare namespace ${namespace} {\n`;
for (const def of defs) declaration += prettyPrint(def, constEnum, 2, true) + "\n";
declaration += "}";
return declaration;
}
}).join("\n\n") + "\n",
exports
};
}
async function readIntermediateTypeFile(file) {
return (await readFileAsync(file, "utf8")).split("\n").filter(Boolean).map((line) => {
line = line.trim();
const parsed = JSON.parse(line);
if (parsed.js_doc) parsed.js_doc = parsed.js_doc.replace(/\\n/g, "\n");
if (parsed.def) parsed.def = parsed.def.replace(/\\n/g, "\n");
return parsed;
}).sort((a, b) => {
if (a.kind === TypeDefKind.Struct) {
if (b.kind === TypeDefKind.Struct) return a.name.localeCompare(b.name);
return -1;
} else if (b.kind === TypeDefKind.Struct) return 1;
else return a.name.localeCompare(b.name);
});
}
function preprocessTypeDef(defs) {
const namespaceGrouped = /* @__PURE__ */ new Map();
const classDefs = /* @__PURE__ */ new Map();
for (const def of defs) {
const namespace = def.js_mod ?? TOP_LEVEL_NAMESPACE;
if (!namespaceGrouped.has(namespace)) namespaceGrouped.set(namespace, []);
const group = namespaceGrouped.get(namespace);
if (def.kind === TypeDefKind.Struct) {
group.push(def);
classDefs.set(def.name, def);
} else if (def.kind === TypeDefKind.Extends) {
const classDef = classDefs.get(def.name);
if (classDef) classDef.extends = def.def;
} else if (def.kind === TypeDefKind.Impl) {
const classDef = classDefs.get(def.name);
if (classDef) {
if (classDef.def) classDef.def += "\n";
classDef.def += def.def;
if (classDef.def) classDef.def = classDef.def.replace(/\\n/g, "\n");
}
} else group.push(def);
}
return namespaceGrouped;
}
function correctStringIdent(src, ident) {
let bracketDepth = 0;
return src.split("\n").map((line) => {
line = line.trim();
if (line === "") return "";
const isInMultilineComment = line.startsWith("*");
const isClosingBracket = line.endsWith("}");
const isOpeningBracket = line.endsWith("{");
const isTypeDeclaration = line.endsWith("=");
const isTypeVariant = line.startsWith("|");
let rightIndent = ident;
if ((isOpeningBracket || isTypeDeclaration) && !isInMultilineComment) {
bracketDepth += 1;
rightIndent += (bracketDepth - 1) * 2;
} else {
if (isClosingBracket && bracketDepth > 0 && !isInMultilineComment && !isTypeVariant) bracketDepth -= 1;
rightIndent += bracketDepth * 2;
}
if (isInMultilineComment) rightIndent += 1;
return `${" ".repeat(rightIndent)}${line}`;
}).join("\n");
}
//#endregion
//#region src/utils/read-config.ts
async function readConfig(options) {
const resolvePath = (...paths) => resolve(options.cwd, ...paths);
return await readNapiConfig(resolvePath(options.packageJsonPath ?? "package.json"), options.configPath ? resolvePath(options.configPath) : void 0);
}
//#endregion
//#region src/api/artifacts.ts
const debug$8 = debugFactory("artifacts");
async function collectArtifacts(userOptions) {
const options = applyDefaultArtifactsOptions(userOptions);
const resolvePath = (...paths) => resolve(options.cwd, ...paths);
const packageJsonPath = resolvePath(options.packageJsonPath);
const { targets, binaryName, packageName } = await readNapiConfig(packageJsonPath, options.configPath ? resolvePath(options.configPath) : void 0);
const distDirs = targets.map((platform) => join(options.cwd, options.npmDir, platform.platformArchABI));
const universalSourceBins = new Set(targets.filter((platform) => platform.arch === "universal").flatMap((p) => {
var _UniArchsByPlatform$p;
return (_UniArchsByPlatform$p = UniArchsByPlatform[p.platform]) === null || _UniArchsByPlatform$p === void 0 ? void 0 : _UniArchsByPlatform$p.map((a) => `${p.platform}-${a}`);
}).filter(Boolean));
await collectNodeBinaries(join(options.cwd, options.outputDir)).then((output) => Promise.all(output.map(async (filePath) => {
debug$8.info(`Read [${colors.yellowBright(filePath)}]`);
const sourceContent = await readFileAsync(filePath);
const parsedName = parse(filePath);
const terms = parsedName.name.split(".");
const platformArchABI = terms.pop();
const _binaryName = terms.join(".");
if (_binaryName !== binaryName) {
debug$8.warn(`[${_binaryName}] is not matched with [${binaryName}], skip`);
return;
}
const dir$1 = distDirs.find((dir$2) => dir$2.includes(platformArchABI));
if (!dir$1 && universalSourceBins.has(platformArchABI)) {
debug$8.warn(`[${platformArchABI}] has no dist dir but it is source bin for universal arch, skip`);
return;
}
if (!dir$1) throw new Error(`No dist dir found for ${filePath}`);
const distFilePath = join(dir$1, parsedName.base);
debug$8.info(`Write file content to [${colors.yellowBright(distFilePath)}]`);
await writeFileAsync(distFilePath, sourceContent);
const distFilePathLocal = join(parse(packageJsonPath).dir, parsedName.base);
debug$8.info(`Write file content to [${colors.yellowBright(distFilePathLocal)}]`);
await writeFileAsync(distFilePathLocal, sourceContent);
})));
const wasiTarget = targets.find((t) => t.platform === "wasi");
if (wasiTarget) {
const wasiDir = join(options.cwd, options.npmDir, wasiTarget.platformArchABI);
const cjsFile = join(options.buildOutputDir ?? options.cwd, `${binaryName}.wasi.cjs`);
const workerFile = join(options.buildOutputDir ?? options.cwd, `wasi-worker.mjs`);
const browserEntry = join(options.buildOutputDir ?? options.cwd, `${binaryName}.wasi-browser.js`);
const browserWorkerFile = join(options.buildOutputDir ?? options.cwd, `wasi-worker-browser.mjs`);
debug$8.info(`Move wasi binding file [${colors.yellowBright(cjsFile)}] to [${colors.yellowBright(wasiDir)}]`);
await writeFileAsync(join(wasiDir, `${binaryName}.wasi.cjs`), await readFileAsync(cjsFile));
debug$8.info(`Move wasi worker file [${colors.yellowBright(workerFile)}] to [${colors.yellowBright(wasiDir)}]`);
await writeFileAsync(join(wasiDir, `wasi-worker.mjs`), await readFileAsync(workerFile));
debug$8.info(`Move wasi browser entry file [${colors.yellowBright(browserEntry)}] to [${colors.yellowBright(wasiDir)}]`);
await writeFileAsync(join(wasiDir, `${binaryName}.wasi-browser.js`), (await readFileAsync(browserEntry, "utf8")).replace(`new URL('./wasi-worker-browser.mjs', import.meta.url)`, `new URL('${packageName}-wasm32-wasi/wasi-worker-browser.mjs', import.meta.url)`));
debug$8.info(`Move wasi browser worker file [${colors.yellowBright(browserWorkerFile)}] to [${colors.yellowBright(wasiDir)}]`);
await writeFileAsync(join(wasiDir, `wasi-worker-browser.mjs`), await readFileAsync(browserWorkerFile));
}
}
async function collectNodeBinaries(root) {
const files = await readdirAsync(root, { withFileTypes: true });
const nodeBinaries = files.filter((file) => file.isFile() && (file.name.endsWith(".node") || file.name.endsWith(".wasm"))).map((file) => join(root, file.name));
const dirs = files.filter((file) => file.isDirectory());
for (const dir$1 of dirs) if (dir$1.name !== "node_modules") nodeBinaries.push(...await collectNodeBinaries(join(root, dir$1.name)));
return nodeBinaries;
}
//#endregion
//#region src/api/templates/js-binding.ts
function createCjsBinding(localName, pkgName, idents, packageVersion) {
return `${bindingHeader}
${createCommonBinding(localName, pkgName, packageVersion)}
module.exports = nativeBinding
${idents.map((ident) => `module.exports.${ident} = nativeBinding.${ident}`).join("\n")}
`;
}
function createEsmBinding(localName, pkgName, idents, packageVersion) {
return `${bindingHeader}
import { createRequire } from 'node:module'
const require = createRequire(import.meta.url)
const __dirname = new URL('.', import.meta.url).pathname
${createCommonBinding(localName, pkgName, packageVersion)}
const { ${idents.join(", ")} } = nativeBinding
${idents.map((ident) => `export { ${ident} }`).join("\n")}
`;
}
const bindingHeader = `// prettier-ignore
/* eslint-disable */
// @ts-nocheck
/* auto-generated by NAPI-RS */
`;
function createCommonBinding(localName, pkgName, packageVersion) {
function requireTuple(tuple, identSize = 8) {
const identLow = " ".repeat(identSize - 2);
const ident = " ".repeat(identSize);
return `try {
${ident}return require('./${localName}.${tuple}.node')
${identLow}} catch (e) {
${ident}loadErrors.push(e)
${identLow}}${packageVersion ? `
${identLow}try {
${ident}const binding = require('${pkgName}-${tuple}')
${ident}const bindingPackageVersion = require('${pkgName}-${tuple}/package.json').version
${ident}if (bindingPackageVersion !== '${packageVersion}' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
${ident} throw new Error(\`Native binding package version mismatch, expected ${packageVersion} but got \${bindingPackageVersion}. You can reinstall dependencies to fix this issue.\`)
${ident}}
${ident}return binding
${identLow}} catch (e) {
${ident}loadErrors.push(e)
${identLow}}` : `
${identLow}try {
${ident}return require('${pkgName}-${tuple}')
${identLow}} catch (e) {
${ident}loadErrors.push(e)
${identLow}}`}`;
}
return `const { readFileSync } = require('node:fs')
let nativeBinding = null
const loadErrors = []
const isMusl = () => {
let musl = false
if (process.platform === 'linux') {
musl = isMuslFromFilesystem()
if (musl === null) {
musl = isMuslFromReport()
}
if (musl === null) {
musl = isMuslFromChildProcess()
}
}
return musl
}
const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-')
const isMuslFromFilesystem = () => {
try {
return readFileSync('/usr/bin/ldd', 'utf-8').includes('musl')
} catch {
return null
}
}
const isMuslFromReport = () => {
let report = null
if (typeof process.report?.getReport === 'function') {
process.report.excludeNetwork = true
report = process.report.getReport()
}
if (!report) {
return null
}
if (report.header && report.header.glibcVersionRuntime) {
return false
}
if (Array.isArray(report.sharedObjects)) {
if (report.sharedObjects.some(isFileMusl)) {
return true
}
}
return false
}
const isMuslFromChildProcess = () => {
try {
return require('child_process').execSync('ldd --version', { encoding: 'utf8' }).includes('musl')
} catch (e) {
// If we reach this case, we don't know if the system is musl or not, so is better to just fallback to false
return false
}
}
function requireNative() {
if (process.env.NAPI_RS_NATIVE_LIBRARY_PATH) {
try {
return require(process.env.NAPI_RS_NATIVE_LIBRARY_PATH);
} catch (err) {
loadErrors.push(err)
}
} else if (process.platform === 'android') {
if (process.arch === 'arm64') {
${requireTuple("android-arm64")}
} else if (process.arch === 'arm') {
${requireTuple("android-arm-eabi")}
} else {
loadErrors.push(new Error(\`Unsupported architecture on Android \${process.arch}\`))
}
} else if (process.platform === 'win32') {
if (process.arch === 'x64') {
if (process.config?.variables?.shlib_suffix === 'dll.a' || process.config?.variables?.node_target_type === 'shared_library') {
${requireTuple("win32-x64-gnu")}
} else {
${requireTuple("win32-x64-msvc")}
}
} else if (process.arch === 'ia32') {
${requireTuple("win32-ia32-msvc")}
} else if (process.arch === 'arm64') {
${requireTuple("win32-arm64-msvc")}
} else {
loadErrors.push(new Error(\`Unsupported architecture on Windows: \${process.arch}\`))
}
} else if (process.platform === 'darwin') {
${requireTuple("darwin-universal", 6)}
if (process.arch === 'x64') {
${requireTuple("darwin-x64")}
} else if (process.arch === 'arm64') {
${requireTuple("darwin-arm64")}
} else {
loadErrors.push(new Error(\`Unsupported architecture on macOS: \${process.arch}\`))
}
} else if (process.platform === 'freebsd') {
if (process.arch === 'x64') {
${requireTuple("freebsd-x64")}
} else if (process.arch === 'arm64') {
${requireTuple("freebsd-arm64")}
} else {
loadErrors.push(new Error(\`Unsupported architecture on FreeBSD: \${process.arch}\`))
}
} else if (process.platform === 'linux') {
if (process.arch === 'x64') {
if (isMusl()) {
${requireTuple("linux-x64-musl", 10)}
} else {
${requireTuple("linux-x64-gnu", 10)}
}
} else if (process.arch === 'arm64') {
if (isMusl()) {
${requireTuple("linux-arm64-musl", 10)}
} else {
${requireTuple("linux-arm64-gnu", 10)}
}
} else if (process.arch === 'arm') {
if (isMusl()) {
${requireTuple("linux-arm-musleabihf", 10)}
} else {
${requireTuple("linux-arm-gnueabihf", 10)}
}
} else if (process.arch === 'loong64') {
if (isMusl()) {
${requireTuple("linux-loong64-musl", 10)}
} else {
${requireTuple("linux-loong64-gnu", 10)}
}
} else if (process.arch === 'riscv64') {
if (isMusl()) {
${requireTuple("linux-riscv64-musl", 10)}
} else {
${requireTuple("linux-riscv64-gnu", 10)}
}
} else if (process.arch === 'ppc64') {
${requireTuple("linux-ppc64-gnu")}
} else if (process.arch === 's390x') {
${requireTuple("linux-s390x-gnu")}
} else {
loadErrors.push(new Error(\`Unsupported architecture on Linux: \${process.arch}\`))
}
} else if (process.platform === 'openharmony') {
if (process.arch === 'arm64') {
${requireTuple("openharmony-arm64")}
} else if (process.arch === 'x64') {
${requireTuple("openharmony-x64")}
} else if (process.arch === 'arm') {
${requireTuple("openharmony-arm")}
} else {
loadErrors.push(new Error(\`Unsupported architecture on OpenHarmony: \${process.arch}\`))
}
} else {
loadErrors.push(new Error(\`Unsupported OS: \${process.platform}, architecture: \${process.arch}\`))
}
}
nativeBinding = requireNative()
if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) {
let wasiBinding = null
let wasiBindingError = null
try {
wasiBinding = require('./${localName}.wasi.cjs')
nativeBinding = wasiBinding
} catch (err) {
if (process.env.NAPI_RS_FORCE_WASI) {
wasiBindingError = err
}
}
if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) {
try {
wasiBinding = require('${pkgName}-wasm32-wasi')
nativeBinding = wasiBinding
} catch (err) {
if (process.env.NAPI_RS_FORCE_WASI) {
if (!wasiBindingError) {
wasiBindingError = err
} else {
wasiBindingError.cause = err
}
loadErrors.push(err)
}
}
}
if (process.env.NAPI_RS_FORCE_WASI === 'error' && !wasiBinding) {
const error = new Error('WASI binding not found and NAPI_RS_FORCE_WASI is set to error')
error.cause = wasiBindingError
throw error
}
}
if (!nativeBinding) {
if (loadErrors.length > 0) {
throw new Error(
\`Cannot find native binding. \` +
\`npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). \` +
'Please try \`npm i\` again after removing both package-lock.json and node_modules directory.',
{
cause: loadErrors.reduce((err, cur) => {
cur.cause = err
return cur
}),
},
)
}
throw new Error(\`Failed to load native binding\`)
}
`;
}
//#endregion
//#region src/api/templates/load-wasi-template.ts
const createWasiBrowserBinding = (wasiFilename, initialMemory = 4e3, maximumMemory = 65536, fs$1 = false, asyncInit = false, buffer = false) => {
return `import {
createOnMessage as __wasmCreateOnMessageForFsProxy,
getDefaultContext as __emnapiGetDefaultContext,
${asyncInit ? `instantiateNapiModule as __emnapiInstantiateNapiModule` : `instantiateNapiModuleSync as __emnapiInstantiateNapiModuleSync`},
WASI as __WASI,
} from '@napi-rs/wasm-runtime'
${fs$1 ? buffer ? `import { memfs, Buffer } from '@napi-rs/wasm-runtime/fs'` : `import { memfs } from '@napi-rs/wasm-runtime/fs'` : ""}
${buffer && !fs$1 ? `import { Buffer } from 'buffer'` : ""}
${fs$1 ? `
export const { fs: __fs, vol: __volume } = memfs()
const __wasi = new __WASI({
version: 'preview1',
fs: __fs,
preopens: {
'/': '/',
},
})` : `
const __wasi = new __WASI({
version: 'preview1',
})`}
const __wasmUrl = new URL('./${wasiFilename}.wasm', import.meta.url).href
const __emnapiContext = __emnapiGetDefaultContext()
${buffer ? "__emnapiContext.feature.Buffer = Buffer" : ""}
const __sharedMemory = new WebAssembly.Memory({
initial: ${initialMemory},
maximum: ${maximumMemory},
shared: true,
})
const __wasmFile = await fetch(__wasmUrl).then((res) => res.arrayBuffer())
const {
instance: __napiInstance,
module: __wasiModule,
napiModule: __napiModule,
} = ${asyncInit ? `await __emnapiInstantiateNapiModule` : `__emnapiInstantiateNapiModuleSync`}(__wasmFile, {
context: __emnapiContext,
asyncWorkPoolSize: 4,
wasi: __wasi,
onCreateWorker() {
const worker = new Worker(new URL('./wasi-worker-browser.mjs', import.meta.url), {
type: 'module',
})
${fs$1 ? ` worker.addEventListener('message', __wasmCreateOnMessageForFsProxy(__fs))\n` : ""}
return worker
},
overwriteImports(importObject) {
importObject.env = {
...importObject.env,
...importObject.napi,
...importObject.emnapi,
memory: __sharedMemory,
}
return importObject
},
beforeInit({ instance }) {
for (const name of Object.keys(instance.exports)) {
if (name.startsWith('__napi_register__')) {
instance.exports[name]()
}
}
},
})
`;
};
const createWasiBinding = (wasmFileName, packageName, initialMemory = 4e3, maximumMemory = 65536) => `/* eslint-disable */
/* prettier-ignore */
/* auto-generated by NAPI-RS */
const __nodeFs = require('node:fs')
const __nodePath = require('node:path')
const { WASI: __nodeWASI } = require('node:wasi')
const { Worker } = require('node:worker_threads')
const {
createOnMessage: __wasmCreateOnMessageForFsProxy,
getDefaultContext: __emnapiGetDefaultContext,
instantiateNapiModuleSync: __emnapiInstantiateNapiModuleSync,
} = require('@napi-rs/wasm-runtime')
const __rootDir = __nodePath.parse(process.cwd()).root
const __wasi = new __nodeWASI({
version: 'preview1',
env: process.env,
preopens: {
[__rootDir]: __rootDir,
}
})
const __emnapiContext = __emnapiGetDefaultContext()
const __sharedMemory = new WebAssembly.Memory({
initial: ${initialMemory},
maximum: ${maximumMemory},
shared: true,
})
let __wasmFilePath = __nodePath.join(__dirname, '${wasmFileName}.wasm')
const __wasmDebugFilePath = __nodePath.join(__dirname, '${wasmFileName}.debug.wasm')
if (__nodeFs.existsSync(__wasmDebugFilePath)) {
__wasmFilePath = __wasmDebugFilePath
} else if (!__nodeFs.existsSync(__wasmFilePath)) {
try {
__wasmFilePath = require.resolve('${packageName}-wasm32-wasi/${wasmFileName}.wasm')
} catch {
throw new Error('Cannot find ${wasmFileName}.wasm file, and ${packageName}-wasm32-wasi package is not installed.')
}
}
const { instance: __napiInstance, module: __wasiModule, napiModule: __napiModule } = __emnapiInstantiateNapiModuleSync(__nodeFs.readFileSync(__wasmFilePath), {
context: __emnapiContext,
asyncWorkPoolSize: (function() {
const threadsSizeFromEnv = Number(process.env.NAPI_RS_ASYNC_WORK_POOL_SIZE ?? process.env.UV_THREADPOOL_SIZE)
// NaN > 0 is false
if (threadsSizeFromEnv > 0) {
return threadsSizeFromEnv
} else {
return 4
}
})(),
reuseWorker: true,
wasi: __wasi,
onCreateWorker() {
const worker = new Worker(__nodePath.join(__dirname, 'wasi-worker.mjs'), {
env: process.env,
})
worker.onmessage = ({ data }) => {
__wasmCreateOnMessageForFsProxy(__nodeFs)(data)
}
// The main thread of Node.js waits for all the active handles before exiting.
// But Rust threads are never waited without \`thread::join\`.
// So here we hack the code of Node.js to prevent the workers from being referenced (active).
// According to https://github.com/nodejs/node/blob/19e0d472728c79d418b74bddff588bea70a403d0/lib/internal/worker.js#L415,
// a worker is consist of two handles: kPublicPort and kHandle.
{
const kPublicPort = Object.getOwnPropertySymbols(worker).find(s =>
s.toString().includes("kPublicPort")
);
if (kPublicPort) {
worker[kPublicPort].ref = () => {};
}
const kHandle = Object.getOwnPropertySymbols(worker).find(s =>
s.toString().includes("kHandle")
);
if (kHandle) {
worker[kHandle].ref = () => {};
}
worker.unref();
}
return worker
},
overwriteImports(importObject) {
importObject.env = {
...importObject.env,
...importObject.napi,
...importObject.emnapi,
memory: __sharedMemory,
}
return importObject
},
beforeInit({ instance }) {
for (const name of Object.keys(instance.exports)) {
if (name.startsWith('__napi_register__')) {
instance.exports[name]()
}
}
},
})
`;
//#endregion
//#region src/api/templates/wasi-worker-template.ts
const WASI_WORKER_TEMPLATE = `import fs from "node:fs";
import { createRequire } from "node:module";
import { parse } from "node:path";
import { WASI } from "node:wasi";
import { parentPort, Worker } from "node:worker_threads";
const require = createRequire(import.meta.url);
const { instantiateNapiModuleSync, MessageHandler, getDefaultContext } = require("@napi-rs/wasm-runtime");
if (parentPort) {
parentPort.on("message", (data) => {
globalThis.onmessage({ data });
});
}
Object.assign(globalThis, {
self: globalThis,
require,
Worker,
importScripts: function (f) {
;(0, eval)(fs.readFileSync(f, "utf8") + "//# sourceURL=" + f);
},
postMessage: function (msg) {
if (parentPort) {
parentPort.postMessage(msg);
}
},
});
const emnapiContext = getDefaultContext();
const __rootDir = parse(process.cwd()).root;
const handler = new MessageHandler({
onLoad({ wasmModule, wasmMemory }) {
const wasi = new WASI({
version: 'preview1',
env: process.env,
preopens: {
[__rootDir]: __rootDir,
},
});
return instantiateNapiModuleSync(wasmModule, {
childThread: true,
wasi,
context: emnapiContext,
overwriteImports(importObject) {
importObject.env = {
...importObject.env,
...importObject.napi,
...importObject.emnapi,
memory: wasmMemory
};
},
});
},
});
globalThis.onmessage = function (e) {
handler.handle(e);
};
`;
const createWasiBrowserWorkerBinding = (fs$1) => {
return `${fs$1 ? `import { instantiateNapiModuleSync, MessageHandler, WASI, createFsProxy } from '@napi-rs/wasm-runtime'
import { memfsExported as __memfsExported } from '@napi-rs/wasm-runtime/fs'
const fs = createFsProxy(__memfsExported)` : `import { instantiateNapiModuleSync, MessageHandler, WASI } from '@napi-rs/wasm-runtime'`}
const handler = new MessageHandler({
onLoad({ wasmModule, wasmMemory }) {
${fs$1 ? `const wasi = new WASI({
fs,
preopens: {
'/': '/',
},
print: function () {
// eslint-disable-next-line no-console
console.log.apply(console, arguments)
},
printErr: function() {
// eslint-disable-next-line no-console
console.error.apply(console, arguments)
},
})` : `const wasi = new WASI({
print: function () {
// eslint-disable-next-line no-console
console.log.apply(console, arguments)
},
printErr: function() {
// eslint-disable-next-line no-console
console.error.apply(console, arguments)
},
})`}
return instantiateNapiModuleSync(wasmModule, {
childThread: true,
wasi,
overwriteImports(importObject) {
importObject.env = {
...importObject.env,
...importObject.napi,
...importObject.emnapi,
memory: wasmMemory,
}
},
})
},
})
globalThis.onmessage = function (e) {
handler.handle(e)
}
`;
};
//#endregion
//#region src/api/build.ts
const debug$7 = debugFactory("build");
const require = createRequire(import.meta.url);
async function buildProject(rawOptions) {
debug$7("napi build command receive options: %O", rawOptions);
const options = {
dtsCache: true,
...rawOptions,
cwd: rawOptions.cwd ?? process.cwd()
};
const resolvePath = (...paths) => resolve(options.cwd, ...paths);
const manifestPath = resolvePath(options.manifestPath ?? "Cargo.toml");
const metadata = await parseMetadata(manifestPath);
const crate = metadata.packages.find((p) => {
if (options.package) return p.name === options.package;
else return p.manifest_path === manifestPath;
});
if (!crate) throw new Error("Unable to find crate to build. It seems you are trying to build a crate in a workspace, try using `--package` option to specify the package to build.");
return new Builder(metadata, crate, await readNapiConfig(resolvePath(options.packageJsonPath ?? "package.json"), options.configPath ? resolvePath(options.configPath) : void 0), options).build();
}
var Builder = class {
args = [];
envs = {};
outputs = [];
target;
crateDir;
outputDir;
targetDir;
enableTypeDef = false;
constructor(metadata, crate, config, options) {
this.metadata = metadata;
this.crate = crate;
this.config = config;
this.options = options;
this.target = options.target ? parseTriple(options.target) : process.env.CARGO_BUILD_TARGET ? parseTriple(process.env.CARGO_BUILD_TARGET) : getSystemDefaultTarget();
this.crateDir = parse(crate.manifest_path).dir;
this.outputDir = resolve(this.options.cwd, options.outputDir ?? this.crateDir);
this.targetDir = options.targetDir ?? process.env.CARGO_BUILD_TARGET_DIR ?? metadata.target_directory;
this.enableTypeDef = this.crate.dependencies.some((dep) => dep.name === "napi-derive" && (dep.uses_default_features || dep.features.includes("type-def")));
if (!this.enableTypeDef) {
const requirementWarning = "`napi-derive` crate is not used or `type-def` feature is not enabled for `napi-derive` crate";
debug$7.warn(`${requirementWarning}. Will skip binding generation for \`.node\`, \`.wasi\` and \`.d.ts\` files.`);
if (this.options.dts || this.options.dtsHeader || this.config.dtsHeader || this.config.dtsHeaderFile) debug$7.warn(`${requirementWarning}. \`dts\` related options are enabled but will be ignored.`);
}
}
get cdyLibName() {
var _this$crate$targets$f;
return (_this$crate$targets$f = this.crate.targets.find((t) => t.crate_types.includes("cdylib"))) === null || _this$crate$targets$f === void 0 ? void 0 : _this$crate$targets$f.name;
}
get binName() {
var _this$crate$targets$f2;
return this.options.bin ?? (this.cdyLibName ? null : (_this$crate$targets$f2 = this.crate.targets.find((t) => t.crate_types.includes("bin"))) === null || _this$crate$targets$f2 === void 0 ? void 0 : _this$crate$targets$f2.name);
}
build() {
if (!this.cdyLibName) {
const warning = "Missing `crate-type = [\"cdylib\"]` in [lib] config. The build result will not be available as node addon.";
if (this.binName) debug$7.warn(warning);
else throw new Error(warning);
}
return this.pickBinary().setPackage().setFeatures().setTarget().pickCrossToolchain().setEnvs().setBypassArgs().exec();
}
pickCrossToolchain() {
if (!this.options.useNapiCross) return this;
if (this.options.useCross) debug$7.warn("You are trying to use both `--cross` and `--use-napi-cross` options, `--use-cross` will be ignored.");
if (this.options.crossCompile) debug$7.warn("You are trying to use both `--cross-compile` and `--use-napi-cross` options, `--cross-compile` will be ignored.");
try {
var _process$env$TARGET_C, _process$env$CC, _process$env$CXX, _process$env$TARGET_C2;
const { version: version$2, download } = require("@napi-rs/cross-toolchain");
const alias = { "s390x-unknown-linux-gnu": "s390x-ibm-linux-gnu" };
const toolchainPath = join(homedir(), ".napi-rs", "cross-toolchain", version$2, this.target.triple);
mkdirSync(toolchainPath, { recursive: true });
if (existsSync(join(toolchainPath, "package.json"))) debug$7(`Toolchain ${toolchainPath} exists, skip extracting`);
else download(process.arch, this.target.triple).unpack(toolchainPath);
const upperCaseTarget = targetToEnvVar(this.target.triple);
const crossTargetName = alias[this.target.triple] ?? this.target.triple;
const linkerEnv = `CARGO_TARGET_${upperCaseTarget}_LINKER`;
this.setEnvIfNotExists(linkerEnv, join(toolchainPath, "bin", `${crossTargetName}-gcc`));
this.setEnvIfNotExists("TARGET_SYSROOT", join(toolchainPath, crossTargetName, "sysroot"));
this.setEnvIfNotExists("TARGET_AR", join(toolchainPath, "bin", `${crossTargetName}-ar`));
this.setEnvIfNotExists("TARGET_RANLIB", join(toolchainPath, "bin", `${crossTargetName}-ranlib`));
this.setEnvIfNotExists("TARGET_READELF", join(toolchainPath, "bin", `${crossTargetName}-readelf`));
this.setEnvIfNotExists("TARGET_C_INCLUDE_PATH", join(toolchainPath, crossTargetName, "sysroot", "usr", "include/"));
this.setEnvIfNotExists("TARGET_CC", join(toolchainPath, "bin", `${crossTargetName}-gcc`));
this.setEnvIfNotExists("TARGET_CXX", join(toolchainPath, "bin", `${crossTargetName}-g++`));
this.setEnvIfNotExists("BINDGEN_EXTRA_CLANG_ARGS", `--sysroot=${this.envs.TARGET_SYSROOT}}`);
if (((_process$env$TARGET_C = process.env.TARGET_CC) === null || _process$env$TARGET_C === void 0 ? void 0 : _process$env$TARGET_C.startsWith("clang")) || ((_process$env$CC = process.env.CC) === null || _process$env$CC === void 0 ? void 0 : _process$env$CC.startsWith("clang")) && !process.env.TARGET_CC) {
const TARGET_CFLAGS = process.env.TARGET_CFLAGS ?? "";
this.envs.TARGET_CFLAGS = `--sysroot=${this.envs.TARGET_SYSROOT} --gcc-toolchain=${toolchainPath} ${TARGET_CFLAGS}`;
}
if (((_process$env$CXX = process.env.CXX) === null || _process$env$CXX === void 0 ? void 0 : _process$env$CXX.startsWith("clang++")) && !process.env.TARGET_CXX || ((_process$env$TARGET_C2 = process.env.TARGET_CXX) === null || _process$env$TARGET_C2 === void 0 ? void 0 : _process$env$TARGET_C2.startsWith("clang++"))) {
const TARGET_CXXFLAGS = process.env.TARGET_CXXFLAGS ?? "";
this.envs.TARGET_CXXFLAGS = `--sysroot=${this.envs.TARGET_SYSROOT} --gcc-toolchain=${toolchainPath} ${TARGET_CXXFLAGS}`;
}
this.envs.PATH = this.envs.PATH ? `${toolchainPath}/bin:${this.envs.PATH}:${process.env.PATH}` : `${toolchainPath}/bin:${process.env.PATH}`;
} catch (e) {
debug$7.warn("Pick cross toolchain failed", e);
}
return this;
}
exec() {
debug$7(`Start building crate: ${this.crate.name}`);
debug$7(" %i", `cargo ${this.args.join(" ")}`);
const controller = new AbortController();
const watch = this.options.watch;
return {
task: new Promise((resolve$1, reject) => {
var _buildProcess$stderr;
if (this.options.useCross && this.options.crossCompile) throw new Error("`--use-cross` and `--cross-compile` can not be used together");
const buildProcess = spawn(process.env.CARGO ?? (this.options.useCross ? "cross" : "cargo"), this.args, {
env: {
...process.env,
...this.envs
},
stdio: watch ? [
"inherit",
"inherit",
"pipe"
] : "inherit",
cwd: this.options.cwd,
signal: controller.signal
});
buildProcess.once("exit", (code) => {
if (code === 0) {
debug$7("%i", `Build crate ${this.crate.name} successfully!`);
resolve$1();
} else reject(/* @__PURE__ */ new Error(`Build failed with exit code ${code}`));
});
buildProcess.once("error", (e) => {
reject(new Error(`Build failed with error: ${e.message}`, { cause: e }));
});
(_buildProcess$stderr = buildProcess.stderr) === null || _buildProcess$stderr === void 0 || _buildProcess$stderr.on("data", (data) => {
const output = data.toString();
console.error(output);
if (/Finished\s(`dev`|`release`)/.test(output)) this.postBuild().catch(() => {});
});
}).then(() => this.postBuild()),
abort: () => controller.abort()
};
}
pickBinary() {
let set = false;
if (this.options.watch) if (process.env.CI) debug$7.warn("Watch mode is not supported in CI environment");
else {
debug$7("Use %i", "cargo-watch");
tryInstallCargoBinary("cargo-watch", "watch");
this.args.push("watch", "--why", "-i", "*.{js,ts,node}", "-w", this.crateDir, "--", "cargo", "build");
set = true;
}
if (this.o