diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 62562b74a3b..00000000000 --- a/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -coverage -node_modules diff --git a/.eslintrc.yml b/.eslintrc.yml deleted file mode 100644 index f9359bf2892..00000000000 --- a/.eslintrc.yml +++ /dev/null @@ -1,14 +0,0 @@ -root: true -env: - es2022: true - node: true -rules: - eol-last: error - eqeqeq: [error, allow-null] - indent: [error, 2, { MemberExpression: "off", SwitchCase: 1 }] - no-trailing-spaces: error - no-unused-vars: [error, { vars: all, args: none, ignoreRestSiblings: true }] - no-restricted-globals: - - error - - name: Buffer - message: Use `import { Buffer } from "node:buffer"` instead of the global Buffer. diff --git a/benchmarks/middleware.js b/benchmarks/middleware.js index fed97ba8ce4..e05ec643e81 100644 --- a/benchmarks/middleware.js +++ b/benchmarks/middleware.js @@ -1,20 +1,19 @@ - -var express = require('..'); -var app = express(); +const express = require('../') +const app = express() // number of middleware -var n = parseInt(process.env.MW || '1', 10); -console.log(' %s middleware', n); +let n = parseInt(process.env.MW || '1', 10) +console.log(' %s middleware', n) while (n--) { - app.use(function(req, res, next){ - next(); - }); + app.use((req, res, next) => { + next() + }) } -app.use(function(req, res){ +app.use((req, res) => { res.send('Hello World') -}); +}) -app.listen(3333); +app.listen(3333) diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000000..66661be5427 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,23 @@ +import globals from 'globals' +import neostandard from 'neostandard' + +// globals present in Node.js but not in browsers. +const nodeGlobals = Object.keys(globals.node).filter(g => !Object.keys(globals.browser).includes(g)) + +// Node.js-specific globals that are allowed. +const allowedGlobals = [ + 'require', 'module', 'exports', '__dirname', 'process', 'setImmediate' +] + +export default [ + ...neostandard({ + env: ['mocha'] + }), + { + rules: { + 'global-require': 'warn', + 'no-restricted-globals': ['error', ...nodeGlobals.filter(g => !allowedGlobals.includes(g))], + 'n/handle-callback-err': 'off' + } + } +] diff --git a/examples/auth/index.js b/examples/auth/index.js index 40b73e6de16..a6e2d641f4f 100644 --- a/examples/auth/index.js +++ b/examples/auth/index.js @@ -4,17 +4,17 @@ * Module dependencies. */ -var express = require('../..'); -var hash = require('pbkdf2-password')() -var path = require('node:path'); -var session = require('express-session'); +const express = require('../../') +const hash = require('pbkdf2-password')() +const path = require('node:path') +const session = require('express-session') -var app = module.exports = express(); +const app = module.exports = express() // config -app.set('view engine', 'ejs'); -app.set('views', path.join(__dirname, 'views')); +app.set('view engine', 'ejs') +app.set('views', path.join(__dirname, 'views')) // middleware @@ -23,112 +23,111 @@ app.use(session({ resave: false, // don't save session if unmodified saveUninitialized: false, // don't create session until something stored secret: 'shhhh, very secret' -})); +})) // Session-persisted message middleware -app.use(function(req, res, next){ - var err = req.session.error; - var msg = req.session.success; - delete req.session.error; - delete req.session.success; - res.locals.message = ''; - if (err) res.locals.message = '
' + err + '
'; - if (msg) res.locals.message = '' + msg + '
'; - next(); -}); +app.use((req, res, next) => { + const err = req.session.error + const msg = req.session.success + delete req.session.error + delete req.session.success + res.locals.message = '' + if (err) res.locals.message = '' + err + '
' + if (msg) res.locals.message = '' + msg + '
' + next() +}) // dummy database -var users = { +const users = { tj: { name: 'tj' } -}; +} // when you create a user, generate a salt // and hash the password ('foobar' is the pass here) -hash({ password: 'foobar' }, function (err, pass, salt, hash) { - if (err) throw err; +hash({ password: 'foobar' }, (err, pass, salt, hash) => { + if (err) throw err // store the salt & hash in the "db" - users.tj.salt = salt; - users.tj.hash = hash; -}); - + users.tj.salt = salt + users.tj.hash = hash +}) // Authenticate using our plain-object database of doom! -function authenticate(name, pass, fn) { - if (!module.parent) console.log('authenticating %s:%s', name, pass); - var user = users[name]; +function authenticate (name, pass, fn) { + if (!module.parent) console.log('authenticating %s:%s', name, pass) + const user = users[name] // query the db for the given username if (!user) return fn(null, null) // apply the same algorithm to the POSTed password, applying // the hash against the pass / salt, if there is a match we // found the user - hash({ password: pass, salt: user.salt }, function (err, pass, salt, hash) { - if (err) return fn(err); + hash({ password: pass, salt: user.salt }, (err, pass, salt, hash) => { + if (err) return fn(err) if (hash === user.hash) return fn(null, user) fn(null, null) - }); + }) } -function restrict(req, res, next) { +function restrict (req, res, next) { if (req.session.user) { - next(); + next() } else { - req.session.error = 'Access denied!'; - res.redirect('/login'); + req.session.error = 'Access denied!' + res.redirect('/login') } } -app.get('/', function(req, res){ - res.redirect('/login'); -}); +app.get('/', (req, res) => { + res.redirect('/login') +}) -app.get('/restricted', restrict, function(req, res){ - res.send('Wahoo! restricted area, click to logout'); -}); +app.get('/restricted', restrict, (req, res) => { + res.send('Wahoo! restricted area, click to logout') +}) -app.get('/logout', function(req, res){ +app.get('/logout', (req, res) => { // destroy the user's session to log them out // will be re-created next request - req.session.destroy(function(){ - res.redirect('/'); - }); -}); + req.session.destroy(() => { + res.redirect('/') + }) +}) -app.get('/login', function(req, res){ - res.render('login'); -}); +app.get('/login', (req, res) => { + res.render('login') +}) -app.post('/login', function (req, res, next) { +app.post('/login', (req, res, next) => { if (!req.body) return res.sendStatus(400) - authenticate(req.body.username, req.body.password, function(err, user){ + authenticate(req.body.username, req.body.password, (err, user) => { if (err) return next(err) if (user) { // Regenerate session when signing in // to prevent fixation - req.session.regenerate(function(){ + req.session.regenerate(() => { // Store the user's primary key // in the session store to be retrieved, // or in this case the entire user object - req.session.user = user; - req.session.success = 'Authenticated as ' + user.name - + ' click to logout. ' - + ' You may now access /restricted.'; - res.redirect(req.get('Referrer') || '/'); - }); + req.session.user = user + req.session.success = 'Authenticated as ' + user.name + + ' click to logout. ' + + ' You may now access /restricted.' + res.redirect(req.get('Referrer') || '/') + }) } else { - req.session.error = 'Authentication failed, please check your ' - + ' username and password.' - + ' (use "tj" and "foobar")'; - res.redirect('/login'); + req.session.error = 'Authentication failed, please check your ' + + ' username and password.' + + ' (use "tj" and "foobar")' + res.redirect('/login') } - }); -}); + }) +}) /* istanbul ignore next */ if (!module.parent) { - app.listen(3000); - console.log('Express started on port 3000'); + app.listen(3000) + console.log('Express started on port 3000') } diff --git a/examples/content-negotiation/db.js b/examples/content-negotiation/db.js index f59b23bf18e..501e84966a1 100644 --- a/examples/content-negotiation/db.js +++ b/examples/content-negotiation/db.js @@ -1,9 +1,9 @@ 'use strict' -var users = []; +const users = [] -users.push({ name: 'Tobi' }); -users.push({ name: 'Loki' }); -users.push({ name: 'Jane' }); +users.push({ name: 'Tobi' }) +users.push({ name: 'Loki' }) +users.push({ name: 'Jane' }) -module.exports = users; +module.exports = users diff --git a/examples/content-negotiation/index.js b/examples/content-negotiation/index.js index 280a4e22998..c417499d07a 100644 --- a/examples/content-negotiation/index.js +++ b/examples/content-negotiation/index.js @@ -1,46 +1,43 @@ 'use strict' -var express = require('../../'); -var app = module.exports = express(); -var users = require('./db'); +const express = require('../../') +const app = module.exports = express() +const users = require('./db') // so either you can deal with different types of formatting // for expected response in index.js -app.get('/', function(req, res){ +app.get('/', (req, res) => { res.format({ - html: function(){ - res.send('Users online: ' + ids.length + '
' + list(ids)); - }); -}); +app.get('/', (req, res, next) => { + online.last(5, (err, ids) => { + if (err) return next(err) + res.send('Users online: ' + ids.length + '
' + list(ids)) + }) +}) /* istanbul ignore next */ if (!module.parent) { - app.listen(3000); - console.log('Express started on port 3000'); + app.listen(3000) + console.log('Express started on port 3000') } diff --git a/examples/params/index.js b/examples/params/index.js index 11eef51a592..d565ac5e373 100644 --- a/examples/params/index.js +++ b/examples/params/index.js @@ -4,71 +4,71 @@ * Module dependencies. */ -var createError = require('http-errors') -var express = require('../../'); -var app = module.exports = express(); +const createError = require('http-errors') +const express = require('../../') +const app = module.exports = express() // Faux database -var users = [ - { name: 'tj' } - , { name: 'tobi' } - , { name: 'loki' } - , { name: 'jane' } - , { name: 'bandit' } -]; +const users = [ + { name: 'tj' }, + { name: 'tobi' }, + { name: 'loki' }, + { name: 'jane' }, + { name: 'bandit' } +] // Convert :to and :from to integers -app.param(['to', 'from'], function(req, res, next, num, name){ - req.params[name] = parseInt(num, 10); - if( isNaN(req.params[name]) ){ - next(createError(400, 'failed to parseInt '+num)); +app.param(['to', 'from'], (req, res, next, num, name) => { + req.params[name] = parseInt(num, 10) + if (isNaN(req.params[name])) { + next(createError(400, 'failed to parseInt ' + num)) } else { - next(); + next() } -}); +}) // Load user by id -app.param('user', function(req, res, next, id){ +app.param('user', (req, res, next, id) => { req.user = users[id] if (req.user) { - next(); + next() } else { - next(createError(404, 'failed to find user')); + next(createError(404, 'failed to find user')) } -}); +}) /** * GET index. */ -app.get('/', function(req, res){ - res.send('Visit /user/0 or /users/0-2'); -}); +app.get('/', (req, res) => { + res.send('Visit /user/0 or /users/0-2') +}) /** * GET :user. */ -app.get('/user/:user', function (req, res) { - res.send('user ' + req.user.name); -}); +app.get('/user/:user', (req, res) => { + res.send('user ' + req.user.name) +}) /** * GET users :from - :to. */ -app.get('/users/:from-:to', function (req, res) { - var from = req.params.from; - var to = req.params.to; - var names = users.map(function(user){ return user.name; }); - res.send('users ' + names.slice(from, to + 1).join(', ')); -}); +app.get('/users/:from-:to', (req, res) => { + const from = req.params.from + const to = req.params.to + const names = users.map((user) => user.name) + res.send('users ' + names.slice(from, to + 1).join(', ')) +}) /* istanbul ignore next */ if (!module.parent) { - app.listen(3000); - console.log('Express started on port 3000'); + app.listen(3000) + console.log('Express started on port 3000') } diff --git a/examples/resource/index.js b/examples/resource/index.js index 627ab24c5a2..bea4411843c 100644 --- a/examples/resource/index.js +++ b/examples/resource/index.js @@ -4,68 +4,65 @@ * Module dependencies. */ -var express = require('../../'); +const express = require('../../') -var app = module.exports = express(); +const app = module.exports = express() // Ad-hoc example resource method -app.resource = function(path, obj) { - this.get(path, obj.index); - this.get(path + '/:a..:b{.:format}', function(req, res){ - var a = parseInt(req.params.a, 10); - var b = parseInt(req.params.b, 10); - var format = req.params.format; - obj.range(req, res, a, b, format); - }); - this.get(path + '/:id', obj.show); - this.delete(path + '/:id', function(req, res){ - var id = parseInt(req.params.id, 10); - obj.destroy(req, res, id); - }); -}; +app.resource = function (path, obj) { + this.get(path, obj.index) + this.get(path + '/:a..:b{.:format}', (req, res) => { + const a = parseInt(req.params.a, 10) + const b = parseInt(req.params.b, 10) + const format = req.params.format + obj.range(req, res, a, b, format) + }) + this.get(path + '/:id', obj.show) + this.delete(path + '/:id', (req, res) => { + const id = parseInt(req.params.id, 10) + obj.destroy(req, res, id) + }) +} // Fake records -var users = [ - { name: 'tj' } - , { name: 'ciaran' } - , { name: 'aaron' } - , { name: 'guillermo' } - , { name: 'simon' } - , { name: 'tobi' } -]; +const users = [ + { name: 'tj' }, + { name: 'ciaran' }, + { name: 'aaron' }, + { name: 'guillermo' }, + { name: 'simon' }, + { name: 'tobi' } +] // Fake controller. -var User = { - index: function(req, res){ - res.send(users); +const User = { + index: function (req, res) { + res.send(users) }, - show: function(req, res){ - res.send(users[req.params.id] || { error: 'Cannot find user' }); + show: function (req, res) { + res.send(users[req.params.id] || { error: 'Cannot find user' }) }, - destroy: function(req, res, id){ - var destroyed = id in users; - delete users[id]; - res.send(destroyed ? 'destroyed' : 'Cannot find user'); + destroy: function (req, res, id) { + const destroyed = id in users + delete users[id] + res.send(destroyed ? 'destroyed' : 'Cannot find user') }, - range: function(req, res, a, b, format){ - var range = users.slice(a, b + 1); + range: function (req, res, a, b, format) { + const range = users.slice(a, b + 1) switch (format) { case 'json': - res.send(range); - break; + res.send(range) + break case 'html': default: - var html = 'First time visiting? view this page in several browsers :)
'; + req.session.views = 1 + body += 'First time visiting? view this page in several browsers :)
' } - res.send(body + 'viewed ' + req.session.views + ' times.
'); -}); + res.send(body + 'viewed ' + req.session.views + ' times.
') +}) /* istanbul ignore next */ if (!module.parent) { - app.listen(3000); - console.log('Express started on port 3000'); + app.listen(3000) + console.log('Express started on port 3000') } diff --git a/examples/session/redis.js b/examples/session/redis.js index bbbdc7fd3e5..f967971db03 100644 --- a/examples/session/redis.js +++ b/examples/session/redis.js @@ -4,36 +4,36 @@ * Module dependencies. */ -var express = require('../..'); -var logger = require('morgan'); -var session = require('express-session'); +const express = require('../../') +const logger = require('morgan') +const session = require('express-session') // pass the express to the connect redis module // allowing it to inherit from session.Store -var RedisStore = require('connect-redis')(session); +const RedisStore = require('connect-redis')(session) -var app = express(); +const app = express() -app.use(logger('dev')); +app.use(logger('dev')) // Populates req.session app.use(session({ resave: false, // don't save session if unmodified saveUninitialized: false, // don't create session until something stored secret: 'keyboard cat', - store: new RedisStore -})); + store: new RedisStore() +})) -app.get('/', function(req, res){ - var body = ''; +app.get('/', (req, res) => { + let body = '' if (req.session.views) { - ++req.session.views; + ++req.session.views } else { - req.session.views = 1; - body += 'First time visiting? view this page in several browsers :)
'; + req.session.views = 1 + body += 'First time visiting? view this page in several browsers :)
' } - res.send(body + 'viewed ' + req.session.views + ' times.
'); -}); + res.send(body + 'viewed ' + req.session.views + ' times.
') +}) -app.listen(3000); -console.log('Express app started on port 3000'); +app.listen(3000) +console.log('Express app started on port 3000') diff --git a/examples/static-files/index.js b/examples/static-files/index.js index b7c697a2f9f..89540176bde 100644 --- a/examples/static-files/index.js +++ b/examples/static-files/index.js @@ -4,13 +4,13 @@ * Module dependencies. */ -var express = require('../..'); -var logger = require('morgan'); -var path = require('node:path'); -var app = express(); +const express = require('../../') +const logger = require('morgan') +const path = require('node:path') +const app = express() // log requests -app.use(logger('dev')); +app.use(logger('dev')) // express on its own has no notion // of a "file". The express.static() @@ -19,7 +19,7 @@ app.use(logger('dev')); // that you pass it. In this case "GET /js/app.js" // will look for "./public/js/app.js". -app.use(express.static(path.join(__dirname, 'public'))); +app.use(express.static(path.join(__dirname, 'public'))) // if you wanted to "prefix" you may use // the mounting feature of Connect, for example @@ -27,17 +27,17 @@ app.use(express.static(path.join(__dirname, 'public'))); // The mount-path "/static" is simply removed before // passing control to the express.static() middleware, // thus it serves the file correctly by ignoring "/static" -app.use('/static', express.static(path.join(__dirname, 'public'))); +app.use('/static', express.static(path.join(__dirname, 'public'))) // if for some reason you want to serve files from // several directories, you can use express.static() // multiple times! Here we're passing "./public/css", // this will allow "GET /style.css" instead of "GET /css/style.css": -app.use(express.static(path.join(__dirname, 'public', 'css'))); - -app.listen(3000); -console.log('listening on port 3000'); -console.log('try:'); -console.log(' GET /hello.txt'); -console.log(' GET /js/app.js'); -console.log(' GET /css/style.css'); +app.use(express.static(path.join(__dirname, 'public', 'css'))) + +app.listen(3000) +console.log('listening on port 3000') +console.log('try:') +console.log(' GET /hello.txt') +console.log(' GET /js/app.js') +console.log(' GET /css/style.css') diff --git a/examples/vhost/index.js b/examples/vhost/index.js index a9499356b42..d63400d953b 100644 --- a/examples/vhost/index.js +++ b/examples/vhost/index.js @@ -4,9 +4,9 @@ * Module dependencies. */ -var express = require('../..'); -var logger = require('morgan'); -var vhost = require('vhost'); +const express = require('../../') +const logger = require('morgan') +const vhost = require('vhost') /* edit /etc/hosts: @@ -18,36 +18,36 @@ edit /etc/hosts: // Main server app -var main = express(); +const main = express() -if (!module.parent) main.use(logger('dev')); +if (!module.parent) main.use(logger('dev')) -main.get('/', function(req, res){ - res.send('Hello from main app!'); -}); +main.get('/', (req, res) => { + res.send('Hello from main app!') +}) -main.get('/:sub', function(req, res){ - res.send('requested ' + req.params.sub); -}); +main.get('/:sub', (req, res) => { + res.send('requested ' + req.params.sub) +}) // Redirect app -var redirect = express(); +const redirect = express() -redirect.use(function(req, res){ - if (!module.parent) console.log(req.vhost); - res.redirect('http://example.com:3000/' + req.vhost[0]); -}); +redirect.use((req, res) => { + if (!module.parent) console.log(req.vhost) + res.redirect('http://example.com:3000/' + req.vhost[0]) +}) // Vhost app -var app = module.exports = express(); +const app = module.exports = express() -app.use(vhost('*.example.com', redirect)); // Serves all subdomains via Redirect app -app.use(vhost('example.com', main)); // Serves top level domain via Main server app +app.use(vhost('*.example.com', redirect)) // Serves all subdomains via Redirect app +app.use(vhost('example.com', main)) // Serves top level domain via Main server app /* istanbul ignore next */ if (!module.parent) { - app.listen(3000); - console.log('Express started on port 3000'); + app.listen(3000) + console.log('Express started on port 3000') } diff --git a/examples/view-constructor/github-view.js b/examples/view-constructor/github-view.js index eabfb2d0c18..58bb92adfdf 100644 --- a/examples/view-constructor/github-view.js +++ b/examples/view-constructor/github-view.js @@ -4,15 +4,15 @@ * Module dependencies. */ -var https = require('node:https'); -var path = require('node:path'); -var extname = path.extname; +const https = require('node:https') +const path = require('node:path') +const extname = path.extname /** * Expose `GithubView`. */ -module.exports = GithubView; +module.exports = GithubView /** * Custom view that fetches and renders @@ -20,34 +20,34 @@ module.exports = GithubView; * render templates from a database etc. */ -function GithubView(name, options){ - this.name = name; - options = options || {}; - this.engine = options.engines[extname(name)]; +function GithubView (name, options) { + this.name = name + options = options || {} + this.engine = options.engines[extname(name)] // "root" is the app.set('views') setting, however // in your own implementation you could ignore this - this.path = '/' + options.root + '/master/' + name; + this.path = '/' + options.root + '/master/' + name } /** * Render the view. */ -GithubView.prototype.render = function(options, fn){ - var self = this; - var opts = { +GithubView.prototype.render = function (options, fn) { + const self = this + const opts = { host: 'raw.githubusercontent.com', port: 443, path: this.path, method: 'GET' - }; - - https.request(opts, function(res) { - var buf = ''; - res.setEncoding('utf8'); - res.on('data', function(str){ buf += str }); - res.on('end', function(){ - self.engine(buf, options, fn); - }); - }).end(); -}; + } + + https.request(opts, (res) => { + let buf = '' + res.setEncoding('utf8') + res.on('data', (str) => { buf += str }) + res.on('end', () => { + self.engine(buf, options, fn) + }) + }).end() +} diff --git a/examples/view-constructor/index.js b/examples/view-constructor/index.js index 3d673670e31..bde51f89b1a 100644 --- a/examples/view-constructor/index.js +++ b/examples/view-constructor/index.js @@ -4,45 +4,43 @@ * Module dependencies. */ -var express = require('../../'); -var GithubView = require('./github-view'); -var md = require('marked').parse; +const express = require('../../') +const GithubView = require('./github-view') +const md = require('marked').parse -var app = module.exports = express(); +const app = module.exports = express() // register .md as an engine in express view system -app.engine('md', function(str, options, fn){ +app.engine('md', (str, options, fn) => { try { - var html = md(str); - html = html.replace(/\{([^}]+)\}/g, function(_, name){ - return options[name] || ''; - }); - fn(null, html); - } catch(err) { - fn(err); + let html = md(str) + html = html.replace(/\{([^}]+)\}/g, (_, name) => options[name] || '') + fn(null, html) + } catch (err) { + fn(err) } -}); +}) // pointing to a particular github repo to load files from it -app.set('views', 'expressjs/express'); +app.set('views', 'expressjs/express') // register a new view constructor -app.set('view', GithubView); +app.set('view', GithubView) -app.get('/', function(req, res){ +app.get('/', (req, res) => { // rendering a view relative to the repo. // app.locals, res.locals, and locals passed // work like they normally would - res.render('examples/markdown/views/index.md', { title: 'Example' }); -}); + res.render('examples/markdown/views/index.md', { title: 'Example' }) +}) -app.get('/Readme.md', function(req, res){ +app.get('/Readme.md', (req, res) => { // rendering a view from https://github.com/expressjs/express/blob/master/Readme.md - res.render('Readme.md'); -}); + res.render('Readme.md') +}) /* istanbul ignore next */ if (!module.parent) { - app.listen(3000); - console.log('Express started on port 3000'); + app.listen(3000) + console.log('Express started on port 3000') } diff --git a/examples/view-locals/index.js b/examples/view-locals/index.js index e6355602d4e..f48fdbf257a 100644 --- a/examples/view-locals/index.js +++ b/examples/view-locals/index.js @@ -4,17 +4,17 @@ * Module dependencies. */ -var express = require('../..'); -var path = require('node:path'); -var User = require('./user'); -var app = express(); +const express = require('../../') +const path = require('node:path') +const User = require('./user') +const app = express() -app.set('views', path.join(__dirname, 'views')); -app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')) +app.set('view engine', 'ejs') // filter ferrets only -function ferrets(user) { +function ferrets (user) { return user.species === 'ferret' } @@ -23,54 +23,48 @@ function ferrets(user) { // in order to expose the "count" // and "users" locals -app.get('/', function(req, res, next){ - User.count(function(err, count){ - if (err) return next(err); - User.all(function(err, users){ - if (err) return next(err); +app.get('/', (req, res, next) => { + User.count((err, count) => { + if (err) return next(err) + User.all((err, users) => { + if (err) return next(err) res.render('index', { title: 'Users', - count: count, + count, users: users.filter(ferrets) - }); + }) }) }) -}); - - - +}) // this approach is cleaner, // less nesting and we have // the variables available // on the request object -function count(req, res, next) { - User.count(function(err, count){ - if (err) return next(err); - req.count = count; - next(); +function count (req, res, next) { + User.count((err, count) => { + if (err) return next(err) + req.count = count + next() }) } -function users(req, res, next) { - User.all(function(err, users){ - if (err) return next(err); - req.users = users; - next(); +function users (req, res, next) { + User.all((err, users) => { + if (err) return next(err) + req.users = users + next() }) } -app.get('/middleware', count, users, function (req, res) { +app.get('/middleware', count, users, (req, res) => { res.render('index', { title: 'Users', count: req.count, users: req.users.filter(ferrets) - }); -}); - - - + }) +}) // this approach is much like the last // however we're explicitly exposing @@ -83,29 +77,29 @@ app.get('/middleware', count, users, function (req, res) { // so in that sense the previous example // is more flexible with `req.users`. -function count2(req, res, next) { - User.count(function(err, count){ - if (err) return next(err); - res.locals.count = count; - next(); +function count2 (req, res, next) { + User.count((err, count) => { + if (err) return next(err) + res.locals.count = count + next() }) } -function users2(req, res, next) { - User.all(function(err, users){ - if (err) return next(err); - res.locals.users = users.filter(ferrets); - next(); +function users2 (req, res, next) { + User.all((err, users) => { + if (err) return next(err) + res.locals.users = users.filter(ferrets) + next() }) } -app.get('/middleware-locals', count2, users2, function (req, res) { +app.get('/middleware-locals', count2, users2, (req, res) => { // you can see now how we have much less // to pass to res.render(). If we have // several routes related to users this // can be a great productivity booster - res.render('index', { title: 'Users' }); -}); + res.render('index', { title: 'Users' }) +}) // keep in mind that middleware may be placed anywhere // and in various combinations, so if you have locals @@ -150,6 +144,6 @@ app.all('/api/*', function(req, res, next){ /* istanbul ignore next */ if (!module.parent) { - app.listen(3000); - console.log('Express started on port 3000'); + app.listen(3000) + console.log('Express started on port 3000') } diff --git a/examples/view-locals/user.js b/examples/view-locals/user.js index aaa6f85ff0e..6f29020f4aa 100644 --- a/examples/view-locals/user.js +++ b/examples/view-locals/user.js @@ -1,36 +1,36 @@ 'use strict' -module.exports = User; +module.exports = User // faux model -function User(name, age, species) { - this.name = name; - this.age = age; - this.species = species; +function User (name, age, species) { + this.name = name + this.age = age + this.species = species } -User.all = function(fn){ +User.all = function (fn) { // process.nextTick makes sure this function API // behaves in an asynchronous manner, like if it // was a real DB query to read all users. - process.nextTick(function(){ - fn(null, users); - }); -}; + process.nextTick(() => { + fn(null, users) + }) +} -User.count = function(fn){ - process.nextTick(function(){ - fn(null, users.length); - }); -}; +User.count = function (fn) { + process.nextTick(() => { + fn(null, users.length) + }) +} // faux database -var users = []; +const users = [] -users.push(new User('Tobi', 2, 'ferret')); -users.push(new User('Loki', 1, 'ferret')); -users.push(new User('Jane', 6, 'ferret')); -users.push(new User('Luna', 1, 'cat')); -users.push(new User('Manny', 1, 'cat')); +users.push(new User('Tobi', 2, 'ferret')) +users.push(new User('Loki', 1, 'ferret')) +users.push(new User('Jane', 6, 'ferret')) +users.push(new User('Luna', 1, 'cat')) +users.push(new User('Manny', 1, 'cat')) diff --git a/examples/web-service/index.js b/examples/web-service/index.js index d1a90362153..b7c18601353 100644 --- a/examples/web-service/index.js +++ b/examples/web-service/index.js @@ -4,18 +4,18 @@ * Module dependencies. */ -var express = require('../../'); +const express = require('../../') -var app = module.exports = express(); +const app = module.exports = express() // create an error with .status. we // can then use the property in our // custom error handler (Connect respects this prop as well) -function error(status, msg) { - var err = new Error(msg); - err.status = status; - return err; +function error (status, msg) { + const err = new Error(msg) + err.status = status + return err } // if we wanted to supply more than JSON, we could @@ -27,91 +27,91 @@ function error(status, msg) { // meaning only paths prefixed with "/api" // will cause this middleware to be invoked -app.use('/api', function(req, res, next){ - var key = req.query['api-key']; +app.use('/api', (req, res, next) => { + const key = req.query['api-key'] // key isn't present - if (!key) return next(error(400, 'api key required')); + if (!key) return next(error(400, 'api key required')) // key is invalid if (apiKeys.indexOf(key) === -1) return next(error(401, 'invalid api key')) // all good, store req.key for route access - req.key = key; - next(); -}); + req.key = key + next() +}) // map of valid api keys, typically mapped to // account info with some sort of database like redis. // api keys do _not_ serve as authentication, merely to // track API usage or help prevent malicious behavior etc. -var apiKeys = ['foo', 'bar', 'baz']; +const apiKeys = ['foo', 'bar', 'baz'] // these two objects will serve as our faux database -var repos = [ +const repos = [ { name: 'express', url: 'https://github.com/expressjs/express' }, { name: 'stylus', url: 'https://github.com/learnboost/stylus' }, { name: 'cluster', url: 'https://github.com/learnboost/cluster' } -]; - -var users = [ - { name: 'tobi' } - , { name: 'loki' } - , { name: 'jane' } -]; - -var userRepos = { - tobi: [repos[0], repos[1]] - , loki: [repos[1]] - , jane: [repos[2]] -}; +] + +const users = [ + { name: 'tobi' }, + { name: 'loki' }, + { name: 'jane' } +] + +const userRepos = { + tobi: [repos[0], repos[1]], + loki: [repos[1]], + jane: [repos[2]] +} // we now can assume the api key is valid, // and simply expose the data // example: http://localhost:3000/api/users/?api-key=foo -app.get('/api/users', function (req, res) { - res.send(users); -}); +app.get('/api/users', (req, res) => { + res.send(users) +}) // example: http://localhost:3000/api/repos/?api-key=foo -app.get('/api/repos', function (req, res) { - res.send(repos); -}); +app.get('/api/repos', (req, res) => { + res.send(repos) +}) // example: http://localhost:3000/api/user/tobi/repos/?api-key=foo -app.get('/api/user/:name/repos', function(req, res, next){ - var name = req.params.name; - var user = userRepos[name]; +app.get('/api/user/:name/repos', (req, res, next) => { + const name = req.params.name + const user = userRepos[name] - if (user) res.send(user); - else next(); -}); + if (user) res.send(user) + else next() +}) // middleware with an arity of 4 are considered // error handling middleware. When you next(err) // it will be passed through the defined middleware // in order, but ONLY those with an arity of 4, ignoring // regular middleware. -app.use(function(err, req, res, next){ +app.use((err, req, res, next) => { // whatever you want here, feel free to populate // properties on `err` to treat it differently in here. - res.status(err.status || 500); - res.send({ error: err.message }); -}); + res.status(err.status || 500) + res.send({ error: err.message }) +}) // our custom JSON 404 middleware. Since it's placed last // it will be the last middleware called, if all others // invoke next() and do not respond. -app.use(function(req, res){ - res.status(404); +app.use((req, res) => { + res.status(404) res.send({ error: "Sorry, can't find that" }) -}); +}) /* istanbul ignore next */ if (!module.parent) { - app.listen(3000); - console.log('Express started on port 3000'); + app.listen(3000) + console.log('Express started on port 3000') } diff --git a/index.js b/index.js index d219b0c878d..9ae72fdf117 100644 --- a/index.js +++ b/index.js @@ -6,6 +6,6 @@ * MIT Licensed */ -'use strict'; +'use strict' -module.exports = require('./lib/express'); +module.exports = require('./lib/express') diff --git a/lib/application.js b/lib/application.js index 838b882aaae..2a2a5e40c25 100644 --- a/lib/application.js +++ b/lib/application.js @@ -6,45 +6,42 @@ * MIT Licensed */ -'use strict'; +'use strict' /** * Module dependencies. * @private */ -var finalhandler = require('finalhandler'); -var debug = require('debug')('express:application'); -var View = require('./view'); -var http = require('node:http'); -var methods = require('./utils').methods; -var compileETag = require('./utils').compileETag; -var compileQueryParser = require('./utils').compileQueryParser; -var compileTrust = require('./utils').compileTrust; -var resolve = require('node:path').resolve; -var once = require('once') -var Router = require('router'); +const finalhandler = require('finalhandler') +const debug = require('debug')('express:application') +const View = require('./view') +const http = require('node:http') +const { methods, compileETag, compileQueryParser, compileTrust } = require('./utils') +const resolve = require('node:path').resolve +const once = require('once') +const Router = require('router') /** * Module variables. * @private */ -var slice = Array.prototype.slice; -var flatten = Array.prototype.flat; +const slice = Array.prototype.slice +const flatten = Array.prototype.flat /** * Application prototype. */ -var app = exports = module.exports = {}; +const app = exports = module.exports = {} /** * Variable for trust proxy inheritance back-compat * @private */ -var trustProxyDefaultSymbol = '@@symbol:trust_proxy_default'; +const trustProxyDefaultSymbol = '@@symbol:trust_proxy_default' /** * Initialize the server. @@ -56,62 +53,62 @@ var trustProxyDefaultSymbol = '@@symbol:trust_proxy_default'; * @private */ -app.init = function init() { - var router = null; +app.init = function init () { + let router = null - this.cache = Object.create(null); - this.engines = Object.create(null); - this.settings = Object.create(null); + this.cache = Object.create(null) + this.engines = Object.create(null) + this.settings = Object.create(null) - this.defaultConfiguration(); + this.defaultConfiguration() // Setup getting to lazily add base router Object.defineProperty(this, 'router', { configurable: true, enumerable: true, - get: function getrouter() { + get: function getrouter () { if (router === null) { router = new Router({ caseSensitive: this.enabled('case sensitive routing'), strict: this.enabled('strict routing') - }); + }) } - return router; + return router } - }); -}; + }) +} /** * Initialize application configuration. * @private */ -app.defaultConfiguration = function defaultConfiguration() { - var env = process.env.NODE_ENV || 'development'; +app.defaultConfiguration = function defaultConfiguration () { + const env = process.env.NODE_ENV || 'development' // default settings - this.enable('x-powered-by'); - this.set('etag', 'weak'); - this.set('env', env); + this.enable('x-powered-by') + this.set('etag', 'weak') + this.set('env', env) this.set('query parser', 'simple') - this.set('subdomain offset', 2); - this.set('trust proxy', false); + this.set('subdomain offset', 2) + this.set('trust proxy', false) // trust proxy inherit back-compat Object.defineProperty(this.settings, trustProxyDefaultSymbol, { configurable: true, value: true - }); + }) - debug('booting in %s mode', env); + debug('booting in %s mode', env) - this.on('mount', function onmount(parent) { + this.on('mount', function onmount (parent) { // inherit trust proxy - if (this.settings[trustProxyDefaultSymbol] === true - && typeof parent.settings['trust proxy fn'] === 'function') { - delete this.settings['trust proxy']; - delete this.settings['trust proxy fn']; + if (this.settings[trustProxyDefaultSymbol] === true && + typeof parent.settings['trust proxy fn'] === 'function') { + delete this.settings['trust proxy'] + delete this.settings['trust proxy fn'] } // inherit protos @@ -119,26 +116,26 @@ app.defaultConfiguration = function defaultConfiguration() { Object.setPrototypeOf(this.response, parent.response) Object.setPrototypeOf(this.engines, parent.engines) Object.setPrototypeOf(this.settings, parent.settings) - }); + }) // setup locals - this.locals = Object.create(null); + this.locals = Object.create(null) // top-most app is mounted at / - this.mountpath = '/'; + this.mountpath = '/' // default locals - this.locals.settings = this.settings; + this.locals.settings = this.settings // default configuration - this.set('view', View); - this.set('views', resolve('views')); - this.set('jsonp callback name', 'callback'); + this.set('view', View) + this.set('views', resolve('views')) + this.set('jsonp callback name', 'callback') if (env === 'production') { - this.enable('view cache'); + this.enable('view cache') } -}; +} /** * Dispatch a req, res pair into the application. Starts pipeline processing. @@ -149,21 +146,21 @@ app.defaultConfiguration = function defaultConfiguration() { * @private */ -app.handle = function handle(req, res, callback) { +app.handle = function handle (req, res, callback) { // final handler - var done = callback || finalhandler(req, res, { + const done = callback || finalhandler(req, res, { env: this.get('env'), onerror: logerror.bind(this) - }); + }) // set powered by header if (this.enabled('x-powered-by')) { - res.setHeader('X-Powered-By', 'Express'); + res.setHeader('X-Powered-By', 'Express') } // set circular references - req.res = res; - res.req = req; + req.res = res + res.req = req // alter the prototypes Object.setPrototypeOf(req, this.request) @@ -171,11 +168,11 @@ app.handle = function handle(req, res, callback) { // setup locals if (!res.locals) { - res.locals = Object.create(null); + res.locals = Object.create(null) } - this.router.handle(req, res, done); -}; + this.router.handle(req, res, done) +} /** * Proxy `Router#use()` to add middleware to the app router. @@ -187,61 +184,61 @@ app.handle = function handle(req, res, callback) { * @public */ -app.use = function use(fn) { - var offset = 0; - var path = '/'; +app.use = function use (fn) { + let offset = 0 + let path = '/' // default path to '/' // disambiguate app.use([fn]) if (typeof fn !== 'function') { - var arg = fn; + let arg = fn while (Array.isArray(arg) && arg.length !== 0) { - arg = arg[0]; + arg = arg[0] } // first arg is the path if (typeof arg !== 'function') { - offset = 1; - path = fn; + offset = 1 + path = fn } } - var fns = flatten.call(slice.call(arguments, offset), Infinity); + const fns = flatten.call(slice.call(arguments, offset), Infinity) if (fns.length === 0) { throw new TypeError('app.use() requires a middleware function') } // get router - var router = this.router; + const router = this.router fns.forEach(function (fn) { // non-express app if (!fn || !fn.handle || !fn.set) { - return router.use(path, fn); + return router.use(path, fn) } - debug('.use app under %s', path); - fn.mountpath = path; - fn.parent = this; + debug('.use app under %s', path) + fn.mountpath = path + fn.parent = this // restore .app property on req and res - router.use(path, function mounted_app(req, res, next) { - var orig = req.app; - fn.handle(req, res, function (err) { + router.use(path, function mountedApp (req, res, next) { + const orig = req.app + fn.handle(req, res, (err) => { Object.setPrototypeOf(req, orig.request) Object.setPrototypeOf(res, orig.response) - next(err); - }); - }); + next(err) + }) + }) // mounted an app - fn.emit('mount', this); - }, this); + fn.emit('mount', this) + }, this) - return this; -}; + return this +} /** * Proxy to the app `Router#route()` @@ -253,9 +250,9 @@ app.use = function use(fn) { * @public */ -app.route = function route(path) { - return this.router.route(path); -}; +app.route = function route (path) { + return this.router.route(path) +} /** * Register the given template engine callback `fn` @@ -291,21 +288,21 @@ app.route = function route(path) { * @public */ -app.engine = function engine(ext, fn) { +app.engine = function engine (ext, fn) { if (typeof fn !== 'function') { - throw new Error('callback function required'); + throw new Error('callback function required') } // get file extension - var extension = ext[0] !== '.' + const extension = ext[0] !== '.' ? '.' + ext - : ext; + : ext // store engine - this.engines[extension] = fn; + this.engines[extension] = fn - return this; -}; + return this +} /** * Proxy to `Router#param()` with one added api feature. The _name_ parameter @@ -319,19 +316,19 @@ app.engine = function engine(ext, fn) { * @public */ -app.param = function param(name, fn) { +app.param = function param (name, fn) { if (Array.isArray(name)) { - for (var i = 0; i < name.length; i++) { - this.param(name[i], fn); + for (let i = 0; i < name.length; i++) { + this.param(name[i], fn) } - return this; + return this } - this.router.param(name, fn); + this.router.param(name, fn) - return this; -}; + return this +} /** * Assign `setting` to `val`, or return `setting`'s value. @@ -348,39 +345,39 @@ app.param = function param(name, fn) { * @public */ -app.set = function set(setting, val) { +app.set = function set (setting, val) { if (arguments.length === 1) { // app.get(setting) - return this.settings[setting]; + return this.settings[setting] } - debug('set "%s" to %o', setting, val); + debug('set "%s" to %o', setting, val) // set value - this.settings[setting] = val; + this.settings[setting] = val // trigger matched settings switch (setting) { case 'etag': - this.set('etag fn', compileETag(val)); - break; + this.set('etag fn', compileETag(val)) + break case 'query parser': - this.set('query parser fn', compileQueryParser(val)); - break; + this.set('query parser fn', compileQueryParser(val)) + break case 'trust proxy': - this.set('trust proxy fn', compileTrust(val)); + this.set('trust proxy fn', compileTrust(val)) // trust proxy inherit back-compat Object.defineProperty(this.settings, trustProxyDefaultSymbol, { configurable: true, value: false - }); + }) - break; + break } - return this; -}; + return this +} /** * Return the app's absolute pathname @@ -396,11 +393,11 @@ app.set = function set(setting, val) { * @private */ -app.path = function path() { +app.path = function path () { return this.parent ? this.parent.path() + this.mountpath - : ''; -}; + : '' +} /** * Check if `setting` is enabled (truthy). @@ -417,9 +414,9 @@ app.path = function path() { * @public */ -app.enabled = function enabled(setting) { - return Boolean(this.set(setting)); -}; +app.enabled = function enabled (setting) { + return Boolean(this.set(setting)) +} /** * Check if `setting` is disabled. @@ -436,9 +433,9 @@ app.enabled = function enabled(setting) { * @public */ -app.disabled = function disabled(setting) { - return !this.set(setting); -}; +app.disabled = function disabled (setting) { + return !this.set(setting) +} /** * Enable `setting`. @@ -448,9 +445,9 @@ app.disabled = function disabled(setting) { * @public */ -app.enable = function enable(setting) { - return this.set(setting, true); -}; +app.enable = function enable (setting) { + return this.set(setting, true) +} /** * Disable `setting`. @@ -460,26 +457,26 @@ app.enable = function enable(setting) { * @public */ -app.disable = function disable(setting) { - return this.set(setting, false); -}; +app.disable = function disable (setting) { + return this.set(setting, false) +} /** * Delegate `.VERB(...)` calls to `router.VERB(...)`. */ -methods.forEach(function (method) { +methods.forEach((method) => { app[method] = function (path) { if (method === 'get' && arguments.length === 1) { // app.get(setting) - return this.set(path); + return this.set(path) } - var route = this.route(path); - route[method].apply(route, slice.call(arguments, 1)); - return this; - }; -}); + const route = this.route(path) + route[method].apply(route, slice.call(arguments, 1)) + return this + } +}) /** * Special-cased "all" method, applying the given route `path`, @@ -491,16 +488,16 @@ methods.forEach(function (method) { * @public */ -app.all = function all(path) { - var route = this.route(path); - var args = slice.call(arguments, 1); +app.all = function all (path) { + const route = this.route(path) + const args = slice.call(arguments, 1) - for (var i = 0; i < methods.length; i++) { - route[methods[i]].apply(route, args); + for (let i = 0; i < methods.length; i++) { + route[methods[i]].apply(route, args) } - return this; -}; + return this +} /** * Render the given view `name` name with `options` @@ -519,60 +516,60 @@ app.all = function all(path) { * @public */ -app.render = function render(name, options, callback) { - var cache = this.cache; - var done = callback; - var engines = this.engines; - var opts = options; - var view; +app.render = function render (name, options, callback) { + const cache = this.cache + let done = callback + const engines = this.engines + let opts = options + let view // support callback function as second arg if (typeof options === 'function') { - done = options; - opts = {}; + done = options + opts = {} } // merge options - var renderOptions = { ...this.locals, ...opts._locals, ...opts }; + const renderOptions = { ...this.locals, ...opts._locals, ...opts } // set .cache unless explicitly provided if (renderOptions.cache == null) { - renderOptions.cache = this.enabled('view cache'); + renderOptions.cache = this.enabled('view cache') } // primed cache if (renderOptions.cache) { - view = cache[name]; + view = cache[name] } // view if (!view) { - var View = this.get('view'); + const View = this.get('view') view = new View(name, { defaultEngine: this.get('view engine'), root: this.get('views'), - engines: engines - }); + engines + }) if (!view.path) { - var dirs = Array.isArray(view.root) && view.root.length > 1 + const dirs = Array.isArray(view.root) && view.root.length > 1 ? 'directories "' + view.root.slice(0, -1).join('", "') + '" or "' + view.root[view.root.length - 1] + '"' : 'directory "' + view.root + '"' - var err = new Error('Failed to lookup view "' + name + '" in views ' + dirs); - err.view = view; - return done(err); + const err = new Error('Failed to lookup view "' + name + '" in views ' + dirs) + err.view = view + return done(err) } // prime the cache if (renderOptions.cache) { - cache[name] = view; + cache[name] = view } } // render - tryRender(view, renderOptions, done); -}; + tryRender(view, renderOptions, done) +} /** * Listen for connections. @@ -595,11 +592,11 @@ app.render = function render(name, options, callback) { * @public */ -app.listen = function listen() { - var server = http.createServer(this) - var args = slice.call(arguments) +app.listen = function listen () { + const server = http.createServer(this) + const args = slice.call(arguments) if (typeof args[args.length - 1] === 'function') { - var done = args[args.length - 1] = once(args[args.length - 1]) + const done = args[args.length - 1] = once(args[args.length - 1]) server.once('error', done) } return server.listen.apply(server, args) @@ -612,9 +609,9 @@ app.listen = function listen() { * @private */ -function logerror(err) { +function logerror (err) { /* istanbul ignore next */ - if (this.get('env') !== 'test') console.error(err.stack || err.toString()); + if (this.get('env') !== 'test') console.error(err.stack || err.toString()) } /** @@ -622,10 +619,10 @@ function logerror(err) { * @private */ -function tryRender(view, options, callback) { +function tryRender (view, options, callback) { try { - view.render(options, callback); + view.render(options, callback) } catch (err) { - callback(err); + callback(err) } } diff --git a/lib/express.js b/lib/express.js index 2d502eb54e4..43a1b0b280c 100644 --- a/lib/express.js +++ b/lib/express.js @@ -6,25 +6,25 @@ * MIT Licensed */ -'use strict'; +'use strict' /** * Module dependencies. */ -var bodyParser = require('body-parser') -var EventEmitter = require('node:events').EventEmitter; -var mixin = require('merge-descriptors'); -var proto = require('./application'); -var Router = require('router'); -var req = require('./request'); -var res = require('./response'); +const bodyParser = require('body-parser') +const { EventEmitter } = require('node:events') +const mixin = require('merge-descriptors') +const proto = require('./application') +const Router = require('router') +const req = require('./request') +const res = require('./response') /** * Expose `createApplication()`. */ -exports = module.exports = createApplication; +exports = module.exports = createApplication /** * Create an express application. @@ -33,13 +33,13 @@ exports = module.exports = createApplication; * @api public */ -function createApplication() { - var app = function(req, res, next) { - app.handle(req, res, next); - }; +function createApplication () { + const app = function (req, res, next) { + app.handle(req, res, next) + } - mixin(app, EventEmitter.prototype, false); - mixin(app, proto, false); + mixin(app, EventEmitter.prototype, false) + mixin(app, proto, false) // expose the prototype that will get set on requests app.request = Object.create(req, { @@ -51,24 +51,24 @@ function createApplication() { app: { configurable: true, enumerable: true, writable: true, value: app } }) - app.init(); - return app; + app.init() + return app } /** * Expose the prototypes. */ -exports.application = proto; -exports.request = req; -exports.response = res; +exports.application = proto +exports.request = req +exports.response = res /** * Expose constructors. */ -exports.Route = Router.Route; -exports.Router = Router; +exports.Route = Router.Route +exports.Router = Router /** * Expose middleware @@ -76,6 +76,6 @@ exports.Router = Router; exports.json = bodyParser.json exports.raw = bodyParser.raw -exports.static = require('serve-static'); +exports.static = require('serve-static') exports.text = bodyParser.text exports.urlencoded = bodyParser.urlencoded diff --git a/lib/request.js b/lib/request.js index 69990da39b6..369bf34e966 100644 --- a/lib/request.js +++ b/lib/request.js @@ -6,28 +6,28 @@ * MIT Licensed */ -'use strict'; +'use strict' /** * Module dependencies. * @private */ -var accepts = require('accepts'); -var isIP = require('node:net').isIP; -var typeis = require('type-is'); -var http = require('node:http'); -var fresh = require('fresh'); -var parseRange = require('range-parser'); -var parse = require('parseurl'); -var proxyaddr = require('proxy-addr'); +const accepts = require('accepts') +const { isIP } = require('node:net') +const typeis = require('type-is') +const http = require('node:http') +const fresh = require('fresh') +const parseRange = require('range-parser') +const parse = require('parseurl') +const proxyaddr = require('proxy-addr') /** * Request prototype. * @public */ -var req = Object.create(http.IncomingMessage.prototype) +const req = Object.create(http.IncomingMessage.prototype) /** * Module exports. @@ -61,26 +61,26 @@ module.exports = req */ req.get = -req.header = function header(name) { +req.header = function header (name) { if (!name) { - throw new TypeError('name argument is required to req.get'); + throw new TypeError('name argument is required to req.get') } if (typeof name !== 'string') { - throw new TypeError('name must be a string to req.get'); + throw new TypeError('name must be a string to req.get') } - var lc = name.toLowerCase(); + const lc = name.toLowerCase() switch (lc) { case 'referer': case 'referrer': - return this.headers.referrer - || this.headers.referer; + return this.headers.referrer || + this.headers.referer default: - return this.headers[lc]; + return this.headers[lc] } -}; +} /** * To do: update docs. @@ -128,10 +128,10 @@ req.header = function header(name) { * @public */ -req.accepts = function(){ - var accept = accepts(this); - return accept.types.apply(accept, arguments); -}; +req.accepts = function () { + const accept = accepts(this) + return accept.types.apply(accept, arguments) +} /** * Check if the given `encoding`s are accepted. @@ -141,10 +141,10 @@ req.accepts = function(){ * @public */ -req.acceptsEncodings = function(){ - var accept = accepts(this); - return accept.encodings.apply(accept, arguments); -}; +req.acceptsEncodings = function () { + const accept = accepts(this) + return accept.encodings.apply(accept, arguments) +} /** * Check if the given `charset`s are acceptable, @@ -155,10 +155,10 @@ req.acceptsEncodings = function(){ * @public */ -req.acceptsCharsets = function(){ - var accept = accepts(this); - return accept.charsets.apply(accept, arguments); -}; +req.acceptsCharsets = function () { + const accept = accepts(this) + return accept.charsets.apply(accept, arguments) +} /** * Check if the given `lang`s are acceptable, @@ -169,9 +169,9 @@ req.acceptsCharsets = function(){ * @public */ -req.acceptsLanguages = function(...languages) { - return accepts(this).languages(...languages); -}; +req.acceptsLanguages = function (...languages) { + return accepts(this).languages(...languages) +} /** * Parse Range header field, capping to the given `size`. @@ -198,11 +198,11 @@ req.acceptsLanguages = function(...languages) { * @public */ -req.range = function range(size, options) { - var range = this.get('Range'); - if (!range) return; - return parseRange(size, range, options); -}; +req.range = function range (size, options) { + const range = this.get('Range') + if (!range) return + return parseRange(size, range, options) +} /** * Parse the query string of `req.url`. @@ -214,18 +214,18 @@ req.range = function range(size, options) { * @api public */ -defineGetter(req, 'query', function query(){ - var queryparse = this.app.get('query parser fn'); +defineGetter(req, 'query', function query () { + const queryparse = this.app.get('query parser fn') if (!queryparse) { // parsing is disabled - return Object.create(null); + return Object.create(null) } - var querystring = parse(this).query; + const querystring = parse(this).query - return queryparse(querystring); -}); + return queryparse(querystring) +}) /** * Check if the incoming request contains the "Content-Type" @@ -253,19 +253,19 @@ defineGetter(req, 'query', function query(){ * @public */ -req.is = function is(types) { - var arr = types; +req.is = function is (types) { + let arr = types // support flattened arguments if (!Array.isArray(types)) { - arr = new Array(arguments.length); - for (var i = 0; i < arr.length; i++) { - arr[i] = arguments[i]; + arr = new Array(arguments.length) + for (let i = 0; i < arr.length; i++) { + arr[i] = arguments[i] } } - return typeis(this, arr); -}; + return typeis(this, arr) +} /** * Return the protocol string "http" or "https" @@ -281,25 +281,25 @@ req.is = function is(types) { * @public */ -defineGetter(req, 'protocol', function protocol(){ - var proto = this.socket.encrypted +defineGetter(req, 'protocol', function protocol () { + const proto = this.socket.encrypted ? 'https' - : 'http'; - var trust = this.app.get('trust proxy fn'); + : 'http' + const trust = this.app.get('trust proxy fn') if (!trust(this.socket.remoteAddress, 0)) { - return proto; + return proto } // Note: X-Forwarded-Proto is normally only ever a // single value, but this is to be safe. - var header = this.get('X-Forwarded-Proto') || proto - var index = header.indexOf(',') + const header = this.get('X-Forwarded-Proto') || proto + const index = header.indexOf(',') return index !== -1 ? header.substring(0, index).trim() : header.trim() -}); +}) /** * Short-hand for: @@ -310,9 +310,9 @@ defineGetter(req, 'protocol', function protocol(){ * @public */ -defineGetter(req, 'secure', function secure(){ - return this.protocol === 'https'; -}); +defineGetter(req, 'secure', function secure () { + return this.protocol === 'https' +}) /** * Return the remote address from the trusted proxy. @@ -324,10 +324,10 @@ defineGetter(req, 'secure', function secure(){ * @public */ -defineGetter(req, 'ip', function ip(){ - var trust = this.app.get('trust proxy fn'); - return proxyaddr(this, trust); -}); +defineGetter(req, 'ip', function ip () { + const trust = this.app.get('trust proxy fn') + return proxyaddr(this, trust) +}) /** * When "trust proxy" is set, trusted proxy addresses + client. @@ -341,16 +341,16 @@ defineGetter(req, 'ip', function ip(){ * @public */ -defineGetter(req, 'ips', function ips() { - var trust = this.app.get('trust proxy fn'); - var addrs = proxyaddr.all(this, trust); +defineGetter(req, 'ips', function ips () { + const trust = this.app.get('trust proxy fn') + const addrs = proxyaddr.all(this, trust) // reverse the order (to farthest -> closest) // and remove socket address addrs.reverse().pop() return addrs -}); +}) /** * Return subdomains as an array. @@ -367,18 +367,18 @@ defineGetter(req, 'ips', function ips() { * @public */ -defineGetter(req, 'subdomains', function subdomains() { - var hostname = this.hostname; +defineGetter(req, 'subdomains', function subdomains () { + const hostname = this.hostname - if (!hostname) return []; + if (!hostname) return [] - var offset = this.app.get('subdomain offset'); - var subdomains = !isIP(hostname) + const offset = this.app.get('subdomain offset') + const subdomains = !isIP(hostname) ? hostname.split('.').reverse() - : [hostname]; + : [hostname] - return subdomains.slice(offset); -}); + return subdomains.slice(offset) +}) /** * Short-hand for `url.parse(req.url).pathname`. @@ -387,9 +387,9 @@ defineGetter(req, 'subdomains', function subdomains() { * @public */ -defineGetter(req, 'path', function path() { - return parse(this).pathname; -}); +defineGetter(req, 'path', function path () { + return parse(this).pathname +}) /** * Parse the "Host" header field to a host. @@ -402,20 +402,20 @@ defineGetter(req, 'path', function path() { * @public */ -defineGetter(req, 'host', function host(){ - var trust = this.app.get('trust proxy fn'); - var val = this.get('X-Forwarded-Host'); +defineGetter(req, 'host', function host () { + const trust = this.app.get('trust proxy fn') + let val = this.get('X-Forwarded-Host') if (!val || !trust(this.socket.remoteAddress, 0)) { - val = this.get('Host'); + val = this.get('Host') } else if (val.indexOf(',') !== -1) { // Note: X-Forwarded-Host is normally only ever a // single value, but this is to be safe. val = val.substring(0, val.indexOf(',')).trimRight() } - return val || undefined; -}); + return val || undefined +}) /** * Parse the "Host" header field to a hostname. @@ -428,21 +428,21 @@ defineGetter(req, 'host', function host(){ * @api public */ -defineGetter(req, 'hostname', function hostname(){ - var host = this.host; +defineGetter(req, 'hostname', function hostname () { + const host = this.host - if (!host) return; + if (!host) return // IPv6 literal support - var offset = host[0] === '[' + const offset = host[0] === '[' ? host.indexOf(']') + 1 - : 0; - var index = host.indexOf(':', offset); + : 0 + const index = host.indexOf(':', offset) return index !== -1 ? host.substring(0, index) - : host; -}); + : host +}) /** * Check if the request is fresh, aka @@ -453,24 +453,24 @@ defineGetter(req, 'hostname', function hostname(){ * @public */ -defineGetter(req, 'fresh', function(){ - var method = this.method; - var res = this.res - var status = res.statusCode +defineGetter(req, 'fresh', function () { + const method = this.method + const res = this.res + const status = res.statusCode // GET or HEAD for weak freshness validation only - if ('GET' !== method && 'HEAD' !== method) return false; + if (method !== 'GET' && method !== 'HEAD') return false // 2xx or 304 as per rfc2616 14.26 - if ((status >= 200 && status < 300) || 304 === status) { + if ((status >= 200 && status < 300) || status === 304) { return fresh(this.headers, { - 'etag': res.get('ETag'), + etag: res.get('ETag'), 'last-modified': res.get('Last-Modified') }) } - return false; -}); + return false +}) /** * Check if the request is stale, aka @@ -481,9 +481,9 @@ defineGetter(req, 'fresh', function(){ * @public */ -defineGetter(req, 'stale', function stale(){ - return !this.fresh; -}); +defineGetter(req, 'stale', function stale () { + return !this.fresh +}) /** * Check if the request was an _XMLHttpRequest_. @@ -492,10 +492,10 @@ defineGetter(req, 'stale', function stale(){ * @public */ -defineGetter(req, 'xhr', function xhr(){ - var val = this.get('X-Requested-With') || ''; - return val.toLowerCase() === 'xmlhttprequest'; -}); +defineGetter(req, 'xhr', function xhr () { + const val = this.get('X-Requested-With') || '' + return val.toLowerCase() === 'xmlhttprequest' +}) /** * Helper function for creating a getter on an object. @@ -505,10 +505,10 @@ defineGetter(req, 'xhr', function xhr(){ * @param {Function} getter * @private */ -function defineGetter(obj, name, getter) { +function defineGetter (obj, name, getter) { Object.defineProperty(obj, name, { configurable: true, enumerable: true, get: getter - }); + }) } diff --git a/lib/response.js b/lib/response.js index 7a2f0ecce56..74d9c58c935 100644 --- a/lib/response.js +++ b/lib/response.js @@ -5,41 +5,39 @@ * MIT Licensed */ -'use strict'; +'use strict' /** * Module dependencies. * @private */ -var contentDisposition = require('content-disposition'); -var createError = require('http-errors') -var deprecate = require('depd')('express'); -var encodeUrl = require('encodeurl'); -var escapeHtml = require('escape-html'); -var http = require('node:http'); -var onFinished = require('on-finished'); -var mime = require('mime-types') -var path = require('node:path'); -var pathIsAbsolute = require('node:path').isAbsolute; -var statuses = require('statuses') -var sign = require('cookie-signature').sign; -var normalizeType = require('./utils').normalizeType; -var normalizeTypes = require('./utils').normalizeTypes; -var setCharset = require('./utils').setCharset; -var cookie = require('cookie'); -var send = require('send'); -var extname = path.extname; -var resolve = path.resolve; -var vary = require('vary'); -const { Buffer } = require('node:buffer'); +const contentDisposition = require('content-disposition') +const createError = require('http-errors') +const deprecate = require('depd')('express') +const encodeUrl = require('encodeurl') +const escapeHtml = require('escape-html') +const http = require('node:http') +const onFinished = require('on-finished') +const mime = require('mime-types') +const path = require('node:path') +const pathIsAbsolute = path.isAbsolute +const statuses = require('statuses') +const { sign } = require('cookie-signature') +const { normalizeType, normalizeTypes, setCharset } = require('./utils') +const cookie = require('cookie') +const send = require('send') +const extname = path.extname +const resolve = path.resolve +const vary = require('vary') +const { Buffer } = require('node:buffer') /** * Response prototype. * @public */ -var res = Object.create(http.ServerResponse.prototype) +const res = Object.create(http.ServerResponse.prototype) /** * Module exports. @@ -61,19 +59,19 @@ module.exports = res * @public */ -res.status = function status(code) { +res.status = function status (code) { // Check if the status code is not an integer if (!Number.isInteger(code)) { - throw new TypeError(`Invalid status code: ${JSON.stringify(code)}. Status code must be an integer.`); + throw new TypeError(`Invalid status code: ${JSON.stringify(code)}. Status code must be an integer.`) } // Check if the status code is outside of Node's valid range if (code < 100 || code > 999) { - throw new RangeError(`Invalid status code: ${JSON.stringify(code)}. Status code must be greater than 99 and less than 1000.`); + throw new RangeError(`Invalid status code: ${JSON.stringify(code)}. Status code must be greater than 99 and less than 1000.`) } - this.statusCode = code; - return this; -}; + this.statusCode = code + return this +} /** * Set Link header field with the given `links`. @@ -94,20 +92,20 @@ res.status = function status(code) { * @public */ -res.links = function(links) { - var link = this.get('Link') || ''; - if (link) link += ', '; - return this.set('Link', link + Object.keys(links).map(function(rel) { +res.links = function (links) { + let link = this.get('Link') || '' + if (link) link += ', ' + return this.set('Link', link + Object.keys(links).map((rel) => { // Allow multiple links if links[rel] is an array if (Array.isArray(links[rel])) { - return links[rel].map(function (singleLink) { - return `<${singleLink}>; rel="${rel}"`; - }).join(', '); + return links[rel].map((singleLink) => { + return `<${singleLink}>; rel="${rel}"` + }).join(', ') } else { - return `<${links[rel]}>; rel="${rel}"`; + return `<${links[rel]}>; rel="${rel}"` } - }).join(', ')); -}; + }).join(', ')) +} /** * Send a response. @@ -122,54 +120,54 @@ res.links = function(links) { * @public */ -res.send = function send(body) { - var chunk = body; - var encoding; - var req = this.req; - var type; +res.send = function send (body) { + let chunk = body + let encoding + const req = this.req + let type // settings - var app = this.app; + const app = this.app switch (typeof chunk) { // string defaulting to html case 'string': if (!this.get('Content-Type')) { - this.type('html'); + this.type('html') } - break; + break case 'boolean': case 'number': case 'object': if (chunk === null) { - chunk = ''; + chunk = '' } else if (ArrayBuffer.isView(chunk)) { if (!this.get('Content-Type')) { - this.type('bin'); + this.type('bin') } } else { - return this.json(chunk); + return this.json(chunk) } - break; + break } // write strings in utf-8 if (typeof chunk === 'string') { - encoding = 'utf8'; - type = this.get('Content-Type'); + encoding = 'utf8' + type = this.get('Content-Type') // reflect this in content-type if (typeof type === 'string') { - this.set('Content-Type', setCharset(type, 'utf-8')); + this.set('Content-Type', setCharset(type, 'utf-8')) } } // determine if ETag should be generated - var etagFn = app.get('etag fn') - var generateETag = !this.get('ETag') && typeof etagFn === 'function' + const etagFn = app.get('etag fn') + const generateETag = !this.get('ETag') && typeof etagFn === 'function' // populate Content-Length - var len + let len if (chunk !== undefined) { if (Buffer.isBuffer(chunk)) { // get length of Buffer @@ -180,30 +178,30 @@ res.send = function send(body) { } else { // convert chunk to Buffer and calculate chunk = Buffer.from(chunk, encoding) - encoding = undefined; + encoding = undefined len = chunk.length } - this.set('Content-Length', len); + this.set('Content-Length', len) } // populate ETag - var etag; + let etag if (generateETag && len !== undefined) { if ((etag = etagFn(chunk, encoding))) { - this.set('ETag', etag); + this.set('ETag', etag) } } // freshness - if (req.fresh) this.status(304); + if (req.fresh) this.status(304) // strip irrelevant headers - if (204 === this.statusCode || 304 === this.statusCode) { - this.removeHeader('Content-Type'); - this.removeHeader('Content-Length'); - this.removeHeader('Transfer-Encoding'); - chunk = ''; + if (this.statusCode === 204 || this.statusCode === 304) { + this.removeHeader('Content-Type') + this.removeHeader('Content-Length') + this.removeHeader('Transfer-Encoding') + chunk = '' } // alter headers for 205 @@ -215,14 +213,14 @@ res.send = function send(body) { if (req.method === 'HEAD') { // skip body for HEAD - this.end(); + this.end() } else { // respond - this.end(chunk, encoding); + this.end(chunk, encoding) } - return this; -}; + return this +} /** * Send JSON response. @@ -236,21 +234,21 @@ res.send = function send(body) { * @public */ -res.json = function json(obj) { +res.json = function json (obj) { // settings - var app = this.app; - var escape = app.get('json escape') - var replacer = app.get('json replacer'); - var spaces = app.get('json spaces'); - var body = stringify(obj, replacer, spaces, escape) + const app = this.app + const escape = app.get('json escape') + const replacer = app.get('json replacer') + const spaces = app.get('json spaces') + const body = stringify(obj, replacer, spaces, escape) // content-type if (!this.get('Content-Type')) { - this.set('Content-Type', 'application/json'); + this.set('Content-Type', 'application/json') } - return this.send(body); -}; + return this.send(body) +} /** * Send JSON response with JSONP callback support. @@ -264,33 +262,33 @@ res.json = function json(obj) { * @public */ -res.jsonp = function jsonp(obj) { +res.jsonp = function jsonp (obj) { // settings - var app = this.app; - var escape = app.get('json escape') - var replacer = app.get('json replacer'); - var spaces = app.get('json spaces'); - var body = stringify(obj, replacer, spaces, escape) - var callback = this.req.query[app.get('jsonp callback name')]; + const app = this.app + const escape = app.get('json escape') + const replacer = app.get('json replacer') + const spaces = app.get('json spaces') + let body = stringify(obj, replacer, spaces, escape) + let callback = this.req.query[app.get('jsonp callback name')] // content-type if (!this.get('Content-Type')) { - this.set('X-Content-Type-Options', 'nosniff'); - this.set('Content-Type', 'application/json'); + this.set('X-Content-Type-Options', 'nosniff') + this.set('Content-Type', 'application/json') } // fixup callback if (Array.isArray(callback)) { - callback = callback[0]; + callback = callback[0] } // jsonp if (typeof callback === 'string' && callback.length !== 0) { - this.set('X-Content-Type-Options', 'nosniff'); - this.set('Content-Type', 'text/javascript'); + this.set('X-Content-Type-Options', 'nosniff') + this.set('Content-Type', 'text/javascript') // restrict callback charset - callback = callback.replace(/[^\[\]\w$.]/g, ''); + callback = callback.replace(/[^[\]\w$.]/g, '') if (body === undefined) { // empty argument @@ -304,11 +302,11 @@ res.jsonp = function jsonp(obj) { // the /**/ is a specific security mitigation for "Rosetta Flash JSONP abuse" // the typeof check is just to reduce client error noise - body = '/**/ typeof ' + callback + ' === \'function\' && ' + callback + '(' + body + ');'; + body = '/**/ typeof ' + callback + ' === \'function\' && ' + callback + '(' + body + ');' } - return this.send(body); -}; + return this.send(body) +} /** * Send given HTTP status code. @@ -325,14 +323,14 @@ res.jsonp = function jsonp(obj) { * @public */ -res.sendStatus = function sendStatus(statusCode) { - var body = statuses.message[statusCode] || String(statusCode) +res.sendStatus = function sendStatus (statusCode) { + const body = statuses.message[statusCode] || String(statusCode) - this.status(statusCode); - this.type('txt'); + this.status(statusCode) + this.type('txt') - return this.send(body); -}; + return this.send(body) +} /** * Transfer the file at the given `path`. @@ -375,15 +373,15 @@ res.sendStatus = function sendStatus(statusCode) { * @public */ -res.sendFile = function sendFile(path, options, callback) { - var done = callback; - var req = this.req; - var res = this; - var next = req.next; - var opts = options || {}; +res.sendFile = function sendFile (path, options, callback) { + let done = callback + const req = this.req + const res = this + const next = req.next + let opts = options || {} if (!path) { - throw new TypeError('path argument is required to res.sendFile'); + throw new TypeError('path argument is required to res.sendFile') } if (typeof path !== 'string') { @@ -392,32 +390,32 @@ res.sendFile = function sendFile(path, options, callback) { // support function as second arg if (typeof options === 'function') { - done = options; - opts = {}; + done = options + opts = {} } if (!opts.root && !pathIsAbsolute(path)) { - throw new TypeError('path must be absolute or specify root to res.sendFile'); + throw new TypeError('path must be absolute or specify root to res.sendFile') } // create file stream - var pathname = encodeURI(path); + const pathname = encodeURI(path) // wire application etag option to send - opts.etag = this.app.enabled('etag'); - var file = send(req, pathname, opts); + opts.etag = this.app.enabled('etag') + const file = send(req, pathname, opts) // transfer - sendfile(res, file, opts, function (err) { - if (done) return done(err); - if (err && err.code === 'EISDIR') return next(); + sendfile(res, file, opts, (err) => { + if (done) return done(err) + if (err && err.code === 'EISDIR') return next() // next() all but write errors if (err && err.code !== 'ECONNABORTED' && err.syscall !== 'write') { - next(err); + next(err) } - }); -}; + }) +} /** * Transfer the file at the given `path` as an attachment. @@ -438,14 +436,14 @@ res.sendFile = function sendFile(path, options, callback) { */ res.download = function download (path, filename, options, callback) { - var done = callback; - var name = filename; - var opts = options || null + let done = callback + let name = filename + let opts = options || null // support function as second or third arg if (typeof filename === 'function') { - done = filename; - name = null; + done = filename + name = null opts = null } else if (typeof options === 'function') { done = options @@ -460,15 +458,15 @@ res.download = function download (path, filename, options, callback) { } // set Content-Disposition when file is sent - var headers = { + const headers = { 'Content-Disposition': contentDisposition(name || path) - }; + } // merge user-provided headers if (opts && opts.headers) { - var keys = Object.keys(opts.headers) - for (var i = 0; i < keys.length; i++) { - var key = keys[i] + const keys = Object.keys(opts.headers) + for (let i = 0; i < keys.length; i++) { + const key = keys[i] if (key.toLowerCase() !== 'content-disposition') { headers[key] = opts.headers[key] } @@ -480,13 +478,13 @@ res.download = function download (path, filename, options, callback) { opts.headers = headers // Resolve the full path for sendFile - var fullPath = !opts.root + const fullPath = !opts.root ? resolve(path) : path // send file return this.sendFile(fullPath, opts, done) -}; +} /** * Set _Content-Type_ response header with `type` through `mime.contentType()` @@ -508,13 +506,13 @@ res.download = function download (path, filename, options, callback) { */ res.contentType = -res.type = function contentType(type) { - var ct = type.indexOf('/') === -1 +res.type = function contentType (type) { + const ct = type.indexOf('/') === -1 ? (mime.contentType(type) || 'application/octet-stream') - : type; + : type - return this.set('Content-Type', ct); -}; + return this.set('Content-Type', ct) +} /** * Respond to the Acceptable formats using an `obj` @@ -573,32 +571,32 @@ res.type = function contentType(type) { * @public */ -res.format = function(obj){ - var req = this.req; - var next = req.next; +res.format = function (obj) { + const req = this.req + const next = req.next - var keys = Object.keys(obj) - .filter(function (v) { return v !== 'default' }) + const keys = Object.keys(obj) + .filter((v) => { return v !== 'default' }) - var key = keys.length > 0 + const key = keys.length > 0 ? req.accepts(keys) - : false; + : false - this.vary("Accept"); + this.vary('Accept') if (key) { - this.set('Content-Type', normalizeType(key).value); - obj[key](req, this, next); + this.set('Content-Type', normalizeType(key).value) + obj[key](req, this, next) } else if (obj.default) { obj.default(req, this, next) } else { next(createError(406, { - types: normalizeTypes(keys).map(function (o) { return o.value }) + types: normalizeTypes(keys).map((o) => { return o.value }) })) } - return this; -}; + return this +} /** * Set _Content-Disposition_ header to _attachment_ with optional `filename`. @@ -608,15 +606,15 @@ res.format = function(obj){ * @public */ -res.attachment = function attachment(filename) { +res.attachment = function attachment (filename) { if (filename) { - this.type(extname(filename)); + this.type(extname(filename)) } - this.set('Content-Disposition', contentDisposition(filename)); + this.set('Content-Disposition', contentDisposition(filename)) - return this; -}; + return this +} /** * Append additional header `field` with value `val`. @@ -633,19 +631,21 @@ res.attachment = function attachment(filename) { * @public */ -res.append = function append(field, val) { - var prev = this.get(field); - var value = val; +res.append = function append (field, val) { + const prev = this.get(field) + let value = val if (prev) { // concat the new and prev vals - value = Array.isArray(prev) ? prev.concat(val) - : Array.isArray(val) ? [prev].concat(val) + value = Array.isArray(prev) + ? prev.concat(val) + : Array.isArray(val) + ? [prev].concat(val) : [prev, val] } - return this.set(field, value); -}; + return this.set(field, value) +} /** * Set header `field` to `val`, or pass @@ -669,28 +669,28 @@ res.append = function append(field, val) { */ res.set = -res.header = function header(field, val) { +res.header = function header (field, val) { if (arguments.length === 2) { - var value = Array.isArray(val) + let value = Array.isArray(val) ? val.map(String) - : String(val); + : String(val) // add charset to content-type if (field.toLowerCase() === 'content-type') { if (Array.isArray(value)) { - throw new TypeError('Content-Type cannot be set to an Array'); + throw new TypeError('Content-Type cannot be set to an Array') } value = mime.contentType(value) } - this.setHeader(field, value); + this.setHeader(field, value) } else { - for (var key in field) { - this.set(key, field[key]); + for (const key in field) { + this.set(key, field[key]) } } - return this; -}; + return this +} /** * Get value for header `field`. @@ -700,9 +700,9 @@ res.header = function header(field, val) { * @public */ -res.get = function(field){ - return this.getHeader(field); -}; +res.get = function (field) { + return this.getHeader(field) +} /** * Clear cookie `name`. @@ -713,14 +713,14 @@ res.get = function(field){ * @public */ -res.clearCookie = function clearCookie(name, options) { +res.clearCookie = function clearCookie (name, options) { // Force cookie expiration by setting expires to the past - const opts = { path: '/', ...options, expires: new Date(1)}; + const opts = { path: '/', ...options, expires: new Date(1) } // ensure maxAge is not passed delete opts.maxAge - return this.cookie(name, '', opts); -}; + return this.cookie(name, '', opts) +} /** * Set cookie `name` to `value`, with the given `options`. @@ -747,24 +747,24 @@ res.clearCookie = function clearCookie(name, options) { */ res.cookie = function (name, value, options) { - var opts = { ...options }; - var secret = this.req.secret; - var signed = opts.signed; + const opts = { ...options } + const secret = this.req.secret + const signed = opts.signed if (signed && !secret) { - throw new Error('cookieParser("secret") required for signed cookies'); + throw new Error('cookieParser("secret") required for signed cookies') } - var val = typeof value === 'object' + let val = typeof value === 'object' ? 'j:' + JSON.stringify(value) - : String(value); + : String(value) if (signed) { - val = 's:' + sign(val, secret); + val = 's:' + sign(val, secret) } if (opts.maxAge != null) { - var maxAge = opts.maxAge - 0 + const maxAge = opts.maxAge - 0 if (!isNaN(maxAge)) { opts.expires = new Date(Date.now() + maxAge) @@ -773,13 +773,13 @@ res.cookie = function (name, value, options) { } if (opts.path == null) { - opts.path = '/'; + opts.path = '/' } - this.append('Set-Cookie', cookie.serialize(name, String(val), opts)); + this.append('Set-Cookie', cookie.serialize(name, String(val), opts)) - return this; -}; + return this +} /** * Set the location header to `url`. @@ -798,9 +798,9 @@ res.cookie = function (name, value, options) { * @public */ -res.location = function location(url) { - return this.set('Location', encodeUrl(url)); -}; +res.location = function location (url) { + return this.set('Location', encodeUrl(url)) +} /** * Redirect to the given `url` with optional response `status` @@ -816,10 +816,10 @@ res.location = function location(url) { * @public */ -res.redirect = function redirect(url) { - var address = url; - var body; - var status = 302; +res.redirect = function redirect (url) { + let address = url + let body + let status = 302 // allow status / url if (arguments.length === 2) { @@ -828,46 +828,46 @@ res.redirect = function redirect(url) { } if (!address) { - deprecate('Provide a url argument'); + deprecate('Provide a url argument') } if (typeof address !== 'string') { - deprecate('Url must be a string'); + deprecate('Url must be a string') } if (typeof status !== 'number') { - deprecate('Status must be a number'); + deprecate('Status must be a number') } // Set location header - address = this.location(address).get('Location'); + address = this.location(address).get('Location') // Support text/{plain,html} by default this.format({ - text: function(){ + text: function () { body = statuses.message[status] + '. Redirecting to ' + address }, - html: function(){ - var u = escapeHtml(address); + html: function () { + const u = escapeHtml(address) body = '' + statuses.message[status] + '. Redirecting to ' + u + '
' }, - default: function(){ - body = ''; + default: function () { + body = '' } - }); + }) // Respond - this.status(status); - this.set('Content-Length', Buffer.byteLength(body)); + this.status(status) + this.set('Content-Length', Buffer.byteLength(body)) if (this.req.method === 'HEAD') { - this.end(); + this.end() } else { - this.end(body); + this.end(body) } -}; +} /** * Add `field` to Vary. If already present in the Vary set, then @@ -878,11 +878,11 @@ res.redirect = function redirect(url) { * @public */ -res.vary = function(field){ - vary(this, field); +res.vary = function (field) { + vary(this, field) - return this; -}; + return this +} /** * Render `view` with the given `options` and optional callback `fn`. @@ -897,121 +897,121 @@ res.vary = function(field){ * @public */ -res.render = function render(view, options, callback) { - var app = this.req.app; - var done = callback; - var opts = options || {}; - var req = this.req; - var self = this; +res.render = function render (view, options, callback) { + const app = this.req.app + let done = callback + let opts = options || {} + const req = this.req + const self = this // support callback function as second arg if (typeof options === 'function') { - done = options; - opts = {}; + done = options + opts = {} } // merge res.locals - opts._locals = self.locals; + opts._locals = self.locals // default callback to respond done = done || function (err, str) { - if (err) return req.next(err); - self.send(str); - }; + if (err) return req.next(err) + self.send(str) + } // render - app.render(view, opts, done); -}; + app.render(view, opts, done) +} // pipe the send file stream -function sendfile(res, file, options, callback) { - var done = false; - var streaming; +function sendfile (res, file, options, callback) { + let done = false + let streaming // request aborted - function onaborted() { - if (done) return; - done = true; + function onaborted () { + if (done) return + done = true - var err = new Error('Request aborted'); - err.code = 'ECONNABORTED'; - callback(err); + const err = new Error('Request aborted') + err.code = 'ECONNABORTED' + callback(err) } // directory - function ondirectory() { - if (done) return; - done = true; + function ondirectory () { + if (done) return + done = true - var err = new Error('EISDIR, read'); - err.code = 'EISDIR'; - callback(err); + const err = new Error('EISDIR, read') + err.code = 'EISDIR' + callback(err) } // errors - function onerror(err) { - if (done) return; - done = true; - callback(err); + function onerror (err) { + if (done) return + done = true + callback(err) } // ended - function onend() { - if (done) return; - done = true; - callback(); + function onend () { + if (done) return + done = true + callback() } // file - function onfile() { - streaming = false; + function onfile () { + streaming = false } // finished - function onfinish(err) { - if (err && err.code === 'ECONNRESET') return onaborted(); - if (err) return onerror(err); - if (done) return; + function onfinish (err) { + if (err && err.code === 'ECONNRESET') return onaborted() + if (err) return onerror(err) + if (done) return - setImmediate(function () { + setImmediate(() => { if (streaming !== false && !done) { - onaborted(); - return; + onaborted() + return } - if (done) return; - done = true; - callback(); - }); + if (done) return + done = true + callback() + }) } // streaming - function onstream() { - streaming = true; + function onstream () { + streaming = true } - file.on('directory', ondirectory); - file.on('end', onend); - file.on('error', onerror); - file.on('file', onfile); - file.on('stream', onstream); - onFinished(res, onfinish); + file.on('directory', ondirectory) + file.on('end', onend) + file.on('error', onerror) + file.on('file', onfile) + file.on('stream', onstream) + onFinished(res, onfinish) if (options.headers) { // set headers on successful transfer - file.on('headers', function headers(res) { - var obj = options.headers; - var keys = Object.keys(obj); + file.on('headers', (res) => { + const obj = options.headers + const keys = Object.keys(obj) - for (var i = 0; i < keys.length; i++) { - var k = keys[i]; - res.setHeader(k, obj[k]); + for (let i = 0; i < keys.length; i++) { + const k = keys[i] + res.setHeader(k, obj[k]) } - }); + }) } // pipe - file.pipe(res); + file.pipe(res) } /** @@ -1029,12 +1029,12 @@ function sendfile(res, file, options, callback) { function stringify (value, replacer, spaces, escape) { // v8 checks arguments.length for optimizing simple call // https://bugs.chromium.org/p/v8/issues/detail?id=4730 - var json = replacer || spaces + let json = replacer || spaces ? JSON.stringify(value, replacer, spaces) - : JSON.stringify(value); + : JSON.stringify(value) if (escape && typeof json === 'string') { - json = json.replace(/[<>&]/g, function (c) { + json = json.replace(/[<>&]/g, (c) => { switch (c.charCodeAt(0)) { case 0x3c: return '\\u003c' diff --git a/lib/utils.js b/lib/utils.js index 4f21e7ef1e3..0a60f7531db 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -5,28 +5,27 @@ * MIT Licensed */ -'use strict'; +'use strict' /** * Module dependencies. * @api private */ -var { METHODS } = require('node:http'); -var contentType = require('content-type'); -var etag = require('etag'); -var mime = require('mime-types') -var proxyaddr = require('proxy-addr'); -var qs = require('qs'); -var querystring = require('node:querystring'); -const { Buffer } = require('node:buffer'); - +const { METHODS } = require('node:http') +const contentType = require('content-type') +const etag = require('etag') +const mime = require('mime-types') +const proxyaddr = require('proxy-addr') +const qs = require('qs') +const querystring = require('node:querystring') +const { Buffer } = require('node:buffer') /** * A list of lowercased HTTP methods that are supported by Node.js. * @api private */ -exports.methods = METHODS.map((method) => method.toLowerCase()); +exports.methods = METHODS.map((method) => method.toLowerCase()) /** * Return strong ETag for `body`. @@ -58,11 +57,11 @@ exports.wetag = createETagGenerator({ weak: true }) * @api private */ -exports.normalizeType = function(type){ +exports.normalizeType = function (type) { return ~type.indexOf('/') ? acceptParams(type) : { value: (mime.lookup(type) || 'application/octet-stream'), params: {} } -}; +} /** * Normalize `types`, for example "html" becomes "text/html". @@ -72,10 +71,9 @@ exports.normalizeType = function(type){ * @api private */ -exports.normalizeTypes = function(types) { - return types.map(exports.normalizeType); -}; - +exports.normalizeTypes = function (types) { + return types.map(exports.normalizeType) +} /** * Parse accept params `str` returning an @@ -87,36 +85,36 @@ exports.normalizeTypes = function(types) { */ function acceptParams (str) { - var length = str.length; - var colonIndex = str.indexOf(';'); - var index = colonIndex === -1 ? length : colonIndex; - var ret = { value: str.slice(0, index).trim(), quality: 1, params: {} }; + const length = str.length + let colonIndex = str.indexOf(';') + let index = colonIndex === -1 ? length : colonIndex + const ret = { value: str.slice(0, index).trim(), quality: 1, params: {} } while (index < length) { - var splitIndex = str.indexOf('=', index); - if (splitIndex === -1) break; + const splitIndex = str.indexOf('=', index) + if (splitIndex === -1) break - var colonIndex = str.indexOf(';', index); - var endIndex = colonIndex === -1 ? length : colonIndex; + colonIndex = str.indexOf(';', index) + const endIndex = colonIndex === -1 ? length : colonIndex if (splitIndex > endIndex) { - index = str.lastIndexOf(';', splitIndex - 1) + 1; - continue; + index = str.lastIndexOf(';', splitIndex - 1) + 1 + continue } - var key = str.slice(index, splitIndex).trim(); - var value = str.slice(splitIndex + 1, endIndex).trim(); + const key = str.slice(index, splitIndex).trim() + const value = str.slice(splitIndex + 1, endIndex).trim() if (key === 'q') { - ret.quality = parseFloat(value); + ret.quality = parseFloat(value) } else { - ret.params[key] = value; + ret.params[key] = value } - index = endIndex + 1; + index = endIndex + 1 } - return ret; + return ret } /** @@ -127,28 +125,28 @@ function acceptParams (str) { * @api private */ -exports.compileETag = function(val) { - var fn; +exports.compileETag = function (val) { + let fn if (typeof val === 'function') { - return val; + return val } switch (val) { case true: case 'weak': - fn = exports.wetag; - break; + fn = exports.wetag + break case false: - break; + break case 'strong': - fn = exports.etag; - break; + fn = exports.etag + break default: - throw new TypeError('unknown value for etag function: ' + val); + throw new TypeError('unknown value for etag function: ' + val) } - return fn; + return fn } /** @@ -159,28 +157,28 @@ exports.compileETag = function(val) { * @api private */ -exports.compileQueryParser = function compileQueryParser(val) { - var fn; +exports.compileQueryParser = function compileQueryParser (val) { + let fn if (typeof val === 'function') { - return val; + return val } switch (val) { case true: case 'simple': - fn = querystring.parse; - break; + fn = querystring.parse + break case false: - break; + break case 'extended': - fn = parseExtendedQueryString; - break; + fn = parseExtendedQueryString + break default: - throw new TypeError('unknown value for query parser function: ' + val); + throw new TypeError('unknown value for query parser function: ' + val) } - return fn; + return fn } /** @@ -191,26 +189,26 @@ exports.compileQueryParser = function compileQueryParser(val) { * @api private */ -exports.compileTrust = function(val) { - if (typeof val === 'function') return val; +exports.compileTrust = function (val) { + if (typeof val === 'function') return val if (val === true) { // Support plain true/false - return function(){ return true }; + return function () { return true } } if (typeof val === 'number') { // Support trusting hop count - return function(a, i){ return i < val }; + return function (a, i) { return i < val } } if (typeof val === 'string') { // Support comma-separated values val = val.split(',') - .map(function (v) { return v.trim() }) + .map((v) => v.trim()) } - return proxyaddr.compile(val || []); + return proxyaddr.compile(val || []) } /** @@ -222,20 +220,20 @@ exports.compileTrust = function(val) { * @api private */ -exports.setCharset = function setCharset(type, charset) { +exports.setCharset = function setCharset (type, charset) { if (!type || !charset) { - return type; + return type } // parse type - var parsed = contentType.parse(type); + const parsed = contentType.parse(type) // set charset - parsed.parameters.charset = charset; + parsed.parameters.charset = charset // format type - return contentType.format(parsed); -}; + return contentType.format(parsed) +} /** * Create an ETag generator function, generating ETags with @@ -248,7 +246,7 @@ exports.setCharset = function setCharset(type, charset) { function createETagGenerator (options) { return function generateETag (body, encoding) { - var buf = !Buffer.isBuffer(body) + const buf = !Buffer.isBuffer(body) ? Buffer.from(body, encoding) : body @@ -264,8 +262,8 @@ function createETagGenerator (options) { * @private */ -function parseExtendedQueryString(str) { +function parseExtendedQueryString (str) { return qs.parse(str, { allowPrototypes: true - }); + }) } diff --git a/lib/view.js b/lib/view.js index d66b4a2d89c..383da24fc7a 100644 --- a/lib/view.js +++ b/lib/view.js @@ -6,34 +6,34 @@ * MIT Licensed */ -'use strict'; +'use strict' /** * Module dependencies. * @private */ -var debug = require('debug')('express:view'); -var path = require('node:path'); -var fs = require('node:fs'); +const debug = require('debug')('express:view') +const path = require('node:path') +const fs = require('node:fs') /** * Module variables. * @private */ -var dirname = path.dirname; -var basename = path.basename; -var extname = path.extname; -var join = path.join; -var resolve = path.resolve; +const dirname = path.dirname +const basename = path.basename +const extname = path.extname +const join = path.join +const resolve = path.resolve /** * Module exports. * @public */ -module.exports = View; +module.exports = View /** * Initialize a new `View` with the given `name`. @@ -49,36 +49,37 @@ module.exports = View; * @public */ -function View(name, options) { - var opts = options || {}; +function View (name, options) { + const opts = options || {} - this.defaultEngine = opts.defaultEngine; - this.ext = extname(name); - this.name = name; - this.root = opts.root; + this.defaultEngine = opts.defaultEngine + this.ext = extname(name) + this.name = name + this.root = opts.root if (!this.ext && !this.defaultEngine) { - throw new Error('No default engine was specified and no extension was provided.'); + throw new Error('No default engine was specified and no extension was provided.') } - var fileName = name; + let fileName = name if (!this.ext) { // get extension from default engine name this.ext = this.defaultEngine[0] !== '.' ? '.' + this.defaultEngine - : this.defaultEngine; + : this.defaultEngine - fileName += this.ext; + fileName += this.ext } if (!opts.engines[this.ext]) { // load engine - var mod = this.ext.slice(1) + const mod = this.ext.slice(1) debug('require "%s"', mod) // default engine export - var fn = require(mod).__express + // eslint-disable-next-line global-require + const fn = require(mod).__express if (typeof fn !== 'function') { throw new Error('Module "' + mod + '" does not provide a view engine.') @@ -88,10 +89,10 @@ function View(name, options) { } // store loaded engine - this.engine = opts.engines[this.ext]; + this.engine = opts.engines[this.ext] // lookup path - this.path = this.lookup(fileName); + this.path = this.lookup(fileName) } /** @@ -101,26 +102,26 @@ function View(name, options) { * @private */ -View.prototype.lookup = function lookup(name) { - var path; - var roots = [].concat(this.root); +View.prototype.lookup = function lookup (name) { + let path + const roots = [].concat(this.root) - debug('lookup "%s"', name); + debug('lookup "%s"', name) - for (var i = 0; i < roots.length && !path; i++) { - var root = roots[i]; + for (let i = 0; i < roots.length && !path; i++) { + const root = roots[i] // resolve the path - var loc = resolve(root, name); - var dir = dirname(loc); - var file = basename(loc); + const loc = resolve(root, name) + const dir = dirname(loc) + const file = basename(loc) // resolve the file - path = this.resolve(dir, file); + path = this.resolve(dir, file) } - return path; -}; + return path +} /** * Render with the given options. @@ -130,33 +131,33 @@ View.prototype.lookup = function lookup(name) { * @private */ -View.prototype.render = function render(options, callback) { - var sync = true; +View.prototype.render = function render (options, callback) { + let sync = true - debug('render "%s"', this.path); + debug('render "%s"', this.path) // render, normalizing sync callbacks - this.engine(this.path, options, function onRender() { + this.engine(this.path, options, function onRender () { if (!sync) { - return callback.apply(this, arguments); + return callback.apply(this, arguments) } // copy arguments - var args = new Array(arguments.length); - var cntx = this; + const args = new Array(arguments.length) + const cntx = this - for (var i = 0; i < arguments.length; i++) { - args[i] = arguments[i]; + for (let i = 0; i < arguments.length; i++) { + args[i] = arguments[i] } // force callback to be async - return process.nextTick(function renderTick() { - return callback.apply(cntx, args); - }); - }); + return process.nextTick(function renderTick () { + return callback.apply(cntx, args) + }) + }) - sync = false; -}; + sync = false +} /** * Resolve the file within the given directory. @@ -166,25 +167,25 @@ View.prototype.render = function render(options, callback) { * @private */ -View.prototype.resolve = function resolve(dir, file) { - var ext = this.ext; +View.prototype.resolve = function resolve (dir, file) { + const ext = this.ext //