@iobroker/adapter-dev
Version:
All developer dependencies an ioBroker adapter developer needs
241 lines • 10.3 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleBuildReactCommand = handleBuildReactCommand;
exports.handleBuildTypeScriptCommand = handleBuildTypeScriptCommand;
exports.handleBuildAllCommand = handleBuildAllCommand;
/** The build script to use esbuild without specifying 1000 CLI options */
// @ts-expect-error esm2cjs/execa needs to be fixed to allow cjs import with types
const execa_1 = require("@esm2cjs/execa");
const ansi_colors_1 = require("ansi-colors");
const esbuild_1 = require("esbuild");
const node_path_1 = __importDefault(require("node:path"));
const tiny_glob_1 = __importDefault(require("tiny-glob"));
const util_1 = require("./util");
const fs_extra_1 = require("fs-extra");
async function findTsc() {
try {
const packageJsonPath = require.resolve("typescript/package.json");
const packageJson = await (0, fs_extra_1.readJson)(packageJsonPath);
const binPath = packageJson.bin.tsc;
return node_path_1.default.join(node_path_1.default.dirname(packageJsonPath), binPath);
}
catch (e) {
(0, util_1.die)(`Could not find tsc executable: ${e.message}`);
}
}
/** Helper function to determine file paths that serve as input for React builds */
async function getReactFilePaths(reactOptions) {
let entryPoints = await (0, tiny_glob_1.default)(`${reactOptions.rootDir}/${reactOptions.pattern}`);
entryPoints = entryPoints.filter((ep) => !ep.endsWith(".d.ts") &&
!ep.endsWith(".test.ts") &&
!ep.endsWith(".test.tsx"));
const tsConfigPath = `${reactOptions.rootDir}/${reactOptions.tsConfig}`;
return { entryPoints, tsConfigPath };
}
/** Helper function to determine file paths that serve as input for TypeScript builds */
async function getTypeScriptFilePaths(typescriptOptions) {
let entryPoints = await (0, tiny_glob_1.default)(`${typescriptOptions.rootDir}/${typescriptOptions.pattern}`);
entryPoints = entryPoints.filter((ep) => !ep.endsWith(".d.ts") && !ep.endsWith(".test.ts"));
const tsConfigPath = `${typescriptOptions.rootDir}/${typescriptOptions.tsConfig}`;
return { entryPoints, tsConfigPath };
}
/** Type-checks the project with the given tsconfig path */
async function typeCheck(tsConfigPath) {
console.log();
console.log((0, ansi_colors_1.gray)(`Type-checking ${tsConfigPath} with tsc...`));
const tscPath = await findTsc();
try {
await (0, execa_1.execaNode)(tscPath, `-p ${tsConfigPath} --noEmit`.split(" "), {
stdout: "inherit",
stderr: "inherit",
});
console.error((0, ansi_colors_1.green)(`✔ Type-checking ${tsConfigPath} succeeded!`));
return true;
}
catch {
console.error((0, ansi_colors_1.red)(`❌ Type-checking ${tsConfigPath} failed!`));
return false;
}
}
async function typeCheckWatch(tsConfigPath) {
console.log();
console.log((0, ansi_colors_1.gray)(`Type-checking ${tsConfigPath} with tsc in watch mode...`));
const tscPath = await findTsc();
return (0, execa_1.execaNode)(tscPath, `-p ${tsConfigPath} --noEmit --watch --preserveWatchOutput`.split(" "), { stdout: "inherit", stderr: "inherit" });
}
function getReactBuildOptions(watch, reactOptions, entryPoints, tsConfigPath) {
return {
entryPoints,
tsconfig: tsConfigPath,
outdir: `${reactOptions.rootDir}/${(watch && reactOptions.watchDir) || reactOptions.outDir}`,
bundle: reactOptions.bundle,
format: reactOptions.format,
// Splitting moves common code from multiple entry points into separate files
// This is only relevant for React builds with output format ESM though
splitting: !watch &&
entryPoints.length > 1 &&
reactOptions.splitting &&
reactOptions.bundle &&
reactOptions.format === "esm",
target: reactOptions.compileTarget,
minify: !watch,
sourcemap: true,
logLevel: "info",
define: {
"process.env.NODE_ENV": watch ? '"development"' : '"production"',
},
...reactOptions.raw,
};
}
function getTypeScriptBuildOptions(watch, typescriptOptions, entryPoints, tsConfigPath) {
return {
entryPoints,
tsconfig: tsConfigPath,
outdir: `${typescriptOptions.rootDir}/${typescriptOptions.outDir}`,
bundle: typescriptOptions.bundle,
minify: false,
sourcemap: true,
logLevel: "info",
platform: "node",
format: typescriptOptions.format || "cjs",
target: typescriptOptions.compileTarget,
define: {
"process.env.NODE_ENV": watch ? '"development"' : '"production"',
},
...typescriptOptions.raw,
};
}
async function buildReact(options) {
const { entryPoints, tsConfigPath } = await getReactFilePaths(options);
// Building React happens in one or two steps:
// 1. fast compile with ESBuild
console.log();
console.log((0, ansi_colors_1.gray)("Compiling React with ESBuild..."));
await (0, esbuild_1.build)(getReactBuildOptions(false, options, entryPoints, tsConfigPath));
// 2. type-check with TypeScript (if there are TSX entry points)
if (entryPoints.some((e) => e.endsWith(".tsx"))) {
if (!(await typeCheck(tsConfigPath))) {
process.exit(1);
}
}
}
async function buildTypeScript(options) {
const { entryPoints, tsConfigPath } = await getTypeScriptFilePaths(options);
// Building TS happens in two steps:
// 1. fast compile with ESBuild
console.log();
console.log((0, ansi_colors_1.gray)("Compiling TypeScript with ESBuild..."));
await (0, esbuild_1.build)(getTypeScriptBuildOptions(false, options, entryPoints, tsConfigPath));
// 2. type-check with TypeScript
if (!(await typeCheck(tsConfigPath))) {
process.exit(1);
}
}
async function buildAll(reactOptions, typescriptOptions) {
await Promise.all([
buildReact(reactOptions),
buildTypeScript(typescriptOptions),
]);
}
async function watchReact(options) {
const { entryPoints, tsConfigPath } = await getReactFilePaths(options);
// Building React happens in one or two steps:
// 1. fast compile with ESBuild
console.log();
console.log((0, ansi_colors_1.gray)("Compiling React with ESBuild in watch mode..."));
const buildCtx = await (0, esbuild_1.context)({
...getReactBuildOptions(true, options, entryPoints, tsConfigPath),
// We could run a separate type checking process after each successful
});
void buildCtx.watch();
// 2. type-check with TypeScript (if there are TSX entry points)
let checkProcess;
if (entryPoints.some((e) => e.endsWith(".tsx"))) {
checkProcess = await typeCheckWatch(tsConfigPath);
}
return { ctx: buildCtx, check: checkProcess };
}
async function watchTypeScript(options) {
const { entryPoints, tsConfigPath } = await getTypeScriptFilePaths(options);
// Building TS happens in two steps:
// 1. fast compile with ESBuild
console.log();
console.log((0, ansi_colors_1.gray)("Compiling TypeScript with ESBuild..."));
const buildCtx = await (0, esbuild_1.context)({
...getTypeScriptBuildOptions(true, options, entryPoints, tsConfigPath),
// We could run a separate type checking process after each successful
});
void buildCtx.watch();
// 2. type-check with TypeScript
const checkProcess = await typeCheckWatch(tsConfigPath);
return { ctx: buildCtx, check: checkProcess };
}
// Entry points for the CLI
async function handleBuildReactCommand(watch, options) {
if (watch) {
// In watch mode, we start the ESBuild and TSC processes in parallel and wait until they end
const { ctx, check } = await watchReact(options);
return new Promise((resolve) => {
check === null || check === void 0 ? void 0 : check.then(() => resolve()).catch(() => resolve());
process.on("SIGINT", () => {
console.log();
console.log((0, ansi_colors_1.gray)("SIGINT received, shutting down..."));
ctx.dispose();
if (check) {
check.kill("SIGINT");
}
else {
resolve();
}
});
});
}
else {
await buildReact(options);
}
}
async function handleBuildTypeScriptCommand(watch, options) {
if (watch) {
// In watch mode, we start the ESBuild and TSC processes in parallel and wait until they end
const { ctx, check } = await watchTypeScript(options);
return new Promise((resolve) => {
check.then(() => resolve()).catch(() => resolve());
process.on("SIGINT", () => {
console.log();
console.log((0, ansi_colors_1.gray)("SIGINT received, shutting down..."));
ctx.dispose();
check.kill("SIGINT");
});
});
}
else {
await buildTypeScript(options);
}
}
async function handleBuildAllCommand(watch, reactOptions, typescriptOptions) {
if (watch) {
// In watch mode, we start the ESBuild and TSC processes in parallel and wait until they end
const { ctx: ctxReact, check: checkReact } = await watchReact(reactOptions);
const { ctx: ctxTS, check: checkTS } = await watchTypeScript(typescriptOptions);
return new Promise((resolve) => {
Promise.all([checkReact, checkTS].filter(Boolean))
.then(() => resolve())
.catch(() => resolve());
process.on("SIGINT", () => {
console.log();
console.log((0, ansi_colors_1.gray)("SIGINT received, shutting down..."));
ctxReact.dispose();
ctxTS.dispose();
checkReact === null || checkReact === void 0 ? void 0 : checkReact.kill("SIGINT");
checkTS.kill("SIGINT");
});
});
}
else {
await buildAll(reactOptions, typescriptOptions);
}
}
//# sourceMappingURL=build-adapter-handlers.js.map