319 lines
9.7 KiB
TypeScript
Executable File
319 lines
9.7 KiB
TypeScript
Executable File
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}`);
|
||
}
|
||
}
|