bob-the-bundler
Version:
Bob The Bundler!
244 lines (243 loc) • 10.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.readPackageJson = exports.runifyCommand = exports.distDir = void 0;
const tslib_1 = require("tslib");
const globby_1 = tslib_1.__importDefault(require("globby"));
const fse = tslib_1.__importStar(require("fs-extra"));
const p_limit_1 = tslib_1.__importDefault(require("p-limit"));
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
const dependency_graph_1 = require("dependency-graph");
const path_1 = require("path");
const ncc_1 = tslib_1.__importDefault(require("@vercel/ncc"));
const tsup_1 = require("tsup");
const child_process_1 = require("child_process");
const command_1 = require("../command");
exports.distDir = "dist";
exports.runifyCommand = (0, command_1.createCommand)((api) => {
const { reporter } = api;
return {
command: "runify",
describe: "Runify",
builder(yargs) {
return yargs.options({
tag: {
describe: "Run only for following tags",
array: true,
type: "string",
},
});
},
async handler({ tag }) {
const [rootPackageJSONPath] = await (0, globby_1.default)("package.json", {
cwd: process.cwd(),
absolute: true,
});
const rootPackageJSON = await fse.readJSON(rootPackageJSONPath);
const isSinglePackage = Array.isArray(rootPackageJSON.workspaces) === false;
if (isSinglePackage) {
return runify((0, path_1.join)(process.cwd(), "package.json"), reporter);
}
const limit = (0, p_limit_1.default)(1);
const packageJsonFiles = await (0, globby_1.default)("packages/**/package.json", {
cwd: process.cwd(),
absolute: true,
ignore: ["**/node_modules/**", `**/${exports.distDir}/**`],
});
const packageJsons = await Promise.all(packageJsonFiles.map((packagePath) => fs_extra_1.default.readJSON(packagePath)));
const depGraph = new dependency_graph_1.DepGraph();
packageJsons.forEach((pkg, i) => {
var _a, _b;
depGraph.addNode(pkg.name, {
path: packageJsonFiles[i],
tags: (_b = (_a = pkg.buildOptions) === null || _a === void 0 ? void 0 : _a.tags) !== null && _b !== void 0 ? _b : [],
});
});
packageJsons.forEach((pkg) => {
var _a, _b, _c;
[
...Object.keys((_a = pkg.dependencies) !== null && _a !== void 0 ? _a : {}),
...Object.keys((_b = pkg.peerDependencies) !== null && _b !== void 0 ? _b : {}),
...Object.keys((_c = pkg.devDependencies) !== null && _c !== void 0 ? _c : {}),
]
.filter((dep) => depGraph.hasNode(dep))
.forEach((dep) => {
depGraph.addDependency(pkg.name, dep);
});
});
const ordered = depGraph.overallOrder();
await Promise.all(ordered.map((name) => {
const data = depGraph.getNodeData(name);
if (tag) {
if (!data.tags.some((t) => tag.includes(t))) {
return;
}
}
return limit(() => runify(depGraph.getNodeData(name).path, reporter));
}));
},
};
});
async function runify(packagePath, reporter) {
var _a, _b, _c, _d, _e, _f;
const cwd = packagePath.replace("/package.json", "");
const pkg = await readPackageJson(cwd);
const buildOptions = pkg.buildOptions || {};
if (!buildOptions.runify) {
return;
}
if (isNext(pkg)) {
const additionalRequire = (_d = (_c = (_b = (_a = pkg === null || pkg === void 0 ? void 0 : pkg.buildOptions) === null || _a === void 0 ? void 0 : _a.runify) === null || _b === void 0 ? void 0 : _b.next) === null || _c === void 0 ? void 0 : _c.header) !== null && _d !== void 0 ? _d : null;
await buildNext(cwd, additionalRequire);
await rewritePackageJson(pkg, cwd, (newPkg) => ({
...newPkg,
dependencies: pkg.dependencies,
type: "commonjs",
}));
}
else {
await compile(cwd, (_e = buildOptions.bin) !== null && _e !== void 0 ? _e : "src/index.ts", buildOptions, Object.keys((_f = pkg.dependencies) !== null && _f !== void 0 ? _f : {}), pkg.type === "module");
await rewritePackageJson(pkg, cwd);
}
reporter.success(`Built ${pkg.name}`);
}
async function readPackageJson(baseDir) {
return JSON.parse(await fs_extra_1.default.readFile((0, path_1.resolve)(baseDir, "package.json"), {
encoding: "utf-8",
}));
}
exports.readPackageJson = readPackageJson;
async function rewritePackageJson(pkg, cwd, modify) {
let filename = "index.js";
// only tsup keep the file name
if (pkg.buildOptions.bin && pkg.buildOptions.tsup) {
const bits = pkg.buildOptions.bin.split("/");
const lastBit = bits[bits.length - 1];
filename = lastBit.replace(".ts", ".js");
}
let newPkg = {
bin: filename,
};
const fields = [
"name",
"version",
"description",
"publishConfig",
"registry",
"repository",
"type",
];
fields.forEach((field) => {
if (typeof pkg[field] !== "undefined") {
newPkg[field] = pkg[field];
}
});
if (modify) {
newPkg = modify(newPkg);
}
await fs_extra_1.default.writeFile((0, path_1.join)(cwd, "dist/package.json"), JSON.stringify(newPkg, null, 2), {
encoding: "utf-8",
});
}
function isNext(pkg) {
var _a, _b;
return ((_a = pkg === null || pkg === void 0 ? void 0 : pkg.dependencies) === null || _a === void 0 ? void 0 : _a.next) || ((_b = pkg === null || pkg === void 0 ? void 0 : pkg.devDependencies) === null || _b === void 0 ? void 0 : _b.next);
}
async function buildNext(cwd, additionalRequire) {
await new Promise((resolve, reject) => {
const child = (0, child_process_1.spawn)("next", ["build"], {
stdio: "inherit",
cwd,
});
child.on("exit", (code) => (code ? reject(code) : resolve(code)));
child.on("error", reject);
});
await fs_extra_1.default.mkdirp((0, path_1.join)(cwd, "dist"));
if (additionalRequire) {
await (0, tsup_1.build)({
entryPoints: [(0, path_1.join)(cwd, additionalRequire)],
outDir: (0, path_1.join)(cwd, "dist"),
target: "node16",
format: ["cjs"],
splitting: false,
skipNodeModulesBundle: true,
});
}
await Promise.all([
fs_extra_1.default.copy((0, path_1.join)(cwd, ".next"), (0, path_1.join)(cwd, "dist/.next"), {
filter(src) {
// copy without webpack cache (it's 900mb...)
return src.includes("cache/webpack") === false;
},
}),
fs_extra_1.default.copy((0, path_1.join)(cwd, "public"), (0, path_1.join)(cwd, "dist/public")),
fs_extra_1.default.writeFile((0, path_1.join)(cwd, "dist/index.js"), [
`#!/usr/bin/env node`,
`process.on('SIGTERM', () => process.exit(0))`,
`process.on('SIGINT', () => process.exit(0))`,
additionalRequire
? `require('${additionalRequire.replace(".ts", "")}')`
: ``,
`
require('next/dist/server/lib/start-server').startServer({
dir: __dirname,
hostname: '0.0.0.0',
port: parseInt(process.env.PORT),
conf: {},
}).then(async (app)=>{
const appUrl = 'http://' + app.hostname + ':' + app.port;
console.log('started server on '+ app.hostname + ':' + app.port + ', url: ' + appUrl);
await app.prepare();
}).catch((err)=>{
console.error(err);
process.exit(1);
})
`,
].join("\n")),
]);
}
async function compile(cwd, entryPoint, buildOptions, dependencies, useEsm = false) {
if (buildOptions.tsup) {
const out = (0, path_1.join)(cwd, "dist");
await (0, tsup_1.build)({
entryPoints: [(0, path_1.join)(cwd, entryPoint)],
outDir: out,
target: "node16",
format: [useEsm ? "esm" : "cjs"],
splitting: false,
sourcemap: true,
clean: true,
shims: true,
skipNodeModulesBundle: false,
noExternal: dependencies,
external: buildOptions.external,
banner: buildOptions.banner
? {
js: buildOptions.banner.includes(".js") ||
buildOptions.banner.includes(".mjs")
? await fs_extra_1.default.readFile((0, path_1.join)(cwd, buildOptions.banner), "utf-8")
: buildOptions.banner,
}
: {},
});
return;
}
const { code, map, assets } = await (0, ncc_1.default)((0, path_1.join)(cwd, entryPoint), {
cache: false,
sourceMap: true,
});
await fs_extra_1.default.mkdirp((0, path_1.join)(cwd, "dist"));
await Promise.all([
fs_extra_1.default.writeFile((0, path_1.join)(cwd, "dist/index.js"), code, {
encoding: "utf-8",
}),
fs_extra_1.default.writeFile((0, path_1.join)(cwd, "dist/index.js.map"), map, {
encoding: "utf-8",
}),
].concat(Object.keys(assets).map(async (filepath) => {
if (filepath.endsWith("package.json")) {
return Promise.resolve();
}
await fs_extra_1.default.ensureDir((0, path_1.dirname)((0, path_1.join)(cwd, "dist", filepath)), {});
await fs_extra_1.default.writeFile((0, path_1.join)(cwd, "dist", filepath), assets[filepath].source);
})));
}