mm_statics 1.9.4 → 1.9.6
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/lib/config.tpl.json +31 -0
- package/lib/manager.js +12 -1
- package/lib/script.tpl.js +31 -0
- package/lib/static.js +2 -0
- package/lib/static_handle.js +156 -3
- package/package.json +7 -6
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
// 路由路径,注意:这里是泛路径,相当于/*
|
|
3
|
+
// 注册域, 即本资源注册到 require('mm_xxx').admin 的哪个域
|
|
4
|
+
"scope": "${scope || 'os'}",
|
|
5
|
+
// 所属应用
|
|
6
|
+
"app": "${app || 'os'}",
|
|
7
|
+
// 所属插件, 应用级资源为空
|
|
8
|
+
"plugin": "${plugin || ''}",
|
|
9
|
+
"path": "${path || '/${name}'}",
|
|
10
|
+
|
|
11
|
+
// 名称, 由中英文和下“_”组成, 用于修改或卸载 例如: demo
|
|
12
|
+
"name": "${name || 'static'}",
|
|
13
|
+
// 标题, 用于查询时
|
|
14
|
+
"title": "${title || 'static资源'}",
|
|
15
|
+
// 状态 0未启用,1启用
|
|
16
|
+
"state": 1,
|
|
17
|
+
// 资源缓存寿命,单位:秒
|
|
18
|
+
"max_age": 0,
|
|
19
|
+
// 是否恒久不变,一般资源文件可能变化,所以这里一般是false
|
|
20
|
+
"immutable": false,
|
|
21
|
+
// 是否使用brotli压缩方式
|
|
22
|
+
"brotli": true,
|
|
23
|
+
// 是否启用gzip压缩方式
|
|
24
|
+
"gzip": true,
|
|
25
|
+
// 是否限制访问指定拓展名的文件,多个
|
|
26
|
+
"extensions": null,
|
|
27
|
+
// 是否转换JS、vue代码为amd标准,true的情况下会把es6和commonJS都转成amd格式
|
|
28
|
+
"convert_amd": false,
|
|
29
|
+
// 调用脚本文件,如果请求改路由情况会调用改脚本文件 ./static.js
|
|
30
|
+
"main": ""
|
|
31
|
+
}
|
package/lib/manager.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const Manager = require("mm_machine").Manager;
|
|
2
2
|
const Static = require("./static");
|
|
3
|
+
const StaticHandle = require("./static_handle");
|
|
3
4
|
const { URLSearchParams } = require('url');
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -37,7 +38,13 @@ class StaticManager extends Manager {
|
|
|
37
38
|
* 资源清单文件名(update 扫描目录时使用)
|
|
38
39
|
* @type {string}
|
|
39
40
|
*/
|
|
40
|
-
filename: "static.json"
|
|
41
|
+
filename: "static.json",
|
|
42
|
+
/**
|
|
43
|
+
* 忽略的请求路径(管理器级,命中后不再遍历任何驱动),
|
|
44
|
+
* 支持 * 通配,如 ['/api*', '/apis*'];驱动级 ignore 可再各自补充
|
|
45
|
+
* @type {Array<string|RegExp>}
|
|
46
|
+
*/
|
|
47
|
+
ignore: []
|
|
41
48
|
};
|
|
42
49
|
|
|
43
50
|
/**
|
|
@@ -69,6 +76,10 @@ class StaticManager extends Manager {
|
|
|
69
76
|
*/
|
|
70
77
|
async run(params, ctx, options) {
|
|
71
78
|
var path = this._requestPath(params, ctx);
|
|
79
|
+
// 管理器级忽略:命中即视为未命中,跳过全部驱动的匹配(接口类请求不再空跑一圈)
|
|
80
|
+
if (StaticHandle.isIgnored(path, this.config.ignore)) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
72
83
|
var infos = this._getInfos();
|
|
73
84
|
var len = infos.length;
|
|
74
85
|
if (path.indexOf(".") !== -1) {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
|
|
2
|
+
module.exports = {
|
|
3
|
+
/**
|
|
4
|
+
* 执行前
|
|
5
|
+
* @param {object} params 请求参数
|
|
6
|
+
* @param {object} ctx 请求上下文
|
|
7
|
+
* @param {object} options 运行选项
|
|
8
|
+
*/
|
|
9
|
+
async before(params, ctx, options) { },
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 执行(服务式调用,不接收 next)
|
|
13
|
+
* @param {object} params 请求参数
|
|
14
|
+
* @param {object} ctx 请求上下文
|
|
15
|
+
* @param {object} options 运行选项
|
|
16
|
+
* @returns {Promise<string>} 命中返回物理文件路径,未命中返回空串
|
|
17
|
+
*/
|
|
18
|
+
async main(params, ctx, options) {
|
|
19
|
+
return await this.run(params, ctx, options);
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 执行后
|
|
24
|
+
* @param {object} params 请求参数
|
|
25
|
+
* @param {object} ctx 请求上下文
|
|
26
|
+
* @param {object} options 运行选项
|
|
27
|
+
*/
|
|
28
|
+
async after(params, ctx, options) {
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
};
|
package/lib/static.js
CHANGED
package/lib/static_handle.js
CHANGED
|
@@ -10,6 +10,79 @@ const { marked } = require('marked');
|
|
|
10
10
|
|
|
11
11
|
var vueCompiler = new VueSFCCompiler();
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* 通配规则编译缓存(规则串 -> 正则),避免热路径上重复构造正则
|
|
15
|
+
* @type {Map<string, RegExp>}
|
|
16
|
+
*/
|
|
17
|
+
var rule_cache = new Map();
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 将忽略规则编译为正则(支持 * 通配,等价于任意字符)
|
|
21
|
+
* @param {string} rule 规则串,如 /api*、*.map、/apis*
|
|
22
|
+
* @returns {RegExp} 匹配整个路径的正则
|
|
23
|
+
*/
|
|
24
|
+
function _compileRule(rule) {
|
|
25
|
+
var re = rule_cache.get(rule);
|
|
26
|
+
if (re) {
|
|
27
|
+
return re;
|
|
28
|
+
}
|
|
29
|
+
// 先转义正则元字符,再把 * 还原为通配
|
|
30
|
+
var escaped = rule.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*');
|
|
31
|
+
re = new RegExp('^' + escaped + '$');
|
|
32
|
+
rule_cache.set(rule, re);
|
|
33
|
+
return re;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 判断请求路径是否命中忽略规则
|
|
38
|
+
*
|
|
39
|
+
* 规则形态(可传单个规则、逗号分隔字符串或数组,数组元素可为字符串或正则):
|
|
40
|
+
* - 前缀通配:/api* ,忽略 /api、/api/user 等
|
|
41
|
+
* - 后缀通配:*.map ,忽略所有 .map 文件
|
|
42
|
+
* - 包含通配:*manage* ,忽略路径中含 manage 的请求
|
|
43
|
+
* - 无通配:/api ,按「路径段前缀」匹配,忽略 /api 与 /api/xxx,但不影响 /apixyz
|
|
44
|
+
* @param {string} target 请求路径
|
|
45
|
+
* @param {string|Array<string|RegExp>} rules 忽略规则
|
|
46
|
+
* @returns {boolean} 命中返回 true
|
|
47
|
+
*/
|
|
48
|
+
function isIgnored(target, rules) {
|
|
49
|
+
if (!rules || !target) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
var list = Array.isArray(rules) ? rules : String(rules).split(',');
|
|
53
|
+
for (var i = 0; i < list.length; i++) {
|
|
54
|
+
var raw = list[i];
|
|
55
|
+
if (!raw) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (raw instanceof RegExp) {
|
|
59
|
+
if (raw.test(target)) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
var rule = String(raw).trim();
|
|
65
|
+
if (!rule) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (rule.indexOf('*') === -1) {
|
|
69
|
+
// 无通配:精确或路径段前缀
|
|
70
|
+
if (target === rule) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
var prefix = rule.endsWith('/') ? rule : rule + '/';
|
|
74
|
+
if (target.startsWith(prefix)) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (_compileRule(rule).test(target)) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
13
86
|
/**
|
|
14
87
|
* 静态文件处理类
|
|
15
88
|
*/
|
|
@@ -56,7 +129,9 @@ class StaticHandle {
|
|
|
56
129
|
// 是否开启文件监听
|
|
57
130
|
watch: false,
|
|
58
131
|
// 监听的文件扩展名
|
|
59
|
-
watch_files: ['.js', '.css', '.html', '.vue', '.json', '.md']
|
|
132
|
+
watch_files: ['.js', '.css', '.html', '.vue', '.json', '.md'],
|
|
133
|
+
// 忽略的请求路径(不参与静态匹配),支持 * 通配,如 ['/api*', '/apis*', '*.map']
|
|
134
|
+
ignore: []
|
|
60
135
|
};
|
|
61
136
|
|
|
62
137
|
/**
|
|
@@ -633,9 +708,72 @@ StaticHandle.prototype._getHeaders = function (file_type) {
|
|
|
633
708
|
case 'swf':
|
|
634
709
|
headers['content-type'] = 'application/x-shockwave-flash';
|
|
635
710
|
break;
|
|
636
|
-
|
|
711
|
+
case 'jpg':
|
|
712
|
+
case 'jpeg':
|
|
713
|
+
headers['content-type'] = 'image/jpeg';
|
|
714
|
+
break;
|
|
715
|
+
case 'png':
|
|
716
|
+
headers['content-type'] = 'image/png';
|
|
717
|
+
break;
|
|
718
|
+
case 'gif':
|
|
719
|
+
headers['content-type'] = 'image/gif';
|
|
720
|
+
break;
|
|
721
|
+
case 'webp':
|
|
722
|
+
headers['content-type'] = 'image/webp';
|
|
723
|
+
break;
|
|
724
|
+
case 'avif':
|
|
725
|
+
headers['content-type'] = 'image/avif';
|
|
726
|
+
break;
|
|
727
|
+
case 'bmp':
|
|
728
|
+
headers['content-type'] = 'image/bmp';
|
|
729
|
+
break;
|
|
730
|
+
case 'svg':
|
|
731
|
+
headers['content-type'] = 'image/svg+xml';
|
|
732
|
+
break;
|
|
733
|
+
case 'ico':
|
|
734
|
+
headers['content-type'] = 'image/x-icon';
|
|
735
|
+
break;
|
|
736
|
+
case 'mp4':
|
|
737
|
+
headers['content-type'] = 'video/mp4';
|
|
738
|
+
break;
|
|
739
|
+
case 'webm':
|
|
740
|
+
headers['content-type'] = 'video/webm';
|
|
741
|
+
break;
|
|
742
|
+
case 'mov':
|
|
743
|
+
headers['content-type'] = 'video/quicktime';
|
|
744
|
+
break;
|
|
745
|
+
case 'woff':
|
|
746
|
+
headers['content-type'] = 'font/woff';
|
|
747
|
+
break;
|
|
748
|
+
case 'woff2':
|
|
749
|
+
headers['content-type'] = 'font/woff2';
|
|
750
|
+
break;
|
|
751
|
+
case 'ttf':
|
|
752
|
+
headers['content-type'] = 'font/ttf';
|
|
753
|
+
break;
|
|
754
|
+
case 'otf':
|
|
755
|
+
headers['content-type'] = 'font/otf';
|
|
756
|
+
break;
|
|
757
|
+
case 'eot':
|
|
758
|
+
headers['content-type'] = 'application/vnd.ms-fontobject';
|
|
759
|
+
break;
|
|
760
|
+
case 'pdf':
|
|
761
|
+
headers['content-type'] = 'application/pdf';
|
|
762
|
+
break;
|
|
763
|
+
case 'json':
|
|
764
|
+
headers['content-type'] = 'application/json; charset=utf-8';
|
|
765
|
+
break;
|
|
766
|
+
case 'txt':
|
|
637
767
|
headers['content-type'] = 'text/plain; charset=utf-8';
|
|
638
768
|
break;
|
|
769
|
+
case 'xml':
|
|
770
|
+
headers['content-type'] = 'application/xml; charset=utf-8';
|
|
771
|
+
break;
|
|
772
|
+
default:
|
|
773
|
+
// 二进制兜底改为 application/octet-stream:text/plain 会诱导浏览器按文本
|
|
774
|
+
// 处理未知类型(开启 nosniff 的部署下图片/字体直接拒载)
|
|
775
|
+
headers['content-type'] = 'application/octet-stream';
|
|
776
|
+
break;
|
|
639
777
|
}
|
|
640
778
|
return headers;
|
|
641
779
|
};
|
|
@@ -829,6 +967,10 @@ StaticHandle.prototype.main = async function (params, ctx, options) {
|
|
|
829
967
|
if (!this._isReadable(this._requestMethod(params, ctx))) {
|
|
830
968
|
return '';
|
|
831
969
|
}
|
|
970
|
+
// 命中忽略规则(如 /api*)直接放行,由事件链处理,不做静态匹配
|
|
971
|
+
if (this._isIgnored(p)) {
|
|
972
|
+
return '';
|
|
973
|
+
}
|
|
832
974
|
// 无扩展名的路径交给目录分支与后续事件处理
|
|
833
975
|
if (p.indexOf('.') === -1) {
|
|
834
976
|
return '';
|
|
@@ -857,6 +999,15 @@ StaticHandle.prototype.main = async function (params, ctx, options) {
|
|
|
857
999
|
}
|
|
858
1000
|
};
|
|
859
1001
|
|
|
1002
|
+
/**
|
|
1003
|
+
* 判断请求路径是否命中本驱动的忽略规则
|
|
1004
|
+
* @param {string} path 请求路径
|
|
1005
|
+
* @returns {boolean} 命中返回 true
|
|
1006
|
+
*/
|
|
1007
|
+
StaticHandle.prototype._isIgnored = function (path) {
|
|
1008
|
+
return isIgnored(path, this.config.ignore);
|
|
1009
|
+
};
|
|
1010
|
+
|
|
860
1011
|
/**
|
|
861
1012
|
* 关闭文件监听器
|
|
862
1013
|
*/
|
|
@@ -867,4 +1018,6 @@ StaticHandle.prototype.closeWatcher = function () {
|
|
|
867
1018
|
}
|
|
868
1019
|
};
|
|
869
1020
|
|
|
870
|
-
module.exports = StaticHandle;
|
|
1021
|
+
module.exports = StaticHandle;
|
|
1022
|
+
// 忽略规则匹配函数,供静态管理器(manager.js)复用
|
|
1023
|
+
module.exports.isIgnored = isIgnored;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mm_statics",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.6",
|
|
4
4
|
"description": "这是超级美眉statics函数模块,用于web服务端statics缓存",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -36,12 +36,14 @@
|
|
|
36
36
|
},
|
|
37
37
|
"homepage": "https://gitee.com/qiuwenwu91/mm_statics#readme",
|
|
38
38
|
"files": [
|
|
39
|
+
"eslint.config.js",
|
|
39
40
|
"index.js",
|
|
40
|
-
"lib/
|
|
41
|
+
"lib/config.tpl.json",
|
|
42
|
+
"lib/manager.js",
|
|
43
|
+
"lib/script.tpl.js",
|
|
41
44
|
"lib/static.js",
|
|
42
45
|
"lib/static_handle.js",
|
|
43
|
-
"lib/
|
|
44
|
-
"eslint.config.js",
|
|
46
|
+
"lib/vue_compiler.js",
|
|
45
47
|
"static/"
|
|
46
48
|
],
|
|
47
49
|
"dependencies": {
|
|
@@ -53,7 +55,7 @@
|
|
|
53
55
|
"mm_corejs": "^1.1.5",
|
|
54
56
|
"mm_es6_to_amd": "^1.5.9",
|
|
55
57
|
"mm_helper": "^1.0.0",
|
|
56
|
-
"mm_machine": "^3.
|
|
58
|
+
"mm_machine": "^3.6.0",
|
|
57
59
|
"prettier": "^3.9.10"
|
|
58
60
|
},
|
|
59
61
|
"devDependencies": {
|
|
@@ -61,7 +63,6 @@
|
|
|
61
63
|
"eslint-plugin-jsdoc": "^65.3.4",
|
|
62
64
|
"koa": "^3.2.1",
|
|
63
65
|
"mm_eslint": "^1.7.7",
|
|
64
|
-
"mm_expand": "^2.5.4",
|
|
65
66
|
"mocha": "^12.0.3",
|
|
66
67
|
"supertest": "^7.3.1"
|
|
67
68
|
}
|