zy-react-library/rollup.config.js

335 lines
10 KiB
JavaScript
Raw Normal View History

import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';
import babel from '@rollup/plugin-babel';
import terser from '@rollup/plugin-terser';
import { readFileSync, existsSync } from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { glob } from 'glob';
// 获取当前文件所在目录的绝对路径
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// 读取 package.json用于获取依赖列表
const pkg = JSON.parse(readFileSync('./package.json', 'utf-8'));
/**
* 收集所有的入口文件
*
* 遍历指定目录查找所有 .js 文件作为打包入口
* 注意json 目录被排除因为 JSON 文件直接复制不作为入口处理
*
* @returns {Object} 入口文件对象格式{ 'components/Table/index.js': '/absolute/path/Table/index.js' }
*/
function getEntryFiles() {
const entries = {};
const baseDir = __dirname;
const srcDir = path.join(baseDir, 'src');
// 定义需要处理的目录排除jsonjson文件直接复制不转换
const dirs = ['components', 'hooks', 'utils', 'regular', 'enum'];
dirs.forEach(dir => {
const dirPath = path.join(srcDir, dir);
if (existsSync(dirPath)) {
// 递归查找当前目录下所有 .js 文件
const files = glob.sync('**/*.js', {
cwd: dirPath, // 在 src/xxx 目录中查找
absolute: false // 返回相对路径
});
// 为每个文件创建入口映射
files.forEach(file => {
// key: 保留源文件的相对路径(包括目录和文件名),如 'components/Table/index.js'
// value: src 目录下文件的绝对路径
const key = path.join(dir, file);
entries[key] = path.join(srcDir, dir, file);
});
}
});
return entries;
}
/**
* Babel 配置
*
* 只转换 JSX 语法不转译 ES6+ 语法
* 这样可以保持代码的现代性减少构建时间
*/
const babelConfig = {
// 将 Babel helpers 捆绑到每个文件中(避免重复引用)
babelHelpers: 'bundled',
// 排除 node_modules 目录,不进行处理
exclude: 'node_modules/**',
// 支持的文件扩展名
extensions: ['.js', '.jsx', '.ts', '.tsx'],
// 使用的预设和插件
presets: [
['@babel/preset-react', {
// 使用新的 JSX 转换方式,自动导入 JSX
runtime: 'automatic'
}]
],
plugins: []
};
/**
* 自定义插件复制类型声明文件和样式文件
*
* 功能
* 1. 复制 .d.ts 类型声明文件
* 2. 复制 .less/.css 等样式文件保持原始格式
2026-01-06 15:20:10 +08:00
* 3. 复制图片文件 (.png, .jpg, .jpeg, .gif, .svg, .webp)
* 4. 复制 .json 数据文件保持原始格式
* 5. 复制 css 文件夹
*
* 注意所有源文件都在 src/ 目录构建输出到根目录
*/
const copyTypesPlugin = () => ({
name: 'copy-types',
// 在生成 bundle 时执行
generateBundle() {
const srcDir = path.join(__dirname, 'src');
const dirs = ['components', 'hooks', 'utils', 'regular', 'enum', 'json'];
const this$1 = this;
// 处理每个目录
dirs.forEach(dir => {
const dirPath = path.join(srcDir, dir);
if (existsSync(dirPath)) {
// ===== 1. 复制类型声明文件 (.d.ts) =====
const dtsFiles = glob.sync('**/*.d.ts', {
cwd: dirPath,
absolute: true
});
dtsFiles.forEach(file => {
const relativePath = path.relative(dirPath, file);
const content = readFileSync(file, 'utf-8');
// 将文件添加到根目录
this$1.emitFile({
type: 'asset',
fileName: path.join(dir, relativePath),
source: content
});
});
// ===== 2. 复制样式文件 (.less, .css, .scss, .sass) =====
const styleFiles = glob.sync('**/*.{less,css,scss,sass}', {
cwd: dirPath,
absolute: true
});
styleFiles.forEach(file => {
const relativePath = path.relative(dirPath, file);
const content = readFileSync(file, 'utf-8');
// 将样式文件添加到根目录,保持原始格式
this$1.emitFile({
type: 'asset',
fileName: path.join(dir, relativePath),
source: content
});
});
2026-01-06 15:20:10 +08:00
// ===== 3. 复制图片文件 (.png, .jpg, .jpeg, .gif, .svg, .webp) =====
const imageFiles = glob.sync('**/*.{png,jpg,jpeg,gif,svg,webp}', {
cwd: dirPath,
absolute: true
});
imageFiles.forEach(file => {
const relativePath = path.relative(dirPath, file);
// 读取二进制文件,明确不使用编码
const content = readFileSync(file, null);
// 将图片文件添加到根目录,保持二进制格式
this$1.emitFile({
type: 'asset',
fileName: path.join(dir, relativePath),
source: content
});
});
}
});
2026-01-06 15:20:10 +08:00
// ===== 4. 复制 json 文件夹(保持原始格式,不转换) =====
const jsonDir = path.join(srcDir, 'json');
if (existsSync(jsonDir)) {
const jsonFiles = glob.sync('**/*.json', {
cwd: jsonDir,
absolute: true
});
jsonFiles.forEach(file => {
const relativePath = path.relative(jsonDir, file);
const content = readFileSync(file, 'utf-8');
// 将 JSON 文件添加到根目录
this$1.emitFile({
type: 'asset',
fileName: path.join('json', relativePath),
source: content
});
});
}
2026-01-06 15:20:10 +08:00
// ===== 5. 复制 css 文件夹 =====
const cssDir = path.join(srcDir, 'css');
if (existsSync(cssDir)) {
const cssFiles = glob.sync('**/*', {
cwd: cssDir,
absolute: true,
nodir: true // 不包括目录本身
});
cssFiles.forEach(file => {
const relativePath = path.relative(cssDir, file);
const content = readFileSync(file, 'utf-8');
this$1.emitFile({
type: 'asset',
fileName: path.join('css', relativePath),
source: content
});
});
}
}
});
/**
* Rollup 插件列表
*
* 按照执行顺序排列
* 1. resolve - 解析模块路径
* 2. babel - 转换 JSX
* 3. commonjs - 转换 CommonJS 模块
* 4. json - 解析 JSON 导入但被标记为 external不会实际转换
2026-01-06 15:20:10 +08:00
* 5. copyTypesPlugin - 复制类型样式和图片文件
* 6. terser - 代码压缩
*/
const plugins = [
// 模块解析插件 - 解析 node_modules 中的模块
resolve({
extensions: ['.js', '.jsx', '.ts', '.tsx'],
browser: true // 浏览器环境优先
}),
// Babel 转换插件 - 只转换 JSX
babel(babelConfig),
// CommonJS 转换插件 - 允许导入 CommonJS 模块
commonjs({
transformMixedEsModules: true // 转换混合的 ES 模块
}),
// JSON 插件 - 处理 JSON 导入(配合 external 使用)
json(),
// 自定义插件 - 复制类型和样式文件
copyTypesPlugin(),
// 代码压缩插件
terser({
compress: {
drop_console: false, // 保留 console
drop_debugger: true, // 移除 debugger
pure_funcs: [], // 不移除任何函数调用
passes: 2 // 压缩次数
},
format: {
comments: false, // 移除注释
beautify: false // 不美化代码,保持压缩格式
},
ecma: 2015, // 目标 ECMAScript 版本
module: true, // 保留 ES 模块格式
toplevel: false // 不压缩顶层作用域
})
];
/**
* External 函数
*
* 判断模块是否应该标记为"外部依赖"
* 外部依赖不会被打包进 bundle而是保留 import 语句
*
* @param {string} id - 模块标识符
* @returns {boolean} - true 表示外部依赖不打包
*/
const external = (id) => {
// 1. 样式文件标记为外部 -> 保留 import 语句
// 原因:样式文件需要消费者项目配置自己的加载器来处理
// 保持 Less 变量(如 @{ant-prefix})不被替换
if (id.endsWith('.less') || id.endsWith('.css') || id.endsWith('.scss') || id.endsWith('.sass')) {
return true;
}
// 2. JSON 文件标记为外部 -> 直接使用原始 .json 文件
// 原因:避免将 JSON 转换成 JS 模块,保持原始格式
// 例如35MB 的 area.json 不会被转换成 56MB 的 area.json.js
if (id.endsWith('.json')) {
return true;
}
2026-01-06 15:20:10 +08:00
// 3. 图片文件标记为外部 -> 保留 import 语句,直接使用原始文件
// 原因:图片文件会通过 copyTypesPlugin 复制到输出目录
if (id.endsWith('.png') || id.endsWith('.jpg') || id.endsWith('.jpeg') ||
id.endsWith('.gif') || id.endsWith('.svg') || id.endsWith('.webp')) {
return true;
}
// 4. npm 依赖包标记为外部 -> 不打包到 bundle 中
// 原因:减少 bundle 体积,避免版本冲突,让消费者项目自己管理依赖
// 从 package.json 的 dependencies 中读取依赖列表
const dependencies = Object.keys(pkg.dependencies || {});
return dependencies.some(dep => id === dep || id.startsWith(dep + '/'));
};
/**
* 创建 Rollup 配置的辅助函数
*
* @param {string} outputDir - 输出目录
* @param {string} format - 输出格式'esm' 'cjs'
* @param {Array} plugins - 插件列表
* @returns {Object} Rollup 配置对象
*/
function createConfig(outputDir, format, plugins) {
return {
// 入口文件对象
input: getEntryFiles(),
// 输出配置
output: {
dir: outputDir, // 输出目录
format: format, // 输出格式:'esm' 或 'cjs'
preserveModules: true, // 保留模块结构,不打包成单个文件
preserveModulesRoot: './', // 保留模块结构的根目录
entryFileNames: '[name]', // 入口文件名:[name] 保留原始文件名(包括 .js 后缀)
exports: 'named' // 导出命名导出
},
// 外部依赖判断函数
external,
// 插件列表
plugins
};
}
/**
* 导出配置
*
* 只输出 ESM 格式到根目录components/, hooks/, utils/
*/
export default createConfig('.', 'esm', plugins);