Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: vue server component #1

Merged
merged 49 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
0b6d260
wip: vue vnode serialization
hi-ogawa May 6, 2024
5111b58
chore: readme
hi-ogawa May 6, 2024
2508807
chore: biome
hi-ogawa May 6, 2024
b8d59e3
test: simplify
hi-ogawa May 6, 2024
511b42e
chore: setup jsx
hi-ogawa May 6, 2024
cfb5ee0
wip: sfc setup
hi-ogawa May 6, 2024
44b4d4d
wip: support sfc
hi-ogawa May 7, 2024
aed6f88
chore: cleanup
hi-ogawa May 7, 2024
ac666ad
fix: fragment, text
hi-ogawa May 7, 2024
1a076c4
test: props
hi-ogawa May 7, 2024
0724e7b
test: props
hi-ogawa May 7, 2024
ea00d23
chore: comment
hi-ogawa May 7, 2024
9ecd3c7
test: app provide
hi-ogawa May 7, 2024
bf52510
test: context global
hi-ogawa May 7, 2024
7871e46
wip: deserialize
hi-ogawa May 7, 2024
895c9f7
test: deserialize
hi-ogawa May 7, 2024
1f495d7
wip: client reference
hi-ogawa May 7, 2024
13e0e17
wip: client slots
hi-ogawa May 7, 2024
76c4781
wip: client reference sfc
hi-ogawa May 7, 2024
26b54d3
wip: setup demo
hi-ogawa May 7, 2024
018553b
wip: demo client reference
hi-ogawa May 7, 2024
61d6412
fix: workaround hydration error
hi-ogawa May 7, 2024
e357eb6
chore: demo nested
hi-ogawa May 7, 2024
61058d0
chore: encode symbol
hi-ogawa May 7, 2024
af011eb
chore: client sfc
hi-ogawa May 7, 2024
43c3784
chore: deploy cloudflare
hi-ogawa May 7, 2024
5c2ab1a
chore: readme
hi-ogawa May 7, 2024
8f1e8e0
chore: tweak nested demo
hi-ogawa May 7, 2024
105349d
chore: tweak
hi-ogawa May 7, 2024
43f0e1b
chore: check hydration error
hi-ogawa May 7, 2024
2f76722
wip: props bug
hi-ogawa May 7, 2024
13e6016
fix: deal with props validation
hi-ogawa May 7, 2024
4aca20b
chore: client side navigation
hi-ogawa May 7, 2024
e6381ee
wip: multi pages
hi-ogawa May 7, 2024
7d9031d
chore: demo shiki
hi-ogawa May 7, 2024
5f29aa4
chore: give up shiki on cloudflare
hi-ogawa May 7, 2024
c7009e5
chore: isLoading state
hi-ogawa May 7, 2024
1361442
ci: setup
hi-ogawa May 7, 2024
7d323b0
chore: biome
hi-ogawa May 7, 2024
168a03c
chore: unused
hi-ogawa May 7, 2024
7ee91ca
test: deterministic snapshot
hi-ogawa May 7, 2024
d14c766
ci: tsc
hi-ogawa May 7, 2024
2db3760
feat: server sfc
hi-ogawa May 8, 2024
47c559b
test: e2e
hi-ogawa May 8, 2024
1804278
ci: e2e
hi-ogawa May 8, 2024
a52fe6e
chore: nojs
hi-ogawa May 8, 2024
eee0514
ci: playwright install
hi-ogawa May 8, 2024
78881b9
chore: server/client sfc slot interleave
hi-ogawa May 8, 2024
e9c82ed
chore: comment
hi-ogawa May 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: ci
on:
push:
branches:
- main
pull_request:

concurrency:
group: ci-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
vue-server:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./vue-server
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: corepack enable
- run: pnpm i
- run: pnpm lint-check
- run: pnpm tsc
- run: pnpm test
- run: pnpm exec playwright install chromium
- run: pnpm test-e2e
- run: pnpm build
- run: pnpm test-e2e-preview
5 changes: 5 additions & 0 deletions vue-server/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
dist
test-results
.vite-node
.wrangler
10 changes: 10 additions & 0 deletions vue-server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# vue-server

https://vue-server-demo.hiro18181.workers.dev/

## references

- https://github.com/hi-ogawa/js-utils/pull/227
- https://github.com/nuxt/nuxt/issues/19772#issuecomment-1519023944
- https://github.com/vuejs/core/blob/c0c9432b64091fa15fd8619cfb06828735356a42/packages/runtime-core/src/vnode.ts#L428
- https://vuejs.org/guide/extras/render-function.html
7 changes: 7 additions & 0 deletions vue-server/biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"$schema": "https://biomejs.dev/schemas/1.7.2/schema.json",
"files": {
"ignore": ["dist", ".wrangler", "test-results"]
},
"linter": { "enabled": false }
}
49 changes: 49 additions & 0 deletions vue-server/e2e/basic.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { type Page, expect, test } from "@playwright/test";
import { testNoJs, waitForHydration } from "./helper";

test("basic @js", async ({ page }) => {
await page.goto("/");
await waitForHydration(page);
await page.getByRole("heading", { name: "Vue Server Component" }).click();
await page.getByText("typeof window = undefined").click();
await page.getByText("Count: 0").click();
await page.getByRole("button", { name: "+" }).click();
await page.getByText("Count: 1").click();
await page.getByRole("button", { name: "-" }).click();
await page.getByText("Count: 0").click();
});

testNoJs("basic @nojs", async ({ page }) => {
await page.goto("/");
await page.getByRole("heading", { name: "Vue Server Component" }).click();
await page.getByText("typeof window = undefined").click();
await page.getByText("Count: 0").click();
});

test("navigation @js", async ({ page }) => {
await page.goto("/");
await waitForHydration(page);
await testNavigation(page, { js: true });
});

testNoJs("navigation @nojs", async ({ page }) => {
await page.goto("/");
await testNavigation(page, { js: false });
});

async function testNavigation(page: Page, options: { js: boolean }) {
await page.getByPlaceholder("(test)").fill("hello");
await page.getByRole("link", { name: "SFC" }).click();
await page.waitForURL("/sfc");
await page.getByRole("heading", { name: "Server SFC" }).click();
await page.getByRole("button", { name: "client counter 0" }).first().click();
if (options.js) {
await page
.getByRole("button", { name: "client counter 1" })
.first()
.click();
}
await expect(page.getByPlaceholder("(test)")).toHaveValue(
options.js ? "hello" : "",
);
}
9 changes: 9 additions & 0 deletions vue-server/e2e/helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import test, { type Page, expect } from "@playwright/test";

export const testNoJs = test.extend({
javaScriptEnabled: ({}, use) => use(false),
});

export async function waitForHydration(page: Page) {
await expect(page.getByText("[mounted: 1]")).toBeVisible();
}
14 changes: 14 additions & 0 deletions vue-server/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>vue server demo</title>
<meta
name="viewport"
content="width=device-width, height=device-height, initial-scale=1.0"
/>
</head>
<body>
<script type="module" src="/src/demo/entry-browser"></script>
</body>
</html>
4 changes: 4 additions & 0 deletions vue-server/misc/cloudflare-workers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# cloudflare-workers

copied from
https://github.com/hi-ogawa/vite-plugins/tree/992368d0c2f23dbb6c2d8c67a7ce0546d610a671/packages/react-server/examples/basic/misc/cloudflare-workers
25 changes: 25 additions & 0 deletions vue-server/misc/cloudflare-workers/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash
set -eu -o pipefail

cd "$(dirname "${BASH_SOURCE[0]}")"

# clean
rm -rf dist
mkdir -p dist/server dist/client

# static
cp -r ../../dist/client/. dist/client
rm -rf dist/client/index.html

# server (bundle by ourselve instead of relying on wrangler)
npx esbuild ../../dist/server/index.js \
--outfile=dist/server/index.js \
--metafile=dist/esbuild-metafile.json \
--define:process.env.NODE_ENV='"production"' \
--define:__VUE_OPTIONS_API__='false' \
--define:__VUE_PROD_DEVTOOLS__='false' \
--define:__VUE_PROD_HYDRATION_MISMATCH_DETAILS__='false' \
--log-override:ignored-bare-import=silent \
--bundle \
--format=esm \
--platform=browser
7 changes: 7 additions & 0 deletions vue-server/misc/cloudflare-workers/wrangler.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name = "vue-server-demo"

main = "dist/server/index.js"
assets = "dist/client"
workers_dev = true
compatibility_date = "2024-01-01"
compatibility_flags = ["nodejs_compat"]
41 changes: 41 additions & 0 deletions vue-server/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"name": "experiments-vue-server",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build && vite build --ssr",
"preview": "vite preview",
"tsc": "tsc -b",
"tsc-dev": "tsc -b --watch --preserveWatchOutput",
"test": "vitest",
"test-e2e": "playwright test",
"test-e2e-preview": "E2E_PREVIEW=1 playwright test",
"lint": "biome check --apply .",
"lint-check": "biome check .",
"cf-build": "SERVER_ENTRY=/src/demo/adapters/cloudflare-workers.ts pnpm build && bash misc/cloudflare-workers/build.sh",
"cf-preview": "cd misc/cloudflare-workers && wrangler dev",
"cf-release": "cd misc/cloudflare-workers && wrangler deploy"
},
"dependencies": {
"@vue/shared": "^3.4.26",
"highlight.js": "^11.9.0",
"vue": "^3.4.26"
},
"devDependencies": {
"@biomejs/biome": "^1.7.2",
"@hattip/adapter-node": "^0.0.45",
"@hiogawa/utils": "1.6.4-pre.2",
"@hiogawa/vite-plugin-ssr-middleware": "^0.0.3",
"@playwright/test": "^1.44.0",
"@types/node": "^20.12.10",
"@vitejs/plugin-vue": "^5.0.4",
"esbuild": "^0.21.0",
"happy-dom": "^14.10.1",
"typescript": "^5.4.5",
"vite": "^5.2.11",
"vitest": "^1.6.0",
"wrangler": "^3.53.1"
},
"packageManager": "pnpm@9.0.6+sha512.f6d863130973207cb7a336d6b439a242a26ac8068077df530d6a86069419853dc1ffe64029ec594a9c505a3a410d19643c870aba6776330f5cfddcf10a9c1617"
}
32 changes: 32 additions & 0 deletions vue-server/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { defineConfig, devices } from "@playwright/test";

const port = Number(process.env.E2E_PORT || 6174);
const isPreview = Boolean(process.env.E2E_PREVIEW);
const command = isPreview
? `pnpm preview --port ${port} --strict-port`
: `pnpm dev --port ${port} --strict-port`;

export default defineConfig({
testDir: "e2e",
use: {
trace: "on-first-retry",
},
projects: [
{
name: "chromium",
use: {
...devices["Desktop Chrome"],
viewport: null,
deviceScaleFactor: undefined,
},
},
],
webServer: {
command,
port,
},
grepInvert: isPreview ? /@dev/ : /@build/,
forbidOnly: !!process.env["CI"],
retries: process.env["CI"] ? 2 : 0,
reporter: "list",
});
Loading