@tarojs/plugin-generator
Version:
Taro 生成器插件
260 lines • 9.87 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.updateBabelConfig = void 0;
/* eslint-disable no-console */
/* eslint-disable brace-style */
const node_path_1 = __importDefault(require("node:path"));
const generator_1 = __importDefault(require("@babel/generator"));
const parser = __importStar(require("@babel/parser"));
const traverse_1 = __importDefault(require("@babel/traverse"));
const t = __importStar(require("@babel/types"));
const dedent_1 = __importDefault(require("dedent"));
const error_1 = require("../../utils/error");
const USE_BUILT_INS = 'useBuiltIns';
const config = (0, dedent_1.default)(`
[
'taro',
{
framework: 'react',
ts: true,
compiler: 'vite',
${USE_BUILT_INS}: process.env.TARO_ENV === 'h5' ? 'usage' : false
}
]
`);
const modifyConfigError = new error_1.GeneratorError({
type: error_1.GeneratorErrorType.modifyConfig,
message: (0, dedent_1.default)(`{
presets: [
[
'taro',
{
framework: 'react',
ts: true,
compiler: 'vite',
${USE_BUILT_INS}: process.env.TARO_ENV === 'h5' ? 'usage' : false
}
]
]
}`),
targetFile: 'babel.config.js',
});
/**
* 更新 babel.config.js
* 主要处理模板里的写法
* 1.
* ```js
* module.exports = {
* presets: [
* ['taro', {
* framework: 'react',
* ts: true,
* compiler: 'webpack5',
* }]
* ]
* }
* ```
* 2.
* ```js
* const taroBabelConfig = {
* framework: 'react',
* ts: true,
* compiler: 'webpack5',
* }
* module.exports = {
* presets: [
* ['taro', taroBabelConfig]
* ]
* }
* ```
*/
async function updateBabelConfig(ctx) {
const { fs } = ctx.helper;
const babelConfigPath = node_path_1.default.join(ctx.paths.appPath, 'babel.config.js');
if (!(await fs.pathExists(babelConfigPath))) {
await fs.writeFile(babelConfigPath, (0, dedent_1.default)(`
module.exports = {
presets: [
[
'taro',
{
framework: 'react',
ts: true,
compiler: 'vite',
${USE_BUILT_INS}: process.env.TARO_ENV === 'h5' ? 'usage' : false
}
]
]
}
`), { encoding: 'utf-8', flag: 'w' });
return;
}
const sourceCode = await fs.readFile(node_path_1.default.join(babelConfigPath), { encoding: 'utf-8' });
const ast = parser.parse(sourceCode, {
sourceType: 'module',
plugins: ['typescript'],
});
(0, traverse_1.default)(ast, {
AssignmentExpression: (expr) => {
const { node } = expr;
// module.exports = {}
if (t.isMemberExpression(node.left) &&
t.isIdentifier(node.left.object) &&
node.left.object.name === 'module' &&
t.isIdentifier(node.left.property) &&
node.left.property.name === 'exports') {
if (t.isObjectExpression(node.right)) {
const presetsProp = node.right.properties.find((p) => t.isObjectProperty(p) && t.isIdentifier(p.key) && p.key.name === 'presets');
// 没有 presets 属性,直接创建
if (!presetsProp) {
node.right.properties.push(t.objectProperty(t.identifier('presets'), t.arrayExpression([parser.parseExpression(config)])));
}
// presets: [...]
else if (t.isArrayExpression(presetsProp.value)) {
handlePresets(presetsProp.value, expr);
}
// presets是变量的情况:presets: presets
else if (t.isIdentifier(presetsProp.value)) {
const variable = presetsProp.value.name;
const binding = expr.scope.getBinding(variable);
// const presets = [...]
if (binding?.path.isVariableDeclarator()) {
const init = binding.path.node.init;
if (t.isArrayExpression(init)) {
handlePresets(init, expr);
}
else {
// 不是合法的 presets
throw modifyConfigError;
}
}
}
}
}
},
});
await fs.writeFile(babelConfigPath, (0, generator_1.default)(ast).code, { encoding: 'utf-8', flag: 'w' });
console.log('✅ 更新 babel.config.js 成功\n');
}
exports.updateBabelConfig = updateBabelConfig;
/**
* 处理 presets
* 遍历 presets 数组
* - 如果元素是字符串,且是 'taro',则替换为 config
* - 如果元素是数组,且第一个元素是字符串,且是 'taro',处理 options,插入 useBuiltIns 属性
* - 如果元素是变量,且变量的值是 'taro',则替换为 config,变量的值为数组,那么执行元素为数组的情况的处理逻辑
*/
function handlePresets(presets, nodePath) {
const { elements } = presets;
let modified = false;
/**
* 处理 presets 里的 taro preset
* - 元组第一项必须是 'taro'
* - 元组第二项是对象或者变量
* - - 对象的情况,插入 useBuiltIns 属性。
* - - 变量的情况,找到变量定义的地方,如果值是对象,插入 useBuiltIns 属性
*/
const handleTaroPreset = (preset) => {
const first = preset.elements[0];
if (t.isStringLiteral(first) && first.value === 'taro') {
const options = preset.elements[1];
if (t.isObjectExpression(options)) {
insertUseBuiltInsProp(options);
modified = true;
}
if (t.isIdentifier(options)) {
const variable = options.name;
const binding = nodePath.scope.getBinding(variable);
if (binding?.path.isVariableDeclarator()) {
// const options = init {}
const init = binding.path.node.init;
if (t.isObjectExpression(init)) {
insertUseBuiltInsProp(init);
modified = true;
return;
}
}
throw modifyConfigError;
}
}
};
// 扫描 presets 数组每一项,presets 每一项可能是字符串、元组、变量
for (let i = 0; i < elements.length; i += 1) {
if (modified) {
break;
}
const el = elements[i];
// ["taro", ...] -> presets: [['taro', {...}], ...]
if (t.isStringLiteral(el)) {
if (el.value === 'taro') {
elements[i] = parser.parseExpression(config);
modified = true;
}
}
// [["taro", {...}], ...]
if (t.isArrayExpression(el)) {
handleTaroPreset(el);
}
/**
* const taroPreset = ['taro', {}];
* presets: [taroPreset, ...]
*/
if (t.isIdentifier(el)) {
const variable = el.name;
const binding = nodePath.scope.getBinding(variable);
if (binding?.path.isVariableDeclarator()) {
const init = binding.path.node.init;
// const taroPreset = 'taro';
if (t.isStringLiteral(init) && init.value === 'taro') {
binding.path.node.init = parser.parseExpression(config);
}
// const taroPreset = ['taro', {...}];
if (t.isArrayExpression(init)) {
handleTaroPreset(init);
}
}
}
}
if (!modified) {
elements.push(parser.parseExpression(config));
}
}
function insertUseBuiltInsProp(obj) {
const useBuiltInProp = obj.properties.find((p) => t.isObjectProperty(p) && t.isIdentifier(p.key) && p.key.name === USE_BUILT_INS);
const builtInValue = parser.parseExpression("process.env.TARO_ENV === 'h5' ? 'usage' : false");
// 如果有 useBuiltIns 属性,直接替换值
if (useBuiltInProp) {
useBuiltInProp.value = builtInValue;
}
// 没有 useBuiltIns 属性,直接添加
else {
obj.properties.push(t.objectProperty(t.identifier(USE_BUILT_INS), builtInValue));
}
}
//# sourceMappingURL=babel.js.map