Skip to content

Commit

Permalink
Major: 1.0 (#64)
Browse files Browse the repository at this point in the history
* chore(sirv): bump “@polka/url” for better decoding

* feat(sirv): inline `opts.single` behavior

* fix(sirv): unknown Content-Type as empty string

* feat(sirv-cli): run HTTP/2 server behind flags

* chore(sirv-cli): update readme

* feat: run `sade` in single-command mode

* feat(sirv): allow `--single` to accept path;

- Closes #35

* fix(sirv): respect existing `Content-Type` header;

- Closes #38
- Closes #39

* fix(sirv-cli): use "-K" for `--http2` alias

* feat: allow precompiled gzip & brotli files;

- Closes #3

* refactor(sirv): consolidate "dev & prod" modes;

- still optimized
- adds ETag support to "dev" mode
- adds brotli/gzip support to "dev" mode
- Related: #3

* chore: bump devdeps

* v1.0.0-next.0

* chore(docs): fix Polka demo usage;

- Closes #42

* fix(sirv): set "no-store" during `dev` mode;

- Closes #45

* v1.0.0-next.1

* fix(sirv): extract `list` into package

* fix(sirv): ignore deeply-nested dotfiles too

* chore(sirv): update `polka/url` dep;

lukeed/polka#119

* v1.0.0-next.2

* feat(sirv-cli): add `--pass` flag for passphrase

* v1.0.0-next.3

* fix(sirv): compare `ETag` against `If-None-Match` header (#56)

* fix(sirv-cli): use `host` for port availability

* Add support for ETag-based caching

When providing an ETag header on responses, the client automatically sends that header value on subsequent requests for the matching resource as an `If-None-Match` request header. If the ETag value received in `If-None-Match` is the same as the ETag computed by the disk crawl, a 304 "Not Modified" status can be returned along with an empty body to indicate that the requestor's cached value should be used.

* chore: use existing `isEtag` var

Co-authored-by: Luke Edwards <luke.edwards05@gmail.com>

* chore(sirv): squash declaration

* v1.0.0-next.4

* break: require Node 10.x minimum;

- Node 8.x is already End of Life (2019-12-31)

* v1.0.0-next.5

* feat(sirv): add type definitions;

- Closes #61

* chore: add `notice` to base readme re: website;

- Closes #62

* feat(sirv): add `filter` option to determine file list;

- supplants default `dotfiles` matcher, responsible for own logic
- Closes #50

* fix(sirv): replace `filter` w/ built-in ".well-known" allowance;

- Closes #50

* chore: rename files

* chore(sirv-cli): rename `boot` file

* chore(sirv): produce `build` files w/ "module" entry

* chore: run tests with esm loader

* chore: re-extract `middleware` helper file

* chore: begin integration test suites;

- include fixtures directory

* chore: use `pnpm` again for lerna;

This reverts commit a56872b.

# Conflicts:
#	lerna.json

* chore(action): install `pnpm` for ci

* chore: add `single` tests

* chore(action): use `curl` installer for pnpm

* chore: add "extensions" tests

* chore: add `dotfiles` test suite

* chore: add `dev` test suite

* chore: add `etag` test suite

* chore: add `brotli` and `gzip` suites

* chore: add `maxAge` and `immutable` test suites

* chore: add `ranges` test suite

* fix(test): remove `only` on dotfiles suite

* chore: import `setHeaders` and `onNoMatch` suites

* chore: cleanup test helpers

* chore: include "build" step in CI

* chore: initial CLI tests

* revert: allow normal resolution

* chore: try `actions/cache` recipe

* chore: add loose CLI tests

* fix(test): terminate child procs correctly

* chore: boot `http2` server within test

* fix(sirv): normalize encoded/accented characters for cache

* debug: server spawner really annoying

* feat: add "assets" option (#53)

* Add option to specify asset paths for SPAs

Allows asset path prefixes to be specified by the CLI when running in
single-page mode.

If a path is requested that doesn't exist, and that path matches one of
the asset path prefixes, the request will 404 instead of retuning a 200
containing the root index.

Fixes #44

* fix: ensure `assets` array & preload prep work

* chore: add `assets` tests

* chore: update options definition

* update `--single` & `--assets` text

Co-authored-by: Luke Edwards <luke.edwards05@gmail.com>

* refactor: rename "assets" to "ignores" option

* fix: include dotfiles & well-known in default ignores

* fix: move `ignores` check into `toAssume` builder

* refactor: hoist bound `lookup` function

* fix(sirv): do not fill cache when `dev` true

* v1.0.0-next.6

* fix(sirv): extension regexp

* v1.0.0-next.7

* fix(sirv): remove premature ignore check;

- ran before extensions could be applied
- effectively reverts to #53

* v1.0.0-next.8

* fix(sirv): allow definition to export type members

* v1.0.0-next.9

* fix(sirv): dedupe extensions list w/ brotli + gzip

* chore: update documentation

* chore: update bench figures;

- include dummy apps directly

* chore: fix spacing

Co-authored-by: Jason Miller <developit@users.noreply.github.com>
Co-authored-by: Carey Metcalfe <carey@cmetcalfe.ca>
  • Loading branch information
3 people authored Jun 10, 2020
1 parent 4977cd3 commit bb948a7
Show file tree
Hide file tree
Showing 43 changed files with 1,971 additions and 312 deletions.
22 changes: 18 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
nodejs: [8, 10, 12]
nodejs: [10, 12]
steps:
- uses: actions/checkout@master
with:
Expand All @@ -18,13 +18,27 @@ jobs:
with:
node-version: ${{ matrix.nodejs }}

- name: (env) pnpm
run: curl -L https://raw.githubusercontent.com/pnpm/self-installer/master/install.js | node

- name: (lerna) restore
uses: actions/cache@master
with:
path: |
node_modules
*/*/node_modules
key: ${{ runner.os }}-${{ hashFiles('**/package.json') }}

- name: Install
run: |
npm install
npm install -g nyc
pnpm install
pnpm add -g nyc
- name: Build
run: pnpm run build

- name: Test w/ Coverage
run: nyc --include=packages npm test
run: nyc --include=packages pnpm test

- name: Report
if: matrix.nodejs >= 12
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ node_modules
*.lock
*.log

public
/tests/public/foobar.*
/packages/*/build.*
7 changes: 7 additions & 0 deletions bench/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"private": true,
"devDependencies": {
"serve-static": "1.14.1",
"sirv": "file:../packages/sirv"
}
}
1 change: 1 addition & 0 deletions bench/public/asset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('testing 123');
5 changes: 5 additions & 0 deletions bench/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<html>
<body>
hello world
</body>
</html>
9 changes: 9 additions & 0 deletions bench/sirv.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const { createServer } = require('http');
const sirv = require('sirv');

const { PORT=3000 } = process.env;

// const handler = sirv('public', { dev: true });
const handler = sirv('public', { dev: false });

createServer(handler).listen(PORT);
13 changes: 13 additions & 0 deletions bench/static.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const { createServer } = require('http');
const serve = require('serve-static');

const { PORT=3000 } = process.env;

const handler = serve('public');

createServer((req, res) => {
handler(req, res, () => {
res.statusCode = 404;
res.end('');
});
}).listen(PORT);
4 changes: 2 additions & 2 deletions lerna.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"version": "0.4.6",
"npmClient": "yarn",
"version": "1.0.0-next.9",
"npmClient": "pnpm",
"packages": [
"packages/*"
]
Expand Down
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,16 @@
},
"scripts": {
"pretest": "lerna bootstrap",
"test": "uvu tests"
"build": "lerna exec bundt index.js",
"test": "uvu -r esm tests -i public -i helpers"
},
"devDependencies": {
"bundt": "1.0.1",
"esm": "3.2.25",
"httpie": "2.0.0-next.7",
"lerna": "3.21.0",
"mime": "2.4.5",
"selfsigned": "1.10.7",
"uvu": "^0.0.10"
}
}
33 changes: 33 additions & 0 deletions packages/sirv-cli/bin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env node
const sade = require('sade');
const boot = require('./index');
const pkg = require('./package');

sade('sirv [dir]')
.version(pkg.version)
.describe('Run a static file server')
.example('build --cors --port 8080')
.example('public --quiet --etag --maxage 31536000 --immutable')
.example('public --http2 --key priv.pem --cert cert.pem')
.example('public -qeim 31536000')
.example('--port 8080 --etag')
.example('--dev')
.option('-D, --dev', 'Enable "dev" mode')
.option('-e, --etag', 'Enable "ETag" header')
.option('-d, --dotfiles', 'Enable dotfile asset requests')
.option('-c, --cors', 'Enable "CORS" headers to allow any origin requestor')
.option('-G, --gzip', 'Send precompiled "*.gz" files when "gzip" is supported', true)
.option('-B, --brotli', 'Send precompiled "*.br" files when "brotli" is supported', true)
.option('-m, --maxage', 'Enable "Cache-Control" header & define its "max-age" value (sec)')
.option('-i, --immutable', 'Enable the "immutable" directive for "Cache-Control" header')
.option('-k, --http2', 'Enable the HTTP/2 protocol. Requires Node.js 8.4.0+')
.option('-C, --cert', 'Path to certificate file for HTTP/2 server')
.option('-K, --key', 'Path to certificate key for HTTP/2 server')
.option('-P, --pass', 'Passphrase to decrypt a certificate key')
.option('-s, --single', 'Serve as single-page application with "index.html" fallback')
.option('-I, --ignores', 'Any URL pattern(s) to ignore "index.html" assumptions')
.option('-q, --quiet', 'Disable logging to terminal')
.option('-H, --host', 'Hostname to bind', 'localhost')
.option('-p, --port', 'Port to bind', 5000)
.action(boot)
.parse(process.argv);
78 changes: 0 additions & 78 deletions packages/sirv-cli/boot.js

This file was deleted.

124 changes: 98 additions & 26 deletions packages/sirv-cli/index.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,98 @@
#!/usr/bin/env node
const sade = require('sade');
const pkg = require('./package');
const boot = require('./boot');

sade('sirv')
.version(pkg.version)
.example('build --cors --port 8080')
.example('start build --cors --port 8080')
.example('public --quiet --etag --maxage 31536000 --immutable')
.example('start public -qeim 31536000')
.example('--port 8080 --etag')
.example('my-app --dev')
.command('start [dir]', 'Start a static file server.', { default:true })
.option('-D, --dev', 'Enable "dev" mode')
.option('-e, --etag', 'Enable "Etag" header')
.option('-d, --dotfiles', 'Enable dotfile asset requests')
.option('-c, --cors', 'Enable "CORS" headers to allow any origin requestor')
.option('-m, --maxage', 'Enable "Cache-Control" header & define its "max-age" value (sec)')
.option('-i, --immutable', 'Enable the "immutable" directive for "Cache-Control" header')
.option('-s, --single', 'Serve single-page applications')
.option('-q, --quiet', 'Disable logging to terminal')
.option('-H, --host', 'Hostname to bind', 'localhost')
.option('-p, --port', 'Port to bind', 5000)
.action(boot)
.parse(process.argv);
const sirv = require('sirv');
const colors = require('kleur');
const semiver = require('semiver');
const { resolve } = require('path');
const { readFileSync } = require('fs');
const laccess = require('local-access');
const clear = require('console-clear');
const tinydate = require('tinydate');
const toPort = require('get-port');

const PAD = ' ';
const { HOST, PORT } = process.env;
const stamp = tinydate('{HH}:{mm}:{ss}');

function toTime() {
return '[' + colors.magenta(stamp()) + '] ';
}

function toMS(arr) {
return colors.white().bold(`${(arr[1] / 1e6).toFixed(2)}ms`);
}

function toCode(code) {
let fn = code >= 400 ? 'red' : code > 300 ? 'yellow' : 'green';
return colors[fn](code);
}

function exit(msg) {
process.stderr.write('\n' + PAD + colors.red().bold('ERROR: ') + msg + '\n\n');
process.exit(1);
}

module.exports = function (dir, opts) {
dir = resolve(dir || '.');
opts.maxAge = opts.m;

if (opts.cors) {
opts.setHeaders = res => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Origin, Content-Type, Accept, Range');
}
}

let server;
let fn = sirv(dir, opts);
let { hrtime, stdout } = process;

if (opts.http2) {
if (semiver(process.version.substring(1), '8.4.0') < 0) {
return exit('HTTP/2 requires Node v8.4.0 or greater');
}

if (!opts.key || !opts.cert) {
return exit('HTTP/2 requires "key" and "cert" values');
}

opts.key = readFileSync(opts.key);
opts.cert = readFileSync(opts.cert);
if (opts.cacert) opts.cacert = readFileSync(opts.cacert);
if (opts.pass) opts.passphrase = opts.pass;

server = require('http2').createSecureServer(opts, fn);
} else {
server = require('http').createServer(fn);
}

if (!opts.quiet) {
let uri, dur, start, dash=colors.gray(' ─ ');
server.on('request', (req, res) => {
start = hrtime();
req.once('end', _ => {
dur = hrtime(start);
uri = req.originalUrl || req.url;
stdout.write(PAD + toTime() + toCode(res.statusCode) + dash + toMS(dur) + dash + uri + '\n');
});
});
}

opts.port = PORT || opts.port;
let hostname = HOST || opts.host || '0.0.0.0';
toPort({ host: hostname, port: opts.port }).then(port => {
let isOther = port != opts.port;
let https = opts.http2 || !!opts.ssl; // TODO
server.listen(port, hostname, err => {
if (err) throw err;
if (opts.quiet) return;

clear(true); // wipe screen, but not history
let { local, network } = laccess({ port, hostname, https });
stdout.write('\n' + PAD + colors.green('Your application is ready~! 🚀\n\n'));
isOther && stdout.write(PAD + colors.italic().dim(`➡ Port ${opts.port} is taken; using ${port} instead\n\n`));
stdout.write(PAD + `${colors.bold('- Local:')} ${local}\n`);
/localhost/i.test(hostname) || stdout.write(PAD + `${colors.bold('- Network:')} ${network}\n`);
let border = '─'.repeat(Math.min(stdout.columns, 36) / 2);
stdout.write('\n' + border + colors.inverse(' LOGS ') + border + '\n\n');
});
});
}
12 changes: 7 additions & 5 deletions packages/sirv-cli/package.json
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
{
"name": "sirv-cli",
"version": "0.4.6",
"version": "1.0.0-next.9",
"description": "A lightweight CLI program to serve static sites~!",
"repository": "lukeed/sirv",
"main": "index.js",
"license": "MIT",
"files": [
"*.js"
],
"bin": {
"sirv": "index.js"
"sirv": "bin.js"
},
"author": {
"name": "Luke Edwards",
"email": "luke@lukeed.com",
"url": "https://lukeed.com"
},
"engines": {
"node": ">= 6"
"node": ">= 10"
},
"dependencies": {
"console-clear": "^1.1.0",
"get-port": "^3.2.0",
"kleur": "^3.0.0",
"local-access": "^1.0.1",
"sade": "^1.4.0",
"sirv": "^0.4.6",
"sade": "^1.6.0",
"semiver": "^1.0.0",
"sirv": "^1.0.0-next.9",
"tinydate": "^1.0.0"
}
}
Loading

0 comments on commit bb948a7

Please sign in to comment.