-
Notifications
You must be signed in to change notification settings - Fork 124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial service worker #140
Changes from 4 commits
717574e
a0b1b4d
4ec00fe
558bcdc
8c7c807
9af11a3
24385be
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/** | ||
* Copyright 2019 Google Inc. All Rights Reserved. | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
export default function constsPlugin(consts) { | ||
return { | ||
name: "consts-plugin", | ||
async resolveId(id) { | ||
if (id !== "consts:") { | ||
return; | ||
} | ||
return id; | ||
}, | ||
load(id) { | ||
if (id !== "consts:") { | ||
return; | ||
} | ||
return Object.entries(consts) | ||
.map( | ||
([key, value]) => `export const ${key} = ${JSON.stringify(value)};` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Couldn’t this just be export ${JSON.stringify(consts)} ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nah, that's invalid syntax. You could do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TIL. I thought when you do const a = 1;
const b = 2;
export {a, b}; that the export is an object literal. It is not :( |
||
) | ||
.join(""); | ||
} | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/** | ||
* Copyright 2018 Google Inc. All Rights Reserved. | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
const resourceListMarker = "___REPLACE_THIS_WITH_RESOURCE_LIST_LATER"; | ||
|
||
export default function resourceList() { | ||
return { | ||
name: "dependencygraph", | ||
resolveId(id) { | ||
if (id !== "resource-list:") { | ||
return null; | ||
} | ||
return id; | ||
}, | ||
load(id) { | ||
if (id !== "resource-list:") { | ||
return null; | ||
} | ||
return `export default ${resourceListMarker};`; | ||
}, | ||
generateBundle(_outputOptions, bundle) { | ||
const resourceListJSON = JSON.stringify(Object.keys(bundle)); | ||
|
||
for (const item of Object.values(bundle)) { | ||
if (!item.code) { | ||
continue; | ||
} | ||
item.code = item.code.replace(resourceListMarker, resourceListJSON); | ||
} | ||
} | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,22 +17,27 @@ import { terser } from "rollup-plugin-terser"; | |
import loadz0r from "rollup-plugin-loadz0r"; | ||
import dependencyGraph from "./dependency-graph-plugin.js"; | ||
import chunkNamePlugin from "./chunk-name-plugin.js"; | ||
import resourceListPlugin from "./resource-list-plugin"; | ||
import postcss from "rollup-plugin-postcss"; | ||
import glsl from "./glsl-plugin.js"; | ||
import cssModuleTypes from "./css-module-types.js"; | ||
import assetPlugin from "./asset-plugin.js"; | ||
import { readFileSync } from "fs"; | ||
import constsPlugin from "./consts-plugin.js"; | ||
|
||
// Delete 'dist' | ||
require("rimraf").sync("dist"); | ||
|
||
export default { | ||
input: "src/bootstrap.ts", | ||
input: { | ||
bootstrap: "src/bootstrap.ts", | ||
sw: "src/sw/index.ts" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is the SW a separate endpoint instead of using the same trick we are using for the Worker? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The worker way gives us a hashed file name 😢 |
||
}, | ||
output: { | ||
dir: "dist", | ||
format: "amd", | ||
sourcemap: true, | ||
entryFileNames: "[name]-[hash].js", | ||
entryFileNames: "[name].js", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't want service worker to have a hash. This also removes the hash from bootstrap, but we always inline it so it's ok. I mentioned this issue to the Rollup folks rollup/rollup#2585 (comment) |
||
chunkFileNames: "[name]-[hash].js" | ||
}, | ||
plugins: [ | ||
|
@@ -47,6 +52,9 @@ export default { | |
return name.replace(/-\w/g, val => val.slice(1).toUpperCase()); | ||
} | ||
}), | ||
constsPlugin({ | ||
version: require("./package.json").version | ||
}), | ||
typescript({ | ||
// Make sure we are using our version of TypeScript. | ||
typescript: require("typescript"), | ||
|
@@ -87,6 +95,7 @@ export default { | |
dependencyGraph({ | ||
propList: ["facadeModuleId", "fileName", "imports", "code", "isAsset"] | ||
}), | ||
resourceListPlugin(), | ||
terser() | ||
] | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,5 @@ | ||
const { parse } = require("path"); | ||
const rescapeRE = require("escape-string-regexp"); | ||
|
||
module.exports = { | ||
repo: "GoogleChromeLabs/graviton", | ||
path: "dist/**/*", | ||
branch: "master", | ||
findRenamed(path, newPaths) { | ||
const parsedPath = parse(path); | ||
if (parsedPath.base.startsWith("bootstrap-")) { | ||
const end = /\..*$/.exec(parsedPath.base)[0]; | ||
const re = new RegExp( | ||
`^${rescapeRE(parsedPath.dir)}/bootstrap-[^\.]+${end}$` | ||
); | ||
return newPaths.find(path => re.test(path)); | ||
} | ||
} | ||
branch: "master" | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
/** Tell the service worker to skip waiting */ | ||
export async function skipWaiting() { | ||
const reg = await navigator.serviceWorker.getRegistration(); | ||
if (!reg || !reg.waiting) { | ||
return; | ||
} | ||
reg.waiting.postMessage("skip-waiting"); | ||
|
||
return new Promise(resolve => { | ||
navigator.serviceWorker.addEventListener( | ||
"controllerchange", | ||
() => resolve(), | ||
{ once: true } | ||
); | ||
}); | ||
} | ||
|
||
export let updateReady = false; | ||
|
||
/** Wait for an installing worker */ | ||
async function installingWorker( | ||
reg: ServiceWorkerRegistration | ||
): Promise<ServiceWorker> { | ||
if (reg.installing) { | ||
return reg.installing; | ||
} | ||
return new Promise<ServiceWorker>(resolve => { | ||
reg.addEventListener("updatefound", () => resolve(reg.installing!), { | ||
once: true | ||
}); | ||
}); | ||
} | ||
|
||
async function watchForUpdate() { | ||
const hasController = !!navigator.serviceWorker.controller; | ||
|
||
// If we don't have a controller, we don't need to check for updates – we've just loaded from the | ||
// network. | ||
if (!hasController) { | ||
return; | ||
} | ||
|
||
const reg = await navigator.serviceWorker.getRegistration(); | ||
// Service worker not registered yet. | ||
if (!reg) { | ||
return; | ||
} | ||
|
||
// Look for updates | ||
if (reg.waiting) { | ||
return; | ||
} | ||
const installing = await installingWorker(reg); | ||
await new Promise<void>(resolve => { | ||
installing.addEventListener("statechange", () => { | ||
if (installing.state === "installed") { | ||
resolve(); | ||
} | ||
}); | ||
}); | ||
|
||
updateReady = true; | ||
} | ||
|
||
/** Set up the service worker and monitor changes */ | ||
export async function init() { | ||
const thisURL = new URL(location.href); | ||
|
||
if (thisURL.searchParams.has("no-sw")) { | ||
const reg = await navigator.serviceWorker.getRegistration(); | ||
if (reg) { | ||
await reg.unregister(); | ||
location.reload(); | ||
} | ||
return; | ||
} | ||
|
||
await navigator.serviceWorker.register("/sw.js"); | ||
|
||
watchForUpdate(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/** | ||
* Copyright 2019 Google Inc. All Rights Reserved. | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
import { Component, h } from "preact"; | ||
import TopBar from "../top-bar/index.js"; | ||
import { loading, loadingInner } from "./style.css"; | ||
|
||
// tslint:disable-next-line:max-classes-per-file | ||
export default class GameLoading extends Component<{}, {}> { | ||
render() { | ||
return ( | ||
<div class={loading}> | ||
<TopBar titleOnly /> | ||
<div class={loadingInner}>…Loading…</div> | ||
</div> | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
.loading { | ||
display: flex; | ||
flex: 1; | ||
align-items: center; | ||
justify-content: center; | ||
position: relative; | ||
} | ||
|
||
@keyframes fade-in-out { | ||
50% { | ||
opacity: 1; | ||
} | ||
} | ||
|
||
.loadingInner { | ||
display: flex; | ||
flex-flow: column; | ||
padding: var(--bar-avoid) var(--side-margin) 0; | ||
box-sizing: border-box; | ||
width: 100%; | ||
text-align: center; | ||
font-size: 2.3rem; | ||
opacity: 0; | ||
animation: fade-in-out 3s ease-in-out infinite 0.5s; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm ending both these new plugins with
:
to signify they're something other thannode_modules
. Open to better suggestions.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m okay with this.