Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 15 additions & 0 deletions examples/vite-ssr-qwik/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"type": "module",
"scripts": {
"build": "vite build --ssr",
"dev": "vite",
"preview": "vite preview"
},
"devDependencies": {
"@qwik.dev/core": "^2.0.0-beta.15",
"@qwik.dev/router": "^2.0.0-beta.15",
"nitro": "latest",
"typescript": "^5.9.3",
"vite": "^7.1.10"
}
}
1 change: 1 addition & 0 deletions examples/vite-ssr-qwik/public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions examples/vite-ssr-qwik/public/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"$schema": "https://json.schemastore.org/web-manifest-combined.json",
"name": "qwik-project-name",
"short_name": "Welcome to Qwik",
"start_url": ".",
"display": "standalone",
"background_color": "#fff",
"description": "A Qwik project app."
}
17 changes: 17 additions & 0 deletions examples/vite-ssr-qwik/src/entry-server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { createQwikRouter } from "./qwik-fetch-adapter.ts";
import render from "./entry.ssr.tsx";

const { router, notFound } = createQwikRouter({ render });

export default {
fetch: async (req: Request) => {
console.log(`[${req.method}] ${req.url}`);
const qwikRouterResponse = await router(req);
if (qwikRouterResponse) {
return qwikRouterResponse;
}

// Path not found
return notFound(req);
},
};
16 changes: 16 additions & 0 deletions examples/vite-ssr-qwik/src/entry.ssr.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { createRenderer } from "@qwik.dev/router";
import Root from "./root.tsx";

export default createRenderer((opts) => {
return {
jsx: <Root />,
options: {
...opts,
containerAttributes: {
lang: "en-us",
...opts.containerAttributes,
},
serverData: { ...opts.serverData },
},
};
});
3 changes: 3 additions & 0 deletions examples/vite-ssr-qwik/src/global.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.pt-2 {
padding-top: 2rem;
}
111 changes: 111 additions & 0 deletions examples/vite-ssr-qwik/src/qwik-fetch-adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { setServerPlatform } from "@qwik.dev/core/server";
import {
type ClientConn,
type ServerRenderOptions,
type ServerRequestEvent,
getNotFound,
isStaticPath,
mergeHeadersCookies,
requestHandler,
} from "@qwik.dev/router/middleware/request-handler";

export function createQwikRouter(opts: QwikRouterFetchOptions) {
if (opts.manifest) {

Check failure on line 13 in examples/vite-ssr-qwik/src/qwik-fetch-adapter.ts

View workflow job for this annotation

GitHub Actions / tests-checks (ubuntu-latest)

Property 'manifest' does not exist on type 'QwikRouterFetchOptions'.

Check failure on line 13 in examples/vite-ssr-qwik/src/qwik-fetch-adapter.ts

View workflow job for this annotation

GitHub Actions / tests-rollup (ubuntu-latest)

Property 'manifest' does not exist on type 'QwikRouterFetchOptions'.
setServerPlatform(opts.manifest);

Check failure on line 14 in examples/vite-ssr-qwik/src/qwik-fetch-adapter.ts

View workflow job for this annotation

GitHub Actions / tests-checks (ubuntu-latest)

Property 'manifest' does not exist on type 'QwikRouterFetchOptions'.

Check failure on line 14 in examples/vite-ssr-qwik/src/qwik-fetch-adapter.ts

View workflow job for this annotation

GitHub Actions / tests-rollup (ubuntu-latest)

Property 'manifest' does not exist on type 'QwikRouterFetchOptions'.
}
async function router(request: Request) {
try {
const url = new URL(request.url);

const serverRequestEv: ServerRequestEvent<Response> = {
mode: "server",
locale: undefined,
url,
env: { get: (p: string) => process.env[p] },
request,
getWritableStream: (status, headers, cookies, resolve) => {
const { readable, writable } = new TransformStream<Uint8Array>();
const response = new Response(readable, {
status,
headers: mergeHeadersCookies(headers, cookies),
});
resolve(response);
return writable;
},
platform: {
ssr: true,
},
getClientConn: () => {
return opts.getClientConn ? opts.getClientConn(request) : {};
},
};

// send request to qwik router request handler
const handledResponse = await requestHandler(serverRequestEv, opts);
if (handledResponse) {
handledResponse.completion.then((v) => {
if (v) {
console.error(v);
}
});
const response = await handledResponse.response;
if (response) {
return response;
}
}

// qwik router did not have a route for this request
return null;
} catch (error: any) {
console.error(error);
return new Response(String(error || "Error"), {
status: 500,
headers: {
"Content-Type": "text/plain; charset=utf-8",
},
});
}
}

const notFound = async (request: Request) => {
try {
const url = new URL(request.url);

// In the development server, we replace the getNotFound function
// For static paths, we assign a static "Not Found" message.
// This ensures consistency between development and production environments for specific URLs.
const notFoundHtml =
!request.headers.get("accept")?.includes("text/html") ||
isStaticPath(request.method || "GET", url)
? "Not Found"
: getNotFound(url.pathname);
return new Response(notFoundHtml, {
status: 404,
headers: {
"Content-Type": "text/html; charset=utf-8",
"X-Not-Found": url.pathname,
},
});
} catch (error) {
console.error(error);
return new Response(String(error || "Error"), {
status: 500,
headers: {
"Content-Type": "text/plain; charset=utf-8",
},
});
}
};

return {
router,
notFound,
};
}

export interface QwikRouterFetchOptions extends Omit<
ServerRenderOptions,
"qwikCityPlan"
> {
getClientConn?: (request: Request) => ClientConn;
}
31 changes: 31 additions & 0 deletions examples/vite-ssr-qwik/src/root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { component$ } from "@qwik.dev/core";
import {
DocumentHeadTags,
RouterOutlet,
useLocation,
useQwikRouter,
} from "@qwik.dev/router";

import "./global.css";

export default component$(() => {
useQwikRouter();
const { url } = useLocation();

return (
<>
<head>
<meta charset="utf-8" />

Check failure on line 18 in examples/vite-ssr-qwik/src/root.tsx

View workflow job for this annotation

GitHub Actions / tests-checks (ubuntu-latest)

Type '{ charset: string; }' is not assignable to type 'DetailedHTMLProps<MetaHTMLAttributes<HTMLMetaElement>, HTMLMetaElement>'.

Check failure on line 18 in examples/vite-ssr-qwik/src/root.tsx

View workflow job for this annotation

GitHub Actions / tests-rollup (ubuntu-latest)

Type '{ charset: string; }' is not assignable to type 'DetailedHTMLProps<MetaHTMLAttributes<HTMLMetaElement>, HTMLMetaElement>'.
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />

<DocumentHeadTags />

<link rel="canonical" href={url.href} />
</head>
<body>
<RouterOutlet />
</body>
</>
);
});
59 changes: 59 additions & 0 deletions examples/vite-ssr-qwik/src/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { component$, useSignal } from "@qwik.dev/core";
import { Form, routeAction$, routeLoader$, server$ } from "@qwik.dev/router";
import type { DocumentHead } from "@qwik.dev/router";

export const useLoader = routeLoader$(() => {
return "data from routeLoader$";
});

export const useAction = routeAction$(async (data) => {
return {
success: true,
firstName: data?.firstName?.toString(),
lastName: data?.lastName?.toString(),
};
});

const serverFunction = server$(() => {
console.log("server function");
return "data from server$, look at the server console!";
});

export default component$(() => {
const action = useAction();
const loaderData = useLoader();
const counterSig = useSignal(0);

return (
<div>
<h1>Hello, Qwik!</h1>
<h2>{loaderData.value}</h2>
<button onClick$={() => (counterSig.value += 1)}>

Check failure on line 31 in examples/vite-ssr-qwik/src/routes/index.tsx

View workflow job for this annotation

GitHub Actions / tests-checks (ubuntu-latest)

Type '{ children: any[]; onClick$: () => any; }' is not assignable to type 'DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>'.

Check failure on line 31 in examples/vite-ssr-qwik/src/routes/index.tsx

View workflow job for this annotation

GitHub Actions / tests-rollup (ubuntu-latest)

Type '{ children: any[]; onClick$: () => any; }' is not assignable to type 'DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>'.
Count: {counterSig.value}
</button>
<br></br>
<br></br>
<button
onClick$={async () => {

Check failure on line 37 in examples/vite-ssr-qwik/src/routes/index.tsx

View workflow job for this annotation

GitHub Actions / tests-checks (ubuntu-latest)

Type '{ children: string; onClick$: () => Promise<void>; }' is not assignable to type 'DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>'.

Check failure on line 37 in examples/vite-ssr-qwik/src/routes/index.tsx

View workflow job for this annotation

GitHub Actions / tests-rollup (ubuntu-latest)

Type '{ children: string; onClick$: () => Promise<void>; }' is not assignable to type 'DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>'.
const res = await serverFunction();
alert(res);
}}
>
server$ function
</button>
<Form class="pt-2" action={action}>

Check failure on line 44 in examples/vite-ssr-qwik/src/routes/index.tsx

View workflow job for this annotation

GitHub Actions / tests-checks (ubuntu-latest)

Tag 'Form' expects at least '2' arguments, but the JSX factory 'React.createElement' provides at most '1'.

Check failure on line 44 in examples/vite-ssr-qwik/src/routes/index.tsx

View workflow job for this annotation

GitHub Actions / tests-checks (ubuntu-latest)

'Form' cannot be used as a JSX component.

Check failure on line 44 in examples/vite-ssr-qwik/src/routes/index.tsx

View workflow job for this annotation

GitHub Actions / tests-rollup (ubuntu-latest)

Tag 'Form' expects at least '2' arguments, but the JSX factory 'React.createElement' provides at most '1'.

Check failure on line 44 in examples/vite-ssr-qwik/src/routes/index.tsx

View workflow job for this annotation

GitHub Actions / tests-rollup (ubuntu-latest)

'Form' cannot be used as a JSX component.
<input name="firstName" />
<input name="lastName" />
<button type="submit">Add user</button>
</Form>
{action.value?.success && (

Check failure on line 49 in examples/vite-ssr-qwik/src/routes/index.tsx

View workflow job for this annotation

GitHub Actions / tests-checks (ubuntu-latest)

Property 'success' does not exist on type '{}'.

Check failure on line 49 in examples/vite-ssr-qwik/src/routes/index.tsx

View workflow job for this annotation

GitHub Actions / tests-rollup (ubuntu-latest)

Property 'success' does not exist on type '{}'.
<p class="pt-2">

Check failure on line 50 in examples/vite-ssr-qwik/src/routes/index.tsx

View workflow job for this annotation

GitHub Actions / tests-checks (ubuntu-latest)

Type '{ children: any[]; class: string; }' is not assignable to type 'DetailedHTMLProps<HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>'.

Check failure on line 50 in examples/vite-ssr-qwik/src/routes/index.tsx

View workflow job for this annotation

GitHub Actions / tests-rollup (ubuntu-latest)

Type '{ children: any[]; class: string; }' is not assignable to type 'DetailedHTMLProps<HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>'.
User {action.value.firstName} {action.value.lastName} added

Check failure on line 51 in examples/vite-ssr-qwik/src/routes/index.tsx

View workflow job for this annotation

GitHub Actions / tests-checks (ubuntu-latest)

Property 'firstName' does not exist on type '{}'.

Check failure on line 51 in examples/vite-ssr-qwik/src/routes/index.tsx

View workflow job for this annotation

GitHub Actions / tests-rollup (ubuntu-latest)

Property 'firstName' does not exist on type '{}'.
successfully
</p>
)}
</div>
);
});

export const head: DocumentHead = { title: "Vite + Nitro + Qwik", meta: [] };
13 changes: 13 additions & 0 deletions examples/vite-ssr-qwik/src/routes/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { component$, Slot } from "@qwik.dev/core";

export default component$(() => {
return (
<>
<header>Layout Header</header>
<main>
<Slot />
</main>
<footer>Layout Footer</footer>
</>
);
});
11 changes: 11 additions & 0 deletions examples/vite-ssr-qwik/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "nitro/tsconfig",
"compilerOptions": {
"lib": ["es2022", "DOM", "WebWorker", "DOM.Iterable"],
"jsx": "react-jsx",
"jsxImportSource": "@qwik.dev/core",
"paths": {
"~/*": ["./src/*"]
}
}
}
63 changes: 63 additions & 0 deletions examples/vite-ssr-qwik/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { defineConfig, type Plugin } from "vite";
import { nitro } from "nitro/vite";
import { qwikVite } from "@qwik.dev/core/optimizer";
import { qwikRouter } from "@qwik.dev/router/vite";

export default defineConfig(() => {
return {
plugins: [
qwikRouter({ devSsrServer: false }),
qwikVite({
ssr: {
input: "./src/entry-server.ts",
outDir: "node_modules/.nitro/vite/services/ssr",
},
}),
qwikPatches(),
nitro({ noExternals: true }),
],
environments: {
ssr: {
build: {
rollupOptions: { input: "./src/entry-server.ts" },
},
resolve: {
external: [
// "@qwik.dev/core/build",
// "@qwik.dev/core/preloader",
// "@qwik.dev/core/server",
],
},
},
},
};
});

function qwikPatches(): Plugin {
return {
name: "qwik-patch-plugin",
enforce: "pre",
config() {},
resolveId: {
order: "pre",
handler(id: string) {
if (id === "@qwik-client-manifest") {
return "virtual:qwik-client-manifest";
}
},
},
load: {
order: "pre",
handler(id: string) {
if (id === "virtual:qwik-client-manifest") {
return `export const manifest= {}`;
}
},
},
transform: {
handler(code: string, id: string) {
// ...
},
},
};
}
Loading
Loading