freddy38510 / quasar-app-extension-ssg

Static Site Generator App Extension for Quasar.
MIT License
148 stars 17 forks source link

`TypeError: Cannot read properties of undefined (reading 'get')` in `server-entry.js` during generation #375

Open renyuneyun opened 7 months ago

renyuneyun commented 7 months ago

While building static site, I encounter this error:

 App •  DONE  • SSR Client compiled with success • 8726ms

 App •  WAIT  • Compiling of SSR Server with Vite in progress...

(!) Experimental legacy.buildSsrCjsExternalHeuristics and ssr.format: 'cjs' are going to be removed in Vite 5.
    Find more information and give feedback at https://github.com/vitejs/vite/discussions/13816.

 App •  DONE  • SSR Server compiled with success • 1474ms

(node:149477) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
/home/ryey/coding/solid-fixer/node_modules/.cache/quasar-app-extension-ssg/server/server-entry.js:1
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("vue"),v=require("quasar"),_=require("vue/server-renderer"),R=require("solid-helper-vue"),q=require("process"),y=require("quasar/wrappers"),S=require("pinia"),m=require("vue-router");function g(t){const r=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(t){for(const e in t)if(e!=="default"){const o=Object.getOwnPropertyDescriptor(t,e);Object.defineProperty(r,e,o.get?o:{enumerable:!0,get:()=>t[e]})}}return r.default=t,Object.freeze(r)}const A=g(q);const d=t=>{const r=s.getCurrentInstance();if(!r||r.type.__isLazilyHydrated)return;const e=s.useSSRContext();if(!e||e._lazilyHydratedComponents&&e._lazilyHydratedComponents.has(t))return;let{parent:o}=r;for(;o;){if(o.type.__isLazilyHydrated){(e._lazilyHydratedComponents||(e._lazilyHydratedComponents=new Set)).add(t);return}o=o.parent}},l=s.defineComponent({__name:"App",__ssrInlineRender:!0,setup(t){return d("src/App.vue"),window.process=A,(r,e,o,a)=>{const u=s.resolveComponent("router-view");e(_.ssrRenderComponent(s.unref(R.SessionProvider),a,{default:s.withCtx((i,n,w,h)=>{if(n)n(_.ssrRenderComponent(u,null,null,w,h));else return[s.createVNode(u)]}),_:1},o))}}}),f=l.setup;l.setup=(t,r)=>{const e=s.useSSRContext();return(e.modules||(e.modules=new Set)).add("src/App.vue"),f?f(t,r):void 0};const p=y.store(()=>S.createPinia()),C=[{path:"/",component:()=>Promise.resolve().then(()=>require("./assets/MainLayout-92809b36.js")),children:[{path:"",component:()=>Promise.resolve().then(()=>require("./assets/IndexPage-9db3dec6.js"))},{path:"callback",component:()=>Promise.resolve().then(()=>require("./assets/LoginCallback-61b0dfed.js"))}]},{path:"/:catchAll(.*)*",component:()=>Promise.resolve().then(()=>require("./assets/ErrorNotFound-f2f0c029.js"))}],c=y.route(function(){const t=m.createMemoryHistory;return m.createRouter({scrollBehavior:()=>({left:0,top:0}),routes:C,history:t("/")})}),I=s.defineComponent({name:"AppWrapper",setup(t){return d(".quasar/app.js"),s.onMounted(()=>{const{proxy:{$q:r}}=s.getCurrentInstance();r.onSSRHydrated!==void 0&&r.onSSRHydrated()}),()=>s.h(l,t)}}),b=typeof window<"u"&&document.body.getAttribute("data-server-rendered")===null,P=async()=>(typeof c=="function"?await c():c).getRoutes();async function H(t,r,e){const o=t(I);o.use(v.Quasar,r,e);const a=typeof p=="function"?await p({ssrContext:e}):p;o.use(a),typeof window<"u"&&b!==!0&&window.__INITIAL_STATE__!==void 0&&(a.state.value=window.__INITIAL_STATE__,delete window.__INITIAL_STATE__);const u=s.markRaw(typeof c=="function"?await c({ssrContext:e,store:a}):c);return a.use(({store:i})=>{i.router=u}),{app:o,store:a,router:u}}const T={config:{}},{components:j,directives:E,...O}=T;async function z(t){const{app:r,router:e,store:o}=await H(s.createSSRApp,O,t);r.use(e);const{url:a}=t.req,u=e.resolve(a);if(u.fullPath!==a)throw{url:u.fullPath};if(u.matched.some(({path:n})=>/^\/:\w*\(\.\*\)\*?$/.test(n))){const n=new Error;throw n.code=404,n}if(await e.push(a),await e.isReady(),e.currentRoute.value.matched.filter(n=>n.components!==void 0).flatMap(n=>Object.values(n.components)).length===0){const n=new Error;throw n.code=404,n}return t.state=s.unref(o.state),r}exports.__quasar_ssg_vue3_lazy_hydration=d;exports.getRoutesFromRouter=P;exports.renderApp=z;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           ^

TypeError: Cannot read properties of undefined (reading 'get')
    at g (/home/ryey/coding/solid-fixer/node_modules/.cache/quasar-app-extension-ssg/server/server-entry.js:1:476)
    at Module.<anonymous> (/home/ryey/coding/solid-fixer/node_modules/.cache/quasar-app-extension-ssg/server/server-entry.js:1:557)
    at Module._compile (node:internal/modules/cjs/loader:1376:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1435:10)
    at Module.load (node:internal/modules/cjs/loader:1207:32)
    at Module._load (node:internal/modules/cjs/loader:1023:12)
    at Module.require (node:internal/modules/cjs/loader:1235:19)
    at require (node:internal/modules/helpers:176:18)
    at createRenderFn (/home/ryey/coding/solid-fixer/node_modules/quasar-app-extension-ssg/src/vite/ssg-create-render-fn.js:27:7)
    at SsgBuilder.generatePages (/home/ryey/coding/solid-fixer/node_modules/quasar-app-extension-ssg/src/vite/ssg-builder.js:83:64)

Node.js v21.2.0

I have set minify: false, in quasar.config.js, but as you can see, the error message is not very helpful on its own. Guess this code is from quasar-app-extension-ssg, which has been minified already.

The command I used and my environment is:

❯ npm run build:ssg

> solid-fixer@0.0.1 build:ssg
> quasar ssg generate

 App • Looking for Quasar App Extension "ssg" command "generate"
 App • Running "ssg" Quasar App Extension...
 App • Running "ssg" > "generate" command

 Build mode............. ssg
 Pkg quasar............. v2.14.1
 Pkg @quasar/app-vite... v1.7.0
 Pkg vite............... v4.5.0
 Pkg ssg................ v5.1.1
 Debugging.............. no
freddy38510 commented 7 months ago

I've just tested throwing an error from IndexPage.vue, using Node.js v21.2.0, and the stack trace is perfectly readable. The same applies when throwing an error from a Quasar boot file.

The error may come from an external library that has already been minified.

Could you try running the quasar ssg generate --debug command ?

As a last resort, please try the quasar ssg dev command.

The minify option is set here.

renyuneyun commented 6 months ago

Hi. Sorry for the delay. Thanks for providing the suggestions. quasar ssg generate --debug did not produce any difference. However, after quasar ssg dev, after opening the address in browser, I got the following error:

22:51:40 [vite] Error when evaluating SSR module /src/layouts/MainLayout.vue: failed to import "@renyuneyun/solid-helper"
|- TypeError: Cannot destructure property 'AbortController' of '(intermediate value)(intermediate value)(intermediate value)' as it is undefined.
    at file:///home/ryey/coding/solid-fixer/node_modules/@renyuneyun/solid-helper/dist/solid-helper.mjs:18300:26
    at ModuleJob.run (node:internal/modules/esm/module_job:218:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
    at async nodeImport (file:///home/ryey/coding/solid-fixer/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:56097:17)
    at async ssrImport (file:///home/ryey/coding/solid-fixer/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:55990:24)
    at async eval (/home/ryey/coding/solid-fixer/src/layouts/MainLayout.vue:8:31)
    at async instantiateModule (file:///home/ryey/coding/solid-fixer/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:56052:9)

[Vue Router warn]: uncaught error during route navigation:
TypeError: Cannot destructure property 'AbortController' of '(intermediate value)(intermediate value)(intermediate value)' as it is undefined.
    at file:///home/ryey/coding/solid-fixer/node_modules/@renyuneyun/solid-helper/dist/solid-helper.mjs:18300:26
    at ModuleJob.run (node:internal/modules/esm/module_job:218:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
    at async nodeImport (file:///home/ryey/coding/solid-fixer/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:56097:17)
    at async ssrImport (file:///home/ryey/coding/solid-fixer/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:55990:24)
    at async eval (/home/ryey/coding/solid-fixer/src/layouts/MainLayout.vue:8:31)
    at async instantiateModule (file:///home/ryey/coding/solid-fixer/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:56052:9)

 App • ⚠   Render failed  /
22:51:40 [vite] Error when evaluating SSR module /src/utils/profile-teller.ts: failed to import "@renyuneyun/solid-helper"
|- TypeError: Cannot destructure property 'AbortController' of '(intermediate value)(intermediate value)(intermediate value)' as it is undefined.
    at file:///home/ryey/coding/solid-fixer/node_modules/@renyuneyun/solid-helper/dist/solid-helper.mjs:18300:26
    at ModuleJob.run (node:internal/modules/esm/module_job:218:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
    at async nodeImport (file:///home/ryey/coding/solid-fixer/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:56097:17)
    at async ssrImport (file:///home/ryey/coding/solid-fixer/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:55990:24)
    at async eval (/home/ryey/coding/solid-fixer/src/layouts/MainLayout.vue:8:31)
    at async instantiateModule (file:///home/ryey/coding/solid-fixer/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:56052:9)

22:51:40 [vite] Error when evaluating SSR module /src/components/ProfileStatus.vue: failed to import "/src/utils/profile-teller.ts"
|- TypeError: Cannot destructure property 'AbortController' of '(intermediate value)(intermediate value)(intermediate value)' as it is undefined.
    at file:///home/ryey/coding/solid-fixer/node_modules/@renyuneyun/solid-helper/dist/solid-helper.mjs:18300:26
    at ModuleJob.run (node:internal/modules/esm/module_job:218:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
    at async nodeImport (file:///home/ryey/coding/solid-fixer/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:56097:17)
    at async ssrImport (file:///home/ryey/coding/solid-fixer/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:55990:24)
    at async eval (/home/ryey/coding/solid-fixer/src/layouts/MainLayout.vue:8:31)
    at async instantiateModule (file:///home/ryey/coding/solid-fixer/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:56052:9)

22:51:40 [vite] Error when evaluating SSR module /src/pages/IndexPage.vue: failed to import "/src/components/ProfileStatus.vue"
|- TypeError: Cannot destructure property 'AbortController' of '(intermediate value)(intermediate value)(intermediate value)' as it is undefined.
    at file:///home/ryey/coding/solid-fixer/node_modules/@renyuneyun/solid-helper/dist/solid-helper.mjs:18300:26
    at ModuleJob.run (node:internal/modules/esm/module_job:218:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
    at async nodeImport (file:///home/ryey/coding/solid-fixer/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:56097:17)
    at async ssrImport (file:///home/ryey/coding/solid-fixer/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:55990:24)
    at async eval (/home/ryey/coding/solid-fixer/src/layouts/MainLayout.vue:8:31)
    at async instantiateModule (file:///home/ryey/coding/solid-fixer/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:56052:9)

file:///home/ryey/coding/solid-fixer/node_modules/@renyuneyun/solid-helper/dist/solid-helper.mjs:18300
const { AbortController: T9, AbortSignal: VW } = typeof self < "u" ? self : typeof window < "u" ? window : (
                         ^

TypeError: Cannot destructure property 'AbortController' of '(intermediate value)(intermediate value)(intermediate value)' as it is undefined.
    at file:///home/ryey/coding/solid-fixer/node_modules/@renyuneyun/solid-helper/dist/solid-helper.mjs:18300:26
    at ModuleJob.run (node:internal/modules/esm/module_job:218:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
    at async nodeImport (file:///home/ryey/coding/solid-fixer/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:56097:17)
    at async ssrImport (file:///home/ryey/coding/solid-fixer/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:55990:24)
    at async eval (/home/ryey/coding/solid-fixer/src/layouts/MainLayout.vue:8:31)
    at async instantiateModule (file:///home/ryey/coding/solid-fixer/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:56052:9)

Node.js v21.2.0

That @renyuneyun/solid-helper library is mine, so I can try to debug more. However, I have no idea where this AbortController is from, so that could take some while.

Anyway, this looks different from the previous error, as one is for the missing property get while the other is for object destruction?

freddy38510 commented 6 months ago

I tried to add abort-controller package to the Rollup external option in the vite config file of your @renyuneyun/solid-helper package, and it fixes the issue.

But making the abort-controller package external could break the library when used in umd format. To solve this problem, you can create two vite.config.ts files, one for the esm format and a second for the umd format.

Apart from this issue, you'll come across other errors linked to the use of web functionalities not available on nodejs. Such as the window global variable. Perhaps you should read this: https://quasar.dev/quasar-cli-vite/developing-ssr/writing-universal-code

renyuneyun commented 6 months ago

Thanks for the information. Indeed I'm aware of the difference between frontend and backend (node), and am trying my best to not running into problems related to that (i.e. I'm trying to write a front-end application as website only); however, due to my limited experience with JS, my practice may very likely miss things.

I managed to solve the abort-controller issue and related ones (e.g. process.nextTick not found), as can be found in the updated vite config (in dev branch). But am stuck at two issues, in separate sections below.

window missing

Immediately after fixing the previous issues, I encountered issues related to window global variable when running quasar ssg generate --debug (I can confirm normal quasar build works):

 App •  DONE  • SSR Server compiled with success • 5941ms

 App •  WAIT  • Initializing route(s) in progress...
 App •  INFO  • The crawler feature will attempt to find dynamic routes
 App •  DONE  • 2 route(s) initialized with success • 3ms

 App •  WAIT  • Generating Pages in progress...

 App • ⚠   Failed to pre-render: "/"  ReferenceError: window is not defined
    at setup (/home/ryey/coding/solid-fixer/src/pages/IndexPage.vue:17:1)
    at _sfc_main.setup (/home/ryey/coding/solid-fixer/src/pages/IndexPage.vue:20:17)
    at callWithErrorHandling (/home/ryey/coding/solid-fixer/node_modules/@vue/runtime-core/dist/runtime-core.cjs.prod.js:18:18)
    at setupStatefulComponent (/home/ryey/coding/solid-fixer/node_modules/@vue/runtime-core/dist/runtime-core.cjs.prod.js:5890:25)
    at setupComponent (/home/ryey/coding/solid-fixer/node_modules/@vue/runtime-core/dist/runtime-core.cjs.prod.js:5877:36)
    at renderComponentVNode (/home/ryey/coding/solid-fixer/node_modules/@vue/server-renderer/dist/server-renderer.cjs.prod.js:354:15)
    at renderVNode (/home/ryey/coding/solid-fixer/node_modules/@vue/server-renderer/dist/server-renderer.cjs.prod.js:483:14)
    at renderComponentSubTree (/home/ryey/coding/solid-fixer/node_modules/@vue/server-renderer/dist/server-renderer.cjs.prod.js:438:7)
    at renderComponentVNode (/home/ryey/coding/solid-fixer/node_modules/@vue/server-renderer/dist/server-renderer.cjs.prod.js:371:12)
    at renderVNode (/home/ryey/coding/solid-fixer/node_modules/@vue/server-renderer/dist/server-renderer.cjs.prod.js:483:14)

Such as the window global variable.

Apologize but I do not understand why this would be a problem, because what I'm generating is a static (web)site? Running as a website, surely rendered in browser, must have a window object, isn't it?

Circular object

After removing all the relevant code using window (i.e. those creating const referencing window), I got the following error (still from quasar ssg generate --debug):

 App •  WAIT  • Generating Pages in progress...

 App • ⚠   Failed to pre-render: "/"  TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'Object'
    --- property 'events' closes the circle
    at JSON.stringify (<anonymous>)
    at serialize (/home/ryey/coding/solid-fixer/node_modules/serialize-javascript/index.js:195:20)
    at renderStoreState (/home/ryey/coding/solid-fixer/node_modules/quasar-app-extension-ssg/src/vite/ssg-create-render-fn.js:77:19)
    at PagesGenerator.render (/home/ryey/coding/solid-fixer/node_modules/quasar-app-extension-ssg/src/vite/ssg-create-render-fn.js:120:37)
    at async #renderPage (/home/ryey/coding/solid-fixer/node_modules/quasar-app-extension-ssg/src/vite/PagesGenerator.js:288:14)
    at async PagesGenerator.generatePage (/home/ryey/coding/solid-fixer/node_modules/quasar-app-extension-ssg/src/vite/PagesGenerator.js:240:16)
    at async PagesGenerator.<anonymous> (/home/ryey/coding/solid-fixer/node_modules/quasar-app-extension-ssg/src/vite/PagesGenerator.js:75:38)

This time I have no little idea. Running npm run build:ssg also leads to this (instead of the very original error in the title). Could this be somehow related to the dependency that I modified (i.e. @renyuneyun/solid-helper)? After further investigation (by commenting and uncommenting parts of the code), I found this is related to a Pinia store: whenever I refer to the session store, this error emerges. Later I found out that if a) I copy all relevant code from the library (my other library) to this project, and b) I install the dependency @inrupt/solid-client-authn-browser, this error do not emerge, and the project builds successfully. However, if either of these does not meet, it happens. (After further modifications, only explicitly installing the dependency is necessary. I'm not entirely sure why, but this could be caused by the fact that I removed the same dependency from another library. More information in #379.) It seems to be an issue related to js library packaging in the end... But hope the error message from quasar-app-extension-ssg be more readable.

About UMD and external

This is probably not an immediate issue, but may affect in the future, so I'm still asking in case there is a quick answer. Please ignore this part if that's not the case.

But making the abort-controller package external could break the library when used in umd format. To solve this problem, you can create two vite.config.ts files, one for the esm format and a second for the umd format.

Thanks for this suggestion. If you have time, could you point out some resources explaining / mentioning this issue? I tried to search things like umd external vite, but did not find any useful information.

freddy38510 commented 6 months ago

Apologize but I do not understand why this would be a problem, because what I'm generating is a static (web)site? Running as a website, surely rendered in browser, must have a window object, isn't it?

SSG is like SSR but at build time. This means that each page is pre-rendered server-side, with the result then written to an html file. When the app runs in browser (client-side), the static markup is hydrated by Vue to make it interactive.

So, when you need to perform conditional logic based on SSR vs. client, you can use process.env.SERVER and/or process.env.CLIENT

if (process.env.CLIENT) {
  // client-only logic

  import('my-fancy-npm-package').then(package => {
    // notice "default" below, which is the prop with which
    // you can access what your npm imported package exports
    package.default.doSomething()
  })

  // access window global variable
  console.log(window)
}

You can also access the window variable inside Vue client-only lifecycle hooks. For example onMounted or onUpdated will NOT be called during SSR and will only be executed on the client.

Quasar boot files can also be executed only at client or server side:

boot: [
  {
    server: false, // run on client-side only!
    path: '<name>' // references /src/boot/<name>.js
  },
  {
    client: false, // run on server-side only!
    path: '<name>' // references /src/boot/<name>.js
  }
]

If you have components that are difficult to adapt to the server, you can use the Quasar No SSR component to run them client-side only.


About your library, the Rollup external option is used for deps that should remain external to the bundle.

It is fine to make the abort-controller dep external when your library runs at server-side, because nodejs know where to find and import the package (node_modules folder). But at client-side, the dep should be available in the bundle.

So, contrary to what I said, it's not specific to the UMD format. In fact, the issue with this specific package is not strictly about making it external or not. When you compile and bundle your library with its deps, Vite imports the browser.js entry file of the abort-controller package from where the error is thrown when executed at server-side.

From the abort-controller documentation:

If your bundler recognizes browser field of package.json, the imported AbortController is the native one and it doesn't contain shim (even if the native implementation was nothing).

By default Vite will use the browser field first when resolving the abort-controller's entry point.

I don't have much time to look for a suitable solution to this problem, but I hope that from now on you'll have a better understanding of what it's all about.

renyuneyun commented 6 months ago

Thanks very much for the patient and detailed explanation. That now makes a lot of sense. So SSR "executes" the scripts (e.g. a Vue component which contains root-level references to window such as <script>const a = window.location</script>) at building/generation time, during when the environment is a node environment, which does not have window. And SSG by this extension uses the same first step as SSR, but saves the generated page. That is where the error appeared. I was not aware of this and did not grasp this idea from the relevant words in the documents back then. I thought the building was just converting/transcompiling the Vue component as a pure JS file, but not executing it. Thanks a lot for the clarification.

And thanks for the information about abort-controller. I'll do further tests to verify my code, and, if something goes wrong, try to find a solution.

freddy38510 commented 6 months ago

That's right. You can read the documentation on SSR and SSG directly on the Vue website, it contains better explanations than mine.

I've got some rewritten documentation in the pipeline and a few features, but I'm running out of time these days to deliver them properly.

Thanks for the feedback, it's always very useful !