Skip to content

Commit

Permalink
Enable service worker for legacy build (#21177)
Browse files Browse the repository at this point in the history
  • Loading branch information
steverep authored Jul 19, 2024
1 parent 729a12a commit 82d84de
Show file tree
Hide file tree
Showing 13 changed files with 211 additions and 114 deletions.
6 changes: 6 additions & 0 deletions .browserslistrc
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,9 @@ not dead
unreleased versions
last 7 years
> 0.05% and supports websockets

[legacy-sw]
# Same as legacy plus supports service workers
unreleased versions
last 7 years
> 0.05% and supports websockets and supports serviceworkers
55 changes: 55 additions & 0 deletions .yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
diff --git a/build/inject-manifest.js b/build/inject-manifest.js
index 60e3d2bb51c11a19fbbedbad65e101082ec41c36..fed6026630f43f86e25446383982cf6fb694313b 100644
--- a/build/inject-manifest.js
+++ b/build/inject-manifest.js
@@ -104,7 +104,7 @@ async function injectManifest(config) {
replaceString: manifestString,
searchString: options.injectionPoint,
});
- filesToWrite[options.swDest] = source;
+ filesToWrite[options.swDest] = source.replace(url, encodeURI(upath_1.default.basename(destPath)));
filesToWrite[destPath] = map;
}
else {
diff --git a/build/lib/translate-url-to-sourcemap-paths.js b/build/lib/translate-url-to-sourcemap-paths.js
index 3220c5474eeac6e8a56ca9b2ac2bd9be48529e43..5f003879a904d4840529a42dd056d288fd213771 100644
--- a/build/lib/translate-url-to-sourcemap-paths.js
+++ b/build/lib/translate-url-to-sourcemap-paths.js
@@ -22,7 +22,7 @@ function translateURLToSourcemapPaths(url, swSrc, swDest) {
const possibleSrcPath = upath_1.default.resolve(upath_1.default.dirname(swSrc), url);
if (fs_extra_1.default.existsSync(possibleSrcPath)) {
srcPath = possibleSrcPath;
- destPath = upath_1.default.resolve(upath_1.default.dirname(swDest), url);
+ destPath = `${swDest}.map`;
}
else {
warning = `${errors_1.errors['cant-find-sourcemap']} ${possibleSrcPath}`;
diff --git a/src/inject-manifest.ts b/src/inject-manifest.ts
index 8795ddcaa77aea7b0356417e4bc4b19e2b3f860c..fcdc68342d9ac53936c9ed40a9ccfc2f5070cad3 100644
--- a/src/inject-manifest.ts
+++ b/src/inject-manifest.ts
@@ -129,7 +129,10 @@ export async function injectManifest(
searchString: options.injectionPoint!,
});

- filesToWrite[options.swDest] = source;
+ filesToWrite[options.swDest] = source.replace(
+ url!,
+ encodeURI(upath.basename(destPath)),
+ );
filesToWrite[destPath] = map;
} else {
// If there's no sourcemap associated with swSrc, a simple string
diff --git a/src/lib/translate-url-to-sourcemap-paths.ts b/src/lib/translate-url-to-sourcemap-paths.ts
index 072eac40d4ef5d095a01cb7f7e392a9e034853bd..f0bbe69e88ef3a415de18a7e9cb264daea273d71 100644
--- a/src/lib/translate-url-to-sourcemap-paths.ts
+++ b/src/lib/translate-url-to-sourcemap-paths.ts
@@ -28,7 +28,7 @@ export function translateURLToSourcemapPaths(
const possibleSrcPath = upath.resolve(upath.dirname(swSrc), url);
if (fse.existsSync(possibleSrcPath)) {
srcPath = possibleSrcPath;
- destPath = upath.resolve(upath.dirname(swDest), url);
+ destPath = `${swDest}.map`;
} else {
warning = `${errors['cant-find-sourcemap']} ${possibleSrcPath}`;
}
19 changes: 15 additions & 4 deletions build-scripts/bundle.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) =>

module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
__DEV__: !isProdBuild,
__BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"),
__BUILD__: JSON.stringify(latestBuild ? "modern" : "legacy"),
__VERSION__: JSON.stringify(env.version()),
__DEMO__: false,
__SUPERVISOR__: false,
Expand Down Expand Up @@ -79,15 +79,20 @@ module.exports.terserOptions = ({ latestBuild, isTestBuild }) => ({
sourceMap: !isTestBuild,
});

module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({
module.exports.babelOptions = ({
latestBuild,
isProdBuild,
isTestBuild,
sw,
}) => ({
babelrc: false,
compact: false,
assumptions: {
privateFieldsAsProperties: true,
setPublicClassFields: true,
setSpreadProperties: true,
},
browserslistEnv: latestBuild ? "modern" : "legacy",
browserslistEnv: latestBuild ? "modern" : `legacy${sw ? "-sw" : ""}`,
presets: [
[
"@babel/preset-env",
Expand Down Expand Up @@ -215,7 +220,13 @@ module.exports.config = {
return {
name: "frontend" + nameSuffix(latestBuild),
entry: {
service_worker: "./src/entrypoints/service_worker.ts",
"service-worker":
!env.useRollup() && !latestBuild
? {
import: "./src/entrypoints/service-worker.ts",
layer: "sw",
}
: "./src/entrypoints/service-worker.ts",
app: "./src/entrypoints/app.ts",
authorize: "./src/entrypoints/authorize.ts",
onboarding: "./src/entrypoints/onboarding.ts",
Expand Down
144 changes: 66 additions & 78 deletions build-scripts/gulp/service-worker.js
Original file line number Diff line number Diff line change
@@ -1,93 +1,81 @@
// Generate service worker.
// Based on manifest, create a file with the content as service_worker.js
// Generate service workers

import fs from "fs-extra";
import { deleteAsync } from "del";
import gulp from "gulp";
import path from "path";
import sourceMapUrl from "source-map-url";
import workboxBuild from "workbox-build";
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { join, relative } from "node:path";
import { injectManifest } from "workbox-build";
import paths from "../paths.cjs";

const swDest = path.resolve(paths.app_output_root, "service_worker.js");
const SW_MAP = {
[paths.app_output_latest]: "modern",
[paths.app_output_es5]: "legacy",
};

const writeSW = (content) => fs.outputFileSync(swDest, content.trim() + "\n");

gulp.task("gen-service-worker-app-dev", (done) => {
writeSW(
`
const SW_DEV =
`
console.debug('Service worker disabled in development');
self.addEventListener('install', (event) => {
// This will activate the dev service worker,
// removing any prod service worker the dev might have running
self.skipWaiting();
});
`
);
done();
});
`.trim() + "\n";

gulp.task("gen-service-worker-app-prod", async () => {
// Read bundled source file
const bundleManifestLatest = fs.readJsonSync(
path.resolve(paths.app_output_latest, "manifest.json")
gulp.task("gen-service-worker-app-dev", async () => {
await mkdir(paths.app_output_root, { recursive: true });
await Promise.all(
Object.values(SW_MAP).map((build) =>
writeFile(join(paths.app_output_root, `sw-${build}.js`), SW_DEV, {
encoding: "utf-8",
})
)
);
let serviceWorkerContent = fs.readFileSync(
paths.app_output_root + bundleManifestLatest["service_worker.js"],
"utf-8"
);

// Delete old file from frontend_latest so manifest won't pick it up
fs.removeSync(
paths.app_output_root + bundleManifestLatest["service_worker.js"]
);
fs.removeSync(
paths.app_output_root + bundleManifestLatest["service_worker.js.map"]
);

// Remove ES5
const bundleManifestES5 = fs.readJsonSync(
path.resolve(paths.app_output_es5, "manifest.json")
);
fs.removeSync(paths.app_output_root + bundleManifestES5["service_worker.js"]);
fs.removeSync(
paths.app_output_root + bundleManifestES5["service_worker.js.map"]
);

const workboxManifest = await workboxBuild.getManifest({
// Files that mach this pattern will be considered unique and skip revision check
// ignore JS files + translation files
dontCacheBustURLsMatching: /(frontend_latest\/.+|static\/translations\/.+)/,

globDirectory: paths.app_output_root,
globPatterns: [
"frontend_latest/*.js",
// Cache all English translations because we catch them as fallback
// Using pattern to match hash instead of * to avoid caching en-GB
// 'v' added as valid hash letter because in dev we hash with 'dev'
"static/translations/**/en-+([a-fv0-9]).json",
// Icon shown on splash screen
"static/icons/favicon-192x192.png",
"static/icons/favicon.ico",
// Common fonts
"static/fonts/roboto/Roboto-Light.woff2",
"static/fonts/roboto/Roboto-Medium.woff2",
"static/fonts/roboto/Roboto-Regular.woff2",
"static/fonts/roboto/Roboto-Bold.woff2",
],
});

for (const warning of workboxManifest.warnings) {
console.warn(warning);
}

// remove source map and add WB manifest
serviceWorkerContent = sourceMapUrl.removeFrom(serviceWorkerContent);
serviceWorkerContent = serviceWorkerContent.replace(
"WB_MANIFEST",
JSON.stringify(workboxManifest.manifestEntries)
);

// Write new file to root
fs.writeFileSync(swDest, serviceWorkerContent);
});

gulp.task("gen-service-worker-app-prod", () =>
Promise.all(
Object.entries(SW_MAP).map(async ([outPath, build]) => {
const manifest = JSON.parse(
await readFile(join(outPath, "manifest.json"), "utf-8")
);
const swSrc = join(paths.app_output_root, manifest["service-worker.js"]);
const buildDir = relative(paths.app_output_root, outPath);
const { warnings } = await injectManifest({
swSrc,
swDest: join(paths.app_output_root, `sw-${build}.js`),
injectionPoint: "__WB_MANIFEST__",
// Files that mach this pattern will be considered unique and skip revision check
// ignore JS files + translation files
dontCacheBustURLsMatching: new RegExp(
`(?:${buildDir}/.+|static/translations/.+)`
),
globDirectory: paths.app_output_root,
globPatterns: [
`${buildDir}/*.js`,
// Cache all English translations because we catch them as fallback
// Using pattern to match hash instead of * to avoid caching en-GB
// 'v' added as valid hash letter because in dev we hash with 'dev'
"static/translations/**/en-+([a-fv0-9]).json",
// Icon shown on splash screen
"static/icons/favicon-192x192.png",
"static/icons/favicon.ico",
// Common fonts
"static/fonts/roboto/Roboto-Light.woff2",
"static/fonts/roboto/Roboto-Medium.woff2",
"static/fonts/roboto/Roboto-Regular.woff2",
"static/fonts/roboto/Roboto-Bold.woff2",
],
globIgnores: [`${buildDir}/service-worker*`],
});
if (warnings.length > 0) {
console.warn(
`Problems while injecting ${build} service worker:\n`,
warnings.join("\n")
);
}
await deleteAsync(`${swSrc}?(.map)`);
})
)
);
12 changes: 9 additions & 3 deletions build-scripts/webpack.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,19 @@ const createWebpackConfig = ({
rules: [
{
test: /\.m?js$|\.ts$/,
use: {
use: (info) => ({
loader: "babel-loader",
options: {
...bundle.babelOptions({ latestBuild, isProdBuild, isTestBuild }),
...bundle.babelOptions({
latestBuild,
isProdBuild,
isTestBuild,
sw: info.issuerLayer === "sw",
}),
cacheDirectory: !isProdBuild,
cacheCompression: false,
},
},
}),
resolve: {
fullySpecified: false,
},
Expand Down Expand Up @@ -235,6 +240,7 @@ const createWebpackConfig = ({
),
},
experiments: {
layers: true,
outputModule: true,
},
};
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,6 @@
"rollup-plugin-visualizer": "5.12.0",
"serve-handler": "6.1.5",
"sinon": "18.0.0",
"source-map-url": "0.4.1",
"systemjs": "6.15.1",
"tar": "7.4.0",
"terser-webpack-plugin": "5.3.10",
Expand All @@ -244,7 +243,7 @@
"webpack-manifest-plugin": "5.0.0",
"webpack-stats-plugin": "1.1.3",
"webpackbar": "6.0.1",
"workbox-build": "7.1.1"
"workbox-build": "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch"
},
"_comment": "Polymer 3.2 contained a bug, fixed in https://github.com/Polymer/polymer/pull/5569, add as patch",
"resolutions": {
Expand Down
2 changes: 1 addition & 1 deletion src/entrypoints/custom-panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ function initialize(
);
}

if (__BUILD__ === "es5") {
if (__BUILD__ === "legacy") {
start = start.then(() => window.loadES5Adapter());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,16 @@ import {
StaleWhileRevalidate,
} from "workbox-strategies";

declare const __WB_MANIFEST__: Parameters<typeof precacheAndRoute>[0];

const noFallBackRegEx =
/\/(api|static|auth|frontend_latest|frontend_es5|local)\/.*/;

const initRouting = () => {
precacheAndRoute(
// @ts-ignore
WB_MANIFEST,
{
// Ignore all URL parameters.
ignoreURLParametersMatching: [/.*/],
}
);
precacheAndRoute(__WB_MANIFEST__, {
// Ignore all URL parameters.
ignoreURLParametersMatching: [/.*/],
});

// Cache static content (including translations) on first access.
registerRoute(
Expand Down Expand Up @@ -56,11 +54,8 @@ const initRouting = () => {
// Get api from network.
registerRoute(/\/(api|auth)\/.*/, new NetworkOnly());

// Get manifest, service worker, onboarding from network.
registerRoute(
/\/(service_worker.js|manifest.json|onboarding.html)/,
new NetworkOnly()
);
// Get manifest and onboarding from network.
registerRoute(/\/(?:manifest\.json|onboarding\.html)/, new NetworkOnly());

// For the root "/" we ignore search
registerRoute(
Expand Down
2 changes: 1 addition & 1 deletion src/panels/config/info/ha-config-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ class HaConfigInfo extends LitElement {
)}
</span>
<span class="version">
${JS_VERSION}${JS_TYPE !== "latest" ? ` ⸱ ${JS_TYPE}` : ""}
${JS_VERSION}${JS_TYPE !== "modern" ? ` ⸱ ${JS_TYPE}` : ""}
</span>
</li>
</ul>
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ declare global {
/* eslint-disable no-var, no-redeclare */
var __DEV__: boolean;
var __DEMO__: boolean;
var __BUILD__: "latest" | "es5";
var __BUILD__: "modern" | "legacy";
var __VERSION__: string;
var __STATIC_PATH__: string;
var __BACKWARDS_COMPAT__: boolean;
Expand Down
2 changes: 1 addition & 1 deletion src/util/custom-panel/load-custom-panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const getUrl = (

// if both module and JS provided, base url on frontend build
if (panelConfig.module_url && panelConfig.js_url) {
if (__BUILD__ === "latest") {
if (__BUILD__ === "modern") {
return {
type: "module",
url: panelConfig.module_url,
Expand Down
Loading

0 comments on commit 82d84de

Please sign in to comment.