Skip to content

Instantly share code, notes, and snippets.

@aurelijusb
Last active June 6, 2018 19:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aurelijusb/2d7ce6e7ed8634c49a2d0645943c9744 to your computer and use it in GitHub Desktop.
Save aurelijusb/2d7ce6e7ed8634c49a2d0645943c9744 to your computer and use it in GitHub Desktop.
PWA for Symfony 4: ServiceWorkerPlugin

Inspired by

Usage

webpack.config.js:

addPlugin(new ServiceWorkerPlugin({
        staticFiles: {
            './assets/workers/manifest.json': '../manifest.json',
            './assets/workers/service-worker.js': '../service-worker.js',
            './assets/workers/version.json': 'version.json',
            './assets/js/main.js': 'js/main.js'
        },
        dynamicFiles: [
            'js/app.js'
        ],
        replace: {
            "@NOTICE@": () => "This file is AUTO GENERATED!",
            "@VERSION@": () => "v0.0.5-" + (new Date()).toISOString(),
        }
    }))
const clearCache = () => {
return caches.keys().then((keyList) => {
return Promise.all(keyList.map(function (key) {
console.log('[ServiceWorker] Removing old cache', key);
return caches.delete(key);
}));
})
};
class Version extends React.Component {
// ...
getVersion() {
const salt = new Date().getTime();
this.state.latest = undefined;
axios.get("build/version.json?salt=" + salt).then((r) => {
console.log('RECEIVED', r.data);
this.setState({latest: r.data.version});
}).catch((e) => console.error('Error ', e));
}
// ...
update() {
if ('serviceWorker' in navigator && navigator.serviceWorker.controller && serviceWorker) {
serviceWorker.unregister().then(clearCache).then(() => {
console.log('[ServiceWorker] Unregistered');
location.reload();
}).catch((e) => {
console.error('[ServiceWorker] Failed to unregister', e);
});
}
}
// ...
}
var serviceWorker = undefined; // Set when service worker is registered
if (('serviceWorker' in navigator) && (typeof disablePwa === "undefined")) {
navigator.serviceWorker
.register('./service-worker.js')
.then(function(sw) {
console.log('Service Worker Registered');
serviceWorker = sw;
})
.catch(function(error) {
console.log('Service Worker Registration failed with ' + error);
});
navigator.serviceWorker.addEventListener('message', event => {
console.info('APP FROM SERVICE WORKER', event);
});
}
// Copies file from source to build path
// Replace text parts in provided files
const fs = require('fs');
function ServiceWorkerPlugin(options) {
console.assert(options.staticFiles instanceof Object, 'Expected option staticFiles: {"pathFrom": "pathTo"}');
console.assert(options.dynamicFiles instanceof Array, 'Expected option dynamicFiles: ["js/app.js"]');
console.assert(options.replace instanceof Object, 'Expected option replace: {"textFrom": "textTo"}');
ServiceWorkerPlugin.prototype.options = options;
}
ServiceWorkerPlugin.prototype.apply = (compiler) => {
compiler.plugin('emit', (compilation, callback) => {
let staticFiles = ServiceWorkerPlugin.prototype.options.staticFiles;
let dynamicFiles = ServiceWorkerPlugin.prototype.options.dynamicFiles;
// Evaluate values once per file change
let replace = new Map(
Object.entries(
ServiceWorkerPlugin.prototype.options.replace
).map((a) => [a[0], a[1]()])
);
dynamicFiles.forEach((file) => {
if (file in compilation.assets) {
let asset = compilation.assets[file];
if (asset.children instanceof Array) {
asset.children.forEach((element, key) => {
if (element._value) {
replace.forEach((to, from) => {
compilation.assets[file].children[key]._value = element._value.replace(from, to);
});
}
})
} else if ((typeof asset._value) === 'string') {
replace.forEach((to, from, _) => {
compilation.assets[file]._value = asset._value.replace(from, to);
});
}
}
});
let remainingAsync = Object.keys(staticFiles).length;
Object.entries(staticFiles).forEach(([sourceFile, destinationFile]) => {
fs.readFile(sourceFile, {encoding: 'utf-8'}, (err, content) => {
if (err) {
throw new Error("Cannot read: " + sourceFile + ": " + err)
}
replace.forEach((to, from) => {
content = content.replace(from, to)
});
compilation.assets[destinationFile] = {
source: () => content,
size: () => content.length,
};
remainingAsync--;
if (remainingAsync <= 0) {
callback();
}
});
});
});
};
module.exports = ServiceWorkerPlugin;
{
"version": "@VERSION@"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment