Appearance
Rollup 插件
一、插件基础
- 本质:一个函数,返回一个对象
- 中文官网
- 流程参考上述官网,非常详细
二、插件案例
开发 commonjs 插件
js
npm i @rollup/plugin-commonjs -D
npm i magic-string -Dsrc 模块测试代码
js
// src/index.js
import title from "./title.js";
console.log(title);
import { home } from "./home.js";
console.log(home);
// src/title.js
module.exports = "title";
// src/home.js
module.exports.home = "china";插件实现代码
js
import { createFilter } from "@rollup/pluginutils";
import MagicString from "magic-string";
import { walk } from "estree-walker";
import path from "path";
function commonjsPlugin(options = {}) {
const defaultExtensions = [".js", ".jsx"];
const { exclude, include, extensions = defaultExtensions } = options;
const regExp = new RegExp(`(${extensions.join("|")})$`);
const userDefinedFilter = createFilter(include, exclude);
const filter = (id) => regExp.test(id) && userDefinedFilter(id);
return {
name: "commonjs",
async transform(code, filename) {
if (!filter(filename)) {
return null;
}
// this.parse = acorn ast语法解析库
// code = module.exports = "title"
// filename = title.js
const result = transformAndCheckExports(this.parse, code, filename);
return result;
},
};
}
function transformAndCheckExports(parse, code, filename) {
const {isEsModule, ast} = analyzeTopLevelStatements((parse, code)
if (isEsModule) {
// 如果是es module,则返回null, 不做任何处理
return null;
}
return transformCommonjs(ast, code, filename);
}
function analyzeTopLevelStatements(parse, code) {
const ast = parse(code)
let isEsModule = false;
for (const statement of ast.body) {
switch (statement.type) {
case "ExportNamedDeclaration":
isEsModule = true;
break;
case "ExportDefaultDeclaration":
isEsModule = true;
break;
case "ImportDeclaration":
isEsModule = true;
break;
default:
break;
}
}
return {isEsModule, ast}
}
function transformCommonjs(options = {}) {
const ms = new MagicString(code);
let moduleExportsAssignment = null
let nameExports = []
walk(ast, {
enter(node) {
switch (node.type) {
case "AssignmentExpression":
if (node.left.type === "MemberExpression") {
const keys = getKeyPath(node.left)
if (keys === 'module.exports') {
moduleExportsAssignment = node
} else if (keys.startsWith('module.exports')) {
nameExports.push({
keys,
node: node.left
})
}
}
break;
default:
break;
}
}
})
if (moduleExportsAssignment) {
const { left } = moduleExportsAssignment
const exportName = path.basename(filename, path.extname(filename)) // title
ms.overwrite(left.start, left.end, exportName) // module.exports = title
ms.prependRight(left.start, `var `)
ms.append(`\n exports default ${exportName}; \n`)
}
if (nameExports.length) {
let exportNames = []
for (let i = 0; i < nameExports.length; i++) {
let { keys, node } = nameExports[i]
let exportName = keys.slice(15)
exportNames.push(exportName)
ms.overwrite(node.start, node.end, exportName)
ms.prependRight(node.start, `var `)
}
ms.append(`\n exports default {${exportNames.join(',')}}; \n`)
}
return {code: ms.toString()}
}
// parts = [module, exports, home]
function getKeyPath(node) {
const parts = []
while (node.type === "MemberExpression") {
parts.unshift(node.property.name)
node = node.object
}
parts.unshift(node.name)
return parts.join('.')
}构建结果
js
var title = "title";
var home = "china";
console.log(title);
console.log(home);