diff --git a/package.json b/package.json index 81f7304c..2ea7f5ce 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "sapper-dev": "cross-env NODE_ENV=development PORT=4002 sapper dev", "before-build": "run-s build-template-html build-third-party-assets", "build": "cross-env NODE_ENV=production run-s build-steps", - "build-steps": "run-s before-build sapper-build", + "build-steps": "run-s before-build sapper-export build-now-json", "sapper-build": "sapper build", "start": "node server.js", "build-and-start": "run-s build start", @@ -35,7 +35,7 @@ "deploy-dev": "DEPLOY_TYPE=dev ./bin/deploy.sh", "deploy-all-travis": "./bin/deploy-all-travis.sh", "backup-mastodon-data": "./bin/backup-mastodon-data.sh", - "sapper-export": "sapper export", + "sapper-export": "cross-env PORT=4002 sapper export", "print-export-info": "node ./bin/print-export-info.js", "export-steps": "run-s before-build sapper-export print-export-info", "export": "cross-env NODE_ENV=production run-s export-steps", @@ -65,7 +65,6 @@ "file-drop-element": "0.2.0", "form-data": "^2.3.3", "glob": "^7.1.3", - "helmet": "^3.15.1", "idb-keyval": "^3.1.0", "indexeddb-getall-shim": "^1.3.5", "inferno-compat": "^7.1.7", @@ -89,7 +88,6 @@ "rollup-plugin-replace": "^2.1.0", "rollup-plugin-terser": "^4.0.4", "sapper": "nolanlawson/sapper#for-pinafore-10", - "serve-static": "^1.13.2", "stringz": "^1.0.0", "svelte": "^2.16.1", "svelte-extras": "^2.0.2", diff --git a/server.js b/server.js index 8d113f6b..23cc92d5 100755 --- a/server.js +++ b/server.js @@ -1,5 +1,42 @@ #!/usr/bin/env node -process.env.PORT = process.env.PORT || 4002 +const path = require('path') +const express = require('express') +const compression = require('compression') +const { routes: nowRoutes } = require('./now.json') -require('./__sapper__/build') +const { PORT = 4002 } = process.env +const app = express() +const exportDir = path.resolve(__dirname, '__sapper__/export') + +const routes = nowRoutes.map(({ src, headers, dest }) => ({ + regex: new RegExp(src), + headers, + dest +})) + +app.use(compression({ threshold: 0 })) + +app.use(express.static(exportDir, { + setHeaders (res, thisPath) { + let localPath = '/' + path.relative(exportDir, thisPath) + for (let { regex, headers } of routes) { + if (regex.test(localPath)) { + res.set(headers) + return + } + } + } +})) + +routes.forEach(({ regex, headers, dest }) => { + app.get(regex, (req, res) => { + res.set(headers) + res.sendFile(path.resolve(exportDir, dest ? req.path.replace(regex, dest) : req.path)) + }) +}) + +app.listen(PORT, () => console.log(`listening on port ${PORT}`)) + +// Handle SIGINT (source: https://git.io/vhJgF) +process.on('SIGINT', () => process.exit(0)) diff --git a/src/server.js b/src/server.js index 66517d21..ee819581 100644 --- a/src/server.js +++ b/src/server.js @@ -1,89 +1,15 @@ +// This is the Sapper server, which we only run during `sapper export`. + import * as sapper from '../__sapper__/server.js' import express from 'express' -import compression from 'compression' -import serveStatic from 'serve-static' -import helmet from 'helmet' -import fetch from 'node-fetch' -import inlineScriptChecksum from './inline-script/checksum' -import { sapperInlineScriptChecksums } from './server/sapperInlineScriptChecksums' const { PORT = 4002 } = process.env const app = express() -const MAX_AGE = 3600 +app.use(express.static('static')) +app.use(sapper.middleware()) -// this allows us to do e.g. `fetch('/_api/blog')` on the server -global.fetch = (url, opts) => { - if (url[0] === '/') { - url = `http://localhost:${PORT}${url}` - } - return fetch(url, opts) -} - -app.use(compression({ threshold: 0 })) - -// CSP only needs to apply to core HTML files, not debug files -// like report.html or the JS/CSS/JSON/image files -const coreHtmlFilesOnly = (fn) => (req, res, next) => { - let coreHtml = !/\.(js|css|json|png|svg|jpe?g|map)$/.test(req.path) && - !(/\/report.html/.test(req.path)) - return coreHtml ? fn(req, res, next) : next() -} - -app.use(coreHtmlFilesOnly(helmet({ - contentSecurityPolicy: { - directives: { - scriptSrc: [ - `'self'`, - `'sha256-${inlineScriptChecksum}'`, - ...sapperInlineScriptChecksums.map(_ => `'sha256-${_}'`) - ], - workerSrc: [`'self'`], - styleSrc: [`'self'`, `'unsafe-inline'`], - frameSrc: [`'none'`], - objectSrc: [`'none'`], - manifestSrc: [`'self'`] - } - }, - referrerPolicy: { - policy: 'no-referrer' - } -}))) - -app.use(serveStatic('static', { - setHeaders: (res) => { - res.setHeader('Cache-Control', `public,max-age=${MAX_AGE}`) - } -})) - -app.use(express.static('__sapper__/build/client/report.html')) -app.use(express.static('__sapper__/build/client/stats.json')) - -// TODO: hack to override Sapper's default cache-control -function overrideSetHeader (req, res, next) { - const origSetHeader = res.setHeader - res.setHeader = function (key, value) { - if (key === 'Cache-Control') { - if (value === 'max-age=31536000, immutable') { // webpack assets - return origSetHeader.apply(this, ['Cache-Control', 'public,max-age=31536000,immutable']) - } - if (value === 'max-age=600') { // HTML files - return origSetHeader.apply(this, ['Cache-Control', `public,max-age=${MAX_AGE}`]) - } - } - - return origSetHeader.apply(this, arguments) - } - return next() -} - -app.use(overrideSetHeader, sapper.middleware()) - -app.listen(PORT, () => { - console.log(`listening on port ${PORT}`) -}) +app.listen(PORT, () => console.log(`listening on port ${PORT}`)) // Handle SIGINT (source: https://git.io/vhJgF) -process.on('SIGINT', function () { - process.exit(0) -}) +process.on('SIGINT', () => process.exit(0)) diff --git a/tests/spec/010-focus.js b/tests/spec/010-focus.js index 0befd90c..7067ca68 100644 --- a/tests/spec/010-focus.js +++ b/tests/spec/010-focus.js @@ -68,7 +68,7 @@ test('notification timeline preserves focus', async t => { await t.click($(`${getNthStatusSelector(5)} .status-header a`)) .expect(getUrl()).contains('/accounts/') .click(goBackButton) - .expect(getUrl()).eql('http://localhost:4002/notifications') + .expect(getUrl()).contains('/notifications') .expect(getNthStatus(0).exists).ok() .expect(getActiveElementInnerText()).eql('quux') .expect(getActiveElementInsideNthStatus()).eql('5') diff --git a/yarn.lock b/yarn.lock index 2d08ad14..39eb8585 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1591,11 +1591,6 @@ camelcase@^5.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA== -camelize@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b" - integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs= - caniuse-lite@^1.0.30000844: version "1.0.30000932" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000932.tgz#d01763e9ce77810962ca7391ff827b5949ce4272" @@ -1965,11 +1960,6 @@ content-disposition@0.5.2: resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= -content-security-policy-builder@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/content-security-policy-builder/-/content-security-policy-builder-2.0.0.tgz#8749a1d542fcbe82237281ea9f716ce68b394dd2" - integrity sha512-j+Nhmj1yfZAikJLImCvPJFE29x/UuBi+/MWqggGGc515JKaZrjuei2RhULJmy0MsstW3E3htl002bwmBNMKr7w== - content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" @@ -2255,11 +2245,6 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -dasherize@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/dasherize/-/dasherize-2.0.0.tgz#6d809c9cd0cf7bb8952d80fc84fa13d47ddb1308" - integrity sha1-bYCcnNDPe7iVLYD8hPoT1H3bEwg= - date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" @@ -2391,11 +2376,6 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= -depd@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -2445,11 +2425,6 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" -dns-prefetch-control@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/dns-prefetch-control/-/dns-prefetch-control-0.1.0.tgz#60ddb457774e178f1f9415f0cabb0e85b0b300b2" - integrity sha1-YN20V3dOF48flBXwyrsOhbCzALI= - doctrine@1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" @@ -2511,11 +2486,6 @@ domutils@^1.5.1, domutils@^1.7.0: dom-serializer "0" domelementtype "1" -dont-sniff-mimetype@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/dont-sniff-mimetype/-/dont-sniff-mimetype-1.0.0.tgz#5932890dc9f4e2f19e5eb02a20026e5e5efc8f58" - integrity sha1-WTKJDcn04vGeXrAqIAJuXl78j1g= - duplexer@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" @@ -2961,11 +2931,6 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" -expect-ct@0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/expect-ct/-/expect-ct-0.1.1.tgz#de84476a2dbcb85000d5903737e9bc8a5ba7b897" - integrity sha512-ngXzTfoRGG7fYens3/RMb6yYoVLvLMfmsSllP/mZPxNHgFq41TmPSLF/nLY7fwoclI2vElvAmILFWGUYqdjfCg== - express@^4.16.3, express@^4.16.4: version "4.16.4" resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e" @@ -3089,11 +3054,6 @@ fastparse@^1.1.1: resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9" integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ== -feature-policy@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/feature-policy/-/feature-policy-0.2.0.tgz#22096de49ab240176878ffe2bde2f6ff04d48c43" - integrity sha512-2hGrlv6efG4hscYVZeaYjpzpT6I2OZgYqE2yDUzeAcKj2D1SH0AsEzqJNXzdoglEddcIXQQYop3lD97XpG75Jw== - figgy-pudding@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" @@ -3313,11 +3273,6 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" -frameguard@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/frameguard/-/frameguard-3.0.0.tgz#7bcad469ee7b96e91d12ceb3959c78235a9272e9" - integrity sha1-e8rUae57lukdEs6zlZx4I1qScuk= - fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" @@ -3695,47 +3650,6 @@ he@1.2.0, he@1.2.x: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -helmet-crossdomain@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/helmet-crossdomain/-/helmet-crossdomain-0.3.0.tgz#707e2df930f13ad61f76ed08e1bb51ab2b2e85fa" - integrity sha512-YiXhj0E35nC4Na5EPE4mTfoXMf9JTGpN4OtB4aLqShKuH9d2HNaJX5MQoglO6STVka0uMsHyG5lCut5Kzsy7Lg== - -helmet-csp@2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/helmet-csp/-/helmet-csp-2.7.1.tgz#e8e0b5186ffd4db625cfcce523758adbfadb9dca" - integrity sha512-sCHwywg4daQ2mY0YYwXSZRsgcCeerUwxMwNixGA7aMLkVmPTYBl7gJoZDHOZyXkqPrtuDT3s2B1A+RLI7WxSdQ== - dependencies: - camelize "1.0.0" - content-security-policy-builder "2.0.0" - dasherize "2.0.0" - platform "1.3.5" - -helmet@^3.15.1: - version "3.15.1" - resolved "https://registry.yarnpkg.com/helmet/-/helmet-3.15.1.tgz#2c80d1a59138b6f23929605afca4b1c88b3298ec" - integrity sha512-hgoNe/sjKlKNvJ3g9Gz149H14BjMMWOCmW/DTXl7IfyKGtIK37GePwZrHNfr4aPXdKVyXcTj26RgRFbPKDy9lw== - dependencies: - depd "2.0.0" - dns-prefetch-control "0.1.0" - dont-sniff-mimetype "1.0.0" - expect-ct "0.1.1" - feature-policy "0.2.0" - frameguard "3.0.0" - helmet-crossdomain "0.3.0" - helmet-csp "2.7.1" - hide-powered-by "1.0.0" - hpkp "2.0.0" - hsts "2.1.0" - ienoopen "1.0.0" - nocache "2.0.0" - referrer-policy "1.1.0" - x-xss-protection "1.1.0" - -hide-powered-by@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hide-powered-by/-/hide-powered-by-1.0.0.tgz#4a85ad65881f62857fc70af7174a1184dccce32b" - integrity sha1-SoWtZYgfYoV/xwr3F0oRhNzM4ys= - highlight-es@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/highlight-es/-/highlight-es-1.0.3.tgz#12abc300a27e686f6f18010134e3a5c6d2fe6930" @@ -3779,16 +3693,6 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w== -hpkp@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/hpkp/-/hpkp-2.0.0.tgz#10e142264e76215a5d30c44ec43de64dee6d1672" - integrity sha1-EOFCJk52IVpdMMROxD3mTe5tFnI= - -hsts@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/hsts/-/hsts-2.1.0.tgz#cbd6c918a2385fee1dd5680bfb2b3a194c0121cc" - integrity sha512-zXhh/DqgrTXJ7erTN6Fh5k/xjMhDGXCqdYN3wvxUvGUQvnxcFfUd8E+6vLg/nk3ss1TYMb+DhRl25fYABioTvA== - html-minifier@^3.5.20: version "3.5.21" resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz#d0040e054730e354db008463593194015212d20c" @@ -3884,11 +3788,6 @@ ieee754@^1.1.4: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b" integrity sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA== -ienoopen@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/ienoopen/-/ienoopen-1.0.0.tgz#346a428f474aac8f50cf3784ea2d0f16f62bda6b" - integrity sha1-NGpCj0dKrI9QzzeE6i0PFvYr2ms= - iferr@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" @@ -5167,11 +5066,6 @@ no-case@^2.2.0: dependencies: lower-case "^1.1.1" -nocache@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.0.0.tgz#202b48021a0c4cbde2df80de15a17443c8b43980" - integrity sha1-ICtIAhoMTL3i34DeFaF0Q8i0OYA= - node-environment-flags@1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.4.tgz#0b784a6551426bfc16d3b2208424dcbc2b2ff038" @@ -5910,11 +5804,6 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" -platform@1.3.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.5.tgz#fb6958c696e07e2918d2eeda0f0bc9448d733444" - integrity sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q== - plugin-error@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-0.1.2.tgz#3b9bb3335ccf00f425e07437e19276967da47ace" @@ -6311,11 +6200,6 @@ redent@^1.0.0: indent-string "^2.1.0" strip-indent "^1.0.1" -referrer-policy@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/referrer-policy/-/referrer-policy-1.1.0.tgz#35774eb735bf50fb6c078e83334b472350207d79" - integrity sha1-NXdOtzW/UPtsB46DM0tHI1AgfXk= - regenerate@^1.2.1: version "1.4.0" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" @@ -6732,7 +6616,7 @@ serialize-javascript@^1.4.0, serialize-javascript@^1.6.1: resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.6.1.tgz#4d1f697ec49429a847ca6f442a2a755126c4d879" integrity sha512-A5MOagrPFga4YaKQSWHryl7AXvbQkEqpw4NNYMTNYUNV51bA8ABHgYFpqKx+YFFrw59xMV1qGH1R4AgoNIVgCw== -serve-static@1.13.2, serve-static@^1.13.2: +serve-static@1.13.2: version "1.13.2" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" integrity sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw== @@ -8087,11 +7971,6 @@ ws@^6.0.0: dependencies: async-limiter "~1.0.0" -x-xss-protection@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/x-xss-protection/-/x-xss-protection-1.1.0.tgz#4f1898c332deb1e7f2be1280efb3e2c53d69c1a7" - integrity sha512-rx3GzJlgEeZ08MIcDsU2vY2B1QEriUKJTSiNHHUIem6eg9pzVOr2TL3Y4Pd6TMAM5D5azGjcxqI62piITBDHVg== - xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"