vite-plugin-pwa icon indicating copy to clipboard operation
vite-plugin-pwa copied to clipboard

Doesn't work with `vite-tsconfig-paths`

Open nodegin opened this issue 1 year ago • 21 comments

The VitePWA plugin seem not respecting or using Vite to transpile the files. this is my vite.config.ts

import baseConfig from '../vite.config.ts'
export default mergeConfig(baseConfig, {
  plugins: [
    react(),
    VitePWA({
      strategies: 'injectManifest',
      srcDir: '.',
      filename: 'service-worker.ts',
    }),
  ],
})

and this is the parent vite.config.ts

export default defineConfig({ plugins: [tsConfigPaths({ root: './' })] });

In service-worker.ts, I used import alias like

import someDep from '@project/module'

when building, it show this error message:

"@project/module" is imported by "core/service-worker.ts", but could not be resolved – treating it as an external dependency.

nodegin avatar Feb 15 '23 11:02 nodegin

also if I use typescript for the custom service worker file,

the dev mode will be broken:

Screen Shot 2023-02-15 at 20 11 34 Screen Shot 2023-02-15 at 20 11 55

it seems the file is not passed to vite to transpile, which means the dev-sw.js is actually a typescript file

nodegin avatar Feb 15 '23 11:02 nodegin

@nodegin on dev, the plugin doesn't build the sw, it's "build" by Vite (esbuild) removing ts annotations, you will need to resolve that dependency, try adding an alias.

can you provide a minimal repro?

userquin avatar Feb 15 '23 11:02 userquin

@userquin I have created a minimal reproduction repo: https://github.com/nodegin/vite-pwa-reprod

I tried build, it does not respect vite plugin as well during build. Alias seem working but not a ideal solution since my project have so many aliases, it should respect the tsconfigPlugin which auto loads the aliases

nodegin avatar Feb 15 '23 16:02 nodegin

dev is working, checking build...

imagen

userquin avatar Feb 15 '23 19:02 userquin

also working on build, you need to add an alias to resolve the library:

// apps/core/vite.config.ts
resolve: {
    alias: {
      '@repro/library': '../../../libs/library/src/index.ts',
    },
  },

imagen

imagen

userquin avatar Feb 15 '23 20:02 userquin

@userquin I understand adding resolve alias would work but as I point out before it seem to be not an ideal solution, because the paths in our real tsconfig.base.json is gigantic, it means we have to duplicate all those paths once more😢 and that's already handled by the tsconfigPlugin which is consumed by vite, is it possible to work together with other plugins?

pgsteven avatar Feb 16 '23 04:02 pgsteven

you can use this (import defaultInjectManifestVitePlugins adding ts plugin in injectManifest.vitePlugins option):

imagen

// apps/core/vite.config.ts
/// <reference types="vitest" />
import { defineConfig } from 'vite';

import { VitePWA, defaultInjectManifestVitePlugins } from 'vite-plugin-pwa';
import viteTsConfigPaths from 'vite-tsconfig-paths';

export default defineConfig({
  cacheDir: '../../node_modules/.vite/core',

  mode: 'development',

/*
  resolve: {
    alias: {
      '@repro/library': '../../../libs/library/src/index.ts',
    },
  },
*/

  server: {
    port: 4200,
    host: 'localhost',
  },

  preview: {
    port: 4300,
    host: 'localhost',
  },

  plugins: [
    viteTsConfigPaths({
      root: '../../',
    }),
    VitePWA({
      mode: 'development',
      strategies: 'injectManifest',
      srcDir: './src',
      filename: 'sw.ts',
      injectManifest: {
        vitePlugins: defaultInjectManifestVitePlugins.concat('vite-tsconfig-paths')
      },
      devOptions: {
        enabled: true,
        type: 'module',
      }
    }),
  ],
});

userquin avatar Feb 16 '23 08:02 userquin

@nodegin @pgsteven have you tested my previous suggestion? you can add the names of any plugin you have in Vite plugins.

userquin avatar Feb 16 '23 14:02 userquin

@userquin Sorry for late reply. I just tested again earlier, and still can't get it working in my real project. After adding the defaultInjectManifestVitePlugins it seems the alias is working now, but then there is another new issue which seems having conflict with the @vitejs/plugin-react plugin. I find it related to the @vitejs/plugin-react plugin, which would automatically add some code to the react component contains window object, and which is unavailable in service workers.

image Screen Shot 2023-02-19 at 5 22 02

I have updated my reproduction repo, if you try to remove the @vitejs/plugin-react plugin by commenting react(), in vite.config.ts, it will work well.

Also, putting @vitejs/plugin-react in defaultInjectManifestVitePlugins does not work

nodegin avatar Feb 18 '23 20:02 nodegin

Why do you need react in the sw? That option is to allow add plugins configured in your vite app in custom rollup buiild for your custom sw, you can check src/modules.ts in the repo.

I Will check your uodated repo tmr.

userquin avatar Feb 18 '23 20:02 userquin

@userquin I didn't use React in SW. The problem is, @repro/library contains both the code I need to use in SW, while also in my core application, it seem this mechanism will break the SW because the module imported contains React pieces

nodegin avatar Feb 18 '23 20:02 nodegin

Split it into 2 modules, one for your app and another for the sw

userquin avatar Feb 18 '23 20:02 userquin

You can have a lot of modules, for your app just re-export everything on one module and in the sw import just another one re-exporting what sw needs. Ofc in sw one dont use any module with react dependencies.

userquin avatar Feb 18 '23 20:02 userquin

But the problem is, if you comment the @vitejs/plugin-react plugin it will work, even it still contains React code.

Also, in the real project the sw.js is migrated from webpack, which also working fine even with React code.

So the problem seem to be vite-plugin-pwa incompatible with @vitejs/plugin-react?

nodegin avatar Feb 18 '23 20:02 nodegin

Splitting is not a solution to us since our app is on production, splitting it basically mean we have to rewrite the whole app and the logics.

nodegin avatar Feb 18 '23 20:02 nodegin

@nodegin

You only need to split it into 2 modules @repro/library, you don't need to change any line in your code base just the library:

  • move logic required by the sw to a separate module
  • Include in @repo/library the import for previous new one module and export * from xxxx (just re-export everything from it: the logic has been only moved
  • add a new map @repro/sw-library for 1
  • Import previous module in the sw

When building the sw we use a separate rollup build configuring some vite internal plugins, the plugin Will also allow you to add any plugin configured plugin in your vite config file.

You only need ts plugin to be include in the rollup build of your sw to resolve some external dependency. The option in my prevoius comment is to include ts plugin, we seek the plugin by name.

You can check L130 on src/modules.ts.

userquin avatar Feb 18 '23 20:02 userquin

If you have a monolitic module with disparate dependencies, you will be able to use it in some environments. For example, your app Will not work on node server if you dont use jsdom or happy-dom to mock window/document. In a sw is even worst, you dont have globalThis/global, polyfills Will also fail to mock sw self.

userquin avatar Feb 18 '23 21:02 userquin

@userquin but I think it is still an issue, ideally even if it contains React code, it should not affect the vite to compile the code? since the sw only import the test property which is purely a string, and as I mentioned before without the plugin-react it works perfectly, even I add something having the window. export const func = () => (window as any)['test']

image

So I really don't get why it can't work with the react plugin?

nodegin avatar Feb 19 '23 06:02 nodegin

@nodegin I still don't know what's failing, building the app, building the sw or running the app build?

userquin avatar Feb 19 '23 17:02 userquin

@userquin running the dev mode with plugin-react is not working, the service worker would be invalid, you can try my reproduction repo

nodegin avatar Feb 21 '23 01:02 nodegin

@nodegin you need to split your library, I've done these changes:

  • add sw-stuff.ts in your library, exporting only test
  • re-export all sw stuff on index.ts: export * from ./sw-stuff
  • add "@repro/sw-library": ["libs/library/src/sw-stuff.ts"] to paths in tsconfig.base.json
  • add @vitejs/react as dev dependency to package.json: "@vitejs/plugin-react": "^3.1.0",
  • move vite-pwa-plugin to devDependencies
  • add navigateFallback: '/' to devOptions (you should also add it to injectManifest entry in pwa plugin options) adding this code to the sw:
/// <reference lib="webworker" />
/// <reference types="vite/client" />

import { clientsClaim, setCacheNameDetails } from 'workbox-core';
import {createHandlerBoundToURL, precacheAndRoute} from 'workbox-precaching';
import { NavigationRoute, registerRoute } from 'workbox-routing'
import {test} from '@repro/sw-library';

import { version } from '../../../package.json';

// ServiceWorkerGlobalScope is a type from the workbox-precaching module
declare const self: Window & ServiceWorkerGlobalScope;

setCacheNameDetails({
  prefix: test,
  suffix: version,
});

// Tells the Service Worker to skip the waiting state and become active.
self.skipWaiting();

// Will make the Service Worker control the all clients right away
// (even if they're controlling other tabs or windows). Without this,
// we could be seeing different versions in different tabs or windows.
clientsClaim();


let allowlist: undefined | RegExp[]
if (import.meta.env.DEV)
  allowlist = [/^\/$/]


precacheAndRoute([...self.__WB_MANIFEST]);

// to allow work offline
registerRoute(new NavigationRoute(
  createHandlerBoundToURL('/'),
  { allowlist },
))

imagen

userquin avatar Feb 21 '23 11:02 userquin