@vyr/pack-class-wrapper-plugin 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +42 -0
- package/loaders/descriptor.js +89 -0
- package/loaders/dynamic-interpreter.js +54 -0
- package/loaders/index.js +30 -0
- package/loaders/input-system.js +31 -0
- package/loaders/object-pool.js +79 -0
- package/package.json +1 -0
- package/utils.js +79 -0
package/index.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const { join, resolve } = require("path")
|
|
2
|
+
|
|
3
|
+
class ClassWrapperPlugin {
|
|
4
|
+
|
|
5
|
+
constructor(rules = []) {
|
|
6
|
+
const handlers = [
|
|
7
|
+
{ path: join(__dirname, '../../packages/universal/engine'), className: 'ObjectPool' },
|
|
8
|
+
{ path: join(__dirname, '../../packages/universal/engine'), className: 'Descriptor' },
|
|
9
|
+
{ path: join(__dirname, '../../packages/universal/engine'), className: 'InputSystem' },
|
|
10
|
+
{ path: join(__dirname, '../../packages/universal/engine'), className: 'DynamicInterpreter' },
|
|
11
|
+
]
|
|
12
|
+
this.rules = handlers.filter(handler => rules.includes(handler.className))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
validate(path) {
|
|
16
|
+
for (const rule of this.rules) {
|
|
17
|
+
if (path.indexOf(rule.path) > -1 && path.indexOf(rule.className) > -1) return rule
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
wrapper(module, rule) {
|
|
22
|
+
const custom = {
|
|
23
|
+
loader: resolve(__dirname, './loaders/index'),
|
|
24
|
+
options: JSON.stringify({ className: rule.className })
|
|
25
|
+
}
|
|
26
|
+
module.loaders.push(custom)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
compiler(module) {
|
|
30
|
+
if (!module.request) return
|
|
31
|
+
const rule = this.validate(module.request)
|
|
32
|
+
if (rule) return this.wrapper(module, rule)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
apply(compiler) {
|
|
36
|
+
compiler.hooks.compilation.tap('class-wrapper-plugin', (compilation) => {
|
|
37
|
+
compilation.hooks.buildModule.tap('class-wrapper-plugin', (module) => this.compiler(module))
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = ClassWrapperPlugin
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
const types = require('@babel/types');
|
|
2
|
+
const { default: traverse } = require('@babel/traverse')
|
|
3
|
+
const { vueImportDeclaration, wrapperDeclaration, wrapperIdentifier, wrapperCall, returnwrapper } = require('../utils')
|
|
4
|
+
|
|
5
|
+
const pushRetuenStatement = (block) => {
|
|
6
|
+
// 假设block是一个BlockStatement节点
|
|
7
|
+
if (!types.isBlockStatement(block)) return
|
|
8
|
+
// 获取BlockStatement的最后一个语句
|
|
9
|
+
const lastStatement = block.body[block.body.length - 1];
|
|
10
|
+
// 检查这个语句是否是ReturnStatement
|
|
11
|
+
if (types.isReturnStatement(lastStatement)) return
|
|
12
|
+
//添加 return 语句
|
|
13
|
+
block.body.push(returnwrapper)
|
|
14
|
+
}
|
|
15
|
+
const wrapperConstructor = (p) => {
|
|
16
|
+
p.traverse({
|
|
17
|
+
CallExpression(path) {
|
|
18
|
+
if (
|
|
19
|
+
types.isMemberExpression(path.node.callee) &&
|
|
20
|
+
path.node.callee.object.type === 'ThisExpression' &&
|
|
21
|
+
path.node.callee.property.name === 'add'
|
|
22
|
+
) {
|
|
23
|
+
|
|
24
|
+
const newCallee = types.memberExpression(wrapperCall, types.identifier('add'));
|
|
25
|
+
path.node.callee = newCallee;
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
ReturnStatement(path) {
|
|
29
|
+
path.replaceWith(returnwrapper)
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
pushRetuenStatement(p.node.body)
|
|
33
|
+
p.stop()
|
|
34
|
+
}
|
|
35
|
+
const validateConstructor = (p, options) => {
|
|
36
|
+
if (types.isClassMethod(p.node) === false) return false
|
|
37
|
+
if (p.node.kind !== 'constructor') return false
|
|
38
|
+
return p.parentPath.parent.id.name === options.className
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const wrapperMethod = (p) => {
|
|
42
|
+
const instance = p.node.params[0]
|
|
43
|
+
p.traverse({
|
|
44
|
+
MemberExpression(path) {
|
|
45
|
+
if (
|
|
46
|
+
path.node.object.type === 'Identifier' &&
|
|
47
|
+
path.node.object.name === 'instance' &&
|
|
48
|
+
!path.parentPath.isMemberExpression()
|
|
49
|
+
) {
|
|
50
|
+
const vueCallInstance = types.callExpression(wrapperIdentifier, [instance]);
|
|
51
|
+
const newMemberExpression = types.memberExpression(vueCallInstance, path.node.property);
|
|
52
|
+
path.replaceWith(newMemberExpression);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
p.stop()
|
|
57
|
+
}
|
|
58
|
+
const validateMethod = (p, options) => {
|
|
59
|
+
if (types.isClassMethod(p.node) === false) return false
|
|
60
|
+
if (p.node.kind !== 'method') return false
|
|
61
|
+
if (p.node.key.name !== 'beenInstantiated') return false
|
|
62
|
+
return p.parentPath.parent.id.name === options.className
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const wrapperDescriptor = (ast, options) => {
|
|
66
|
+
//添加导入语句
|
|
67
|
+
ast.program.body.splice(2, 0, vueImportDeclaration)
|
|
68
|
+
ast.program.body.splice(3, 0, wrapperDeclaration)
|
|
69
|
+
|
|
70
|
+
traverse(ast, {
|
|
71
|
+
enter: (p) => {
|
|
72
|
+
if (validateConstructor(p, options)) wrapperConstructor(p)
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
traverse(ast, {
|
|
77
|
+
enter: (p) => {
|
|
78
|
+
if (validateMethod(p, options)) wrapperMethod(p)
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
const validateDescriptor = (options) => {
|
|
83
|
+
return options.className === 'Descriptor'
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
module.exports = {
|
|
87
|
+
wrapperDescriptor,
|
|
88
|
+
validateDescriptor,
|
|
89
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const types = require('@babel/types');
|
|
2
|
+
const { parse } = require('@babel/parser')
|
|
3
|
+
const { default: traverse } = require('@babel/traverse')
|
|
4
|
+
|
|
5
|
+
const wrapperMethod = (p) => {
|
|
6
|
+
const newCode = `
|
|
7
|
+
function checkCode(){
|
|
8
|
+
if (this._currentDepth > 3000) {
|
|
9
|
+
observer.trigger('__VYR_RUNTIME@remote', { type: 'Dynamic', uuid: this.unit.uuid })
|
|
10
|
+
return
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
// 解析新代码为AST
|
|
16
|
+
const newAst = parse(newCode, {
|
|
17
|
+
sourceType: 'module',
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// 获取函数声明中的函数体语句
|
|
21
|
+
const functionDeclaration = newAst.program.body[0];
|
|
22
|
+
const functionBodyStatements = functionDeclaration.body.body;
|
|
23
|
+
|
|
24
|
+
// 获取目标方法体的语句数组
|
|
25
|
+
const methodBody = p.node.body.body;
|
|
26
|
+
|
|
27
|
+
// 将函数体中的语句插入到目标方法体的开头
|
|
28
|
+
methodBody.unshift(...functionBodyStatements);
|
|
29
|
+
|
|
30
|
+
p.stop()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const validateMethod = (p, options) => {
|
|
34
|
+
if (types.isClassMethod(p.node) === false) return false
|
|
35
|
+
if (p.node.kind !== 'method') return false
|
|
36
|
+
if (p.node.key.name !== '_markSubDynamic') return false
|
|
37
|
+
return p.parentPath.parent.id.name === options.className
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const wrapperDynamicInterpreter = (ast, options) => {
|
|
41
|
+
traverse(ast, {
|
|
42
|
+
enter: (p) => {
|
|
43
|
+
if (validateMethod(p, options)) wrapperMethod(p)
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
const validateDynamicInterpreter = (options) => {
|
|
48
|
+
return options.className === 'DynamicInterpreter'
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = {
|
|
52
|
+
wrapperDynamicInterpreter,
|
|
53
|
+
validateDynamicInterpreter,
|
|
54
|
+
}
|
package/loaders/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const { parse } = require('@babel/parser')
|
|
2
|
+
const { default: generate } = require('@babel/generator');
|
|
3
|
+
const { validateObjectPool, wrapperObjectPool } = require('./object-pool');
|
|
4
|
+
const { validateDescriptor, wrapperDescriptor } = require('./descriptor');
|
|
5
|
+
const { validateInputSystem, wrapperInputSystem } = require('./input-system');
|
|
6
|
+
const { validateDynamicInterpreter, wrapperDynamicInterpreter } = require('./dynamic-interpreter')
|
|
7
|
+
|
|
8
|
+
const wrappers = [
|
|
9
|
+
{ validate: validateObjectPool, executor: wrapperObjectPool },
|
|
10
|
+
{ validate: validateDescriptor, executor: wrapperDescriptor },
|
|
11
|
+
{ validate: validateInputSystem, executor: wrapperInputSystem },
|
|
12
|
+
{ validate: validateDynamicInterpreter, executor: wrapperDynamicInterpreter },
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
module.exports = function (source) {
|
|
16
|
+
const options = this.getOptions()
|
|
17
|
+
|
|
18
|
+
const ast = parse(source, { sourceType: 'module', plugins: ['typescript'] })
|
|
19
|
+
|
|
20
|
+
for (const wrapper of wrappers) {
|
|
21
|
+
if (wrapper.validate(options)) {
|
|
22
|
+
wrapper.executor(ast, options)
|
|
23
|
+
break
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const { code } = generate(ast, { retainLines: false, concise: false, jsescOption: { minimal: true, sourceType: 'module' } })
|
|
28
|
+
|
|
29
|
+
return code
|
|
30
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const types = require("@babel/types");
|
|
2
|
+
const { default: traverse } = require("@babel/traverse");
|
|
3
|
+
const { ifInputSystemStatement } = require("../utils");
|
|
4
|
+
|
|
5
|
+
const wrapperMethod = (p) => {
|
|
6
|
+
p.node.body.body.unshift(ifInputSystemStatement);
|
|
7
|
+
p.stop();
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const validateMethod = (p, options) => {
|
|
11
|
+
if (types.isClassMethod(p.node) === false) return false;
|
|
12
|
+
if (p.node.kind !== "method") return false;
|
|
13
|
+
if (p.node.key.name !== "listen") return false;
|
|
14
|
+
return p.parentPath.parent.id.name === options.className;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const wrapperInputSystem = (ast, options) => {
|
|
18
|
+
const onEnter = (p) => {
|
|
19
|
+
if (validateMethod(p, options)) wrapperMethod(p);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
traverse(ast, { enter: onEnter });
|
|
23
|
+
};
|
|
24
|
+
const validateInputSystem = (options) => {
|
|
25
|
+
return options.className === "InputSystem";
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
module.exports = {
|
|
29
|
+
wrapperInputSystem,
|
|
30
|
+
validateInputSystem,
|
|
31
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const types = require('@babel/types');
|
|
2
|
+
const { default: traverse } = require('@babel/traverse')
|
|
3
|
+
const { vueImportDeclaration, wrapperDeclaration, wrapperIdentifier } = require('../utils')
|
|
4
|
+
|
|
5
|
+
const replaceMethodGet = (path) => {
|
|
6
|
+
const left = path.node.argument.left;
|
|
7
|
+
const right = path.node.argument.right;
|
|
8
|
+
if (types.isNullLiteral(right)) {
|
|
9
|
+
// 提取 instance.deref() 表达式
|
|
10
|
+
const derefCall = left;
|
|
11
|
+
// 创建 const ref = instance.deref(); 语句
|
|
12
|
+
const refDeclaration = types.variableDeclaration('const', [
|
|
13
|
+
types.variableDeclarator(
|
|
14
|
+
types.identifier('ref'),
|
|
15
|
+
derefCall
|
|
16
|
+
)
|
|
17
|
+
]);
|
|
18
|
+
// 创建 ref? v(ref) : null; 表达式
|
|
19
|
+
const conditionalExpression = types.conditionalExpression(
|
|
20
|
+
types.identifier('ref'),
|
|
21
|
+
types.callExpression(wrapperIdentifier, [types.identifier('ref')]),
|
|
22
|
+
types.nullLiteral()
|
|
23
|
+
);
|
|
24
|
+
// 替换原语句
|
|
25
|
+
path.replaceWith(refDeclaration);
|
|
26
|
+
path.insertAfter(types.returnStatement(conditionalExpression))
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const wrapperMethod = (p, method) => {
|
|
31
|
+
p.traverse({
|
|
32
|
+
CallExpression(path) {
|
|
33
|
+
const { node } = path;
|
|
34
|
+
|
|
35
|
+
// 检查是否是 instance.deref() 调用
|
|
36
|
+
if (types.isMemberExpression(node.callee) &&
|
|
37
|
+
types.isIdentifier(node.callee.property, { name: "deref" }) &&
|
|
38
|
+
types.isIdentifier(node.callee.object, { name: "instance" }) &&
|
|
39
|
+
node.arguments.length === 0) {
|
|
40
|
+
|
|
41
|
+
const wrappedCall = types.callExpression(
|
|
42
|
+
wrapperIdentifier,
|
|
43
|
+
[node]
|
|
44
|
+
);
|
|
45
|
+
path.replaceWith(wrappedCall);
|
|
46
|
+
|
|
47
|
+
path.skip();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
const validateMethod = (p, options) => {
|
|
53
|
+
if (types.isClassMethod(p.node) === false) return false
|
|
54
|
+
if (p.node.static === false) return false
|
|
55
|
+
if (p.parentPath.parent.id.name !== options.className) return
|
|
56
|
+
return ['get'].includes(p.node.key.name) ? p.node.key.name : false
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const wrapperObjectPool = (ast, options) => {
|
|
60
|
+
//添加导入语句
|
|
61
|
+
ast.program.body.splice(2, 0, vueImportDeclaration)
|
|
62
|
+
ast.program.body.splice(3, 0, wrapperDeclaration)
|
|
63
|
+
|
|
64
|
+
traverse(ast, {
|
|
65
|
+
enter: (p) => {
|
|
66
|
+
const result = validateMethod(p, options)
|
|
67
|
+
if (result === false) return
|
|
68
|
+
wrapperMethod(p, result)
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
const validateObjectPool = (options) => {
|
|
73
|
+
return options.className === 'ObjectPool'
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
module.exports = {
|
|
77
|
+
wrapperObjectPool,
|
|
78
|
+
validateObjectPool,
|
|
79
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"name":"@vyr/pack-class-wrapper-plugin","version":"0.0.4","description":"","main":"./index.js","author":"","license":"ISC"}
|
package/utils.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const types = require('@babel/types');
|
|
2
|
+
|
|
3
|
+
// 创建具名导入的标识符
|
|
4
|
+
const reactiveIdentifier = types.identifier("reactive")
|
|
5
|
+
|
|
6
|
+
// 构建 import { reactive } from 'vue'
|
|
7
|
+
const vueImportDeclaration = types.importDeclaration(
|
|
8
|
+
[
|
|
9
|
+
types.importSpecifier(
|
|
10
|
+
reactiveIdentifier, // 导入的变量名
|
|
11
|
+
reactiveIdentifier // 如果重命名,可以用 types.identifier('newName')
|
|
12
|
+
)
|
|
13
|
+
],
|
|
14
|
+
types.stringLiteral('vue')
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
// 1. 构建条件表达式 (window['Runtime'] === 'editor')
|
|
18
|
+
const windowRuntime = types.memberExpression(
|
|
19
|
+
types.identifier('window'),
|
|
20
|
+
types.stringLiteral('__VYR_RUNTIME_MODE'),
|
|
21
|
+
true // computed: true 因使用方括号访问
|
|
22
|
+
);
|
|
23
|
+
const runtimeCheck = types.binaryExpression(
|
|
24
|
+
'===',
|
|
25
|
+
windowRuntime,
|
|
26
|
+
types.stringLiteral('editor')
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
// 2. 构建 reactive 调用 (reactive(raw))
|
|
30
|
+
const reactiveCall = types.callExpression(reactiveIdentifier, [types.identifier('raw')])
|
|
31
|
+
|
|
32
|
+
// 3. 组装三元表达式
|
|
33
|
+
const conditionalExpr = types.conditionalExpression(
|
|
34
|
+
runtimeCheck,
|
|
35
|
+
reactiveCall,
|
|
36
|
+
types.identifier('raw') // 直接返回 raw
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// 4. 构建箭头函数体
|
|
40
|
+
const returnStatement = types.returnStatement(conditionalExpr);
|
|
41
|
+
const functionBody = types.blockStatement([returnStatement]);
|
|
42
|
+
|
|
43
|
+
// 5. 创建箭头函数表达式
|
|
44
|
+
const arrowFunction = types.arrowFunctionExpression(
|
|
45
|
+
[types.identifier('raw')], // 参数列表
|
|
46
|
+
functionBody
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// 6. 组装变量声明
|
|
50
|
+
const wrapperIdentifier = types.identifier('wrapper');
|
|
51
|
+
const wrapperDeclarator = types.variableDeclarator(wrapperIdentifier, arrowFunction)
|
|
52
|
+
const wrapperDeclaration = types.variableDeclaration('const', [wrapperDeclarator])
|
|
53
|
+
const wrapperCall = types.callExpression(wrapperIdentifier, [types.thisExpression()])
|
|
54
|
+
const returnwrapper = types.returnStatement(wrapperCall);
|
|
55
|
+
|
|
56
|
+
const inputRuntime = types.memberExpression(
|
|
57
|
+
types.identifier('window'),
|
|
58
|
+
types.stringLiteral('__VYR_RUNTIME.DISABLED_INPUT'),
|
|
59
|
+
true
|
|
60
|
+
);
|
|
61
|
+
const disabledInputSystem = types.binaryExpression(
|
|
62
|
+
"===",
|
|
63
|
+
inputRuntime,
|
|
64
|
+
types.booleanLiteral(true)
|
|
65
|
+
)
|
|
66
|
+
const ifInputSystemStatement = types.ifStatement(
|
|
67
|
+
disabledInputSystem,
|
|
68
|
+
types.returnStatement(),
|
|
69
|
+
null
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
module.exports = {
|
|
73
|
+
vueImportDeclaration,
|
|
74
|
+
wrapperDeclaration,
|
|
75
|
+
wrapperIdentifier,
|
|
76
|
+
wrapperCall,
|
|
77
|
+
returnwrapper,
|
|
78
|
+
ifInputSystemStatement,
|
|
79
|
+
}
|