Socket
Socket
Sign inDemoInstall

serve

Package Overview
Dependencies
Maintainers
4
Versions
152
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

serve - npm Package Compare versions

Comparing version 6.5.5 to 6.5.6

166

bin/serve.js
#!/usr/bin/env node
// Native
const path = require('path')
const https = require('https')
const path = require('path');
const https = require('https');
// Packages
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 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');
// Utilities
const pkg = require('../package')
const listening = require('../lib/listening')
const serverHandler = require('../lib/server')
const { options, minimist } = require('../lib/options')
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)
console.error(
`${red(
'Error!'
)} Serve requires at least version 6 of Node. Please upgrade!`
);
process.exit(1);
}
// Register the list of options
args.options(options)
args.options(options);
// And initialize `args`
const flags = args.parse(process.argv, { minimist })
const flags = args.parse(process.argv, {minimist});
// Figure out the content directory
const [directory] = args.sub
const [directory] = args.sub;
// Don't log anything to the console if silent mode is enabled
if (flags.silent) {
console.log = () => {}
console.log = () => {};
}
process.env.ASSET_DIR = Math.random()
.toString(36)
.substr(2, 10)
.toString(36)
.substr(2, 10);
let current = process.cwd()
let current = process.cwd();
if (directory) {
current = path.resolve(process.cwd(), directory)
current = path.resolve(process.cwd(), directory);
}
let ignoredFiles = ['.DS_Store', '.git/']
let ignoredFiles = ['.DS_Store', '.git/'];
if (flags.ignore && flags.ignore.length > 0) {
ignoredFiles = ignoredFiles.concat(flags.ignore.split(','))
ignoredFiles = ignoredFiles.concat(flags.ignore.split(','));
}
const handler = coroutine(function*(req, res) {
yield serverHandler(req, res, flags, current, ignoredFiles)
})
const handler = coroutine(function *run(req, res) {
yield serverHandler(req, res, flags, current, ignoredFiles);
});
const httpsOpts = {
key: cert.key,
cert: cert.cert,
passphrase: cert.passphrase
}
key: cert.key,
cert: cert.cert,
passphrase: cert.passphrase
};
const microHttps = fn =>
https.createServer(httpsOpts, (req, res) => micro.run(req, res, 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))
? microHttps(flags.unzipped ? handler : compress(handler))
: micro(flags.unzipped ? handler : compress(handler));
let { port } = flags
let {port} = flags;
detect(port).then(async open => {
const { NODE_ENV } = process.env
const {NODE_ENV} = process.env;
if (NODE_ENV !== 'production') {
const update = await checkForUpdate(pkg)
if (NODE_ENV !== 'production') {
const update = await checkForUpdate(pkg);
if (update) {
const message = `${bold(
'UPDATE AVAILABLE:'
)} The latest version of \`serve\` is ${update.latest}`
console.log(
boxen(message, {
padding: 1,
borderColor: 'green',
margin: 1
})
)
}
}
if (update) {
const message = `${bold(
'UPDATE AVAILABLE:'
)} The latest version of \`serve\` is ${update.latest}`;
let inUse = open !== port
console.log(
boxen(message, {
padding: 1,
borderColor: 'green',
margin: 1
})
);
}
}
if (inUse) {
port = open
let inUse = open !== port;
inUse = {
old: flags.port,
open
}
}
if (inUse) {
port = open;
const listenArgs = [
server,
current,
inUse,
flags.clipless !== true,
flags.open,
flags.ssl,
flags.local
]
inUse = {
old: flags.port,
open
};
}
if (flags.local) {
server.listen(port, 'localhost', listening.bind(this, ...listenArgs))
} else {
server.listen(port, listening.bind(this, ...listenArgs))
}
})
const listenArgs = [
server,
current,
inUse,
flags.clipless !== true,
flags.open,
flags.ssl,
flags.local
];
if (flags.local) {
server.listen(port, 'localhost', listening.bind(this, ...listenArgs));
} else {
server.listen(port, listening.bind(this, ...listenArgs));
}
});
// Native
const { spawn } = require('child_process')
const path = require('path')
const {spawn} = require('child_process');
const path = require('path');
// Packages
const dargs = require('dargs')
const dargs = require('dargs');
module.exports = (directory = process.cwd(), options = {}) => {
const scriptPath = path.join(__dirname, '..', 'bin', 'serve.js')
const aliases = { cors: 'C' }
const scriptPath = path.join(__dirname, '..', 'bin', 'serve.js');
const aliases = {cors: 'C'};
options._ = [directory] // Let dargs handle the directory argument
options._ = [directory]; // Let dargs handle the directory argument
// The CLI only understands comma-separated values for ignored files
// So we join the string array with commas
if (options.ignore) {
options.ignore = options.ignore.join(',')
}
// The CLI only understands comma-separated values for ignored files
// So we join the string array with commas
if (options.ignore) {
options.ignore = options.ignore.join(',');
}
const args = [scriptPath, ...dargs(options, { aliases })]
const args = [scriptPath, ...dargs(options, {aliases})];
const cli = spawn('node', args, {
stdio: 'inherit'
})
const cli = spawn('node', args, {
stdio: 'inherit'
});
return {
stop() {
cli.kill()
}
}
}
return {
stop() {
cli.kill();
}
};
};
// Native
const { basename } = require('path')
const {basename} = require('path');
// Packages
const { write: copy } = require('clipboardy')
const ip = require('ip')
const pathType = require('path-type')
const chalk = require('chalk')
const boxen = require('boxen')
const { coroutine } = require('bluebird')
const opn = require('opn')
const {write: copy} = require('clipboardy');
const ip = require('ip');
const pathType = require('path-type');
const chalk = require('chalk');
const boxen = require('boxen');
const {coroutine} = require('bluebird');
const opn = require('opn');
module.exports = coroutine(function*(
server,
current,
inUse,
clipboard,
open,
ssl,
isLocal
module.exports = coroutine(function *firework(
server,
current,
inUse,
clipboard,
open,
ssl,
isLocal
) {
const details = server.address()
const { isTTY } = process.stdout
const details = server.address();
const {isTTY} = process.stdout;
const shutdown = () => {
server.close()
const shutdown = () => {
server.close();
process.exit(0);
};
// eslint-disable-next-line unicorn/no-process-exit
process.exit(0)
}
process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown)
process.on('SIGTERM', shutdown)
let isDir;
let isDir
try {
isDir = yield pathType.dir(current);
} catch (err) {
isDir = false;
}
try {
isDir = yield pathType.dir(current)
} catch (err) {
isDir = false
}
if (!isDir) {
const base = basename(current);
if (!isDir) {
const base = basename(current)
console.error(
chalk.red(`Specified directory ${chalk.bold(`"${base}"`)} doesn't exist!`)
);
console.error(
chalk.red(`Specified directory ${chalk.bold(`"${base}"`)} doesn't exist!`)
)
process.exit(1);
}
// eslint-disable-next-line unicorn/no-process-exit
process.exit(1)
}
if (process.env.NODE_ENV !== 'production') {
let message = chalk.green('Serving!');
if (process.env.NODE_ENV !== 'production') {
let message = chalk.green('Serving!')
if (inUse) {
message += ` ${chalk.red(`(on port ${inUse.open}, because ${inUse.old} is already in use)`)}`;
}
if (inUse) {
message +=
' ' +
chalk.red(
`(on port ${inUse.open}, because ${inUse.old} is already in use)`
)
}
message += '\n\n';
message += '\n\n'
const localURL = `http${ssl ? 's' : ''}://localhost:${details.port}`;
message += `- ${chalk.bold('Local: ')} ${localURL}`;
const localURL = `http${ssl ? 's' : ''}://localhost:${details.port}`
message += `- ${chalk.bold('Local: ')} ${localURL}`
if (!isLocal) {
try {
const ipAddress = ip.address();
const url = `http${ssl ? 's' : ''}://${ipAddress}:${details.port}`;
if (!isLocal) {
try {
const ipAddress = ip.address()
const url = `http${ssl ? 's' : ''}://${ipAddress}:${details.port}`
message += `\n- ${chalk.bold('On Your Network: ')} ${url}`;
} catch (err) {
console.error(err);
}
}
message += `\n- ${chalk.bold('On Your Network: ')} ${url}`
} catch (err) {}
}
if (isTTY && clipboard) {
try {
yield copy(localURL);
message += `\n\n${chalk.grey('Copied local address to clipboard!')}`;
} catch (err) {
console.error(err);
}
}
if (isTTY && clipboard) {
try {
yield copy(localURL)
message += `\n\n${chalk.grey('Copied local address to clipboard!')}`
} catch (err) {}
}
if (isTTY && open) {
try {
opn(localURL);
} catch (err) {
console.error(err);
}
}
if (isTTY && open) {
try {
opn(localURL)
} catch (err) {}
}
if (!isTTY) {
console.log(`serve: Running on port ${details.port}`);
return;
}
if (!isTTY) {
console.log(`serve: Running on port ${details.port}`)
return
}
console.log(
boxen(message, {
padding: 1,
borderColor: 'green',
margin: 1
})
)
}
})
console.log(
boxen(message, {
padding: 1,
borderColor: 'green',
margin: 1
})
);
}
});

@@ -1,90 +0,90 @@

const envPort = process.env.PORT
const defaultPort = isNaN(envPort) ? 5000 : parseInt(envPort, 10)
const envPort = process.env.PORT;
const defaultPort = isNaN(envPort) ? 5000 : parseInt(envPort, 10);
exports.options = [
{
name: 'port',
description: 'Port to listen on',
defaultValue: defaultPort
},
{
name: 'cache',
description: 'Time in milliseconds for caching files in the browser'
},
{
name: 'single',
description: 'Serve single page applications (sets `-c` to 1 day)'
},
{
name: 'unzipped',
description: 'Disable GZIP compression'
},
{
name: 'ignore',
description: 'Files and directories to ignore'
},
{
name: 'auth',
description: 'Serve behind basic auth'
},
{
name: 'cors',
description: 'Setup * CORS headers to allow requests from any origin',
defaultValue: false
},
{
name: 'silent',
description: `Don't log anything to the console`
},
{
name: ['n', 'clipless'],
description: `Don't copy address to clipboard`,
defaultValue: false
},
{
name: 'open',
description: 'Open local address in browser',
defaultValue: false
},
{
name: 'treeless',
description: `Don't display statics tree`,
defaultValue: false
},
{
name: ['T', 'ssl'],
description: 'Serve content using SSL',
defaultValue: false
},
{
name: 'local',
description: 'Serve content only on localhost',
defaultValue: false
}
]
{
name: 'port',
description: 'Port to listen on',
defaultValue: defaultPort
},
{
name: 'cache',
description: 'Time in milliseconds for caching files in the browser'
},
{
name: 'single',
description: 'Serve single page applications (sets `-c` to 1 day)'
},
{
name: 'unzipped',
description: 'Disable GZIP compression'
},
{
name: 'ignore',
description: 'Files and directories to ignore'
},
{
name: 'auth',
description: 'Serve behind basic auth'
},
{
name: 'cors',
description: 'Setup * CORS headers to allow requests from any origin',
defaultValue: false
},
{
name: 'silent',
description: `Don't log anything to the console`
},
{
name: ['n', 'clipless'],
description: `Don't copy address to clipboard`,
defaultValue: false
},
{
name: 'open',
description: 'Open local address in browser',
defaultValue: false
},
{
name: 'treeless',
description: `Don't display statics tree`,
defaultValue: false
},
{
name: ['T', 'ssl'],
description: 'Serve content using SSL',
defaultValue: false
},
{
name: 'local',
description: 'Serve content only on localhost',
defaultValue: false
}
];
exports.minimist = {
alias: {
a: 'auth',
C: 'cors',
S: 'silent',
s: 'single',
u: 'unzipped',
n: 'clipless',
o: 'open',
t: 'treeless',
l: 'local'
},
boolean: [
'auth',
'cors',
'silent',
'single',
'unzipped',
'clipless',
'open',
'treeless',
'ssl',
'local'
]
}
'alias': {
a: 'auth',
C: 'cors',
S: 'silent',
s: 'single',
u: 'unzipped',
n: 'clipless',
o: 'open',
t: 'treeless',
l: 'local'
},
'boolean': [
'auth',
'cors',
'silent',
'single',
'unzipped',
'clipless',
'open',
'treeless',
'ssl',
'local'
]
};
// Native
const path = require('path')
const path = require('path');
// Packages
const pathType = require('path-type')
const filesize = require('filesize')
const fs = require('fs-extra')
const { coroutine } = require('bluebird')
const pathType = require('path-type');
const filesize = require('filesize');
const fs = require('fs-extra');
const {coroutine} = require('bluebird');
// Utilities
const prepareView = require('./view')
const prepareView = require('./view');
module.exports = coroutine(function*(port, current, dir, ignoredFiles) {
let files = []
const subPath = path.relative(current, dir)
module.exports = coroutine(function *renderer(port, current, dir, ignoredFiles) {
let files = [];
const subPath = path.relative(current, dir);
if (!fs.existsSync(dir)) {
return false
}
if (!fs.existsSync(dir)) {
return false;
}
try {
files = yield fs.readdir(dir)
} catch (err) {
throw err
}
try {
files = yield fs.readdir(dir);
} catch (err) {
throw err;
}
for (const file of files) {
const filePath = path.resolve(dir, file)
const index = files.indexOf(file)
const details = path.parse(filePath)
for (const file of files) {
const filePath = path.resolve(dir, file);
const index = files.indexOf(file);
const details = path.parse(filePath);
details.relative = path.join(subPath, details.base)
details.relative = path.join(subPath, details.base);
if (yield pathType.dir(filePath)) {
details.base += '/'
} else {
details.ext = details.ext.split('.')[1] || 'txt'
if (yield pathType.dir(filePath)) {
details.base += '/';
} else {
details.ext = details.ext.split('.')[1] || 'txt';
let fileStats
let fileStats;
try {
fileStats = yield fs.stat(filePath)
} catch (err) {
throw err
}
try {
fileStats = yield fs.stat(filePath);
} catch (err) {
throw err;
}
details.size = filesize(fileStats.size, { round: 0 })
}
details.size = filesize(fileStats.size, {round: 0});
}
details.title = details.base
details.title = details.base;
if (ignoredFiles.indexOf(details.base) > -1) {
delete files[index]
} else {
files[files.indexOf(file)] = details
}
}
if (ignoredFiles.indexOf(details.base) > -1) {
delete files[index];
} else {
files[files.indexOf(file)] = details;
}
}
const directory = path.join(path.basename(current), subPath, '/')
const pathParts = directory.split(path.sep)
const directory = path.join(path.basename(current), subPath, '/');
const pathParts = directory.split(path.sep);
// Sort to list directories first, then sort alphabetically
files = files.sort((a, b) => {
const aIsDir = a.base.endsWith('/')
const bIsDir = b.base.endsWith('/')
if (aIsDir && !bIsDir) return -1
if (bIsDir && !aIsDir) return 1
if (a.base > b.base) return 1
if (a.base < b.base) return -1
return 0
})
// Sort to list directories first, then sort alphabetically
files = files.sort((a, b) => {
const aIsDir = a.base.endsWith('/');
const bIsDir = b.base.endsWith('/');
if (aIsDir && !bIsDir) {
return -1;
}
if (bIsDir && !aIsDir) {
return 1;
}
if (a.base > b.base) {
return 1;
}
if (a.base < b.base) {
return -1;
}
return 0;
});
// Add parent directory to the head of the sorted files array
if (dir.indexOf(current + '/') > -1) {
const directoryPath = [...pathParts]
directoryPath.shift()
// Add parent directory to the head of the sorted files array
if (dir.indexOf(`${current}/`) > -1) {
const directoryPath = [...pathParts];
directoryPath.shift();
files.unshift({
base: '..',
relative: path.join(...directoryPath, '..'),
title: path.join(...pathParts.slice(0, -2), '/')
})
}
files.unshift({
base: '..',
relative: path.join(...directoryPath, '..'),
title: path.join(...pathParts.slice(0, -2), '/')
});
}
const render = prepareView()
const render = prepareView();
const paths = []
pathParts.pop()
const paths = [];
pathParts.pop();
for (const part in pathParts) {
if (!{}.hasOwnProperty.call(pathParts, part)) {
continue
}
for (const part in pathParts) {
if (!{}.hasOwnProperty.call(pathParts, part)) {
continue;
}
let before = 0
const parents = []
let before = 0;
const parents = [];
while (before <= part) {
parents.push(pathParts[before])
before++
}
while (before <= part) {
parents.push(pathParts[before]);
before++;
}
parents.shift()
parents.shift();
paths.push({
name: pathParts[part],
url: parents.join('/')
})
}
paths.push({
name: pathParts[part],
url: parents.join('/')
});
}
const details = {
port,
files,
assetDir: process.env.ASSET_DIR,
directory,
nodeVersion: process.version.split('v')[1],
paths
}
const details = {
port,
files,
assetDir: process.env.ASSET_DIR,
directory,
nodeVersion: process.version.split('v')[1],
paths
};
return render(details)
})
return render(details);
});
// Native
const path = require('path')
const { parse, format } = require('url')
const path = require('path');
const {parse, format} = require('url');
// Packages
const micro = require('micro')
const auth = require('basic-auth')
const { red } = require('chalk')
const fs = require('fs-extra')
const pathType = require('path-type')
const mime = require('mime-types')
const stream = require('send')
const { coroutine } = require('bluebird')
const isPathInside = require('path-is-inside')
const micro = require('micro');
const auth = require('basic-auth');
const {red} = require('chalk');
const fs = require('fs-extra');
const pathType = require('path-type');
const mime = require('mime-types');
const stream = require('send');
const {coroutine} = require('bluebird');
const isPathInside = require('path-is-inside');
// Utilities
const renderDirectory = require('./render')
const renderDirectory = require('./render');
module.exports = coroutine(function*(req, res, flags, current, ignoredFiles) {
const headers = {}
module.exports = coroutine(function *server(req, res, flags, current, ignoredFiles) {
const headers = {};
if (flags.cors) {
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Headers'] =
'Origin, X-Requested-With, Content-Type, Accept, Range'
}
if (flags.cors) {
headers['Access-Control-Allow-Origin'] = '*';
headers['Access-Control-Allow-Headers'] =
'Origin, X-Requested-With, Content-Type, Accept, Range';
}
for (const header in headers) {
if (!{}.hasOwnProperty.call(headers, header)) {
continue
}
for (const header in headers) {
if (!{}.hasOwnProperty.call(headers, header)) {
continue;
}
res.setHeader(header, headers[header])
}
res.setHeader(header, headers[header]);
}
if (flags.auth) {
const credentials = auth(req)
if (flags.auth) {
const credentials = auth(req);
if (!process.env.SERVE_USER || !process.env.SERVE_PASSWORD) {
const error =
if (!process.env.SERVE_USER || !process.env.SERVE_PASSWORD) {
const error =
'The environment variables "SERVE_USER" ' +
'and/or "SERVE_PASSWORD" are missing!'
console.error(red(error))
'and/or "SERVE_PASSWORD" are missing!';
console.error(red(error));
// eslint-disable-next-line unicorn/no-process-exit
process.exit(1)
}
process.exit(1);
}
if (
!credentials ||
if (
!credentials ||
credentials.name !== process.env.SERVE_USER ||
credentials.pass !== process.env.SERVE_PASSWORD
) {
res.statusCode = 401
res.setHeader('WWW-Authenticate', 'Basic realm="User Visible Realm"')
return micro.send(res, 401, 'Access Denied')
}
}
) {
res.statusCode = 401;
res.setHeader('WWW-Authenticate', 'Basic realm="User Visible Realm"');
return micro.send(res, 401, 'Access Denied');
}
}
const { pathname } = parse(req.url)
const { ASSET_DIR } = process.env
const {pathname} = parse(req.url);
const {ASSET_DIR} = process.env;
let related = path.parse(path.join(current, pathname))
let assetRequest = false
let related = path.parse(path.join(current, pathname));
let assetRequest = false;
if (path.basename(related.dir) === ASSET_DIR) {
assetRequest = true
related = decodeURIComponent(path.join(__dirname, '/../assets/styles.css'))
} else {
related = decodeURIComponent(path.format(related))
if (path.basename(related.dir) === ASSET_DIR) {
assetRequest = true;
related = decodeURIComponent(path.join(__dirname, '/../assets/styles.css'));
} else {
related = decodeURIComponent(path.format(related));
const relatedResolved = path.resolve(related)
const relatedCurrent = path.resolve(current)
const relatedResolved = path.resolve(related);
const relatedCurrent = path.resolve(current);
const isSame = relatedResolved === relatedCurrent
const isSame = relatedResolved === relatedCurrent;
if (!isSame && !isPathInside(relatedResolved, relatedCurrent)) {
return micro.send(res, 400, 'Bad Request')
}
}
if (!isSame && !isPathInside(relatedResolved, relatedCurrent)) {
return micro.send(res, 400, 'Bad Request');
}
}
let notFoundResponse = 'Not Found'
let notFoundResponse = 'Not Found';
try {
const custom404Path = path.join(current, '/404.html')
notFoundResponse = yield fs.readFile(custom404Path, 'utf-8')
} catch (err) {}
try {
const custom404Path = path.join(current, '/404.html');
notFoundResponse = yield fs.readFile(custom404Path, 'utf-8');
// Don't allow rendering ignored files
const ignored = !ignoredFiles.every(item => {
return !decodeURIComponent(pathname).includes(item)
})
// We shouldn't do anything here, because it is okay if
// the error template doesn't exist.
if (ignored || (!assetRequest && related.indexOf(current) !== 0)) {
return micro.send(res, 404, notFoundResponse)
}
// eslint-disable-next-line no-empty
} catch (err) {}
const relatedExists = yield fs.exists(related)
// Don't allow rendering ignored files
const ignored = !ignoredFiles.every(item => !decodeURIComponent(pathname).includes(item));
if (!relatedExists && !flags.single) {
return micro.send(res, 404, notFoundResponse)
}
if (ignored || (!assetRequest && related.indexOf(current) !== 0)) {
return micro.send(res, 404, notFoundResponse);
}
const streamOptions = {}
const cacheValue = parseInt(flags.cache, 10)
const relatedExists = yield fs.exists(related);
if (cacheValue > 0) {
streamOptions.maxAge = flags.cache
} else if (cacheValue === 0) {
// Disable the cache control by `send`, as there's no support for `no-cache`.
// Set header manually.
streamOptions.cacheControl = false
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
} else if (flags.single) {
// Cache assets of single page applications for a day.
// Later in the code, we'll define that `index.html` never
// gets cached!
streamOptions.maxAge = 86400000
}
if (!relatedExists && !flags.single) {
return micro.send(res, 404, notFoundResponse);
}
// Check if directory
if (relatedExists && (yield pathType.dir(related))) {
// Normalize path to trailing slash
// Otherwise problems like #70 will occur
const url = parse(req.url)
const streamOptions = {};
const cacheValue = parseInt(flags.cache, 10);
if (url.pathname.substr(-1) !== '/') {
url.pathname += '/'
const newPath = format(url)
if (cacheValue > 0) {
streamOptions.maxAge = flags.cache;
} else if (cacheValue === 0) {
// Disable the cache control by `send`, as there's no support for `no-cache`.
// Set header manually.
streamOptions.cacheControl = false;
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
} else if (flags.single) {
// Cache assets of single page applications for a day.
// Later in the code, we'll define that `index.html` never
// gets cached!
streamOptions.maxAge = 86400000;
}
res.writeHead(302, {
Location: newPath
})
// Check if directory
if (relatedExists && (yield pathType.dir(related))) {
// Normalize path to trailing slash
// Otherwise problems like #70 will occur
const url = parse(req.url);
res.end()
return
}
if (url.pathname.substr(-1) !== '/') {
url.pathname += '/';
const newPath = format(url);
let indexPath = path.join(related, '/index.html')
res.setHeader('Content-Type', mime.contentType(path.extname(indexPath)))
res.writeHead(302, {
Location: newPath
});
if (!(yield fs.exists(indexPath))) {
// Try to render the current directory's content
const port = flags.port || req.socket.localPort
const renderedDir = yield renderDirectory(
port,
current,
related,
ignoredFiles
)
res.end();
return;
}
// If it works, send the directory listing to the user
// If is treeless, stepover
if (renderedDir && !flags.treeless) {
return micro.send(res, 200, renderedDir)
}
let indexPath = path.join(related, '/index.html');
res.setHeader('Content-Type', mime.contentType(path.extname(indexPath)));
// And if it doesn't, see if it's a single page application
// If that's not true either, send an error
if (!flags.single) {
return micro.send(res, 404, notFoundResponse)
}
if (!(yield fs.exists(indexPath))) {
// Try to render the current directory's content
const port = flags.port || req.socket.localPort;
const renderedDir = yield renderDirectory(
port,
current,
related,
ignoredFiles
);
// But IF IT IS true, then this is a hit to a directory with no `index.html`
// Load the SPA's root index file
indexPath = path.join(current, '/index.html')
}
// If it works, send the directory listing to the user
// If is treeless, stepover
if (renderedDir && !flags.treeless) {
return micro.send(res, 200, renderedDir);
}
if (flags.single && indexPath === path.join(current, '/index.html')) {
// Don't cache the `index.html` file for single page applications
streamOptions.maxAge = 0
}
// And if it doesn't, see if it's a single page application
// If that's not true either, send an error
if (!flags.single) {
return micro.send(res, 404, notFoundResponse);
}
return stream(req, indexPath, streamOptions).pipe(res)
}
// But IF IT IS true, then this is a hit to a directory with no `index.html`
// Load the SPA's root index file
indexPath = path.join(current, '/index.html');
}
// Serve `index.html` for single page applications if:
// - The path does not match any file or directory OR
// - The path matches exactly `/index.html`
if (
flags.single &&
if (flags.single && indexPath === path.join(current, '/index.html')) {
// Don't cache the `index.html` file for single page applications
streamOptions.maxAge = 0;
}
return stream(req, indexPath, streamOptions).pipe(res);
}
// Serve `index.html` for single page applications if:
// - The path does not match any file or directory OR
// - The path matches exactly `/index.html`
if (
flags.single &&
(!relatedExists || related === path.join(current, '/index.html'))
) {
// Don't cache the `index.html` file for single page applications
streamOptions.maxAge = 0
) {
// Don't cache the `index.html` file for single page applications
streamOptions.maxAge = 0;
// Load `index.html` and send it to the client
const indexPath = path.join(current, '/index.html')
return stream(req, indexPath, streamOptions).pipe(res)
}
// Load `index.html` and send it to the client
const indexPath = path.join(current, '/index.html');
return stream(req, indexPath, streamOptions).pipe(res);
}
// TODO remove the change below when https://github.com/zeit/serve/issues/303 is fixed.
//
// Details:
// Register the mime type for `.wasm` files. This change is currently not
// possible on upstream because of an unrelated issue. Once `mime` is
// updated, this line can be removed.
// https://github.com/pillarjs/send/pull/154
stream.mime.types.wasm = 'application/wasm'
stream.mime.extensions['application/wasm'] = 'wasm'
// TODO remove the change below when https://github.com/zeit/serve/issues/303 is fixed.
//
// Details:
// Register the mime type for `.wasm` files. This change is currently not
// possible on upstream because of an unrelated issue. Once `mime` is
// updated, this line can be removed.
// https://github.com/pillarjs/send/pull/154
stream.mime.types.wasm = 'application/wasm';
stream.mime.extensions['application/wasm'] = 'wasm';
// Serve files without a mime type as html for SPA, and text for non SPA
// eslint-disable-next-line camelcase
stream.mime.default_type = flags.single ? 'text/html' : 'text/plain'
// Serve files without a mime type as html for SPA, and text for non SPA
// eslint-disable-next-line camelcase
stream.mime.default_type = flags.single ? 'text/html' : 'text/plain';
return stream(
req,
related,
Object.assign(
{
dotfiles: 'allow'
},
streamOptions
)
).pipe(res)
})
return stream(
req,
related,
Object.assign(
{
dotfiles: 'allow'
},
streamOptions
)
).pipe(res);
});
// Native
const path = require('path')
const path = require('path');
// Packages
const fs = require('fs-extra')
const { compile } = require('handlebars')
const fs = require('fs-extra');
const {compile} = require('handlebars');
module.exports = () => {
let viewContent = false
const viewPath = path.normalize(path.join(__dirname, '/../views/index.hbs'))
let viewContent = false;
const viewPath = path.normalize(path.join(__dirname, '/../views/index.hbs'));
try {
viewContent = fs.readFileSync(viewPath, 'utf8')
} catch (err) {
throw err
}
try {
viewContent = fs.readFileSync(viewPath, 'utf8');
} catch (err) {
throw err;
}
return compile(viewContent)
}
return compile(viewContent);
};
{
"name": "serve",
"version": "6.5.5",
"version": "6.5.6",
"description": "Static file serving and directory listing",
"scripts": {
"precommit": "lint-staged",
"lint": "xo",
"test": "yarn lint && NODE_ENV=test nyc ava"
"test": "yarn lint && NODE_ENV=test nyc ava",
"lint": "zeit-eslint --ext .jsx,.js .",
"lint-staged": "git diff --diff-filter=ACMRT --cached --name-only '*.js' '*.jsx' | xargs zeit-eslint"
},

@@ -24,12 +24,2 @@ "files": [

},
"xo": {
"extends": "prettier"
},
"lint-staged": {
"*.js": [
"yarn lint",
"prettier --single-quote --no-semi --write",
"git add"
]
},
"keywords": [

@@ -44,36 +34,42 @@ "now",

"devDependencies": {
"@zeit/eslint-config-node": "0.2.13",
"@zeit/git-hooks": "0.1.4",
"ava": "0.25.0",
"eslint-config-prettier": "2.9.0",
"husky": "0.14.3",
"lint-staged": "7.0.0",
"node-fetch": "2.1.1",
"nyc": "11.4.1",
"prettier": "1.11.1",
"test-listen": "1.1.0",
"xo": "0.20.3"
"eslint": "4.19.1",
"node-fetch": "2.1.2",
"nyc": "11.7.1",
"test-listen": "1.1.0"
},
"dependencies": {
"args": "3.0.8",
"args": "4.0.0",
"basic-auth": "2.0.0",
"bluebird": "3.5.1",
"boxen": "1.3.0",
"chalk": "2.3.2",
"chalk": "2.4.0",
"clipboardy": "1.2.3",
"dargs": "5.1.0",
"detect-port": "1.2.2",
"filesize": "3.6.0",
"filesize": "3.6.1",
"fs-extra": "5.0.0",
"handlebars": "4.0.11",
"ip": "1.1.5",
"micro": "9.1.0",
"micro": "9.1.4",
"micro-compress": "1.0.0",
"mime-types": "2.1.18",
"node-version": "1.1.0",
"node-version": "1.1.3",
"openssl-self-signed-certificate": "1.1.6",
"opn": "5.2.0",
"opn": "5.3.0",
"path-is-inside": "1.0.2",
"path-type": "3.0.0",
"send": "0.16.2",
"update-check": "1.2.0"
"update-check": "1.3.2"
},
"eslintConfig": {
"extends": [
"@zeit/eslint-config-node"
]
},
"git": {
"pre-commit": "lint-staged"
}
}
# serve
[![Build Status](https://travis-ci.org/zeit/serve.svg?branch=master)](https://travis-ci.org/zeit/serve)
[![Build Status](https://circleci.com/gh/zeit/serve.svg?&style=shield)](https://circleci.com/gh/zeit/serve)
[![Join the community on Spectrum](https://withspectrum.github.io/badge/badge.svg)](https://spectrum.chat/micro/serve)
Ever wanted to share a project on your network by running just a command? Then this module is exactly what you're looking for: It provides a neat interface for listing the directory's contents and switching into sub folders.
Have you ever wanted to share a project on your network by running just a command? Then this module is exactly what you're looking for: It provides a neat interface for listing the directory's contents and switching into sub folders.

@@ -14,3 +14,3 @@ In addition, it's also awesome when it comes to serving static sites!

Install it (needs at least Node LTS):
Firstly, install the package from [npm](https://npmjs.com/release) (you'll need at least Node.js 7.6.0):

@@ -21,5 +21,11 @@ ```bash

And run this command in your terminal:
Alternatively, you can use [Yarn](https://yarnpkg.com/en/) to install it:
```bash
yarn global add serve
```
Once that's done, you can run this command inside your project's directory:
```bash
serve [options] <path>

@@ -26,0 +32,0 @@ ```

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc