├── .eslintignore
├── src
├── index.ts
├── server
│ ├── index.ts
│ ├── express.ts
│ └── common.ts
├── node
│ ├── utils.ts
│ ├── cli.ts
│ ├── build.ts
│ ├── server.ts
│ └── render.ts
├── utils
│ ├── document-ready.ts
│ └── state.ts
├── components.ts
├── config.ts
├── entry.ts
└── types.ts
├── pnpm-workspace.yaml
├── .npmrc
├── examples
├── vitesse-lite-naive-ui
│ ├── src
│ │ ├── composables
│ │ │ ├── index.ts
│ │ │ └── dark.ts
│ │ ├── pages
│ │ │ ├── [...all].vue
│ │ │ ├── README.md
│ │ │ ├── index.vue
│ │ │ └── form.vue
│ │ ├── server.ts
│ │ ├── styles
│ │ │ └── main.css
│ │ ├── components
│ │ │ ├── Counter.vue
│ │ │ ├── README.md
│ │ │ └── Footer.vue
│ │ ├── App.vue
│ │ ├── main.ts
│ │ └── express.js
│ ├── .gitignore
│ ├── test
│ │ ├── basic.test.ts
│ │ ├── __snapshots__
│ │ │ └── component.test.ts.snap
│ │ └── component.test.ts
│ ├── vue-shims.d.ts
│ ├── bin
│ │ └── www.js
│ ├── tsconfig.json
│ ├── tsconfig copy.json
│ ├── vite.config.noexternal.ts
│ ├── public
│ │ ├── favicon.svg
│ │ └── naive-ui.svg
│ ├── index.html
│ ├── unocss.config.ts
│ ├── vite.config.ts
│ ├── package.json
│ ├── components.d.ts
│ └── auto-imports.d.ts
├── basic
│ ├── public
│ │ └── favicon.ico
│ ├── src
│ │ ├── assets
│ │ │ └── logo.png
│ │ ├── pages
│ │ │ ├── fetch.vue
│ │ │ └── index.vue
│ │ ├── main.ts
│ │ ├── server.ts
│ │ ├── App.vue
│ │ ├── components
│ │ │ └── HelloWorld.vue
│ │ └── express.js
│ ├── vue-shim.d.ts
│ ├── bin
│ │ └── www.js
│ ├── tsconfig.json
│ ├── vite.config.ts
│ ├── vite.config.noexternal.ts
│ ├── .gitignore
│ ├── index.html
│ └── package.json
└── README.md
├── .gitignore
├── .eslintrc.json
├── bin
└── vite-ssr.js
├── tsconfig.json
├── tsup.config.ts
├── .github
└── workflows
│ └── release.yml
├── LICENSE
├── package.json
└── README.md
/.eslintignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './entry'
2 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - examples/*
3 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | shamefully-hoist = true
2 | ignore-workspace-root-check = true
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/src/composables/index.ts:
--------------------------------------------------------------------------------
1 | export * from './dark'
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | .history
3 | node_modules
4 | *.cache
5 | dist
6 | *.log
7 | .idea
8 |
--------------------------------------------------------------------------------
/examples/basic/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliuq/vite-ssr-vue3/master/examples/basic/public/favicon.ico
--------------------------------------------------------------------------------
/examples/basic/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliuq/vite-ssr-vue3/master/examples/basic/src/assets/logo.png
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/src/pages/[...all].vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | Not Found
4 |
5 |
6 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/src/composables/dark.ts:
--------------------------------------------------------------------------------
1 | export const isDark = useDark()
2 | export const toggleDark = useToggle(isDark)
3 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@antfu/eslint-config",
3 | "rules": {
4 | "@typescript-eslint/no-unused-vars": "off"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/examples/basic/src/pages/fetch.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | Fetch Page
7 |
8 |
--------------------------------------------------------------------------------
/examples/basic/src/pages/index.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | Index Page
7 |
8 |
--------------------------------------------------------------------------------
/src/server/index.ts:
--------------------------------------------------------------------------------
1 | export { createRender } from '../node/render'
2 | export { createServer as createExpressServer, startServer as startExpressServer } from './express'
3 |
--------------------------------------------------------------------------------
/bin/vite-ssr.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | 'use strict'
3 | if (typeof __dirname !== 'undefined')
4 | require('../dist/node/cli.cjs')
5 | else import('../dist/node/cli.mjs')
6 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .vite-ssg-dist
3 | .vite-ssg-temp
4 | *.local
5 | dist
6 | dist-ssr
7 | node_modules
8 | .idea/
9 | *.log
10 |
--------------------------------------------------------------------------------
/examples/basic/vue-shim.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.vue' {
2 | import type { DefineComponent } from 'vue'
3 | const component: DefineComponent<{}, {}, any>
4 | export default component
5 | }
6 |
--------------------------------------------------------------------------------
/examples/basic/bin/www.js:
--------------------------------------------------------------------------------
1 | const { startExpressServer: startServer } = require('vite-ssr-vue3/server')
2 | const { createApp } = require('../dist/server/main.cjs')
3 |
4 | startServer({ createApp })
5 |
--------------------------------------------------------------------------------
/examples/basic/src/main.ts:
--------------------------------------------------------------------------------
1 | import { ViteSSR } from 'vite-ssr-vue3'
2 | import routes from 'virtual:generated-pages'
3 | import App from './App.vue'
4 |
5 | export const createApp = ViteSSR(App, { routes })
6 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/test/basic.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from 'vitest'
2 |
3 | describe('Hi', () => {
4 | it('should works', () => {
5 | expect(1 + 1).toEqual(2)
6 | })
7 | })
8 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/vue-shims.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.vue' {
2 | import type { DefineComponent } from 'vue'
3 | const component: DefineComponent<{}, {}, any>
4 | export default component
5 | }
6 |
--------------------------------------------------------------------------------
/examples/basic/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "types": [
5 | "vite/client",
6 | "vite-plugin-pages/client"
7 | ]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/bin/www.js:
--------------------------------------------------------------------------------
1 | const { startExpressServer: startServer } = require('vite-ssr-vue3/server')
2 | const { createApp } = require('../dist/server/main.cjs')
3 |
4 | startServer({ createApp })
5 |
--------------------------------------------------------------------------------
/examples/basic/src/server.ts:
--------------------------------------------------------------------------------
1 | // Start server
2 | import { startExpressServer as startServer } from 'vite-ssr-vue3/server'
3 | import { createApp } from './main'
4 |
5 | startServer({ createApp, root: __dirname, outDir: '..' })
6 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "types": [
5 | "vite/client",
6 | "vite-plugin-pages/client"
7 | ]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/tsconfig copy.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "types": [
5 | "vite/client",
6 | "vite-plugin-pages/client"
7 | ]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/test/__snapshots__/component.test.ts.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1
2 |
3 | exports[`Counter.vue > should render 1`] = `"
10
"`;
4 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/src/server.ts:
--------------------------------------------------------------------------------
1 | // Start server
2 | import { startExpressServer as startServer } from 'vite-ssr-vue3/server'
3 | import { createApp } from './main'
4 |
5 | startServer({ createApp, root: __dirname, outDir: '..' })
6 |
--------------------------------------------------------------------------------
/src/node/utils.ts:
--------------------------------------------------------------------------------
1 | import { blue, gray, yellow } from 'kolorist'
2 |
3 | export function buildLog(text: string, count?: number) {
4 | // eslint-disable-next-line no-console
5 | console.log(`\n${gray('[vite-ssr]')} ${yellow(text)}${count ? blue(` (${count})`) : ''}`)
6 | }
7 |
--------------------------------------------------------------------------------
/examples/basic/vite.config.ts:
--------------------------------------------------------------------------------
1 | import type { UserConfig } from 'vite'
2 | import { defineConfig } from 'vite'
3 | import vue from '@vitejs/plugin-vue'
4 | import Pages from 'vite-plugin-pages'
5 |
6 | // https://vitejs.dev/config/
7 |
8 | export default defineConfig({
9 | plugins: [vue(), Pages()],
10 | } as UserConfig)
11 |
--------------------------------------------------------------------------------
/src/utils/document-ready.ts:
--------------------------------------------------------------------------------
1 | export function documentReady(_passThrough?: any) {
2 | if (document.readyState === 'loading') {
3 | return new Promise((resolve) => {
4 | document.addEventListener('DOMContentLoaded', () => resolve(_passThrough))
5 | })
6 | }
7 |
8 | return Promise.resolve(_passThrough)
9 | }
10 |
--------------------------------------------------------------------------------
/examples/basic/vite.config.noexternal.ts:
--------------------------------------------------------------------------------
1 | import config from './vite.config'
2 | /**
3 | * @type {import('vite').UserConfig}
4 | */
5 | module.exports = Object.assign(config, {
6 | ssrOptions: {
7 | serverConfig: {
8 | build: {
9 | ssr: './src/server',
10 | },
11 | ssr: {
12 | noExternal: /./,
13 | },
14 | },
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/examples/basic/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/vite.config.noexternal.ts:
--------------------------------------------------------------------------------
1 | import config from './vite.config'
2 | /**
3 | * @type {import('vite').UserConfig}
4 | */
5 | module.exports = Object.assign(config, {
6 | ssrOptions: {
7 | serverConfig: {
8 | build: {
9 | ssr: './src/server',
10 | },
11 | ssr: {
12 | noExternal: /./,
13 | },
14 | },
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/src/styles/main.css:
--------------------------------------------------------------------------------
1 | html,
2 | body,
3 | #app {
4 | height: 100%;
5 | margin: 0;
6 | padding: 0;
7 | }
8 |
9 | html.dark {
10 | background: #121212;
11 | }
12 |
13 |
14 | button.n-button {
15 | color: var(--n-text-color);
16 | background-color: var(--n-color);
17 | border-radius: var(--n-border-radius);
18 | font-weight: var(--n-font-weight);
19 | }
20 |
--------------------------------------------------------------------------------
/examples/basic/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/src/components/Counter.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 | {{ count }}
12 |
15 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/src/components/README.md:
--------------------------------------------------------------------------------
1 | ## Components
2 |
3 | Components in this dir will be auto-registered and on-demand, powered by [`unplugin-vue-components`](https://github.com/antfu/unplugin-vue-components).
4 |
5 | ### Icons
6 |
7 | You can use icons from almost any icon sets by the power of [Iconify](https://iconify.design/).
8 |
9 | It will only bundle the icons you use. Check out [`unplugin-icons`](https://github.com/antfu/unplugin-icons) for more details.
10 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "ESNext",
4 | "target": "es2020",
5 | "lib": [
6 | "ESNext",
7 | "DOM"
8 | ],
9 | "esModuleInterop": true,
10 | "strict": true,
11 | "strictNullChecks": true,
12 | "moduleResolution": "Node",
13 | "resolveJsonModule": true,
14 | "skipLibCheck": true,
15 | "jsx": "preserve"
16 | },
17 | "exclude": [
18 | "**/dist",
19 | "**/node_modules",
20 | "**/test"
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/src/pages/README.md:
--------------------------------------------------------------------------------
1 | ## File-based Routing
2 |
3 | Routes will be auto-generated for Vue files in this dir with the same file structure.
4 | Check out [`vite-plugin-pages`](https://github.com/hannoeru/vite-plugin-pages) for more details.
5 |
6 | ### Path Aliasing
7 |
8 | `~/` is aliased to `./src/` folder.
9 |
10 | For example, instead of having
11 |
12 | ```ts
13 | import { isDark } from '../../../../composables'
14 | ```
15 |
16 | now, you can use
17 |
18 | ```ts
19 | import { isDark } from '~/composables'
20 | ```
21 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/src/components/Footer.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
21 |
22 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/src/App.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import type { Options } from 'tsup'
2 |
3 | export default {
4 | entryPoints: {
5 | 'index': 'src/index.ts',
6 | 'server': 'src/server/index.ts',
7 | 'node/cli': 'src/node/cli.ts',
8 | },
9 | dts: true,
10 | target: 'node14',
11 | format: [
12 | 'esm',
13 | 'cjs',
14 | ],
15 | external: [
16 | 'vue',
17 | 'vite',
18 | 'vue/server-renderer',
19 | 'vue/compiler-sfc',
20 | ],
21 | clean: true,
22 | esbuildOptions: (options: any, { format }: any) => {
23 | options.outExtension = { '.js': format === 'cjs' ? '.cjs' : format === 'esm' ? '.mjs' : '.js' }
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/src/main.ts:
--------------------------------------------------------------------------------
1 | import routes from 'virtual:generated-pages'
2 | import { ViteSSR } from 'vite-ssr-vue3'
3 | import { setup } from '@css-render/vue3-ssr'
4 | import App from './App.vue'
5 |
6 | import '@unocss/reset/tailwind.css'
7 | import './styles/main.css'
8 | import 'uno.css'
9 |
10 | export const createApp = ViteSSR(App, { routes }, (ctx) => {
11 | if (!ctx.isClient) {
12 | let collect: any
13 | ctx.onBeforePageRender = () => {
14 | collect = setup(ctx.app).collect
15 | }
16 | ctx.onPageRender = ({ preloadLinks }) => {
17 | preloadLinks += collect()
18 | return { preloadLinks }
19 | }
20 | }
21 | })
22 |
--------------------------------------------------------------------------------
/examples/basic/src/App.vue:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
23 |
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # How to run/build the examples
2 |
3 | ```bash
4 | # Clone repo
5 | git clone https://github.com/aliuq/vite-ssr-vue3.git
6 | cd vite-ssr-vue3
7 |
8 | # Install all project dependencies and build project
9 | pnpm install
10 | pnpm build
11 |
12 | # Basic example
13 | pnpm basic:dev
14 | pnpm basic:build
15 | pnpm basic:serve
16 | pnpm basic:serve:bin
17 | pnpm basic:serve:custom
18 |
19 | pnpm basic:build:noexternal
20 | pnpm basic:preview
21 |
22 | # Vitesse lite and naive-ui
23 | pnpm vl-naive:dev
24 | pnpm vl-naive:build
25 | pnpm vl-naive:serve
26 | pnpm vl-naive:serve:bin
27 | pnpm vl-naive:serve:custom
28 |
29 | pnpm vl-naive:build:noexternal
30 | pnpm vl-naive:preview
31 | ```
32 |
--------------------------------------------------------------------------------
/examples/basic/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "basic",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "vite-ssr --port 3333",
7 | "build": "vite-ssr build",
8 | "serve": "vite-ssr --mode production",
9 | "serve:bin": "node ./bin/www",
10 | "serve:custom": "node ./src/express",
11 | "build:noexternal": "vite-ssr build --config ./vite.config.noexternal.ts",
12 | "preview": "node ./dist/server/server.cjs"
13 | },
14 | "dependencies": {
15 | "vue": "^3.2.25"
16 | },
17 | "devDependencies": {
18 | "@vitejs/plugin-vue": "^2.3.0",
19 | "typescript": "^4.5.4",
20 | "vite": "^2.9.1",
21 | "vite-plugin-pages": "^0.22.0",
22 | "vite-ssr-vue3": "workspace:*"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - 'v*'
7 |
8 | jobs:
9 | release:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v2
13 | with:
14 | fetch-depth: 0
15 | - uses: actions/setup-node@v2
16 | with:
17 | node-version: '14'
18 | registry-url: https://registry.npmjs.org/
19 | - run: npm i -g pnpm
20 | - run: pnpm install -F vite-ssr-vue3 --frozen-lockfile
21 | - run: npm publish --access public
22 | env:
23 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
24 | - run: npx conventional-github-releaser -p angular
25 | env:
26 | CONVENTIONAL_GITHUB_RELEASER_TOKEN: ${{secrets.GITHUB_TOKEN}}
27 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/test/component.test.ts:
--------------------------------------------------------------------------------
1 | import { mount } from '@vue/test-utils'
2 | import { describe, expect, it } from 'vitest'
3 | import Counter from '../src/components/Counter.vue'
4 |
5 | describe('Counter.vue', () => {
6 | it('should render', () => {
7 | const wrapper = mount(Counter, { props: { initial: 10 } })
8 | expect(wrapper.text()).toContain('10')
9 | expect(wrapper.html()).toMatchSnapshot()
10 | })
11 |
12 | it('should be interactive', async() => {
13 | const wrapper = mount(Counter, { props: { initial: 0 } })
14 | expect(wrapper.text()).toContain('0')
15 |
16 | expect(wrapper.find('.inc').exists()).toBe(true)
17 |
18 | await wrapper.get('button').trigger('click')
19 |
20 | expect(wrapper.text()).toContain('1')
21 | })
22 | })
23 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vitesse Lite
8 |
9 |
10 |
11 |
12 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/src/pages/index.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
+
15 |

16 |
17 |
18 | Vitesse Lite
19 | +
20 | Naive UI
21 |
22 |
23 |
24 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/node/cli.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-expressions */
2 | import { bold, gray, red, reset, underline } from 'kolorist'
3 | import yargs from 'yargs'
4 | import { hideBin } from 'yargs/helpers'
5 | import { bugs } from '../../package.json'
6 | import { startServer } from './server'
7 | import { build } from './build'
8 |
9 | yargs(hideBin(process.argv))
10 | .scriptName('vite-ssr')
11 | .usage('$0 [args]')
12 | .command(
13 | 'build',
14 | 'Build SSR',
15 | args => args,
16 | args => build(args as any),
17 | )
18 | .command(
19 | '*',
20 | 'development and production environment',
21 | args => args,
22 | async args => (await startServer)(args),
23 | )
24 | .fail((msg, err, yargs) => {
25 | console.error(`\n${gray('[vite-ssr]')} ${bold(red('An internal error occurred.'))}`)
26 | console.error(`${gray('[vite-ssr]')} ${reset(`Please report an issue, if none already exists: ${underline(bugs)}`)}`)
27 | yargs.exit(1, err)
28 | })
29 | .showHelpOnFail(false)
30 | .help()
31 | .argv
32 |
--------------------------------------------------------------------------------
/src/utils/state.ts:
--------------------------------------------------------------------------------
1 | // https://github.com/yahoo/serialize-javascript
2 | const UNSAFE_CHARS_REGEXP = /[<>\/\u2028\u2029]/g
3 | const ESCAPED_CHARS = {
4 | '<': '\\u003C',
5 | '>': '\\u003E',
6 | '/': '\\u002F',
7 | '\u2028': '\\u2028',
8 | '\u2029': '\\u2029',
9 | }
10 |
11 | function escapeUnsafeChars(unsafeChar: string) {
12 | return ESCAPED_CHARS[unsafeChar as keyof typeof ESCAPED_CHARS]
13 | }
14 |
15 | export function serializeState(state: any) {
16 | if (state == null || Object.keys(state).length === 0)
17 | return null
18 | try {
19 | return JSON.stringify(JSON.stringify(state || {})).replace(
20 | UNSAFE_CHARS_REGEXP,
21 | escapeUnsafeChars,
22 | )
23 | }
24 | catch (error) {
25 | console.error('[SSR] On state serialization -', error, state)
26 | return null
27 | }
28 | }
29 |
30 | export function deserializeState(state: string) {
31 | try {
32 | return JSON.parse(state || '{}')
33 | }
34 | catch (error) {
35 | console.error('[SSR] On state deserialization -', error, state)
36 | return {}
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/unocss.config.ts:
--------------------------------------------------------------------------------
1 | import {
2 | defineConfig,
3 | presetAttributify,
4 | presetIcons,
5 | presetUno,
6 | presetWebFonts,
7 | // transformerDirectives,
8 | // transformerVariantGroup,
9 | } from 'unocss'
10 |
11 | export default defineConfig({
12 | shortcuts: [
13 | ['btn', 'px-4 py-1 rounded inline-block bg-teal-600 text-white cursor-pointer hover:bg-teal-700 disabled:cursor-default disabled:bg-gray-600 disabled:opacity-50'],
14 | ['icon-btn', 'text-[0.9em] inline-block cursor-pointer select-none opacity-75 transition duration-200 ease-in-out hover:opacity-100 hover:text-teal-600'],
15 | ],
16 | presets: [
17 | presetUno(),
18 | presetAttributify(),
19 | presetIcons({
20 | scale: 1.2,
21 | warn: true,
22 | }),
23 | presetWebFonts({
24 | fonts: {
25 | sans: 'DM Sans',
26 | serif: 'DM Serif Display',
27 | mono: 'DM Mono',
28 | },
29 | }),
30 | ],
31 | // transformers: [
32 | // transformerDirectives(),
33 | // transformerVariantGroup(),
34 | // ],
35 | })
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 liuq
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/examples/basic/src/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 | {{ msg }}
11 |
12 |
13 | Recommended IDE setup:
14 | VSCode
15 | +
16 | Volar
17 |
18 |
19 | See README.md for more information.
20 |
21 |
22 |
23 | Vite Docs
24 |
25 | |
26 | Vue 3 Docs
27 |
28 |
29 |
32 |
33 | Edit
34 | components/HelloWorld.vue to test hot module replacement.
35 |
36 |
37 |
38 |
55 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/vite.config.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import path from 'path'
4 | import { defineConfig } from 'vite'
5 | import Vue from '@vitejs/plugin-vue'
6 | import Pages from 'vite-plugin-pages'
7 | import Components from 'unplugin-vue-components/vite'
8 | import AutoImport from 'unplugin-auto-import/vite'
9 | import Unocss from 'unocss/vite'
10 | import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
11 |
12 | export default defineConfig({
13 | resolve: {
14 | alias: {
15 | '~/': `${path.resolve(__dirname, 'src')}/`,
16 | },
17 | },
18 | plugins: [
19 | Vue({
20 | reactivityTransform: true,
21 | }),
22 |
23 | // https://github.com/hannoeru/vite-plugin-pages
24 | Pages(),
25 |
26 | // https://github.com/antfu/unplugin-auto-import
27 | AutoImport({
28 | imports: [
29 | 'vue',
30 | 'vue/macros',
31 | 'vue-router',
32 | '@vueuse/core',
33 | ],
34 | dts: true,
35 | }),
36 |
37 | // https://github.com/antfu/vite-plugin-components
38 | Components({
39 | resolvers: [NaiveUiResolver()],
40 | }),
41 |
42 | // https://github.com/antfu/unocss
43 | // see unocss.config.ts for config
44 | Unocss(),
45 | ],
46 |
47 | // https://github.com/vitest-dev/vitest
48 | test: {
49 | environment: 'jsdom',
50 | },
51 | })
52 |
--------------------------------------------------------------------------------
/src/components.ts:
--------------------------------------------------------------------------------
1 | import type { App } from 'vue'
2 | import { defineComponent, inject, onMounted, ref } from 'vue'
3 | import { useRoute } from 'vue-router'
4 | import type { ViteSSRContext } from './types'
5 |
6 | export const ClientOnly = defineComponent({
7 | name: 'ClientOnly',
8 | setup(_: any, { slots }: any) {
9 | const show = ref(false)
10 | onMounted(() => {
11 | show.value = true
12 | })
13 |
14 | return () => (show.value && slots.default ? slots.default() : null)
15 | },
16 | })
17 |
18 | const CONTEXT_SYMBOL = Symbol('unique-context-symbol')
19 | export function provideContext(app: App, context: ViteSSRContext) {
20 | app.provide(CONTEXT_SYMBOL, context)
21 | }
22 |
23 | export function useContext() {
24 | return inject(CONTEXT_SYMBOL) as ViteSSRContext
25 | }
26 |
27 | export async function useFetch(key: string, fn: () => Promise) {
28 | const { initialState } = useContext()
29 | const { name } = useRoute()
30 | key = `${name as string}_${key}`
31 | const state = ref(initialState[key] || null)
32 | // @ts-expect-error Support client side hmr, need to update state
33 | if (import.meta.hot)
34 | state.value = await fn()
35 |
36 | if (!state.value) {
37 | state.value = await fn()
38 | // @ts-expect-error global variable
39 | if (import.meta.env.SSR)
40 | initialState[key] = state.value
41 | }
42 | return state
43 | }
44 |
--------------------------------------------------------------------------------
/examples/basic/src/express.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | const path = require('path')
3 | const express = require('express')
4 | const { createRender } = require('vite-ssr-vue3/server')
5 |
6 | async function createServer() {
7 | const out = path.join(__dirname, '../dist')
8 | const resolve = p => path.resolve(out, p)
9 | const app = express()
10 | app.use(express.static(resolve('./client'), {
11 | index: false,
12 | maxAge: '1y',
13 | }))
14 |
15 | const render = await createRender({ isProd: true, root: __dirname, outDir: '../dist' })
16 | const { createApp } = require(resolve('./server/main.cjs'))
17 |
18 | app.use('*', async(req, res) => {
19 | try {
20 | const url = req.originalUrl
21 |
22 | if (req.method !== 'GET' || url === '/sw.js' || url === '/favicon.ico')
23 | return
24 |
25 | const context = await createApp(false)
26 | let html = await render(url, { context })
27 |
28 | html += '\n'
29 |
30 | res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
31 | }
32 | catch (error) {
33 | console.error(error)
34 | res.status(500).end(error.stack)
35 | }
36 | })
37 |
38 | return { server: app }
39 | }
40 |
41 | createServer().then(({ server }) =>
42 | server.listen(3000, () => {
43 | // eslint-disable-next-line no-console
44 | console.info('http://localhost:3000')
45 | }),
46 | )
47 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/src/express.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | const path = require('path')
3 | const express = require('express')
4 | const { createRender } = require('vite-ssr-vue3/server')
5 |
6 | async function createServer() {
7 | const out = path.join(__dirname, '../dist')
8 | const resolve = p => path.resolve(out, p)
9 | const app = express()
10 | app.use(express.static(resolve('./client'), {
11 | index: false,
12 | maxAge: '1y',
13 | }))
14 |
15 | const render = await createRender({ isProd: true, root: __dirname, outDir: '../dist' })
16 | const { createApp } = require(resolve('./server/main.cjs'))
17 |
18 | app.use('*', async(req, res) => {
19 | try {
20 | const url = req.originalUrl
21 |
22 | if (req.method !== 'GET' || url === '/sw.js' || url === '/favicon.ico')
23 | return
24 |
25 | const context = await createApp(false)
26 | let html = await render(url, { context })
27 |
28 | html += '\n'
29 |
30 | res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
31 | }
32 | catch (error) {
33 | console.error(error)
34 | res.status(500).end(error.stack)
35 | }
36 | })
37 |
38 | return { server: app }
39 | }
40 |
41 | createServer().then(({ server }) =>
42 | server.listen(3000, () => {
43 | // eslint-disable-next-line no-console
44 | console.info('http://localhost:3000')
45 | }),
46 | )
47 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vitesse-lite-naive-ui",
3 | "private": true,
4 | "packageManager": "pnpm@6.32.3",
5 | "files": [
6 | "dist",
7 | "bin",
8 | "*.d.ts"
9 | ],
10 | "bin": "./bin/www.js",
11 | "scripts": {
12 | "dev": "vite-ssr --port 3333",
13 | "build": "vite-ssr build",
14 | "serve": "vite-ssr --mode production",
15 | "serve:bin": "node ./bin/www",
16 | "serve:custom": "node ./src/express",
17 | "build:noexternal": "vite-ssr build --config ./vite.config.noexternal.ts",
18 | "preview": "node ./dist/server/server.cjs",
19 | "lint": "eslint .",
20 | "typecheck": "vue-tsc --noEmit",
21 | "test": "vitest"
22 | },
23 | "dependencies": {
24 | "@vueuse/core": "^8.2.4",
25 | "vue": "^3.2.31",
26 | "vue-router": "^4.0.14"
27 | },
28 | "devDependencies": {
29 | "@antfu/eslint-config": "^0.19.4",
30 | "@css-render/vue3-ssr": "^0.15.9",
31 | "@iconify-json/carbon": "^1.1.2",
32 | "@types/node": "^17.0.23",
33 | "@unocss/reset": "^0.30.11",
34 | "@vitejs/plugin-vue": "^2.3.1",
35 | "@vue/test-utils": "^2.0.0-rc.18",
36 | "eslint": "^8.12.0",
37 | "jsdom": "^19.0.0",
38 | "naive-ui": "^2.26.4",
39 | "pnpm": "^6.32.4",
40 | "typescript": "^4.6.3",
41 | "unocss": "^0.30.11",
42 | "unplugin-auto-import": "^0.6.9",
43 | "unplugin-vue-components": "^0.18.5",
44 | "vite": "^2.9.1",
45 | "vite-plugin-pages": "^0.22.0",
46 | "vite-ssr-vue3": "workspace:*",
47 | "vitest": "^0.8.4",
48 | "vue-tsc": "^0.33.9"
49 | },
50 | "eslintConfig": {
51 | "extends": "@antfu"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/server/express.ts:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 | import { performance } from 'perf_hooks'
3 | import express from 'express'
4 | import type { CreateServerOptions, ViteSSRContext } from '../types'
5 | import { createRender } from '../node/render'
6 | import { createStartServer } from './common'
7 |
8 | export async function createServer({
9 | createApp,
10 | routePath,
11 | root = process.cwd(),
12 | outDir = 'dist',
13 | }: CreateServerOptions) {
14 | const out = path.isAbsolute(outDir) ? outDir : path.join(root, outDir)
15 | const resolve = (dir: string) => path.resolve(out, dir)
16 |
17 | const app = express()
18 | app.use(express.static(resolve('./client'), {
19 | index: false,
20 | maxAge: '1y',
21 | }))
22 |
23 | const render = await createRender({ isProd: true, root, outDir })
24 |
25 | app.use('*', async(req, res, next) => {
26 | try {
27 | const url = req.originalUrl || req.url
28 | if (req.method !== 'GET' || url === '/favicon.ico')
29 | return next()
30 |
31 | globalThis.__ssr_start_time = performance.now()
32 | const context: ViteSSRContext = await createApp(false, routePath)
33 | let html = await render(url, { context })
34 |
35 | const time = Math.round(performance.now() - globalThis.__ssr_start_time)
36 | html += `\n\n`
37 |
38 | return res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
39 | }
40 | catch (error: any) {
41 | console.error(error)
42 | res.status(500).end(error.stack)
43 | }
44 | })
45 |
46 | return { server: app }
47 | }
48 |
49 | export const startServer = createStartServer(createServer)
50 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/components.d.ts:
--------------------------------------------------------------------------------
1 | // generated by unplugin-vue-components
2 | // We suggest you to commit this file into source control
3 | // Read more: https://github.com/vuejs/vue-next/pull/3399
4 |
5 | declare module 'vue' {
6 | export interface GlobalComponents {
7 | Counter: typeof import('./src/components/Counter.vue')['default']
8 | Footer: typeof import('./src/components/Footer.vue')['default']
9 | NButton: typeof import('naive-ui')['NButton']
10 | NCheckbox: typeof import('naive-ui')['NCheckbox']
11 | NCheckboxGroup: typeof import('naive-ui')['NCheckboxGroup']
12 | NConfigProvider: typeof import('naive-ui')['NConfigProvider']
13 | NDatePicker: typeof import('naive-ui')['NDatePicker']
14 | NForm: typeof import('naive-ui')['NForm']
15 | NFormItem: typeof import('naive-ui')['NFormItem']
16 | NFormItemGi: typeof import('naive-ui')['NFormItemGi']
17 | NGrid: typeof import('naive-ui')['NGrid']
18 | NH1: typeof import('naive-ui')['NH1']
19 | NInput: typeof import('naive-ui')['NInput']
20 | NInputNumber: typeof import('naive-ui')['NInputNumber']
21 | NMessageProvider: typeof import('naive-ui')['NMessageProvider']
22 | NRadio: typeof import('naive-ui')['NRadio']
23 | NRadioButton: typeof import('naive-ui')['NRadioButton']
24 | NRadioGroup: typeof import('naive-ui')['NRadioGroup']
25 | NSelect: typeof import('naive-ui')['NSelect']
26 | NSlider: typeof import('naive-ui')['NSlider']
27 | NSpace: typeof import('naive-ui')['NSpace']
28 | NSwitch: typeof import('naive-ui')['NSwitch']
29 | NTimePicker: typeof import('naive-ui')['NTimePicker']
30 | NTransfer: typeof import('naive-ui')['NTransfer']
31 | }
32 | }
33 |
34 | export { }
35 |
--------------------------------------------------------------------------------
/src/node/build.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 | import { isAbsolute, join } from 'path'
3 | import type { ResolvedConfig, UserConfig } from 'vite'
4 | import { mergeConfig, build as viteBuild } from 'vite'
5 | import { getEntryPoint, resolveViteConfig } from '../config'
6 | import { buildLog } from './utils'
7 |
8 | export interface CliOptions {
9 | mode?: string
10 | format?: string
11 | config?: string
12 | }
13 |
14 | export async function build(cliOptions: CliOptions = {}) {
15 | const mode = process.env.MODE || process.env.NODE_ENV || cliOptions.mode || 'production'
16 | const config: ResolvedConfig = await resolveViteConfig(mode, { configFile: cliOptions.config })
17 |
18 | const cwd = process.cwd()
19 | const root = config.root || cwd
20 | const outDir = config.build.outDir || 'dist'
21 | const out = isAbsolute(outDir) ? outDir : join(root, outDir)
22 |
23 | const { input, clientConfig = {}, serverConfig = {} } = Object.assign({}, config.ssrOptions || {}, cliOptions)
24 |
25 | buildLog('Build for client...')
26 | await viteBuild(mergeConfig({
27 | build: {
28 | ssrManifest: true,
29 | outDir: join(out, 'client'),
30 | rollupOptions: {
31 | input: {
32 | app: join(root, input || 'index.html'),
33 | },
34 | },
35 | },
36 | mode: config.mode,
37 | }, clientConfig) as UserConfig)
38 |
39 | buildLog('Build for server...')
40 | await viteBuild(mergeConfig({
41 | build: {
42 | ssr: await getEntryPoint(config.ssrOptions || {}, config),
43 | outDir: join(out, 'server'),
44 | minify: false,
45 | cssCodeSplit: false,
46 | rollupOptions: {
47 | output: {
48 | entryFileNames: '[name].cjs',
49 | format: 'cjs',
50 | },
51 | },
52 | },
53 | mode: config.mode,
54 | }, serverConfig) as UserConfig)
55 | }
56 |
--------------------------------------------------------------------------------
/src/server/common.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-var-requires */
2 | import type { AddressInfo, Server } from 'net'
3 | import { performance } from 'perf_hooks'
4 | import type { ViteDevServer } from 'vite'
5 | import { bold, cyan } from 'kolorist'
6 | import type { CreateServerOptions } from '../types'
7 |
8 | function printInfo(server: Server, vite?: ViteDevServer | null) {
9 | // eslint-disable-next-line no-console
10 | const info = vite?.config ? vite.config.logger.info : console.info
11 |
12 | const address = server.address()
13 | const isAddressInfo = (x: any): x is AddressInfo => x?.address
14 | if (isAddressInfo(address)) {
15 | const ad = cyan(`http://localhost:${bold(address.port)}`)
16 | try {
17 | let msg = `\n express v${require('express/package.json').version}`
18 | if (vite?.config)
19 | msg += ` + vite v${require('vite/package.json').version}`
20 |
21 | info(
22 | cyan(msg),
23 | vite?.config ? { clear: !vite.config.logger.hasWarned } : '',
24 | )
25 | }
26 | catch (err) {
27 | // TODO: Avoid error
28 | }
29 | finally {
30 | info('\n -- SSR mode \n')
31 |
32 | info(` > Running at: ${ad}`)
33 |
34 | if (vite?.config && vite.config.plugins.find(p => p.name.includes('unocss:inspector')))
35 | info(` > Unocss: ${ad}${cyan('/__unocss')}`)
36 |
37 | if (vite?.config && vite.config.plugins.find(p => p.name.includes('vite-plugin-inspect')))
38 | info(` > Inspect: ${ad}${cyan('/__inspect')}`)
39 |
40 | const time = Math.round(performance.now() - globalThis.__ssr_ready_time)
41 | info(cyan(`\n ready in ${time}ms.\n`))
42 | }
43 | }
44 | }
45 |
46 | export function createStartServer(createServer: any) {
47 | return async(args: CreateServerOptions | any) => {
48 | globalThis.__ssr_ready_time = performance.now()
49 | const { server, vite = null } = await createServer(args)
50 | const ser = server.listen(args.port || 3000, () => {
51 | printInfo(ser, vite)
52 | })
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/public/naive-ui.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/config.ts:
--------------------------------------------------------------------------------
1 | import path, { join as _join, resolve as _resolve } from 'path'
2 | import { resolveConfig } from 'vite'
3 | import type { InlineConfig, ResolvedConfig } from 'vite'
4 | import fs from 'fs-extra'
5 |
6 | import type { ViteSSROptions } from './types'
7 |
8 | let __CONFIG__: ResolvedConfig
9 |
10 | export async function getConfig(mode: string | undefined = process.env.NODE_ENV) {
11 | if (!__CONFIG__)
12 | __CONFIG__ = await resolveConfig({}, 'build', mode)
13 |
14 | const ssrOptions: ViteSSROptions = Object.assign({
15 | input: 'index.html',
16 | entry: 'src/main',
17 | rootContainerId: 'app',
18 | useViteMiddleware: process.env.NODE_ENV !== 'production',
19 | mode: __CONFIG__.mode,
20 | }, __CONFIG__?.ssrOptions || {})
21 |
22 | const join = (dir: string) => _join(__CONFIG__.root, dir)
23 |
24 | const resolve = (dir: string) => _resolve(__CONFIG__.root, dir)
25 |
26 | return {
27 | config: __CONFIG__,
28 | ssrOptions,
29 | join,
30 | resolve,
31 | }
32 | }
33 |
34 | export async function getSsrOptions(mode?: string | undefined) {
35 | const { ssrOptions } = await getConfig(mode)
36 | return ssrOptions
37 | }
38 |
39 | export async function getEntry(mode?: string | undefined) {
40 | const { join } = await getConfig(mode)
41 | const html = await getIndexTemplate(mode)
42 | //
43 | const matches = html.match(/
64 | const matches = indexHtml.match(/`
86 | : ''
87 |
88 | const html = template
89 | .replace('', `\n${preloadLinks}`)
90 | .replace(
91 | '',
92 | `${appHtml}
${stateScript}`,
93 | )
94 |
95 | return html
96 | }
97 |
98 | return render
99 | }
100 |
101 | async function renderPreloadLinks(modules: any, manifest: any) {
102 | let links = ''
103 | const seen = new Set()
104 | const { basename } = await import('path')
105 |
106 | modules && modules.forEach((id: any) => {
107 | const files = manifest[id]
108 | if (files) {
109 | files.forEach((file: any) => {
110 | if (!seen.has(file)) {
111 | seen.add(file)
112 | const filename = basename(file)
113 | if (manifest[filename]) {
114 | for (const depFile of manifest[filename]) {
115 | links += renderPreloadLink(depFile)
116 | seen.add(depFile)
117 | }
118 | }
119 | links += renderPreloadLink(file)
120 | }
121 | })
122 | }
123 | })
124 | return links
125 | }
126 |
127 | function renderPreloadLink(file: string) {
128 | if (file.endsWith('.js')) {
129 | return ``
130 | }
131 | else if (file.endsWith('.css')) {
132 | return ``
133 | }
134 | else if (file.endsWith('.woff')) {
135 | return ` `
136 | }
137 | else if (file.endsWith('.woff2')) {
138 | return ` `
139 | }
140 | else if (file.endsWith('.gif')) {
141 | return ` `
142 | }
143 | else if (file.endsWith('.jpg') || file.endsWith('.jpeg')) {
144 | return ` `
145 | }
146 | else if (file.endsWith('.png')) {
147 | return ` `
148 | }
149 | else {
150 | // TODO
151 | return ''
152 | }
153 | }
154 |
155 | function getDefaultTemplate(ssrOptions: ViteSSROptions) {
156 | const { rootContainerId = 'app', entry = 'src/main.ts' } = ssrOptions
157 | const template = `
158 |
159 |
160 |
161 |
162 |
163 |
164 | Vite SSR
165 |
171 |
172 |
173 |
174 |
175 |
176 |
177 | `
178 | return template
179 | }
180 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Vite-SSR-Vue3
2 |
3 | 
4 |
5 | Vite plugin for vue Server-Side Rendering, Used express to create a server.
6 |
7 | ## Usage
8 |
9 | ### Install
10 |
11 | ```bash
12 | # npm
13 | npm install -D vite-ssr-vue3 vue-router
14 |
15 | # yarn
16 | yarn add -D vite-ssr-vue3 vue-router
17 |
18 | # pnpm
19 | pnpm add -D vite-ssr-vue3 vue-router
20 |
21 | # pnpm workspaces
22 | pnpm add -D vite-ssr-vue3 vue-router -F
23 | ```
24 |
25 | Modify `package.json` scripts to:
26 |
27 | ```diff
28 | {
29 | "scripts": {
30 | - "dev": "vite --port 3333",
31 | + "dev": "vite-ssr --port 3333",
32 | - "build": "vite build",
33 | + "build": "vite-ssr build",
34 | + "serve": "vite-ssr --mode production"
35 | },
36 | }
37 | ```
38 |
39 | ```ts
40 | // src/main.ts
41 | import { ViteSSR } from 'vite-ssr-vue3'
42 | import routes from './routes'
43 | import App from './App.vue'
44 |
45 | // Export `createApp` function is required by vite-ssr
46 | export const createApp = ViteSSR(App, { routes }, (context) => {
47 | // if (import.meta.env.SSR) {
48 | // // Running in server
49 | // } else {
50 | // // Running in browser
51 | // }
52 | })
53 | ```
54 |
55 | ### Initial State
56 |
57 | ```ts
58 | // src/main.ts
59 | import { ViteSSR } from 'vite-ssr-vue3'
60 | import routes from './routes'
61 | import App from './App.vue'
62 |
63 | export const createApp = ViteSSR(App, { routes }, (context) => {
64 | const { app, initialState } = context
65 |
66 | // Use pinia to store your data
67 | const pinia = createPinia()
68 |
69 | if (import.meta.env.SSR)
70 | initialState.pinia = pinia.state.value
71 | else
72 | pinia.state.value = initialState.pinia
73 |
74 | })
75 | ```
76 |
77 | ### Data Fetching
78 |
79 | ```ts
80 | import { useFetch } from 'vite-ssr-vue3'
81 |
82 | const counts = await useFetch('counts', () => Promise.resolve([1, 2, 3]))
83 | ```
84 |
85 | ### Full Control Build
86 |
87 | `vite-ssr build` command default equals build client and server, so you can customize your build process.
88 |
89 | ```diff
90 | {
91 | "scripts": {
92 | + "build": "vite-ssr build",
93 | - "build:client": "vite build --ssrManifest --outDir dist/client",
94 | - "build:server": "vite build --ssr src/main.ts --outDir dist/server",
95 | },
96 | }
97 | ```
98 |
99 | vite-ssr support separate options for client and server building, Configure them for separate builds
100 |
101 | ```ts
102 | import { defineConfig } from 'vite'
103 |
104 | export default defineConfig({
105 | ssrOptions: {
106 | // Client build options
107 | clientConfig: {},
108 | // Server build options
109 | serverConfig: {},
110 | },
111 | })
112 | ```
113 |
114 | Compiled with `vite-ssr build`, there are three ways to start the server:
115 |
116 | 1. Run `vite-ssr --mode production`
117 | 2. Use vite-ssr built-in express server, then run `node ./bin/www`
118 |
119 | ```js
120 | // ./bin/www.js
121 | const { startExpressServer: startServer } = require('vite-ssr-vue3/server')
122 | const { createApp } = require('../dist/server/main.cjs')
123 |
124 | startServer({ createApp })
125 | ```
126 |
127 | 3. Customize server
128 |
129 | `vite-ssr-vue3/server` export a function `createRender` to allows you create a render function.
130 |
131 | Express example
132 |
133 | ```js
134 | // ./src/express.js
135 | // @ts-check
136 | const path = require('path')
137 | const express = require('express')
138 | const { createRender } = require('vite-ssr-vue3/server')
139 |
140 | async function createServer() {
141 | const out = path.join(__dirname, '../dist')
142 | const resolve = p => path.resolve(out, p)
143 | const app = express()
144 | app.use(express.static(resolve('./client'), {
145 | index: false,
146 | maxAge: '1y',
147 | }))
148 |
149 | const render = await createRender({ isProd: true, root: __dirname, outDir: '../dist' })
150 | const { createApp } = require(resolve('./server/main.cjs'))
151 |
152 | app.use('*', async(req, res) => {
153 | try {
154 | const url = req.originalUrl
155 |
156 | if (req.method !== 'GET' || url === '/sw.js' || url === '/favicon.ico')
157 | return
158 |
159 | const context = await createApp(false)
160 | let html = await render(url, { context })
161 |
162 | html += '\n'
163 |
164 | res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
165 | }
166 | catch (error) {
167 | -console.error(error)
168 | res.status(500).end(error.stack)
169 | }
170 | })
171 |
172 | return { server: app }
173 | }
174 |
175 | createServer().then(({ server }) =>
176 | server.listen(3000, () => {
177 | // eslint-disable-next-line no-console
178 | console.info('http://localhost:3000')
179 | }),
180 | )
181 | ```
182 |
183 |
184 |
185 | In the above three ways, we need to strongly depend on the server, although we can use `ssr.noExternal` to package dependencies, but the server cannot be packaged in, you must install server package `npm install express`
186 |
187 | we can modify the server building entry file to avoid this, create a new file to wrapper your server instance(*`vite-ssr` will not works*), configure `ssr.noExternal: /./` to package all dependencies. if you want to runing anywhere, this is recommended.
188 |
189 | ```ts
190 | // src/server.ts
191 | import { startExpressServer as startServer } from 'vite-ssr-vue3/server'
192 | import { createApp } from './main'
193 |
194 | startServer({ createApp, root: __dirname, outDir: '..' })
195 | ```
196 |
197 | ```ts
198 | // vite.config.noexternal.ts
199 | import type { UserConfig } from 'vite'
200 | import { defineConfig } from 'vite'
201 |
202 | export default defineConfig({
203 | ssrOptions: {
204 | serverConfig: {
205 | build: {
206 | ssr: './src/server',
207 | },
208 | ssr: {
209 | noExternal: /./,
210 | },
211 | },
212 | },
213 | } as UserConfig)
214 | ```
215 |
216 | then run `vite-ssr build:noexternal`, wait for a while, now you can run `node dist/server/server.cjs` (Required right path) to start the server anywhere.
217 |
218 | ## Thanks
219 |
220 | Lots of references to the following repos.
221 |
222 | + [vite playground ssr-vue](https://github.com/vitejs/vite/tree/main/packages/playground/ssr-vue)
223 | + [vite-ssg](https://github.com/antfu/vite-ssg)
224 | + [vite-ssr](https://github.com/frandiox/vite-ssr)
225 |
226 | ## License
227 |
228 | [MIT](https://github.com/aliuq/vite-ssr-vue3/blob/master/LICENSE)
229 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/src/pages/form.vue:
--------------------------------------------------------------------------------
1 |
134 |
135 |
136 |
137 |
138 | Form
139 |
143 |
144 | 小
145 |
146 |
147 | 中
148 |
149 |
150 | 大
151 |
152 |
153 |
154 | /
155 |
156 |
157 |
158 |
168 |
169 |
170 |
171 |
172 |
181 |
182 |
183 |
188 |
189 |
190 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 | Option 1
208 |
209 |
210 | Option 2
211 |
212 |
213 | Option 3
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 | Radio 1
223 |
224 |
225 | Radio 2
226 |
227 |
228 | Radio 3
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 | Radio 1
237 |
238 |
239 | Radio 2
240 |
241 |
242 | Radio 3
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
260 |
261 |
262 |
263 |
264 |
268 |
269 |
270 |
275 |
276 |
277 |
278 |
279 |
280 | 验证
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
293 |
--------------------------------------------------------------------------------
/examples/vitesse-lite-naive-ui/auto-imports.d.ts:
--------------------------------------------------------------------------------
1 | // Generated by 'unplugin-auto-import'
2 | // We suggest you to commit this file into source control
3 | declare global {
4 | const $: typeof import('vue/macros')['$']
5 | const $$: typeof import('vue/macros')['$$']
6 | const $computed: typeof import('vue/macros')['$computed']
7 | const $customRef: typeof import('vue/macros')['$customRef']
8 | const $ref: typeof import('vue/macros')['$ref']
9 | const $shallowRef: typeof import('vue/macros')['$shallowRef']
10 | const $toRef: typeof import('vue/macros')['$toRef']
11 | const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
12 | const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
13 | const computed: typeof import('vue')['computed']
14 | const computedAsync: typeof import('@vueuse/core')['computedAsync']
15 | const computedEager: typeof import('@vueuse/core')['computedEager']
16 | const computedInject: typeof import('@vueuse/core')['computedInject']
17 | const computedWithControl: typeof import('@vueuse/core')['computedWithControl']
18 | const controlledComputed: typeof import('@vueuse/core')['controlledComputed']
19 | const controlledRef: typeof import('@vueuse/core')['controlledRef']
20 | const createApp: typeof import('vue')['createApp']
21 | const createEventHook: typeof import('@vueuse/core')['createEventHook']
22 | const createGlobalState: typeof import('@vueuse/core')['createGlobalState']
23 | const createInjectionState: typeof import('@vueuse/core')['createInjectionState']
24 | const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn']
25 | const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable']
26 | const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn']
27 | const customRef: typeof import('vue')['customRef']
28 | const debouncedRef: typeof import('@vueuse/core')['debouncedRef']
29 | const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch']
30 | const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
31 | const defineComponent: typeof import('vue')['defineComponent']
32 | const eagerComputed: typeof import('@vueuse/core')['eagerComputed']
33 | const effectScope: typeof import('vue')['effectScope']
34 | const EffectScope: typeof import('vue')['EffectScope']
35 | const extendRef: typeof import('@vueuse/core')['extendRef']
36 | const getCurrentInstance: typeof import('vue')['getCurrentInstance']
37 | const getCurrentScope: typeof import('vue')['getCurrentScope']
38 | const h: typeof import('vue')['h']
39 | const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
40 | const inject: typeof import('vue')['inject']
41 | const isDefined: typeof import('@vueuse/core')['isDefined']
42 | const isReadonly: typeof import('vue')['isReadonly']
43 | const isRef: typeof import('vue')['isRef']
44 | const logicAnd: typeof import('@vueuse/core')['logicAnd']
45 | const logicNot: typeof import('@vueuse/core')['logicNot']
46 | const logicOr: typeof import('@vueuse/core')['logicOr']
47 | const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
48 | const markRaw: typeof import('vue')['markRaw']
49 | const nextTick: typeof import('vue')['nextTick']
50 | const onActivated: typeof import('vue')['onActivated']
51 | const onBeforeMount: typeof import('vue')['onBeforeMount']
52 | const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
53 | const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
54 | const onClickOutside: typeof import('@vueuse/core')['onClickOutside']
55 | const onDeactivated: typeof import('vue')['onDeactivated']
56 | const onErrorCaptured: typeof import('vue')['onErrorCaptured']
57 | const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke']
58 | const onLongPress: typeof import('@vueuse/core')['onLongPress']
59 | const onMounted: typeof import('vue')['onMounted']
60 | const onRenderTracked: typeof import('vue')['onRenderTracked']
61 | const onRenderTriggered: typeof import('vue')['onRenderTriggered']
62 | const onScopeDispose: typeof import('vue')['onScopeDispose']
63 | const onServerPrefetch: typeof import('vue')['onServerPrefetch']
64 | const onStartTyping: typeof import('@vueuse/core')['onStartTyping']
65 | const onUnmounted: typeof import('vue')['onUnmounted']
66 | const onUpdated: typeof import('vue')['onUpdated']
67 | const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
68 | const provide: typeof import('vue')['provide']
69 | const reactify: typeof import('@vueuse/core')['reactify']
70 | const reactifyObject: typeof import('@vueuse/core')['reactifyObject']
71 | const reactive: typeof import('vue')['reactive']
72 | const reactiveComputed: typeof import('@vueuse/core')['reactiveComputed']
73 | const reactiveOmit: typeof import('@vueuse/core')['reactiveOmit']
74 | const reactivePick: typeof import('@vueuse/core')['reactivePick']
75 | const readonly: typeof import('vue')['readonly']
76 | const ref: typeof import('vue')['ref']
77 | const refAutoReset: typeof import('@vueuse/core')['refAutoReset']
78 | const refDebounced: typeof import('@vueuse/core')['refDebounced']
79 | const refDefault: typeof import('@vueuse/core')['refDefault']
80 | const refThrottled: typeof import('@vueuse/core')['refThrottled']
81 | const refWithControl: typeof import('@vueuse/core')['refWithControl']
82 | const resolveComponent: typeof import('vue')['resolveComponent']
83 | const shallowReactive: typeof import('vue')['shallowReactive']
84 | const shallowReadonly: typeof import('vue')['shallowReadonly']
85 | const shallowRef: typeof import('vue')['shallowRef']
86 | const syncRef: typeof import('@vueuse/core')['syncRef']
87 | const syncRefs: typeof import('@vueuse/core')['syncRefs']
88 | const templateRef: typeof import('@vueuse/core')['templateRef']
89 | const throttledRef: typeof import('@vueuse/core')['throttledRef']
90 | const throttledWatch: typeof import('@vueuse/core')['throttledWatch']
91 | const toRaw: typeof import('vue')['toRaw']
92 | const toReactive: typeof import('@vueuse/core')['toReactive']
93 | const toRef: typeof import('vue')['toRef']
94 | const toRefs: typeof import('vue')['toRefs']
95 | const triggerRef: typeof import('vue')['triggerRef']
96 | const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount']
97 | const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount']
98 | const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted']
99 | const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose']
100 | const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted']
101 | const unref: typeof import('vue')['unref']
102 | const unrefElement: typeof import('@vueuse/core')['unrefElement']
103 | const until: typeof import('@vueuse/core')['until']
104 | const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
105 | const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue']
106 | const useAsyncState: typeof import('@vueuse/core')['useAsyncState']
107 | const useAttrs: typeof import('vue')['useAttrs']
108 | const useBase64: typeof import('@vueuse/core')['useBase64']
109 | const useBattery: typeof import('@vueuse/core')['useBattery']
110 | const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints']
111 | const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel']
112 | const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation']
113 | const useCached: typeof import('@vueuse/core')['useCached']
114 | const useClamp: typeof import('@vueuse/core')['useClamp']
115 | const useClipboard: typeof import('@vueuse/core')['useClipboard']
116 | const useColorMode: typeof import('@vueuse/core')['useColorMode']
117 | const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog']
118 | const useCounter: typeof import('@vueuse/core')['useCounter']
119 | const useCssModule: typeof import('vue')['useCssModule']
120 | const useCssVar: typeof import('@vueuse/core')['useCssVar']
121 | const useCssVars: typeof import('vue')['useCssVars']
122 | const useCycleList: typeof import('@vueuse/core')['useCycleList']
123 | const useDark: typeof import('@vueuse/core')['useDark']
124 | const useDateFormat: typeof import('@vueuse/core')['useDateFormat']
125 | const useDebounce: typeof import('@vueuse/core')['useDebounce']
126 | const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory']
127 | const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn']
128 | const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion']
129 | const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation']
130 | const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio']
131 | const useDevicesList: typeof import('@vueuse/core')['useDevicesList']
132 | const useDisplayMedia: typeof import('@vueuse/core')['useDisplayMedia']
133 | const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility']
134 | const useDraggable: typeof import('@vueuse/core')['useDraggable']
135 | const useElementBounding: typeof import('@vueuse/core')['useElementBounding']
136 | const useElementByPoint: typeof import('@vueuse/core')['useElementByPoint']
137 | const useElementHover: typeof import('@vueuse/core')['useElementHover']
138 | const useElementSize: typeof import('@vueuse/core')['useElementSize']
139 | const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility']
140 | const useEventBus: typeof import('@vueuse/core')['useEventBus']
141 | const useEventListener: typeof import('@vueuse/core')['useEventListener']
142 | const useEventSource: typeof import('@vueuse/core')['useEventSource']
143 | const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper']
144 | const useFavicon: typeof import('@vueuse/core')['useFavicon']
145 | const useFetch: typeof import('@vueuse/core')['useFetch']
146 | const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess']
147 | const useFocus: typeof import('@vueuse/core')['useFocus']
148 | const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin']
149 | const useFps: typeof import('@vueuse/core')['useFps']
150 | const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
151 | const useGamepad: typeof import('@vueuse/core')['useGamepad']
152 | const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
153 | const useIdle: typeof import('@vueuse/core')['useIdle']
154 | const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll']
155 | const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver']
156 | const useInterval: typeof import('@vueuse/core')['useInterval']
157 | const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn']
158 | const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier']
159 | const useLastChanged: typeof import('@vueuse/core')['useLastChanged']
160 | const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage']
161 | const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys']
162 | const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory']
163 | const useMediaControls: typeof import('@vueuse/core')['useMediaControls']
164 | const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery']
165 | const useMemoize: typeof import('@vueuse/core')['useMemoize']
166 | const useMemory: typeof import('@vueuse/core')['useMemory']
167 | const useMounted: typeof import('@vueuse/core')['useMounted']
168 | const useMouse: typeof import('@vueuse/core')['useMouse']
169 | const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement']
170 | const useMousePressed: typeof import('@vueuse/core')['useMousePressed']
171 | const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver']
172 | const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage']
173 | const useNetwork: typeof import('@vueuse/core')['useNetwork']
174 | const useNow: typeof import('@vueuse/core')['useNow']
175 | const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination']
176 | const useOnline: typeof import('@vueuse/core')['useOnline']
177 | const usePageLeave: typeof import('@vueuse/core')['usePageLeave']
178 | const useParallax: typeof import('@vueuse/core')['useParallax']
179 | const usePermission: typeof import('@vueuse/core')['usePermission']
180 | const usePointer: typeof import('@vueuse/core')['usePointer']
181 | const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe']
182 | const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme']
183 | const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark']
184 | const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages']
185 | const useRafFn: typeof import('@vueuse/core')['useRafFn']
186 | const useRefHistory: typeof import('@vueuse/core')['useRefHistory']
187 | const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver']
188 | const useRoute: typeof import('vue-router')['useRoute']
189 | const useRouter: typeof import('vue-router')['useRouter']
190 | const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea']
191 | const useScriptTag: typeof import('@vueuse/core')['useScriptTag']
192 | const useScroll: typeof import('@vueuse/core')['useScroll']
193 | const useScrollLock: typeof import('@vueuse/core')['useScrollLock']
194 | const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage']
195 | const useShare: typeof import('@vueuse/core')['useShare']
196 | const useSlots: typeof import('vue')['useSlots']
197 | const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition']
198 | const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis']
199 | const useStorage: typeof import('@vueuse/core')['useStorage']
200 | const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync']
201 | const useStyleTag: typeof import('@vueuse/core')['useStyleTag']
202 | const useSwipe: typeof import('@vueuse/core')['useSwipe']
203 | const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
204 | const useTextSelection: typeof import('@vueuse/core')['useTextSelection']
205 | const useThrottle: typeof import('@vueuse/core')['useThrottle']
206 | const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory']
207 | const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn']
208 | const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo']
209 | const useTimeout: typeof import('@vueuse/core')['useTimeout']
210 | const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn']
211 | const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll']
212 | const useTimestamp: typeof import('@vueuse/core')['useTimestamp']
213 | const useTitle: typeof import('@vueuse/core')['useTitle']
214 | const useToggle: typeof import('@vueuse/core')['useToggle']
215 | const useTransition: typeof import('@vueuse/core')['useTransition']
216 | const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams']
217 | const useUserMedia: typeof import('@vueuse/core')['useUserMedia']
218 | const useVibrate: typeof import('@vueuse/core')['useVibrate']
219 | const useVirtualList: typeof import('@vueuse/core')['useVirtualList']
220 | const useVModel: typeof import('@vueuse/core')['useVModel']
221 | const useVModels: typeof import('@vueuse/core')['useVModels']
222 | const useWakeLock: typeof import('@vueuse/core')['useWakeLock']
223 | const useWebNotification: typeof import('@vueuse/core')['useWebNotification']
224 | const useWebSocket: typeof import('@vueuse/core')['useWebSocket']
225 | const useWebWorker: typeof import('@vueuse/core')['useWebWorker']
226 | const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn']
227 | const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus']
228 | const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll']
229 | const useWindowSize: typeof import('@vueuse/core')['useWindowSize']
230 | const watch: typeof import('vue')['watch']
231 | const watchAtMost: typeof import('@vueuse/core')['watchAtMost']
232 | const watchDebounced: typeof import('@vueuse/core')['watchDebounced']
233 | const watchEffect: typeof import('vue')['watchEffect']
234 | const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable']
235 | const watchOnce: typeof import('@vueuse/core')['watchOnce']
236 | const watchPausable: typeof import('@vueuse/core')['watchPausable']
237 | const watchThrottled: typeof import('@vueuse/core')['watchThrottled']
238 | const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
239 | const whenever: typeof import('@vueuse/core')['whenever']
240 | }
241 | export {}
242 |
--------------------------------------------------------------------------------