“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第n篇文章,点击查看活动详情”
一、插件的上下文
在插件运行时,会给插件的上下文绑定一些工具函数,这些工具函数,可以通过this访问到。
1)this.addWatchFile
添加一个监听文件,当监听文件发送变化时,会重新构建。
入参:
- id:string 。 文件路径,可以是相对路径或者绝对路径
无返回值
2)this.emitFile
在构建输出中,发出一个新文件,然后返回这个文件的引用id,这个引用id可以在各个地方用来获取到发出的这个新文件。
入参:
- emittedFile:可以是EmittedChunk或者EmittedAsset这两种类型中的一个。
返回值:
- string : 引用id。
入参的一般格式
- name和fileName : 如果提供了fileName,则直接当作生成的文件名。如果提供了name,name会当作output.chunkFileNames 或者 output.assetFileNames 配置中的[name]参数,后面可能会加唯一的数字。如果都没提供,就用默认的。
- type:如果是'chunk',则根据id参数,来生成一个chunk。如果是'asset',则生成的新文件中,使用'source'作为文件内容。
- id:type为chunk时,用来当作入口,参数和resolveID的钩子函数参数相同。
- importer : 如果提供了,和id配合使用,当作id的上级文件,导入id所在文件使用。
在默认情况下,rollup假设发出的chunk是独立于其他入口而执行的,甚至在所有代码之前执行。这意味着,如果发出的chunk和现有的入口之间存在共享的依赖的话,rollup将会将这些入口之间的依赖,分离为一个单独的chunk。这时候可以配置'implicitlyLoadeAferOneOf'这个参数,这个参数是一个id列表。所以,如果emitFile的类型是chunk,其实相当于多配置了一个input入口。
3)this.getModuleIds
获取所有模块的id。返回可迭代的模块id类数组对象。
4)this.getModuleInfo
根据模块id,查询模块信息。
入参:
- id:模块id
返回值:
- info: ModuleInfo 结构的返回值,或者null
5)this.parse
将代码转为AST。
入参:
- code:string
- acornOptions:acorn的一些参数
出参:
ESTree.Program
\
二、插件开发示例
我们假设需要处理js文件中导入的css,这种情况常见,我们在vue项目,需要挂载全局样式时就是在打包的入口,导入css文件,例如如下代码:
import demo from './demo.js';
import './index.css';
import './reset.less';
export default function () {
console.log(demo.name);
}
如果直接 用rollup打包,则会报错,提示需要用插件处理非JavaScript格式的文件。
接下来我们自己开发一个插件,来捕获这个错误,并且能解析css文件。组件部分基础代码:
function pluginExample(){
return {
name:"rollup-plugin-example"
}
}
1、在导入阶段进行截停,不对css格式的文件进行转为module操作。
这需要我们在resolveId 钩子函数中,进行截停。在pluginExample 中添加 resolveId 钩子函数 (示例中,没有对css、less等进行详细处理,只是做演示用):
function pluginExample() {
let css_map = [];
return {
name: 'rollup-plugin-example',
/**
* 根据id判断导入的文件类型,如果是样式文件,则返回false,不进行打包
* @param {*} id
* @returns string || false || null
*/
resolveId(id) {
let fileType = id.split('.');
fileType = fileType.slice(-1)[0];
switch (fileType) {
case 'css':
css_map[id] = fs.readFile(id, { encoding: 'utf8' });
break;
case 'less':
css_map[id] = fs.readFile(id, { encoding: 'utf8' });
break;
case 'sass':
case 'scss':
css_map[id] = fs.readFile(id, { encoding: 'utf8' });
break;
default:
console.log(fileType);
return null;
}
return false;
}
};
}
2、将所有样式文件进行合并
在拿到所有的样式文件后,我们把样式文件进行汇总(注意,这里可能会报错,因为如果是rollup的工程,配置的eslint对钩子函数的顺序有要求,不用管):
function pluginExample() {
let css_map = [];
return {
name: 'rollup-plugin-example',
/**
* 根据id判断导入的文件类型,如果是样式文件,则返回false,不进行打包
* @param {*} id
* @returns string || false || null
*/
resolveId(id) {
let fileType = id.split('.');
fileType = fileType.slice(-1)[0];
switch (fileType) {
case 'css':
css_map[id] = fs.readFile(id, { encoding: 'utf8' });
break;
case 'less':
css_map[id] = fs.readFile(id, { encoding: 'utf8' });
break;
case 'sass':
case 'scss':
css_map[id] = fs.readFile(id, { encoding: 'utf8' });
break;
default:
console.log(fileType);
return null;
}
return false;
},
// 构建结束后,将所有的样式文件,拼接到一起
buildEnd() {
css_map = Promise.all(Object.keys(css_map).map(cb => css_map[cb])).then(data => {
return data.join('\r\n');
});
}
};
}
3、获取输出配置,进行文件写入
为了输出到准确的位置,我们需要在输出阶段的钩子函数中,获取到输出配置,然后拼接出输出的目录:
function pluginExample() {
let css_map = [];
return {
name: 'rollup-plugin-example',
/**
* 根据id判断导入的文件类型,如果是样式文件,则返回false,不进行打包
* @param {*} id
* @returns string || false || null
*/
resolveId(id) {
let fileType = id.split('.');
fileType = fileType.slice(-1)[0];
switch (fileType) {
case 'css':
css_map[id] = fs.readFile(id, { encoding: 'utf8' });
break;
case 'less':
css_map[id] = fs.readFile(id, { encoding: 'utf8' });
break;
case 'sass':
case 'scss':
css_map[id] = fs.readFile(id, { encoding: 'utf8' });
break;
default:
console.log(fileType);
return null;
}
return false;
},
// 构建结束后,将所有的样式文件,拼接到一起
buildEnd() {
css_map = Promise.all(Object.keys(css_map).map(cb => css_map[cb])).then(data => {
return data.join('\r\n');
});
},
// 一进入输出阶段,就开始写样式文件
outputOptions(opts) {
// 判断file中是否能解出上级文件地址
const dir = opts.dir || path.join(opts.file, '../');
css_map.then(chunk => {
fs.writeFile(dir + 'bundle.css', chunk).then(e => {
console.log(e ? '存储失败' : 'css文件处理成功', e ? e : '');
});
});
return null
}
};
}
4、完善
通过这个示例,基本就能实现css文件的打包功能。在示例中,我们还可以给函数传一个参数,来控制我们最终打包的css的文件的名称,或者哪些css文件可以单独生成文件。