Skip to content

Commit 9ae7025

Browse files
authored
feat: vue server component (#1)
1 parent 702fcf8 commit 9ae7025

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+5316
-0
lines changed

.github/workflows/ci.yml

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: ci
2+
on:
3+
push:
4+
branches:
5+
- main
6+
pull_request:
7+
8+
concurrency:
9+
group: ci-${{ github.event.pull_request.number || github.ref }}
10+
cancel-in-progress: true
11+
12+
jobs:
13+
vue-server:
14+
runs-on: ubuntu-latest
15+
defaults:
16+
run:
17+
working-directory: ./vue-server
18+
steps:
19+
- uses: actions/checkout@v4
20+
- uses: actions/setup-node@v4
21+
with:
22+
node-version: 20
23+
- run: corepack enable
24+
- run: pnpm i
25+
- run: pnpm lint-check
26+
- run: pnpm tsc
27+
- run: pnpm test
28+
- run: pnpm exec playwright install chromium
29+
- run: pnpm test-e2e
30+
- run: pnpm build
31+
- run: pnpm test-e2e-preview

vue-server/.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules
2+
dist
3+
test-results
4+
.vite-node
5+
.wrangler

vue-server/README.md

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# vue-server
2+
3+
https://vue-server-demo.hiro18181.workers.dev/
4+
5+
## references
6+
7+
- https://github.com/hi-ogawa/js-utils/pull/227
8+
- https://github.com/nuxt/nuxt/issues/19772#issuecomment-1519023944
9+
- https://github.com/vuejs/core/blob/c0c9432b64091fa15fd8619cfb06828735356a42/packages/runtime-core/src/vnode.ts#L428
10+
- https://vuejs.org/guide/extras/render-function.html

vue-server/biome.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"$schema": "https://biomejs.dev/schemas/1.7.2/schema.json",
3+
"files": {
4+
"ignore": ["dist", ".wrangler", "test-results"]
5+
},
6+
"linter": { "enabled": false }
7+
}

vue-server/e2e/basic.test.ts

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { type Page, expect, test } from "@playwright/test";
2+
import { testNoJs, waitForHydration } from "./helper";
3+
4+
test("basic @js", async ({ page }) => {
5+
await page.goto("/");
6+
await waitForHydration(page);
7+
await page.getByRole("heading", { name: "Vue Server Component" }).click();
8+
await page.getByText("typeof window = undefined").click();
9+
await page.getByText("Count: 0").click();
10+
await page.getByRole("button", { name: "+" }).click();
11+
await page.getByText("Count: 1").click();
12+
await page.getByRole("button", { name: "-" }).click();
13+
await page.getByText("Count: 0").click();
14+
});
15+
16+
testNoJs("basic @nojs", async ({ page }) => {
17+
await page.goto("/");
18+
await page.getByRole("heading", { name: "Vue Server Component" }).click();
19+
await page.getByText("typeof window = undefined").click();
20+
await page.getByText("Count: 0").click();
21+
});
22+
23+
test("navigation @js", async ({ page }) => {
24+
await page.goto("/");
25+
await waitForHydration(page);
26+
await testNavigation(page, { js: true });
27+
});
28+
29+
testNoJs("navigation @nojs", async ({ page }) => {
30+
await page.goto("/");
31+
await testNavigation(page, { js: false });
32+
});
33+
34+
async function testNavigation(page: Page, options: { js: boolean }) {
35+
await page.getByPlaceholder("(test)").fill("hello");
36+
await page.getByRole("link", { name: "SFC" }).click();
37+
await page.waitForURL("/sfc");
38+
await page.getByRole("heading", { name: "Server SFC" }).click();
39+
await page.getByRole("button", { name: "client counter 0" }).first().click();
40+
if (options.js) {
41+
await page
42+
.getByRole("button", { name: "client counter 1" })
43+
.first()
44+
.click();
45+
}
46+
await expect(page.getByPlaceholder("(test)")).toHaveValue(
47+
options.js ? "hello" : "",
48+
);
49+
}

vue-server/e2e/helper.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import test, { type Page, expect } from "@playwright/test";
2+
3+
export const testNoJs = test.extend({
4+
javaScriptEnabled: ({}, use) => use(false),
5+
});
6+
7+
export async function waitForHydration(page: Page) {
8+
await expect(page.getByText("[mounted: 1]")).toBeVisible();
9+
}

vue-server/index.html

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>vue server demo</title>
6+
<meta
7+
name="viewport"
8+
content="width=device-width, height=device-height, initial-scale=1.0"
9+
/>
10+
</head>
11+
<body>
12+
<script type="module" src="/src/demo/entry-browser"></script>
13+
</body>
14+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# cloudflare-workers
2+
3+
copied from
4+
https://github.com/hi-ogawa/vite-plugins/tree/992368d0c2f23dbb6c2d8c67a7ce0546d610a671/packages/react-server/examples/basic/misc/cloudflare-workers
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#!/bin/bash
2+
set -eu -o pipefail
3+
4+
cd "$(dirname "${BASH_SOURCE[0]}")"
5+
6+
# clean
7+
rm -rf dist
8+
mkdir -p dist/server dist/client
9+
10+
# static
11+
cp -r ../../dist/client/. dist/client
12+
rm -rf dist/client/index.html
13+
14+
# server (bundle by ourselve instead of relying on wrangler)
15+
npx esbuild ../../dist/server/index.js \
16+
--outfile=dist/server/index.js \
17+
--metafile=dist/esbuild-metafile.json \
18+
--define:process.env.NODE_ENV='"production"' \
19+
--define:__VUE_OPTIONS_API__='false' \
20+
--define:__VUE_PROD_DEVTOOLS__='false' \
21+
--define:__VUE_PROD_HYDRATION_MISMATCH_DETAILS__='false' \
22+
--log-override:ignored-bare-import=silent \
23+
--bundle \
24+
--format=esm \
25+
--platform=browser
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
name = "vue-server-demo"
2+
3+
main = "dist/server/index.js"
4+
assets = "dist/client"
5+
workers_dev = true
6+
compatibility_date = "2024-01-01"
7+
compatibility_flags = ["nodejs_compat"]

vue-server/package.json

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"name": "experiments-vue-server",
3+
"private": true,
4+
"type": "module",
5+
"scripts": {
6+
"dev": "vite",
7+
"build": "vite build && vite build --ssr",
8+
"preview": "vite preview",
9+
"tsc": "tsc -b",
10+
"tsc-dev": "tsc -b --watch --preserveWatchOutput",
11+
"test": "vitest",
12+
"test-e2e": "playwright test",
13+
"test-e2e-preview": "E2E_PREVIEW=1 playwright test",
14+
"lint": "biome check --apply .",
15+
"lint-check": "biome check .",
16+
"cf-build": "SERVER_ENTRY=/src/demo/adapters/cloudflare-workers.ts pnpm build && bash misc/cloudflare-workers/build.sh",
17+
"cf-preview": "cd misc/cloudflare-workers && wrangler dev",
18+
"cf-release": "cd misc/cloudflare-workers && wrangler deploy"
19+
},
20+
"dependencies": {
21+
"@vue/shared": "^3.4.26",
22+
"highlight.js": "^11.9.0",
23+
"vue": "^3.4.26"
24+
},
25+
"devDependencies": {
26+
"@biomejs/biome": "^1.7.2",
27+
"@hattip/adapter-node": "^0.0.45",
28+
"@hiogawa/utils": "1.6.4-pre.2",
29+
"@hiogawa/vite-plugin-ssr-middleware": "^0.0.3",
30+
"@playwright/test": "^1.44.0",
31+
"@types/node": "^20.12.10",
32+
"@vitejs/plugin-vue": "^5.0.4",
33+
"esbuild": "^0.21.0",
34+
"happy-dom": "^14.10.1",
35+
"typescript": "^5.4.5",
36+
"vite": "^5.2.11",
37+
"vitest": "^1.6.0",
38+
"wrangler": "^3.53.1"
39+
},
40+
"packageManager": "[email protected]+sha512.f6d863130973207cb7a336d6b439a242a26ac8068077df530d6a86069419853dc1ffe64029ec594a9c505a3a410d19643c870aba6776330f5cfddcf10a9c1617"
41+
}

vue-server/playwright.config.ts

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { defineConfig, devices } from "@playwright/test";
2+
3+
const port = Number(process.env.E2E_PORT || 6174);
4+
const isPreview = Boolean(process.env.E2E_PREVIEW);
5+
const command = isPreview
6+
? `pnpm preview --port ${port} --strict-port`
7+
: `pnpm dev --port ${port} --strict-port`;
8+
9+
export default defineConfig({
10+
testDir: "e2e",
11+
use: {
12+
trace: "on-first-retry",
13+
},
14+
projects: [
15+
{
16+
name: "chromium",
17+
use: {
18+
...devices["Desktop Chrome"],
19+
viewport: null,
20+
deviceScaleFactor: undefined,
21+
},
22+
},
23+
],
24+
webServer: {
25+
command,
26+
port,
27+
},
28+
grepInvert: isPreview ? /@dev/ : /@build/,
29+
forbidOnly: !!process.env["CI"],
30+
retries: process.env["CI"] ? 2 : 0,
31+
reporter: "list",
32+
});

0 commit comments

Comments
 (0)