Generate ServiceWorker using workbox 6 - how to import "registerRoute" from "workbox-routing"? - service-worker

I have upgraded my project to use workbox 6 and have modified my code accordingly.
After injecting manifest (generating serviceWorker.js) my browser reports error:
Service worker error TypeError: ServiceWorker script at
http://127.0.0.1:8080/serviceWorker.js for scope
http://127.0.0.1:8080/ threw an exception during script evaluation. app.js:218:23
I have removed code to determine what causes the error and am now left with:
serviceWorker.js (generated from serviceWroker-base.js)
importScripts('workbox-sw.js');
import { registerRoute } from 'workbox-routing';
import { StaleWhileRevalidate } from 'workbox-strategies';
const CACHE_DYNAMIC_NAME = 'dynamic-DEBUG-001'
webpack.config.js
const {InjectManifest} = require('workbox-webpack-plugin')
const workboxWebpackInjectPlugin = new InjectManifest({
swSrc: './serviceWorker.js'
})
// build WEBPACK CONFIG
const config = {}
//...
config.plugins = [
nodeEnvPlugin,
firebasePlugin,
cssExtractPlugin,
workboxWebpackInjectPlugin,
]
//...
return config
If I remove the imports of registerRoute and StaleWhileRevalidate in serviceWorker.js then the service worker registers successfully - but then I cannot register routes. ;) I have installed workbox-routing and workbox-strategies.
package.json
"scripts": {
"generate:sw": "workbox injectManifest"
},
"dependencies": {
...
"workbox-routing": "^6.0.2",
"workbox-strategies": "^6.0.2"
},
"devDependencies": {
...
"webpack": "^4.41.2"
}
generate:sw is the command I run to inject manifest and create serviceWorker.js.
My suspicion is that the imoprts are not handled correctly? How can I use registerRoute and StaleWhileRevalidate in my service worker?
Kind regards /K

The info at https://developers.google.com/web/tools/workbox/guides/using-bundlers might be helpful.
You don't need to include importScripts('workbox-sw.js');
If you plan on using precaching:
You can run InjectManifest via workbox-webpack-plugin and it will take care of both compiling your service worker (i.e. inlining the ES module imports into a final, runnable service worker file) as well as replacing a self.__WB_MANIFEST inside your service worker file with the actual precache manifest based on the assets in your webpack build.
If you don't plan on using precaching:
You can add your service worker file, including the ES module imports, as a entry point in your webpack config, and that should handle inlining the ES module imports into a final, runnable service worker file.
If you're already using webpack, then your workbox injectManifest step isn't needed. See the previous two points.

Related

Rollup configuring alias for semantic-ui-less #import

I have been configuring rollup for creating custom react component library on top of fomantic-ui.
I have already setup the rollup.config.js
ALthough in the configuration, I need to resolve an import:
#import (multiple) '../../theme.config';
the import is part of fomantic-ui-less library, which needs to be resovled to:
path.join(__dirname, '/themes/theme.config')
and I do have themes/theme.config at my project root.
when I run build command it throws following error:
[!] (plugin postcss) Error: '../../theme.config' wasn't found. Tried - E:\Projects\UILibrary\node_modules\fomantic-ui-less\theme.config,..\..\theme.config
and I have used the rollup-plugin-postcss plugin and #rollup/plugin-alias, and called it inside plugins array
...
plugins: [
...
alias({
entries: [
find: '../../theme.config$',
replacement: path.join(__dirname, '/themes/theme.config')
]
})
postcss(),
...
]
I have also tried changing the order of plugins.
And the worst part is, it is working when configuring the storybook, using webpack alias.
.storybook/main.js
webpackFinal: async (config) => {
config.resolve.alias = {
"../../theme.config$": path.join(__dirname, "../themes/theme.config")
}
...
}

Q: How to ensure vendor chunk hash doesn't change with webpacker?

I have a Rails 6 project with webpacker 4.2.2 configured to split vendor chunks into individual files:
# config/webpack/environment.js
const { environment } = require('#rails/webpacker')
const webpack = require('webpack')
environment.config.merge({
plugins: [
new webpack.HashedModuleIdsPlugin(),
],
optimization: {
minimize: true,
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
// #see https://hackernoon.com/the-100-correct-way-to-split-your-chunks-with-webpack-f8a9df5b7758
vendor: {
test: /[\\/]node_modules[\\/]/,
name(module) {
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
return `npm.${packageName.replace('#', '')}`;
},
priority: 10,
},
}
}
}
})
module.exports = environment
When we precompile our assets, this produces fingerprinted files for each NPM dependency, which we upload for long-term caching and CDN-based distribution.
However, we're noticing that when we add a new library to the pack, this unexpectedly causes a rehash of many chunk files for dependencies that have not changed at all.
For example, this change in my app/javascript/packs/application.js:
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
require("channels")
import 'msr'
import copy from 'clipboard-copy'
+import axios from 'axios'
will produce the following change in my output chunks (produced from running bin/rails webpacker:compile):
--- a 2020-07-06 18:39:52.202440803 +0000
+++ b 2020-07-06 18:39:52.210440748 +0000
## -1,6 +1,8 ##
-application-1e8721172ae65f57286b.chunk.js
-npm.clipboard-copy-10b42ffbc97b4e927071.chunk.js
-npm.msr-01ea266e2c932167f10b.chunk.js
-npm.rails-a4564cfc542024efeb95.chunk.js
-npm.turbolinks-eeef46ff44962af9ac87.chunk.js
-npm.webpack-7226f5cf46a8c4e61c26.chunk.js
+application-bad0ed20808541f88894.chunk.js
+npm.axios-40b4b54ebace2b9e3907.chunk.js
+npm.clipboard-copy-79d2051f48603e0267e0.chunk.js
+npm.msr-f5a4252b7a7e0a94157f.chunk.js
+npm.process-cfe824ecbab5abe0eecc.chunk.js
+npm.rails-aa1c430d6ceee3ca6bd6.chunk.js
+npm.turbolinks-e28554dbfd4b75aa12e5.chunk.js
+npm.webpack-35f718d9a20b8bca2927.chunk.js
This is a double whammy because of unnecessary cache invalidation and additional CDN transfer costs.
My question is, is there a way to ensure the vendor chunk doesn't get rehashed because of dependency changes?
I don't know if this is a limitation with the way that webpack's SplitChunksPlugin works, but any advice is appreciated.
By the way, I've prepared a minimal Rails project that reproduces the situation I've described above: https://github.com/alextsui05/webpacker-vendor-chunks
A detailed summary is included in the README on the repository, and I invite any answerers to discuss based on that code.
Try setting the option moduleIds: 'hashed'
https://v4.webpack.js.org/configuration/optimization/#optimizationmoduleids

What classspath is used for executing Grails' application.groovy

What classspath is used for compiling/executing Grails' application.groovy?
In my application.groovy, I instantiate a custom class (contained in a dependency's jar) and assign it to one of the config properties, like so:
environments {
production {
configProperty = new com.example.CustomClass()
I recently upgraded my application from Grails 3.1.5 to 3.2.2, and now this no longer works.
I receive an error like the following when I try to run grails run-app:
Error occurred running Grails CLI: startup failed:
script14788250424471597489853.groovy: 43: unable to resolve class com.example.CustomClass
# line 43, column 33.
configProperty = new com.example.CustomClass()
(Notice that the code is in the production block, but I'm running in development (run-app). That makes me think it's the compilation of this script that is failing.)
So I'm guessing I just need to add my dependency (that contains the CustomClass) to the appropriate classpath, but I'm not sure which one.
I'm using gradle, and have the following in my build.gradle file, to pull in the dependency containing CustomClass:
buildscript {
dependencies {
classpath "com.example:custom-module:1.1"
// ...
dependencies {
compile group: 'com.example', name: 'custom-module', version:'1.1'
}
The grails-app/conf/application.groovy file shouldn't reference application classes because it is read before compilation. If you wish to reference application classes in configuration please use grails-app/conf/runtime.groovy

How to find path to the package directory when the script is running with `pub run` command

I am writing a package that loads additional data from the lib directory and would like to provide an easy way to load this data with something like this:
const dataPath = 'mypackage/data/data.json';
initializeMyLibrary(dataPath).then((_) {
// library is ready
});
I've made two separate libraries browser.dart and standalone.dart, similar to how it is done in the Intl package.
It is quite easy to load this data from the "browser" environment, but when it comes to the "standalone" environment, it is not so easy, because of the pub run command.
When the script is running with simple $ dart myscript.dart, I can find a package path using dart:io.Platform Platform.script and Platform.packageRoot properties.
But when the script is running with $ pub run tool/mytool, the correct way to load data should be:
detect that the script is running from the pub run command
find the pub server host
load data from this server, because there could be pub transformers and we can't load data directly from the file system.
And even if I want to load data directly from the file system, when the script is running with pub run, Platform.script returns /mytool path.
So, the question is there any way to find that the script is running from pub run and how to find server host for the pub server?
I am not sure that this is the right way, but when I am running script with pub run, Package.script actually returns http://localhost:<port>/myscript.dart. So, when the scheme is http, I can download using http client, and when it is a file, load from the file system.
Something like this:
import 'dart:async';
import 'dart:io';
import 'package:path/path.dart' as ospath;
Future<List<int>> loadAsBytes(String path) {
final script = Platform.script;
final scheme = Platform.script.scheme;
if (scheme.startsWith('http')) {
return new HttpClient().getUrl(
new Uri(
scheme: script.scheme,
host: script.host,
port: script.port,
path: 'packages/' + path)).then((req) {
return req.close();
}).then((response) {
return response.fold(
new BytesBuilder(),
(b, d) => b..add(d)).then((builder) {
return builder.takeBytes();
});
});
} else if (scheme == 'file') {
return new File(
ospath.join(ospath.dirname(script.path), 'packages', path)).readAsBytes();
}
throw new Exception('...');
}

how to deploy your dart app (using Web ui) without using Pub Deploy

What is the best strategy to deploy a Dart Web-ui app manually ?
pub deploy doesn't work for me and I have raised bug report. So am thinking what is the best way to manually deploy.
This is how I started:
1) From project root I compile the webui components (dwc.dart)
2) change directory to web/out then run dart2js
3) copy all .js files into that scripts/js public folder on server
4) copy appname.html to server changing css and script paths to option 3
5) Make sure dart.js is also in the same directory as item 3
this is as far as I got. So what else do I need to do ?
A few questions:
1) Do I manually change the file paths in the generated .js files to point to public folders on server for the files they are referencing and make sure those files are on server also ?
2) Do I need to copy all packages to server also ?
3) Any preferred file structure on server?
Any tips on this really appreciated.
Thanks.
I wrote a Grunt script for it (since I had no time to look up how to properly write code for Grunt, I did not share the code since it's a mess) but I basically do this:
compiling a list of files with dwc to a given out dir
compile it to javascript
clean up all non-deployable files
change some paths inside the HTML to match the server paths (for some reasons, this gets changed by the compilation process)
remove all packages except the ones I really need (JS interopt and browser)
Since I'm only using the JS version, I remove all dart packages. Since the paths inside the HTML files are up to you, you can already use a structure that suits you/your server.
I can provide you with a Grunt script to understand the order of tasks. Practically the order I use is this one:
Create the build directory. I usually use /build/web. I usually create these files (index.html, main.dart, /css and so on into the /web dir). I create the rest of components into /lib directory.
Compile the .dart file that contains the main() function ("main.dart" in my case for simpler projects) file to Javascript and put it into /build/web directory
Copy the other needed files and folders to the /build/web directory. Also, during this process you'll be copying the packages that your project needs. You'll see in the example provided below.
Remove all empty folders from the project
You can create a Grunt task to open the /index.html file in the browser once the building process has ended (I will not provide this example)
The structure of the dart test project:
testApp
- gruntfile.js
- package.js
/lib
/packages
/angular
/web
- index.html
- main.dart
/css
/img
So, the Grunt example script to cover steps from 1 - 4 looks like this (copy it to gruntfile.js):
module.exports = function (grunt) {
grunt.initConfig({
// 1.
// create build web directory
mkdir: {
build: {
options: {
create: ['build/web']
}
}
},
// 2.
// compile dart files
dart2js: {
options: {
// use this to fix a problem into dart2js node module. The module calls dart2js not dart2js.bat.
// this is needed for Windows. So use the path to your dart2js.bat file
"dart2js_bin": "C:/dart/dart-sdk/bin/dart2js.bat"
},
compile: {
files: {'build/web/main.dart.js': 'web/main.dart'}
}
},
// 3.
// copy all needed files, including all needed packages
// except the .dart files.
copy: {
build: {
files: [
{
expand: true,
src: [
'web/!(*.dart)',
'web/css/*.css',
'web/res/*.svg',
'web/packages/angular/**/!(*.dart)',
'web/packages/browser/**/!(*.dart)'
],
dest: 'build'
}
]
}
},
// 4.
// remove empty directories copied using the previous task
cleanempty: {
build: {
options: {
files: false
},
src: ['build/web/packages/**/*']
}
},
});
require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
grunt.registerTask('default', [
'mkdir:build',
'dart2js',
'copy:build',
'cleanempty:build'
]);
};
So this is the Grunt script example.
Create a /gruntfile.js file into your project's root directory and copy/paste the script to it.
Create a /package.json file into your project's root directory and copy/paste the following script:
{
"name": "testApp",
"version": "0.0.1",
"description": "SomeDescriptionForTheTestApp",
"main": "",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "YourName",
"peerDependencies": {
"grunt-cli": "^0.1.13"
},
"devDependencies": {
"grunt": "^0.4.5",
"grunt-cleanempty": "^1.0.3",
"grunt-contrib-copy": "^0.7.0",
"grunt-dart2js": "0.0.5",
"grunt-mkdir": "^0.1.2",
"matchdep": "^0.3.0"
}
}
Open Command Prompt in Windows, Terminal in Linux, navigate to your project's root directory and use this command:
npm install
Wait untill all Grunt modules needed will be downloaded to your local project. Once this is finished, issue this command in Command Prompt or Terminal:
node -e "require('grunt').cli()"
You can use this to initiate Grunt default task without having Grunt installed globally on your system.
Now, to know the exact build structure for your project (including the packages that the project needs), make a build using Pub Build. Then you will be able to instruct Grunt to create the same dir structure.
You can add other tasks (like minification) if you want.
Hope this will help you all to understand the process and get you started with a test app first. Add your comments to make this even better and simplify it even more.

Resources