beesbuild
Version:
构建工具链
239 lines (238 loc) • 8.97 kB
JavaScript
import path from "path";
import chalk from "chalk-unified";
import {
consola,
getBuildConfig,
handleManifest,
parseManifest,
pathExtra
} from "@beesbuild/utils";
import fs, { existsSync, mkdirSync, writeFileSync } from "fs-extra";
import jiti from "jiti";
import { resolveModuleExportNames } from "mlly";
import { fromPairs } from "lodash-unified";
import * as prettier from "prettier";
import { resolveAliases } from "../utils.mjs";
import { DEFAULT_EXTENSIONS } from "../variables.mjs";
import { getShebang, makeExecutable } from "./plugins/shebang.mjs";
import { createTypesProject } from "./untyped.mjs";
function tryResolve(id, rootDir = process.cwd()) {
const _require = jiti(rootDir, { interopDefault: true, esmResolve: true });
try {
return _require.resolve(id);
} catch (error) {
if (error.code !== "MODULE_NOT_FOUND") {
consola.error(`Error trying import ${id} from ${rootDir}`, error);
}
return id;
}
}
const stubBuild = async (module, ctx) => {
const rootDir = ctx.options.rootDir;
const outDir = ctx.options.outDir || ".";
const entryFile = ctx.options.entries.find(
({ isEntry }) => isEntry
);
let entryFilepath = (entryFile == null ? void 0 : entryFile.input) ? path.resolve(rootDir, entryFile.input) : path.resolve(rootDir, "src");
const buildConfig = fromPairs(
getBuildConfig({
rootDir,
outDir,
manifest: ctx.pkg,
rollup: {
emitCJS: true,
emitDts: true,
emitUmd: true
},
entryFile: entryFile == null ? void 0 : entryFile.input
})
);
const { output, ext } = buildConfig[module];
const dir = path.resolve(output.path, `index.${ext}`);
if (existsSync(dir)) return;
const info = parseManifest(entryFilepath);
entryFilepath = path.join(info.dir, info.name);
const d = path.relative(path.dirname(dir), entryFilepath);
const ddir = pathExtra.join(d.startsWith(".") ? d : `./${d}`);
consola.info(`\u3010Start:d\u3011for stub types file: ${d} generated`);
consola.info(`\u3010Start:dir\u3011for stub types file: ${buildConfig.dts.output.name} generated`);
consola.info(`\u3010Start:ddir\u3011for stub types file: ${ddir} generated`);
const code = [];
switch (module) {
case "cjs":
code.push(`module.exports = require('${ddir}');`);
break;
default:
code.push(`export * from '${ddir}';`, `export { default } from '${ddir}';`);
break;
}
const codeStr = await prettier.format(code.join("\n"), {
parser: "typescript",
singleQuote: true
});
consola.success(chalk.cyan(`\u3010End\u3011for stub types file: ${dir} generated`));
if (!existsSync(path.dirname(dir))) {
mkdirSync(path.dirname(dir), {
recursive: true
});
}
writeFileSync(dir, codeStr, "utf8");
return Promise.resolve();
};
const runBrowserRollupStub = async (modules, ctx) => {
if (modules.includes("dts")) {
let typesProject = ctx.caches[ctx.uid]["typesProject"];
if (!typesProject) {
typesProject = createTypesProject(ctx);
ctx.caches[ctx.uid]["typesProject"] = typesProject;
const sourceFiles = typesProject.addSourceFiles();
consola.warn(sourceFiles.length, "sourceFiles");
}
typesProject.typeCheck(true);
}
return Promise.allSettled(modules.map((module) => stubBuild(module, ctx)));
};
const rollupStub = async (ctx) => {
var _a, _b;
if (((_a = ctx.options) == null ? void 0 : _a.runEnv) === "Browser") {
return runBrowserRollupStub(["esm", "cjs", "dts"], ctx);
}
const rootDir = ctx.options.rootDir;
const serializedJitiOptions = (_dirname) => {
const JitiOptions = {
...ctx.options.stubOptions.jiti,
alias: {
...resolveAliases(ctx),
...ctx.options.stubOptions.jiti.alias
}
};
for (const [key, p] of Object.entries(JitiOptions.alias)) {
const pp = path.join(_dirname, path.relative(rootDir, p));
JitiOptions.alias[key] = `${pp}/`;
}
return JSON.stringify(JitiOptions, null, 2);
};
const { esm, cjs, dts } = handleManifest(ctx.pkg, ctx.options.rootDir);
const entries = ctx.options.entries.filter(
(entry) => entry.builder === "rollup" && entry.isEntry
);
for (const option of entries) {
const { input, name } = option;
const outDir = path.join(ctx.options.outDir, name != null ? name : "dist");
const output = path.resolve(rootDir, outDir);
const resolvedEntry = path.normalize(tryResolve(input, rootDir) || input);
const resolvedEntryWithoutExt = resolvedEntry.slice(
0,
Math.max(0, resolvedEntry.length - path.extname(resolvedEntry).length)
);
const code = await fs.readFile(resolvedEntry, "utf8");
const shebang = getShebang(code);
let dirname = path.dirname(output);
await fs.mkdir(dirname, { recursive: true });
const rollup = ctx.options.rollup;
let cjsOutput = `${output}.${cjs.ext}`;
if (rollup.emitCJS) {
if (ctx.pkg.main) {
const dir = path.dirname(path.resolve(ctx.options.rootDir, cjs.fullPath));
if (dir !== dirname) {
dirname = dir;
await fs.mkdir(dirname, { recursive: true });
}
cjsOutput = path.resolve(ctx.options.rootDir, cjs.fullPath);
}
const _dirname2 = path.relative(path.dirname(cjsOutput), rootDir);
const cjsPath = path.join(_dirname2, path.relative(rootDir, input));
const code2 = [
shebang,
`const jiti = require('jiti');`,
`const path = require('node:path');`,
`const cjsPath = path.resolve(__dirname, '${cjsPath}')`,
"",
`/** @type { import('${cjsPath}') } */`,
`const _module = jiti(null, ${serializedJitiOptions(_dirname2)})(cjsPath);`,
"",
"module.exports = _module;",
""
].join("\n");
await fs.writeFile(cjsOutput, code2);
}
const namedExports = await resolveModuleExportNames(resolvedEntry, {
extensions: DEFAULT_EXTENSIONS
}).catch((error) => {
consola.warn(ctx, `Cannot analyze ${resolvedEntry} for exports:${error}`);
return [];
});
const hasDefaultExport = namedExports.includes("default") || namedExports.length === 0;
let esmOutput = `${output}.${esm.ext}`;
if (ctx.pkg.module) {
const dir = path.dirname(path.resolve(ctx.options.rootDir, esm.fullPath));
if (dir !== dirname) {
dirname = dir;
await fs.mkdir(dirname, { recursive: true });
}
esmOutput = path.resolve(ctx.options.rootDir, esm.fullPath);
}
if (((_b = ctx.argv) == null ? void 0 : _b.runEnv) === "Browser") {
const code2 = [
shebang,
`/** @type {import(${JSON.stringify(resolvedEntryWithoutExt)})} */`,
`export * from ${JSON.stringify(resolvedEntry)};`,
"",
`/** @type {import(${JSON.stringify(resolvedEntryWithoutExt)})} */`,
`import * as _module from ${JSON.stringify(resolvedEntry)};`,
hasDefaultExport ? "\nexport default _module;" : "",
...namedExports.filter((name2) => name2 !== "default").map((name2) => `export const ${name2} = _module.${name2};`),
""
].join("\n");
await fs.writeFile(esmOutput, code2);
} else {
const _dirname2 = path.relative(path.dirname(esmOutput), rootDir);
const esmPath = path.join(_dirname2, path.relative(rootDir, input));
const code2 = [
shebang,
`import jiti from 'jiti';`,
`import path from 'node:path';`,
"",
`const pathname = new URL(".", import.meta.url).pathname;`,
`const mjsPath = path.resolve(pathname, '${esmPath}');`,
`/** @type {import(${JSON.stringify(esmPath)})} */`,
`const _module = jiti(null, ${serializedJitiOptions(_dirname2)})(mjsPath);`,
"",
hasDefaultExport ? "export default _module;" : "",
"",
...namedExports.filter((name2) => name2 !== "default").map((name2) => `export const ${name2} = _module.${name2};`),
""
].join("\n");
await fs.writeFile(esmOutput, code2);
}
let dtsOutput = `${output}.d.ts`;
if (ctx.pkg.typings || ctx.pkg.types) {
const dir = path.dirname(path.resolve(ctx.options.rootDir, dts.fullPath));
if (dir !== dirname) {
dirname = dir;
await fs.mkdir(dirname, { recursive: true });
}
dtsOutput = path.resolve(ctx.options.rootDir, dts.fullPath);
}
const _dirname = path.relative(path.dirname(dtsOutput), rootDir);
const typesPath = path.join(_dirname, path.relative(rootDir, input));
const dtsCode = [
`export * from '${typesPath}';`,
hasDefaultExport ? `export { default } from '${typesPath}';` : "",
""
].join("\n");
await fs.writeFile(dtsOutput, dtsCode);
if (shebang) {
await makeExecutable(cjsOutput);
await makeExecutable(esmOutput);
}
}
await ctx.hooks.callHook("rollup:done", ctx);
return;
};
export {
rollupStub,
runBrowserRollupStub,
stubBuild,
tryResolve
};