antfu-collective / vite-ssg

Static site generation for Vue 3 on Vite
MIT License
1.33k stars 136 forks source link

Can't avoid crash when trying to discriminate between build environment and client side. #230

Closed YannDuv closed 2 years ago

YannDuv commented 2 years ago
    "vue": "3.2.25",
    "vite": "2.8.0",
    "vite-ssg": "0.19.2",

I have a dialog that has a dependency on @a11y/focus-trap which is a web component. As it is a web component, it uses stuff like document.createElement('template'). So I need to prevent it from being imported during SSG.

I tried 2 solutions without success :

  1. Test window presence with dynamic import
if (window) {
  import('@a11y/focus-trap')
}

Leads to the following error:

[vite-ssg] An internal error occurred.
[vite-ssg] Please report an issue, if none already exists: https://github.com/antfu/vite-ssg/issues
file:///Users/doe/Projects/front/.vite-ssg-temp/main.mjs:63
if (window) {
^

ReferenceError: window is not defined
    at file:///Users/doe/Projects/front/.vite-ssg-temp/main.mjs:63:1
    at ModuleJob.run (node:internal/modules/esm/module_job:183:25)
    at async Loader.import (node:internal/modules/esm/loader:178:24)
    at async importModuleDynamicallyWrapper (node:internal/vm/module:437:15)
    at async Object.build (/Users/doe/Projects/front/node_modules/vite-ssg/dist/chunks/build.cjs:171:87)
    at async Object.handler (/Users/doe/Projects/front/node_modules/vite-ssg/dist/node/cli.cjs:25:3)
  1. Use ClientOnly

Which is the only way in the documentation to prevent running code in SSG. I used it around all usage of the dialog component that does the import. Sadly it doesn't change anything.

[vite-ssg] An internal error occurred.
[vite-ssg] Please report an issue, if none already exists: https://github.com/antfu/vite-ssg/issues
file:///Users/doe/Projects/front/.vite-ssg-temp/main.mjs:466
const template = document.createElement("template");
                 ^

ReferenceError: document is not defined
    at file:///Users/doe/Projects/front/.vite-ssg-temp/main.mjs:466:18
    at ModuleJob.run (node:internal/modules/esm/module_job:183:25)
    at async Loader.import (node:internal/modules/esm/loader:178:24)
    at async importModuleDynamicallyWrapper (node:internal/vm/module:437:15)
    at async Object.build (/Users/doe/Projects/front/node_modules/vite-ssg/dist/chunks/build.cjs:171:87)
    at async Object.handler (/Users/doe/Projects/front/node_modules/vite-ssg/dist/node/cli.cjs:25:3)

It would be nice to have an identified solution in the documentation to prevent code from running during SSG.

YannDuv commented 2 years ago

I got the same issue when working with crypto (from window.crypto). It's also a problem because i use it to generate ID in rendered content. Moreover, crypto should also be available in a node context.

userquin commented 2 years ago

check for window with if (typeof window !== 'undefined')

earlAchromatic commented 2 years ago

I am having this issue as well, even with tests for window. As far as I can tell, what is happening is that even if you conditionally/dynamically import a module, if that module has dependencies then those are getting bundled in somewhere (main.mjs for example) and when Vite runs the ssr if those dependencies make use of window it will throw an error.

It seems like this problem is what the mock option is for (simulating window, document, etc) but I have not had any luck with that either.

userquin commented 2 years ago

@YannDuv @earlAchromatic can you provide a minimal repro?

earlAchromatic commented 2 years ago

See repro here: https://github.com/earlAchromatic/vite-ssg-build-v-client

or use npx degit earlAchromatic/vite-ssg-build-v-client vite-ssg-repro

Details are in the README. Note that I did solve the issue on my end - it turned out to be library specific.

I left the repro in case you want to look anyways. I am still curious why mock doesn't work in the case where window is accessed on the server during build (though perhaps that is a separate issue).

userquin commented 2 years ago

@earlAchromatic I've found the problem with mock, it is being loaded after loading the app SSR entry and should be loaded before loading that entry (or building the SSR entry), and so the error is there: I've your repo working with ESM patching 3d-force-graph and three-forcegraph dependencies (we need to add "type": "module" to both packages since the extensions are not .mjs).

I'll check updating the vite-ssg::build.mjs manually loading jsdom before SSR entry...

3d-force-graph/package.json ```json "type": "module", "sideEffects": false, "exports": { ".": { "require": "./dist/3d-force-graph.common.js", "import": "./dist/3d-force-graph.module.js", "types": "./dist/3d-force-graph.d.ts" } }, ```
three-forcegraph/package.json ```json "type": "module", "sideEffects": false, "exports": { ".": { "require": "./dist/three-forcegraph.common.js", "import": "./dist/three-forcegraph.module.js", "types": "./dist/three-forcegraph.d.ts" } }, ```
userquin commented 2 years ago

it works (loading jsdom before SSR entry), but then I get some errors loading three dependency:

we also need to patch this another package:

three-render-objects/package.json ```json "type": "module", "sideEffects": false, "exports": { ".": { "require": "./dist/three-render-objects.common.js", "import": "./dist/three-render-objects.module.js", "types": "./dist/three-render-objects.d.ts" } }, ```

and then we got another error from @vueuse/core:

[vite-ssg] An internal error occurred.
[vite-ssg] Please report an issue, if none already exists: https://github.com/antfu/vite-ssg/issues
file:///F:/work/projects/quini/GitHub/issue-repro/vite-ssg-issues/vite-ssg-build-v-client-master/node_modules/.pnpm/@vueuse+core@8.3.1_vue@3.2.33/node_modules/@vueuse/core/index.mjs:500
      mediaQuery = window.matchMedia(query);
                          ^

TypeError: window.matchMedia is not a function
userquin commented 2 years ago

@earlAchromatic with your repo and applying previous patches, I can now use this on the src/pages/index.vue sfc (using ESM), we just need to patch/await useMediaQuery to be fixed:

<script setup>
import ForceGraph3D from '3d-force-graph'
onMounted(async () => {
  // const ForceGraph3D = await import('3d-force-graph')
  const N = 300
  const gData = {
    nodes: [...Array(N).keys()].map(i => ({ id: i })),
    links: [...Array(N).keys()]
      .filter(id => id)
      .map(id => ({
        source: id,
        target: Math.round(Math.random() * (id - 1)),
      })),
  }

  ForceGraph3D()(document.getElementById('3d-graph')).graphData(gData)
})
</script>

<template>
  <div id="3d-graph" />
</template>
userquin commented 2 years ago

A small fix allowing use useDark on src/composables/dark.ts module would be:

export const isDark = typeof window !== 'undefined' && typeof window.matchMedia !== 'undefined' ? useDark() : ref(false)
export const toggleDark = useToggle(isDark)

build just works:

F:\work\projects\quini\GitHub\issue-repro\vite-ssg-issues\vite-ssg-build-v-client-master>pnpm run build

> @ build F:\work\projects\quini\GitHub\issue-repro\vite-ssg-issues\vite-ssg-build-v-client-master
> vite-ssg build

[vite-ssg] Build for client...
vite v2.9.7 building for production...
✓ 370 modules transformed.
dist/index.html               0.83 KiB
dist/ssr-manifest.json        13.02 KiB
dist/assets/app.3b68aa3a.js   793.00 KiB / gzip: 220.18 KiB

(!) Some chunks are larger than 500 KiB after minification. Consider:
- Using dynamic import() to code-split the application
- Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/guide/en/#outputmanualchunks
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.

[vite-ssg] Build for server...
vite v2.9.7 building SSR bundle for production...
✓ 8 modules transformed.
.vite-ssg-temp/main.mjs   3.36 KiB

[vite-ssg] Rendering Pages... (1)
dist/index.html       1.22 KiB

[vite-ssg] Build finished.
userquin commented 2 years ago

I'll make a PR to fix it (loading jsdom before building or before loading SSR entry, I'm checking it right now)...

We also need to patch build.mjs, adding this before the SSR build:

imagen

and removing this:

imagen

imagen

userquin commented 2 years ago

@earlAchromatic here the same repo patching the deps to use ESM: also using a local vite-ssg loading the jsdom before SSR build, use pnpm install && pnpm run build && pnpm run preview (check the postinstall script on scripts/patch.ts module):

https://github.com/userquin/vite-ssg-build-v-client-master

earlAchromatic commented 2 years ago

@userquin Nice work! Thank you taking a deep dive into it. Patch script is great 😄

userquin commented 2 years ago

@earlAchromatic if you want to also use apollo with vite-ssg I have also a patch for it, just check https://github.com/userquin/vitesse-stackter-clean-architect, from #241