TypeScript包自动生成子路径导出,支持桶导入与组件直接导入
TypeScript包双导入方式配置方案
要实现既支持桶导入又支持组件名子路径导入,同时避免手动维护繁琐配置,按以下步骤操作:
1. 配置桶导入基础
在根目录的src/index.ts中统一导出所有组件,直接支持桶导入:
// src/index.ts export { TextBox, RadioButton } from './inputs'; export { PrimaryButton, SecondaryButton } from './buttons';
此时其他项目即可通过import { TextBox, PrimaryButton } from 'common-package'导入组件。
2. 自动生成导出配置
手动编写package.json的exports和typeVersions太繁琐,写个Node脚本自动扫描组件并生成配置:
创建generate-exports.js文件:
const fs = require('fs'); const path = require('path'); const srcDir = path.resolve(__dirname, 'src'); const components = []; // 递归扫描src下的所有组件文件(排除index.ts) function scanDir(dir) { const files = fs.readdirSync(dir); for (const file of files) { const fullPath = path.join(dir, file); const stats = fs.statSync(fullPath); if (stats.isDirectory()) scanDir(fullPath); else if (file.endsWith('.tsx') && file !== 'index.ts') { const componentName = path.basename(file, '.tsx'); components.push(componentName); } } } scanDir(srcDir); // 生成exports配置 const exportsConfig = { '.': { types: './dist/index.d.ts', import: './dist/index.js', require: './dist/index.cjs' } }; // 为每个组件添加子路径导出规则 components.forEach(name => { exportsConfig[`./${name}`] = { types: `./dist/${name}.d.ts`, import: `./dist/${name}.js`, require: `./dist/${name}.cjs` }; }); // 生成typeVersions配置 const typeVersionsConfig = { '*': components.reduce((acc, name) => { acc[`${name}`] = [`dist/${name}.d.ts`]; return acc; }, { '': ['dist/index.d.ts'] }) }; // 更新package.json const pkgPath = path.resolve(__dirname, 'package.json'); const pkg = require(pkgPath); pkg.exports = exportsConfig; pkg.typeVersions = typeVersionsConfig; fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
3. 配置组件单独打包
默认TypeScript编译会保留原目录结构,需要用Rollup将每个组件单独打包到dist根目录:
先安装依赖:
npm install rollup @rollup/plugin-typescript @rollup/plugin-node-resolve @rollup/plugin-commonjs typescript --save-dev
创建rollup.config.js:
import typescript from '@rollup/plugin-typescript'; import { nodeResolve } from '@rollup/plugin-node-resolve'; import commonjs from '@rollup/plugin-commonjs'; import fs from 'fs'; import path from 'path'; const srcDir = path.resolve(__dirname, 'src'); const components = []; // 扫描组件文件 function scanDir(dir) { const files = fs.readdirSync(dir); for (const file of files) { const fullPath = path.join(dir, file); const stats = fs.statSync(fullPath); if (stats.isDirectory()) scanDir(fullPath); else if (file.endsWith('.tsx') && file !== 'index.ts') { components.push({ name: path.basename(file, '.tsx'), input: fullPath }); } } } scanDir(srcDir); // 生成每个组件的打包配置 const componentConfigs = components.map(comp => ({ input: comp.input, output: [ { file: `dist/${comp.name}.js`, format: 'esm' }, { file: `dist/${comp.name}.cjs`, format: 'cjs' } ], plugins: [ nodeResolve(), commonjs(), typescript({ declaration: true, declarationDir: 'dist', outDir: 'dist' }) ] })); // 根入口的打包配置 const rootConfig = { input: 'src/index.ts', output: [ { file: 'dist/index.js', format: 'esm' }, { file: 'dist/index.cjs', format: 'cjs' } ], plugins: [ nodeResolve(), commonjs(), typescript({ declaration: true, declarationDir: 'dist', outDir: 'dist' }) ] }; export default [...componentConfigs, rootConfig];
4. 串联打包流程
在package.json中添加脚本,自动生成配置并打包:
{ "scripts": { "generate-exports": "node generate-exports.js", "build": "npm run generate-exports && rollup -c" } }
5. 测试导入方式
运行npm run build完成打包后,其他项目即可使用两种导入方式:
- 桶导入:
import { TextBox, PrimaryButton } from 'common-package' - 子路径导入:
import TextBox from 'common-package/TextBox'
后续新增组件时,只需重新执行npm run build,脚本会自动扫描新增组件并更新导出配置。
内容的提问来源于stack exchange,提问作者eVolve
相关产品推荐
相关产品推荐

