mm_hook_func 0.0.0-stage → 1.0.1
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/example/index.js +50 -0
- package/index.js +24 -0
- package/lib/hook_func.js +178 -0
- package/lib/render_hook.js +26 -0
- package/package.json +34 -5
- package/test.js +22 -0
- package/tests/hook_func.test.js +130 -0
- package/README.md +0 -3
package/example/index.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mm_hook_func 钩子函数模块示例
|
|
3
|
+
* 演示 HookFunc 的函数注册表:addFunc / hasFunc / runFunc / listFuncs / delFunc
|
|
4
|
+
* 运行:node example/index.js
|
|
5
|
+
*/
|
|
6
|
+
const { HookFunc, getHookFunc } = require('../index.js');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 主函数
|
|
10
|
+
* @returns {Promise<void>}
|
|
11
|
+
*/
|
|
12
|
+
async function main() {
|
|
13
|
+
console.log('=== 钩子函数注册表 ===');
|
|
14
|
+
|
|
15
|
+
// 获取单例(多次获取返回同一实例)
|
|
16
|
+
var hook = getHookFunc();
|
|
17
|
+
var hook2 = getHookFunc();
|
|
18
|
+
console.log('getHookFunc 单例 ->', hook === hook2);
|
|
19
|
+
|
|
20
|
+
// 注册函数
|
|
21
|
+
hook.addFunc('html_filter', function (ctx, params, html) {
|
|
22
|
+
return html.replace(/<script[\s\S]*?<\/script>/ig, '');
|
|
23
|
+
});
|
|
24
|
+
hook.addFunc('hello', function (name) {
|
|
25
|
+
return '你好, ' + name + '!';
|
|
26
|
+
});
|
|
27
|
+
console.log('注册后函数列表 ->', hook.listFuncs().join(', '));
|
|
28
|
+
console.log('hasFunc("hello") ->', hook.hasFunc('hello'));
|
|
29
|
+
|
|
30
|
+
// 运行函数
|
|
31
|
+
console.log('runFunc("hello", "张三") ->', await hook.runFunc('hello', '张三'));
|
|
32
|
+
console.log('runFunc("html_filter") 过滤脚本标签 ->',
|
|
33
|
+
await hook.runFunc('html_filter', {}, {}, '<p>正文</p><script>alert(1)</script>'));
|
|
34
|
+
|
|
35
|
+
// 未注册函数:原样返回最后一个参数
|
|
36
|
+
console.log('runFunc("no_such_func") ->', await hook.runFunc('no_such_func', 'fallback'));
|
|
37
|
+
|
|
38
|
+
// 删除函数
|
|
39
|
+
console.log('delFunc("hello") ->', hook.delFunc('hello'));
|
|
40
|
+
console.log('删除后 hasFunc("hello") ->', hook.hasFunc('hello'));
|
|
41
|
+
|
|
42
|
+
// 直接实例化
|
|
43
|
+
var h2 = new HookFunc();
|
|
44
|
+
console.log('\n直接实例化 HookFunc ->', h2 instanceof HookFunc);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
main().catch(function (err) {
|
|
48
|
+
console.error(err);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
});
|
package/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const { HookFunc } = require('./lib/hook_func.js');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 钩子函数单例
|
|
5
|
+
* @type {HookFunc|null}
|
|
6
|
+
*/
|
|
7
|
+
let hook_func = null;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 获取钩子函数单例
|
|
11
|
+
* @param {object} config 配置选项
|
|
12
|
+
* @returns {HookFunc} 钩子函数实例
|
|
13
|
+
*/
|
|
14
|
+
function getHookFunc(config) {
|
|
15
|
+
if (!hook_func) {
|
|
16
|
+
hook_func = new HookFunc(config);
|
|
17
|
+
}
|
|
18
|
+
return hook_func;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
22
|
+
HookFunc,
|
|
23
|
+
getHookFunc
|
|
24
|
+
};
|
package/lib/hook_func.js
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { Base } = require('mm_mod');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 钩子函数类
|
|
7
|
+
*
|
|
8
|
+
* 维护一个「函数名 → 函数体」的注册表,供业务按名调用可替换的函数;
|
|
9
|
+
* 同时可作为插件通过 bind(web) 向 web 请求管道的 render 阶段注册钩子,
|
|
10
|
+
* 在渲染阶段对字符串结果按配置的函数名做统一处理。
|
|
11
|
+
*/
|
|
12
|
+
class HookFunc extends Base {
|
|
13
|
+
/**
|
|
14
|
+
* 模块默认配置
|
|
15
|
+
*/
|
|
16
|
+
static config = {
|
|
17
|
+
name: 'hook_func',
|
|
18
|
+
title: '钩子函数模块',
|
|
19
|
+
description: '函数名值注册表,并向 web 管道注册 render 钩子',
|
|
20
|
+
render_name: 'html_filter'
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 函数注册表
|
|
25
|
+
* @type {Object<string, Function>}
|
|
26
|
+
*/
|
|
27
|
+
#funcs = {};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 已挂载的 web 服务器实例
|
|
31
|
+
* @type {Object|null}
|
|
32
|
+
*/
|
|
33
|
+
#web = null;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 添加函数
|
|
37
|
+
* @param {string} name 函数名称
|
|
38
|
+
* @param {Function} func 函数体
|
|
39
|
+
* @returns {HookFunc} 当前实例
|
|
40
|
+
* @throws {TypeError} 名称非法或函数体非函数时
|
|
41
|
+
*/
|
|
42
|
+
addFunc(name, func) {
|
|
43
|
+
if (typeof name !== 'string' || name === '') {
|
|
44
|
+
throw new TypeError(`${this._label()} name must be a non-empty string`);
|
|
45
|
+
}
|
|
46
|
+
if (typeof func !== 'function') {
|
|
47
|
+
throw new TypeError(`${this._label()} func must be a function`);
|
|
48
|
+
}
|
|
49
|
+
this.#funcs[name] = func;
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 删除函数
|
|
55
|
+
* @param {string} name 函数名称
|
|
56
|
+
* @returns {boolean} 是否删除成功
|
|
57
|
+
*/
|
|
58
|
+
delFunc(name) {
|
|
59
|
+
if (!this.hasFunc(name)) return false;
|
|
60
|
+
delete this.#funcs[name];
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 获取函数
|
|
66
|
+
* @param {string} name 函数名称
|
|
67
|
+
* @returns {Function|undefined} 函数体,未注册时为 undefined
|
|
68
|
+
*/
|
|
69
|
+
getFunc(name) {
|
|
70
|
+
return this.#funcs[name];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 判断函数是否存在
|
|
75
|
+
* @param {string} name 函数名称
|
|
76
|
+
* @returns {boolean} 是否存在
|
|
77
|
+
*/
|
|
78
|
+
hasFunc(name) {
|
|
79
|
+
return typeof this.#funcs[name] === 'function';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 列出所有函数名称
|
|
84
|
+
* @returns {Array<string>} 函数名称列表
|
|
85
|
+
*/
|
|
86
|
+
listFuncs() {
|
|
87
|
+
return Object.keys(this.#funcs);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 清空函数注册表
|
|
92
|
+
* @returns {HookFunc} 当前实例
|
|
93
|
+
*/
|
|
94
|
+
clear() {
|
|
95
|
+
this.#funcs = {};
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* 运行函数
|
|
101
|
+
* @param {string} name 函数名称
|
|
102
|
+
* @param {...*} args 函数参数
|
|
103
|
+
* @returns {Promise<*>} 函数返回值;未注册时原样返回最后一个参数
|
|
104
|
+
*/
|
|
105
|
+
async runFunc(name, ...args) {
|
|
106
|
+
const func = this.getFunc(name);
|
|
107
|
+
if (!func) {
|
|
108
|
+
return args.length > 0 ? args[args.length - 1] : undefined;
|
|
109
|
+
}
|
|
110
|
+
return await func(...args);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 绑定 web 服务器:向其请求管道的 render 阶段注册渲染钩子
|
|
115
|
+
*
|
|
116
|
+
* 支持两代管线:
|
|
117
|
+
* - 新代(mm_event 七阶段链):web 提供 getEventManager(),本方法向其注册一个
|
|
118
|
+
* render 阶段的事件驱动(驱动脚本 lib/render_hook.js);
|
|
119
|
+
* - 旧代(Handle/Hooks):web 提供 getHandle(),保持原有 on('render') 注册方式兼容。
|
|
120
|
+
*
|
|
121
|
+
* @param {Object} web web 服务器实例,需提供 getEventManager() 或 getHandle() 方法
|
|
122
|
+
* @returns {HookFunc} 当前实例
|
|
123
|
+
* @throws {TypeError} web 不具备 getEventManager/getHandle 方法时
|
|
124
|
+
*/
|
|
125
|
+
bind(web) {
|
|
126
|
+
if (!web) {
|
|
127
|
+
throw new TypeError(`${this._label()} web is required`);
|
|
128
|
+
}
|
|
129
|
+
// 旧代管线(Handle/Hooks):保留兼容
|
|
130
|
+
if (typeof web.getHandle === 'function') {
|
|
131
|
+
this.#web = web;
|
|
132
|
+
web.getHandle().on('render', (params, ctx, options) => this._runRender(params, ctx, options), 100, 'hook_func');
|
|
133
|
+
return this;
|
|
134
|
+
}
|
|
135
|
+
// 新代管线(mm_event 七阶段链):注册 render 阶段事件驱动
|
|
136
|
+
if (typeof web.getEventManager === 'function') {
|
|
137
|
+
const manager = web.getEventManager();
|
|
138
|
+
if (!manager || typeof manager.add !== 'function') {
|
|
139
|
+
throw new TypeError(`${this._label()} web.getEventManager() must return an event manager with add()`);
|
|
140
|
+
}
|
|
141
|
+
this.#web = web;
|
|
142
|
+
/**
|
|
143
|
+
* 已注册的渲染阶段事件驱动
|
|
144
|
+
* @type {Object|null}
|
|
145
|
+
*/
|
|
146
|
+
this._render_drive = manager.add({
|
|
147
|
+
name: 'hook_func_render',
|
|
148
|
+
title: '统一渲染钩子',
|
|
149
|
+
description: '由 mm_hook_func 注册:渲染阶段对字符串结果执行配置的过滤函数(默认 html_filter)',
|
|
150
|
+
target: '*',
|
|
151
|
+
stage: 'render',
|
|
152
|
+
sort: 100,
|
|
153
|
+
state: 1,
|
|
154
|
+
dir: __dirname,
|
|
155
|
+
main: 'render_hook.js',
|
|
156
|
+
func_name: 'main'
|
|
157
|
+
});
|
|
158
|
+
return this;
|
|
159
|
+
}
|
|
160
|
+
throw new TypeError(`${this._label()} web must provide getEventManager() or getHandle()`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* 执行 render 钩子:对字符串结果调用配置的渲染函数
|
|
165
|
+
* @param {Object} ctx 上下文对象
|
|
166
|
+
* @param {Object} params 请求参数
|
|
167
|
+
* @returns {Promise<*>} 渲染后的结果;结果非字符串时返回 undefined
|
|
168
|
+
*/
|
|
169
|
+
async _runRender(ctx, params) {
|
|
170
|
+
const html = ctx.result;
|
|
171
|
+
if (typeof html !== 'string') return undefined;
|
|
172
|
+
return await this.runFunc(this.config.render_name, ctx, params, html);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
module.exports = {
|
|
177
|
+
HookFunc
|
|
178
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { getHookFunc } = require('../index.js');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 渲染阶段钩子(mm_event 七阶段链驱动脚本)
|
|
7
|
+
*
|
|
8
|
+
* 由 HookFunc.bind(web) 注册到 web 作用域事件管理器的 render 阶段:
|
|
9
|
+
* 对上下文结果中的字符串执行配置的渲染函数(默认 html_filter),
|
|
10
|
+
* 返回过滤后的内容,事件管理器会将其写回 ctx.result。
|
|
11
|
+
*
|
|
12
|
+
* @param {Object} params 请求参数
|
|
13
|
+
* @param {Object} ctx 请求上下文(ctx.result 为待渲染内容)
|
|
14
|
+
* @param {Object} options 事件选项
|
|
15
|
+
* @returns {Promise<string|undefined>} 过滤后的内容;结果非字符串时返回 undefined
|
|
16
|
+
*/
|
|
17
|
+
async function main(params, ctx, options) {
|
|
18
|
+
const hf = getHookFunc();
|
|
19
|
+
const html = ctx && ctx.result;
|
|
20
|
+
if (typeof html !== 'string') {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
return await hf.runFunc(hf.config.render_name, ctx, options, html);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = { main };
|
package/package.json
CHANGED
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
"name": "mm_hook_func",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "钩子函数模块,提供函数名值注册表并可作为插件向 web 管道注册 render 钩子。",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://gitee.com/qiuwenwu91/mm_hook_func.git"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"mm_mod",
|
|
12
|
+
"mm_infra",
|
|
13
|
+
"mm_common",
|
|
14
|
+
"hook",
|
|
15
|
+
"func",
|
|
16
|
+
"render"
|
|
17
|
+
],
|
|
18
|
+
"layer": "infra",
|
|
19
|
+
"group": "common",
|
|
20
|
+
"author": "邱文武",
|
|
21
|
+
"license": "ISC",
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"mm_corejs": "^1.1.4",
|
|
24
|
+
"mm_mod": "^1.0.1"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=12.0.0"
|
|
28
|
+
},
|
|
29
|
+
"directories": {
|
|
30
|
+
"lib": "lib"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"test": "node test.js && node --test tests/hook_func.test.js"
|
|
34
|
+
}
|
|
35
|
+
}
|
package/test.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const assert = require('assert');
|
|
3
|
+
const mod = require('./index.js');
|
|
4
|
+
|
|
5
|
+
// 验证导出结构
|
|
6
|
+
assert.strictEqual(typeof mod.HookFunc, 'function', '应导出 HookFunc 类');
|
|
7
|
+
assert.strictEqual(typeof mod.getHookFunc, 'function', '应导出 getHookFunc 函数');
|
|
8
|
+
|
|
9
|
+
// 验证单例:多次获取返回同一实例
|
|
10
|
+
const h1 = mod.getHookFunc();
|
|
11
|
+
const h2 = mod.getHookFunc();
|
|
12
|
+
assert.ok(h1 instanceof mod.HookFunc, 'getHookFunc 应返回 HookFunc 实例');
|
|
13
|
+
assert.strictEqual(h1, h2, 'getHookFunc 应返回单例');
|
|
14
|
+
|
|
15
|
+
// 验证函数注册表基本功能
|
|
16
|
+
const fn = () => 'ok';
|
|
17
|
+
h1.addFunc('test_func', fn);
|
|
18
|
+
assert.strictEqual(h1.hasFunc('test_func'), true, 'addFunc 后 hasFunc 应为 true');
|
|
19
|
+
assert.strictEqual(h1.delFunc('test_func'), true, 'delFunc 应删除成功');
|
|
20
|
+
assert.strictEqual(h1.hasFunc('test_func'), false, 'delFunc 后 hasFunc 应为 false');
|
|
21
|
+
|
|
22
|
+
console.log('mm_hook_func 模块测试通过');
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* mm_hook_func 单元测试
|
|
4
|
+
* @description 覆盖函数注册表、两代管线 bind(旧 Handle / 新 mm_event 七阶段链)与渲染驱动脚本
|
|
5
|
+
*/
|
|
6
|
+
const test = require('node:test');
|
|
7
|
+
const assert = require('assert');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
const { HookFunc, getHookFunc } = require('../index.js');
|
|
11
|
+
|
|
12
|
+
test('导出结构与单例', () => {
|
|
13
|
+
assert.strictEqual(typeof HookFunc, 'function');
|
|
14
|
+
assert.strictEqual(typeof getHookFunc, 'function');
|
|
15
|
+
const h1 = getHookFunc();
|
|
16
|
+
const h2 = getHookFunc();
|
|
17
|
+
assert.ok(h1 instanceof HookFunc);
|
|
18
|
+
assert.strictEqual(h1, h2, 'getHookFunc 应返回单例');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('函数注册表:增删查与运行', async () => {
|
|
22
|
+
const hf = new HookFunc();
|
|
23
|
+
assert.strictEqual(hf.hasFunc('x'), false);
|
|
24
|
+
hf.addFunc('x', (a) => `${a}!`);
|
|
25
|
+
assert.strictEqual(hf.hasFunc('x'), true);
|
|
26
|
+
assert.strictEqual(await hf.runFunc('x', 'hi'), 'hi!');
|
|
27
|
+
assert.strictEqual(hf.delFunc('x'), true);
|
|
28
|
+
assert.strictEqual(hf.delFunc('x'), false);
|
|
29
|
+
// 未注册时 runFunc 原样返回最后一个参数
|
|
30
|
+
assert.strictEqual(await hf.runFunc('none', 'a', 'b'), 'b');
|
|
31
|
+
assert.deepStrictEqual(hf.listFuncs(), []);
|
|
32
|
+
hf.addFunc('a', () => 1);
|
|
33
|
+
hf.addFunc('b', () => 2);
|
|
34
|
+
hf.clear();
|
|
35
|
+
assert.deepStrictEqual(hf.listFuncs(), []);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('注册表参数校验', () => {
|
|
39
|
+
const hf = new HookFunc();
|
|
40
|
+
assert.throws(() => hf.addFunc('', () => {}), TypeError);
|
|
41
|
+
assert.throws(() => hf.addFunc(null, () => {}), TypeError);
|
|
42
|
+
assert.throws(() => hf.addFunc('y', 'not-a-fn'), TypeError);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('bind 新代管线:向事件管理器注册 render 阶段驱动', () => {
|
|
46
|
+
const hf = new HookFunc();
|
|
47
|
+
const captured = [];
|
|
48
|
+
const web = {
|
|
49
|
+
getEventManager: () => ({
|
|
50
|
+
add: (config) => {
|
|
51
|
+
captured.push(config);
|
|
52
|
+
return { config };
|
|
53
|
+
},
|
|
54
|
+
}),
|
|
55
|
+
};
|
|
56
|
+
hf.bind(web);
|
|
57
|
+
assert.strictEqual(captured.length, 1, '应注册一个事件驱动');
|
|
58
|
+
const cfg = captured[0];
|
|
59
|
+
assert.strictEqual(cfg.name, 'hook_func_render');
|
|
60
|
+
assert.strictEqual(cfg.stage, 'render', '应注册到 render 阶段');
|
|
61
|
+
assert.strictEqual(cfg.target, '*');
|
|
62
|
+
assert.strictEqual(cfg.state, 1);
|
|
63
|
+
assert.strictEqual(cfg.main, 'render_hook.js', '驱动脚本应为 lib/render_hook.js');
|
|
64
|
+
assert.ok(cfg.dir && cfg.dir.endsWith('lib'), '驱动目录应指向模块 lib 目录');
|
|
65
|
+
// 重复绑定同一 web 不应无限累积(第二次 add 覆盖同名驱动由管理器负责)
|
|
66
|
+
hf.bind(web);
|
|
67
|
+
assert.strictEqual(captured.length, 2);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('bind 新代管线:渲染驱动脚本对 ctx.result 执行 html_filter', async () => {
|
|
71
|
+
// 驱动脚本内部使用 getHookFunc() 单例,此处也用单例注册函数
|
|
72
|
+
const hf = getHookFunc();
|
|
73
|
+
hf.clear();
|
|
74
|
+
let cfg = null;
|
|
75
|
+
const web = {
|
|
76
|
+
getEventManager: () => ({
|
|
77
|
+
add: (config) => {
|
|
78
|
+
cfg = config;
|
|
79
|
+
return { config };
|
|
80
|
+
},
|
|
81
|
+
}),
|
|
82
|
+
};
|
|
83
|
+
hf.bind(web);
|
|
84
|
+
// 模拟事件管理器加载驱动脚本并执行 main
|
|
85
|
+
const drive = require(path.join(cfg.dir, cfg.main));
|
|
86
|
+
assert.strictEqual(typeof drive.main, 'function', '驱动脚本应导出 main 函数');
|
|
87
|
+
|
|
88
|
+
// 场景1:注册了 html_filter 时对字符串结果生效
|
|
89
|
+
hf.addFunc('html_filter', (ctx, options, html) => html.replace(/bad/g, 'good'));
|
|
90
|
+
const ctx1 = { result: 'bad word' };
|
|
91
|
+
const ret1 = await drive.main({}, ctx1, {});
|
|
92
|
+
assert.strictEqual(ret1, 'good word');
|
|
93
|
+
assert.strictEqual(ctx1.result, 'bad word', '驱动仅返回结果,写回 ctx.result 由事件管理器负责');
|
|
94
|
+
|
|
95
|
+
// 场景2:结果非字符串时返回 undefined
|
|
96
|
+
const ctx2 = { result: { json: true } };
|
|
97
|
+
assert.strictEqual(await drive.main({}, ctx2, {}), undefined);
|
|
98
|
+
|
|
99
|
+
// 场景3:未注册 html_filter 时原样返回内容(runFunc 回退语义)
|
|
100
|
+
hf.delFunc('html_filter');
|
|
101
|
+
const ctx3 = { result: 'plain' };
|
|
102
|
+
assert.strictEqual(await drive.main({}, ctx3, {}), 'plain');
|
|
103
|
+
hf.clear();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('bind 旧代管线:getHandle 兼容路径仍可用', () => {
|
|
107
|
+
const hf = new HookFunc();
|
|
108
|
+
const registered = [];
|
|
109
|
+
const web = {
|
|
110
|
+
getHandle: () => ({
|
|
111
|
+
on: (stage, fn, priority, name) => registered.push({ stage, fn, priority, name }),
|
|
112
|
+
}),
|
|
113
|
+
};
|
|
114
|
+
hf.bind(web);
|
|
115
|
+
assert.strictEqual(registered.length, 1);
|
|
116
|
+
assert.strictEqual(registered[0].stage, 'render');
|
|
117
|
+
assert.strictEqual(registered[0].name, 'hook_func');
|
|
118
|
+
assert.strictEqual(typeof registered[0].fn, 'function');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('bind 非法 web 抛出 TypeError', () => {
|
|
122
|
+
const hf = new HookFunc();
|
|
123
|
+
assert.throws(() => hf.bind(null), TypeError);
|
|
124
|
+
assert.throws(() => hf.bind({}), TypeError, '既无 getEventManager 也无 getHandle 时应抛错');
|
|
125
|
+
assert.throws(
|
|
126
|
+
() => hf.bind({ getEventManager: () => ({}) }),
|
|
127
|
+
TypeError,
|
|
128
|
+
'事件管理器缺少 add 方法时应抛错'
|
|
129
|
+
);
|
|
130
|
+
});
|
package/README.md
DELETED