jj.js 0.8.8 → 0.10.0

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.
@@ -1,65 +1,65 @@
1
- module.exports = `<!doctype html>
2
- <html>
3
- <head>
4
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5
- <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"/>
6
- <title>jj.js</title>
7
- <style type="text/css">
8
- * { padding: 0; margin: 0; }
9
- body { background: #fff; font-family: "Microsoft Yahei","Helvetica Neue",Helvetica,Arial,sans-serif; color: #333; font-size: 16px; }
10
- .message { padding: 24px 48px; }
11
- .message h1 { font-size: 36px; font-weight: normal; line-height: 50px; margin-bottom: 12px; }
12
- .message h3 { line-height: 1.8em; font-size: 28px; font-weight: normal; }
13
- .message .code {
14
- line-height: 1.5em;
15
- font-size: 12px;
16
- white-space: pre-wrap;
17
- background: #303030;
18
- color: #f1f1f1;
19
- padding: 10px .2em 10px 3.2em;
20
- border-radius: 2px;
21
- -moz-box-shadow: inset 0 0 10px #000;
22
- box-shadow: inset 0 0 10px #000;
23
- counter-reset: line {$begin};
24
- }
25
- .message .code .line {
26
- display: block;
27
- padding-left: 1em;
28
- padding-right: 1em;
29
- min-height: 1.5em;
30
- position: relative;
31
- }
32
- .message .code .line:before {
33
- counter-increment: line;
34
- content: counter(line);
35
- display: inline-block;
36
- box-sizing: border-box;
37
- border-right: 1px solid #ddd;
38
- width: 3.2em;
39
- padding-right: .5em;
40
- color: #888;
41
- text-align: right;
42
- position: absolute;
43
- left: -3em;
44
- }
45
- .message .code .line:nth-child({$nth}) {
46
- background-color: #efed0b;
47
- color: #303030;
48
- }
49
- .message .code .line:nth-child({$nth}):before {
50
- background-color: #efed0b;
51
- color: #333;
52
- }
53
- .message .stack{ line-height: 1.8em; font-size: 14px; }
54
- </style>
55
- </head>
56
- <body>
57
- <div class="message">
58
- <h1 class="h0">jj.js</h1>
59
- <h3>{$msg}</h3>
60
- <pre class="code">{$code}</pre>
61
- <h3>Call Stack</h3>
62
- <p class="stack">{$stack}</p>
63
- </div>
64
- </body>
1
+ module.exports = `<!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5
+ <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"/>
6
+ <title>jj.js</title>
7
+ <style type="text/css">
8
+ * { padding: 0; margin: 0; }
9
+ body { background: #fff; font-family: "Microsoft Yahei","Helvetica Neue",Helvetica,Arial,sans-serif; color: #333; font-size: 16px; }
10
+ .message { padding: 24px 48px; }
11
+ .message h1 { font-size: 36px; font-weight: normal; line-height: 50px; margin-bottom: 12px; }
12
+ .message h3 { line-height: 1.8em; font-size: 28px; font-weight: normal; }
13
+ .message .code {
14
+ line-height: 1.5em;
15
+ font-size: 12px;
16
+ white-space: pre-wrap;
17
+ background: #303030;
18
+ color: #f1f1f1;
19
+ padding: 10px .2em 10px 3.2em;
20
+ border-radius: 2px;
21
+ -moz-box-shadow: inset 0 0 10px #000;
22
+ box-shadow: inset 0 0 10px #000;
23
+ counter-reset: line {$begin};
24
+ }
25
+ .message .code .line {
26
+ display: block;
27
+ padding-left: 1em;
28
+ padding-right: 1em;
29
+ min-height: 1.5em;
30
+ position: relative;
31
+ }
32
+ .message .code .line:before {
33
+ counter-increment: line;
34
+ content: counter(line);
35
+ display: inline-block;
36
+ box-sizing: border-box;
37
+ border-right: 1px solid #ddd;
38
+ width: 3.2em;
39
+ padding-right: .5em;
40
+ color: #888;
41
+ text-align: right;
42
+ position: absolute;
43
+ left: -3em;
44
+ }
45
+ .message .code .line:nth-child({$nth}) {
46
+ background-color: #efed0b;
47
+ color: #303030;
48
+ }
49
+ .message .code .line:nth-child({$nth}):before {
50
+ background-color: #efed0b;
51
+ color: #333;
52
+ }
53
+ .message .stack{ line-height: 1.8em; font-size: 14px; }
54
+ </style>
55
+ </head>
56
+ <body>
57
+ <div class="message">
58
+ <h1 class="h0">jj.js</h1>
59
+ <h3>{$msg}</h3>
60
+ <pre class="code">{$code}</pre>
61
+ <h3>Call Stack</h3>
62
+ <p class="stack">{$stack}</p>
63
+ </div>
64
+ </body>
65
65
  </html>`;
package/lib/tpl/jump.js CHANGED
@@ -1,41 +1,41 @@
1
- module.exports = `<!doctype html>
2
- <html>
3
- <head>
4
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5
- <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"/>
6
- <title>跳转提示</title>
7
- <style type="text/css">
8
- *{ padding: 0; margin: 0; }
9
- body{ background: #fff; font-family: "Microsoft Yahei","Helvetica Neue",Helvetica,Arial,sans-serif; color: #333; font-size: 16px; }
10
- .message{ padding: 24px 48px; }
11
- .message h1{ font-size: 42px; font-weight: normal; line-height: 120px; margin-bottom: 12px; display: none; }
12
- .message .h{$state}{ display: initial; }
13
- .message .text{ line-height: 1.8em; font-size: 36px; }
14
- .message .jump{ padding-top: 10px; }
15
- .message .jump a{ color: #333; }
16
- </style>
17
- </head>
18
- <body>
19
- <div class="message">
20
- <h1 class="h1">◠‿◠</h1>
21
- <h1 class="h0">&gt;﹏&lt;</h1>
22
- <p class="text">{$msg}</p>
23
- <p class="jump">
24
- 页面自动 <a id="href" href="{$url}">跳转</a> 等待时间:<b id="wait">{$wait}</b>
25
- </p>
26
- </div>
27
- <script>
28
- (function(){
29
- var wait = document.getElementById('wait'),
30
- href = document.getElementById('href').href;
31
- var interval = setInterval(function(){
32
- var time = --wait.innerHTML;
33
- if(time <= 0) {
34
- location.href = href;
35
- clearInterval(interval);
36
- };
37
- }, 1000);
38
- })();
39
- </script>
40
- </body>
1
+ module.exports = `<!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5
+ <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"/>
6
+ <title>跳转提示</title>
7
+ <style type="text/css">
8
+ *{ padding: 0; margin: 0; }
9
+ body{ background: #fff; font-family: "Microsoft Yahei","Helvetica Neue",Helvetica,Arial,sans-serif; color: #333; font-size: 16px; }
10
+ .message{ padding: 24px 48px; }
11
+ .message h1{ font-size: 42px; font-weight: normal; line-height: 120px; margin-bottom: 12px; display: none; }
12
+ .message .h{$state}{ display: initial; }
13
+ .message .text{ line-height: 1.8em; font-size: 36px; }
14
+ .message .jump{ padding-top: 10px; }
15
+ .message .jump a{ color: #333; }
16
+ </style>
17
+ </head>
18
+ <body>
19
+ <div class="message">
20
+ <h1 class="h1">◠‿◠</h1>
21
+ <h1 class="h0">&gt;﹏&lt;</h1>
22
+ <p class="text">{$msg}</p>
23
+ <p class="jump">
24
+ 页面自动 <a id="href" href="{$url}">跳转</a> 等待时间:<b id="wait">{$wait}</b>
25
+ </p>
26
+ </div>
27
+ <script>
28
+ (function(){
29
+ var wait = document.getElementById('wait'),
30
+ href = document.getElementById('href').href;
31
+ var interval = setInterval(function(){
32
+ var time = --wait.innerHTML;
33
+ if(time <= 0) {
34
+ location.href = href;
35
+ clearInterval(interval);
36
+ };
37
+ }, 1000);
38
+ })();
39
+ </script>
40
+ </body>
41
41
  </html>`;
@@ -0,0 +1,22 @@
1
+ module.exports = `/**
2
+ * @type {typeof import('jj.js/types')}
3
+ */
4
+ const JJCtx = require('jj.js').Ctx;
5
+
6
+ /**
7
+ * @typedef {import('jj.js/types').Config} JJConfig
8
+ */
9
+
10
+ __TYPES__
11
+
12
+ /**
13
+ * @class Ctx
14
+ */
15
+ class Ctx extends JJCtx {
16
+ __PROPERTY__
17
+ }
18
+
19
+ /**
20
+ * @module types
21
+ */
22
+ module.exports = Ctx;`;
package/lib/types.js ADDED
@@ -0,0 +1,236 @@
1
+ const path = require('path');
2
+ const fsPromises = require('fs').promises;
3
+ const {app: cfg_app} = require('./config');
4
+ const toHump = require('./utils/str').toHump;
5
+ const Logger = require('./logger');
6
+
7
+ /**
8
+ * watch
9
+ */
10
+ function start() {
11
+ const watch = require('watch');
12
+ const filter = function(f, stat) {
13
+ const static_dir = cfg_app.static_dir ? path.join(cfg_app.base_dir, cfg_app.static_dir) : '';
14
+ return (!~f.indexOf('.') || /\.js(on)?$/.test(f)) && !~f.indexOf('types.js') && (static_dir && !~f.indexOf(static_dir));
15
+ };
16
+
17
+ let prevFile = {file: null,action: null,stat: null};
18
+ watch.watchTree(cfg_app.base_dir, {ignoreDotFiles: true, ignoreDirectoryPattern: /node_modules/, filter}, (f, curr, prev) => {
19
+ if(typeof f == "object" && prev === null && curr === null) {
20
+ // Finished walking the tree
21
+ Object.keys(f).forEach(file => {
22
+ Logger.system("wacth:", file);
23
+ });
24
+ } else if(prev === null) {
25
+ // f is a new file
26
+ if(prevFile.file != f || prevFile.action != "created") {
27
+ prevFile = {file: f, action: "created", stat: curr};
28
+ createFile(f);
29
+ }
30
+ } else if(curr.nlink === 0) {
31
+ // f was removed
32
+ if(prevFile.file != f || prevFile.action != "removed") {
33
+ prevFile = { file: f, action: "removed", stat: curr };
34
+ createFile(f);
35
+ }
36
+ } else {
37
+ // f was changed
38
+ if(prevFile.file === null) {
39
+ createFile(f);
40
+ } else {
41
+ // stat might return null, so catch errors
42
+ try {
43
+ if (prevFile.stat.mtime.getTime() !== curr.mtime.getTime()) {
44
+ createFile(f);
45
+ }
46
+ } catch(e) {
47
+ createFile(f);
48
+ }
49
+ }
50
+ }
51
+ });
52
+ }
53
+
54
+ /**
55
+ * createTypesFile
56
+ */
57
+ async function createFile(f) {
58
+ if(this._f == f) {
59
+ return;
60
+ }
61
+ if(this._f) {
62
+ delete require.cache[require.resolve(this._f)];
63
+ }
64
+ this._f = f;
65
+
66
+ const f_info = f.replace(cfg_app.base_dir + path.sep, '').split(path.sep);
67
+ if(f_info[0] && f_info[0] != cfg_app.common_app && !~f_info[0].indexOf('.')) {
68
+ this._APP = f_info[0];
69
+ }
70
+
71
+ const ignore = ['node_modules', 'docker', 'package-lock.json', 'types.js', '.git', '.gitignore'];
72
+ ignore.push(path.basename(module.parent.parent.parent.filename));
73
+ cfg_app.static_dir && ignore.push(path.join(cfg_app.base_dir, cfg_app.static_dir));
74
+
75
+ const node_list = await getNodeList('.', ignore);
76
+ calcNodeList(node_list, this._APP);
77
+
78
+ const types_tpl = require('./tpl/types');
79
+ const types_str = types_tpl.replace('__TYPES__', createTypes(node_list)).replace('__PROPERTY__', createProps(node_list));
80
+ fsPromises.writeFile('types.js', types_str).then(_ => {
81
+ Logger.system('createTypes success:', path.join(cfg_app.base_dir, 'types.js'));
82
+ }).catch(err => {
83
+ Logger.system('createTypes error:', err);
84
+ });
85
+ }
86
+
87
+ // 生成类型定义
88
+ function createTypes(node_list, base_type = '') {
89
+ let types = '';
90
+ let props = '';
91
+ Object.entries(node_list).forEach(([path, node]) => {
92
+ const type_name = toHump(base_type + '_' + node.node_name);
93
+ if(node.file_type == 'dir') {
94
+ types += createTypes(node.children, type_name);
95
+ } else {
96
+ if(!node.is_class) {
97
+ types += `/**\n * @typedef {typeof import('${path}')} ${type_name}\n */\n\n`;
98
+ } else {
99
+ types += `/**\n * @typedef {typeof import('${path}')} ${type_name}Class\n`;
100
+ types += ` * @typedef {typeof import('${path}').prototype} ${type_name}Instance\n`;
101
+ types += ` * @typedef {(${type_name}Class & ${type_name}Instance)} ${type_name}\n */\n\n`;
102
+ }
103
+ }
104
+
105
+ props += ` * @property {${type_name}} ${node.node_name} - ${path}\n`;
106
+ });
107
+
108
+ if(base_type) {
109
+ types += `/**\n * @typedef {object} ${base_type}\n${props} */\n\n`;
110
+ }
111
+
112
+ return types;
113
+ }
114
+
115
+ // 生成智能属性
116
+ function createProps(node_list) {
117
+ let props = '';
118
+
119
+ Object.entries(node_list).forEach(([path, node]) => {
120
+ const type_name = toHump('_' + node.node_name);
121
+ if(type_name != 'Config') {
122
+ props += `\n /** @type {${type_name}} */\n $${node.node_name}\n`;
123
+ }
124
+ });
125
+
126
+ let type_config = 'JJConfig';
127
+ if(node_list['./config']) {
128
+ type_config = '(JJConfig & Config)';
129
+ }
130
+ props += `\n /** @type {${type_config}} */\n $config\n`;
131
+
132
+ return props;
133
+ }
134
+
135
+ // 获取节点对象
136
+ async function getNodeList(dir, ignore=['node_modules', 'docker', 'package-lock.json', '.git', '.gitignore']) {
137
+ const isClass = require('is-class');
138
+ const files = await fsPromises.readdir(path.join(cfg_app.base_dir, dir), {withFileTypes: true});
139
+ const type_list = {};
140
+ for(const dirent of files) {
141
+ const file_name = dirent.name;
142
+ const file_path = dir + '/' + file_name;
143
+ const node_name = path.parse(file_name).name;
144
+ const abs_path = path.join(cfg_app.base_dir, file_path);
145
+ if(ignore.filter(n => abs_path.includes(n)).length) {
146
+ continue;
147
+ }
148
+
149
+ const file_type = dirent.isFile() ? 'file' : dirent.isDirectory() ? 'dir' : '';
150
+ const regFile = /.+\.js(on)?$/.test(file_name);
151
+ const regDir = !file_name.includes('.');
152
+ if(file_type == 'file' && !regFile) {
153
+ continue;
154
+ }
155
+ if(file_type == 'dir' && !regDir) {
156
+ continue;
157
+ }
158
+
159
+ if(file_type == 'file') {
160
+ try {
161
+ // @ts-ignore
162
+ type_list[file_path] = {node_name, file_name, file_type, is_class: isClass(require(abs_path))};
163
+ } catch(e) {
164
+ // 包含语法错误时,require会出错
165
+ }
166
+ } else if(file_type == 'dir') {
167
+ type_list[file_path] = {node_name, file_name, file_type, children: await getNodeList(file_path, ignore)};;
168
+ }
169
+
170
+ };
171
+ return type_list;
172
+ }
173
+
174
+ // 复制子节点
175
+ function copyNodeList(node_list) {
176
+ const copy_node_list = {};
177
+ Object.entries(node_list).forEach(([path, node]) => {
178
+ copy_node_list[path] = {...node};
179
+ if(node.children) {
180
+ copy_node_list[path].children = copyNodeList(node.children);
181
+ }
182
+ });
183
+ return copy_node_list;
184
+ }
185
+
186
+ // 处理common库
187
+ function calcNodeList(node_list, _APP) {
188
+ // 处理common
189
+ const node_common = './' + cfg_app.common_app;
190
+ if(cfg_app.common_app && node_list[node_common] && node_list[node_common].children) {
191
+ const children = copyNodeList(node_list[node_common].children);
192
+ Object.entries(children).forEach(([path, node]) => {
193
+ node_list[path] = node;
194
+ });
195
+ }
196
+
197
+ // 处理app
198
+ const node_app = './' + _APP;
199
+ if(_APP && node_list[node_app] && node_list[node_app].children) {
200
+ Object.entries(node_list[node_app].children).forEach(([path, node]) => {
201
+ const path_common = path.replace(node_app, node_common);
202
+ if(!node_list[path_common]) {
203
+ node_list[path] = node;
204
+ } else if(node_list[path_common].file_type == 'file') {
205
+ delete node_list[path_common];
206
+ node_list[path] = node;
207
+ } else if(node.children) {
208
+ Object.entries(node.children).forEach(([p, n]) => {
209
+ const p_commmon = p.replace(node_app, node_common);
210
+ node_list[path_common].children[p_commmon] && delete node_list[path_common].children[p_commmon];
211
+ node_list[path_common].children[p] = n;
212
+ });
213
+ }
214
+ });
215
+ }
216
+
217
+ // 特殊处理view和response
218
+ const node_view = './' + _APP + '/view';
219
+ const common_view = './' + cfg_app.common_app + '/view';
220
+ if(node_list[node_view] && !node_list[node_view].is_class) {
221
+ delete node_list[node_view];
222
+ }
223
+ if(node_list[common_view] && !node_list[common_view].is_class) {
224
+ delete node_list[common_view];
225
+ }
226
+ const node_response = './' + _APP + '/response';
227
+ const common_response = './' + cfg_app.common_app + '/response';
228
+ if(node_list[node_response] && !node_list[node_response].is_class) {
229
+ delete node_list[node_response];
230
+ }
231
+ if(node_list[common_response] && !node_list[common_response].is_class) {
232
+ delete node_list[common_response];
233
+ }
234
+ }
235
+
236
+ module.exports = start;
package/lib/upload.js CHANGED
@@ -2,41 +2,85 @@ const pt = require('path');
2
2
  const fs = require('fs');
3
3
  const utils = require('./utils/utils');
4
4
  const Context = require('./context');
5
-
5
+ const {app: cfg_app} = require('./config');
6
+
7
+ /**
8
+ * @typedef {import('formidable').Files} Files
9
+ * @typedef {import('formidable').File} File
10
+ * @typedef {import('../types').UploadData} UploadData
11
+ * @typedef {import('../types').ValidateRule} ValidateRule
12
+ */
13
+
14
+ /**
15
+ * @extends Context
16
+ */
6
17
  class Upload extends Context
7
18
  {
19
+ /**
20
+ * 设置上传文件
21
+ * @public
22
+ * @param {(string|File)} file
23
+ * @returns {this}
24
+ */
8
25
  file(file) {
9
- this._file = typeof file == 'string' ? this.getFile(file) : file;
26
+ /**
27
+ * @private
28
+ */
29
+ this._file = /** @type {File} */ (typeof file == 'string' ? this.getFile(file) : file);
10
30
  return this;
11
31
  }
12
32
 
33
+ /**
34
+ * 设置检查规则
35
+ * @public
36
+ * @param {ValidateRule} [rule]
37
+ * @returns {this}
38
+ */
13
39
  validate(rule={}) {
40
+ /**
41
+ * @private
42
+ */
14
43
  this._rule = rule;
15
44
  return this;
16
45
  }
17
46
 
47
+ /**
48
+ * 设置存储文件名或函数
49
+ * @public
50
+ * @param {(string|function)} [name]
51
+ * @returns {this}
52
+ */
18
53
  rule(name) {
54
+ /**
55
+ * @private
56
+ */
19
57
  this._name = name;
20
58
  return this;
21
59
  }
22
60
 
61
+ /**
62
+ * 保存文件到目录
63
+ * @public
64
+ * @param {string} [dir]
65
+ * @returns {Promise<(boolean|UploadData)>}
66
+ */
23
67
  async save(dir) {
24
68
  if(!this.check()) {
25
69
  return false;
26
70
  }
27
71
 
28
72
  let savename = '';
29
- if(this._name || this._name === 0) {
73
+ if(this._name) {
30
74
  savename = (typeof this._name == 'function' ? this._name() : this._name) + '';
31
75
  } else {
32
- savename = utils.date.format('YYYY/mmdd/') + utils.md5(new Date().getTime() + Math.random().toString(36).substr(2));
76
+ savename = utils.date.format('YYYY/mmdd/') + utils.md5(Date.now() + Math.random().toString(36).substr(2));
33
77
  }
34
78
  if(!pt.extname(savename)) {
35
79
  savename += pt.extname(this._file.originalFilename);
36
80
  }
37
81
 
38
82
  try {
39
- const filePath = pt.join(this.$config.app.base_dir, dir, savename);
83
+ const filePath = pt.join(cfg_app.base_dir, dir, savename);
40
84
  await utils.fs.mkdirs(pt.dirname(filePath));
41
85
  const reader = fs.createReadStream(this._file.filepath);
42
86
  const writer = fs.createWriteStream(filePath);
@@ -65,14 +109,30 @@ class Upload extends Context
65
109
  }
66
110
  }
67
111
 
112
+ /**
113
+ * 获取上传文件
114
+ * @public
115
+ * @param {string} [name] - 为空时,获取所有上传文件
116
+ * @returns {(File|File[]|Files)}
117
+ */
68
118
  getFile(name) {
69
119
  return typeof name == 'string' ? this.ctx.request.files[name] : this.ctx.request.files;
70
120
  }
71
121
 
122
+ /**
123
+ * 获取错误信息
124
+ * @public
125
+ * @returns {string}
126
+ */
72
127
  getError() {
73
128
  return this._error;
74
129
  }
75
130
 
131
+ /**
132
+ * 检查上传文件
133
+ * @public
134
+ * @returns {boolean}
135
+ */
76
136
  check() {
77
137
  if(!this._file) {
78
138
  this._error = '上传文件为找到!';
@@ -104,6 +164,12 @@ class Upload extends Context
104
164
  return true;
105
165
  }
106
166
 
167
+ /**
168
+ * 检查上传文件后缀
169
+ * @public
170
+ * @param {(string|array)} ext
171
+ * @returns {boolean}
172
+ */
107
173
  checkExt(ext) {
108
174
  if(typeof ext == 'string') {
109
175
  ext = ext.split(',');
@@ -114,6 +180,12 @@ class Upload extends Context
114
180
  return true;
115
181
  }
116
182
 
183
+ /**
184
+ * 检查上传文件mimetype
185
+ * @public
186
+ * @param {(string|array)} type
187
+ * @returns {boolean}
188
+ */
117
189
  checkType(type) {
118
190
  if(typeof type == 'string') {
119
191
  type = type.split(',');
@@ -124,6 +196,11 @@ class Upload extends Context
124
196
  return true;
125
197
  }
126
198
 
199
+ /**
200
+ * 检查上传图片
201
+ * @public
202
+ * @returns {boolean}
203
+ */
127
204
  checkImg() {
128
205
  const fileExt = pt.extname(this._file.originalFilename).slice(1).toLowerCase();
129
206
  const fileType = this._file.mimetype.toLowerCase().split('/');
package/lib/url.js CHANGED
@@ -9,8 +9,20 @@ const querystring = require("querystring");
9
9
  const Context = require('./context');
10
10
  const {toLine} = require('./utils/str');
11
11
 
12
+ /**
13
+ * @extends Context
14
+ */
12
15
  class Url extends Context
13
16
  {
17
+ /**
18
+ * 编译生成url
19
+ * @public
20
+ * @param {string} [url] - 网址,支持智能解析
21
+ * @param {object} [vars] - 网址参数
22
+ * @param {string} [ext] - 网址后缀
23
+ * @param {string} [domain] - 网址域名
24
+ * @returns {string}
25
+ */
14
26
  build(url='', vars, ext='', domain='') {
15
27
  if(vars && typeof vars != 'object') {
16
28
  [vars, ext, domain] = [{}, vars, ext];
@@ -47,6 +59,13 @@ class Url extends Context
47
59
  return domain + urls.join('#');
48
60
  }
49
61
 
62
+ /**
63
+ * 由命名路由反向生成url
64
+ * @public
65
+ * @param {*} url - 路由名字
66
+ * @param {*} query - 网址参数
67
+ * @returns {string}
68
+ */
50
69
  ruleUrl(url, query = {}) {
51
70
  if(!routes[url]) {
52
71
  return url;