kotori-bot
Version:
Cross-platform chatbot framework base on Node.js and TypeScript
215 lines (214 loc) • 8.58 kB
JavaScript
/**
* @Package kotori-bot
* @Version 1.7.0
* @Author Arimura Sena <me@hotaru.icu>
* @Copyright 2024-2025 Hotaru. All rights reserved.
* @License BAN-ZHINESE-USING
* @Link https://github.com/kotorijs/kotori
* @Date 17:26:14
*/
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var build_exports = {};
__export(build_exports, {
default: () => build
});
module.exports = __toCommonJS(build_exports);
var import_get_packages = require("@manypkg/get-packages");
var import_shelljs = __toESM(require("shelljs"));
var import_node_path = __toESM(require("node:path"));
var import_node_fs = require("node:fs");
var import_node_events = require("node:events");
var import_common = require("./common");
var import_picomatch = require("picomatch");
var BuildType = /* @__PURE__ */ ((BuildType2) => {
BuildType2[BuildType2["Tsup"] = 0] = "Tsup";
BuildType2[BuildType2["ReScript"] = 1] = "ReScript";
BuildType2[BuildType2["None"] = 2] = "None";
return BuildType2;
})(BuildType || {});
const state = {
succeed: 0,
failed: 0
};
const defaultConfig = {
buildOptions: {
"*": {
clean: true,
// dts: true,
entryPoints: ["./src"],
bundle: false
// format: ['cjs', 'esm'],
// target: 'es2020',
// sourcemap: true,
}
},
banner: "",
include: [],
exclude: [],
ignoreError: false,
types: false,
onlyTypes: false,
silent: false
};
function generateBanner(template, pkg) {
return Object.entries({
name: pkg.name ?? "",
version: pkg.version ?? "",
author: Array.isArray(pkg.author) ? pkg.author.join(", ") : pkg.author ?? "",
license: pkg.license ?? "BAN-ZHINESE-USING",
date: (/* @__PURE__ */ new Date()).toLocaleTimeString()
}).reduce((acc, [key, value]) => acc.replaceAll(`%${key}%`, value), template);
}
function getBuildType(pkg, config) {
const hasRescript = (0, import_node_fs.existsSync)(import_node_path.default.join(pkg.dir, "rescript.json"));
if (hasRescript) {
return config.include.some((pattern) => (0, import_picomatch.isMatch)(pattern, pkg.packageJson.name)) ? 0 /* Tsup */ : 1 /* ReScript */;
}
return config.exclude.some((pattern) => (0, import_picomatch.isMatch)(pattern, pkg.packageJson.name)) ? 2 /* None */ : 0 /* Tsup */;
}
function getBuildConfig(pkg, config) {
const baseConfig = config.buildOptions["*"] || {};
const [tsconfig, _] = ["tsconfig.json", "package.json"].map((file) => {
const filepath = import_node_path.default.join(pkg.dir, file);
if (!(0, import_node_fs.existsSync)(filepath)) return {};
const str = (0, import_node_fs.readFileSync)(filepath, "utf-8");
try {
return JSON.parse(str);
} catch (e) {
throw new Error(`Parse Error at ${filepath}: ${String(e)}`);
}
});
const pkgConfig = {
outDir: tsconfig?.compilerOptions?.outDir ?? "./dist",
banner: {
// biome-ignore lint:
js: generateBanner(config.banner, pkg.packageJson)
}
};
const matchingConfigs = Object.entries(config.buildOptions).filter(([pattern]) => pattern !== "*" && (0, import_picomatch.isMatch)(pattern, pkg.packageJson.name)).map(([, config2]) => config2);
const options = { ...baseConfig, ...pkgConfig, ...matchingConfigs };
return options;
}
async function buildPackageByTsup(pkg, config) {
import_shelljs.default.cd(pkg.dir);
const tsupFile = import_node_path.default.resolve(import_common.CWD, "tsup.config.json");
try {
const cfg = getBuildConfig(pkg, config);
if (cfg.outDir && (0, import_node_fs.existsSync)(cfg.outDir)) (0, import_node_fs.rmSync)(cfg.outDir, { recursive: true });
(0, import_node_fs.writeFileSync)(tsupFile, JSON.stringify(cfg, null, 2));
if (!config.onlyTypes && import_shelljs.default.exec("pnpm exec tsup", { silent: config.silent }).code !== 0) {
throw new Error(`Failed to build ${pkg.packageJson.name}`);
}
const isMainPack = pkg.packageJson.name === "kotori-bot";
if ((config.types || config.onlyTypes) && (!pkg.dir.includes("packages") || isMainPack)) {
const child = import_shelljs.default.exec("tsc --build", {}, () => {
});
if (!config.silent) process.stdin.on("data", (data) => child.stdin?.write(data));
await new Promise(
(resolve, reject) => child.on(
"exit",
(code) => code === 0 ? resolve(null) : reject(new Error(`Failed to build ${pkg.packageJson.name} types`))
)
);
}
state.succeed += 1;
} catch (err) {
state.failed += 1;
console.error(err);
if (!config.ignoreError) import_shelljs.default.exit(1);
} finally {
if ((0, import_node_fs.existsSync)(tsupFile)) (0, import_node_fs.rmSync)(tsupFile);
import_shelljs.default.cd(import_common.CWD);
}
}
async function buildPackageByReScript(pkg, config) {
import_shelljs.default.cd(pkg.dir);
try {
if (import_shelljs.default.exec("pnpm exec rescript", { silent: config.silent }).code !== 0) {
throw new Error(`Failed to build ${pkg.packageJson.name} with rescript`);
}
state.succeed += 1;
} catch (err) {
state.failed += 1;
console.error(err);
if (!config.ignoreError) import_shelljs.default.exit(1);
} finally {
import_shelljs.default.cd(import_common.CWD);
}
}
async function build(initConfig, filters) {
const start = Date.now();
try {
(0, import_common.ensureWorkspaceRoot)();
const config = {
...defaultConfig,
...initConfig,
buildOptions: { ...defaultConfig.buildOptions, ...initConfig.buildOptions }
};
if (config.silent) globalThis.console.log = () => {
};
const pkgs = /* @__PURE__ */ ((pkgs2) => pkgs2)((0, import_get_packages.getPackagesSync)(import_common.CWD).packages.filter((pkg) => (0, import_common.matchesFilter)(pkg, filters)));
if (pkgs.length === 0) {
console.log("No packages matched the filter criteria");
return;
}
if (config.onlyTypes || config.types) {
pkgs.map((pkg) => import_node_path.default.join(pkg.dir, "tsconfig.tsbuildinfo")).filter(import_node_fs.existsSync).map((file) => (0, import_node_fs.rmSync)(file));
}
console.log(`Building ${pkgs.length} packages...`);
(0, import_node_events.setMaxListeners)(Number.POSITIVE_INFINITY);
await Promise.all(
pkgs.map((pkg) => {
switch (getBuildType(pkg, config)) {
case 0 /* Tsup */:
console.log(`Building ${pkg.packageJson.name} ...`);
return buildPackageByTsup(pkg, config);
case 1 /* ReScript */:
if (config.onlyTypes) {
console.log(`Skiped to build ${pkg.packageJson.name} with rescript`);
return;
}
console.log(`Building ${pkg.packageJson.name} with rescript...`);
return buildPackageByReScript(pkg, config);
default:
console.log(`Skipping ${pkg.packageJson.name}...`);
return Promise.resolve();
}
})
);
console.log(
`Build summary: ${state.succeed} succeed, ${state.failed} failed, ${pkgs.length - state.succeed - state.failed} skipped, in ${(Date.now() - start) / 1e3}s.`
);
import_shelljs.default.exit(0);
} catch (error) {
console.error("Build failed:", error);
import_shelljs.default.exit(1);
}
}