GatsbyJS: Import npm package in serviceworker file (gatsby-plugin-offline) - javascript

We use gatsby-plugin-offline in order to get a auto-generated service worker. It works fine since all pages are available offline now. With the following code in gatsby-config.js, some additional code can be added to the service worker.
{
resolve: `gatsby-plugin-offline`,
options: {
appendScript: 'src/custom-sw-code.js',
},
},
Within custom-sw-code.js we could not manage to import any npm package so far. If we use import or require on top of the file, browsers complain about non-working serviceworker code (after building and serving the app).
Do you know, how to import packages in the serviceworker?

If you want to pull in external scripts into a service worker, importScripts() will help. (Another option is to use static ES module imports, but they aren't supported in all browsers as of this writing.)
For each script URL you pass in, importScripts() will synchronously download and execute the code within the context of the current ServiceWorkerGlobalScope. The service worker will also take care of automatically caching the scripts, so you don't need to manually add those URLs to a cache, and the request for those scripts won't trigger your service worker's fetch event listeners.
If you're loading code from npm from a CDN, make sure that you're requesting either a UMD or IIFE bundle, and make note of what the exported global symbols are, as that's what will end up being exposed in the ServiceWorkerGlobalScope.

Related

What are the "Query Suffixes" referred to in the Vite Docs?

I have been reading through the Vite documentation for Web workers, and it mentioned importing a file using "Query Suffixes," which I have never encountered and am not sure what to search for to learn more. I'm not sure if this is native to Node.js, Vite.js, or if it is a plugin that is built into Vite.
This is the specific section that I am referring to:
Import with Query Suffixes
A web worker script can be directly imported by appending ?worker or ?sharedworker to the import request. The default export will be a custom worker constructor:
import MyWorker from './worker?worker'
const worker = new MyWorker()
The worker script can also use import statements instead of importScripts() - note during dev this relies on browser native support and currently only works in Chrome, but for the production build it is compiled away.
By default, the worker script will be emitted as a separate chunk in the production build. If you wish to inline the worker as base64 strings, add the inline query:
import MyWorker from './worker?worker&inline'
If you wish to retrieve the worker as a URL, add the url query:
import MyWorker from './worker?worker&url'
See Worker Options for details on configuring the bundling of all workers.
Update:
I found an MDN page on import that seems like a step in the direction that I am looking for, as well as this MDN page on import.meta that looks a lot like what I am searching for. I tried following that lead, but it didn't help me understand this Vite feature any better.
Is the ?worker query suffix a custom Vite implementation of import.meta?

Natively import ES module dependencies from npm without bundling/transpiling first-party source

Background
I'm trying to create a "buildless" JavaScript app, one where I don't need a watch task running to transpile JSX, re-bundle code, etc every time I save any source file.
It works fine with just first-party code, but I'm stuck when I try to import dependencies from npm.
Goal
I want to achieve this kind of workflow:
npm install foo (assume it's an ES module, not CommonJS)
Edit source/index.js and add import { bar } from 'foo'
npm run build. Something (webpack, rollup, a custom script, whatever) runs, and bundles foo and its dependencies into ./build/vendor.js (without anything from source/).
Edit index.html to add <script src="build/vendor.js" type="module"...
I can reload source/index.js in my browser, and bar will be available. I won't have to run npm run build until the next time I add/remove a dependency.
I've gotten webpack to split dependencies into a separate file, but to import from that file in a buildless context, I'd have to import { bar } from './build/vendor.js. At that point webpack will no longer bundle bar, since it's not a relative import.
I've also tried Snowpack, which is closer to what I want conceptually, but I still couldn't configure it to achieve the above workflow.
I could just write a simple script to copy files from node_modules to build/, but I'd like to use a bundled in order to get tree shaking, etc. It's hard to find something that supports this workflow, though.
I figured out how to do this, using Import Maps and Snowpack.
High-Level Explanation
I used Import Maps to translate bare module specifiers like import { v4 } from 'uuid' into a URL. They're currently just a drafted standard, but are supported in Chrome behind an experimental flag, and have a shim.
With that, you can use bare import statements in your code, so that a bundler understands them and can work correctly, do tree-shaking, etc. When the browser parses the import, though, it'll see it as import { v4 } from 'http://example.org/vendor/uuid.js', and download it like a normal ES module.
Once those are setup, you can use any bundler to install the packages, but it needs to be configured to build individual bundles, instead of combining all packages into one. Snowpack does a really good job at this, because it's designed for an unbundled development workflow. It uses esbuild under the hood, which is 10x faster than Webpack, because it avoids unnecessarily re-building packages that haven't changed. It still does tree-shaking, etc.
Implementation - Minimal Example
index.html
<!doctype html>
<!-- either use "defer" or load this polyfill after the scripts below-->
<script defer src="es-module-shims.js"></script>
<script type="importmap-shim">
{
"imports": {
"uuid": "https://example.org/build/uuid.js"
}
}
</script>
<script type="module-shim">
import { v4 } from "uuid";
console.log(v4);
</script>
snowpack.config.js
module.exports = {
packageOptions: {
source: 'remote',
},
};
packageOptions.source = remote tells Snowpack to handle dependencies itself, rather than expecting npm to do it.
Run npx snowpack add {module slug - e.g., 'uuid'} to register a dependency in the snowpack.deps.json file, and install it in the build folder.
package.json
"scripts": {
"build": "snowpack build"
}
Call this script whenever you add/remove/update dependencies. There's no need for a watch script.
Implementation - Full Example
Check out iandunn/no-build-tools-no-problems/f1bb3052. Here's direct links to the the relevant lines:
snowpack.config.js
snowpack.deps.json
package.json
core.php outputs the shim
plugin.php - outputs the import map
passphrase-generator.js - imports the modules. (They're commented out in this example, for reasons outside the scope of this answer, just uncomment them, run the bundle script, and they'll work).
If you are willing to use an online service, the Skypack CDN seems to work nicely for this. For instance I wanted to use the sample-player NPM module and I've chosen to use a bundle-less workflow for my project using only ES6 modules as I'm targeting embedded Chromium latest version so don't need to worry about legacy browser support, so all I needed to do was:
import SamplePlayer from "https://cdn.skypack.dev/sample-player#^0.5.5";
// init() once the page has finished loading.
window.onload = init;
function init() {
console.log('hello sampler', SamplePlayer)
}
and in my html:
<script src="./src/sampler/sampler.js" type="module"></script>
And of course you could just look inside the JS file the CDN generates at the above url and download the generated all-in-one js file it points to, in order to use it offline as well if needed.

JS - creating new worker causes 404

I'm using Worker API to create new worker, it's as simple as:
var myWorker = new Worker('src/worker.js');
Unfortunately I'm using webpack to build my project and linking to src/worker.js returns 404.
How can I use it to create my Worker?
import hardWorker from './src/hardWorker.js';
var myWorker = new Worker(HOW TO PUT HARDWORKER HERE?);
Try using the worker-loader plugin for webpack, which can be found here: https://github.com/webpack-contrib/worker-loader
This registers scripts loaded through it as a Web worker, and bundles all of your workers in the main bundle that webpack creates. Using it in your app is as simple as this:
import MyWorker from 'worker-loader!./Worker.js';
const worker = new MyWorker();
Or you can add loader configuration parameters to your webpack.config so all files matching a specific pattern are loaded and registered as workers. The worker-loader has an example of this.
The issue is that the worker API expects a URL to load the work script from and webpack tries to bundle everything into a single file, meaning the src/worker.js file isn't where it needs to be when the browser tries to load it.
There are a couple options.
You could manually copy the worker.js file to wherever the build is trying to load the file from.
You could set up webpack to copy the file to the right location using the CopyWebpackPlugin.
Depending on how complicated your worker is, you could just put the code in a string and create an object URL so the browser doesn't have to load anything from the server at all. This is an approach I've used in the past and have written a helper library for to Workers a little easier to use.
Hope that helps!

Attempting to load a JavaScript sdk into an Angular2 application. Can't find all dependencies

I'm attempting to make use of this library: https://github.com/MagicTheGathering/mtg-sdk-javascript in an Angular2 application.
Unfortunately, I've been going in circles trying to load it into my application.
Firstly, on the TypeScript side if I import it using:
import { } from 'mtgsdk';
there are no types to load into the {}.
If I attempt to load it using something similar to:
import * as mtg from 'mtgsdk'
I'm unable to because it says that it's unable to find a module named mtgsdk.
I've installed the module using
npm install --save mtgsdk
Also, npm installs work fine for other modules.
The application compiles fine if I load it in using require via something similar to this:
var mtg = require('mtgsdk');
Taking that approach, I'm able to compile and launch but in the browser I get a number of errors about modules that it can't find. I figure they are prerequisites for the sdk that didn't get loaded so I start bringing them in via package.json.
For every one that I bring in, I then have to go to systemjs.config.js and add an entry pointing to the module's entry point and often have to specify a default extension using blocks like this:
pointer
'mtgsdk': 'npm:mtgsdk/lib/index.js',
'request-promise': 'npm:request-promise/lib/rp.js',
'ramda': 'npm:ramda/dist/ramda.js',
'emitter20': 'npm:emitter20/index.js',
'bluebird': 'npm:bluebird/js/browser/bluebird.js',
'request': 'npm:request/index.js'
default extension
'request-promise':
{
defaultExtension: 'js'
}
I'm not sure if that's the right approach though because the more dependencies I add, the more that I end up requiring. At one point I had literally gotten up to 50 extra dependencies added because every time I launched, the browser console would find more that were needed.
Is there any easier way to load all of these in?
Also, some of them (such as tough-cookie and request-promise-core) were very problematic to load and I couldn't get the browser console to stop complaining about them. Finally, some of them seemed very basic such as url, http, https. Those seem like they should already be present.
Using systemjs was utilized in the previous versions of Angular 2, However Angular 2 has evolved to Angular 4, with super new features like Angular CLI.
I recommend your use Angular CLI, with #angular/cli.
Importing Node modules
Since mtgsdk is a node-module, you can easily import it using
import * as mtg from 'mtgsdk'
However for your program to compile, you must install a type definition for it. or declare one for it in /typings.json or your app might not build.
Importing Client Scripts
For client scripts like firebase.js you won't need to add client scripts as entries in systemjs.config.js again.
Using #angular/cli, you would easily add them in the scripts[] array in your angular-cli.json for automatic compilation.
Then access them like this
declare const firebase: any;
Here is a quickstart tutorial to set up Angular with #angular/cli.

How to import other javascript module in PhantomJS or CasperJS

I'm trying to build a functional test using CasperJS.
caseperjs is run by a backend test suite using the following command:
PHANTOMJS_EXECUTABLE=../client/node_modules/phantomjs/bin/phantomjs ../client/ext_modules/casperjs/bin/casperjs test ../client/test/functional/init.coffee
In init.coffee I want to import/include other module (file) which seats just next to it. How to do it?
The following doesn't works:
require("user")
All I want is to get a content from other file into init.coffee
After trying a number of the other suggestions (each expected to work in the context of their corresponding environments), hit on this solution:
phantom.page.injectJs( 'script.js');
As of 1.1, CasperJS relies on PhantomJS’ native require():
https://groups.google.com/forum/#!topic/phantomjs/0-DYnNn_6Bs
Injecting dependencies
While injecting additional modules, CasperJS looks for path relative to cur directory
(the place where we run a casperjs command)
We can inject dependency using clientScripts option. However the injected dependencies can't
use require "globally". They are injected immediately to every page loaded.
casper.options.clientScripts = ["path/relative/to/cur/dir"]
Also we can inject modules using commandline args:
casperjs test --includes=foo.js,bar.js path/to/the/test/file
Using require
To import user modules use:
require "./user-module.coffee"
Then in user modules you can also use require. Using require paths are resolved
relative to the current file (where require is called).
If in user module you want to import casper libs, then you need to patch require,
check: https://casperjs.readthedocs.org/en/latest/writing_modules.html
There's a section about that in the docs
var require = patchRequire(global.require);
require('./user');
In your case you should use global.require since you're using CoffeeScript.

Categories

Resources