Use precache manifest generated by webpack plugin (#2464)
This commit is contained in:
		
							parent
							
								
									de463e1f99
								
							
						
					
					
						commit
						8b139f08da
					
				@ -1,6 +1,6 @@
 | 
			
		||||
const { withPlausibleProxy } = require('next-plausible')
 | 
			
		||||
const { InjectManifest } = require('workbox-webpack-plugin')
 | 
			
		||||
const { generatePrecacheManifest } = require('./sw/build.js')
 | 
			
		||||
const CopyPlugin = require('copy-webpack-plugin')
 | 
			
		||||
const webpack = require('webpack')
 | 
			
		||||
 | 
			
		||||
let isProd = process.env.NODE_ENV === 'production'
 | 
			
		||||
@ -215,15 +215,24 @@ module.exports = withPlausibleProxy()({
 | 
			
		||||
  },
 | 
			
		||||
  webpack: (config, { isServer, dev, defaultLoaders }) => {
 | 
			
		||||
    if (isServer) {
 | 
			
		||||
      generatePrecacheManifest()
 | 
			
		||||
      const workboxPlugin = new InjectManifest({
 | 
			
		||||
        // ignore the precached manifest which includes the webpack assets
 | 
			
		||||
        // since they are not useful to us
 | 
			
		||||
        exclude: [/.*/],
 | 
			
		||||
        // by default, webpack saves service worker at .next/server/
 | 
			
		||||
        include: [/\/(icons|maskable|splash)\//, /\.(webp|ttf|woff|woff2)$/],
 | 
			
		||||
        swDest: '../../public/sw.js',
 | 
			
		||||
        swSrc: './sw/index.js',
 | 
			
		||||
        webpackCompilationPlugins: [
 | 
			
		||||
          // we want to precache these static assets so we copy them to include them in the webpack pipeline
 | 
			
		||||
          // so InjectManifest can inject them into the service worker manifest
 | 
			
		||||
          new CopyPlugin({
 | 
			
		||||
            patterns: [
 | 
			
		||||
              { from: 'public/icons', to: '../icons' },
 | 
			
		||||
              { from: 'public/maskable', to: '../maskable' },
 | 
			
		||||
              { from: 'public/splash', to: '../splash' },
 | 
			
		||||
              { from: 'public/waiting.webp', to: '../waiting.webp' },
 | 
			
		||||
              { from: 'public/Lightningvolt-xoqm.ttf', to: '../Lightningvolt-xoqm.ttf' },
 | 
			
		||||
              { from: 'public/Lightningvolt-xoqm.woff', to: '../Lightningvolt-xoqm.woff' },
 | 
			
		||||
              { from: 'public/Lightningvolt-xoqm.woff2', to: '../Lightningvolt-xoqm.woff2' }
 | 
			
		||||
            ]
 | 
			
		||||
          }),
 | 
			
		||||
          // this is need to allow the service worker to access these environment variables
 | 
			
		||||
          // from lib/constants.js
 | 
			
		||||
          new webpack.DefinePlugin({
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										124
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										124
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@ -114,6 +114,7 @@
 | 
			
		||||
      },
 | 
			
		||||
      "devDependencies": {
 | 
			
		||||
        "@next/eslint-plugin-next": "^14.2.15",
 | 
			
		||||
        "copy-webpack-plugin": "^13.0.1",
 | 
			
		||||
        "eslint": "^9.12.0",
 | 
			
		||||
        "jest": "^29.7.0",
 | 
			
		||||
        "standard": "^17.1.2"
 | 
			
		||||
@ -6508,6 +6509,24 @@
 | 
			
		||||
        "url": "https://github.com/sponsors/epoberezkin"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/ajv-formats": {
 | 
			
		||||
      "version": "2.1.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
 | 
			
		||||
      "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "ajv": "^8.0.0"
 | 
			
		||||
      },
 | 
			
		||||
      "peerDependencies": {
 | 
			
		||||
        "ajv": "^8.0.0"
 | 
			
		||||
      },
 | 
			
		||||
      "peerDependenciesMeta": {
 | 
			
		||||
        "ajv": {
 | 
			
		||||
          "optional": true
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/ajv-keywords": {
 | 
			
		||||
      "version": "3.5.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
 | 
			
		||||
@ -8048,6 +8067,63 @@
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
 | 
			
		||||
      "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/copy-webpack-plugin": {
 | 
			
		||||
      "version": "13.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-13.0.1.tgz",
 | 
			
		||||
      "integrity": "sha512-J+YV3WfhY6W/Xf9h+J1znYuqTye2xkBUIGyTPWuBAT27qajBa5mR4f8WBmfDY3YjRftT2kqZZiLi1qf0H+UOFw==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "glob-parent": "^6.0.1",
 | 
			
		||||
        "normalize-path": "^3.0.0",
 | 
			
		||||
        "schema-utils": "^4.2.0",
 | 
			
		||||
        "serialize-javascript": "^6.0.2",
 | 
			
		||||
        "tinyglobby": "^0.2.12"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">= 18.12.0"
 | 
			
		||||
      },
 | 
			
		||||
      "funding": {
 | 
			
		||||
        "type": "opencollective",
 | 
			
		||||
        "url": "https://opencollective.com/webpack"
 | 
			
		||||
      },
 | 
			
		||||
      "peerDependencies": {
 | 
			
		||||
        "webpack": "^5.1.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/copy-webpack-plugin/node_modules/ajv-keywords": {
 | 
			
		||||
      "version": "5.1.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
 | 
			
		||||
      "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "fast-deep-equal": "^3.1.3"
 | 
			
		||||
      },
 | 
			
		||||
      "peerDependencies": {
 | 
			
		||||
        "ajv": "^8.8.2"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/copy-webpack-plugin/node_modules/schema-utils": {
 | 
			
		||||
      "version": "4.3.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz",
 | 
			
		||||
      "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@types/json-schema": "^7.0.9",
 | 
			
		||||
        "ajv": "^8.9.0",
 | 
			
		||||
        "ajv-formats": "^2.1.1",
 | 
			
		||||
        "ajv-keywords": "^5.1.0"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">= 10.13.0"
 | 
			
		||||
      },
 | 
			
		||||
      "funding": {
 | 
			
		||||
        "type": "opencollective",
 | 
			
		||||
        "url": "https://opencollective.com/webpack"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/core-js-compat": {
 | 
			
		||||
      "version": "3.38.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz",
 | 
			
		||||
@ -10234,6 +10310,24 @@
 | 
			
		||||
        "bser": "2.1.1"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/fdir": {
 | 
			
		||||
      "version": "6.5.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
 | 
			
		||||
      "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12.0.0"
 | 
			
		||||
      },
 | 
			
		||||
      "peerDependencies": {
 | 
			
		||||
        "picomatch": "^3 || ^4"
 | 
			
		||||
      },
 | 
			
		||||
      "peerDependenciesMeta": {
 | 
			
		||||
        "picomatch": {
 | 
			
		||||
          "optional": true
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/file-entry-cache": {
 | 
			
		||||
      "version": "8.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
 | 
			
		||||
@ -19677,6 +19771,36 @@
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
 | 
			
		||||
      "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/tinyglobby": {
 | 
			
		||||
      "version": "0.2.14",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
 | 
			
		||||
      "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "fdir": "^6.4.4",
 | 
			
		||||
        "picomatch": "^4.0.2"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12.0.0"
 | 
			
		||||
      },
 | 
			
		||||
      "funding": {
 | 
			
		||||
        "url": "https://github.com/sponsors/SuperchupuDev"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/tinyglobby/node_modules/picomatch": {
 | 
			
		||||
      "version": "4.0.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
 | 
			
		||||
      "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      },
 | 
			
		||||
      "funding": {
 | 
			
		||||
        "url": "https://github.com/sponsors/jonschlinkert"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/tldts": {
 | 
			
		||||
      "version": "6.1.51",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.51.tgz",
 | 
			
		||||
 | 
			
		||||
@ -130,6 +130,7 @@
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@next/eslint-plugin-next": "^14.2.15",
 | 
			
		||||
    "copy-webpack-plugin": "^13.0.1",
 | 
			
		||||
    "eslint": "^9.12.0",
 | 
			
		||||
    "jest": "^29.7.0",
 | 
			
		||||
    "standard": "^17.1.2"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										61
									
								
								sw/build.js
									
									
									
									
									
								
							
							
						
						
									
										61
									
								
								sw/build.js
									
									
									
									
									
								
							@ -1,61 +0,0 @@
 | 
			
		||||
const { createHash } = require('crypto')
 | 
			
		||||
const { readdirSync, readFileSync, statSync, writeFileSync } = require('fs')
 | 
			
		||||
const { join } = require('path')
 | 
			
		||||
 | 
			
		||||
const getRevision = filePath => createHash('md5').update(readFileSync(filePath)).digest('hex')
 | 
			
		||||
const walkSync = dir => readdirSync(dir, { withFileTypes: true }).flatMap(file =>
 | 
			
		||||
  file.isDirectory() ? walkSync(join(dir, file.name)) : join(dir, file.name))
 | 
			
		||||
 | 
			
		||||
function formatBytes (bytes, decimals = 2) {
 | 
			
		||||
  if (bytes === 0) {
 | 
			
		||||
    return '0 B'
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const k = 1024
 | 
			
		||||
  const sizes = ['B', 'KB', 'MB']
 | 
			
		||||
 | 
			
		||||
  const i = Math.floor(Math.log(bytes) / Math.log(k))
 | 
			
		||||
  const formattedSize = parseFloat((bytes / Math.pow(k, i)).toFixed(decimals))
 | 
			
		||||
 | 
			
		||||
  return `${formattedSize} ${sizes[i]}`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function generatePrecacheManifest () {
 | 
			
		||||
  const manifest = []
 | 
			
		||||
  let size = 0
 | 
			
		||||
 | 
			
		||||
  const addToManifest = (filePath, url, s) => {
 | 
			
		||||
    const revision = getRevision(filePath)
 | 
			
		||||
    manifest.push({ url, revision })
 | 
			
		||||
    size += s
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const staticDir = join(__dirname, '../public')
 | 
			
		||||
  const staticFiles = walkSync(staticDir)
 | 
			
		||||
  const staticMatch = f => [/\.(webp|jpe?g|ico|png|ttf|woff|woff2)$/].some(m => m.test(f))
 | 
			
		||||
  staticFiles.filter(staticMatch).forEach(file => {
 | 
			
		||||
    const stats = statSync(file)
 | 
			
		||||
    addToManifest(file, file.slice(staticDir.length), stats.size)
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  const pagesDir = join(__dirname, '../pages')
 | 
			
		||||
  const precacheURLs = ['/offline']
 | 
			
		||||
  const pagesFiles = walkSync(pagesDir)
 | 
			
		||||
  const fileToUrl = f => f.slice(pagesDir.length).replace(/\.js$/, '')
 | 
			
		||||
  const pageMatch = f => precacheURLs.some(url => fileToUrl(f) === url)
 | 
			
		||||
  pagesFiles.filter(pageMatch).forEach(file => {
 | 
			
		||||
    const stats = statSync(file)
 | 
			
		||||
    // This is not ideal since dependencies of the pages may have changed
 | 
			
		||||
    // but we would still generate the same revision ...
 | 
			
		||||
    // The ideal solution would be to create a revision from the file generated by webpack
 | 
			
		||||
    // in .next/server/pages but the file may not exist yet when we run this script
 | 
			
		||||
    addToManifest(file, fileToUrl(file), stats.size)
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  const output = 'sw/precache-manifest.json'
 | 
			
		||||
  writeFileSync(output, JSON.stringify(manifest, null, 2))
 | 
			
		||||
 | 
			
		||||
  console.log(`Created precache manifest at ${output}. Cache will include ${manifest.length} URLs with a size of ${formatBytes(size)}.`)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = { generatePrecacheManifest }
 | 
			
		||||
							
								
								
									
										19
									
								
								sw/index.js
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								sw/index.js
									
									
									
									
									
								
							@ -5,7 +5,6 @@ import { setDefaultHandler } from 'workbox-routing'
 | 
			
		||||
import { NetworkOnly } from 'workbox-strategies'
 | 
			
		||||
import { enable } from 'workbox-navigation-preload'
 | 
			
		||||
 | 
			
		||||
import manifest from './precache-manifest.json'
 | 
			
		||||
import ServiceWorkerStorage from 'serviceworker-storage'
 | 
			
		||||
import { CLEAR_NOTIFICATIONS, DELETE_SUBSCRIPTION, STORE_SUBSCRIPTION } from '@/components/serviceworker'
 | 
			
		||||
 | 
			
		||||
@ -19,11 +18,19 @@ self.__WB_DISABLE_DEV_LOGS = true
 | 
			
		||||
// https://developer.chrome.com/docs/workbox/modules/workbox-navigation-preload/
 | 
			
		||||
enable()
 | 
			
		||||
 | 
			
		||||
// ignore precache manifest generated by InjectManifest
 | 
			
		||||
// they statically check for the presence of this variable
 | 
			
		||||
console.log(self.__WB_MANIFEST)
 | 
			
		||||
// precache the manifest we generated ourselves
 | 
			
		||||
precacheAndRoute(manifest)
 | 
			
		||||
precacheAndRoute(self.__WB_MANIFEST,
 | 
			
		||||
  {
 | 
			
		||||
    // this returns url fallbacks, we map CDN urls to the origin because that's how they are cached
 | 
			
		||||
    // source: https://github.com/GoogleChrome/workbox/blob/e26d8d7507f9412ba029922f3d9920e68710f2cf/packages/workbox-precaching/src/utils/generateURLVariations.ts#L54-L59
 | 
			
		||||
    urlManipulation: ({ url }) => {
 | 
			
		||||
      if (url.hostname === 'a.stacker.news') {
 | 
			
		||||
        const newUrl = new URL(url.pathname, 'https://stacker.news')
 | 
			
		||||
        return [newUrl]
 | 
			
		||||
      }
 | 
			
		||||
      return [url]
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// immediately replace existing service workers with this one
 | 
			
		||||
// (no wait until this one becomes active)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user