2022-05-19 00:40:55 +08:00
|
|
|
import * as assert from 'assert';
|
|
|
|
import chalk from 'chalk';
|
|
|
|
import { existsSync, readFileSync } from 'fs';
|
|
|
|
import { merge } from 'lodash';
|
|
|
|
import { isAbsolute, join, sep } from 'path';
|
|
|
|
import rimraf from 'rimraf';
|
|
|
|
import signale from 'signale';
|
|
|
|
import babel from './babel';
|
2023-08-02 00:07:52 +08:00
|
|
|
import { buildPluginClient, buildPluginServer, deleteJsFiles } from './buildPlugin';
|
2022-05-19 00:40:55 +08:00
|
|
|
import getUserConfig, { CONFIG_FILES } from './getUserConfig';
|
|
|
|
import randomColor from './randomColor';
|
|
|
|
import registerBabel from './registerBabel';
|
|
|
|
import rollup from './rollup';
|
|
|
|
import { Dispose, IBundleOptions, IBundleTypeOutput, ICjs, IEsm, IOpts } from './types';
|
2023-07-07 14:35:22 +08:00
|
|
|
import { getExistFiles, getLernaPackages } from './utils';
|
2022-05-19 00:40:55 +08:00
|
|
|
|
|
|
|
export function getBundleOpts(opts: IOpts): IBundleOptions[] {
|
|
|
|
const { cwd, buildArgs = {}, rootConfig = {} } = opts;
|
2023-07-07 14:35:22 +08:00
|
|
|
const entry = getExistFiles({
|
2022-05-19 00:40:55 +08:00
|
|
|
cwd,
|
2023-07-07 14:35:22 +08:00
|
|
|
files: [
|
|
|
|
'src/index.tsx',
|
|
|
|
'src/index.ts',
|
|
|
|
'src/index.jsx',
|
|
|
|
'src/index.js',
|
|
|
|
'src/server/index.ts',
|
|
|
|
'src/server/index.js',
|
|
|
|
'src/client/index.js',
|
|
|
|
'src/client/index.ts',
|
2023-08-02 00:07:52 +08:00
|
|
|
'src/client/index.tsx',
|
2023-07-07 14:35:22 +08:00
|
|
|
],
|
|
|
|
onlyOne: false,
|
2022-05-19 00:40:55 +08:00
|
|
|
returnRelative: true,
|
|
|
|
});
|
|
|
|
const userConfig = getUserConfig({ cwd, customPath: buildArgs.config });
|
|
|
|
const userConfigs = Array.isArray(userConfig) ? userConfig : [userConfig];
|
|
|
|
return (userConfigs as any).map((userConfig) => {
|
|
|
|
const bundleOpts = merge(
|
|
|
|
{
|
|
|
|
entry,
|
|
|
|
},
|
|
|
|
rootConfig,
|
|
|
|
userConfig,
|
2023-08-02 00:07:52 +08:00
|
|
|
buildArgs,
|
2022-05-19 00:40:55 +08:00
|
|
|
);
|
|
|
|
|
|
|
|
// Support config esm: 'rollup' and cjs: 'rollup'
|
|
|
|
if (typeof bundleOpts.esm === 'string') {
|
|
|
|
bundleOpts.esm = { type: bundleOpts.esm };
|
|
|
|
}
|
|
|
|
if (typeof bundleOpts.cjs === 'string') {
|
|
|
|
bundleOpts.cjs = { type: bundleOpts.cjs };
|
|
|
|
}
|
|
|
|
|
|
|
|
return bundleOpts;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function validateBundleOpts(bundleOpts: IBundleOptions, { cwd, rootPath }) {
|
|
|
|
if (bundleOpts.runtimeHelpers) {
|
|
|
|
const pkgPath = join(cwd, 'package.json');
|
|
|
|
assert.ok(existsSync(pkgPath), `@babel/runtime dependency is required to use runtimeHelpers`);
|
|
|
|
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
|
|
assert.ok(
|
|
|
|
(pkg.dependencies || {})['@babel/runtime'],
|
2023-08-02 00:07:52 +08:00
|
|
|
`@babel/runtime dependency is required to use runtimeHelpers`,
|
2022-05-19 00:40:55 +08:00
|
|
|
);
|
|
|
|
}
|
2023-08-02 00:07:52 +08:00
|
|
|
if (bundleOpts.cjs && (bundleOpts.cjs as ICjs).lazy && (bundleOpts.cjs as ICjs).type === 'rollup') {
|
2022-05-19 00:40:55 +08:00
|
|
|
throw new Error(
|
|
|
|
`
|
|
|
|
cjs.lazy don't support rollup.
|
2023-08-02 00:07:52 +08:00
|
|
|
`.trim(),
|
2022-05-19 00:40:55 +08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
if (!bundleOpts.esm && !bundleOpts.cjs && !bundleOpts.umd) {
|
|
|
|
throw new Error(
|
|
|
|
`
|
|
|
|
None format of ${chalk.cyan(
|
2023-08-02 00:07:52 +08:00
|
|
|
'cjs | esm | umd',
|
2022-05-19 00:40:55 +08:00
|
|
|
)} is configured, checkout https://github.com/umijs/father for usage details.
|
2023-08-02 00:07:52 +08:00
|
|
|
`.trim(),
|
2022-05-19 00:40:55 +08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
if (bundleOpts.entry) {
|
|
|
|
const tsConfigPath = join(cwd, 'tsconfig.json');
|
2023-08-02 00:07:52 +08:00
|
|
|
const tsConfig = existsSync(tsConfigPath) || (rootPath && existsSync(join(rootPath, 'tsconfig.json')));
|
2022-05-19 00:40:55 +08:00
|
|
|
if (
|
|
|
|
!tsConfig &&
|
|
|
|
((Array.isArray(bundleOpts.entry) && bundleOpts.entry.some(isTypescriptFile)) ||
|
|
|
|
(!Array.isArray(bundleOpts.entry) && isTypescriptFile(bundleOpts.entry)))
|
|
|
|
) {
|
2023-08-02 00:07:52 +08:00
|
|
|
signale.info(`Project using ${chalk.cyan('typescript')} but tsconfig.json not exists. Use default config.`);
|
2022-05-19 00:40:55 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function isTypescriptFile(filePath) {
|
|
|
|
return filePath.endsWith('.ts') || filePath.endsWith('.tsx');
|
|
|
|
}
|
|
|
|
|
2023-08-02 00:07:52 +08:00
|
|
|
function isPluginPackage(name: string) {
|
|
|
|
const prefixes = (process.env.PLUGIN_PACKAGE_PREFIX || '').split(',');
|
|
|
|
for (const prefix of prefixes) {
|
|
|
|
if (prefix.includes('preset')) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (name.startsWith(prefix)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-05-19 00:40:55 +08:00
|
|
|
interface IExtraBuildOpts {
|
|
|
|
pkg?: string | { name?: string };
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function build(opts: IOpts, extraOpts: IExtraBuildOpts = {}) {
|
|
|
|
const { cwd, rootPath, watch, buildArgs = {}, clean = true } = opts;
|
|
|
|
const { pkg } = extraOpts;
|
|
|
|
|
|
|
|
const dispose: Dispose[] = [];
|
|
|
|
|
|
|
|
const customConfigPath =
|
2023-08-02 00:07:52 +08:00
|
|
|
buildArgs.config && (isAbsolute(buildArgs.config) ? buildArgs.config : join(process.cwd(), buildArgs.config));
|
2022-05-19 00:40:55 +08:00
|
|
|
|
|
|
|
// register babel for config files
|
|
|
|
registerBabel({
|
|
|
|
cwd,
|
|
|
|
only: customConfigPath ? CONFIG_FILES.concat(customConfigPath) : CONFIG_FILES,
|
|
|
|
});
|
|
|
|
|
|
|
|
const pkgName = (typeof pkg === 'string' ? pkg : pkg?.name) || 'unknown';
|
|
|
|
|
2023-08-02 00:07:52 +08:00
|
|
|
function log(msg, ...args) {
|
|
|
|
console.log(`${pkg ? `${randomColor(`${pkgName}`)}: ` : ''}${msg}`, ...args);
|
2022-05-19 00:40:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Get user config
|
|
|
|
const bundleOptsArray = getBundleOpts(opts);
|
2023-08-02 00:07:52 +08:00
|
|
|
const isPlugin = isPluginPackage(pkgName);
|
|
|
|
|
|
|
|
// Clean dist
|
|
|
|
if (clean && !isPlugin) {
|
|
|
|
log(chalk.gray(`Clean dist directory`));
|
|
|
|
rimraf.sync(join(cwd, 'dist'));
|
|
|
|
}
|
2022-05-19 00:40:55 +08:00
|
|
|
|
|
|
|
for (const bundleOpts of bundleOptsArray) {
|
|
|
|
validateBundleOpts(bundleOpts, { cwd, rootPath });
|
|
|
|
|
|
|
|
// Build umd
|
|
|
|
if (bundleOpts.umd) {
|
|
|
|
log(`Build umd`);
|
|
|
|
await rollup({
|
|
|
|
cwd,
|
|
|
|
rootPath,
|
|
|
|
log,
|
|
|
|
type: 'umd',
|
|
|
|
entry: bundleOpts.entry,
|
|
|
|
watch,
|
|
|
|
dispose,
|
|
|
|
bundleOpts,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build cjs
|
|
|
|
if (bundleOpts.cjs) {
|
|
|
|
const cjs = bundleOpts.cjs as IBundleTypeOutput;
|
2023-08-02 00:07:52 +08:00
|
|
|
log(`Build ${isPlugin ? 'd.ts' : 'cjs'} with ${cjs.type}`);
|
2022-05-19 00:40:55 +08:00
|
|
|
if (cjs.type === 'babel') {
|
2023-08-02 00:07:52 +08:00
|
|
|
await babel({ cwd, rootPath, watch, dispose, isPlugin, type: 'cjs', log, bundleOpts });
|
|
|
|
if (isPlugin) {
|
|
|
|
log(cwd);
|
|
|
|
deleteJsFiles(cwd, log);
|
|
|
|
await buildPluginServer(cwd, log);
|
|
|
|
await buildPluginClient(cwd, log);
|
|
|
|
const buildFile = join(cwd, 'build.js');
|
|
|
|
if (existsSync(buildFile)) {
|
|
|
|
log('build others');
|
|
|
|
try {
|
|
|
|
await require(buildFile).run(log);
|
|
|
|
} catch (error) {
|
|
|
|
console.error(error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-05-19 00:40:55 +08:00
|
|
|
} else {
|
|
|
|
await rollup({
|
|
|
|
cwd,
|
|
|
|
rootPath,
|
|
|
|
log,
|
|
|
|
type: 'cjs',
|
|
|
|
entry: bundleOpts.entry,
|
|
|
|
watch,
|
|
|
|
dispose,
|
|
|
|
bundleOpts,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build esm
|
|
|
|
if (bundleOpts.esm) {
|
|
|
|
const esm = bundleOpts.esm as IEsm;
|
|
|
|
log(`Build esm with ${esm.type}`);
|
|
|
|
const importLibToEs = esm && esm.importLibToEs;
|
|
|
|
if (esm && esm.type === 'babel') {
|
|
|
|
await babel({ cwd, rootPath, watch, dispose, type: 'esm', importLibToEs, log, bundleOpts });
|
|
|
|
} else {
|
|
|
|
await rollup({
|
|
|
|
cwd,
|
|
|
|
rootPath,
|
|
|
|
log,
|
|
|
|
type: 'esm',
|
|
|
|
entry: bundleOpts.entry,
|
|
|
|
importLibToEs,
|
|
|
|
watch,
|
|
|
|
dispose,
|
|
|
|
bundleOpts,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return dispose;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getPkgRelativePath(cwd, pkg) {
|
|
|
|
const basePath = cwd.split(sep).join('/') + '/packages/';
|
|
|
|
const dir = pkg.contents.split(sep).join('/');
|
|
|
|
return dir.substring(basePath.length);
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function buildForLerna(opts: IOpts) {
|
|
|
|
const { cwd, rootConfig = {}, buildArgs = {}, packages = [] } = opts;
|
|
|
|
|
|
|
|
// register babel for config files
|
|
|
|
registerBabel({
|
|
|
|
cwd,
|
|
|
|
only: CONFIG_FILES,
|
|
|
|
});
|
|
|
|
|
|
|
|
const userConfig = merge(getUserConfig({ cwd }), rootConfig, buildArgs);
|
|
|
|
|
|
|
|
let pkgs = await getLernaPackages(cwd, userConfig.pkgFilter);
|
|
|
|
// support define pkgs in lerna
|
|
|
|
if (userConfig.pkgs) {
|
2023-08-02 00:07:52 +08:00
|
|
|
pkgs = pkgs.filter((pkg) => userConfig.pkgs.includes(getPkgRelativePath(cwd, pkg)));
|
2022-05-19 00:40:55 +08:00
|
|
|
}
|
|
|
|
const dispose: Dispose[] = [];
|
|
|
|
for (const pkg of pkgs) {
|
|
|
|
const pkgName = getPkgRelativePath(cwd, pkg);
|
|
|
|
if (userConfig.excludePkgs && userConfig.excludePkgs.includes(pkgName)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (packages.length && !packages.includes(pkgName)) continue;
|
|
|
|
// build error when .DS_Store includes in packages root
|
|
|
|
const pkgPath = pkg.contents;
|
2023-08-02 00:07:52 +08:00
|
|
|
assert.ok(existsSync(join(pkgPath, 'package.json')), `package.json not found in packages/${pkg}`);
|
2022-05-19 00:40:55 +08:00
|
|
|
process.chdir(pkgPath);
|
|
|
|
dispose.push(
|
|
|
|
...(await build(
|
|
|
|
{
|
|
|
|
// eslint-disable-line
|
|
|
|
...opts,
|
|
|
|
buildArgs: opts.buildArgs,
|
|
|
|
rootConfig: userConfig,
|
|
|
|
cwd: pkgPath,
|
|
|
|
rootPath: cwd,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
pkg,
|
2023-08-02 00:07:52 +08:00
|
|
|
},
|
|
|
|
)),
|
2022-05-19 00:40:55 +08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
return dispose;
|
|
|
|
}
|
|
|
|
|
|
|
|
export default async function (opts: IOpts) {
|
|
|
|
const useLerna = existsSync(join(opts.cwd, 'lerna.json'));
|
|
|
|
const isLerna = useLerna && process.env.LERNA !== 'none';
|
|
|
|
|
|
|
|
const dispose = isLerna ? await buildForLerna(opts) : await build(opts);
|
|
|
|
return () => dispose.forEach((e) => e());
|
|
|
|
}
|