zy-react-library/rollup.config.js

335 lines
10 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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 等样式文件(保持原始格式)
* 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
});
});
// ===== 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
});
});
}
});
// ===== 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
});
});
}
// ===== 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不会实际转换
* 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;
}
// 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);