|
| 1 | +const { copy, remove, readFile, writeFile, readdir } = require('fs-extra'); |
| 2 | +const { resolve, join } = require('path'); |
| 3 | +const { build } = require('./build'); |
| 4 | +const { openInBrowser } = require('@parcel/utils'); |
| 5 | +const workbox = require('workbox-build'); |
| 6 | + |
| 7 | +const hashRegExp = /\.[0-9a-fA-F]{8}\./; |
| 8 | + |
| 9 | +const removeRevisionManifestTransform = async (manifestEntries) => ({ |
| 10 | + manifest: manifestEntries.map((entry) => |
| 11 | + hashRegExp.test(entry.url) ? { ...entry, revision: null } : entry, |
| 12 | + ), |
| 13 | +}); |
| 14 | + |
| 15 | +const workboxConfig = { |
| 16 | + globDirectory: 'dist/client', |
| 17 | + globPatterns: ['**/*.{html,js,css,png,svg,jpg,gif,json,ico}'], |
| 18 | + swDest: 'dist/client/sw.js', |
| 19 | + clientsClaim: true, |
| 20 | + skipWaiting: true, |
| 21 | + manifestTransforms: [removeRevisionManifestTransform], |
| 22 | + ignoreURLParametersMatching: [/.*/], |
| 23 | +}; |
| 24 | + |
| 25 | +async function fixWebManifest({ dest }) { |
| 26 | + // browsers can't detect service-worker updates if the manifest changes name |
| 27 | + const htmlContent = await readFile(join(dest, 'index.html'), 'utf8'); |
| 28 | + const [, manifestFilename] = htmlContent.match( |
| 29 | + /<link rel="manifest" href="\/?(site\.[0-9a-fA-F]{8}\.webmanifest)">/, |
| 30 | + ); |
| 31 | + |
| 32 | + // replace site.e5465fc8.webmanifest with manifest.json |
| 33 | + const replacer = new RegExp(manifestFilename, 'g'); |
| 34 | + const newContent = htmlContent.replace(replacer, 'manifest.json', 'utf8'); |
| 35 | + |
| 36 | + // fix image paths in manifest.json |
| 37 | + const iconSrcHashTable = (await readdir(dest)) |
| 38 | + .filter((file) => /\.png/.test(file)) |
| 39 | + .reduce((index, file) => { |
| 40 | + index[file.replace(hashRegExp, '.')] = file; |
| 41 | + return index; |
| 42 | + }, {}); |
| 43 | + |
| 44 | + const manifest = JSON.parse( |
| 45 | + await readFile(join(dest, manifestFilename), 'utf8'), |
| 46 | + ); |
| 47 | + |
| 48 | + manifest.icons.forEach((icon) => { |
| 49 | + icon.src = iconSrcHashTable[icon.src]; |
| 50 | + }); |
| 51 | + |
| 52 | + await Promise.all([ |
| 53 | + // rename manifest file from site.e5465fc8.webmanifest to manifest.json |
| 54 | + remove(join(dest, manifestFilename)), |
| 55 | + // write updated html content referring to the renamed manifest |
| 56 | + writeFile(join(dest, 'index.html'), newContent), |
| 57 | + // write the fixed manifest json |
| 58 | + writeFile( |
| 59 | + join(dest, 'manifest.json'), |
| 60 | + JSON.stringify(manifest, '', ' '), |
| 61 | + 'utf8', |
| 62 | + ), |
| 63 | + ]); |
| 64 | +} |
| 65 | + |
| 66 | +async function main() { |
| 67 | + const dest = resolve('dist/client'); |
| 68 | + await remove(dest); |
| 69 | + |
| 70 | + const entries = ['src/index.html', 'src/embed.js']; |
| 71 | + |
| 72 | + if (process.env.NODE_ENV === 'development') { |
| 73 | + entries.push('src/embed.html'); |
| 74 | + } |
| 75 | + |
| 76 | + const parcel = await build({ |
| 77 | + entries, |
| 78 | + dest: 'dist/client', |
| 79 | + port: 1234, |
| 80 | + }); |
| 81 | + |
| 82 | + await fixWebManifest({ dest }); |
| 83 | + |
| 84 | + await workbox.generateSW(workboxConfig); |
| 85 | + |
| 86 | + await Promise.all([ |
| 87 | + copy('_redirects', join(dest, '_redirects')), |
| 88 | + copy('public/favicon.ico', join(dest, 'favicon.ico')), |
| 89 | + copy('public/icon.png', join(dest, 'icon.png')), |
| 90 | + copy('.well-known', join(dest, '.well-known')), |
| 91 | + ]); |
| 92 | + |
| 93 | + if (parcel.watching) { |
| 94 | + openInBrowser(`http://localhost:${parcel.config.serve.port}`); |
| 95 | + } |
| 96 | +} |
| 97 | + |
| 98 | +main(); |
0 commit comments