使用Rollup构建可执行CLI脚本的模块打包异常问题
问题场景
我正在开发一个同时包含库和小型CLI工具的包,CLI工具位于bin/cli.ts,代码如下:
import yargs from 'yargs'; import myPackage from '../dist/esm/index.js'; import { someCommand } from './someCommand'; yargs.<a bunch of stuff>;
期望构建后:yargs和myPackage保留为外部导入,someCommand被打包进CLI脚本。
当前使用的rollup.config.js配置:
import ts from 'rollup-plugin-ts'; import shebang from 'rollup-plugin-add-shebang'; export default [ { input: 'src/index.ts', output: [ { file: 'dist/esm/index.js', format: 'esm', }, { file: 'dist/cjs/index.js', format: 'cjs', }, ], plugins: [ ts({}), ], }, { input: 'bin/cli.ts', output: [ { file: 'dist-bin/cli.js', format: 'esm', }, ], plugins: [ ts({}), shebang({ shebang: '#!/usr/bin/env node', }), ], }, ];
构建后生成的dist-bin/cli.js与dist/esm/index.js内容几乎一致,仅多了shebang,完全没有包含yargs或someCommand相关逻辑,显然配置有误,想确认Rollup是否适合该场景,尽量不想新增构建系统。
解决方案
Rollup完全适配这种同时构建库和CLI工具的场景,只需调整配置和代码即可解决问题:
1. 修正CLI的导入路径
不要导入构建后的库文件,改为直接导入源码(避免构建顺序问题,同时让Rollup正确识别依赖关系):
// bin/cli.ts import yargs from 'yargs'; import myPackage from '../src/index.ts'; // 替换原导入路径 import { someCommand } from './someCommand'; // 确保yargs逻辑被执行,比如添加.argv触发解析 yargs .command(someCommand) .argv;
2. 配置Rollup的external选项
在CLI的构建配置中,通过external指定不需要打包的外部依赖,让Rollup保留这些模块的导入语句:
// rollup.config.js import ts from 'rollup-plugin-ts'; import shebang from 'rollup-plugin-add-shebang'; export default [ // 库的构建配置保持不变 { input: 'src/index.ts', output: [ { file: 'dist/esm/index.js', format: 'esm' }, { file: 'dist/cjs/index.js', format: 'cjs' }, ], plugins: [ts({})], }, // 修改后的CLI构建配置 { input: 'bin/cli.ts', output: [ { file: 'dist-bin/cli.js', format: 'esm' }, ], // 指定外部依赖,Rollup不会打包这些模块 external: ['yargs', '../src/index.ts'], plugins: [ ts({}), shebang({ shebang: '#!/usr/bin/env node' }), ], }, ];
如果后续发布包时,CLI需要导入已发布的库(而非源码),可以将../src/index.ts替换为你的包名(比如external: ['yargs', 'your-package-name']),并确保package.json的main或exports配置正确。
3. 确保CLI代码不会被Tree-shaking移除
Rollup的Tree-shaking会移除未被使用的代码,所以必须确保yargs的逻辑被实际执行(比如调用.argv),否则相关代码会被误删。
4. 检查TypeScript配置
确保bin目录被包含在tsconfig.json的include范围内,让rollup-plugin-ts能正确编译CLI的TypeScript文件:
// tsconfig.json { "include": ["src/**/*", "bin/**/*"], // 其他配置... }
内容的提问来源于stack exchange,提问作者Thom Smith

