ultimate-express 1.0.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.
- package/.github/workflows/test.yml +27 -0
- package/EXPRESS_LICENSE +26 -0
- package/README.md +269 -0
- package/package.json +76 -0
- package/src/application.js +295 -0
- package/src/index.js +23 -0
- package/src/middlewares.js +95 -0
- package/src/request.js +324 -0
- package/src/response.js +686 -0
- package/src/router.js +490 -0
- package/src/utils.js +285 -0
- package/src/view.js +103 -0
- package/src/workers/fs.js +14 -0
- package/tests/index.js +64 -0
- package/tests/parts/.test/index.html +11 -0
- package/tests/parts/big.jpg +0 -0
- package/tests/parts/index.art +12 -0
- package/tests/parts/index.dot +12 -0
- package/tests/parts/index.ejs +12 -0
- package/tests/parts/index.handlebars +2 -0
- package/tests/parts/index.html +12 -0
- package/tests/parts/index.mustache +12 -0
- package/tests/parts/index.pug +6 -0
- package/tests/parts/index.swig +12 -0
- package/tests/parts/layouts/main.handlebars +12 -0
- package/tests/preload.cjs +5 -0
- package/tests/singular.js +41 -0
- package/tests/tests/app/app-engine.js +8 -0
- package/tests/tests/app/app-on-mount.js +24 -0
- package/tests/tests/app/app-path.js +21 -0
- package/tests/tests/app/app-route.js +30 -0
- package/tests/tests/app/options.js +16 -0
- package/tests/tests/app/setting-inheritance.js +16 -0
- package/tests/tests/engines/art-template.js +33 -0
- package/tests/tests/engines/dot.js +28 -0
- package/tests/tests/engines/ejs.js +25 -0
- package/tests/tests/engines/handlebars.js +28 -0
- package/tests/tests/engines/mustache.js +28 -0
- package/tests/tests/engines/pug.js +25 -0
- package/tests/tests/engines/swig.js +28 -0
- package/tests/tests/errors/error-handling-middleware.js +22 -0
- package/tests/tests/errors/next-error-optimized.js +26 -0
- package/tests/tests/errors/next-error.js +26 -0
- package/tests/tests/errors/unexpected-error-handling.js +18 -0
- package/tests/tests/listen/listen-random.js +11 -0
- package/tests/tests/listen/listen-specific.js +17 -0
- package/tests/tests/middlewares/body-json.js +31 -0
- package/tests/tests/middlewares/body-raw-deflate.js +43 -0
- package/tests/tests/middlewares/body-raw-gzip.js +43 -0
- package/tests/tests/middlewares/body-raw.js +41 -0
- package/tests/tests/middlewares/body-text.js +27 -0
- package/tests/tests/middlewares/body-urlencoded.js +30 -0
- package/tests/tests/middlewares/cookie-parser-signed.js +31 -0
- package/tests/tests/middlewares/cookie-parser.js +28 -0
- package/tests/tests/middlewares/cookie-session.js +40 -0
- package/tests/tests/middlewares/cors.js +29 -0
- package/tests/tests/middlewares/errorhandler.js +26 -0
- package/tests/tests/middlewares/express-fileupload-temp.js +46 -0
- package/tests/tests/middlewares/express-fileupload.js +28 -0
- package/tests/tests/middlewares/express-rate-limit.js +33 -0
- package/tests/tests/middlewares/express-session.js +37 -0
- package/tests/tests/middlewares/express-static-options.js +72 -0
- package/tests/tests/middlewares/express-static.js +40 -0
- package/tests/tests/middlewares/method-override.js +33 -0
- package/tests/tests/middlewares/multer.js +43 -0
- package/tests/tests/middlewares/multiple-middlewares.js +37 -0
- package/tests/tests/middlewares/response-time.js +29 -0
- package/tests/tests/middlewares/serve-index.js +38 -0
- package/tests/tests/middlewares/serve-static.js +38 -0
- package/tests/tests/middlewares/vhost.js +50 -0
- package/tests/tests/params/array-param.js +30 -0
- package/tests/tests/params/nested-params.js +24 -0
- package/tests/tests/params/param-errors.js +56 -0
- package/tests/tests/params/param-function.js +49 -0
- package/tests/tests/params/param-next-route.js +48 -0
- package/tests/tests/params/param-optimized.js +38 -0
- package/tests/tests/params/param-use.js +39 -0
- package/tests/tests/params/param.js +68 -0
- package/tests/tests/params/params-regex.js +26 -0
- package/tests/tests/params/params-use.js +20 -0
- package/tests/tests/params/params.js +35 -0
- package/tests/tests/req/req-accepts-charsets.js +40 -0
- package/tests/tests/req/req-accepts-encodings.js +36 -0
- package/tests/tests/req/req-accepts-languages.js +41 -0
- package/tests/tests/req/req-accepts.js +41 -0
- package/tests/tests/req/req-app.js +17 -0
- package/tests/tests/req/req-baseurl.js +38 -0
- package/tests/tests/req/req-connection.js +19 -0
- package/tests/tests/req/req-fresh.js +59 -0
- package/tests/tests/req/req-get.js +78 -0
- package/tests/tests/req/req-headers-distinct.js +72 -0
- package/tests/tests/req/req-headers.js +72 -0
- package/tests/tests/req/req-host.js +45 -0
- package/tests/tests/req/req-hostname.js +45 -0
- package/tests/tests/req/req-ip.js +19 -0
- package/tests/tests/req/req-is.js +44 -0
- package/tests/tests/req/req-original-url.js +29 -0
- package/tests/tests/req/req-param.js +29 -0
- package/tests/tests/req/req-protocol.js +20 -0
- package/tests/tests/req/req-query.js +23 -0
- package/tests/tests/req/req-range.js +48 -0
- package/tests/tests/req/req-raw-headers.js +72 -0
- package/tests/tests/req/req-subdomains.js +48 -0
- package/tests/tests/req/req-url-nested.js +27 -0
- package/tests/tests/req/req-url-optimized-router.js +26 -0
- package/tests/tests/req/req-url-optimized.js +23 -0
- package/tests/tests/req/req-url.js +36 -0
- package/tests/tests/req/req-xhr.js +23 -0
- package/tests/tests/res/head-content-length.js +18 -0
- package/tests/tests/res/head.js +47 -0
- package/tests/tests/res/injecting.js +25 -0
- package/tests/tests/res/piping.js +23 -0
- package/tests/tests/res/res-app.js +17 -0
- package/tests/tests/res/res-append.js +24 -0
- package/tests/tests/res/res-attachment.js +19 -0
- package/tests/tests/res/res-clear-cookie.js +18 -0
- package/tests/tests/res/res-cookie.js +22 -0
- package/tests/tests/res/res-download.js +36 -0
- package/tests/tests/res/res-format.js +57 -0
- package/tests/tests/res/res-get.js +18 -0
- package/tests/tests/res/res-headers-sent.js +18 -0
- package/tests/tests/res/res-json.js +17 -0
- package/tests/tests/res/res-jsonp.js +25 -0
- package/tests/tests/res/res-links.js +21 -0
- package/tests/tests/res/res-location.js +34 -0
- package/tests/tests/res/res-redirect.js +46 -0
- package/tests/tests/res/res-remove-header.js +19 -0
- package/tests/tests/res/res-send-file.js +17 -0
- package/tests/tests/res/res-send-status.js +17 -0
- package/tests/tests/res/res-send.js +69 -0
- package/tests/tests/res/res-set.js +28 -0
- package/tests/tests/res/res-status.js +18 -0
- package/tests/tests/res/res-type.js +19 -0
- package/tests/tests/res/res-vary.js +19 -0
- package/tests/tests/res/res-write.js +29 -0
- package/tests/tests/routers/complex-routers.js +34 -0
- package/tests/tests/routers/empty-router.js +25 -0
- package/tests/tests/routers/lot-of-routes.js +38 -0
- package/tests/tests/routers/mergeparams.js +42 -0
- package/tests/tests/routers/nested-routers.js +52 -0
- package/tests/tests/routers/router-options.js +68 -0
- package/tests/tests/routers/routers.js +45 -0
- package/tests/tests/routers/simple-routers.js +35 -0
- package/tests/tests/routing/all.js +47 -0
- package/tests/tests/routing/array-arguments.js +35 -0
- package/tests/tests/routing/array-use.js +33 -0
- package/tests/tests/routing/async-use.js +25 -0
- package/tests/tests/routing/complex-routes.js +50 -0
- package/tests/tests/routing/lot-of-param-routes.js +26 -0
- package/tests/tests/routing/lot-of-routes.js +59 -0
- package/tests/tests/routing/next-existent-optimized-route.js +29 -0
- package/tests/tests/routing/next-existent-route.js +29 -0
- package/tests/tests/routing/next-nonexistent-optimized-route.js +19 -0
- package/tests/tests/routing/next-nonexistent-route.js +19 -0
- package/tests/tests/routing/next-special-cases.js +54 -0
- package/tests/tests/routing/next-unoptimized.js +39 -0
- package/tests/tests/routing/no-path-use.js +29 -0
- package/tests/tests/routing/non-string-routes.js +27 -0
- package/tests/tests/routing/req-multiple-mountpaths.js +34 -0
- package/tests/tests/routing/simple-routes.js +29 -0
- package/tests/tests/routing/simple-use.js +52 -0
- package/tests/tests/routing/some-middlewares.js +27 -0
- package/tests/tests/routing/special-characters.js +28 -0
- package/tests/tests/routing/star.js +31 -0
- package/tests/tests/routing/sub-apps.js +32 -0
- package/tests/tests/routing/trailing-slash.js +37 -0
- package/tests/tests/routing/weird-route-start.js +18 -0
- package/tests/tests/send-file/accept-ranges.js +26 -0
- package/tests/tests/send-file/callback.js +23 -0
- package/tests/tests/send-file/default-error-routing.js +21 -0
- package/tests/tests/send-file/dotfiles.js +24 -0
- package/tests/tests/send-file/etag.js +19 -0
- package/tests/tests/send-file/fs-threads.js +39 -0
- package/tests/tests/send-file/head.js +49 -0
- package/tests/tests/send-file/headers.js +23 -0
- package/tests/tests/send-file/if-match.js +31 -0
- package/tests/tests/send-file/if-range.js +37 -0
- package/tests/tests/send-file/if-unmodified-since.js +32 -0
- package/tests/tests/send-file/immutable.js +22 -0
- package/tests/tests/send-file/large-file.js +19 -0
- package/tests/tests/send-file/last-modified.js +21 -0
- package/tests/tests/send-file/max-age.js +21 -0
- package/tests/tests/send-file/path-traversal.js +35 -0
- package/tests/tests/send-file/range.js +57 -0
- package/tests/tests/send-file/simple.js +18 -0
- package/tests/tests/settings/case-sensitive-routing.js +48 -0
- package/tests/tests/settings/env-errors.js +38 -0
- package/tests/tests/settings/etag.js +36 -0
- package/tests/tests/settings/json-escape.js +45 -0
- package/tests/tests/settings/json-replacer.js +50 -0
- package/tests/tests/settings/json-spaces.js +44 -0
- package/tests/tests/settings/query-parser.js +45 -0
- package/tests/tests/settings/strict-routing.js +64 -0
- package/tests/tests/settings/subdomain-offset.js +38 -0
- package/tests/tests/settings/trust-proxy-host.js +58 -0
- package/tests/tests/settings/trust-proxy-ip.js +58 -0
- package/tests/tests/settings/trust-proxy-ips.js +58 -0
- package/tests/tests/settings/trust-proxy-protocol.js +46 -0
- package/tests/tests/settings/x-powered-by.js +32 -0
- package/tests/uws.js +14 -0
package/src/utils.js
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
const mime = require("mime-types");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const proxyaddr = require("proxy-addr");
|
|
4
|
+
const qs = require("qs");
|
|
5
|
+
const etag = require("etag");
|
|
6
|
+
const { Stats } = require("fs");
|
|
7
|
+
|
|
8
|
+
function removeDuplicateSlashes(path) {
|
|
9
|
+
return path.replace(/\/{2,}/g, '/');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function patternToRegex(pattern, isPrefix = false) {
|
|
13
|
+
if(pattern instanceof RegExp) {
|
|
14
|
+
return pattern;
|
|
15
|
+
}
|
|
16
|
+
if(isPrefix && pattern === '/') {
|
|
17
|
+
return new RegExp(``);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let regexPattern = pattern
|
|
21
|
+
.replace(/\./g, '\\.')
|
|
22
|
+
.replace(/\-/g, '\\-')
|
|
23
|
+
.replace(/\*/g, '.*') // Convert * to .*
|
|
24
|
+
.replace(/:(\w+)(\(.+?\))?/g, (match, param, regex) => {
|
|
25
|
+
return `(?<${param}>${regex ? regex + '($|\\/)' : '[^/]+'})`;
|
|
26
|
+
}); // Convert :param to capture group
|
|
27
|
+
|
|
28
|
+
return new RegExp(`^${regexPattern}${isPrefix ? '(?=$|\/)' : '$'}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function needsConversionToRegex(pattern) {
|
|
32
|
+
if(pattern instanceof RegExp) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
if(pattern === '/*') {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return pattern.includes('*') ||
|
|
40
|
+
pattern.includes('?') ||
|
|
41
|
+
pattern.includes('+') ||
|
|
42
|
+
pattern.includes('(') ||
|
|
43
|
+
pattern.includes(')') ||
|
|
44
|
+
pattern.includes(':') ||
|
|
45
|
+
pattern.includes('{') ||
|
|
46
|
+
pattern.includes('}') ||
|
|
47
|
+
pattern.includes('[') ||
|
|
48
|
+
pattern.includes(']');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function acceptParams(str) {
|
|
52
|
+
const parts = str.split(/ *; */);
|
|
53
|
+
const ret = { value: parts[0], quality: 1, params: {} }
|
|
54
|
+
|
|
55
|
+
for (let i = 1; i < parts.length; ++i) {
|
|
56
|
+
const pms = parts[i].split(/ *= */);
|
|
57
|
+
if ('q' === pms[0]) {
|
|
58
|
+
ret.quality = parseFloat(pms[1]);
|
|
59
|
+
} else {
|
|
60
|
+
ret.params[pms[0]] = pms[1];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return ret;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function normalizeType(type) {
|
|
68
|
+
return ~type.indexOf('/') ?
|
|
69
|
+
acceptParams(type) :
|
|
70
|
+
{ value: (mime.lookup(type) || 'application/octet-stream'), params: {} };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function stringify(value, replacer, spaces, escape) {
|
|
74
|
+
let json = replacer || spaces
|
|
75
|
+
? JSON.stringify(value, replacer, spaces)
|
|
76
|
+
: JSON.stringify(value);
|
|
77
|
+
|
|
78
|
+
if (escape && typeof json === 'string') {
|
|
79
|
+
json = json.replace(/[<>&]/g, function (c) {
|
|
80
|
+
switch (c.charCodeAt(0)) {
|
|
81
|
+
case 0x3c:
|
|
82
|
+
return '\\u003c'
|
|
83
|
+
case 0x3e:
|
|
84
|
+
return '\\u003e'
|
|
85
|
+
case 0x26:
|
|
86
|
+
return '\\u0026'
|
|
87
|
+
default:
|
|
88
|
+
return c
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return json;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const defaultSettings = {
|
|
97
|
+
'jsonp callback name': 'callback',
|
|
98
|
+
'env': () => process.env.NODE_ENV ?? 'development',
|
|
99
|
+
'etag': 'weak',
|
|
100
|
+
'etag fn': () => createETagGenerator({ weak: true }),
|
|
101
|
+
'query parser': 'extended',
|
|
102
|
+
'query parser fn': () => qs.parse,
|
|
103
|
+
'subdomain offset': 2,
|
|
104
|
+
'trust proxy': false,
|
|
105
|
+
'views': () => path.join(process.cwd(), 'views'),
|
|
106
|
+
'view cache': () => process.env.NODE_ENV === 'production',
|
|
107
|
+
'x-powered-by': true,
|
|
108
|
+
'case sensitive routing': true
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
function compileTrust(val) {
|
|
112
|
+
if (typeof val === 'function') return val;
|
|
113
|
+
|
|
114
|
+
if (val === true) {
|
|
115
|
+
// Support plain true/false
|
|
116
|
+
return function(){ return true };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (typeof val === 'number') {
|
|
120
|
+
// Support trusting hop count
|
|
121
|
+
return function(a, i){ return i < val };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (typeof val === 'string') {
|
|
125
|
+
// Support comma-separated values
|
|
126
|
+
val = val.split(',')
|
|
127
|
+
.map(function (v) { return v.trim() })
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return proxyaddr.compile(val || []);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const shownWarnings = new Set();
|
|
134
|
+
function deprecated(oldMethod, newMethod, full = false) {
|
|
135
|
+
const err = new Error();
|
|
136
|
+
const pos = full ? err.stack.split('\n').slice(1).join('\n') : err.stack.split('\n')[3].trim().split('(').slice(1).join('(').split(')').slice(0, -1).join(')');
|
|
137
|
+
if(shownWarnings.has(pos)) return;
|
|
138
|
+
shownWarnings.add(pos);
|
|
139
|
+
console.warn(`${new Date().toLocaleString('en-UK', {
|
|
140
|
+
weekday: 'short',
|
|
141
|
+
year: 'numeric',
|
|
142
|
+
month: 'short',
|
|
143
|
+
day: 'numeric',
|
|
144
|
+
hour: 'numeric',
|
|
145
|
+
minute: 'numeric',
|
|
146
|
+
second: 'numeric',
|
|
147
|
+
timeZone: 'GMT',
|
|
148
|
+
timeZoneName: 'short'
|
|
149
|
+
})} u-express deprecated ${oldMethod}: Use ${newMethod} instead at ${pos}`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function findIndexStartingFrom(arr, fn, index = 0) {
|
|
153
|
+
for(let i = index; i < arr.length; i++) {
|
|
154
|
+
if(fn(arr[i], i, arr)) {
|
|
155
|
+
return i;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return -1;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
function decode (path) {
|
|
162
|
+
try {
|
|
163
|
+
return decodeURIComponent(path)
|
|
164
|
+
} catch (err) {
|
|
165
|
+
return -1
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const UP_PATH_REGEXP = /(?:^|[\\/])\.\.(?:[\\/]|$)/;
|
|
170
|
+
|
|
171
|
+
function containsDotFile(parts) {
|
|
172
|
+
for(let i = 0; i < parts.length; i++) {
|
|
173
|
+
const part = parts[i];
|
|
174
|
+
if(part.length > 1 && part[0] === '.') {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function parseTokenList(str) {
|
|
183
|
+
let end = 0;
|
|
184
|
+
const list = [];
|
|
185
|
+
let start = 0;
|
|
186
|
+
|
|
187
|
+
// gather tokens
|
|
188
|
+
for (let i = 0, len = str.length; i < len; i++) {
|
|
189
|
+
switch(str.charCodeAt(i)) {
|
|
190
|
+
case 0x20: /* */
|
|
191
|
+
if (start === end) {
|
|
192
|
+
start = end = i + 1;
|
|
193
|
+
}
|
|
194
|
+
break;
|
|
195
|
+
case 0x2c: /* , */
|
|
196
|
+
if (start !== end) {
|
|
197
|
+
list.push(str.substring(start, end));
|
|
198
|
+
}
|
|
199
|
+
start = end = i + 1;
|
|
200
|
+
break;
|
|
201
|
+
default:
|
|
202
|
+
end = i + 1;
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// final token
|
|
208
|
+
if (start !== end) {
|
|
209
|
+
list.push(str.substring(start, end));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return list;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
function parseHttpDate(date) {
|
|
217
|
+
const timestamp = date && Date.parse(date);
|
|
218
|
+
return typeof timestamp === 'number' ? timestamp : NaN;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function isPreconditionFailure(req, res) {
|
|
222
|
+
const match = req.headers['if-match'];
|
|
223
|
+
|
|
224
|
+
// if-match
|
|
225
|
+
if(match) {
|
|
226
|
+
const etag = res.get('etag');
|
|
227
|
+
return !etag || (match !== '*' && parseTokenList(match).every(match => {
|
|
228
|
+
return match !== etag && match !== 'W/' + etag && 'W/' + match !== etag;
|
|
229
|
+
}));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// if-unmodified-since
|
|
233
|
+
const unmodifiedSince = parseHttpDate(req.headers['if-unmodified-since']);
|
|
234
|
+
if(!isNaN(unmodifiedSince)) {
|
|
235
|
+
const lastModified = parseHttpDate(res.get('Last-Modified'));
|
|
236
|
+
return isNaN(lastModified) || lastModified > unmodifiedSince;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function createETagGenerator(options) {
|
|
243
|
+
return function generateETag (body, encoding) {
|
|
244
|
+
const buf = !(body instanceof Stats) && !Buffer.isBuffer(body) ? Buffer.from(body, encoding) : body;
|
|
245
|
+
return etag(buf, options);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function isRangeFresh(req, res) {
|
|
250
|
+
const ifRange = req.headers['if-range'];
|
|
251
|
+
if(!ifRange) {
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// if-range as etag
|
|
256
|
+
if(ifRange.indexOf('"') !== -1) {
|
|
257
|
+
const etag = res.get('etag');
|
|
258
|
+
return Boolean(etag && ifRange.indexOf(etag) !== -1);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// if-range as modified date
|
|
262
|
+
const lastModified = res.get('Last-Modified');
|
|
263
|
+
return parseHttpDate(lastModified) <= parseHttpDate(ifRange);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
module.exports = {
|
|
267
|
+
removeDuplicateSlashes,
|
|
268
|
+
patternToRegex,
|
|
269
|
+
needsConversionToRegex,
|
|
270
|
+
acceptParams,
|
|
271
|
+
normalizeType,
|
|
272
|
+
stringify,
|
|
273
|
+
defaultSettings,
|
|
274
|
+
compileTrust,
|
|
275
|
+
deprecated,
|
|
276
|
+
UP_PATH_REGEXP,
|
|
277
|
+
decode,
|
|
278
|
+
containsDotFile,
|
|
279
|
+
parseTokenList,
|
|
280
|
+
parseHttpDate,
|
|
281
|
+
isPreconditionFailure,
|
|
282
|
+
createETagGenerator,
|
|
283
|
+
isRangeFresh,
|
|
284
|
+
findIndexStartingFrom
|
|
285
|
+
};
|
package/src/view.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
|
|
4
|
+
module.exports = class View {
|
|
5
|
+
constructor(name, options) {
|
|
6
|
+
this.name = name;
|
|
7
|
+
this.options = options ? Object.assign({}, options) : {};
|
|
8
|
+
this.defaultEngine = options.defaultEngine;
|
|
9
|
+
this.ext = path.extname(name);
|
|
10
|
+
this.root = options.root;
|
|
11
|
+
|
|
12
|
+
if (!this.ext && !this.defaultEngine) {
|
|
13
|
+
throw new Error('No default engine was specified and no extension was provided.');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let fileName = name;
|
|
17
|
+
if(!this.ext) {
|
|
18
|
+
this.ext = this.defaultEngine[0] !== '.'
|
|
19
|
+
? '.' + this.defaultEngine
|
|
20
|
+
: this.defaultEngine;
|
|
21
|
+
|
|
22
|
+
fileName += this.ext;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!this.options.engines[this.ext]) {
|
|
26
|
+
const mod = this.ext.slice(1);
|
|
27
|
+
|
|
28
|
+
// default engine export
|
|
29
|
+
const fn = require(mod).__express;
|
|
30
|
+
|
|
31
|
+
if (typeof fn !== 'function') {
|
|
32
|
+
throw new Error('Module "' + mod + '" does not provide a view engine.')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
this.options.engines[this.ext] = fn;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this.engine = this.options.engines[this.ext];
|
|
39
|
+
this.path = path.join(this.root, fileName);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
lookup(name) {
|
|
43
|
+
let path;
|
|
44
|
+
let roots = [].concat(this.root);
|
|
45
|
+
for (let i = 0; i < roots.length && !path; i++) {
|
|
46
|
+
const root = roots[i];
|
|
47
|
+
|
|
48
|
+
// resolve the path
|
|
49
|
+
const loc = path.resolve(root, name);
|
|
50
|
+
const dir = path.dirname(loc);
|
|
51
|
+
const file = path.basename(loc);
|
|
52
|
+
|
|
53
|
+
// resolve the file
|
|
54
|
+
path = this.resolve(dir, file);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return path;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ill be real idk what exactly this does but express implements it this way
|
|
61
|
+
render(options, callback) {
|
|
62
|
+
let sync = true;
|
|
63
|
+
this.engine(this.path, options, function onRender() {
|
|
64
|
+
if(!sync) {
|
|
65
|
+
return callback.apply(this, arguments);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return process.nextTick(() => {
|
|
69
|
+
return callback.apply(this, arguments);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
sync = false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
resolve(dir, file) {
|
|
77
|
+
const ext = this.ext;
|
|
78
|
+
|
|
79
|
+
// <path>.<ext>
|
|
80
|
+
let path = path.join(dir, file);
|
|
81
|
+
let stat = tryStat(path);
|
|
82
|
+
|
|
83
|
+
if(stat && stat.isFile()) {
|
|
84
|
+
return path;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// <path>/index.<ext>
|
|
88
|
+
path = path.join(dir, path.basename(file, ext) + ext);
|
|
89
|
+
stat = tryStat(path);
|
|
90
|
+
|
|
91
|
+
if(stat && stat.isFile()) {
|
|
92
|
+
return path;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function tryStat(path) {
|
|
98
|
+
try {
|
|
99
|
+
return fs.statSync(path);
|
|
100
|
+
} catch (e) {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const { parentPort } = require("worker_threads");
|
|
3
|
+
|
|
4
|
+
parentPort.on('message', (message) => {
|
|
5
|
+
if(message.type === 'readFile') {
|
|
6
|
+
try {
|
|
7
|
+
const data = fs.readFileSync(message.path);
|
|
8
|
+
const ab = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
9
|
+
parentPort.postMessage({ key: message.key, data: ab }, [ab]);
|
|
10
|
+
} catch(err) {
|
|
11
|
+
parentPort.postMessage({ key: message.key, err: String(err) });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
});
|
package/tests/index.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// npm run test - runs all tests
|
|
2
|
+
// npm run test routing - runs all tests in the routing category
|
|
3
|
+
// npm run test tests/tests/routing - runs all tests in the routing category
|
|
4
|
+
// npm run test tests/tests/listen/listen-random.js - runs the test at tests/tests/listen/listen-random.js
|
|
5
|
+
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const test = require("node:test");
|
|
9
|
+
const childProcess = require("node:child_process");
|
|
10
|
+
const assert = require("node:assert");
|
|
11
|
+
|
|
12
|
+
const testPath = path.join(__dirname, 'tests');
|
|
13
|
+
|
|
14
|
+
let testCategories = fs.readdirSync(testPath).sort((a, b) => parseInt(a) - parseInt(b));
|
|
15
|
+
const filterPath = process.argv[2];
|
|
16
|
+
|
|
17
|
+
if(filterPath) {
|
|
18
|
+
if(!filterPath.endsWith('.js')) {
|
|
19
|
+
testCategories = testCategories.filter(category => category.startsWith(path.basename(filterPath)));
|
|
20
|
+
} else {
|
|
21
|
+
testCategories = [path.dirname(filterPath).split(path.sep).pop()];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
for (const testCategory of testCategories) {
|
|
26
|
+
test(testCategory, async () => {
|
|
27
|
+
let tests = fs.readdirSync(path.join(__dirname, 'tests', testCategory)).sort((a, b) => parseInt(a) - parseInt(b));
|
|
28
|
+
for (const testName of tests) {
|
|
29
|
+
if(filterPath && filterPath.endsWith('.js')) {
|
|
30
|
+
if(path.basename(testName) !== path.basename(filterPath)) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
let testPath = path.join(__dirname, 'tests', testCategory, testName);
|
|
35
|
+
let testCode = fs.readFileSync(testPath, 'utf8').replace(`const express = require("../../../src/index.js");`, 'const express = require("express");');
|
|
36
|
+
fs.writeFileSync(testPath, testCode);
|
|
37
|
+
let testDescription = testCode.split('\n')[0].slice(2).trim();
|
|
38
|
+
if(testDescription.endsWith('OFF')) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
await new Promise(resolve => {
|
|
43
|
+
test(testDescription, () => {
|
|
44
|
+
process.stdout.write(testDescription + '...');
|
|
45
|
+
try {
|
|
46
|
+
let expressOutput = childProcess.execSync(`node ${testPath}`).toString();
|
|
47
|
+
|
|
48
|
+
fs.writeFileSync(testPath, testCode.replace(`const express = require("express");`, `const express = require("../../../src/index.js");`));
|
|
49
|
+
let uExpressOutput = childProcess.execSync(`node ${testPath}`).toString();
|
|
50
|
+
|
|
51
|
+
assert.strictEqual(uExpressOutput, expressOutput);
|
|
52
|
+
console.log('\x1b[32mOK\x1b[0m');
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.log('\x1b[31mFAIL\x1b[0m');
|
|
55
|
+
throw error;
|
|
56
|
+
} finally {
|
|
57
|
+
fs.writeFileSync(testPath, testCode);
|
|
58
|
+
resolve();
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>{{ title }}</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<h1>{{ message }}</h1>
|
|
10
|
+
<span>{{ asdf }}</span>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>[[= title ]]</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<h1>[[= message ]]</h1>
|
|
10
|
+
<span>[[= asdf ]]</span>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title><%= title %></title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<h1><%= message %></h1>
|
|
10
|
+
<span><%= asdf %></span>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>{{ title }}</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<h1>{{ message }}</h1>
|
|
10
|
+
<span>{{ asdf }}</span>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>{{ title }}</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<h1>{{ message }}</h1>
|
|
10
|
+
<span>{{ asdf }}</span>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const childProcess = require("child_process");
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const exitHook = require("exit-hook");
|
|
4
|
+
const { exit } = require("process");
|
|
5
|
+
|
|
6
|
+
let args = process.argv.slice(2);
|
|
7
|
+
|
|
8
|
+
let u = args.some(arg => arg === '-u');
|
|
9
|
+
args = args.filter(arg => arg !== '-u');
|
|
10
|
+
|
|
11
|
+
let path = args[0];
|
|
12
|
+
|
|
13
|
+
if (!path) {
|
|
14
|
+
console.error('Usage: node singular.js [-u] <path>');
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (u) {
|
|
19
|
+
console.log('Running as µExpress');
|
|
20
|
+
let code = fs.readFileSync(path, 'utf8');
|
|
21
|
+
fs.writeFileSync(path, code.replace('const express = require("express");', 'const express = require("../../../src/index.js");'));
|
|
22
|
+
} else {
|
|
23
|
+
let code = fs.readFileSync(path, 'utf8');
|
|
24
|
+
fs.writeFileSync(path, code.replace(`const express = require("../../../src/index.js");`, `const express = require("express");`));
|
|
25
|
+
console.log('Running as normal Express');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let node = childProcess.spawn('node', ['-r', './tests/preload.cjs', path]);
|
|
29
|
+
|
|
30
|
+
node.stdout.on('data', data => {
|
|
31
|
+
console.log(data.toString());
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
node.stderr.on('data', data => {
|
|
35
|
+
console.error(data.toString());
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
exitHook(() => {
|
|
39
|
+
let code = fs.readFileSync(path, 'utf8');
|
|
40
|
+
fs.writeFileSync(path, code.replaceAll(`const express = require("../../../src/index.js");`, `const express = require("express");`));
|
|
41
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// must emit 'mount' when using subapp
|
|
2
|
+
|
|
3
|
+
const express = require("express");
|
|
4
|
+
|
|
5
|
+
const app = express();
|
|
6
|
+
const student = express();
|
|
7
|
+
const teacher = express();
|
|
8
|
+
|
|
9
|
+
teacher.on('mount', (parent) => {
|
|
10
|
+
console.log('aaaaaaaaaaaaaaaaaaaaaaaaaa');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
student.on('mount', (parent) => {
|
|
14
|
+
console.log('dewd');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
app.use('/student', student);
|
|
18
|
+
app.use('/teacher', teacher);
|
|
19
|
+
|
|
20
|
+
app.listen(13333, (err) => {
|
|
21
|
+
console.log("Server is running on port 13333");
|
|
22
|
+
|
|
23
|
+
process.exit(0);
|
|
24
|
+
});
|