335 lines
10 KiB
JavaScript
335 lines
10 KiB
JavaScript
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');
|
||
|
||
// 定义需要处理的目录(排除json,json文件直接复制不转换)
|
||
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);
|