Skip to content

Commit 3bace8c

Browse files
authored
fix: Restore DNS prefetching for static resources (outline#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
1 parent 27fca28 commit 3bace8c

File tree

7 files changed

+76
-53
lines changed

7 files changed

+76
-53
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@
203203
"url-loader": "^0.6.2",
204204
"webpack": "4.44.1",
205205
"webpack-cli": "^3.3.12",
206-
"webpack-manifest-plugin": "^2.2.0"
206+
"webpack-manifest-plugin": "^3.0.0"
207207
},
208208
"resolutions": {
209209
"dot-prop": "^5.2.0",

server/app.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,21 @@ const isProduction = process.env.NODE_ENV === "production";
2323
const isTest = process.env.NODE_ENV === "test";
2424

2525
// Construct scripts CSP based on services in use by this installation
26+
const defaultSrc = ["'self'"];
2627
const scriptSrc = [
2728
"'self'",
2829
"'unsafe-inline'",
2930
"'unsafe-eval'",
3031
"gist.github.com",
32+
"browser.sentry-cdn.com",
3133
];
3234

3335
if (env.GOOGLE_ANALYTICS_ID) {
3436
scriptSrc.push("www.google-analytics.com");
3537
}
36-
if (env.SENTRY_DSN) {
37-
scriptSrc.push("browser.sentry-cdn.com");
38-
}
3938
if (env.CDN_URL) {
4039
scriptSrc.push(env.CDN_URL);
40+
defaultSrc.push(env.CDN_URL);
4141
}
4242

4343
app.use(compress());
@@ -167,7 +167,7 @@ app.use(helmet());
167167
app.use(
168168
contentSecurityPolicy({
169169
directives: {
170-
defaultSrc: ["'self'"],
170+
defaultSrc,
171171
scriptSrc,
172172
styleSrc: ["'self'", "'unsafe-inline'", "github.githubassets.com"],
173173
imgSrc: ["*", "data:", "blob:"],

server/routes.js

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { languages } from "../shared/i18n";
1010
import environment from "./env";
1111
import apexRedirect from "./middlewares/apexRedirect";
1212
import { opensearchResponse } from "./utils/opensearch";
13+
import prefetchTags from "./utils/prefetchTags";
1314
import { robotsResponse } from "./utils/robots";
1415

1516
const isProduction = process.env.NODE_ENV === "production";
@@ -50,6 +51,7 @@ const renderApp = async (ctx, next) => {
5051
ctx.body = page
5152
.toString()
5253
.replace(/\/\/inject-env\/\//g, env)
54+
.replace(/\/\/inject-prefetch\/\//g, prefetchTags)
5355
.replace(/\/\/inject-sentry-dsn\/\//g, process.env.SENTRY_DSN || "")
5456
.replace(/\/\/inject-slack-app-id\/\//g, process.env.SLACK_APP_ID || "");
5557
};

server/static/index.html

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<title>Outline</title>
55
<meta name="slack-app-id" content="//inject-slack-app-id//" />
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
//inject-prefetch//
78
<link
89
rel="shortcut icon"
910
type="image/png"

server/utils/prefetchTags.js

+44-35
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,57 @@
22
import fs from "fs";
33
import path from "path";
44
import * as React from "react";
5-
import webpackConfig from "../../webpack.config";
5+
import ReactDOMServer from "react-dom/server";
6+
import env from "../env";
67

7-
const PUBLIC_PATH = webpackConfig.output.publicPath;
8+
const prefetchTags = [];
89

9-
const prefetchTags = [
10-
<link
11-
rel="dns-prefetch"
12-
href={process.env.AWS_S3_UPLOAD_BUCKET_URL}
13-
key="dns"
14-
/>,
15-
];
10+
if (process.env.AWS_S3_UPLOAD_BUCKET_URL) {
11+
prefetchTags.push(
12+
<link
13+
rel="dns-prefetch"
14+
href={process.env.AWS_S3_UPLOAD_BUCKET_URL}
15+
key="dns"
16+
/>
17+
);
18+
}
1619

20+
let manifestData = {};
1721
try {
1822
const manifest = fs.readFileSync(
1923
path.join(__dirname, "../../app/manifest.json"),
2024
"utf8"
2125
);
22-
const manifestData = JSON.parse(manifest);
23-
Object.values(manifestData).forEach((filename) => {
24-
if (typeof filename !== "string") return;
25-
if (filename.endsWith(".js")) {
26-
prefetchTags.push(
27-
<link
28-
rel="prefetch"
29-
href={`${PUBLIC_PATH}${filename}`}
30-
key={filename}
31-
as="script"
32-
/>
33-
);
34-
} else if (filename.endsWith(".css")) {
35-
prefetchTags.push(
36-
<link
37-
rel="prefetch"
38-
href={`${PUBLIC_PATH}${filename}`}
39-
key={filename}
40-
as="style"
41-
/>
42-
);
43-
}
44-
});
45-
} catch (_e) {
46-
// no-op
26+
manifestData = JSON.parse(manifest);
27+
} catch (err) {
28+
console.warn(err);
4729
}
4830

49-
export default prefetchTags;
31+
Object.values(manifestData).forEach((filename) => {
32+
if (typeof filename !== "string") return;
33+
if (!env.CDN_URL) return;
34+
35+
if (filename.endsWith(".js")) {
36+
// Preload resources you have high-confidence will be used in the current
37+
// page.Prefetch resources likely to be used for future navigations
38+
const shouldPreload =
39+
filename.includes("/main") ||
40+
filename.includes("/runtime") ||
41+
filename.includes("/vendors");
42+
43+
prefetchTags.push(
44+
<link
45+
rel={shouldPreload ? "preload" : "prefetch"}
46+
href={filename}
47+
key={filename}
48+
as="script"
49+
/>
50+
);
51+
} else if (filename.endsWith(".css")) {
52+
prefetchTags.push(
53+
<link rel="prefetch" href={filename} key={filename} as="style" />
54+
);
55+
}
56+
});
57+
58+
export default ReactDOMServer.renderToString(prefetchTags);

webpack.config.prod.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable */
22
const path = require('path');
33
const webpack = require('webpack');
4-
const ManifestPlugin = require('webpack-manifest-plugin');
4+
const { WebpackManifestPlugin } = require('webpack-manifest-plugin');
55
const TerserPlugin = require('terser-webpack-plugin');
66

77
commonWebpackConfig = require('./webpack.config');
@@ -42,7 +42,7 @@ productionWebpackConfig = Object.assign(commonWebpackConfig, {
4242

4343
productionWebpackConfig.plugins = [
4444
...productionWebpackConfig.plugins,
45-
new ManifestPlugin()
45+
new WebpackManifestPlugin()
4646
];
4747

4848
module.exports = productionWebpackConfig;

yarn.lock

+22-11
Original file line numberDiff line numberDiff line change
@@ -7946,7 +7946,7 @@ lodash.uniq@^4.5.0:
79467946
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
79477947
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
79487948

7949-
"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:
7949+
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:
79507950
version "4.17.20"
79517951
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
79527952
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
@@ -8728,7 +8728,7 @@ object.assign@^4.0.4, object.assign@^4.1.0, object.assign@^4.1.1:
87288728
has-symbols "^1.0.1"
87298729
object-keys "^1.1.1"
87308730

8731-
object.entries@^1.1.0, object.entries@^1.1.2:
8731+
object.entries@^1.1.2:
87328732
version "1.1.2"
87338733
resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.2.tgz#bc73f00acb6b6bb16c203434b10f9a7e797d3add"
87348734
integrity sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA==
@@ -11071,7 +11071,7 @@ sort-keys@^2.0.0:
1107111071
dependencies:
1107211072
is-plain-obj "^1.0.0"
1107311073

11074-
source-list-map@^2.0.0:
11074+
source-list-map@^2.0.0, source-list-map@^2.0.1:
1107511075
version "2.0.1"
1107611076
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
1107711077
integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
@@ -11552,6 +11552,11 @@ tapable@^1.0.0, tapable@^1.1.3:
1155211552
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
1155311553
integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
1155411554

11555+
tapable@^2.0.0:
11556+
version "2.2.0"
11557+
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b"
11558+
integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==
11559+
1155511560
tar@^6.0.2:
1155611561
version "6.0.5"
1155711562
resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.5.tgz#bde815086e10b39f1dcd298e89d596e1535e200f"
@@ -12599,15 +12604,13 @@ webpack-hot-middleware@2.x:
1259912604
querystring "^0.2.0"
1260012605
strip-ansi "^3.0.0"
1260112606

12602-
webpack-manifest-plugin@^2.2.0:
12603-
version "2.2.0"
12604-
resolved "https://registry.yarnpkg.com/webpack-manifest-plugin/-/webpack-manifest-plugin-2.2.0.tgz#19ca69b435b0baec7e29fbe90fb4015de2de4f16"
12605-
integrity sha512-9S6YyKKKh/Oz/eryM1RyLVDVmy3NSPV0JXMRhZ18fJsq+AwGxUY34X54VNwkzYcEmEkDwNxuEOboCZEebJXBAQ==
12607+
webpack-manifest-plugin@^3.0.0:
12608+
version "3.0.0"
12609+
resolved "https://registry.yarnpkg.com/webpack-manifest-plugin/-/webpack-manifest-plugin-3.0.0.tgz#426644300e5dc41a75a9c996c4d4f876eb3c2b5b"
12610+
integrity sha512-nbORTdky2HxD8XSaaT+zrsHb30AAgyWAWgCLWaAeQO21VGCScGb52ipqlHA/njix1Z8OW8IOlo4+XK0OKr1fkw==
1260612611
dependencies:
12607-
fs-extra "^7.0.0"
12608-
lodash ">=3.5 <5"
12609-
object.entries "^1.1.0"
12610-
tapable "^1.0.0"
12612+
tapable "^2.0.0"
12613+
webpack-sources "^2.2.0"
1261112614

1261212615
webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3:
1261312616
version "1.4.3"
@@ -12617,6 +12620,14 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3:
1261712620
source-list-map "^2.0.0"
1261812621
source-map "~0.6.1"
1261912622

12623+
webpack-sources@^2.2.0:
12624+
version "2.2.0"
12625+
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.2.0.tgz#058926f39e3d443193b6c31547229806ffd02bac"
12626+
integrity sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w==
12627+
dependencies:
12628+
source-list-map "^2.0.1"
12629+
source-map "^0.6.1"
12630+
1262012631
webpack@4.44.1:
1262112632
version "4.44.1"
1262212633
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.1.tgz#17e69fff9f321b8f117d1fda714edfc0b939cc21"

0 commit comments

Comments
 (0)