Git Product home page Git Product logo

vite-ssr's Issues

Base path

Hi,
is there possibility to use this configuration https://vitejs.dev/config/#base to configure base path?

In my case it is not working if I use this in vite.config.ts with this configuration: "build": "node node_modules/vite-ssr/cli.js build",

Add built-in production server and expose server APIs

It doesn't make sense to re-implement production server every time.

Similarly to vite preview, there could be vite-ssr preview that runs a local web server.

Also, there should be lower-level exports (say in vite-ssr/server) for:

  • renderHtml (so that the end user doesn't need to deal with importing from dist folder, suppress type warnings, etc.)
  • createApp or something along the lines, an Express/Connect app that doesn't listen by itself but could be injected into own http server

ssr-manifest.json should be stored in dist/server

I believe ssr-manifest.json is logically a server dependency and thus should rather be stored in dist/server.

Let's say I want to deploy a SSR server as a micro-service (and publish assets at CDN).
For the micro-service, I will need the entire dist/server folder and for some reason one file from client (which is not used by the client itself). How so?

bootsrap

how can I load bootrap 5 in client only?

SPA mode: deserialization error

The documentation says one can use SPA mode:

SPA mode: vite dev command runs Vite directly without any SSR.

However this results in deserialization error:

state.js:27 [SSR] On state deserialization - SyntaxError: Unexpected token u in JSON at position 0
    at JSON.parse (<anonymous>)
    at deserializeState (state.js:24)
    at viteSSR2 (entry-client.js:20)
    at main.ts:6 undefined

Change routes, based on locale detected in base method (way to localize paths)

Hi,

Not sure it's the right place but I'm taking a chance here since it may generate ideas for ways to implement this (in a smart way that I'm tring to) in the future.

I'm trying to give to ViteSSR different routes based on localization.

First idea was to try to set the routes (with router.addroute()) inside of the base method inside ViteSSR export, but it's too late to edit the routes, it absolutely has to happen before.

Then I tried editing the routes in the async(ctx) => {}, and it's working but for the SSR part only, because on hydration, the page disappears. I assume it's because SSR and Vue hydration don't have the same routes to work with.

I'm still learning Vue and I'm sorry if some things seems obvious. I'm using the boilerplate by the author of this package to help me getting started: https://github.com/frandiox/vitesse-ssr-template

Here is what I got to:

export default viteSSR(
   App,
   {
     // Use Router's base for i18n routes
     base: ({ url }) => {
       const locale = extractLocaleFromPath(url.pathname)
       return locale === DEFAULT_LOCALE ? '/' : `/${locale}/`
     },
     routes,
   },
   async(ctx) => {
     // install all modules under `modules/`
     Object.values(import.meta.globEager('./modules/*.ts')).map(i =>
       i.install?.(ctx),
     )
 
     const { app, url, router, isClient, initialState, initialRoute } = ctx
     setLocalizedRoutes(router)
...

setLocalizedRoutes() adds the localized routes to the router (and works, because router.getRoutes() gives me the good localized routes juste under).

I tried setting up before ViteSSR export, but router.currentRoute is always set at / and no way to check the language from URL then, and it makes sense it's not set yet.

Would there be a way to get routes based on language to work in that context? I don't mind losing the instant translation on language switching.

Any help would be welcome, thank you so so much.

Module Node Server Export

We need a module export option for node applications that runs with mjs.

For example I have a fastify server with module system and I cannot import dist/server/main.js becauase it uses require.

There can be an option like this for module nodejs or vite-ssr can bundle another file as main.mjs

plugins: [
   ssr({
     build: {
        esm: true
     }
   })
]

Any way to make a server local variable for every request?

Hello πŸ‘‹ I want to create a variable, that is from the initialState, but is local for only one request.

In this example, I have a stack, that adds and pops numbers. The value is coming from the initialState. Everything works fine, except, that the initialState stays the same for every page refresh. Is there a way to define a "local" kinda variable?

import { useContext } from 'vite-ssr'
import { ref, readonly } from 'vue'

const state = ref<number[]>([])

export function useStack() {
  const context = useContext()

  if (context.isClient) {
    state.value = context.initialState.stack || []
  }

  return {
    add(n: number) {
      state.value.push(n)
      context.initialState.stack = state.value
    },

    pop() {
      state.value.pop()
      context.initialState.stack = state.value
    },

    get state() {
      return readonly(state.value)
    },
  }
}
<script lang="ts" setup>
import { onMounted, onServerPrefetch } from 'vue'
import { useStack } from './composables'

const stack = useStack()

onMounted(() => {
  console.log(stack.state) // expect to be [1] on every request, but get [1, ...1 * n request]
})

onServerPrefetch(() => {
  stack.add(1)
})
</script>

<template>
  <pre>{{ stack.state }}</pre>
</template>

how to use nested route in react-router

It only resolve root of the path pages, but can't resolve some page in child folder.

.pages
β”œβ”€β”€ About.jsx
β”œβ”€β”€ Home.jsx
β”œβ”€β”€ note           
β”‚   β”œβ”€β”€ api.jsx
β”‚   └── index.jsx        // can't resolve
└── notes
    └── :id.jsx

Use router instance in some composable or any other external file

Hi,

maybe I am missing something, but currently the router instance is only getting returned inside the viteSSR HookParams.

What if I want to use some kind of axios interceptor, in order to automatically redirect the user to some route in case of a 401 for example.

Also I am thinking about implementing an auth composable in which there should be a login and logout method. These need to be able to redirect the user to some routes.

How would you build this kind of thing? Do I really have to build everything router related inside the hook function?

Thank your very much in advance for all your work and effort.

Don't remove `client/index.html` during build

vite-ssr build removes index.html from the client bundle:

vite-ssr/src/build/index.ts

Lines 113 to 119 in f376c92

// index.html file is not used in SSR and might be
// served by mistake, so let's remove it to avoid issues.
await fs
.unlink(
path.join(clientBuildOptions.build?.outDir as string, 'index.html')
)
.catch(() => null)

I was not expecting this behaviour as a normal Vite SSR build would include the index.html in the client bundle.

For my purposes, I do want the index.html in my client bundle, since I am only selectively using SSR on a few routes on my site, and serving a regular SPA for the rest.

Would it be possible to remove this behaviour from vite-ssr? If the intention is to provide a easy to use SSR plugin for Vite users, I feel that it would be better if vite-ssr's behaviour does not deviate too much from Vite's own SSR support. I would also be happy with a vite-ssr CLI flag to retain index.html!

Happy to submit a PR for either option. Thanks again for this easy-to-use package!

Build error with plugin legacy

If you use @vitejs/plugin-legacy in the project and try to build server, this error occures

A:\Desktop\VUE\kyzc-portfolio\node_modules\.pnpm\[email protected]_1875ae38a9ea4201aa9b0ba5cafe20a3\node_modules\vite-ssr\build\index.js:20
    const indexHtml = clientResult.output.find((file) => file.type === 'asset' && file.fileName === 'index.html');
                                          ^

TypeError: Cannot read property 'find' of undefined
    at module.exports (A:\Desktop\VUE\kyzc-portfolio\node_modules\.pnpm\[email protected]_1875ae38a9ea4201aa9b0ba5cafe20a3\node_modules\vite-ssr\build\index.js:20:43)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async A:\Desktop\VUE\kyzc-portfolio\node_modules\.pnpm\[email protected]_1875ae38a9ea4201aa9b0ba5cafe20a3\node_modules\vite-ssr\cli.js:19:9
 ERROR  Command failed with exit code 1.

Internal server error: Unexpected token '.'

Hi @frandiox, thanks for such an awesome library.

I'm currently getting the following error when I try to declare and use Vuex store

[vite] Error when evaluating SSR module /src/store/store.getters.ts:
SyntaxError: Unexpected token '.'
    at new Function (<anonymous>)
    at instantiateModule (/Users/xxx/.../node_modules/.pnpm/[email protected]/node_modules/vite/dist/node/chunks/dep-6b5f3ba8.js:68222:9)
SyntaxError: Unexpected token '.'
    at new Function (<anonymous>)
    at instantiateModule (/Users/xxx/.../node_modules/.pnpm/[email protected]/node_modules/vite/dist/node/chunks/dep-6b5f3ba8.js:68222:9)
3:38:59 PM [vite] Internal server error: Unexpected token '.'

store.getters.ts

import { GetterTree } from 'vuex'
import { StoreState } from './store.state'

const getters: GetterTree<StoreState, StoreState> = {
  isAuthenticated(state) {
    return !!state.user?.uid
  }
}

export default getters

If I removed the optional chaining state.user?.uid and did state.user && state.user.uid it works alright. This has caused me to dynamically import createStore and initialize the store when not in SSR mode

const unwrapStore = () => import('./store/store.index')

export default viteSSR(
  App,
  { routes, base: () => '/' },
  async ({ app, router, initialState, initialRoute }) => {
  
   if (!import.meta.env.SSR) {
      const r = await unwrapStore()
 	  const store = r.default()
      app.use(store, r.key)
      store.replaceState(initialState)
    }
})

Now the problem is that I can't use the store in setup() in SSR mode. So the following will either fail or throw an error cannot reference commit of undefined

setup() {
  const store = useStore()

  store.commit("ACTION", payload)
}

Extract Apollo cache for SSR

Hello,

I'm trying to use Apollo Client with SSR and React.
My idea was to create a InMemoryCache and extract data from the store. It seams that viteSSR does not wait the resolution of a query. If I add a setTimeout to the hook of viteSSR, I cant print the good content of the store.
According the Apollo Documentation, I have to use renderToStringWithData. But I have no idea how I can manage with vite-ssr plugin. Is it possible?

Thank you very much.

./server/index.js

// This is the server renderer we just built
const main = import('../dist/server/main.js');

server.get('*', async (req, res) => {
  const url = `${req.protocol}://${req.get('host')}${req.originalUrl}`;
  const renderPage = (await main).default.default;

  const apolloCache = new InMemoryCache();

  const { html } = await renderPage(url, {
    manifest,
    apolloCache,
    preload: true,
  });

  res.setHeader('Cache-Control', 'max-age=0');
  res.end(html);
});

./src/App.jsx

const App = ({
  isClient,
  apolloCache = new InMemoryCache(),
}) => {
  const client = new ApolloClient({
    link: createHttpLink({
      uri: 'http://localhost:8080/graph',
      credentials: 'same-origin',
    }),
    ssrMode: !isClient,
    cache: apolloCache,
    credentials: 'same-origin',
  });
...

./src/main.jsx

export default viteSSR(App, { routes }, ({
  initialState,
  apolloCache,
}) => {
  if (import.meta.env.SSR) {
    setTimeout(() => {
      console.log(apolloCache.extract()) // not empty
    }, 1000)
    initialState.apolloCache = apolloCache.extract();
  }
  // Custom initialization hook
})

[v0.10.6] `request` & `response` not available in entry hook when importing from `vite-ssr/vue`

Hey!

After I've upgraded from v0.10.5 to v0.10.6, TypeScript no longer knows that the request & response properties exist in the entry hook.

import viteSSR from 'vite-ssr/vue'

export default viteSSR(
  App,
  {
    routes,
    // This works fine now
    pageProps: {
      passToPage: false,
    },
  },
  // Property `request`/`response` does not exist on type ...
  ({ app, initialState, router, request, response }) => {
    // ...
  }
)

Template refs not available in ClientOnly

When a template ref is applied to a VNode slotted in ClientOnly, the ref is never available

Example

<!-- This template won't work -->
<template lang="pug">
.relative: ClientOnly: .absolute.inset-0(ref="foo")
</template>

<!-- This template works -->
<template lang="pug">
.relative: .absolute.inset-0(ref="foo")
</template>

<!-- Log ref in watcher & mount hook -->
<script setup lang="ts">
import { onMounted, ref, watch } from 'vue'

const foo = ref<HTMLDivElement | null>(null)

watch(foo, x => console.info(x))
onMounted(() => console.info(foo?.value))
</script>

package.json for afffected project

{
  "private": true,
  "engines": {
    "node": ">=14"
  },
  "scripts": {
    "bi": "bi --pm pnpm",
    "fetch": "node ./scripts/fetch-metadata",
    "gen": "node ./scripts/generate-entities",
    "dev": "vite-ssr --port 3333 --open",
    "dev:spa": "vite --port 3333 --open",
    "clean": "rm -rf dist; rm -rf api/renderer",
    "build": "pnpm clean; cross-env NODE_ENV=production vite-ssr build; ./scripts/move-dist.sh",
    "preview": "vc dev"
  },
  "dependencies": {
    "@replygirl/tc": "^3.0.0",
    "@vue/reactivity": "^3.0.11",
    "@vue/server-renderer": "^3.0.11",
    "@vueuse/core": "^4.9.3",
    "@vueuse/head": "^0.5.1",
    "axios": "^0.21.1",
    "breeze-client": "^2.1.1",
    "change-case": "^4.1.2",
    "element-plus": "^1.0.2-beta.44",
    "graphql": "^15.5.0",
    "hash-sum": "^2.0.0",
    "node-fetch": "^2.6.1",
    "nprogress": "^0.2.0",
    "pluralize": "^8.0.0",
    "prism-theme-vars": "^0.2.2",
    "querystring": "^0.2.1",
    "vite-ssr": "^0.8.1",
    "vue": "^3.0.11",
    "vue-i18n": "^9.1.6",
    "vue-router": "^4.0.6",
    "vue3-google-map": "^0.7.6"
  },
  "devDependencies": {
    "@antfu/eslint-config": "^0.6.5",
    "@iconify/json": "^1.1.341",
    "@intlify/vite-plugin-vue-i18n": "^2.1.2",
    "@replygirl/curse": "^3.0.0-beta.0",
    "@types/apple-mapkit-js-browser": "^5.50.0",
    "@types/axios": "^0.14.0",
    "@types/btoa": "^1.2.3",
    "@types/dotenv-flow": "^3.1.0",
    "@types/eslint": "^7.2.10",
    "@types/eslint-config-prettier": "^6.11.0",
    "@types/eslint-plugin-prettier": "^3.1.0",
    "@types/hash-sum": "^1.0.0",
    "@types/node": "^14.14.43",
    "@types/node-fetch": "^2.5.10",
    "@types/nprogress": "^0.2.0",
    "@types/pluralize": "^0.0.29",
    "@types/prettier": "^2.2.3",
    "@types/pug": "^2.0.4",
    "@types/rimraf": "^3.0.0",
    "@typescript-eslint/eslint-plugin": "^4.23.0",
    "@vercel/node": "^1.10.0",
    "@vitejs/plugin-vue": "^1.2.2",
    "@vue/compiler-sfc": "^3.0.11",
    "better-install": "^0.1.0",
    "breeze-entity-generator": "^1.0.0",
    "btoa": "^1.2.1",
    "cross-env": "^7.0.3",
    "dotenv-flow": "^3.2.0",
    "eslint": "^7.26.0",
    "eslint-config-prettier": "^8.3.0",
    "eslint-plugin-prettier": "^3.4.0",
    "markdown-it-prism": "^2.1.6",
    "pnpm": "^6.3.0",
    "prettier": "2.3.0",
    "pug": "^3.0.2",
    "rimraf": "^3.0.2",
    "ts-node": "^9.1.1",
    "type-fest": "^1.1.1",
    "typescript": "^4.2.4",
    "vite": "^2.3.0",
    "vite-plugin-components": "^0.9.0",
    "vite-plugin-icons": "^0.5.0",
    "vite-plugin-md": "^0.6.6",
    "vite-plugin-pages": "^0.11.1",
    "vite-plugin-pwa": "^0.7.3",
    "vite-plugin-vue-layouts": "^0.3.1",
    "vite-plugin-windicss": "^0.15.10",
    "vite-ssg": "^0.10.0"
  }
}

Outdir option not working

When you have outDir option in vite.config.ts vite-ssr doesn't follow the rule and bundles every file to dist in main folder.

Exports and export assignments are not permitted in module augmentations

Hi and thanks for what looks to be a very promising library

node_modules/vite-ssr/index.d.ts:21:3 - error TS2666: Exports and export assignments are not permitted in module augmentations.

Got this while linting the project with vue-tsc --noEmit

// This is a generic mix of framework types
import type { SharedContext, SharedOptions } from './utils/types'

declare module 'vite-ssr' {
  const handler: (
    App: any,
    options: SharedOptions & {
      routes: Array<Record<string, any>>
      routerOptions?: Record<string, any>
      styleCollector?: (ctx: any) => any
    },
    hook?: (
      params: SharedContext & {
        app: any
        router: any
        initialRoute: any
      }
    ) => any
  ) => any

  **export** default handler
  export const ClientOnly: any
  export const useContext: () => SharedContext
}

Updated export default handler to

  export default function (
      App: any,
      options: SharedOptions & {
          routes: Array<Record<string, any>>
          routerOptions?: Record<string, any>
          styleCollector?: (ctx: any) => any
      },
      hook?: (
          params: SharedContext & {
              app: any
              router: any
              initialRoute: any
          }
      ) => any
  ): any

And it stopped complaining.
Unfortunately, my skills in JS/TS only allow me to do so much -- I am completely clueless how to fix that so that the built version of the library on NPM will generate the proper .d.ts file

Anyone any ideas?

Server-side redirect

vite-ssr currently lacks any reasonable option to perform a server-side redirect.

Even though response object is passed in context, one can't use it like:

export default viteSSR(App, { routes }, ({ response }) => {
  // Simplified version
  // In real code, that could be injected/provided as a consumable hook for Vue pages
  response.setHeader("location", "/foo")
  response.statusCode = 302
  response.end()
})

because vite-ssr will always try to end it itself:

vite-ssr/src/dev/server.ts

Lines 103 to 107 in d758af9

const htmlParts = await render(url, { request, response, ...context })
const html = buildHtmlDocument(template, htmlParts)
response.setHeader('Content-Type', 'text/html')
response.end(html)

Suggestions:

  • at the very least, wrap the 3 last 3 lines like:
  if (!response.writableEnded) {
     const html = buildHtmlDocument(template, htmlParts)
     response.setHeader('Content-Type', 'text/html')
     response.end(html)
  }
  • or rather, allow to inject status, statusText, headers and body not only from options.getRenderContext but also from viteSSR.

Dynamic imports doesn't work in dev:SSR mode

This works:

setup() {
   const component = defineAsyncComponent(() => import(`../HelloWorld.vue`));
   return { component }; 
}

This doesn't

setup() {
    const name = "HelloWorld";
    const component = defineAsyncComponent(() => import(`../${name}.vue`));
    return { component };
}

Error:

Error: failed to load module for ssr: ../HelloWorld.vue
    at instantiateModule (C:\Projects\vite-ssr\node_modules\vite\dist\node\chunks\dep-2c03f3f9.js:69022:15)

It works when u first load another page then navigate over VueRouter / Dev:SPA also build seems to work.

Way to pass router configuration [VUE]

Hi - great package!

What do you think about adding the possibility to pass router configuration to the viteSSR? Right now it's not possible to leverage other configuration options like scrollBehavior or linkActiveClass.

Happy to work sth out and submit a PR as well.

Hard crash (process exit), something causes importing client entry during SSR

This is a spin-off of #43 where I incorrectly identified the problem at first.

Problem

In a small reproducible case, something causes importing client entry during SSR, which leads to hard crash (process exit). I had this on a larger project, but then simplified the setup to bare minimum for the reproduction.

Reproduction

https://github.com/IlyaSemenov/vite-ssr-crash

~/tmp/4
❯ git clone https://github.com/IlyaSemenov/vite-ssr-crash.git
Cloning into 'vite-ssr-crash'...
remote: Enumerating objects: 26, done.
remote: Counting objects: 100% (26/26), done.
remote: Compressing objects: 100% (17/17), done.
remote: Total 26 (delta 5), reused 25 (delta 4), pack-reused 0
Receiving objects: 100% (26/26), 13.52 KiB | 157.00 KiB/s, done.
Resolving deltas: 100% (5/5), done.

~/tmp/4
❯ cd vite-ssr-crash

~/tmp/4/vite-ssr-crash master
❯ nodenv local 16.0.0

~/tmp/4/vite-ssr-crash master
❯ yarn
yarn install v1.22.10
[1/4] πŸ”  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] πŸ”—  Linking dependencies...
warning "vite-ssr > @rollup/[email protected]" has unmet peer dependency "rollup@^1.20.0 || ^2.0.0".
warning "vite-ssr > [email protected]" has unmet peer dependency "react@^16.8.0 || ^17.0.0".
warning "vite-ssr > @rollup/plugin-replace > @rollup/[email protected]" has unmet peer dependency "rollup@^1.20.0||^2.0.0".
warning Workspaces can only be enabled in private projects.
[4/4] πŸ”¨  Building fresh packages...
✨  Done in 1.69s.

~/tmp/4/vite-ssr-crash master
❯ yarn dev
yarn run v1.22.10
$ vite-ssr
Pre-bundling dependencies:
  vite-ssr
  vue
  vite-ssr/vue/entry-client
  vite-ssr/vue/entry-server
(this will be run only when your dependencies or config have changed)

  vite v2.3.8 dev server running at:

  > Local: http://localhost:3000/
  > Network: use `--host` to expose

 -- SSR mode

>>>>>>>>>>>>>> (in a separate console) curl localhost:3000

[Function (anonymous)]
TypeError: render is not a function
    at handleSsrRequest (/Users/semenov/tmp/4/vite-ssr-crash/node_modules/vite-ssr/dev/server.js:88:37)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
12:57:31 AM [vite] Internal server error: render is not a function
      at handleSsrRequest (/Users/semenov/tmp/4/vite-ssr-crash/node_modules/vite-ssr/dev/server.js:88:37)
      at processTicksAndRejections (node:internal/process/task_queues:96:5)
/node_modules/vite-ssr/vue/entry-client.js:20
    const url = window.location;
                ^

ReferenceError: window is not defined
    at Module.viteSSR [as default] (/node_modules/vite-ssr/vue/entry-client.js:20:17)
    at eval (/Users/semenov/tmp/4/vite-ssr-crash/src/main.ts:12:61)
    at instantiateModule (/Users/semenov/tmp/4/vite-ssr-crash/node_modules/vite/dist/node/chunks/dep-0ed4fbc0.js:69982:166)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

The two key points are:

  • non-empty alias section in vite config
  • importing (not even using - just importing and accessing) onServerPrefetch

UPDATE:

I came up with even simpler version without onServerPrefetch, which crashes the same way.

https://github.com/IlyaSemenov/vite-ssr-crash/tree/no_routes

git clone https://github.com/IlyaSemenov/vite-ssr-crash.git -b no_routes

Double JSON stringifying

I noticed that the __INITIAL_STATE__ object that was being inserted into the HTML had lots of escaped double quote characters (i.e., \"), and it was because the entire initial state object was itself a string. Apparently the initial state is put through JSON.stringify twice before unsafe characters are escaped! That seemed odd and unnecessary to me, so in my project I changed that part of the code in serializeState to JSON.stringify only once and in deserializeState to simply return state || {} without extra parsing. It turns out this works just fineβ€”all unsafe characters are properly escaped, and there is far less overhead in transmitting/parsing the initial state object this way.

A couple questions:

  • Is there a real reason the initial state object is first serialized to a JSON string and then that JSON string is itself stringified again?
  • If not, would you accept a PR that removes the needless extra JSON.stringify?

Example not work

Hi, i download this project with example and i try start this. After start he say me "Error: Cannot find module 'vite-ssr/build'"

nodejs 15.4

SSR breaks when adding attributes to the app wrapper div

If you just start a fresh vite project and follow the vite-ssr vue documentation. When you run vite-ssr dev or build an ssr version and host it, you will get an empty page source (just an empty #app <div/>)

Tried debugging it but I cannot find the cause. Is this something you can reproduce?

Will this project be written in TypeScript?

Hi, I've just dug around the source code of this project for a while, after comparing to other Vite SSR projects, I believe this is the best SSR plugin for Vite I could find in the market despite the number of stars this project got... I really appreciate your efforts for this project!
But I feel like "unsafe" to modify any options without the type hintings, really wish it can be written in ts for benefitting both the users and contributors πŸ˜€ anyway, great works! Thanks!

import { createSSRApp } from 'vue' *(Cannot use import statement outside a module)

After I run vite-ssr build, then I follow the example for deploying to Serverless function (Vercel) but i ran into such problem.

There's no error during build phases.

Below are the error while previewing Vercel locally.

C:\Coding\my-vitesse-app\serverless\node_modules\vite-ssr\vue\entry-server.js:1
import { createSSRApp } from 'vue';
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at wrapSafe (internal/modules/cjs/loader.js:979:16)
    at Module._compile (internal/modules/cjs/loader.js:1027:27)
    at Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
    at Object.require.extensions.<computed> [as .js] (C:\Users\sians\AppData\Roaming\npm\node_modules\vercel\node_modules\ts-node\src\index.ts:832:44)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14)
    at Module.require (internal/modules/cjs/loader.js:952:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at Module.<anonymous> (C:\Coding\my-vitesse-app\serverless\api\renderer\main.js:21:15)     
    at Module._compile (internal/modules/cjs/loader.js:1063:30)
Error! `node api/index.js` failed with exit code 1

Then I tried to deploy straight to vercel, similar error as such

undefined	ERROR	/var/task/node_modules/vite-ssr/vue/entry-server.js:1
import { createSSRApp } from 'vue';
^^^^^^
SyntaxError: Cannot use import statement outside a module
    at wrapSafe (internal/modules/cjs/loader.js:979:16)
    at Module._compile (internal/modules/cjs/loader.js:1027:27)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14)
    at Module.require (internal/modules/cjs/loader.js:952:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at Module.<anonymous> (/var/task/api/renderer/main.js:9:15)
    at Module._compile (internal/modules/cjs/loader.js:1063:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
RequestId: 0cf21fd7-254a-4550-97c3-c6bc3948db7d Error: Runtime exited with error: exit status 1
Runtime.ExitError

Finally, I've tried deploy as ExpressJS, similar issue happens.

Custom index.html

I have a testing environment that can use either vite or webpack, and to do this I've create a separate index.vite.html and index.webpack.html for each bundler. The following plugin works for vite without SSR, but vite-ssr has index.html hardcoded. I can't actually have a file called index.html or webpack will try to serve it.

{
  name: 'configure-server',
  configureServer(server) {
    return () => {
      server.middlewares.use('/', (req, res, next) => {
        if (req.url === '/index.html') req.url = '/index.vite.html'
        return next()
      })
    }
  }
},

async function getIndexTemplate(url: string) {
// Template should be fresh in every request
const indexHtml = await fs.readFile(resolve('index.html'), 'utf-8')
return await server.transformIndexHtml(url, indexHtml)
}

Hook after renderToString and/or app.mount

Heya, me again.

(vue.js 3.0 app)

I was reading the source code and noticed that our initialization hook is run early on, which is great, however, in my application (similar to https://pinia.esm.dev/ssr/#state-hydration example) I have my custom data stores.
Trouble is, those are filled with data by components at arbitrary levels deep, and I only have the data once the app has been fully rendered, as I rely on onServerPrefetch(async () => {})

That means that the initialState variable passed to the userland hook can't have the final data the server has fetched, as my hook is ran very early on. I need another hook right after this await

renderToString(app, context).then(deferred.resolve).catch(deferred.reject)
const body = await deferred.promise

To make things consistent on the client end, we could be running that hook after app.mount('#app', true)

Other than that, using this library has been a great experience so far!

initialState adds unnecessary attributes. Extraneous non-props attributes warning.

Hey! Thank you for the great SSR solution! I have an issue with the initialState: when I pass something inside, it adds attributes to the component.

image

The issue resolves by adding a wrapper to the page, but it adds unnecessary attributes 😒
Is there any solution to that?

image

P.S I tested your Vue example and got the same warn.
image

Multiple entry points

I'd like to be able to build a separate mobile and desktop app with some shared code, and the same npm dependencies, but different builds. I would expect that most people would want to set up projects in this way, but always find it difficult to do for some reason.

From what I can see, it looks like vite-ssr could make this easier by:

  1. Either allowing configurable entrypoint for both client and server (mobile.html/desktop.html vs index.html), or allowing for dynamically resolving the App that is passed to viteSSR()
  2. Allowing for configurable outDir

At this stage I'm not really sure how feasible this would be, so i might just rewrite or move a file in my package.json scripts until things become easier.

Crash on Vue 3.1 using onServerPrefetch

vite-ssr crashes when using onServerPrefetch.

Example code:

<script lang="ts">
import { defineComponent, onServerPrefetch, ref } from "vue"

export default defineComponent({
	setup() {
		const data = ref("initial")
		onServerPrefetch(async () => {
			data.value = "server"
		})
		return { data }
	},
})
</script>

<template>
	<div>data = {{ data }}</div>
</template>

Steps to reproduce

~/tmp/2
❯ git clone https://github.com/IlyaSemenov/vite-ssr-crash.git
Cloning into 'vite-ssr-crash'...
remote: Enumerating objects: 13, done.
remote: Counting objects: 100% (13/13), done.
remote: Compressing objects: 100% (11/11), done.
remote: Total 13 (delta 0), reused 13 (delta 0), pack-reused 0
Receiving objects: 100% (13/13), 12.28 KiB | 172.00 KiB/s, done.

~/tmp/2
❯ cd vite-ssr-crash

~/tmp/2/vite-ssr-crash master
❯ nodenv local 16.0.0

~/tmp/2/vite-ssr-crash master
❯ yarn
yarn install v1.22.10
[1/4] πŸ”  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] πŸ”—  Linking dependencies...
warning "vite-ssr > @rollup/[email protected]" has unmet peer dependency "rollup@^1.20.0 || ^2.0.0".
warning "vite-ssr > [email protected]" has unmet peer dependency "react@^16.8.0 || ^17.0.0".
warning "vite-ssr > @rollup/plugin-replace > @rollup/[email protected]" has unmet peer dependency "rollup@^1.20.0||^2.0.0".
warning Workspaces can only be enabled in private projects.
[4/4] πŸ”¨  Building fresh packages...
✨  Done in 1.65s.

~/tmp/2/vite-ssr-crash master
❯ yarn dev
yarn run v1.22.10
$ vite-ssr
Pre-bundling dependencies:
  vite-ssr
  @vue/runtime-core
  vite-ssr/vue/entry-client
  vite-ssr/vue/entry-server
(this will be run only when your dependencies or config have changed)

  vite v2.3.8 dev server running at:

  > Local: http://localhost:3000/
  > Network: use `--host` to expose

 -- SSR mode

TypeError: render is not a function
    at handleSsrRequest (/Users/semenov/tmp/2/vite-ssr-crash/node_modules/vite-ssr/dev/server.js:88:37)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
10:40:51 PM [vite] Internal server error: render is not a function
      at handleSsrRequest (/Users/semenov/tmp/2/vite-ssr-crash/node_modules/vite-ssr/dev/server.js:88:37)
      at processTicksAndRejections (node:internal/process/task_queues:96:5)
/node_modules/vite-ssr/vue/entry-client.js:20
    const url = window.location;
                ^

ReferenceError: window is not defined
    at Module.viteSSR [as default] (/node_modules/vite-ssr/vue/entry-client.js:20:17)
    at eval (/Users/semenov/tmp/2/vite-ssr-crash/src/main.ts:12:61)
    at instantiateModule (/Users/semenov/tmp/2/vite-ssr-crash/node_modules/vite/dist/node/chunks/dep-0ed4fbc0.js:69982:166)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

Can I use `async setup` with Suspense instead of `serverPrefetch`?

Hi, thanks for all the efforts to create this project !!!

I noticed that Support Suspense in SSR was possible in vue3.
Does this mean I can use fetch data in async setup instead of serverPrefetch ?
I didn't see any example code in the readme shows how to use this plugins with the vue3's setup method, so if I'm wrong would you show me an example that works with the setup method?

Thanks a lot!

Custom Context Types

Hi, I'm trying to add custom types to context but none of things I tried worked. Is there a way for this?

import {Request, Response} from 'express';

declare module 'vite-ssr' {
   export interface Context {
      request: Request,
      response: Response
   }
}

or

import {Request, Response} from 'express';

declare module 'vite-ssr/vue' {
   export interface Context {
      request: Request,
      response: Response
   }
}

HMR Infinite Loop with wss settings

Our vite config has the following config to connect via ssl in dev.

  server: {
    hmr: {
      host: 'leveluptutorials.dev',
      port: 443,
      protocol: 'wss',
    },
  },

This config works fine with the standard vite cli, but causes an infinite loop using vite ssr. It's hard to debug because of the nature of things, but it seems like the wss connection is failing causing a refresh on loop.

The network shows a 502 request.

Request URL: https://leveluptutorials.dev/__vite_ping
Request Method: GET
Status Code: 502 
Remote Address: 127.0.0.1:443
Referrer Policy: strict-origin-when-cross-origin

It's kind of tough to put together a reproduction given the ssl requirements, I can provide as much as possible to help and can dive into your code to see if there is anything that sticks out.

Edit:
Some additional information. The looping we're seeing is the same as if the hmr config settings were not set at all. This tells me that this library might not be seeing the server.hmr options.

Unable to resolve path to module 'vite-ssr'.

Hello,

I'm using eslint with:

  extends: ['plugin:react/recommended', 'airbnb'],

unfortunately when I try to import 'vite-ssr' I have this error:

ESLint: Unable to resolve path to module 'vite-ssr'.(import/no-unresolved)

If someone has an idea :)

[Vue Router warn] No match found for [...] '/node_modules/.vite/chunk...'

vite-ssr v0.10.6
vue-router v4.0.10
vite v2.3.8

Sometimes I'm getting following messages in the console when the server (dev & production) is running:

[Vue Router warn]: No match found for location with path "/node_modules/.vite/chunk-YSFGVYJQ.js?v=7feddd3a"
[Vue Router warn]: No match found for location with path "/node_modules/.vite/chunk-WM2BNGP4.js?v=7feddd3a"
[Vue Router warn]: No match found for location with path "/node_modules/.vite/chunk-AU43JL3D.js?v=7feddd3a"
[Vue Router warn]: No match found for location with path "/node_modules/.vite/chunk-AJTGOAVB.js?v=7feddd3a"
[Vue Router warn]: No match found for location with path "/node_modules/.vite/chunk-YSFGVYJQ.js?v=7feddd3a"
[Vue Router warn]: No match found for location with path "/node_modules/.vite/chunk-WM2BNGP4.js?v=7feddd3a"
[Vue Router warn]: No match found for location with path "/node_modules/.vite/chunk-AU43JL3D.js?v=7feddd3a"
[Vue Router warn]: No match found for location with path "/node_modules/.vite/chunk-AJTGOAVB.js?v=7feddd3a"

Any idea where this is coming from since the router is configured by vite-ssr? I'm using vite-plugin-pages for the routes.

EDIT 1:
Not sure what caused this but it's gone after reinstalling all dependencies.

EDIT 2:
Happens again for me over and over again when starting the development server. πŸ˜•

Route params not exposed as props in SPA mode

Say I define my routes as follows:

export const routes = [
  {
    path: '/',
    component: () => import('./pages/Home.vue'),
    name: Routes.Home,
  },
  {
    name: 'UserProfile',
    path: '/user/:uid',
    component: () => import('./pages/User.vue'),
    props: true,
  },
  {
    path: '/:catchAll(.*)',
    name: 'not-found',
    component: () => import('./pages/404.vue'),
  }
]

...In SPA mode, I don't get the vid param as a prop and in SSR mode, I have to patch to.meta.state as shown below to get vid param injected into the component as a prop.

router.beforeEach((to, from, next) => {
   if (to.meta.state) {
       return next()
   }
   // pass route params to the page as props
   to.meta.state = Object.assign(
    {},
    to.meta.state,
    initialState,
    initialRoute.params,
   )
  next()
})

I guess I'm not doing something right

Don't store full filenames in ssr-manifest.json and main.js

Currently, ssr-manifest.json stores full local filenames:

{
  "/Users/semenov/work/xxx/node_modules/@vue/shared/dist/shared.esm-bundler.js": [
    "/assets/vendor.8b10200d.js"
  ],
  "/Users/semenov/work/xxx/node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js": [
    "/assets/vendor.8b10200d.js"
  ],

main.js also has this:

./server/main.js:  (ssrContext.modules || (ssrContext.modules = new Set())).add("/Users/semenov/work/xxx/src/pages/index.vue");
./server/main.js:  (ssrContext.modules || (ssrContext.modules = new Set())).add("/Users/semenov/work/xxx/src/App.vue");
  • That is not safe from the privacy point of view.
  • That most probably means that the package is not redistributable (I am yet to try, but I wouldn't be surprised if it won't work)

Add initialState example with a store (vuex/pinia)

Hey!

The docs describe the use of Vuex for the initial state but I'm not 100% sure how I need to use that in a component.
For me, it sounds like that I either need to use app.provide (& inject in components) or a store like Vuex. Is this correct?

Currently my code sets the state of the store to the value of the initialStore parameter. The value is available in my components, but only on the client-side (I think?), because it shows the title and instantly deletes it. Do I still need to set the initial state to that value in the component like it's in the Homepage.vue example when I use a store?

This is my setup() code of my component:

const store = useStore()
const title = ref(store.title || null)

if (!title.value) {
  onServerPrefetch(() => {
    title.value = 'Fetched title'
    store.$patch({
      title: title.value
    })
  })
}

The same code works when I inject the initialState and set it like initialState.title = title.value.

EDIT:
For reference there is actually this documentation (https://pinia.esm.dev/ssr/#state-hydration) for pinia in their docs which works perfectly.

`dev/server.js` may be replaced by vite plugin hooks?

Looking at the dev/server.js, I noticed that the motivation to use such a dedicated file is because these two things

middlewareMode: true,

app.use(async (request, response, next) => {

I think you can utilize these two hooks to get the same result:
https://vitejs.dev/guide/api-plugin.html#config
https://vitejs.dev/guide/api-plugin.html#configureserver

Have I missed anything else? let me know if I'm wrong.

[SSR] On state serialization - TypeError: Converting circular structure to JSON

Hey comunity!

I found myself in weird position where I found something that may be a bug(?) - namely: When this plugin tries to stringify state (in utils/state.js) that is then injected to FE, the JSON.stringify throws and error that theres circular structure.

It throws error like this:

`[SSR] On state serialization - TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'Object'
    |     property 'something' -> object with constructor 'Object'
    |     property 'something' -> object with constructor 'Object'
    |     ...
    |     property 'something' -> object with constructor 'Array'
    --- index 0 closes the circle
    at JSON.stringify ()
    at serializeState (/node_modules/vite-ssr/utils/state.js:32:36)
    at eval (/node_modules/vite-ssr/vue/entry-server.js:64:36)
    at runMicrotasks ()
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
    at async handleSsrRequest (C:\Users\Workgroup-L\Desktop\Work\radixal\mortage\mortgage\FE\node_modules\vite-ssr\dev\server.js:88:31)`

Should it use flatted package or have something like this?

Open to make a PR myself! Thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.