Comparing version 6.5.8 to 7.0.0
326
bin/serve.js
@@ -5,140 +5,258 @@ #!/usr/bin/env node | ||
const path = require('path'); | ||
const https = require('https'); | ||
// Packages | ||
const Ajv = require('ajv'); | ||
const dotProp = require('dot-prop'); | ||
const checkForUpdate = require('update-check'); | ||
const chalk = require('chalk'); | ||
const micro = require('micro'); | ||
const args = require('args'); | ||
const compress = require('micro-compress'); | ||
const detect = require('detect-port'); | ||
const {coroutine} = require('bluebird'); | ||
const checkForUpdate = require('update-check'); | ||
const {red, bold} = require('chalk'); | ||
const nodeVersion = require('node-version'); | ||
const cert = require('openssl-self-signed-certificate'); | ||
const boxen = require('boxen'); | ||
const arg = require('arg'); | ||
const handler = require('serve-handler'); | ||
const schema = require('@zeit/schemas/deployment/config-static'); | ||
const fs = require('fs-extra'); | ||
// Utilities | ||
const pkg = require('../package'); | ||
const listening = require('../lib/listening'); | ||
const serverHandler = require('../lib/server'); | ||
const {options, minimist} = require('../lib/options'); | ||
// Throw an error if node version is too low | ||
if (nodeVersion.major < 6) { | ||
console.error( | ||
`${red( | ||
'Error!' | ||
)} Serve requires at least version 6 of Node. Please upgrade!` | ||
); | ||
process.exit(1); | ||
} | ||
const warning = message => chalk`{yellow WARNING:} ${message}`; | ||
const info = message => chalk`{magenta INFO:} ${message}`; | ||
const error = message => chalk`{red ERROR:} ${message}`; | ||
// Register the list of options | ||
args.options(options); | ||
const updateCheck = async isDebugging => { | ||
let update = null; | ||
// And initialize `args` | ||
const flags = args.parse(process.argv, {minimist}); | ||
try { | ||
update = await checkForUpdate(pkg); | ||
} catch (err) { | ||
const suffix = isDebugging ? ':' : ' (use `--debug` to see full error)'; | ||
console.error(warning(`Checking for updates failed${suffix}`)); | ||
// Figure out the content directory | ||
const [directory] = args.sub; | ||
if (isDebugging) { | ||
console.error(err); | ||
} | ||
} | ||
// Don't log anything to the console if silent mode is enabled | ||
if (flags.silent) { | ||
console.log = () => {}; | ||
} | ||
if (!update) { | ||
return; | ||
} | ||
process.env.ASSET_DIR = Math.random() | ||
.toString(36) | ||
.substr(2, 10); | ||
console.log(`${chalk.bgRed('UPDATE AVAILABLE')} The latest version of \`serve\` is ${update.latest}`); | ||
}; | ||
let current = process.cwd(); | ||
const getHelp = () => chalk` | ||
{bold.cyan serve} - Static file serving and directory listing | ||
if (directory) { | ||
current = path.resolve(process.cwd(), directory); | ||
} | ||
{bold USAGE} | ||
let ignoredFiles = ['.DS_Store', '.git/']; | ||
{bold $} {cyan serve} --help | ||
{bold $} {cyan serve} --version | ||
{bold $} {cyan serve} [-l {underline listen_uri} [-l ...]] [{underline directory}] | ||
if (flags.ignore && flags.ignore.length > 0) { | ||
ignoredFiles = ignoredFiles.concat(flags.ignore.split(',')); | ||
} | ||
By default, {cyan serve} will listen on {bold 0.0.0.0:3000} and serve the | ||
current working directory on that address. | ||
const handler = coroutine(function *run(req, res) { | ||
yield serverHandler(req, res, flags, current, ignoredFiles); | ||
}); | ||
Specifying a single {bold --listen} argument will overwrite the default, not supplement it. | ||
const httpsOpts = { | ||
key: cert.key, | ||
cert: cert.cert | ||
{bold OPTIONS} | ||
--help Shows this help message | ||
-v, --version Displays the current version of serve | ||
-l, --listen {underline listen_uri} Specify a URI endpoint on which to listen (see below) - | ||
more than one may be specified to listen in multiple places | ||
-d, --debug Show debugging information | ||
{bold ENDPOINTS} | ||
Listen endpoints (specified by the {bold --listen} or {bold -l} options above) instruct {cyan micro} | ||
to listen on one or more interfaces/ports, UNIX domain sockets, or Windows named pipes. | ||
For TCP (traditional host/port) endpoints: | ||
{bold $} {cyan serve} -l tcp://{underline hostname}:{underline 1234} | ||
For UNIX domain socket endpoints: | ||
{bold $} {cyan serve} -l unix:{underline /path/to/socket.sock} | ||
For Windows named pipe endpoints: | ||
{bold $} {cyan serve} -l pipe:\\\\.\\pipe\\{underline PipeName} | ||
`; | ||
const parseEndpoint = str => { | ||
const url = new URL(str); | ||
switch (url.protocol) { | ||
case 'pipe:': { | ||
// some special handling | ||
const cutStr = str.replace(/^pipe:/, ''); | ||
if (cutStr.slice(0, 4) !== '\\\\.\\') { | ||
throw new Error(`Invalid Windows named pipe endpoint: ${str}`); | ||
} | ||
return [cutStr]; | ||
} | ||
case 'unix:': | ||
if (!url.pathname) { | ||
throw new Error(`Invalid UNIX domain socket endpoint: ${str}`); | ||
} | ||
return [url.pathname]; | ||
case 'tcp:': | ||
url.port = url.port || '3000'; | ||
return [parseInt(url.port, 10), url.hostname]; | ||
default: | ||
throw new Error(`Unknown --listen endpoint scheme (protocol): ${url.protocol}`); | ||
} | ||
}; | ||
const microHttps = fn => | ||
https.createServer(httpsOpts, (req, res) => micro.run(req, res, fn)); | ||
const server = flags.ssl | ||
? microHttps(flags.unzipped ? handler : compress(handler)) | ||
: micro(flags.unzipped ? handler : compress(handler)); | ||
const registerShutdown = fn => { | ||
let run = false; | ||
let {port} = flags; | ||
const wrapper = () => { | ||
if (!run) { | ||
run = true; | ||
fn(); | ||
} | ||
}; | ||
detect(port).then(async open => { | ||
const {NODE_ENV} = process.env; | ||
process.on('SIGINT', wrapper); | ||
process.on('SIGTERM', wrapper); | ||
process.on('exit', wrapper); | ||
}; | ||
if (NODE_ENV !== 'production') { | ||
try { | ||
const update = await checkForUpdate(pkg); | ||
const startEndpoint = (endpoint, config) => { | ||
const server = micro(async (request, response) => handler(request, response, config)); | ||
if (update) { | ||
const message = `${bold( | ||
'UPDATE AVAILABLE:' | ||
)} The latest version of \`serve\` is ${update.latest}`; | ||
server.on('error', err => { | ||
console.error('serve:', err.stack); | ||
process.exit(1); | ||
}); | ||
console.log( | ||
boxen(message, { | ||
padding: 1, | ||
borderColor: 'green', | ||
margin: 1 | ||
}) | ||
); | ||
} | ||
server.listen(...endpoint, () => { | ||
const details = server.address(); | ||
registerShutdown(() => server.close()); | ||
// `micro` is designed to run only in production, so | ||
// this message is perfectly for prod | ||
if (typeof details === 'string') { | ||
console.log(info(`Accepting connections at ${details}`)); | ||
} else if (typeof details === 'object' && details.port) { | ||
console.log(info(`Accepting connections at http://localhost:${details.port}`)); | ||
} else { | ||
console.log(info('Accepting connections')); | ||
} | ||
}); | ||
}; | ||
const loadConfig = async (cwd, entry) => { | ||
const paths = { | ||
'serve.json': null, | ||
'now.json': 'static', | ||
'package.json': 'now.static' | ||
}; | ||
const config = {}; | ||
for (const file of Object.keys(paths)) { | ||
const location = path.join(entry, file); | ||
const prop = paths[file]; | ||
try { | ||
const content = await fs.readJSON(location); | ||
Object.assign(config, prop ? dotProp.get(content, prop) : content); | ||
} catch (err) { | ||
console.log( | ||
boxen( | ||
`${bold( | ||
'UPDATE CHECK FAILED:' | ||
)} ${err.message}`, { | ||
padding: 1, | ||
borderColor: 'red', | ||
margin: 1 | ||
} | ||
) | ||
); | ||
continue; | ||
} | ||
if (Object.keys(config).length !== 0) { | ||
console.log(info(`Discovered configuration in \`${file}\``)); | ||
break; | ||
} | ||
} | ||
let inUse = open !== port; | ||
if (entry) { | ||
const {public} = config; | ||
config.public = path.relative(cwd, (public ? path.join(entry, public) : entry)); | ||
} | ||
if (inUse) { | ||
port = open; | ||
if (Object.keys(config).length !== 0) { | ||
const ajv = new Ajv(); | ||
const validateSchema = ajv.compile(schema); | ||
inUse = { | ||
old: flags.port, | ||
open | ||
}; | ||
if (!validateSchema(config)) { | ||
const defaultMessage = error('The configuration you provided is wrong:'); | ||
const {message, params} = validateSchema.errors[0]; | ||
console.error(`${defaultMessage}\n${message}\n${JSON.stringify(params)}`); | ||
process.exit(1); | ||
} | ||
} | ||
const listenArgs = [ | ||
server, | ||
current, | ||
inUse, | ||
flags.clipless !== true, | ||
flags.open, | ||
flags.ssl, | ||
flags.local | ||
]; | ||
return config; | ||
}; | ||
if (flags.local) { | ||
server.listen(port, 'localhost', listening.bind(this, ...listenArgs)); | ||
} else { | ||
server.listen(port, listening.bind(this, ...listenArgs)); | ||
(async () => { | ||
let args = null; | ||
try { | ||
args = arg({ | ||
'--help': Boolean, | ||
'--version': Boolean, | ||
'--listen': [parseEndpoint], | ||
'--debug': Boolean, | ||
'-h': '--help', | ||
'-v': '--version', | ||
'-l': '--listen', | ||
'-d': '--debug' | ||
}); | ||
} catch (err) { | ||
console.error(error(err.message)); | ||
process.exit(1); | ||
} | ||
}); | ||
await updateCheck(args['--debug']); | ||
if (args['--version']) { | ||
console.log(pkg.version); | ||
return; | ||
} | ||
if (args['--help']) { | ||
console.log(getHelp()); | ||
return; | ||
} | ||
if (!args['--listen']) { | ||
// Default endpoint | ||
args['--listen'] = [[3000]]; | ||
} | ||
if (args._.length > 1) { | ||
console.error(error('Please provide one path argument at maximum')); | ||
process.exit(1); | ||
} | ||
const cwd = process.cwd(); | ||
const entry = args._.length > 0 ? path.join(cwd, args._[0]) : cwd; | ||
const config = await loadConfig(cwd, entry); | ||
for (const endpoint of args['--listen']) { | ||
startEndpoint(endpoint, config); | ||
} | ||
registerShutdown(() => { | ||
console.log(`\n${info('Gracefully shutting down. Please wait...')}`); | ||
process.on('SIGINT', () => { | ||
console.log(`\n${warning('Force-closing all open sockets...')}`); | ||
process.exit(0); | ||
}); | ||
}); | ||
})(); |
{ | ||
"name": "serve", | ||
"version": "6.5.8", | ||
"version": "7.0.0", | ||
"description": "Static file serving and directory listing", | ||
"scripts": { | ||
"test": "yarn lint && NODE_ENV=test nyc ava", | ||
"test": "yarn lint", | ||
"lint": "zeit-eslint --ext .jsx,.js .", | ||
@@ -11,15 +11,8 @@ "lint-staged": "git diff --diff-filter=ACMRT --cached --name-only '*.js' '*.jsx' | xargs zeit-eslint" | ||
"files": [ | ||
"bin", | ||
"lib", | ||
"assets", | ||
"views" | ||
"bin" | ||
], | ||
"repository": "zeit/serve", | ||
"main": "./lib/api.js", | ||
"bin": { | ||
"serve": "./bin/serve.js" | ||
}, | ||
"engines": { | ||
"node": ">=7.6.0" | ||
}, | ||
"keywords": [ | ||
@@ -36,32 +29,4 @@ "now", | ||
"@zeit/git-hooks": "0.1.4", | ||
"ava": "0.25.0", | ||
"eslint": "4.19.1", | ||
"node-fetch": "2.1.2", | ||
"nyc": "11.8.0", | ||
"test-listen": "1.1.0" | ||
"eslint": "4.19.1" | ||
}, | ||
"dependencies": { | ||
"args": "4.0.0", | ||
"basic-auth": "2.0.0", | ||
"bluebird": "3.5.1", | ||
"boxen": "1.3.0", | ||
"chalk": "2.4.1", | ||
"clipboardy": "1.2.3", | ||
"dargs": "5.1.0", | ||
"detect-port": "1.2.3", | ||
"filesize": "3.6.1", | ||
"fs-extra": "6.0.1", | ||
"handlebars": "4.0.11", | ||
"ip": "1.1.5", | ||
"micro": "9.3.1", | ||
"micro-compress": "1.0.0", | ||
"mime-types": "2.1.18", | ||
"node-version": "1.1.3", | ||
"openssl-self-signed-certificate": "1.1.6", | ||
"opn": "5.3.0", | ||
"path-is-inside": "1.0.2", | ||
"path-type": "3.0.0", | ||
"send": "0.16.2", | ||
"update-check": "1.5.1" | ||
}, | ||
"eslintConfig": { | ||
@@ -74,3 +39,14 @@ "extends": [ | ||
"pre-commit": "lint-staged" | ||
}, | ||
"dependencies": { | ||
"@zeit/schemas": "1.1.1", | ||
"ajv": "6.5.0", | ||
"arg": "2.0.0", | ||
"chalk": "2.4.1", | ||
"dot-prop": "4.2.0", | ||
"fs-extra": "6.0.1", | ||
"micro": "9.3.1", | ||
"serve-handler": "2.3.10", | ||
"update-check": "1.5.2" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 5 instances in 1 package
9
3
2
1
11127
4
199
59
+ Added@zeit/schemas@1.1.1
+ Addedajv@6.5.0
+ Addedarg@2.0.0
+ Addeddot-prop@4.2.0
+ Addedserve-handler@2.3.10
+ Added@zeit/schemas@1.1.1(transitive)
+ Addedajv@6.5.0(transitive)
+ Addedbalanced-match@1.0.2(transitive)
+ Addedbrace-expansion@1.1.11(transitive)
+ Addedconcat-map@0.0.1(transitive)
+ Addeddot-prop@4.2.0(transitive)
+ Addedfast-deep-equal@2.0.1(transitive)
+ Addedfast-json-stable-stringify@2.1.0(transitive)
+ Addedfast-url-parser@1.1.3(transitive)
+ Addedglob-slash@1.0.0(transitive)
+ Addedglob-slasher@1.0.1(transitive)
+ Addedis-obj@1.0.1(transitive)
+ Addedjson-schema-traverse@0.3.1(transitive)
+ Addedlodash@4.17.21(transitive)
+ Addedlodash._objecttypes@2.4.1(transitive)
+ Addedlodash.isobject@2.4.1(transitive)
+ Addedmime@2.3.1(transitive)
+ Addedminimatch@3.0.4(transitive)
+ Addedpath-to-regexp@2.2.1(transitive)
+ Addedpunycode@1.4.12.3.1(transitive)
+ Addedsafe-buffer@5.2.1(transitive)
+ Addedserve-handler@2.3.10(transitive)
+ Addedtoxic@1.0.1(transitive)
+ Addedupdate-check@1.5.2(transitive)
+ Addeduri-js@4.4.1(transitive)
- Removedargs@4.0.0
- Removedbasic-auth@2.0.0
- Removedbluebird@3.5.1
- Removedboxen@1.3.0
- Removedclipboardy@1.2.3
- Removeddargs@5.1.0
- Removeddetect-port@1.2.3
- Removedfilesize@3.6.1
- Removedhandlebars@4.0.11
- Removedip@1.1.5
- Removedmicro-compress@1.0.0
- Removedmime-types@2.1.18
- Removednode-version@1.1.3
- Removedopn@5.3.0
- Removedpath-is-inside@1.0.2
- Removedpath-type@3.0.0
- Removedsend@0.16.2
- Removedaccepts@1.3.8(transitive)
- Removedaddress@1.2.2(transitive)
- Removedalign-text@0.1.4(transitive)
- Removedamdefine@1.0.1(transitive)
- Removedansi-align@2.0.0(transitive)
- Removedansi-regex@3.0.1(transitive)
- Removedarch@2.2.0(transitive)
- Removedargs@4.0.0(transitive)
- Removedasync@1.5.2(transitive)
- Removedbasic-auth@2.0.0(transitive)
- Removedbluebird@3.5.1(transitive)
- Removedboxen@1.3.0(transitive)
- Removedcamelcase@1.2.14.1.05.0.0(transitive)
- Removedcenter-align@0.1.3(transitive)
- Removedchalk@2.3.2(transitive)
- Removedcli-boxes@1.0.0(transitive)
- Removedclipboardy@1.2.3(transitive)
- Removedcliui@2.1.0(transitive)
- Removedcompressible@2.0.18(transitive)
- Removedcompression@1.7.4(transitive)
- Removedcross-spawn@5.1.0(transitive)
- Removeddargs@5.1.0(transitive)
- Removeddebug@2.6.9(transitive)
- Removeddecamelize@1.2.0(transitive)
- Removeddepd@1.1.2(transitive)
- Removeddestroy@1.0.4(transitive)
- Removeddetect-port@1.2.3(transitive)
- Removedee-first@1.1.1(transitive)
- Removedencodeurl@1.0.2(transitive)
- Removedescape-html@1.0.3(transitive)
- Removedetag@1.8.1(transitive)
- Removedexeca@0.7.00.8.0(transitive)
- Removedfilesize@3.6.1(transitive)
- Removedfresh@0.5.2(transitive)
- Removedget-stream@3.0.0(transitive)
- Removedhandlebars@4.0.11(transitive)
- Removedip@1.1.5(transitive)
- Removedis-buffer@1.1.6(transitive)
- Removedis-fullwidth-code-point@2.0.0(transitive)
- Removedis-wsl@1.1.0(transitive)
- Removedisexe@2.0.0(transitive)
- Removedkind-of@3.2.2(transitive)
- Removedlazy-cache@1.0.4(transitive)
- Removedleven@2.1.0(transitive)
- Removedlongest@1.0.1(transitive)
- Removedlru-cache@4.1.5(transitive)
- Removedmicro-compress@1.0.0(transitive)
- Removedmime@1.4.1(transitive)
- Removedmime-db@1.33.01.52.0(transitive)
- Removedmime-types@2.1.182.1.35(transitive)
- Removedminimist@0.0.10(transitive)
- Removedmri@1.1.0(transitive)
- Removedms@2.0.0(transitive)
- Removednegotiator@0.6.3(transitive)
- Removednode-version@1.1.3(transitive)
- Removednpm-run-path@2.0.2(transitive)
- Removedon-finished@2.3.0(transitive)
- Removedon-headers@1.0.2(transitive)
- Removedopenssl-self-signed-certificate@1.1.6(transitive)
- Removedopn@5.3.0(transitive)
- Removedoptimist@0.6.1(transitive)
- Removedp-finally@1.0.0(transitive)
- Removedpath-is-inside@1.0.2(transitive)
- Removedpath-key@2.0.1(transitive)
- Removedpath-type@3.0.0(transitive)
- Removedpify@3.0.0(transitive)
- Removedpseudomap@1.0.2(transitive)
- Removedrange-parser@1.2.1(transitive)
- Removedrepeat-string@1.6.1(transitive)
- Removedright-align@0.1.3(transitive)
- Removedsafe-buffer@5.1.15.1.2(transitive)
- Removedsend@0.16.2(transitive)
- Removedshebang-command@1.2.0(transitive)
- Removedshebang-regex@1.0.0(transitive)
- Removedsignal-exit@3.0.7(transitive)
- Removedsource-map@0.4.40.5.7(transitive)
- Removedstatuses@1.4.0(transitive)
- Removedstring-width@2.1.1(transitive)
- Removedstrip-ansi@4.0.0(transitive)
- Removedstrip-eof@1.0.0(transitive)
- Removedterm-size@1.2.0(transitive)
- Removeduglify-js@2.8.29(transitive)
- Removeduglify-to-browserify@1.0.2(transitive)
- Removedupdate-check@1.5.1(transitive)
- Removedvary@1.1.2(transitive)
- Removedwhich@1.3.1(transitive)
- Removedwidest-line@2.0.1(transitive)
- Removedwindow-size@0.1.0(transitive)
- Removedwordwrap@0.0.20.0.3(transitive)
- Removedyallist@2.1.2(transitive)
- Removedyargs@3.10.0(transitive)
Updatedupdate-check@1.5.2