import { basename, extname, join } from 'path'; import { terser } from 'rollup-plugin-terser'; import babel from 'rollup-plugin-babel'; import replace from 'rollup-plugin-replace'; import json from 'rollup-plugin-json'; import nodeResolve from 'rollup-plugin-node-resolve'; import typescript from 'rollup-plugin-typescript2'; import commonjs from 'rollup-plugin-commonjs'; import postcss from 'rollup-plugin-postcss-umi'; import { ModuleFormat, RollupOptions } from 'rollup'; import { camelCase } from 'lodash'; import tempDir from 'temp-dir'; import autoprefixer from 'autoprefixer'; import NpmImport from 'less-plugin-npm-import'; import getBabelConfig from './getBabelConfig'; import { IBundleOptions } from './types'; interface IGetRollupConfigOpts { cwd: string; entry: string; type: ModuleFormat; bundleOpts: IBundleOptions; } interface IPkg { dependencies?: Object; peerDependencies?: Object; name?: string; } export default function(opts: IGetRollupConfigOpts): RollupOptions[] { const { type, entry, cwd, bundleOpts } = opts; const { umd, esm, cjs, file, target = 'browser', cssModules: modules, extraPostCSSPlugins = [], extraBabelPresets = [], extraBabelPlugins = [], autoprefixer: autoprefixerOpts, namedExports, runtimeHelpers: runtimeHelpersOpts, replace: replaceOpts, } = 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({ target, typescript: false, runtimeHelpers, }), runtimeHelpers, exclude: /\/node_modules\//, babelrc: false, // ref: https://github.com/rollup/rollup-plugin-babel#usage extensions, }; 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 || {}), ]; // umd 只要 external peerDependencies const externalPeerDeps = Object.keys(pkg.peerDependencies || {}); function getPkgNameByid(id) { if (id.charAt(0) === '@') { return id .split('/') .slice(0, 2) .join('/'); } else { return id.split('/')[0]; } } function testExternal(pkgs, id) { return pkgs.includes(getPkgNameByid(id)); } const terserOpts = { compress: { pure_getters: true, unsafe: true, unsafe_comps: true, warnings: false, }, }; const plugins = [ postcss({ modules, use: [ [ 'less', { plugins: [new NpmImport({ prefix: '~' })], javascriptEnabled: true, }, ], ], plugins: [autoprefixer(autoprefixerOpts), ...extraPostCSSPlugins], }), ...(replaceOpts && Object.keys(replaceOpts || {}).length ? [replace(replaceOpts)] : []), nodeResolve({ jsnext: true, extensions, }), ...(isTypeScript ? [ typescript({ cacheRoot: `${tempDir}/.rollup_plugin_typescript2_cache`, // TODO: 支持往上找 tsconfig.json // 比如 lerna 的场景不需要每个 package 有个 tsconfig.json tsconfig: join(cwd, 'tsconfig.json'), tsconfigDefaults: { compilerOptions: { // Generate declaration files by default declaration: true, }, }, tsconfigOverride: { compilerOptions: { // Support dynamic import target: 'esnext', }, }, }), ] : []), babel(babelOpts), json(), ]; switch (type) { case 'esm': return [ { input, output: { format, file: join( cwd, `dist/${(esm && (esm as any).file) || `${name}.esm`}.js`, ), }, plugins: [ ...plugins, ...(esm && (esm as any).minify ? [terser(terserOpts)] : []), ], external: testExternal.bind(null, external), }, ...(esm && (esm as any).mjs ? [ { input, output: { format, file: join( cwd, `dist/${(esm && (esm as any).file) || `${name}`}.mjs`, ), }, plugins: [ ...plugins, replace({ 'process.env.NODE_ENV': JSON.stringify('production'), }), terser(terserOpts), ], external: testExternal.bind(null, externalPeerDeps), }, ] : []), ]; case 'cjs': return [ { input, output: { format, file: join(cwd, `dist/${(cjs && (cjs as any).file) || name}.js`), }, plugins: [ ...plugins, ...(cjs && (cjs as any).minify ? [terser(terserOpts)] : []), ], external: testExternal.bind(null, external), }, ]; case 'umd': // Add umd related plugins plugins.push( commonjs({ include: /node_modules/, namedExports, }), ); return [ { input, output: { format, 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: [ ...plugins, replace({ 'process.env.NODE_ENV': JSON.stringify('development'), }), ], external: testExternal.bind(null, externalPeerDeps), }, ...(umd && umd.minFile === false ? [] : [ { input, output: { format, 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: [ ...plugins, replace({ 'process.env.NODE_ENV': JSON.stringify('production'), }), terser(terserOpts), ], external: testExternal.bind(null, externalPeerDeps), }, ]), ]; default: throw new Error(`Unsupported type ${type}`); } }