ultimate-express 1.3.0 → 1.3.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/README.md +1 -1
- package/package.json +4 -2
- package/src/application.js +10 -9
- package/src/middlewares.js +4 -4
- package/src/request.js +5 -5
- package/src/response.js +17 -20
- package/src/router.js +5 -4
- package/src/types.d.ts +49 -4
- package/src/utils.js +5 -0
- package/src/view.js +2 -1
package/README.md
CHANGED
|
@@ -289,6 +289,7 @@ Almost all middlewares that are compatible with Express are compatible with µEx
|
|
|
289
289
|
- ✅ [body-parser](https://npmjs.com/package/body-parser) (use `express.text()` etc instead for better performance)
|
|
290
290
|
- ✅ [cookie-parser](https://npmjs.com/package/cookie-parser)
|
|
291
291
|
- ✅ [cookie-session](https://npmjs.com/package/cookie-session)
|
|
292
|
+
- ✅ [compression](https://npmjs.com/package/compression)
|
|
292
293
|
- ✅ [serve-static](https://npmjs.com/package/serve-static) (use `express.static()` instead for better performance)
|
|
293
294
|
- ✅ [serve-index](https://npmjs.com/package/serve-index)
|
|
294
295
|
- ✅ [cors](https://npmjs.com/package/cors)
|
|
@@ -304,7 +305,6 @@ Almost all middlewares that are compatible with Express are compatible with µEx
|
|
|
304
305
|
|
|
305
306
|
Middlewares and modules that are confirmed to not work:
|
|
306
307
|
|
|
307
|
-
- ❌ [compression](https://npmjs.com/package/compression) - doesn't error, but doesn't compress
|
|
308
308
|
- ❌ [express-async-errors](https://npmjs.com/package/express-async-errors) - doesn't work, use `app.set('catch async errors', true)` instead.
|
|
309
309
|
|
|
310
310
|
## Tested view engines
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultimate-express",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "The Ultimate Express. Fastest http server with full Express compatibility, based on uWebSockets.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"@types/express": "^4.0.0",
|
|
43
43
|
"accepts": "^1.3.8",
|
|
44
44
|
"bytes": "^3.1.2",
|
|
45
|
-
"cookie": "^0.
|
|
45
|
+
"cookie": "^1.0.1",
|
|
46
46
|
"cookie-signature": "^1.2.1",
|
|
47
47
|
"encodeurl": "^2.0.0",
|
|
48
48
|
"etag": "^1.8.1",
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"body-parser": "^1.20.3",
|
|
65
|
+
"compression": "^1.7.4",
|
|
65
66
|
"cookie-parser": "^1.4.6",
|
|
66
67
|
"cookie-session": "^2.1.0",
|
|
67
68
|
"cors": "^2.8.5",
|
|
@@ -81,6 +82,7 @@
|
|
|
81
82
|
"multer": "^1.4.5-lts.1",
|
|
82
83
|
"mustache-express": "^1.3.2",
|
|
83
84
|
"pako": "^2.1.0",
|
|
85
|
+
"pkg-pr-new": "^0.0.29",
|
|
84
86
|
"pug": "^3.0.3",
|
|
85
87
|
"response-time": "^2.3.2",
|
|
86
88
|
"serve-index": "^1.9.1",
|
package/src/application.js
CHANGED
|
@@ -16,7 +16,7 @@ limitations under the License.
|
|
|
16
16
|
|
|
17
17
|
const uWS = require("uWebSockets.js");
|
|
18
18
|
const Router = require("./router.js");
|
|
19
|
-
const { removeDuplicateSlashes, defaultSettings, compileTrust, createETagGenerator, fastQueryParse } = require("./utils.js");
|
|
19
|
+
const { removeDuplicateSlashes, defaultSettings, compileTrust, createETagGenerator, fastQueryParse, NullObject } = require("./utils.js");
|
|
20
20
|
const querystring = require("fast-querystring");
|
|
21
21
|
const ViewClass = require("./view.js");
|
|
22
22
|
const path = require("path");
|
|
@@ -27,7 +27,7 @@ const cpuCount = os.cpus().length;
|
|
|
27
27
|
|
|
28
28
|
let workers = [];
|
|
29
29
|
let taskKey = 0;
|
|
30
|
-
const workerTasks =
|
|
30
|
+
const workerTasks = new NullObject();
|
|
31
31
|
|
|
32
32
|
function createWorker() {
|
|
33
33
|
const worker = new Worker(path.join(__dirname, 'worker.js'));
|
|
@@ -47,7 +47,7 @@ function createWorker() {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
class Application extends Router {
|
|
50
|
-
constructor(settings =
|
|
50
|
+
constructor(settings = new NullObject()) {
|
|
51
51
|
super(settings);
|
|
52
52
|
if(!settings?.uwsOptions) {
|
|
53
53
|
settings.uwsOptions = {};
|
|
@@ -62,8 +62,8 @@ class Application extends Router {
|
|
|
62
62
|
this.uwsApp = uWS.App(settings.uwsOptions);
|
|
63
63
|
this.ssl = false;
|
|
64
64
|
}
|
|
65
|
-
this.cache =
|
|
66
|
-
this.engines =
|
|
65
|
+
this.cache = new NullObject();
|
|
66
|
+
this.engines = new NullObject();
|
|
67
67
|
this.locals = {
|
|
68
68
|
settings: this.settings
|
|
69
69
|
};
|
|
@@ -206,7 +206,7 @@ class Application extends Router {
|
|
|
206
206
|
throw err;
|
|
207
207
|
}
|
|
208
208
|
this.port = uWS.us_socket_local_port(socket);
|
|
209
|
-
callback(this.port);
|
|
209
|
+
if(callback) callback(this.port);
|
|
210
210
|
};
|
|
211
211
|
let fn = 'listen';
|
|
212
212
|
let args = [];
|
|
@@ -229,6 +229,7 @@ class Application extends Router {
|
|
|
229
229
|
}
|
|
230
230
|
this.listenCalled = true;
|
|
231
231
|
this.uwsApp[fn](...args);
|
|
232
|
+
return this.uwsApp;
|
|
232
233
|
}
|
|
233
234
|
|
|
234
235
|
address() {
|
|
@@ -260,10 +261,10 @@ class Application extends Router {
|
|
|
260
261
|
render(name, options, callback) {
|
|
261
262
|
if(typeof options === 'function') {
|
|
262
263
|
callback = options;
|
|
263
|
-
options =
|
|
264
|
+
options = new NullObject();
|
|
264
265
|
}
|
|
265
266
|
if(!options) {
|
|
266
|
-
options =
|
|
267
|
+
options = new NullObject();
|
|
267
268
|
} else {
|
|
268
269
|
options = Object.assign({}, options);
|
|
269
270
|
}
|
|
@@ -318,4 +319,4 @@ class Application extends Router {
|
|
|
318
319
|
|
|
319
320
|
module.exports = function(options) {
|
|
320
321
|
return new Application(options);
|
|
321
|
-
}
|
|
322
|
+
}
|
package/src/middlewares.js
CHANGED
|
@@ -20,10 +20,10 @@ const bytes = require('bytes');
|
|
|
20
20
|
const zlib = require('fast-zlib');
|
|
21
21
|
const typeis = require('type-is');
|
|
22
22
|
const querystring = require('fast-querystring');
|
|
23
|
-
const { fastQueryParse } = require('./utils.js');
|
|
23
|
+
const { fastQueryParse, NullObject } = require('./utils.js');
|
|
24
24
|
|
|
25
25
|
function static(root, options) {
|
|
26
|
-
if(!options) options =
|
|
26
|
+
if(!options) options = new NullObject();
|
|
27
27
|
if(typeof options.index === 'undefined') options.index = 'index.html';
|
|
28
28
|
if(typeof options.redirect === 'undefined') options.redirect = true;
|
|
29
29
|
if(typeof options.fallthrough === 'undefined') options.fallthrough = true;
|
|
@@ -132,7 +132,7 @@ function createInflate(contentEncoding) {
|
|
|
132
132
|
function createBodyParser(defaultType, beforeReturn) {
|
|
133
133
|
return function(options) {
|
|
134
134
|
if(typeof options !== 'object') {
|
|
135
|
-
options =
|
|
135
|
+
options = new NullObject();
|
|
136
136
|
}
|
|
137
137
|
if(typeof options.limit === 'undefined') options.limit = bytes('100kb');
|
|
138
138
|
else options.limit = bytes(options.limit);
|
|
@@ -160,7 +160,7 @@ function createBodyParser(defaultType, beforeReturn) {
|
|
|
160
160
|
return next();
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
req.body =
|
|
163
|
+
req.body = new NullObject();
|
|
164
164
|
|
|
165
165
|
// skip reading body for non-json content type
|
|
166
166
|
if(!type) {
|
package/src/request.js
CHANGED
|
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
const { patternToRegex, deprecated } = require("./utils.js");
|
|
17
|
+
const { patternToRegex, deprecated, NullObject } = require("./utils.js");
|
|
18
18
|
const accepts = require("accepts");
|
|
19
19
|
const typeis = require("type-is");
|
|
20
20
|
const parseRange = require("range-parser");
|
|
@@ -64,7 +64,7 @@ module.exports = class Request extends Readable {
|
|
|
64
64
|
this._opPath = this._opPath.slice(0, -1);
|
|
65
65
|
}
|
|
66
66
|
this.method = req.getCaseSensitiveMethod().toUpperCase();
|
|
67
|
-
this.params =
|
|
67
|
+
this.params = new NullObject();
|
|
68
68
|
|
|
69
69
|
this._gotParams = new Set();
|
|
70
70
|
this._stack = [];
|
|
@@ -206,7 +206,7 @@ module.exports = class Request extends Readable {
|
|
|
206
206
|
if(qp) {
|
|
207
207
|
this.#cachedQuery = qp(this.urlQuery.slice(1));
|
|
208
208
|
} else {
|
|
209
|
-
this.#cachedQuery =
|
|
209
|
+
this.#cachedQuery = new NullObject();
|
|
210
210
|
}
|
|
211
211
|
return this.#cachedQuery;
|
|
212
212
|
}
|
|
@@ -351,7 +351,7 @@ module.exports = class Request extends Readable {
|
|
|
351
351
|
if(this.#cachedHeaders) {
|
|
352
352
|
return this.#cachedHeaders;
|
|
353
353
|
}
|
|
354
|
-
let headers =
|
|
354
|
+
let headers = new NullObject();
|
|
355
355
|
this.#rawHeadersEntries.forEach((val) => {
|
|
356
356
|
const key = val[0].toLowerCase();
|
|
357
357
|
const value = val[1];
|
|
@@ -382,7 +382,7 @@ module.exports = class Request extends Readable {
|
|
|
382
382
|
if(this.#cachedDistinctHeaders) {
|
|
383
383
|
return this.#cachedDistinctHeaders;
|
|
384
384
|
}
|
|
385
|
-
let headers =
|
|
385
|
+
let headers = new NullObject();
|
|
386
386
|
this.#rawHeadersEntries.forEach((val) => {
|
|
387
387
|
if(!headers[val[0]]) {
|
|
388
388
|
headers[val[0]] = [];
|
package/src/response.js
CHANGED
|
@@ -20,7 +20,7 @@ const vary = require("vary");
|
|
|
20
20
|
const encodeUrl = require("encodeurl");
|
|
21
21
|
const {
|
|
22
22
|
normalizeType, stringify, deprecated, UP_PATH_REGEXP, decode,
|
|
23
|
-
containsDotFile, isPreconditionFailure, isRangeFresh,
|
|
23
|
+
containsDotFile, isPreconditionFailure, isRangeFresh, NullObject
|
|
24
24
|
} = require("./utils.js");
|
|
25
25
|
const { Writable } = require("stream");
|
|
26
26
|
const { isAbsolute } = require("path");
|
|
@@ -70,7 +70,7 @@ module.exports = class Response extends Writable {
|
|
|
70
70
|
this._res = res;
|
|
71
71
|
this.headersSent = false;
|
|
72
72
|
this.app = app;
|
|
73
|
-
this.locals =
|
|
73
|
+
this.locals = new NullObject();
|
|
74
74
|
this.finished = false;
|
|
75
75
|
this.aborted = false;
|
|
76
76
|
this.statusCode = 200;
|
|
@@ -208,12 +208,9 @@ module.exports = class Response extends Writable {
|
|
|
208
208
|
_implicitHeader() {
|
|
209
209
|
// compatibility function
|
|
210
210
|
// usually should send headers but this is useless for us
|
|
211
|
-
|
|
211
|
+
this.writeHead(this.statusCode);
|
|
212
212
|
}
|
|
213
213
|
status(code) {
|
|
214
|
-
if(this.headersSent) {
|
|
215
|
-
throw new Error('Can\'t set status: Response was already sent');
|
|
216
|
-
}
|
|
217
214
|
this.statusCode = parseInt(code);
|
|
218
215
|
return this;
|
|
219
216
|
}
|
|
@@ -282,21 +279,23 @@ module.exports = class Response extends Writable {
|
|
|
282
279
|
}
|
|
283
280
|
if(typeof body === 'string') {
|
|
284
281
|
const contentType = this.headers['content-type'];
|
|
285
|
-
if(
|
|
282
|
+
if(!contentType){
|
|
283
|
+
this.type('html'); // string defaulting to html
|
|
284
|
+
} else if(!contentType.includes(';')) {
|
|
286
285
|
this.headers['content-type'] += '; charset=utf-8';
|
|
287
286
|
}
|
|
288
287
|
}
|
|
289
288
|
return this.end(body);
|
|
290
289
|
}
|
|
291
|
-
sendFile(path, options =
|
|
290
|
+
sendFile(path, options = new NullObject(), callback) {
|
|
292
291
|
if(typeof path !== 'string') {
|
|
293
292
|
throw new TypeError('path argument is required to res.sendFile');
|
|
294
293
|
}
|
|
295
294
|
if(typeof options === 'function') {
|
|
296
295
|
callback = options;
|
|
297
|
-
options =
|
|
296
|
+
options = new NullObject();
|
|
298
297
|
}
|
|
299
|
-
if(!options) options =
|
|
298
|
+
if(!options) options = new NullObject();
|
|
300
299
|
let done = callback;
|
|
301
300
|
if(!done) done = this.req.next;
|
|
302
301
|
// default options
|
|
@@ -488,16 +487,16 @@ module.exports = class Response extends Writable {
|
|
|
488
487
|
download(path, filename, options, callback) {
|
|
489
488
|
let done = callback;
|
|
490
489
|
let name = filename;
|
|
491
|
-
let opts = options ||
|
|
490
|
+
let opts = options || new NullObject();
|
|
492
491
|
|
|
493
492
|
// support function as second or third arg
|
|
494
493
|
if (typeof filename === 'function') {
|
|
495
494
|
done = filename;
|
|
496
495
|
name = null;
|
|
497
|
-
opts =
|
|
496
|
+
opts = new NullObject();
|
|
498
497
|
} else if (typeof options === 'function') {
|
|
499
498
|
done = options;
|
|
500
|
-
opts =
|
|
499
|
+
opts = new NullObject();
|
|
501
500
|
}
|
|
502
501
|
|
|
503
502
|
// support optional filename, where options may be in it's place
|
|
@@ -518,7 +517,7 @@ module.exports = class Response extends Writable {
|
|
|
518
517
|
}
|
|
519
518
|
set(field, value) {
|
|
520
519
|
if(this.headersSent) {
|
|
521
|
-
throw new Error('
|
|
520
|
+
throw new Error('Cannot set headers after they are sent to the client');
|
|
522
521
|
}
|
|
523
522
|
if(typeof field === 'object') {
|
|
524
523
|
for(const header in field) {
|
|
@@ -577,10 +576,10 @@ module.exports = class Response extends Writable {
|
|
|
577
576
|
render(view, options, callback) {
|
|
578
577
|
if(typeof options === 'function') {
|
|
579
578
|
callback = options;
|
|
580
|
-
options =
|
|
579
|
+
options = new NullObject();
|
|
581
580
|
}
|
|
582
581
|
if(!options) {
|
|
583
|
-
options =
|
|
582
|
+
options = new NullObject();
|
|
584
583
|
} else {
|
|
585
584
|
options = Object.assign({}, options);
|
|
586
585
|
}
|
|
@@ -594,7 +593,7 @@ module.exports = class Response extends Writable {
|
|
|
594
593
|
}
|
|
595
594
|
cookie(name, value, options) {
|
|
596
595
|
if(!options) {
|
|
597
|
-
options =
|
|
596
|
+
options = new NullObject();
|
|
598
597
|
}
|
|
599
598
|
let val = typeof value === 'object' ? "j:"+JSON.stringify(value) : String(value);
|
|
600
599
|
if(options.maxAge != null) {
|
|
@@ -707,9 +706,7 @@ module.exports = class Response extends Writable {
|
|
|
707
706
|
let ct = type.indexOf('/') === -1
|
|
708
707
|
? (mime.contentType(type) || 'application/octet-stream')
|
|
709
708
|
: type;
|
|
710
|
-
|
|
711
|
-
ct += '; charset=UTF-8';
|
|
712
|
-
}
|
|
709
|
+
|
|
713
710
|
return this.set('content-type', ct);
|
|
714
711
|
}
|
|
715
712
|
contentType(type) {
|
package/src/router.js
CHANGED
|
@@ -18,6 +18,7 @@ const { patternToRegex, needsConversionToRegex, deprecated, findIndexStartingFro
|
|
|
18
18
|
const Response = require("./response.js");
|
|
19
19
|
const Request = require("./request.js");
|
|
20
20
|
const { EventEmitter } = require("tseep");
|
|
21
|
+
const { NullObject } = require("./utils.js");
|
|
21
22
|
|
|
22
23
|
let routeKey = 0;
|
|
23
24
|
|
|
@@ -268,7 +269,7 @@ module.exports = class Router extends EventEmitter {
|
|
|
268
269
|
const fn = async (res, req) => {
|
|
269
270
|
const { request, response } = this.handleRequest(res, req);
|
|
270
271
|
if(route.optimizedParams) {
|
|
271
|
-
request.optimizedParams =
|
|
272
|
+
request.optimizedParams = new NullObject();
|
|
272
273
|
for(let i = 0; i < route.optimizedParams.length; i++) {
|
|
273
274
|
request.optimizedParams[route.optimizedParams[i]] = req.getParameter(i);
|
|
274
275
|
}
|
|
@@ -319,7 +320,7 @@ module.exports = class Router extends EventEmitter {
|
|
|
319
320
|
|
|
320
321
|
_extractParams(pattern, path) {
|
|
321
322
|
let match = pattern.exec(path);
|
|
322
|
-
const obj = match?.groups ??
|
|
323
|
+
const obj = match?.groups ?? new NullObject();
|
|
323
324
|
for(let i = 1; i < match.length; i++) {
|
|
324
325
|
obj[i - 1] = match[i];
|
|
325
326
|
}
|
|
@@ -342,7 +343,7 @@ module.exports = class Router extends EventEmitter {
|
|
|
342
343
|
}
|
|
343
344
|
}
|
|
344
345
|
} else {
|
|
345
|
-
req.params =
|
|
346
|
+
req.params = new NullObject();
|
|
346
347
|
if(req._paramStack.length > 0) {
|
|
347
348
|
for(let params of req._paramStack) {
|
|
348
349
|
req.params = {...params, ...req.params};
|
|
@@ -533,7 +534,7 @@ module.exports = class Router extends EventEmitter {
|
|
|
533
534
|
}
|
|
534
535
|
|
|
535
536
|
route(path) {
|
|
536
|
-
let fns =
|
|
537
|
+
let fns = new NullObject();
|
|
537
538
|
for(let method of methods) {
|
|
538
539
|
fns[method] = (...callbacks) => {
|
|
539
540
|
return this.createRoute(method.toUpperCase(), path, fns, ...callbacks);
|
package/src/types.d.ts
CHANGED
|
@@ -1,4 +1,49 @@
|
|
|
1
|
-
declare module
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
declare module "ultimate-express" {
|
|
2
|
+
import e from "@types/express";
|
|
3
|
+
import { AppOptions } from "uWebSockets.js";
|
|
4
|
+
|
|
5
|
+
type Settings = {
|
|
6
|
+
uwsOptions?: AppOptions;
|
|
7
|
+
threads?: number;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
namespace express {
|
|
11
|
+
export import json = e.json;
|
|
12
|
+
export import raw = e.raw;
|
|
13
|
+
export import text = e.text;
|
|
14
|
+
|
|
15
|
+
// export import application = e.application;
|
|
16
|
+
export import request = e.request;
|
|
17
|
+
export import response = e.response;
|
|
18
|
+
|
|
19
|
+
export import static = e.static;
|
|
20
|
+
// export import query = e.query;
|
|
21
|
+
|
|
22
|
+
export import urlencoded = e.urlencoded;
|
|
23
|
+
|
|
24
|
+
export import RouterOptions = e.RouterOptions;
|
|
25
|
+
export import Application = e.Application;
|
|
26
|
+
export import CookieOptions = e.CookieOptions;
|
|
27
|
+
export import Errback = e.Errback;
|
|
28
|
+
export import ErrorRequestHandler = e.ErrorRequestHandler;
|
|
29
|
+
export import Express = e.Express;
|
|
30
|
+
export import Handler = e.Handler;
|
|
31
|
+
export import IRoute = e.IRoute;
|
|
32
|
+
export import IRouter = e.IRouter;
|
|
33
|
+
export import IRouterHandler = e.IRouterHandler;
|
|
34
|
+
export import IRouterMatcher = e.IRouterMatcher;
|
|
35
|
+
export import MediaType = e.MediaType;
|
|
36
|
+
export import NextFunction = e.NextFunction;
|
|
37
|
+
export import Locals = e.Locals;
|
|
38
|
+
export import Request = e.Request;
|
|
39
|
+
export import RequestHandler = e.RequestHandler;
|
|
40
|
+
export import RequestParamHandler = e.RequestParamHandler;
|
|
41
|
+
export import Response = e.Response;
|
|
42
|
+
export import Router = e.Router;
|
|
43
|
+
export import Send = e.Send;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function express(settings?: Settings): e.Express;
|
|
47
|
+
|
|
48
|
+
export = express;
|
|
49
|
+
}
|
package/src/utils.js
CHANGED
|
@@ -315,6 +315,10 @@ function isRangeFresh(req, res) {
|
|
|
315
315
|
return parseHttpDate(lastModified) <= parseHttpDate(ifRange);
|
|
316
316
|
}
|
|
317
317
|
|
|
318
|
+
// fast null object
|
|
319
|
+
const NullObject = function() {};
|
|
320
|
+
NullObject.prototype = Object.create(null);
|
|
321
|
+
|
|
318
322
|
module.exports = {
|
|
319
323
|
removeDuplicateSlashes,
|
|
320
324
|
patternToRegex,
|
|
@@ -326,6 +330,7 @@ module.exports = {
|
|
|
326
330
|
compileTrust,
|
|
327
331
|
deprecated,
|
|
328
332
|
UP_PATH_REGEXP,
|
|
333
|
+
NullObject,
|
|
329
334
|
decode,
|
|
330
335
|
containsDotFile,
|
|
331
336
|
parseTokenList,
|
package/src/view.js
CHANGED
|
@@ -16,11 +16,12 @@ limitations under the License.
|
|
|
16
16
|
|
|
17
17
|
const path = require("path");
|
|
18
18
|
const fs = require("fs");
|
|
19
|
+
const { NullObject } = require("./utils.js");
|
|
19
20
|
|
|
20
21
|
module.exports = class View {
|
|
21
22
|
constructor(name, options) {
|
|
22
23
|
this.name = name;
|
|
23
|
-
this.options = options ? Object.assign({}, options) :
|
|
24
|
+
this.options = options ? Object.assign({}, options) : new NullObject();
|
|
24
25
|
this.defaultEngine = options.defaultEngine;
|
|
25
26
|
this.ext = path.extname(name);
|
|
26
27
|
this.root = options.root;
|