17 changed files with 446 additions and 92 deletions
Binary file not shown.
@ -0,0 +1,31 @@ |
|||
<script setup lang="ts"> |
|||
// This starter template is using Vue 3 <script setup> SFCs |
|||
// Check out https://vuejs.org/api/sfc-script-setup.html#script-setup |
|||
import HelloWorld from './components/HelloWorld.vue'; |
|||
</script> |
|||
|
|||
<template> |
|||
<div> |
|||
<a href="https://vite.dev" target="_blank"> |
|||
<img src="/vite.svg" class="logo" alt="Vite logo" />AAA |
|||
</a> |
|||
<a href="https://vuejs.org/" target="_blank"> |
|||
<img src="./assets/vue.svg" class="logo vue" alt="Vue logo" /> |
|||
</a> |
|||
</div> |
|||
<HelloWorld msg="Vite + Vue" /> |
|||
</template> |
|||
|
|||
<style scoped> |
|||
.logo { |
|||
height: 6em; |
|||
padding: 1.5em; |
|||
will-change: filter; |
|||
} |
|||
.logo:hover { |
|||
filter: drop-shadow(0 0 2em #646cffaa); |
|||
} |
|||
.logo.vue:hover { |
|||
filter: drop-shadow(0 0 2em #42b883aa); |
|||
} |
|||
</style> |
|||
|
After Width: | Height: | Size: 496 B |
@ -0,0 +1,38 @@ |
|||
<script setup lang="ts"> |
|||
import { ref } from 'vue'; |
|||
|
|||
defineProps<{ msg: string }>(); |
|||
|
|||
const count = ref(0); |
|||
</script> |
|||
|
|||
<template> |
|||
<h1>{{ msg }}</h1> |
|||
|
|||
<div class="card"> |
|||
<button type="button" @click="count++">count 啊啊撒打算 {{ count }}</button> |
|||
<p> |
|||
Edit |
|||
<code>components/HelloWorld.vue</code> to test HMR |
|||
</p> |
|||
</div> |
|||
|
|||
<p> |
|||
Check out |
|||
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank" |
|||
>create-vue</a |
|||
>, the official Vue + Vite starter |
|||
</p> |
|||
<p> |
|||
Install |
|||
<a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a> |
|||
in your IDE for a better DX |
|||
</p> |
|||
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p> |
|||
</template> |
|||
|
|||
<style scoped> |
|||
.read-the-docs { |
|||
color: #888; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,6 @@ |
|||
import './style.css' |
|||
import { createApp } from "./main" |
|||
|
|||
const { app } = createApp() |
|||
|
|||
app.mount('#app') |
|||
@ -0,0 +1,15 @@ |
|||
import { renderToString } from 'vue/server-renderer' |
|||
import { createApp } from './main' |
|||
|
|||
export async function render(_url: string) { |
|||
const { app } = createApp() |
|||
|
|||
// passing SSR context object which will be available via useSSRContext()
|
|||
// @vitejs/plugin-vue injects code into a component's setup() that registers
|
|||
// itself on ctx.modules. After the render, ctx.modules would contain all the
|
|||
// components that have been instantiated during this render call.
|
|||
const ctx = {} |
|||
const html = await renderToString(app, ctx) |
|||
|
|||
return { html } |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
import { createSSRApp } from 'vue' |
|||
import App from './App.vue' |
|||
|
|||
// SSR requires a fresh app instance per request, therefore we export a function
|
|||
// that creates a fresh app instance. If using Vuex, we'd also be creating a
|
|||
// fresh store here.
|
|||
export function createApp() { |
|||
const app = createSSRApp(App) |
|||
return { app } |
|||
} |
|||
@ -0,0 +1,79 @@ |
|||
:root { |
|||
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; |
|||
line-height: 1.5; |
|||
font-weight: 400; |
|||
|
|||
color-scheme: light dark; |
|||
color: rgba(255, 255, 255, 0.87); |
|||
background-color: #242424; |
|||
|
|||
font-synthesis: none; |
|||
text-rendering: optimizeLegibility; |
|||
-webkit-font-smoothing: antialiased; |
|||
-moz-osx-font-smoothing: grayscale; |
|||
} |
|||
|
|||
a { |
|||
font-weight: 500; |
|||
color: #646cff; |
|||
text-decoration: inherit; |
|||
} |
|||
a:hover { |
|||
color: #535bf2; |
|||
} |
|||
|
|||
body { |
|||
margin: 0; |
|||
display: flex; |
|||
place-items: center; |
|||
min-width: 320px; |
|||
min-height: 100vh; |
|||
} |
|||
|
|||
h1 { |
|||
font-size: 3.2em; |
|||
line-height: 1.1; |
|||
} |
|||
|
|||
button { |
|||
border-radius: 8px; |
|||
border: 1px solid transparent; |
|||
padding: 0.6em 1.2em; |
|||
font-size: 1em; |
|||
font-weight: 500; |
|||
font-family: inherit; |
|||
background-color: #1a1a1a; |
|||
cursor: pointer; |
|||
transition: border-color 0.25s; |
|||
} |
|||
button:hover { |
|||
border-color: #646cff; |
|||
} |
|||
button:focus, |
|||
button:focus-visible { |
|||
outline: 4px auto -webkit-focus-ring-color; |
|||
} |
|||
|
|||
.card { |
|||
padding: 2em; |
|||
} |
|||
|
|||
#app { |
|||
max-width: 1280px; |
|||
margin: 0 auto; |
|||
padding: 2rem; |
|||
text-align: center; |
|||
} |
|||
|
|||
@media (prefers-color-scheme: light) { |
|||
:root { |
|||
color: #213547; |
|||
background-color: #ffffff; |
|||
} |
|||
a:hover { |
|||
color: #747bff; |
|||
} |
|||
button { |
|||
background-color: #f9f9f9; |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
/// <reference types="vite/client" />
|
|||
|
|||
declare module '*.vue' { |
|||
import type { DefineComponent } from 'vue' |
|||
const component: DefineComponent<{}, {}, any> |
|||
export default component |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
<head> |
|||
<meta charset="UTF-8" /> |
|||
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
|||
<title>Vite + Vue + TS</title> |
|||
<!--app-head--> |
|||
</head> |
|||
<body> |
|||
<div id="app"><!--app-html--></div> |
|||
<script type="module" src="/client/entry-client.ts"></script> |
|||
</body> |
|||
</html> |
|||
|
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,66 @@ |
|||
import fs from 'node:fs/promises'; |
|||
import Koa from "koa"; |
|||
import c2k from 'koa-connect'; |
|||
import Send from 'koa-send'; |
|||
// Constants
|
|||
const isProduction = process.env.NODE_ENV === 'production'; |
|||
const port = process.env.PORT || 5173; |
|||
const base = process.env.BASE || '/'; |
|||
// Cached production assets
|
|||
const templateHtml = isProduction |
|||
? await fs.readFile('./dist/client/index.html', 'utf-8') |
|||
: ''; |
|||
const app = new Koa(); |
|||
// Add Vite or respective production middlewares
|
|||
/** @type {import('vite').ViteDevServer | undefined} */ |
|||
let vite; |
|||
if (!isProduction) { |
|||
const { createServer } = await import('vite'); |
|||
vite = await createServer({ |
|||
server: { middlewareMode: true }, |
|||
appType: 'custom', |
|||
base, |
|||
}); |
|||
app.use(c2k(vite.middlewares)); |
|||
} |
|||
else { |
|||
app.use(Send({ root: 'dist/client', index: false })); |
|||
} |
|||
app.use(async (ctx, next) => { |
|||
try { |
|||
const url = ctx.path.replace(base, ''); |
|||
/** @type {string} */ |
|||
let template; |
|||
/** @type {import('./client/entry-server.ts').render} */ |
|||
let render; |
|||
if (!isProduction) { |
|||
// Always read fresh template in development
|
|||
template = await fs.readFile('./index.html', 'utf-8'); |
|||
template = await vite.transformIndexHtml(url, template); |
|||
render = (await vite.ssrLoadModule('/client/entry-server.ts')).render; |
|||
} |
|||
else { |
|||
template = templateHtml; |
|||
// @ts-ignore
|
|||
render = (await import('./dist/server/entry-server.js')).render; |
|||
} |
|||
const rendered = await render(url); |
|||
const html = template |
|||
.replace(`<!--app-head-->`, rendered.head ?? '') |
|||
.replace(`<!--app-html-->`, rendered.html ?? ''); |
|||
ctx.status = 200; |
|||
ctx.set({ 'Content-Type': 'text/html' }); |
|||
ctx.body = html; |
|||
} |
|||
catch (e) { |
|||
vite?.ssrFixStacktrace(e); |
|||
console.log(e.stack); |
|||
ctx.status = 500; |
|||
ctx.body = e.stack; |
|||
} |
|||
await next(); |
|||
}); |
|||
// Start http server
|
|||
app.listen(port, () => { |
|||
console.log(`Server started at http://localhost:${port}`); |
|||
}); |
|||
@ -0,0 +1,71 @@ |
|||
import fs from 'node:fs/promises' |
|||
import Koa from "koa" |
|||
import c2k from 'koa-connect' |
|||
import { ViteDevServer } from 'vite' |
|||
import Send from 'koa-send' |
|||
|
|||
// Constants
|
|||
const isProduction = process.env.NODE_ENV === 'production' |
|||
const port = process.env.PORT || 5173 |
|||
const base = process.env.BASE || '/' |
|||
|
|||
// Cached production assets
|
|||
const templateHtml = isProduction |
|||
? await fs.readFile('./dist/client/index.html', 'utf-8') |
|||
: '' |
|||
|
|||
const app = new Koa() |
|||
|
|||
// Add Vite or respective production middlewares
|
|||
/** @type {import('vite').ViteDevServer | undefined} */ |
|||
let vite: ViteDevServer |
|||
if (!isProduction) { |
|||
const { createServer } = await import('vite') |
|||
vite = await createServer({ |
|||
server: { middlewareMode: true }, |
|||
appType: 'custom', |
|||
base, |
|||
}) |
|||
app.use(c2k(vite.middlewares)) |
|||
} else { |
|||
app.use(Send({ root: 'dist/client', index: false })) |
|||
} |
|||
app.use(async (ctx, next) => { |
|||
try { |
|||
const url = ctx.path.replace(base, '') |
|||
/** @type {string} */ |
|||
let template |
|||
/** @type {import('./client/entry-server.ts').render} */ |
|||
let render |
|||
if (!isProduction) { |
|||
// Always read fresh template in development
|
|||
template = await fs.readFile('./index.html', 'utf-8') |
|||
template = await vite.transformIndexHtml(url, template) |
|||
render = (await vite.ssrLoadModule('/client/entry-server.ts')).render |
|||
} else { |
|||
template = templateHtml |
|||
// @ts-ignore
|
|||
render = (await import('./dist/server/entry-server.js')).render |
|||
} |
|||
|
|||
const rendered = await render(url) |
|||
|
|||
const html = template |
|||
.replace(`<!--app-head-->`, rendered.head ?? '') |
|||
.replace(`<!--app-html-->`, rendered.html ?? '') |
|||
ctx.status = 200 |
|||
ctx.set({ 'Content-Type': 'text/html' }) |
|||
ctx.body = html |
|||
} catch (e: Error | any) { |
|||
vite?.ssrFixStacktrace(e) |
|||
console.log(e.stack) |
|||
ctx.status = 500 |
|||
ctx.body = e.stack |
|||
} |
|||
await next() |
|||
}) |
|||
|
|||
// Start http server
|
|||
app.listen(port, () => { |
|||
console.log(`Server started at http://localhost:${port}`) |
|||
}) |
|||
@ -0,0 +1,82 @@ |
|||
import { dirname, resolve } from "node:path" |
|||
import { fileURLToPath } from "node:url" |
|||
import module from "node:module" |
|||
import { defineConfig } from "vite" |
|||
import pkg from "./package.json" |
|||
import { viteStaticCopy } from "vite-plugin-static-copy" |
|||
|
|||
const __dirname = dirname(fileURLToPath(import.meta.url)) |
|||
|
|||
function getExternal(): string[] { |
|||
return [...Object.keys(pkg.dependencies || {}), ...module.builtinModules] |
|||
} |
|||
|
|||
export default defineConfig({ |
|||
publicDir: false, |
|||
resolve: { |
|||
alias: { |
|||
"@": resolve(__dirname, "src"), |
|||
db: resolve(__dirname, "src/db"), |
|||
config: resolve(__dirname, "src/config"), |
|||
utils: resolve(__dirname, "src/utils"), |
|||
}, |
|||
}, |
|||
build: { |
|||
lib: { |
|||
entry: resolve(__dirname, "src/main.js"), |
|||
formats: ["es"], |
|||
fileName: () => `[name].js`, |
|||
}, |
|||
outDir: resolve(__dirname, "dist"), |
|||
rollupOptions: { |
|||
external: getExternal(), |
|||
// watch: {
|
|||
// include: "src/**",
|
|||
// exclude: "node_modules/**",
|
|||
// },
|
|||
output: { |
|||
preserveModules: true, |
|||
preserveModulesRoot: "src", |
|||
inlineDynamicImports: false, |
|||
}, |
|||
}, |
|||
}, |
|||
plugins: [ |
|||
viteStaticCopy({ |
|||
targets: [ |
|||
{ |
|||
src: "public", |
|||
dest: "", |
|||
}, |
|||
{ |
|||
src: "src/views", |
|||
dest: "", |
|||
}, |
|||
{ |
|||
src: "src/db/migrations", |
|||
dest: "db", |
|||
}, |
|||
{ |
|||
src: "src/db/seeds", |
|||
dest: "db", |
|||
}, |
|||
{ |
|||
src: "entrypoint.sh", |
|||
dest: "", |
|||
}, |
|||
{ |
|||
src: "package.json", |
|||
dest: "", |
|||
}, |
|||
{ |
|||
src: "knexfile.mjs", |
|||
dest: "", |
|||
}, |
|||
{ |
|||
src: "bun.lockb", |
|||
dest: "", |
|||
}, |
|||
], |
|||
}), |
|||
], |
|||
}) |
|||
@ -1,82 +1,7 @@ |
|||
import { dirname, resolve } from "node:path" |
|||
import { fileURLToPath } from "node:url" |
|||
import module from "node:module" |
|||
import { defineConfig } from "vite" |
|||
import pkg from "./package.json" |
|||
import { viteStaticCopy } from "vite-plugin-static-copy" |
|||
|
|||
const __dirname = dirname(fileURLToPath(import.meta.url)) |
|||
|
|||
function getExternal(): string[] { |
|||
return [...Object.keys(pkg.dependencies || {}), ...module.builtinModules] |
|||
} |
|||
import { defineConfig } from 'vite' |
|||
import vue from '@vitejs/plugin-vue' |
|||
|
|||
// https://vite.dev/config/
|
|||
export default defineConfig({ |
|||
publicDir: false, |
|||
resolve: { |
|||
alias: { |
|||
"@": resolve(__dirname, "src"), |
|||
db: resolve(__dirname, "src/db"), |
|||
config: resolve(__dirname, "src/config"), |
|||
utils: resolve(__dirname, "src/utils"), |
|||
}, |
|||
}, |
|||
build: { |
|||
lib: { |
|||
entry: resolve(__dirname, "src/main.js"), |
|||
formats: ["es"], |
|||
fileName: () => `[name].js`, |
|||
}, |
|||
outDir: resolve(__dirname, "dist"), |
|||
rollupOptions: { |
|||
external: getExternal(), |
|||
// watch: {
|
|||
// include: "src/**",
|
|||
// exclude: "node_modules/**",
|
|||
// },
|
|||
output: { |
|||
preserveModules: true, |
|||
preserveModulesRoot: "src", |
|||
inlineDynamicImports: false, |
|||
}, |
|||
}, |
|||
}, |
|||
plugins: [ |
|||
viteStaticCopy({ |
|||
targets: [ |
|||
{ |
|||
src: "public", |
|||
dest: "", |
|||
}, |
|||
{ |
|||
src: "src/views", |
|||
dest: "", |
|||
}, |
|||
{ |
|||
src: "src/db/migrations", |
|||
dest: "db", |
|||
}, |
|||
{ |
|||
src: "src/db/seeds", |
|||
dest: "db", |
|||
}, |
|||
{ |
|||
src: "entrypoint.sh", |
|||
dest: "", |
|||
}, |
|||
{ |
|||
src: "package.json", |
|||
dest: "", |
|||
}, |
|||
{ |
|||
src: "knexfile.mjs", |
|||
dest: "", |
|||
}, |
|||
{ |
|||
src: "bun.lockb", |
|||
dest: "", |
|||
}, |
|||
], |
|||
}), |
|||
], |
|||
plugins: [vue()], |
|||
}) |
|||
|
|||
Loading…
Reference in new issue