import path, { resolve } from 'path'; import chalk from 'chalk'; import { rollup, OutputOptions, watch, RollupOptions } from 'rollup'; import readPkgUp from 'read-pkg-up'; import fs from 'fs-extra'; import rollupTypescript from '@rollup/plugin-typescript'; import { nodeResolve } from '@rollup/plugin-node-resolve'; import commonjs from '@rollup/plugin-commonjs'; import json from '@rollup/plugin-json'; import { babel, getBabelOutputPlugin } from '@rollup/plugin-babel'; import { terser } from 'rollup-plugin-terser'; import { generateDtsBundle } from 'dts-bundle-generator'; import * as dtsLozalize from './dts-localize'; const { bold, gray, greenBright } = chalk; interface Options { input: string; externals: string[]; cwd: string; optimized?: boolean; watch?: boolean; } async function dts({ input, externals, cwd, ...options }: Options) { if (options.watch) { try { const [out] = await generateDtsBundle( [ { filePath: input, output: { inlineDeclareGlobals: false, sortNodes: true, noBanner: true, }, }, ], { followSymlinks: false } ); await fs.outputFile('dist/types/index.d.ts', out); } catch (e) { console.log(e.message); } } else { const [out] = await generateDtsBundle( [ { filePath: input, output: { inlineDeclareGlobals: false, sortNodes: true, noBanner: true, }, }, ], { followSymlinks: false } ); const bundledDTSfile = path.join(cwd, 'dist/ts-tmp/index.d.ts'); const localizedDTSout = path.join(cwd, 'dist/types'); await fs.outputFile(bundledDTSfile, out); await dtsLozalize.run([bundledDTSfile], localizedDTSout, { externals, cwd, }); } } async function removeDist() { await fs.remove('dist'); } async function mapper() { await fs.emptyDir(path.join(process.cwd(), 'dist', 'types')); await fs.writeFile( path.join(process.cwd(), 'dist', 'types', 'index.d.ts'), `export * from '../../src/index';` ); } const makeExternalPredicate = (externals: string[]) => { if (externals.length === 0) { return () => false; } const pattern = new RegExp(`^(${externals.join('|')})($|/)`); return (id: string) => pattern.test(id); }; async function build(options: Options) { const { input, externals, cwd, optimized } = options; const setting: RollupOptions = { input, external: makeExternalPredicate(externals), plugins: [ nodeResolve({ preferBuiltins: true, }), commonjs(), babel({ babelHelpers: 'external', skipPreflightCheck: true, compact: false, }), json(), rollupTypescript({ lib: ['es2015', 'dom', 'esnext'], target: 'es6' }), ], }; const outputs: OutputOptions[] = [ { dir: resolve(cwd, './dist/esm'), format: 'es', sourcemap: optimized, preferConst: true, plugins: [ getBabelOutputPlugin({ compact: false, presets: [ [ '@babel/preset-env', { shippedProposals: true, useBuiltIns: 'usage', corejs: '3', modules: false, targets: { chrome: '100' }, }, ], ], }), optimized ? terser({ output: { comments: false }, module: true }) : null, ].filter(Boolean), }, { dir: resolve(cwd, './dist/cjs'), format: 'commonjs', plugins: [ getBabelOutputPlugin({ compact: false, presets: [ [ '@babel/preset-env', { shippedProposals: true, useBuiltIns: 'usage', corejs: '3', modules: false, targets: { node: '14' }, }, ], ], }), optimized ? terser({ output: { comments: false }, module: true }) : null, ].filter(Boolean), }, ]; if (options.watch) { const watcher = watch({ ...setting, output: outputs }); watcher.on('change', (event) => { console.log(`${greenBright('changed')}: ${event.replace(path.resolve(cwd, '../..'), '.')}`); }); } else { const bundler = await rollup(setting); await Promise.all(outputs.map((config) => bundler.write(config))); await bundler.close(); } } const hasFlag = (flags: string[], name: string) => !!flags.find((s) => s.startsWith(`--${name}`)); export async function run({ cwd, flags }: { cwd: string; flags: string[] }) { const { packageJson: pkg } = await readPkgUp({ cwd }); const message = gray(`Built: ${bold(`${pkg.name}@${pkg.version}`)}`); console.time(message); const shouldReset = hasFlag(flags, 'reset'); const shouldWatch = hasFlag(flags, 'watch'); const shouldOptimize = hasFlag(flags, 'optimized'); if (shouldReset) { await removeDist(); } const input = path.join(cwd, pkg.bundlerEntrypoint); const externals = Object.keys({ ...pkg.dependencies, ...pkg.peerDependencies, }); const options: Options = { cwd, externals, input, optimized: shouldOptimize, watch: shouldWatch, }; if (!shouldOptimize) { console.log(`skipping generating types for ${process.cwd()}`); } await Promise.all([ // build(options), ...(options.optimized ? [dts(options)] : [mapper()]), ]); console.timeEnd(message); }