nocobase/packages/father-build/src/getRollupConfig.ts

319 lines
9.7 KiB
TypeScript
Raw Normal View History

2021-05-23 00:38:08 +00:00
import { existsSync } from 'fs'
import { basename, extname, join } from 'path';
import { ModuleFormat, RollupOptions } from 'rollup';
import url from '@rollup/plugin-url';
import json from '@rollup/plugin-json';
import replace from '@rollup/plugin-replace';
import commonjs from '@rollup/plugin-commonjs';
import nodeResolve from '@rollup/plugin-node-resolve';
import inject, { RollupInjectOptions } from '@rollup/plugin-inject';
import babel, { RollupBabelInputPluginOptions } from '@rollup/plugin-babel';
import postcss from 'rollup-plugin-postcss';
import { terser } from 'rollup-plugin-terser';
import typescript2 from 'rollup-plugin-typescript2';
import { camelCase } from 'lodash';
import tempDir from 'temp-dir';
import autoprefixer from 'autoprefixer';
import NpmImport from 'less-plugin-npm-import';
import svgr from '@svgr/rollup';
import getBabelConfig from './getBabelConfig';
import { IBundleOptions } from './types';
interface IGetRollupConfigOpts {
cwd: string;
rootPath: string;
entry: string;
type: ModuleFormat;
importLibToEs?: boolean;
bundleOpts: IBundleOptions;
}
interface IPkg {
dependencies?: Object;
peerDependencies?: Object;
name?: string;
}
export default function(opts: IGetRollupConfigOpts): RollupOptions[] {
const { type, entry, cwd, rootPath, importLibToEs, bundleOpts } = opts;
const {
umd,
esm,
cjs,
file,
target = 'browser',
extractCSS = false,
injectCSS = true,
cssModules: modules,
extraPostCSSPlugins = [],
extraBabelPresets = [],
extraBabelPlugins = [],
extraRollupPlugins = [],
autoprefixer: autoprefixerOpts,
include = /node_modules/,
runtimeHelpers: runtimeHelpersOpts,
replace: replaceOpts,
inject: injectOpts,
extraExternals = [],
externalsExclude = [],
nodeVersion,
typescriptOpts,
nodeResolveOpts = {},
disableTypeCheck,
lessInRollupMode = {},
sassInRollupMode = {},
} = bundleOpts;
const entryExt = extname(entry);
const name = file || basename(entry, entryExt);
const isTypeScript = entryExt === '.ts' || entryExt === '.tsx';
const extensions = ['.js', '.jsx', '.ts', '.tsx', '.es6', '.es', '.mjs'];
let pkg = {} as IPkg;
try {
pkg = require(join(cwd, 'package.json')); // eslint-disable-line
} catch (e) {}
// cjs 不给浏览器用,所以无需 runtimeHelpers
const runtimeHelpers = type === 'cjs' ? false : runtimeHelpersOpts;
const babelOpts = {
...(getBabelConfig({
type,
target: type === 'esm' ? 'browser' : target,
// watch 模式下有几率走的 babel原因未知。
// ref: https://github.com/umijs/father/issues/158
typescript: true,
runtimeHelpers,
nodeVersion,
}).opts),
// ref: https://github.com/rollup/plugins/tree/master/packages/babel#babelhelpers
babelHelpers: (runtimeHelpers ? 'runtime' : 'bundled') as RollupBabelInputPluginOptions['babelHelpers'],
exclude: /\/node_modules\//,
babelrc: false,
// ref: https://github.com/rollup/rollup-plugin-babel#usage
extensions,
};
if (importLibToEs && type === 'esm') {
babelOpts.plugins.push(require.resolve('../lib/importLibToEs'));
}
babelOpts.presets.push(...extraBabelPresets);
babelOpts.plugins.push(...extraBabelPlugins);
// rollup configs
const input = join(cwd, entry);
const format = type;
// ref: https://rollupjs.org/guide/en#external
// 潜在问题:引用包的子文件时会报 warning比如 @babel/runtime/helpers/esm/createClass
// 解决方案:可以用 function 处理
const external = [
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {}),
...extraExternals,
];
// umd 只要 external peerDependencies
const externalPeerDeps = [
...Object.keys(pkg.peerDependencies || {}),
...extraExternals,
];
function getPkgNameByid(id) {
const splitted = id.split('/');
// @ 和 @tmp 是为了兼容 umi 的逻辑
if (id.charAt(0) === '@' && splitted[0] !== '@' && splitted[0] !== '@tmp') {
return splitted
.slice(0, 2)
.join('/');
} else {
return id.split('/')[0];
}
}
function testExternal(pkgs, excludes, id) {
if (excludes.includes(id)) {
return false;
}
return pkgs.includes(getPkgNameByid(id));
}
const terserOpts = {
compress: {
pure_getters: true,
unsafe: true,
unsafe_comps: true,
warnings: false,
},
};
function getPlugins(opts = {} as { minCSS: boolean; }) {
const { minCSS } = opts;
return [
url(),
svgr(),
postcss({
extract: extractCSS,
inject: injectCSS,
modules,
// modules => all .less will convert into css modules
...(modules ? { autoModules: false } : {}),
minimize: !!minCSS,
use: {
less: {
plugins: [new NpmImport({ prefix: '~' })],
javascriptEnabled: true,
...lessInRollupMode
},
sass: {
...sassInRollupMode,
},
stylus: false,
},
plugins: [autoprefixer({
// https://github.com/postcss/autoprefixer/issues/776
remove: false,
...autoprefixerOpts,
}), ...extraPostCSSPlugins],
}),
...(injectOpts ? [inject(injectOpts as RollupInjectOptions)] : []),
...(replaceOpts && Object.keys(replaceOpts || {}).length ? [replace(replaceOpts)] : []),
nodeResolve({
mainFields: ['module', 'jsnext:main', 'main'],
extensions,
...nodeResolveOpts,
}),
...(isTypeScript
? [
typescript2({
cwd,
// @see https://github.com/umijs/father/issues/61#issuecomment-544822774
clean: true,
cacheRoot: `${tempDir}/.rollup_plugin_typescript2_cache`,
// 支持往上找 tsconfig.json
// 比如 lerna 的场景不需要每个 package 有个 tsconfig.json
tsconfig: [join(cwd, 'tsconfig.json'), join(rootPath, 'tsconfig.json')].find(existsSync),
tsconfigDefaults: {
compilerOptions: {
// Generate declaration files by default
declaration: true,
},
},
tsconfigOverride: {
compilerOptions: {
// Support dynamic import
target: 'esnext',
},
},
check: !disableTypeCheck,
...(typescriptOpts || {}),
}),
]
: []),
babel(babelOpts),
json(),
...(extraRollupPlugins || []),
];
}
switch (type) {
case 'esm':
return [
{
input,
output: {
format,
file: join(cwd, `dist/${(esm && (esm as any).file) || `${name}.esm`}.js`),
},
plugins: [...getPlugins(), ...(esm && (esm as any).minify ? [terser(terserOpts)] : [])],
external: testExternal.bind(null, external, externalsExclude),
},
...(esm && (esm as any).mjs
? [
{
input,
output: {
format,
file: join(cwd, `dist/${(esm && (esm as any).file) || `${name}`}.mjs`),
},
plugins: [
...getPlugins(),
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
terser(terserOpts),
],
external: testExternal.bind(null, externalPeerDeps, externalsExclude),
},
]
: []),
];
case 'cjs':
return [
{
input,
output: {
format,
file: join(cwd, `dist/${(cjs && (cjs as any).file) || name}.js`),
},
plugins: [...getPlugins(), ...(cjs && (cjs as any).minify ? [terser(terserOpts)] : [])],
external: testExternal.bind(null, external, externalsExclude),
},
];
case 'umd':
// Add umd related plugins
const extraUmdPlugins = [
commonjs({
include,
// namedExports options has been remove from https://github.com/rollup/plugins/pull/149
}),
];
return [
{
input,
output: {
format,
sourcemap: umd && umd.sourcemap,
file: join(cwd, `dist/${(umd && umd.file) || `${name}.umd`}.js`),
globals: umd && umd.globals,
name: (umd && umd.name) || (pkg.name && camelCase(basename(pkg.name))),
},
plugins: [
...getPlugins(),
...extraUmdPlugins,
replace({
'process.env.NODE_ENV': JSON.stringify('development'),
}),
],
external: testExternal.bind(null, externalPeerDeps, externalsExclude),
},
...(umd && umd.minFile === false
? []
: [
{
input,
output: {
format,
sourcemap: umd && umd.sourcemap,
file: join(cwd, `dist/${(umd && umd.file) || `${name}.umd`}.min.js`),
globals: umd && umd.globals,
name: (umd && umd.name) || (pkg.name && camelCase(basename(pkg.name))),
},
plugins: [
...getPlugins({ minCSS: true }),
...extraUmdPlugins,
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
terser(terserOpts),
],
external: testExternal.bind(null, externalPeerDeps, externalsExclude),
},
]),
];
default:
throw new Error(`Unsupported type ${type}`);
}
}