ionic-team / stencil

A toolchain for building scalable, enterprise-ready component systems on top of TypeScript and Web Component standards. Stencil components can be distributed natively to React, Angular, Vue, and traditional web developers from a single, framework-agnostic codebase.
https://stenciljs.com
Other
12.55k stars 784 forks source link

feat: bundle service-worker js / ts #3159

Open johnjenkins opened 2 years ago

johnjenkins commented 2 years ago

Prerequisites

Describe the Feature Request

Add the serviceWorker: { swSrc: '../whatever.ts' } to stencil's rollup bundling.

Describe the Use Case

Workbox now offers typed bundles for all their functionality allowing consumers to cherry pick what they require and keep the served end result smaller whilst also getting the benefits of intellisense and type safety.

However to utilise these benefits, the service worker file must be bundled via rollup / webpack etc - it must be a single file at run-time; import {...} from 'workbox-... or require('workbox-...') will not work.

Describe Preferred Solution

I'd propose stencil could run the sw file through typescript (if '.ts') and bundle to a single file via rollup.

Describe Alternatives

At the moment I have a separate rollup task running before stencil. It runs typescript, bundles then copies to the stencil src root:

// sw.rollup.config.ts

import typescript from '@rollup/plugin-typescript';
import commonjs from '@rollup/plugin-commonjs';
import nodeResolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';

const isProduction = process.env.NODE_ENV === 'production';

/** @type {import('rollup').RollupOptions} */
const options = {
  input: 'sw/sw.ts',
  output: {
    dir: './app',
    format: 'iife',
    banner: '// Autogenerated file. Edit sw/sw.ts instead'
  },
  plugins: [
    typescript(),
    nodeResolve(),
    commonjs(),
    replace({
      'process.env.NODE_ENV': JSON.stringify(isProduction ? 'production' : 'development'),
      preventAssignment: true,
    }),
  ]
}
export default options;

Related Code

No response

Additional Information

No response

MrAntix commented 1 year ago

would this remove the need for specific version importsScripts in the sw.js file too?

importScripts("workbox-v4.3.1/workbox-sw.js");

self.addEventListener("message", ({ data }) => {
  if (data === "skipWaiting") {
    self.skipWaiting();
  }
});

self.workbox.precaching.precacheAndRoute([]);

This often catches me out, as there is no compile time check and I only run service workers in prod

johnjenkins commented 1 year ago

@MrAntix yep - my sw.ts just looks like a normal module e.g.

import { registerRoute, setCatchHandler } from 'workbox-routing';
import { NetworkFirst, StaleWhileRevalidate, CacheFirst } from 'workbox-strategies';
import { CacheableResponsePlugin } from 'workbox-cacheable-response';
import { precacheAndRoute, matchPrecache, PrecacheEntry } from 'workbox-precaching';
...

/**
 * Cache CSS, JS, and Web Worker requests with a Stale While Revalidate strategy;
 * Uses a cached response for a request if it's available and updates the cache in the background with a response from the network
 */
registerRoute(
  // Check to see if the request's destination is style for stylesheets, script for JavaScript, or worker for web worker
  ({ request }) =>
    request.destination === 'style' ||
    request.destination === 'script' ||
    request.destination === 'worker',
  new StaleWhileRevalidate({
    cacheName: 'scripts',
    plugins: [
      // Ensure that only requests that result in a 200 status are cached
      new CacheableResponsePlugin({
        statuses: [200],
      }),
    ],
  }),
);

and it generates my sw.js using whatever workbox I have installed via my package.json