• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

expressjs / express / 20732483111

05 Jan 2026 11:31PM UTC coverage: 98.756%. First build
20732483111

Pull #2653

github

web-flow
Merge 68a40c373 into b5aae8759
Pull Request #2653: Resolve paths for views asynchronously

51 of 58 new or added lines in 2 files covered. (87.93%)

794 of 804 relevant lines covered (98.76%)

2249.47 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.44
/lib/application.js
1
/*!
2
 * express
3
 * Copyright(c) 2009-2013 TJ Holowaychuk
4
 * Copyright(c) 2013 Roman Shtylman
5
 * Copyright(c) 2014-2015 Douglas Christopher Wilson
6
 * MIT Licensed
7
 */
8

9
'use strict';
10

11
/**
12
 * Module dependencies.
13
 * @private
14
 */
15

16
var finalhandler = require('finalhandler');
8✔
17
var debug = require('debug')('express:application');
8✔
18
var View = require('./view');
8✔
19
var http = require('node:http');
8✔
20
var methods = require('./utils').methods;
8✔
21
var compileETag = require('./utils').compileETag;
8✔
22
var compileQueryParser = require('./utils').compileQueryParser;
8✔
23
var compileTrust = require('./utils').compileTrust;
8✔
24
var resolve = require('node:path').resolve;
8✔
25
var once = require('once')
8✔
26
var Router = require('router');
8✔
27

28
/**
29
 * Module variables.
30
 * @private
31
 */
32

33
var slice = Array.prototype.slice;
8✔
34
var flatten = Array.prototype.flat;
8✔
35

36
/**
37
 * Application prototype.
38
 */
39

40
var app = exports = module.exports = {};
8✔
41

42
/**
43
 * Variable for trust proxy inheritance back-compat
44
 * @private
45
 */
46

47
var trustProxyDefaultSymbol = '@@symbol:trust_proxy_default';
8✔
48

49
/**
50
 * Initialize the server.
51
 *
52
 *   - setup default configuration
53
 *   - setup default middleware
54
 *   - setup route reflection methods
55
 *
56
 * @private
57
 */
58

59
app.init = function init() {
8✔
60
  var router = null;
7,566✔
61

62
  this.cache = Object.create(null);
7,566✔
63
  this.engines = Object.create(null);
7,566✔
64
  this.settings = Object.create(null);
7,566✔
65

66
  this.defaultConfiguration();
7,566✔
67

68
  // Setup getting to lazily add base router
69
  Object.defineProperty(this, 'router', {
7,566✔
70
    configurable: true,
71
    enumerable: true,
72
    get: function getrouter() {
73
      if (router === null) {
19,830✔
74
        router = new Router({
6,886✔
75
          caseSensitive: this.enabled('case sensitive routing'),
76
          strict: this.enabled('strict routing')
77
        });
78
      }
79

80
      return router;
19,830✔
81
    }
82
  });
83
};
84

85
/**
86
 * Initialize application configuration.
87
 * @private
88
 */
89

90
app.defaultConfiguration = function defaultConfiguration() {
8✔
91
  var env = process.env.NODE_ENV || 'development';
7,566✔
92

93
  // default settings
94
  this.enable('x-powered-by');
7,566✔
95
  this.set('etag', 'weak');
7,566✔
96
  this.set('env', env);
7,566✔
97
  this.set('query parser', 'simple')
7,566✔
98
  this.set('subdomain offset', 2);
7,566✔
99
  this.set('trust proxy', false);
7,566✔
100

101
  // trust proxy inherit back-compat
102
  Object.defineProperty(this.settings, trustProxyDefaultSymbol, {
7,566✔
103
    configurable: true,
104
    value: true
105
  });
106

107
  debug('booting in %s mode', env);
7,566✔
108

109
  this.on('mount', function onmount(parent) {
7,566✔
110
    // inherit trust proxy
111
    if (this.settings[trustProxyDefaultSymbol] === true
248✔
112
      && typeof parent.settings['trust proxy fn'] === 'function') {
113
      delete this.settings['trust proxy'];
240✔
114
      delete this.settings['trust proxy fn'];
240✔
115
    }
116

117
    // inherit protos
118
    Object.setPrototypeOf(this.request, parent.request)
248✔
119
    Object.setPrototypeOf(this.response, parent.response)
248✔
120
    Object.setPrototypeOf(this.engines, parent.engines)
248✔
121
    Object.setPrototypeOf(this.settings, parent.settings)
248✔
122
  });
123

124
  // setup locals
125
  this.locals = Object.create(null);
7,566✔
126

127
  // top-most app is mounted at /
128
  this.mountpath = '/';
7,566✔
129

130
  // default locals
131
  this.locals.settings = this.settings;
7,566✔
132

133
  // default configuration
134
  this.set('view', View);
7,566✔
135
  this.set('views', resolve('views'));
7,566✔
136
  this.set('jsonp callback name', 'callback');
7,566✔
137

138
  if (env === 'production') {
7,566✔
139
    this.enable('view cache');
8✔
140
  }
141
};
142

143
/**
144
 * Dispatch a req, res pair into the application. Starts pipeline processing.
145
 *
146
 * If no callback is provided, then default error handlers will respond
147
 * in the event of an error bubbling through the stack.
148
 *
149
 * @private
150
 */
151

152
app.handle = function handle(req, res, callback) {
8✔
153
  // final handler
154
  var done = callback || finalhandler(req, res, {
9,232✔
155
    env: this.get('env'),
156
    onerror: logerror.bind(this)
157
  });
158

159
  // set powered by header
160
  if (this.enabled('x-powered-by')) {
9,232✔
161
    res.setHeader('X-Powered-By', 'Express');
9,208✔
162
  }
163

164
  // set circular references
165
  req.res = res;
9,232✔
166
  res.req = req;
9,232✔
167

168
  // alter the prototypes
169
  Object.setPrototypeOf(req, this.request)
9,232✔
170
  Object.setPrototypeOf(res, this.response)
9,232✔
171

172
  // setup locals
173
  if (!res.locals) {
9,232✔
174
    res.locals = Object.create(null);
8,744✔
175
  }
176

177
  this.router.handle(req, res, done);
9,232✔
178
};
179

180
/**
181
 * Proxy `Router#use()` to add middleware to the app router.
182
 * See Router#use() documentation for details.
183
 *
184
 * If the _fn_ parameter is an express app, then it will be
185
 * mounted at the _route_ specified.
186
 *
187
 * @public
188
 */
189

190
app.use = function use(fn) {
8✔
191
  var offset = 0;
6,912✔
192
  var path = '/';
6,912✔
193

194
  // default path to '/'
195
  // disambiguate app.use([fn])
196
  if (typeof fn !== 'function') {
6,912✔
197
    var arg = fn;
488✔
198

199
    while (Array.isArray(arg) && arg.length !== 0) {
488✔
200
      arg = arg[0];
48✔
201
    }
202

203
    // first arg is the path
204
    if (typeof arg !== 'function') {
488✔
205
      offset = 1;
464✔
206
      path = fn;
464✔
207
    }
208
  }
209

210
  var fns = flatten.call(slice.call(arguments, offset), Infinity);
6,912✔
211

212
  if (fns.length === 0) {
6,912✔
213
    throw new TypeError('app.use() requires a middleware function')
8✔
214
  }
215

216
  // get router
217
  var router = this.router;
6,904✔
218

219
  fns.forEach(function (fn) {
6,904✔
220
    // non-express app
221
    if (!fn || !fn.handle || !fn.set) {
7,072✔
222
      return router.use(path, fn);
6,824✔
223
    }
224

225
    debug('.use app under %s', path);
248✔
226
    fn.mountpath = path;
248✔
227
    fn.parent = this;
248✔
228

229
    // restore .app property on req and res
230
    router.use(path, function mounted_app(req, res, next) {
248✔
231
      var orig = req.app;
456✔
232
      fn.handle(req, res, function (err) {
456✔
233
        Object.setPrototypeOf(req, orig.request)
256✔
234
        Object.setPrototypeOf(res, orig.response)
256✔
235
        next(err);
256✔
236
      });
237
    });
238

239
    // mounted an app
240
    fn.emit('mount', this);
248✔
241
  }, this);
242

243
  return this;
6,872✔
244
};
245

246
/**
247
 * Proxy to the app `Router#route()`
248
 * Returns a new `Route` instance for the _path_.
249
 *
250
 * Routes are isolated middleware stacks for specific paths.
251
 * See the Route api docs for details.
252
 *
253
 * @public
254
 */
255

256
app.route = function route(path) {
8✔
257
  return this.router.route(path);
3,542✔
258
};
259

260
/**
261
 * Register the given template engine callback `fn`
262
 * as `ext`.
263
 *
264
 * By default will `require()` the engine based on the
265
 * file extension. For example if you try to render
266
 * a "foo.ejs" file Express will invoke the following internally:
267
 *
268
 *     app.engine('ejs', require('ejs').__express);
269
 *
270
 * For engines that do not provide `.__express` out of the box,
271
 * or if you wish to "map" a different extension to the template engine
272
 * you may use this method. For example mapping the EJS template engine to
273
 * ".html" files:
274
 *
275
 *     app.engine('html', require('ejs').renderFile);
276
 *
277
 * In this case EJS provides a `.renderFile()` method with
278
 * the same signature that Express expects: `(path, options, callback)`,
279
 * though note that it aliases this method as `ejs.__express` internally
280
 * so if you're using ".ejs" extensions you don't need to do anything.
281
 *
282
 * Some template engines do not follow this convention, the
283
 * [Consolidate.js](https://github.com/tj/consolidate.js)
284
 * library was created to map all of node's popular template
285
 * engines to follow this convention, thus allowing them to
286
 * work seamlessly within Express.
287
 *
288
 * @param {String} ext
289
 * @param {Function} fn
290
 * @return {app} for chaining
291
 * @public
292
 */
293

294
app.engine = function engine(ext, fn) {
8✔
295
  if (typeof fn !== 'function') {
344✔
296
    throw new Error('callback function required');
8✔
297
  }
298

299
  // get file extension
300
  var extension = ext[0] !== '.'
336✔
301
    ? '.' + ext
302
    : ext;
303

304
  // store engine
305
  this.engines[extension] = fn;
336✔
306

307
  return this;
336✔
308
};
309

310
/**
311
 * Proxy to `Router#param()` with one added api feature. The _name_ parameter
312
 * can be an array of names.
313
 *
314
 * See the Router#param() docs for more details.
315
 *
316
 * @param {String|Array} name
317
 * @param {Function} fn
318
 * @return {app} for chaining
319
 * @public
320
 */
321

322
app.param = function param(name, fn) {
8✔
323
  if (Array.isArray(name)) {
168✔
324
    for (var i = 0; i < name.length; i++) {
16✔
325
      this.param(name[i], fn);
32✔
326
    }
327

328
    return this;
16✔
329
  }
330

331
  this.router.param(name, fn);
152✔
332

333
  return this;
152✔
334
};
335

336
/**
337
 * Assign `setting` to `val`, or return `setting`'s value.
338
 *
339
 *    app.set('foo', 'bar');
340
 *    app.set('foo');
341
 *    // => "bar"
342
 *
343
 * Mounted servers inherit their parent server's settings.
344
 *
345
 * @param {String} setting
346
 * @param {*} [val]
347
 * @return {Server} for chaining
348
 * @public
349
 */
350

351
app.set = function set(setting, val) {
8✔
352
  if (arguments.length === 1) {
141,092✔
353
    // app.get(setting)
354
    return this.settings[setting];
48,572✔
355
  }
356

357
  debug('set "%s" to %o', setting, val);
92,520✔
358

359
  // set value
360
  this.settings[setting] = val;
92,520✔
361

362
  // trigger matched settings
363
  switch (setting) {
92,520✔
364
    case 'etag':
365
      this.set('etag fn', compileETag(val));
7,678✔
366
      break;
7,670✔
367
    case 'query parser':
368
      this.set('query parser fn', compileQueryParser(val));
7,622✔
369
      break;
7,614✔
370
    case 'trust proxy':
371
      this.set('trust proxy fn', compileTrust(val));
7,814✔
372

373
      // trust proxy inherit back-compat
374
      Object.defineProperty(this.settings, trustProxyDefaultSymbol, {
7,814✔
375
        configurable: true,
376
        value: false
377
      });
378

379
      break;
7,814✔
380
  }
381

382
  return this;
92,504✔
383
};
384

385
/**
386
 * Return the app's absolute pathname
387
 * based on the parent(s) that have
388
 * mounted it.
389
 *
390
 * For example if the application was
391
 * mounted as "/admin", which itself
392
 * was mounted as "/blog" then the
393
 * return value would be "/blog/admin".
394
 *
395
 * @return {String}
396
 * @private
397
 */
398

399
app.path = function path() {
8✔
400
  return this.parent
48✔
401
    ? this.parent.path() + this.mountpath
402
    : '';
403
};
404

405
/**
406
 * Check if `setting` is enabled (truthy).
407
 *
408
 *    app.enabled('foo')
409
 *    // => false
410
 *
411
 *    app.enable('foo')
412
 *    app.enabled('foo')
413
 *    // => true
414
 *
415
 * @param {String} setting
416
 * @return {Boolean}
417
 * @public
418
 */
419

420
app.enabled = function enabled(setting) {
8✔
421
  return Boolean(this.set(setting));
24,340✔
422
};
423

424
/**
425
 * Check if `setting` is disabled.
426
 *
427
 *    app.disabled('foo')
428
 *    // => true
429
 *
430
 *    app.enable('foo')
431
 *    app.disabled('foo')
432
 *    // => false
433
 *
434
 * @param {String} setting
435
 * @return {Boolean}
436
 * @public
437
 */
438

439
app.disabled = function disabled(setting) {
8✔
440
  return !this.set(setting);
24✔
441
};
442

443
/**
444
 * Enable `setting`.
445
 *
446
 * @param {String} setting
447
 * @return {app} for chaining
448
 * @public
449
 */
450

451
app.enable = function enable(setting) {
8✔
452
  return this.set(setting, true);
7,886✔
453
};
454

455
/**
456
 * Disable `setting`.
457
 *
458
 * @param {String} setting
459
 * @return {app} for chaining
460
 * @public
461
 */
462

463
app.disable = function disable(setting) {
8✔
464
  return this.set(setting, false);
64✔
465
};
466

467
/**
468
 * Delegate `.VERB(...)` calls to `router.VERB(...)`.
469
 */
470

471
methods.forEach(function (method) {
8✔
472
  app[method] = function (path) {
278✔
473
    if (method === 'get' && arguments.length === 1) {
27,606✔
474
      // app.get(setting)
475
      return this.set(path);
24,192✔
476
    }
477

478
    var route = this.route(path);
3,414✔
479
    route[method].apply(route, slice.call(arguments, 1));
3,414✔
480
    return this;
3,144✔
481
  };
482
});
483

484
/**
485
 * Special-cased "all" method, applying the given route `path`,
486
 * middleware, and callback to _every_ HTTP method.
487
 *
488
 * @param {String} path
489
 * @param {Function} ...
490
 * @return {app} for chaining
491
 * @public
492
 */
493

494
app.all = function all(path) {
8✔
495
  var route = this.route(path);
48✔
496
  var args = slice.call(arguments, 1);
48✔
497

498
  for (var i = 0; i < methods.length; i++) {
48✔
499
    route[methods[i]].apply(route, args);
1,668✔
500
  }
501

502
  return this;
48✔
503
};
504

505
/**
506
 * Render the given view `name` name with `options`
507
 * and a callback accepting an error and the
508
 * rendered template string.
509
 *
510
 * Example:
511
 *
512
 *    app.render('email', { name: 'Tobi' }, function(err, html){
513
 *      // ...
514
 *    })
515
 *
516
 * @param {String} name
517
 * @param {Object|Function} options or fn
518
 * @param {Function} callback
519
 * @public
520
 */
521

522
app.render = function render(name, options, callback) {
8✔
523
  var cache = this.cache;
648✔
524
  var done = callback;
648✔
525
  var engines = this.engines;
648✔
526
  var opts = options;
648✔
527
  var view;
528

529
  // support callback function as second arg
530
  if (typeof options === 'function') {
648✔
531
    done = options;
176✔
532
    opts = {};
176✔
533
  }
534

535
  // merge options
536
  var renderOptions = { ...this.locals, ...opts._locals, ...opts };
648✔
537

538
  // set .cache unless explicitly provided
539
  if (renderOptions.cache == null) {
648✔
540
    renderOptions.cache = this.enabled('view cache');
632✔
541
  }
542

543
  // primed cache
544
  if (renderOptions.cache) {
648✔
545
    view = cache[name];
32✔
546
  }
547

548
  // view
549
  if (!view) {
648✔
550
    var View = this.get('view');
632✔
551

552
    view = new View(name, {
632✔
553
      defaultEngine: this.get('view engine'),
554
      root: this.get('views'),
555
      engines: engines
556
    });
557
  }
558

559
  if (!view.path) {
632✔
560
    view.lookupMain(function (err) {
568✔
561
      if (err) return done(err);
568✔
562

563
      // prime the cache
564
      if (renderOptions.cache) {
544✔
NEW
565
        cache[name] = view;
×
566
      }
567

568
      // render
569
      tryRender(view, renderOptions, done);
544✔
570
    });
571
  } else {
572
    // prime the cache
573
    if (renderOptions.cache) {
64✔
574
      cache[name] = view;
32✔
575
    }
576

577
    // render
578
    tryRender(view, renderOptions, done);
64✔
579
  }
580
}
581

582
/**
583
 * Listen for connections.
584
 *
585
 * A node `http.Server` is returned, with this
586
 * application (which is a `Function`) as its
587
 * callback. If you wish to create both an HTTP
588
 * and HTTPS server you may do so with the "http"
589
 * and "https" modules as shown here:
590
 *
591
 *    var http = require('node:http')
592
 *      , https = require('node:https')
593
 *      , express = require('express')
594
 *      , app = express();
595
 *
596
 *    http.createServer(app).listen(80);
597
 *    https.createServer({ ... }, app).listen(443);
598
 *
599
 * @return {http.Server}
600
 * @public
601
 */
602

603
app.listen = function listen() {
8✔
604
  var server = http.createServer(this)
72✔
605
  var args = slice.call(arguments)
72✔
606
  if (typeof args[args.length - 1] === 'function') {
72✔
607
    var done = args[args.length - 1] = once(args[args.length - 1])
40✔
608
    server.once('error', done)
40✔
609
  }
610
  return server.listen.apply(server, args)
72✔
611
}
612

613
/**
614
 * Log error using console.error.
615
 *
616
 * @param {Error} err
617
 * @private
618
 */
619

620
function logerror(err) {
621
  /* istanbul ignore next */
622
  if (this.get('env') !== 'test') console.error(err.stack || err.toString());
623
}
624

625
/**
626
 * Try rendering a view.
627
 * @private
628
 */
629

630
function tryRender(view, options, callback) {
631
  try {
608✔
632
    view.render(options, callback);
608✔
633
  } catch (err) {
634
    callback(err);
8✔
635
  }
636
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc