diff --git a/package.json b/package.json
index b1a71983a3ab..e6702902c0e0 100644
--- a/package.json
+++ b/package.json
@@ -35,7 +35,7 @@
"fetch:supporters": "node src/utilities/fetch-supporters.js",
"fetch:starter-kits": "node src/utilities/fetch-starter-kits.js",
"prebuild": "npm run clean",
- "build": "run-s fetch content && cross-env NODE_ENV=production webpack --config webpack.prod.js",
+ "build": "run-s fetch content && cross-env NODE_ENV=production webpack --config webpack.ssg.js && cross-env NODE_ENV=production webpack --config webpack.prod.js",
"postbuild": "npm run sitemap",
"test": "npm run lint",
"lint": "run-s lint:*",
@@ -109,6 +109,7 @@
"modularscale-sass": "^3.0.3",
"node-sass": "^4.5.3",
"npm-run-all": "^4.1.1",
+ "offline-plugin": "^5.0.7",
"optimize-css-assets-webpack-plugin": "^5.0.1",
"postcss-loader": "^2.1.3",
"redirect-webpack-plugin": "^0.1.1",
diff --git a/src/components/Site/Site.jsx b/src/components/Site/Site.jsx
index 419b98ebec4d..4cbe5033f3fc 100644
--- a/src/components/Site/Site.jsx
+++ b/src/components/Site/Site.jsx
@@ -31,6 +31,11 @@ import './Site.scss';
// Load Content Tree
import Content from '../../_content.json';
+// call offline plugin so it can build
+if (isClient) {
+ require('offline-plugin/runtime').install();
+}
+
class Site extends React.Component {
state = {
mobileSidebarOpen: false
@@ -77,6 +82,7 @@ class Site extends React.Component {
+ } />
{pages.map(page => (
item.endsWith(endsWith))
+ : filesInDist;
+};
\ No newline at end of file
diff --git a/webpack.common.js b/webpack.common.js
index ea7e608010f2..956e6ef4317d 100644
--- a/webpack.common.js
+++ b/webpack.common.js
@@ -148,6 +148,7 @@ module.exports = (env = {}) => ({
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/',
- filename: '[name].bundle.js'
+ filename: '[name].bundle.js',
+ chunkFilename: '[name].[chunkhash].chunk.js'
}
});
diff --git a/webpack.prod.js b/webpack.prod.js
index f0c82f6de796..6fb7a169122d 100644
--- a/webpack.prod.js
+++ b/webpack.prod.js
@@ -1,27 +1,22 @@
// Import External Dependencies
const merge = require('webpack-merge');
-const SSGPlugin = require('static-site-generator-webpack-plugin');
-const RedirectWebpackPlugin = require('redirect-webpack-plugin');
-const CopyWebpackPlugin = require('copy-webpack-plugin');
-const flattenContentTree = require('./src/utilities/flatten-content-tree');
-const contentTree = require('./src/_content.json');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
+const OfflinePlugin = require('offline-plugin');
// Load Common Configuration
const common = require('./webpack.common.js');
-// content tree to path array
-const paths = [
- ...flattenContentTree(contentTree),
- '/vote',
- '/organization',
- '/starter-kits'
-];
+// find css files for sw
+const cssFiles = require('./src/utilities/find-files-in-dist')('.css');
+// find favicons
+const favicons = require('./src/utilities/find-files-in-dist')('.ico');
-// Prod only config
-const prod = {
+// fall back all urls to app shell
+
+module.exports = env => merge(common(env), {
mode: 'production',
+ target: 'web',
optimization: {
minimizer: [
new TerserJSPlugin({}),
@@ -29,83 +24,16 @@ const prod = {
]
},
plugins: [
- new CopyWebpackPlugin([
- {
- from: './assets/PWA',
- to: './'
- },
- {
- from: './assets/icon-square-small-slack.png',
- to: './assets/'
- },
- {
- from: './assets/icon-square-big.svg',
- to: './assets/'
- },
- 'CNAME'
- ])
+ new OfflinePlugin({
+ autoUpdate: true,
+ publicPath: '/',
+ appShell: '/app-shell/',
+ // make sure to cache homepage and app shell as app shell for the rest of the pages.
+ externals: ['/app-shell/', '/', '/manifest.json', ...cssFiles, ...favicons],
+ excludes: [],
+ AppCache: {
+ publicPath: '/'
+ }
+ })
]
-};
-
-// Export both SSG and SPA configurations
-module.exports = env => [
- merge(common(env), prod, {
- target: 'node',
- entry: {
- index: './server.jsx'
- },
- plugins: [
- new SSGPlugin({
- globals: {
- window: {}
- },
- paths,
- locals: {
- content: contentTree
- }
- }),
- new RedirectWebpackPlugin({
- redirects: {
- 'support': '/contribute/',
- 'writers-guide': '/contribute/writers-guide/',
- 'get-started': '/guides/getting-started/',
- 'get-started/install-webpack': '/guides/installation/',
- 'get-started/why-webpack': '/guides/why-webpack/',
- 'pluginsapi': '/api/plugins/',
- 'pluginsapi/compiler': '/api/compiler-hooks/',
- 'pluginsapi/template': '/api/template/',
- 'api/passing-a-config': '/configuration/configuration-types/',
- 'api/plugins/compiler': '/api/compiler-hooks/',
- 'api/plugins/compilation': '/api/compilation/',
- 'api/plugins/module-factories': '/api/module-methods/',
- 'api/plugins/parser': '/api/parser/',
- 'api/plugins/tapable': '/api/tapable/',
- 'api/plugins/template': '/api/template/',
- 'api/plugins/resolver': '/api/resolver/',
- 'development': '/contribute/',
- 'development/plugin-patterns': '/contribute/plugin-patterns/',
- 'development/release-process': '/contribute/release-process/',
- 'development/how-to-write-a-loader': '/contribute/writing-a-loader/',
- 'development/how-to-write-a-plugin': '/contribute/writing-a-plugin/',
- 'guides/code-splitting-import': '/guides/code-splitting/',
- 'guides/code-splitting-require': '/guides/code-splitting/',
- 'guides/code-splitting-async': '/guides/code-splitting/',
- 'guides/code-splitting-css': '/guides/code-splitting/',
- 'guides/code-splitting-libraries': '/guides/code-splitting/',
- 'guides/why-webpack': '/comparison/',
- 'guides/production-build': '/guides/production/',
- 'migrating': '/migrate/3/',
- 'plugins/no-emit-on-errors-plugin': '/configuration/optimization/#optimization-noemitonerrors',
- 'concepts/mode': '/configuration/mode'
- }
- })
- ],
- output: {
- filename: 'server.[name].js',
- libraryTarget: 'umd'
- }
- }),
- merge(common(env), prod, {
- target: 'web'
- })
-];
+});
diff --git a/webpack.ssg.js b/webpack.ssg.js
new file mode 100644
index 000000000000..373b62ecf5bd
--- /dev/null
+++ b/webpack.ssg.js
@@ -0,0 +1,95 @@
+// Import External Dependencies
+const merge = require('webpack-merge');
+const SSGPlugin = require('static-site-generator-webpack-plugin');
+const RedirectWebpackPlugin = require('redirect-webpack-plugin');
+const CopyWebpackPlugin = require('copy-webpack-plugin');
+const flattenContentTree = require('./src/utilities/flatten-content-tree');
+const contentTree = require('./src/_content.json');
+
+// Load Common Configuration
+const common = require('./webpack.common.js');
+
+// content tree to path array
+const paths = [
+ ...flattenContentTree(contentTree),
+ '/vote',
+ '/organization',
+ '/starter-kits',
+ '/app-shell'
+];
+
+module.exports = env => merge(common(env), {
+ mode: 'production',
+ target: 'node',
+ entry: {
+ index: './server.jsx'
+ },
+ output: {
+ filename: 'server.[name].js',
+ libraryTarget: 'umd'
+ },
+ optimization: {
+ splitChunks: false
+ },
+ plugins: [
+ new SSGPlugin({
+ globals: {
+ window: {}
+ },
+ paths,
+ locals: {
+ content: contentTree
+ }
+ }),
+ new RedirectWebpackPlugin({
+ redirects: {
+ 'support': '/contribute/',
+ 'writers-guide': '/contribute/writers-guide/',
+ 'get-started': '/guides/getting-started/',
+ 'get-started/install-webpack': '/guides/installation/',
+ 'get-started/why-webpack': '/guides/why-webpack/',
+ 'pluginsapi': '/api/plugins/',
+ 'pluginsapi/compiler': '/api/compiler-hooks/',
+ 'pluginsapi/template': '/api/template/',
+ 'api/passing-a-config': '/configuration/configuration-types/',
+ 'api/plugins/compiler': '/api/compiler-hooks/',
+ 'api/plugins/compilation': '/api/compilation/',
+ 'api/plugins/module-factories': '/api/module-methods/',
+ 'api/plugins/parser': '/api/parser/',
+ 'api/plugins/tapable': '/api/tapable/',
+ 'api/plugins/template': '/api/template/',
+ 'api/plugins/resolver': '/api/resolver/',
+ 'development': '/contribute/',
+ 'development/plugin-patterns': '/contribute/plugin-patterns/',
+ 'development/release-process': '/contribute/release-process/',
+ 'development/how-to-write-a-loader': '/contribute/writing-a-loader/',
+ 'development/how-to-write-a-plugin': '/contribute/writing-a-plugin/',
+ 'guides/code-splitting-import': '/guides/code-splitting/',
+ 'guides/code-splitting-require': '/guides/code-splitting/',
+ 'guides/code-splitting-async': '/guides/code-splitting/',
+ 'guides/code-splitting-css': '/guides/code-splitting/',
+ 'guides/code-splitting-libraries': '/guides/code-splitting/',
+ 'guides/why-webpack': '/comparison/',
+ 'guides/production-build': '/guides/production/',
+ 'migrating': '/migrate/3/',
+ 'plugins/no-emit-on-errors-plugin': '/configuration/optimization/#optimization-noemitonerrors',
+ 'concepts/mode': '/configuration/mode'
+ }
+ }),
+ new CopyWebpackPlugin([
+ {
+ from: './assets/PWA',
+ to: './'
+ },
+ {
+ from: './assets/icon-square-small-slack.png',
+ to: './assets/'
+ },
+ {
+ from: './assets/icon-square-big.svg',
+ to: './assets/'
+ },
+ 'CNAME'
+ ])
+ ]
+ });
diff --git a/yarn.lock b/yarn.lock
index 825f292eb416..45fc677618f9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3057,16 +3057,16 @@ deep-equal@^1.0.0, deep-equal@^1.0.1:
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=
+deep-extend@^0.5.1, deep-extend@~0.5.1:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.5.1.tgz#b894a9dd90d3023fbf1c55a394fb858eb2066f1f"
+ integrity sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==
+
deep-extend@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
-deep-extend@~0.5.1:
- version "0.5.1"
- resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.5.1.tgz#b894a9dd90d3023fbf1c55a394fb858eb2066f1f"
- integrity sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==
-
deep-is@~0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
@@ -3434,6 +3434,11 @@ ee-first@1.1.1:
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
+ejs@^2.3.4:
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0"
+ integrity sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==
+
electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.124, electron-to-chromium@^1.3.30, electron-to-chromium@^1.3.47:
version "1.3.125"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.125.tgz#dbde0e95e64ebe322db0eca764d951f885a5aff2"
@@ -6374,7 +6379,7 @@ loader-runner@^2.3.0:
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357"
integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==
-loader-utils@^0.2.10, loader-utils@^0.2.16:
+loader-utils@0.2.x, loader-utils@^0.2.10, loader-utils@^0.2.16:
version "0.2.17"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348"
integrity sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=
@@ -7071,7 +7076,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
-"minimatch@2 || 3", minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2, minimatch@~3.0.4:
+"minimatch@2 || 3", minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2, minimatch@~3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
@@ -7666,6 +7671,17 @@ obuf@^1.0.0, obuf@^1.1.2:
resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==
+offline-plugin@^5.0.7:
+ version "5.0.7"
+ resolved "https://registry.yarnpkg.com/offline-plugin/-/offline-plugin-5.0.7.tgz#26936ad1a7699f4d67e0a095a258972a4ccf1788"
+ integrity sha512-ArMFt4QFjK0wg8B5+R/6tt65u6Dk+Pkx4PAcW5O7mgIF3ywMepaQqFOQgfZD4ybanuGwuJihxUwMRgkzd+YGYw==
+ dependencies:
+ deep-extend "^0.5.1"
+ ejs "^2.3.4"
+ loader-utils "0.2.x"
+ minimatch "^3.0.3"
+ slash "^1.0.0"
+
on-finished@~2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"