diff --git a/package-lock.json b/package-lock.json index 6a1cd758ca..e674eafb81 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2489,6 +2489,12 @@ "optimism": "^0.6.6" } }, + "apollo-cache-persist": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/apollo-cache-persist/-/apollo-cache-persist-0.1.1.tgz", + "integrity": "sha512-/7GAyblPR169ryW3ugbtHqiU0UGkhIt10NeaO2gn2ClxjLHF/nIkJD5mx/0OCF2vLNbbnzLZVDeIO1pf72TrEA==", + "dev": true + }, "apollo-client": { "version": "2.4.7", "resolved": "https://registry.npmjs.org/apollo-client/-/apollo-client-2.4.7.tgz", @@ -2957,7 +2963,7 @@ }, "util": { "version": "0.10.3", - "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -6573,7 +6579,7 @@ }, "d": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/d/-/d-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", "dev": true, "requires": { @@ -8905,24 +8911,28 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, "optional": true }, "ansi-regex": { "version": "2.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, "aproba": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.4", - "bundled": true, + "resolved": false, + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", "dev": true, "optional": true, "requires": { @@ -8932,12 +8942,14 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, "brace-expansion": { "version": "1.1.11", - "bundled": true, + "resolved": false, + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -8946,34 +8958,40 @@ }, "chownr": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true }, "concat-map": { "version": "0.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true }, "core-util-is": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true, "optional": true }, "debug": { "version": "2.6.9", - "bundled": true, + "resolved": false, + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "optional": true, "requires": { @@ -8982,25 +9000,29 @@ }, "deep-extend": { "version": "0.5.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==", "dev": true, "optional": true }, "delegates": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true, "optional": true }, "detect-libc": { "version": "1.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true, "optional": true }, "fs-minipass": { "version": "1.2.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "dev": true, "optional": true, "requires": { @@ -9009,13 +9031,15 @@ }, "fs.realpath": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true, "optional": true }, "gauge": { "version": "2.7.4", - "bundled": true, + "resolved": false, + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "optional": true, "requires": { @@ -9031,7 +9055,8 @@ }, "glob": { "version": "7.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "optional": true, "requires": { @@ -9045,13 +9070,15 @@ }, "has-unicode": { "version": "2.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true, "optional": true }, "iconv-lite": { "version": "0.4.21", - "bundled": true, + "resolved": false, + "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==", "dev": true, "optional": true, "requires": { @@ -9060,7 +9087,8 @@ }, "ignore-walk": { "version": "3.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "dev": true, "optional": true, "requires": { @@ -9069,7 +9097,8 @@ }, "inflight": { "version": "1.0.6", - "bundled": true, + "resolved": false, + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "optional": true, "requires": { @@ -9079,18 +9108,21 @@ }, "inherits": { "version": "2.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, "ini": { "version": "1.3.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { "number-is-nan": "^1.0.0" @@ -9098,13 +9130,15 @@ }, "isarray": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true, "optional": true }, "minimatch": { "version": "3.0.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -9112,12 +9146,14 @@ }, "minimist": { "version": "0.0.8", - "bundled": true, + "resolved": false, + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, "minipass": { "version": "2.2.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", "dev": true, "requires": { "safe-buffer": "^5.1.1", @@ -9126,7 +9162,8 @@ }, "minizlib": { "version": "1.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==", "dev": true, "optional": true, "requires": { @@ -9135,7 +9172,8 @@ }, "mkdirp": { "version": "0.5.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { "minimist": "0.0.8" @@ -9143,13 +9181,15 @@ }, "ms": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true, "optional": true }, "needle": { "version": "2.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==", "dev": true, "optional": true, "requires": { @@ -9160,7 +9200,8 @@ }, "node-pre-gyp": { "version": "0.10.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==", "dev": true, "optional": true, "requires": { @@ -9178,7 +9219,8 @@ }, "nopt": { "version": "4.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "dev": true, "optional": true, "requires": { @@ -9188,13 +9230,15 @@ }, "npm-bundled": { "version": "1.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==", "dev": true, "optional": true }, "npm-packlist": { "version": "1.1.10", - "bundled": true, + "resolved": false, + "integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==", "dev": true, "optional": true, "requires": { @@ -9204,7 +9248,8 @@ }, "npmlog": { "version": "4.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "optional": true, "requires": { @@ -9216,18 +9261,21 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, "object-assign": { "version": "4.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, "optional": true }, "once": { "version": "1.4.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { "wrappy": "1" @@ -9235,19 +9283,22 @@ }, "os-homedir": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true, "optional": true }, "osenv": { "version": "0.1.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "optional": true, "requires": { @@ -9257,19 +9308,22 @@ }, "path-is-absolute": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, "optional": true }, "process-nextick-args": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true, "optional": true }, "rc": { "version": "1.2.7", - "bundled": true, + "resolved": false, + "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==", "dev": true, "optional": true, "requires": { @@ -9281,7 +9335,8 @@ "dependencies": { "minimist": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true, "optional": true } @@ -9289,7 +9344,8 @@ }, "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": false, + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "optional": true, "requires": { @@ -9304,7 +9360,8 @@ }, "rimraf": { "version": "2.6.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "optional": true, "requires": { @@ -9313,42 +9370,49 @@ }, "safe-buffer": { "version": "5.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", "dev": true }, "safer-buffer": { "version": "2.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "optional": true }, "sax": { "version": "1.2.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true, "optional": true }, "semver": { "version": "5.5.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true, "optional": true }, "string-width": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { "code-point-at": "^1.0.0", @@ -9358,7 +9422,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "optional": true, "requires": { @@ -9367,7 +9432,8 @@ }, "strip-ansi": { "version": "3.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -9375,13 +9441,15 @@ }, "strip-json-comments": { "version": "2.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "optional": true }, "tar": { "version": "4.4.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-O+v1r9yN4tOsvl90p5HAP4AEqbYhx4036AGMm075fH9F8Qwi3oJ+v4u50FkT/KkvywNGtwkk0zRI+8eYm1X/xg==", "dev": true, "optional": true, "requires": { @@ -9396,13 +9464,15 @@ }, "util-deprecate": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true, "optional": true }, "wide-align": { "version": "1.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", "dev": true, "optional": true, "requires": { @@ -9411,12 +9481,14 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, "yallist": { "version": "3.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", "dev": true } } @@ -14094,7 +14166,7 @@ }, "next-tick": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, diff --git a/package.json b/package.json index f9e2a157ee..a28cf09616 100644 --- a/package.json +++ b/package.json @@ -38,9 +38,12 @@ "devDependencies": { "@magento/directive-parser": "^0.1.1", "@magento/eslint-config": "^1.2.3", + "@storybook/addon-actions": "^3.4.2", + "@storybook/addons": "^3.4.6", "@storybook/react": "^3.4.2", "apollo-boost": "^0.1.20", "apollo-cache-inmemory": "^1.3.9", + "apollo-cache-persist": "^0.1.1", "apollo-client": "^2.4.5", "apollo-link-context": "^1.0.9", "apollo-server": "^2.0.5", diff --git a/packages/peregrine/src/Router/MagentoRouteHandler.js b/packages/peregrine/src/Router/MagentoRouteHandler.js index 3834e9bd5a..2de21af426 100644 --- a/packages/peregrine/src/Router/MagentoRouteHandler.js +++ b/packages/peregrine/src/Router/MagentoRouteHandler.js @@ -26,9 +26,17 @@ export default class MagentoRouteHandler extends Component { } }; + // TODO: Add the ability to customize the cache name + async addToCache(urls) { + const myCache = await window.caches.open( + `workbox-runtime-${location.origin}/` + ); + await myCache.addAll(urls); + } + componentDidMount() { mountedInstances.add(this); - this.getRouteComponent(this.props.location.pathname); + this.getRouteComponent(); } componentDidUpdate() { @@ -36,8 +44,16 @@ export default class MagentoRouteHandler extends Component { const { pathname } = props.location; const isKnown = state.componentMap.has(pathname); - if (!isKnown) { - this.getRouteComponent(pathname); + // `NOTFOUND` component needs a unique id + // currently it is set to -1 + const isNotFoundComponent = isKnown + ? state.componentMap.get(pathname).id === -1 + : false; + + const shouldReloadRoute = isNotFoundComponent && navigator.onLine; + + if (!isKnown || shouldReloadRoute) { + this.getRouteComponent(); } } @@ -88,6 +104,8 @@ export default class MagentoRouteHandler extends Component { return; } + this.addToCache([pathname]); + this.setState(({ componentMap }) => ({ componentMap: new Map(componentMap).set(pathname, { RootComponent, diff --git a/packages/peregrine/src/Router/__tests__/resolveUnknownRoute.test.js b/packages/peregrine/src/Router/__tests__/resolveUnknownRoute.test.js index d02fd682ae..2a921e9918 100644 --- a/packages/peregrine/src/Router/__tests__/resolveUnknownRoute.test.js +++ b/packages/peregrine/src/Router/__tests__/resolveUnknownRoute.test.js @@ -7,7 +7,48 @@ const urlResolverRes = (type, id) => } }); +const NotFoundManifest = { + NotFound: { + rootChunkID: -1, + rootModuleID: 100, + pageTypes: ['NOTFOUND'] + } +}; + +const mockManifest = { + Category: { + rootChunkID: 2, + rootModuleID: 100, + pageTypes: ['CATEGORY'] + }, + Product: { + rootChunkID: 1, + rootModuleID: 99, + pageTypes: ['PRODUCT'] + }, + ...NotFoundManifest +}; + +const cachedResponse = JSON.stringify({ + 'foo-bar.html': { + ...JSON.parse(urlResolverRes('PRODUCT')) + } +}); + +const isOnline = _value => ({ + get: () => _value, + set: v => (_value = v) +}); + +Object.defineProperty(navigator, 'onLine', isOnline(true)); + +function clearLocalStorage(item) { + localStorage.setItem(item, null); +} + beforeEach(() => { + navigator.onLine = true; + clearLocalStorage('urlResolve'); document.body.innerHTML = ''; resolveUnknownRoute.preloadDone = false; fetch.resetMocks(); @@ -18,7 +59,7 @@ test('Preload path: resolves directly from preload element', async () => { ''; const res = await resolveUnknownRoute({ route: 'foo-bar.html', - apiBase: 'https://store.com' + apiBase: 'https://example.com' }); expect(res).toMatchObject({ type: 'PRODUCT', @@ -26,6 +67,59 @@ test('Preload path: resolves directly from preload element', async () => { }); }); +test('returns NOTFOUND when offline and requested content is not in cache ', async () => { + navigator.onLine = false; + + fetch.mockResponseOnce(JSON.stringify(mockManifest)); + const res = await resolveUnknownRoute({ + route: 'foo-bar.html', + apiBase: 'https://store.com', + __tmp_webpack_public_path__: 'https://dev-server.com/pub' + }); + + expect(res).toHaveProperty('id', NotFoundManifest.NotFound.rootChunkID); +}); + +test('stores response of urlResolver in cache', async () => { + fetch.mockResponseOnce(urlResolverRes('PRODUCT')); + fetch.mockResponseOnce(JSON.stringify(mockManifest)); + + const url = 'foo-bar.html'; + + await resolveUnknownRoute({ + route: url, + apiBase: 'https://store.com', + __tmp_webpack_public_path__: 'https://dev-server.com/pub' + }); + + expect(localStorage.getItem('urlResolve')).not.toBeNull(); +}); + +test('does not call fetchRoute when response is cached', async () => { + localStorage.setItem('urlResolve', cachedResponse); + + fetch.mockResponseOnce(JSON.stringify(mockManifest)); + await resolveUnknownRoute({ + route: 'foo-bar.html', + apiBase: 'https://store.com', + __tmp_webpack_public_path__: 'https://dev-server.com/pub' + }); + + expect(fetch).toHaveBeenCalledTimes(0); +}); + +test('calls fetchRoute when response is not cached', async () => { + fetch.mockResponseOnce(urlResolverRes('PRODUCT')); + fetch.mockResponseOnce(JSON.stringify(mockManifest)); + await resolveUnknownRoute({ + route: 'foo-bar.html', + apiBase: 'https://store.com', + __tmp_webpack_public_path__: 'https://dev-server.com/pub' + }); + + expect(fetch).toHaveBeenCalledTimes(1); +}); + test('urlResolver path: resolve using fetch to GraphQL after one preload', async () => { fetch.mockResponseOnce(urlResolverRes('PRODUCT', 'VA-11')); document.body.innerHTML = diff --git a/packages/peregrine/src/Router/resolveUnknownRoute.js b/packages/peregrine/src/Router/resolveUnknownRoute.js index f186651a47..1c4e46d1e9 100644 --- a/packages/peregrine/src/Router/resolveUnknownRoute.js +++ b/packages/peregrine/src/Router/resolveUnknownRoute.js @@ -42,11 +42,37 @@ export default async function resolveUnknownRoute(opts) { } /** - * @description Calls the GraphQL API for results from the urlResolver query + * @description Checks if route is stored in localStorage, if not call `fetchRoute` * @param {{ route: string, apiBase: string}} opts * @returns {Promise<{type: "PRODUCT" | "CATEGORY" | "CMS_PAGE"}>} */ function remotelyResolveRoute(opts) { + let urlResolve = localStorage.getItem('urlResolve'); + urlResolve = JSON.parse(urlResolve); + + // If it exists in localStorage, use that value + // TODO: This can be handled by workbox once this issue is resolved in the + // graphql repo: https://github.com/magento/graphql-ce/issues/229 + if ((urlResolve && urlResolve[opts.route]) || !navigator.onLine) { + if (urlResolve && urlResolve[opts.route]) { + return Promise.resolve(urlResolve[opts.route].data.urlResolver); + } else { + return Promise.resolve({ + type: 'NOTFOUND', + id: -1 + }); + } + } else { + return fetchRoute(opts); + } +} + +/** + * @description Calls the GraphQL API for results from the urlResolver query + * @param {{ route: string, apiBase: string}} opts + * @returns {Promise<{type: "PRODUCT" | "CATEGORY" | "CMS_PAGE"}>} + */ +function fetchRoute(opts) { const url = new URL('/graphql', opts.apiBase); return fetch(url, { method: 'POST', @@ -66,5 +92,18 @@ function remotelyResolveRoute(opts) { }) }) .then(res => res.json()) - .then(res => res.data.urlResolver); + .then(res => { + storeURLResolveResult(res, opts); + return res.data.urlResolver; + }); +} + +// TODO: This can be handled by workbox once this issue is resolved in the +// graphql repo: https://github.com/magento/graphql-ce/issues/229 +function storeURLResolveResult(res, opts) { + const storedRoute = localStorage.getItem('urlResolve'); + const item = JSON.parse(storedRoute) || {}; + + item[opts.route] = res; + localStorage.setItem('urlResolve', JSON.stringify(item)); } diff --git a/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js b/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js index 609e803ffd..f5d9197ef2 100644 --- a/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js +++ b/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js @@ -13,7 +13,7 @@ class ServiceWorkerPlugin { ServiceWorkerPlugin.validateOptions('ServiceWorkerPlugin', config); this.config = config; } - applyWorkbox(compiler) { + applyGenerateSW(compiler) { const config = { // `globDirectory` and `globPatterns` must match at least 1 file // otherwise workbox throws an error @@ -28,25 +28,48 @@ class ServiceWorkerPlugin { swDest: this.config.serviceWorkerFileName }; - if (this.config.runtimeCacheAssetPath) { - config.runtimeCaching = [ - { - urlPattern: new RegExp(this.config.runtimeCacheAssetPath), - handler: 'staleWhileRevalidate' - } - ]; + if (this.config.runtimeCacheConfig) { + config.runtimeCaching = this.config.runtimeCacheConfig; } new WorkboxPlugin.GenerateSW(config).apply(compiler); } + + configureInjectManifest() { + let injectManifest; + if (this.config.injectManifestConfig) { + injectManifest = new WorkboxPlugin.InjectManifest( + this.config.injectManifestConfig + ); + } else { + injectManifest = new WorkboxPlugin.InjectManifest({ + swSrc: this.config.paths.src + '/sw.js', + swDest: this.config.paths.dest + '/sw.js' + }); + } + return injectManifest; + } + + applyInjectManifest(compiler) { + this.configureInjectManifest().apply(compiler); + } + apply(compiler) { if (this.config.env.mode === 'development') { // add a WriteFilePlugin to write out the service worker to the filesystem so it can be served by M2, even though it's under dev - if (this.config.enableServiceWorkerDebugging) { + if ( + this.config.enableServiceWorkerDebugging && + !this.config.injectManifest + ) { new WriteFileWebpackPlugin({ test: new RegExp(this.config.serviceWorkerFileName + '$'), log: true }).apply(compiler); - this.applyWorkbox(compiler); + this.applyGenerateSW(compiler); + } else if ( + this.config.enableServiceWorkerDebugging && + this.config.injectManifest + ) { + this.applyInjectManifest(compiler); } else { // TODO: (feature) emit a structured { code, severity, resolution } object // on Environment that might throw and might not @@ -58,5 +81,13 @@ class ServiceWorkerPlugin { this.applyWorkbox(compiler); } } + + applyWorkbox(compiler) { + if (this.config.injectManifest) { + this.applyInjectManifest(compiler); + } else { + this.applyGenerateSW(compiler); + } + } } module.exports = ServiceWorkerPlugin; diff --git a/packages/pwa-buildpack/src/WebpackTools/plugins/__tests__/ServiceWorkerPlugin.spec.js b/packages/pwa-buildpack/src/WebpackTools/plugins/__tests__/ServiceWorkerPlugin.spec.js index 463682f28c..76473a47ad 100644 --- a/packages/pwa-buildpack/src/WebpackTools/plugins/__tests__/ServiceWorkerPlugin.spec.js +++ b/packages/pwa-buildpack/src/WebpackTools/plugins/__tests__/ServiceWorkerPlugin.spec.js @@ -134,3 +134,34 @@ test('.apply generates and writes out a serviceworker when enableServiceWorkerDe }) ); }); + +test('.apply uses `InjectManifest` when `injectManifest` is `true`', () => { + const injectManifestConfig = { + swSrc: 'path/to/sw', + swDest: 'path/to/dest' + }; + const plugin = new ServiceWorkerPlugin({ + env: { + mode: 'development' + }, + enableServiceWorkerDebugging: true, + serviceWorkerFileName: 'sw.js', + injectManifest: true, + paths: { + output: 'path/to/assets' + }, + injectManifestConfig + }); + + const fakeCompiler = {}; + const workboxApply = jest.fn(); + WorkboxPlugin.InjectManifest.mockImplementationOnce(() => ({ + apply: workboxApply + })); + + plugin.apply(fakeCompiler); + + expect(WorkboxPlugin.InjectManifest).toHaveBeenCalledWith( + expect.objectContaining(injectManifestConfig) + ); +}); diff --git a/packages/venia-concept/media/favicon.ico b/packages/venia-concept/media/favicon.ico deleted file mode 100644 index fd57e1d1a9..0000000000 Binary files a/packages/venia-concept/media/favicon.ico and /dev/null differ diff --git a/packages/venia-concept/src/RootComponents/NotFound/index.js b/packages/venia-concept/src/RootComponents/NotFound/index.js new file mode 100644 index 0000000000..c098bd07f4 --- /dev/null +++ b/packages/venia-concept/src/RootComponents/NotFound/index.js @@ -0,0 +1,6 @@ +/** + * @RootComponent + * description = 'Page to display when offline' + * pageTypes = NOTFOUND + */ +export { default } from './notFound'; diff --git a/packages/venia-concept/src/RootComponents/NotFound/notFound.css b/packages/venia-concept/src/RootComponents/NotFound/notFound.css new file mode 100644 index 0000000000..e98375699b --- /dev/null +++ b/packages/venia-concept/src/RootComponents/NotFound/notFound.css @@ -0,0 +1,10 @@ +.root { + padding: 1rem; +} + +.title { + font-size: 1.5rem; + font-weight: 400; + margin: 0 0 1rem; + padding: 0.5rem; +} diff --git a/packages/venia-concept/src/RootComponents/NotFound/notFound.js b/packages/venia-concept/src/RootComponents/NotFound/notFound.js new file mode 100644 index 0000000000..654fc1fa0f --- /dev/null +++ b/packages/venia-concept/src/RootComponents/NotFound/notFound.js @@ -0,0 +1,28 @@ +import React, { Component } from 'react'; +import classify from 'src/classify'; +import defaultClasses from './notFound.css'; + +class NotFound extends Component { + // TODO: Should not be a default here, we just don't have + // the wiring in place to map route info down the tree (yet) + static defaultProps = { + id: 3 + }; + + goBack() { + history.back(); + } + + render() { + const { classes } = this.props; + + return ( +
+

Offline!

+ +
+ ); + } +} + +export default classify(defaultClasses)(NotFound); diff --git a/packages/venia-concept/src/actions/app/actions.js b/packages/venia-concept/src/actions/app/actions.js index 972465ba4d..3074eb31b6 100644 --- a/packages/venia-concept/src/actions/app/actions.js +++ b/packages/venia-concept/src/actions/app/actions.js @@ -1,6 +1,6 @@ import { createActions } from 'redux-actions'; const prefix = 'APP'; -const actionTypes = ['TOGGLE_DRAWER']; +const actionTypes = ['TOGGLE_DRAWER', 'SET_ONLINE', 'SET_OFFLINE']; export default createActions(...actionTypes, { prefix }); diff --git a/packages/venia-concept/src/components/App/app.js b/packages/venia-concept/src/components/App/app.js index 7fa1681b99..b4298a4b7c 100644 --- a/packages/venia-concept/src/components/App/app.js +++ b/packages/venia-concept/src/components/App/app.js @@ -8,6 +8,7 @@ import Main from 'src/components/Main'; import Mask from 'src/components/Mask'; import MiniCart from 'src/components/MiniCart'; import Navigation from 'src/components/Navigation'; +import OnlineIndicator from 'src/components/OnlineIndicator'; import defaultClasses from './app.css'; const renderRoutingError = props => ; @@ -25,8 +26,18 @@ class App extends Component { closeDrawer: func.isRequired }; + get onlineIndicator() { + const { app } = this.props; + const { hasBeenOffline, isOnline } = app; + + // Only show online indicator when + // online after being offline + return hasBeenOffline ? : null; + } + render() { const { app, classes, closeDrawer } = this.props; + const { onlineIndicator } = this; const { drawer, overlay } = app; const navIsOpen = drawer === 'nav'; const cartIsOpen = drawer === 'cart'; @@ -35,6 +46,7 @@ class App extends Component { return (
+ {onlineIndicator} {renderRoutingError}
diff --git a/packages/venia-concept/src/components/Main/main.css b/packages/venia-concept/src/components/Main/main.css index b85bd1465e..3b91ade884 100644 --- a/packages/venia-concept/src/components/Main/main.css +++ b/packages/venia-concept/src/components/Main/main.css @@ -5,18 +5,16 @@ z-index: 1; } -.page { - min-height: 100vh; -} - -/* state: masked */ - .root_masked { composes: root; height: 100vh; overflow: hidden; } +.page { + min-height: 100vh; +} + .page_masked { composes: page; } diff --git a/packages/venia-concept/src/components/OnlineIndicator/index.js b/packages/venia-concept/src/components/OnlineIndicator/index.js new file mode 100644 index 0000000000..5a0a7d60d3 --- /dev/null +++ b/packages/venia-concept/src/components/OnlineIndicator/index.js @@ -0,0 +1 @@ +export { default } from './onlineIndicator'; diff --git a/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.css b/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.css new file mode 100644 index 0000000000..cbbc0ba36e --- /dev/null +++ b/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.css @@ -0,0 +1,67 @@ +.root { + align-items: center; + display: inline-flex; + font-size: 0.9em; + height: 0px; + justify-content: flex-start; + margin-bottom: 0.3em; + overflow: hidden; + padding: 0 2.4rem; + position: sticky; + top: 0; + width: 100%; +} + +.offline { + composes: root; + background-color: rgb(var(--venia-warning-light)); + animation-name: slidein; + animation-duration: 244ms; + animation-fill-mode: forwards; +} + +.online { + composes: root; + background-color: rgb(var(--venia-teal-light)); + animation-name: slideinandhide; + animation-duration: 3s; + animation-fill-mode: forwards; +} + +.offline span { + color: rgb(var(--venia-warning-dark)); +} + +.online span { + color: rgb(var(--venia-teal)); +} + +.root p { + margin-left: 1em; + line-height: 1.3em; +} + +@keyframes slidein { + from { + height: 0; + } + to { + height: 3rem; + } +} + +@keyframes slideinandhide { + 0% { + height: 3rem; + opacity: 0.5; + } + 10% { + opacity: 1; + } + 80% { + height: 3rem; + } + 100% { + height: 0; + } +} diff --git a/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.js b/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.js new file mode 100644 index 0000000000..e0fe9506b4 --- /dev/null +++ b/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.js @@ -0,0 +1,33 @@ +import React, { Component } from 'react'; +import Icon from 'src/components/Icon'; +import PropTypes from 'prop-types'; + +import classify from 'src/classify'; +import defaultClasses from './onlineIndicator.css'; + +class OnlineIndicator extends Component { + static propTypes = { + classes: PropTypes.shape({ + root: PropTypes.string + }), + isOnline: PropTypes.bool + }; + + render() { + const { isOnline, classes } = this.props; + + return !isOnline ? ( +
+ +

You are offline. Some features may be unavailable.

+
+ ) : ( +
+ +

You are online.

+
+ ); + } +} + +export default classify(defaultClasses)(OnlineIndicator); diff --git a/packages/venia-concept/src/index.css b/packages/venia-concept/src/index.css index 7f9d3a19f5..9123492462 100644 --- a/packages/venia-concept/src/index.css +++ b/packages/venia-concept/src/index.css @@ -9,10 +9,13 @@ html { --venia-grey: 246, 246, 246; --venia-teal: 0, 134, 138; --venia-teal-alt: 224, 240, 241; + --venia-teal-light: 212, 246, 241; --venia-text: 33, 33, 33; --venia-text-alt: 117, 117, 117; --venia-text-hint: 158, 158, 158; --venia-text-spot: 255, 99, 51; + --venia-warning-dark: 249, 93, 94; + --venia-warning-light: 254, 229, 232; } html { diff --git a/packages/venia-concept/src/index.js b/packages/venia-concept/src/index.js index f53d7c1578..dc8cb1e348 100644 --- a/packages/venia-concept/src/index.js +++ b/packages/venia-concept/src/index.js @@ -1,6 +1,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { ApolloClient } from 'apollo-client'; +import { persistCache } from 'apollo-cache-persist'; import { ApolloProvider } from 'react-apollo'; import { setContext } from 'apollo-link-context'; import { createHttpLink } from 'apollo-link-http'; @@ -10,6 +11,7 @@ import { Router, Util } from '@magento/peregrine'; import store from 'src/store'; // import { getUserDetails } from 'src/actions/user'; +import app from 'src/actions/app'; import App from 'src/components/App'; import './index.css'; // store.dispatch(getUserDetails()); @@ -36,9 +38,16 @@ const authLink = setContext((_, { headers }) => { }; }); +const cache = new InMemoryCache(); + +persistCache({ + cache, + storage: window.localStorage +}); + const apolloClient = new ApolloClient({ link: authLink.concat(httpLink), - cache: new InMemoryCache() + cache: cache }); ReactDOM.render( @@ -64,3 +73,10 @@ if (process.env.SERVICE_WORKER && 'serviceWorker' in navigator) { }); }); } + +window.addEventListener('online', () => { + store.dispatch(app.setOnline()); +}); +window.addEventListener('offline', () => { + store.dispatch(app.setOffline()); +}); diff --git a/packages/venia-concept/src/reducers/app.js b/packages/venia-concept/src/reducers/app.js index e657da621d..55da7c89d6 100644 --- a/packages/venia-concept/src/reducers/app.js +++ b/packages/venia-concept/src/reducers/app.js @@ -6,6 +6,8 @@ export const name = 'app'; const initialState = { drawer: null, + hasBeenOffline: !navigator.onLine, + isOnline: navigator.onLine, overlay: false, pending: {} }; @@ -17,6 +19,19 @@ const reducerMap = { drawer: payload, overlay: !!payload }; + }, + [actions.setOnline]: state => { + return { + ...state, + isOnline: true + }; + }, + [actions.setOffline]: state => { + return { + ...state, + isOnline: false, + hasBeenOffline: true + }; } }; diff --git a/packages/venia-concept/src/sw.js b/packages/venia-concept/src/sw.js new file mode 100644 index 0000000000..8d26e1a92a --- /dev/null +++ b/packages/venia-concept/src/sw.js @@ -0,0 +1,48 @@ +const thirtyDays = 30 * 24 * 60 * 60; +workbox.skipWaiting(); +workbox.clientsClaim(); + +workbox.routing.registerRoute('/', workbox.strategies.staleWhileRevalidate()); + +workbox.routing.registerRoute( + new RegExp('\\.html$'), + workbox.strategies.networkFirst() +); + +workbox.routing.registerRoute( + new RegExp('/.\\.js$'), + workbox.strategies.staleWhileRevalidate() +); + +workbox.routing.registerRoute( + /\/media\/catalog.*\.(?:png|gif|jpg|jpeg|svg)$/, + workbox.strategies.staleWhileRevalidate({ + cacheName: 'catalog', + plugins: [ + new workbox.expiration.Plugin({ + maxEntries: 60, + maxAgeSeconds: thirtyDays // 30 Days + }) + ] + }) +); + +workbox.routing.registerRoute( + /\.(?:png|gif|jpg|jpeg|svg)$/, + workbox.strategies.cacheFirst({ + cacheName: 'images', + plugins: [ + new workbox.expiration.Plugin({ + maxEntries: 60, + maxAgeSeconds: thirtyDays // 30 Days + }) + ] + }) +); + +workbox.precaching.precacheAndRoute(self.__precacheManifest || []); + +// This "catch" handler is triggered when any of the other routes fail to +// generate a response. + +// TODO: Add fallbacks diff --git a/packages/venia-concept/static/favicon.ico b/packages/venia-concept/static/favicon.ico new file mode 100644 index 0000000000..5b4f3f947f Binary files /dev/null and b/packages/venia-concept/static/favicon.ico differ diff --git a/packages/venia-concept/static/icons/venia_circle_144.png b/packages/venia-concept/static/icons/venia_circle_144.png new file mode 100644 index 0000000000..6db6f53ba8 Binary files /dev/null and b/packages/venia-concept/static/icons/venia_circle_144.png differ diff --git a/packages/venia-concept/static/icons/venia_circle_192.png b/packages/venia-concept/static/icons/venia_circle_192.png new file mode 100644 index 0000000000..4a0c3c3d20 Binary files /dev/null and b/packages/venia-concept/static/icons/venia_circle_192.png differ diff --git a/packages/venia-concept/static/icons/venia_circle_512.png b/packages/venia-concept/static/icons/venia_circle_512.png new file mode 100644 index 0000000000..cf2c8e483c Binary files /dev/null and b/packages/venia-concept/static/icons/venia_circle_512.png differ diff --git a/packages/venia-concept/static/icons/venia_square_144.png b/packages/venia-concept/static/icons/venia_square_144.png new file mode 100644 index 0000000000..db7e1c1ac9 Binary files /dev/null and b/packages/venia-concept/static/icons/venia_square_144.png differ diff --git a/packages/venia-concept/static/icons/venia_square_192.png b/packages/venia-concept/static/icons/venia_square_192.png new file mode 100644 index 0000000000..6cfe4069bc Binary files /dev/null and b/packages/venia-concept/static/icons/venia_square_192.png differ diff --git a/packages/venia-concept/static/icons/venia_square_512.png b/packages/venia-concept/static/icons/venia_square_512.png new file mode 100644 index 0000000000..9c0ea7d006 Binary files /dev/null and b/packages/venia-concept/static/icons/venia_square_512.png differ diff --git a/packages/venia-concept/static/manifest.json b/packages/venia-concept/static/manifest.json new file mode 100644 index 0000000000..dce2ee7acc --- /dev/null +++ b/packages/venia-concept/static/manifest.json @@ -0,0 +1,23 @@ +{ + "name": "Venia", + "short_name": "Venia", + "start_url": "/", + "theme_color": "#ff6334", + "display": "standalone", + "background_color": "#fff", + "description": "Shop the look", + "icons": [{ + "src": "/icons/venia_circle_144.png", + "sizes": "144x144", + "type": "image/png" + },{ + "src": "/icons/venia_circle_192.png", + "sizes": "192x192", + "type": "image/png" + },{ + "src": "/icons/venia_circle_512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} diff --git a/packages/venia-concept/media/veniaClosed.png b/packages/venia-concept/static/veniaClosed.png similarity index 100% rename from packages/venia-concept/media/veniaClosed.png rename to packages/venia-concept/static/veniaClosed.png diff --git a/packages/venia-concept/templates/doctype-and-head-start.mst b/packages/venia-concept/templates/doctype-and-head-start.mst index efa899076b..e94d6d7d47 100644 --- a/packages/venia-concept/templates/doctype-and-head-start.mst +++ b/packages/venia-concept/templates/doctype-and-head-start.mst @@ -4,3 +4,5 @@ + + diff --git a/packages/venia-concept/venia-upward.yml b/packages/venia-concept/venia-upward.yml index 535d0c8cd9..cf0b1799f2 100644 --- a/packages/venia-concept/venia-upward.yml +++ b/packages/venia-concept/venia-upward.yml @@ -8,8 +8,8 @@ response: pattern: '^/(graphql|rest|media/)' use: proxy - matches: request.url.pathname - pattern: '^/(favicon.ico|veniaClosed.png)' - use: media + pattern: '^/(icons/.+|favicon.ico$|manifest.json$|veniaClosed.png$)' + use: static - matches: urlKey pattern: '.' use: appShell @@ -111,6 +111,6 @@ bundles: directory: inline: './dist' -media: +static: directory: - inline: './media' + inline: './static' diff --git a/packages/venia-concept/webpack.config.js b/packages/venia-concept/webpack.config.js index d5f942baa6..cde16bb200 100644 --- a/packages/venia-concept/webpack.config.js +++ b/packages/venia-concept/webpack.config.js @@ -17,6 +17,8 @@ const TerserPlugin = require('terser-webpack-plugin'); const configureBabel = require('./babel.config.js'); const themePaths = { + images: path.resolve(__dirname, 'images'), + templates: path.resolve(__dirname, 'templates'), src: path.resolve(__dirname, 'src'), output: path.resolve(__dirname, 'dist') }; @@ -133,7 +135,13 @@ module.exports = async function(env) { env: { mode }, enableServiceWorkerDebugging, serviceWorkerFileName, - paths: themePaths + paths: themePaths, + injectManifest: true, + injectManifestConfig: { + include: [/\.js$/], + swSrc: './src/sw.js', + swDest: 'sw.js' + } }) ], optimization: { @@ -153,7 +161,6 @@ module.exports = async function(env) { }; if (mode === 'development') { config.devtool = 'eval-source-map'; - const devServerConfig = { publicPath: config.output.publicPath, graphqlPlayground: {