vusion-api
Version:
Vusion Node.js API
840 lines (734 loc) • 32.9 kB
text/typescript
import * as babel from '@babel/core';
import generate from '@babel/generator';
import { uniqueInMap } from '../utils/shared';
import { Options as PrettierOptions } from 'prettier';
import * as prettier from 'prettier/standalone';
import * as parserBabylon from 'prettier/parser-babylon';
/**
* @TODO - Load babel Config
* @TODO - Load prettier Config
*/
const prettierConfig = {
"tabWidth": 4,
"singleQuote": true,
"quoteProps": "as-needed",
"trailingComma": "all",
"arrowParens": "always",
"endOfLine": "lf"
};
export const SIMPLIFIED_NODE_TYPE: { [type: string]: string } = {
object: 'ObjectExpression',
number: 'NumericLiteral',
string: 'StringLiteral',
id: 'Identifier',
}
export const LIFECYCLE_HOOKS = [
'beforeCreate',
'created',
'beforeMount',
'mounted',
'beforeUpdate',
'updated',
'activated',
'deactivated',
'beforeDestroy',
'destroyed'
];
export const ORDER_IN_COMPONENTS = [
'el',
'name',
'parent',
'functional',
['delimiters', 'comments'],
['components', 'directives', 'filters'],
'extends',
'mixins',
'inheritAttrs',
'model',
['props', 'propsData'],
'fetch',
'asyncData',
'data',
'computed',
'watch',
LIFECYCLE_HOOKS,
'methods',
'head',
['template', 'render'],
'renderError'
];
export const ORDER_IN_COMPONENTS_MAP: { [key: string]: number } = {};
ORDER_IN_COMPONENTS.forEach((key, index) => {
if (Array.isArray(key)) {
key.forEach((subkey) => ORDER_IN_COMPONENTS_MAP[subkey] = index);
} else
ORDER_IN_COMPONENTS_MAP[key] = index;
});
export class DeclarationHandler {
state: { [name: string]: string | Array<string> };
constructor(
public node: babel.types.Node,
public parent?: babel.types.Node,
) {
this.resetState();
}
resetState() {
this.state = {};
}
after(values: string | Array<string>) {
this.state.after = values;
return this;
}
is(type: string) {
if (SIMPLIFIED_NODE_TYPE[type])
type = SIMPLIFIED_NODE_TYPE[type];
return this.node.type === type;
}
id(name: string) {
// 先试试硬 assign 有没有问题
if (this.node.type !== 'Identifier')
Object.assign(this.node, babel.types.identifier(name));
return this;
}
name() {
if (this.node.type !== 'Identifier')
throw new TypeError('This is not an Identifier!');
return this.node.name;
}
object() {
// 先试试硬 assign 有没有问题
if (this.node.type !== 'ObjectExpression')
Object.assign(this.node, babel.types.objectExpression([]));
return this;
}
properties() {
if (this.node.type !== 'ObjectExpression')
throw new TypeError('This is not an ObjectExpression!');
return this.node.properties;
}
private _set(key: string, value: string | babel.types.Expression | babel.types.PatternLike, force?: boolean) {
if (this.node.type !== 'ObjectExpression')
throw new Error(`${force ? 'set' : 'ensure'} method can only be called on an objectExpression`);
let pos;
const after = this.state.after || [];
let index = this.node.properties.findIndex((property, index) => {
if (property.type === 'ObjectProperty' && property.key.type === 'Identifier' && after.includes(property.key.name))
pos = index;
return property.type === 'ObjectProperty' && property.key.type === 'Identifier' && property.key.name === key;
});
if (pos === undefined)
pos = this.node.properties.length;
let valueNode: babel.types.Expression | babel.types.PatternLike;
if (typeof value === 'string') {
const valueDeclaration = babel.template(`const value = ${value}`)() as babel.types.VariableDeclaration;
valueNode = valueDeclaration.declarations[0].init;
} else
valueNode = value;
const objectProperty = babel.types.objectProperty(babel.types.identifier(key), valueNode);
if (~index)
force && this.node.properties.splice(index, 1, objectProperty);
else
this.node.properties.splice(pos, 0, objectProperty);
this.resetState();
return this;
}
/**
* 确保拥有此属性。如果没有,则将第二个参数设为此属性
* @param key
* @param value
*/
ensure(key: string, value: string | babel.types.Expression | babel.types.PatternLike = 'undefined') {
return this._set(key, value);
}
/**
* 给对象的属性赋值
* @param key 键的名称
* @param value 值的名称
*/
set(key: string, value: string | babel.types.Expression | babel.types.PatternLike) {
return this._set(key, value, true);
}
setMethod(key: string, objectMethod: babel.types.ObjectMethod) {
if (this.node.type !== 'ObjectExpression')
throw new Error(`setMethod method can only be called on an objectExpression`);
let pos;
const after = this.state.after || [];
let index = this.node.properties.findIndex((property, index) => {
if ((property.type === 'ObjectProperty' || property.type === 'ObjectMethod') && property.key.type === 'Identifier' && after.includes(property.key.name))
pos = index;
return (property.type === 'ObjectProperty' || property.type === 'ObjectMethod') && property.key.type === 'Identifier' && property.key.name === key;
});
if (pos === undefined)
pos = this.node.properties.length;
if (~index)
this.node.properties.splice(index, 1, objectMethod);
else
this.node.properties.splice(pos, 0, objectMethod);
this.resetState();
return this;
}
/**
* 获取对象的属性值
* @param key 键的名称
*/
get(key: string) {
if (this.node.type !== 'ObjectExpression')
throw new Error('get method can only be called on an objectExpression');
const objectProperty = this.node.properties.find((property, index) => {
return property.type === 'ObjectProperty' && property.key.type === 'Identifier' && property.key.name === key;
}) as babel.types.ObjectProperty;
return objectProperty && new DeclarationHandler(objectProperty.value, objectProperty);
}
getMethod(key: string) {
if (this.node.type !== 'ObjectExpression')
throw new Error('getMethod can only be called on an objectExpression');
return this.node.properties.find((property, index) => {
return property.type === 'ObjectMethod' && property.key.type === 'Identifier' && property.key.name === key;
}) as babel.types.ObjectMethod;
}
has(key: string) {
return !!this.get(key);
}
delete(key: string) {
if (this.node.type !== 'ObjectExpression')
throw new Error('get method can only be called on an objectExpression');
let index = this.node.properties.findIndex((property, index) => {
return property.type === 'ObjectProperty' && property.key.type === 'Identifier' && property.key.name === key;
});
~index && this.node.properties.splice(index, 1);
return this;
}
}
export class FromsHandler {
constructor(public body: babel.types.Statement[]) {}
has(source: string) {
let existingIndex = this.body.findIndex((node) => {
return (node.type === 'ImportDeclaration' || node.type === 'ExportAllDeclaration' || node.type === 'ExportNamedDeclaration') && node.source && node.source.value === source;
});
return !!~existingIndex;
}
delete(source: string) {
let existingIndex = this.body.findIndex((node) => {
return (node.type === 'ImportDeclaration' || node.type === 'ExportAllDeclaration' || node.type === 'ExportNamedDeclaration') && node.source && node.source.value === source;
});
~existingIndex && this.body.splice(existingIndex, 1);
}
}
export class ImportsHandler {
constructor(public body: babel.types.Statement[]) {}
lastIndex() {
let i;
for (i = this.body.length - 1; i >= 0; i--) {
const node = this.body[i];
if (node.type === 'ImportDeclaration')
break;
}
return i;
}
last() {
return this.body[this.lastIndex()];
}
findIndexFrom(source: string) {
return this.body.findIndex((node) => {
return (node.type === 'ImportDeclaration') && node.source && node.source.value === source;
});
}
findFrom(source: string) {
const index = this.findIndexFrom(source);
return ~index ? this.body[index] : undefined;
}
hasFrom(source: string) {
const index = this.findIndexFrom(source);
return !!~index;
}
deleteFrom(source: string) {
const index = this.findIndexFrom(source);
~index && this.body.splice(index, 1);
}
get(identifer: string) {
}
}
export class ExportsHandler {
constructor(public body: babel.types.Statement[]) {}
lastIndex() {
let i;
for (i = this.body.length - 1; i >= 0; i--) {
const node = this.body[i];
if (node.type === 'ExportNamedDeclaration' || node.type === 'ExportAllDeclaration')
break;
}
return i;
}
last() {
return this.body[this.lastIndex()];
}
}
/**
* 没有处理析构的情形
*/
export class StatementHandler {
declarators: babel.types.VariableDeclarator[] = [];
constructor(public body: babel.types.Statement[]) {
body.forEach((node) => {
if (node.type === 'VariableDeclaration')
this.declarators.push(...node.declarations);
});
}
has(identifier: string) {
return !!this.get(identifier);
}
get(identifier: string) {
const declarator = this.declarators.find((declarator) => (declarator.id as babel.types.Identifier).name === identifier);
return declarator && new DeclarationHandler(declarator.init, declarator);
}
return(value?: string) {
let result = this.body.find((node) => node.type === 'ReturnStatement') as babel.types.ReturnStatement;
if (!result && value !== undefined) {
result = babel.template(`return ${value}`)() as babel.types.ReturnStatement;
this.body.push(result);
}
return result;
}
// map(func: (value: babel.types.VariableDeclarator, index: number, array: babel.types.VariableDeclarator[]) => any, thisArg?: any) {
// return this.declarators.map(func, thisArg || this);
// }
}
/**
* JS AST 处理器
* 该 class 可以在两端(node, browser)运行
*/
class ScriptHandler {
code: string;
ast: babel.types.File;
dirty: boolean = false;
state: { [name: string]: string | number | Array<string> };
constructor(code: string = '', options?: Object) {
this.code = code;
this.resetState();
this.ast = this.parse(code);
}
parse(code: string) {
return babel.parse(code, {
filename: 'file.js',
// Must require manually in VSCode
plugins: [require('@babel/plugin-syntax-dynamic-import')],
}) as babel.types.File;
}
generate(babelOptions?: babel.GeneratorOptions, prettierOptions?: PrettierOptions, prettierUseBabel?: boolean) {
if (prettierUseBabel) {
// prettier 直接用 ast format 会把注释干掉,很蛋疼,所以目前还是先用 babel 生成再 format 比较好
return prettier.format(this.code, Object.assign({} as PrettierOptions, prettierConfig, {
parser: () => this.ast,
}, prettierOptions));
// return generate(this.ast, {}, this.code).code + '\n';
} else {
const code = generate(this.ast, Object.assign({
retainLines: true,
concise: true,
compact: true,
}, babelOptions)).code;
let formatted = prettier.format(code, Object.assign({
parser: 'babel',
plugins: [parserBabylon],
} as PrettierOptions, prettierConfig, prettierOptions));
return formatted.replace(/component: \(\) =>\s+import\(([\s\S]+?)\),/g, (m, $1) => {
return `component: () => import(${$1.replace(/\n\s+/g, ' ').trim()}),`;
});
}
}
resetState() {
this.state = {};
}
/**
* 引入
* @param specifier 指示符
* @example
* $js.import('*').from('./u-button.vue');
* $js.import('UButton').from('./u-button.vue');
* $js.import({ default: 'UButton', UButton2: '', UButton3: 'UButton3' }).from('./u-button.vue');
*/
import(specifier: string | { [imported: string]: string }) {
if (typeof specifier === 'object') {
const insideString = Object.keys(specifier).map((imported) => {
const identifer = (specifier as { [imported: string]: string })[imported];
return imported + (identifer === imported || identifer === '' ? '' : ' as ' + identifer);
}).join(', ');
specifier = `{ ${insideString} }`;
}
this.state.declaration = 'import';
this.state.specifier = specifier;
return this;
}
/**
* 用于在全部的 import 集合中处理查找、删除等操作
*/
imports() {
return new ImportsHandler(this.ast.program.body);
}
/**
* 用于在全部的 export 集合中处理查找、删除等操作
*/
exports() {
return new ExportsHandler(this.ast.program.body);
}
export(specifier?: string | { [imported: string]: string }) {
if (typeof specifier === 'object') {
const insideString = Object.keys(specifier).map((imported) => {
const identifer = (specifier as { [imported: string]: string })[imported];
return imported + (identifer === imported || identifer === '' ? '' : ' as ' + identifer);
}).join(', ');
specifier = `{ ${insideString} }`;
}
this.state.declaration = 'export';
this.state.specifier = specifier;
return this;
}
/**
* 从哪里引入
* 如果遇到相同路径,以前的会被替换;如果不存在相同路径,则添加到最后一个 ImportDeclaration 之后
* @param source 文件路径
*/
from(source: string) {
const body = this.ast.program.body;
if (this.state.declaration === 'import') {
let existingIndex = body.findIndex((node) => node.type === 'ImportDeclaration' && node.source && node.source.value === source);
const importString = this.state.specifier;
if (!importString)
throw new Error('No import called before from');
const importDeclaration = babel.template(`import ${importString} from '${source}'`)() as babel.types.ImportDeclaration;
if (~existingIndex) {
body.splice(existingIndex, 1, importDeclaration);
this.state.lastIndex = existingIndex;
} else {
let i;
for (i = body.length - 1; i >= 0; i--) {
const node = body[i];
if (node.type === 'ImportDeclaration')
break;
}
i++;
body.splice(i, 0, importDeclaration);
this.state.lastIndex = i;
}
} else if (this.state.declaration === 'export') {
let existingIndex = body.findIndex((node) => (node.type === 'ExportAllDeclaration' || node.type === 'ExportNamedDeclaration') && node.source && node.source.value === source);
const exportString = this.state.specifier;
if (!exportString)
throw new Error('No export called before from');
const exportDeclaration = babel.template(`export ${exportString} from '${source}'`)() as babel.types.ExportNamedDeclaration;
if (~existingIndex) {
body.splice(existingIndex, 1, exportDeclaration);
this.state.lastIndex = existingIndex;
} else {
let i;
for (i = body.length - 1; i >= 0; i--) {
const node = body[i];
if (node.type === 'ExportAllDeclaration' || node.type === 'ExportNamedDeclaration')
break;
}
i++;
body.splice(i, 0, exportDeclaration);
this.state.lastIndex = i;
}
} else {
throw new Error('You must call import or export before from');
}
this.resetState();
return this;
}
/**
* 获取所有包含 from 的 import 和 export 声明
* import xxx from 'source'/export xxx from 'source'
* 一般用于判断存在或删除
*/
froms() {
return new FromsHandler(this.ast.program.body);
}
default() {
let result: DeclarationHandler;
babel.traverse(this.ast, {
ExportDefaultDeclaration(nodeInfo) {
result = new DeclarationHandler(nodeInfo.node.declaration, nodeInfo.node);
},
});
return result;
}
// delete() {
// if (this.state.lastIndex === undefined)
// return;
// // throw new Error('Import node index Required!');
// const body = this.ast.program.body;
// body.splice(this.state.lastIndex as number, 1);
// this.state.lastIndex = undefined;
// }
/**
* 用于在当前级别的作用域所有变量集合中处理查找、删除等操作
*/
variables() {
return new StatementHandler(this.ast.program.body);
}
/**
* 处理 mixins 不去重了,直接复用
*/
mergeArray(thisArray: babel.types.ArrayExpression, thatArray: babel.types.ArrayExpression) {
const thisIdentifiers: Map<string, true> = new Map();
thisArray.elements.forEach((element) => {
if (element.type === 'Identifier')
thisIdentifiers.set(element.name, true);
});
const replacements: { [old: string]: string } = {};
thatArray.elements.forEach((element) => {
if (element.type === 'Identifier') {
if (thisIdentifiers.has(element.name))
return;
// const newName = uniqueInMap(element.name, thisIdentifiers);
// if (newName !== element.name)
// element.name = replacements[element.name] = newName;
}
thisArray.elements.push(element);
});
return replacements;
}
mergeObject(thisObject: babel.types.ObjectExpression, thatObject: babel.types.ObjectExpression) {
const thisKeys: Map<string, true> = new Map();
thisObject.properties.forEach((property) => {
if (property.type !== 'SpreadElement' && property.key.type === 'Identifier')
thisKeys.set(property.key.name, true);
});
const replacements: { [old: string]: string } = {};
thatObject.properties.forEach((property) => {
if (property.type !== 'SpreadElement' && property.key.type === 'Identifier') {
const newName = uniqueInMap(property.key.name, thisKeys);
if (newName !== property.key.name)
property.key.name = replacements[property.key.name] = newName;
}
thisObject.properties.push(property);
});
return replacements;
}
/**
* 没有管 params 不相同的情况
* @param thisFunction
* @param thatFunction
*/
mergeFunction(thisFunction: babel.types.ObjectMethod | babel.types.FunctionExpression | babel.types.ArrowFunctionExpression, thatFunction: babel.types.ObjectMethod | babel.types.FunctionExpression | babel.types.ArrowFunctionExpression) {
const thisBody = (thisFunction.body as babel.types.BlockStatement).body;
const thatBody = (thatFunction.body as babel.types.BlockStatement).body;
const thisVariables: Map<string, true> = new Map();
const thisStatement = new StatementHandler(thisBody);
const thisReturn = thisStatement.return();
const thisReturnIndex = thisReturn ? thisBody.indexOf(thisReturn) : thisBody.length;
thisStatement.declarators.forEach((declarator) => {
if (declarator.id.type === 'Identifier')
thisVariables.set(declarator.id.name, true);
// else @TODO: 处理其它析构等情形
});
let replacements: { [key: string]: { [old: string]: string } } = { variables: {}, return: {} };
thatBody.forEach((node) => {
if (node.type === 'VariableDeclaration') {
node.declarations.forEach((declarator) => {
if (declarator.id.type === 'Identifier') {
const newName = uniqueInMap(declarator.id.name, thisVariables);
if (newName !== declarator.id.name)
declarator.id.name = replacements['variables'][declarator.id.name] = newName;
}
});
} else if (thisReturn && node.type === 'ReturnStatement') {
if (thisReturn.argument.type === 'ObjectExpression' && node.argument.type === 'ObjectExpression') {
replacements['return'] = this.mergeObject(
thisReturn.argument,
node.argument,
);
return;
} else {
console.error('Returns cannot be merged!');
}
}
thisBody.splice(thisReturnIndex, 0, node);
});
return replacements;
}
mergeVueObject(thisObject: babel.types.ObjectExpression, thatObject: babel.types.ObjectExpression) {
const thisProperties = thisObject.properties;
const thisPropertiesMap: { [key: string]: babel.types.ObjectProperty | babel.types.ObjectMethod } = {};
thisProperties.forEach((property) => {
if (property.type !== 'SpreadElement' && property.key.type === 'Identifier')
thisPropertiesMap[property.key.name] = property;
});
let thatOrderIndex = -1;
const orderIndexOf = (optionsKey: string, lastIndex: number) => {
const index = ORDER_IN_COMPONENTS_MAP[optionsKey];
return index === undefined ? lastIndex : index;
}
const OBJECT_OPTIONS = ['components', 'directives', 'filters', 'props', 'propsData', 'computed', 'watch', 'methods'];
const replacements: { [key: string]: { [old: string]: string } } = {};
thatObject.properties.forEach((thatProperty) => {
// 直接合并 { ...obj } 的情况
if (thatProperty.type === 'SpreadElement' || thatProperty.key.type !== 'Identifier')
return thisProperties.push(thatProperty);
const thatKey = thatProperty.key.name;
let insertIndex = thisProperties.length;
if (thisPropertiesMap[thatKey]) {
const thisProperty = thisPropertiesMap[thatKey];
if (OBJECT_OPTIONS.includes(thatKey)) {
replacements[thatKey] = this.mergeObject(
(thisProperty as babel.types.ObjectProperty).value as babel.types.ObjectExpression,
(thatProperty as babel.types.ObjectProperty).value as babel.types.ObjectExpression,
);
return;
} else if (thatKey === 'mixins') {
replacements['mixins'] = this.mergeArray(
(thisProperty as babel.types.ObjectProperty).value as babel.types.ArrayExpression,
(thatProperty as babel.types.ObjectProperty).value as babel.types.ArrayExpression,
);
return;
} else if (LIFECYCLE_HOOKS.includes(thatKey)) {
let thisFunction: babel.types.ObjectMethod | babel.types.FunctionExpression | babel.types.ArrowFunctionExpression;
let thatFunction: babel.types.ObjectMethod | babel.types.FunctionExpression | babel.types.ArrowFunctionExpression;
if (thisProperty.type === 'ObjectMethod')
thisFunction = thisProperty;
else {
thisFunction = thisProperty.value as babel.types.FunctionExpression;
}
if (thatProperty.type === 'ObjectMethod')
thatFunction = thatProperty;
else {
thatFunction = thatProperty.value as babel.types.FunctionExpression;
}
/**
* data: Object 的情况不处理
*/
this.mergeFunction(thisFunction, thatFunction);
return;
} else if (thatKey === 'data') {
let thisFunction: babel.types.ObjectMethod | babel.types.FunctionExpression | babel.types.ArrowFunctionExpression;
let thatFunction: babel.types.ObjectMethod | babel.types.FunctionExpression | babel.types.ArrowFunctionExpression;
if (thisProperty.type === 'ObjectMethod')
thisFunction = thisProperty;
else {
thisFunction = thisProperty.value as babel.types.FunctionExpression;
}
if (thatProperty.type === 'ObjectMethod')
thatFunction = thatProperty;
else {
thatFunction = thatProperty.value as babel.types.FunctionExpression;
}
/**
* data: Object 的情况不处理
*/
const dataResult = this.mergeFunction(thisFunction, thatFunction);
replacements['data'] = dataResult.return;
return;
} else {
// [
// 'el',
// 'name',
// 'parent',
// 'functional',
// ['delimiters', 'comments'],
// 'extends',
// 'inheritAttrs',
// 'model',
// ['template', 'render'],
// 'renderError'
// ]
if (thisProperty.type === 'ObjectProperty' && thatProperty.type === 'ObjectProperty'
&& generate(thisProperty.value, { minified: true }).code === generate(thisProperty.value, { minified: true }).code)
return;
console.warn(`不确定如何合并选项 ${thatKey},处理的结果是将它和原选项并存!\n`);
insertIndex = thisProperties.indexOf(thisPropertiesMap[thatKey]) + 1;
}
} else {
/* 找到合适插入的位置 */
thatOrderIndex = orderIndexOf(thatProperty.key.name, thatOrderIndex);
let thisOrderIndex = -1;
for (let i = 0; i < thisProperties.length; i++) {
const thisProperty = thisProperties[i];
if (thisProperty.type !== 'SpreadElement' && thisProperty.key.type === 'Identifier') {
thisOrderIndex = orderIndexOf(thisProperty.key.name, thisOrderIndex);
if (thisProperty.key.name === thatProperty.key.name) {
insertIndex = i + 1;
break;
} else if (thisOrderIndex > thatOrderIndex) {
insertIndex = i;
break;
}
}
}
}
thisProperties.splice(insertIndex, 0, thatProperty);
});
return replacements;
}
/**
* 将另一个 that 的脚本合并到当前样式中
* 原则是处理能够名单里的代码,其它的自然合并(因此可能会有问题)
* @TODO 目前对另一个 that 的样式 ast 有修改
* @param that 另一个 ScriptHandler
*/
merge(that: ScriptHandler) {
const thisBody = this.ast.program.body;
const thatBody = that.ast.program.body;
const imports = this.imports();
let afterImportIndex = imports.lastIndex() + 1;
let exportDefaultIndex = thisBody.findIndex((node) => node.type === 'ExportDefaultDeclaration');
const hasExportDefault = !!~exportDefaultIndex;
if (!hasExportDefault)
exportDefaultIndex = thisBody.length;
const thisVariables: Map<string, true> = new Map();
this.variables().declarators.forEach((declarator) => {
if (declarator.id.type === 'Identifier')
thisVariables.set(declarator.id.name, true);
// else @TODO: 处理其它析构等情形
});
let replacements: { [key: string]: { [old: string]: string } } = { variables: {} };
thatBody.forEach((node) => {
if (node.type === 'ImportDeclaration') { // @TODO 暂时不去重 import identifier,block 的这种情况比较少。因为 import 周边文件就变成 external() 了
const index = imports.findIndexFrom(node.source.value);
if (~index && generate(thisBody[index], { minified: true }).code === generate(node, { minified: true }).code)
thisBody.splice(index, 1, node);
else
thisBody.splice(afterImportIndex++, 0, node);
} else if (node.type === 'VariableDeclaration') {
node.declarations.forEach((declarator) => {
if (declarator.id.type === 'Identifier') {
const newName = uniqueInMap(declarator.id.name, thisVariables);
if (newName !== declarator.id.name)
declarator.id.name = replacements['variables'][declarator.id.name] = newName;
}
});
thisBody.splice(exportDefaultIndex++, 0, node);
} else if (node.type === 'ExportDefaultDeclaration' && hasExportDefault) { // 这个地方虽然在循环里,但只会走一遍;如果你有两个 export default,我把代码吃了!!
/*
处理 Vue 的大片
按照 Vue ComponentOptions 进行处理 https://github.com/vuejs/vue/blob/dev/types/options.d.ts#L67
顺序参照 https://eslint.vuejs.org/rules/order-in-components.html#options
*/
const thisExportDefault = this.export().default();
if (!thisExportDefault.is('object')) // 如果不是 object 不处理了
return thisBody.splice(exportDefaultIndex++, 0, node);
const thatExportDefault = that.export().default();
if (!thatExportDefault.is('object')) // 如果不是 object 不处理了
return thisBody.splice(exportDefaultIndex++, 0, node);
replacements = this.mergeVueObject(
thisExportDefault.node as babel.types.ObjectExpression,
thatExportDefault.node as babel.types.ObjectExpression,
);
} else { // 默认插入到 export default 的位置或最后
thisBody.splice(exportDefaultIndex++, 0, node);
}
});
/* 处理代码中的 this */
const identifierMap = { ...replacements['props'], ...replacements['data'], ...replacements['computed'], ...replacements['methods'] };
babel.traverse(that.ast, {
Identifier(nodeInfo) {
if (nodeInfo.parent.type === 'MemberExpression' && nodeInfo.parent.object.type === 'ThisExpression') {
if (identifierMap[nodeInfo.node.name])
nodeInfo.node.name = identifierMap[nodeInfo.node.name];
}
},
});
return replacements;
}
}
export default ScriptHandler;