From 3bace8c9e463043e08c2481f3e16e5d88b6a9868 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Mon, 18 Jan 2021 15:48:46 -0800 Subject: [PATCH] fix: Restore DNS prefetching for static resources (#1820) * fix: Restore DNS prefetching for static resources * fix: CDN paths feat: preload instead of prefetch for key bundles * csp * fix: Turns out prefetch-src is still behind a flag in Chrome, not publicly available yet --- package.json | 2 +- server/app.js | 8 ++-- server/routes.js | 2 + server/static/index.html | 1 + server/utils/prefetchTags.js | 79 ++++++++++++++++++++---------------- webpack.config.prod.js | 4 +- yarn.lock | 33 ++++++++++----- 7 files changed, 76 insertions(+), 53 deletions(-) diff --git a/package.json b/package.json index dee247ae..71398386 100644 --- a/package.json +++ b/package.json @@ -203,7 +203,7 @@ "url-loader": "^0.6.2", "webpack": "4.44.1", "webpack-cli": "^3.3.12", - "webpack-manifest-plugin": "^2.2.0" + "webpack-manifest-plugin": "^3.0.0" }, "resolutions": { "dot-prop": "^5.2.0", diff --git a/server/app.js b/server/app.js index 50551907..56b86b77 100644 --- a/server/app.js +++ b/server/app.js @@ -23,21 +23,21 @@ const isProduction = process.env.NODE_ENV === "production"; const isTest = process.env.NODE_ENV === "test"; // Construct scripts CSP based on services in use by this installation +const defaultSrc = ["'self'"]; const scriptSrc = [ "'self'", "'unsafe-inline'", "'unsafe-eval'", "gist.github.com", + "browser.sentry-cdn.com", ]; if (env.GOOGLE_ANALYTICS_ID) { scriptSrc.push("www.google-analytics.com"); } -if (env.SENTRY_DSN) { - scriptSrc.push("browser.sentry-cdn.com"); -} if (env.CDN_URL) { scriptSrc.push(env.CDN_URL); + defaultSrc.push(env.CDN_URL); } app.use(compress()); @@ -167,7 +167,7 @@ app.use(helmet()); app.use( contentSecurityPolicy({ directives: { - defaultSrc: ["'self'"], + defaultSrc, scriptSrc, styleSrc: ["'self'", "'unsafe-inline'", "github.githubassets.com"], imgSrc: ["*", "data:", "blob:"], diff --git a/server/routes.js b/server/routes.js index b1792019..0efae32c 100644 --- a/server/routes.js +++ b/server/routes.js @@ -10,6 +10,7 @@ import { languages } from "../shared/i18n"; import environment from "./env"; import apexRedirect from "./middlewares/apexRedirect"; import { opensearchResponse } from "./utils/opensearch"; +import prefetchTags from "./utils/prefetchTags"; import { robotsResponse } from "./utils/robots"; const isProduction = process.env.NODE_ENV === "production"; @@ -50,6 +51,7 @@ const renderApp = async (ctx, next) => { ctx.body = page .toString() .replace(/\/\/inject-env\/\//g, env) + .replace(/\/\/inject-prefetch\/\//g, prefetchTags) .replace(/\/\/inject-sentry-dsn\/\//g, process.env.SENTRY_DSN || "") .replace(/\/\/inject-slack-app-id\/\//g, process.env.SLACK_APP_ID || ""); }; diff --git a/server/static/index.html b/server/static/index.html index db53cc12..4b6c67ec 100644 --- a/server/static/index.html +++ b/server/static/index.html @@ -4,6 +4,7 @@ Outline + //inject-prefetch// , -]; +if (process.env.AWS_S3_UPLOAD_BUCKET_URL) { + prefetchTags.push( + + ); +} +let manifestData = {}; try { const manifest = fs.readFileSync( path.join(__dirname, "../../app/manifest.json"), "utf8" ); - const manifestData = JSON.parse(manifest); - Object.values(manifestData).forEach((filename) => { - if (typeof filename !== "string") return; - if (filename.endsWith(".js")) { - prefetchTags.push( - - ); - } else if (filename.endsWith(".css")) { - prefetchTags.push( - - ); - } - }); -} catch (_e) { - // no-op + manifestData = JSON.parse(manifest); +} catch (err) { + console.warn(err); } -export default prefetchTags; +Object.values(manifestData).forEach((filename) => { + if (typeof filename !== "string") return; + if (!env.CDN_URL) return; + + if (filename.endsWith(".js")) { + // Preload resources you have high-confidence will be used in the current + // page.Prefetch resources likely to be used for future navigations + const shouldPreload = + filename.includes("/main") || + filename.includes("/runtime") || + filename.includes("/vendors"); + + prefetchTags.push( + + ); + } else if (filename.endsWith(".css")) { + prefetchTags.push( + + ); + } +}); + +export default ReactDOMServer.renderToString(prefetchTags); diff --git a/webpack.config.prod.js b/webpack.config.prod.js index ee61f901..60a285b6 100644 --- a/webpack.config.prod.js +++ b/webpack.config.prod.js @@ -1,7 +1,7 @@ /* eslint-disable */ const path = require('path'); const webpack = require('webpack'); -const ManifestPlugin = require('webpack-manifest-plugin'); +const { WebpackManifestPlugin } = require('webpack-manifest-plugin'); const TerserPlugin = require('terser-webpack-plugin'); commonWebpackConfig = require('./webpack.config'); @@ -42,7 +42,7 @@ productionWebpackConfig = Object.assign(commonWebpackConfig, { productionWebpackConfig.plugins = [ ...productionWebpackConfig.plugins, - new ManifestPlugin() + new WebpackManifestPlugin() ]; module.exports = productionWebpackConfig; diff --git a/yarn.lock b/yarn.lock index 1ded221c..aa15eec2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7946,7 +7946,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -"lodash@>=3.5 <5", lodash@^4.0.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5: +lodash@^4.0.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== @@ -8728,7 +8728,7 @@ object.assign@^4.0.4, object.assign@^4.1.0, object.assign@^4.1.1: has-symbols "^1.0.1" object-keys "^1.1.1" -object.entries@^1.1.0, object.entries@^1.1.2: +object.entries@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.2.tgz#bc73f00acb6b6bb16c203434b10f9a7e797d3add" integrity sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA== @@ -11071,7 +11071,7 @@ sort-keys@^2.0.0: dependencies: is-plain-obj "^1.0.0" -source-list-map@^2.0.0: +source-list-map@^2.0.0, source-list-map@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== @@ -11552,6 +11552,11 @@ tapable@^1.0.0, tapable@^1.1.3: resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== +tapable@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b" + integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw== + tar@^6.0.2: version "6.0.5" resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.5.tgz#bde815086e10b39f1dcd298e89d596e1535e200f" @@ -12599,15 +12604,13 @@ webpack-hot-middleware@2.x: querystring "^0.2.0" strip-ansi "^3.0.0" -webpack-manifest-plugin@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/webpack-manifest-plugin/-/webpack-manifest-plugin-2.2.0.tgz#19ca69b435b0baec7e29fbe90fb4015de2de4f16" - integrity sha512-9S6YyKKKh/Oz/eryM1RyLVDVmy3NSPV0JXMRhZ18fJsq+AwGxUY34X54VNwkzYcEmEkDwNxuEOboCZEebJXBAQ== +webpack-manifest-plugin@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/webpack-manifest-plugin/-/webpack-manifest-plugin-3.0.0.tgz#426644300e5dc41a75a9c996c4d4f876eb3c2b5b" + integrity sha512-nbORTdky2HxD8XSaaT+zrsHb30AAgyWAWgCLWaAeQO21VGCScGb52ipqlHA/njix1Z8OW8IOlo4+XK0OKr1fkw== dependencies: - fs-extra "^7.0.0" - lodash ">=3.5 <5" - object.entries "^1.1.0" - tapable "^1.0.0" + tapable "^2.0.0" + webpack-sources "^2.2.0" webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: version "1.4.3" @@ -12617,6 +12620,14 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: source-list-map "^2.0.0" source-map "~0.6.1" +webpack-sources@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.2.0.tgz#058926f39e3d443193b6c31547229806ffd02bac" + integrity sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w== + dependencies: + source-list-map "^2.0.1" + source-map "^0.6.1" + webpack@4.44.1: version "4.44.1" resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.1.tgz#17e69fff9f321b8f117d1fda714edfc0b939cc21"