Skip to content

Commit

Permalink
Add vite deno integration test
Browse files Browse the repository at this point in the history
  • Loading branch information
redabacha committed Sep 2, 2024
1 parent b94e223 commit 1228943
Show file tree
Hide file tree
Showing 13 changed files with 309 additions and 45 deletions.
4 changes: 4 additions & 0 deletions integration/helpers/vite-deno-template/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
/app/deno.d.ts
/build
/package.json
25 changes: 25 additions & 0 deletions integration/helpers/vite-deno-template/app/root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {
Links,
Meta,
Outlet,
Scripts,
ScrollRestoration,
} from "@remix-run/react";

export default function App() {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta />
<Links />
</head>
<body>
<Outlet />
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}
41 changes: 41 additions & 0 deletions integration/helpers/vite-deno-template/app/routes/_index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { MetaFunction } from "@remix-run/react";

export const meta: MetaFunction = () => {
return [
{ title: "New Remix App" },
{ name: "description", content: "Welcome to Remix!" },
];
};

export default function Index() {
return (
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}>
<h1>Welcome to Remix</h1>
<ul>
<li>
<a
target="_blank"
href="https://remix.run/tutorials/blog"
rel="noreferrer"
>
15m Quickstart Blog Tutorial
</a>
</li>
<li>
<a
target="_blank"
href="https://remix.run/tutorials/jokes"
rel="noreferrer"
>
Deep Dive Jokes App Tutorial
</a>
</li>
<li>
<a target="_blank" href="https://remix.run/docs" rel="noreferrer">
Remix Docs
</a>
</li>
</ul>
</div>
);
}
28 changes: 28 additions & 0 deletions integration/helpers/vite-deno-template/deno.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"tasks": {
"build": "deno run -A npm:@remix-run/dev@^2.11.2 vite:build",
"dev": "deno run -A npm:@remix-run/dev@^2.11.2 vite:dev",
"typecheck": "deno check '**/*' && deno run -A npm:typescript@^5.5.4/tsc",
"typegen": "deno types > ./app/deno.d.ts"
},
"exclude": ["app/", "build/"],
"nodeModulesDir": true,
"imports": {
"@remix-run/dev": "npm:@remix-run/dev@^2.11.2",
"@remix-run/express": "npm:@remix-run/express@^2.11.2",
"@remix-run/react": "npm:@remix-run/react@^2.11.2",
"@remix-run/server-runtime": "npm:@remix-run/server-runtime@^2.11.2",
"@std/http": "jsr:@std/http@^1.0.4",
"@std/path": "jsr:@std/path@^1.0.3",
"@types/node": "npm:@types/node@^22.5.1",
"@types/react": "npm:@types/react@^18.3.5",
"@types/react-dom": "npm:@types/react-dom@^18.3.0",
"isbot": "npm:isbot@^5.1.17",
"postcss": "npm:postcss@^8.4.41",
"react": "npm:react@^18.3.1",
"react-dom": "npm:react-dom@^18.3.1",
"typescript": "npm:typescript@^5.5.4",
"vite": "npm:vite@^5.4.2",
"vite-tsconfig-paths": "npm:vite-tsconfig-paths@^5.0.1"
}
}
Binary file not shown.
32 changes: 32 additions & 0 deletions integration/helpers/vite-deno-template/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"include": [
"app/**/*.ts",
"app/**/*.tsx",
"app/**/.server/**/*.ts",
"app/**/.server/**/*.tsx",
"app/**/.client/**/*.ts",
"app/**/.client/**/*.tsx"
],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"types": ["vite/client"],
"isolatedModules": true,
"esModuleInterop": true,
"jsx": "react-jsx",
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"target": "ES2022",
"strict": true,
"allowJs": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
"paths": {
"~/*": ["./app/*"]
},

// Vite takes care of building everything, not tsc.
"noEmit": true
}
}
16 changes: 16 additions & 0 deletions integration/helpers/vite-deno-template/vite.config.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";

export default defineConfig({
plugins: [
remix({
future: {
v3_fetcherPersist: true,
v3_relativeSplatPath: true,
v3_throwAbortReason: true,
},
}),
tsconfigPaths(),
],
});
79 changes: 68 additions & 11 deletions integration/helpers/vite.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import type { Page } from "@playwright/test";
import { test as base, expect } from "@playwright/test";
import dedent from "dedent";
import fse from "fs-extra";
import getPort from "get-port";
import glob from "glob";
import { spawn, spawnSync, type ChildProcess } from "node:child_process";
import path from "node:path";
import fs from "node:fs/promises";
import path from "node:path";
import type { Readable } from "node:stream";
import url from "node:url";
import fse from "fs-extra";
import shell from "shelljs";
import stripIndent from "strip-indent";
import waitOn from "wait-on";
import getPort from "get-port";
import shell from "shelljs";
import glob from "glob";
import dedent from "dedent";
import type { Page } from "@playwright/test";
import { test as base, expect } from "@playwright/test";

const denoBin = "deno"; // assume deno is globally installed
const remixBin = "node_modules/@remix-run/dev/dist/cli.js";
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
const root = path.resolve(__dirname, "../..");
Expand Down Expand Up @@ -90,7 +91,10 @@ export const EXPRESS_SERVER = (args: {
app.listen(port, () => console.log('http://localhost:' + port));
`;

type TemplateName = "vite-template" | "vite-cloudflare-template";
type TemplateName =
| "vite-template"
| "vite-cloudflare-template"
| "vite-deno-template";

export async function createProject(
files: Record<string, string> = {},
Expand Down Expand Up @@ -143,6 +147,23 @@ export const viteBuild = ({
});
};

export const viteBuildDeno = ({
cwd,
env = {},
}: {
cwd: string;
env?: Record<string, string>;
}) => {
return spawnSync(denoBin, ["run", "-A", remixBin, "vite:build"], {
cwd,
env: {
...process.env,
...colorEnv,
...env,
},
});
};

export const viteRemixServe = async ({
cwd,
port,
Expand Down Expand Up @@ -204,14 +225,16 @@ type ServerArgs = {
};

const createDev =
(nodeArgs: string[]) =>
(args: string[], runtime = node) =>
async ({ cwd, port, env, basename }: ServerArgs): Promise<() => unknown> => {
let proc = node(nodeArgs, { cwd, env });
let proc = runtime(args, { cwd, env });
await waitForServer(proc, { port, basename });
return () => proc.kill();
};

export const viteDev = createDev([remixBin, "vite:dev"]);
export const viteDevDeno = createDev(["run", "-A", remixBin, "vite:dev"], deno);

export const customDev = createDev(["./server.mjs"]);

// Used for testing errors thrown on build when we don't want to start and
Expand Down Expand Up @@ -240,6 +263,13 @@ type Fixtures = {
port: number;
cwd: string;
}>;
viteDevDeno: (
files: Files,
templateName?: TemplateName
) => Promise<{
port: number;
cwd: string;
}>;
customDev: (files: Files) => Promise<{
port: number;
cwd: string;
Expand Down Expand Up @@ -272,6 +302,17 @@ export const test = base.extend<Fixtures>({
stop?.();
},
// eslint-disable-next-line no-empty-pattern
viteDevDeno: async ({}, use) => {
let stop: (() => unknown) | undefined;
await use(async (files, template) => {
let port = await getPort();
let cwd = await createProject(await files({ port }), template);
stop = await viteDevDeno({ cwd, port });
return { port, cwd };
});
stop?.();
},
// eslint-disable-next-line no-empty-pattern
customDev: async ({}, use) => {
let stop: (() => unknown) | undefined;
await use(async (files) => {
Expand Down Expand Up @@ -331,6 +372,22 @@ function node(
return proc;
}

function deno(
args: string[],
options: { cwd: string; env?: Record<string, string> }
) {
let proc = spawn(denoBin, args, {
cwd: options.cwd,
env: {
...process.env,
...colorEnv,
...options.env,
},
stdio: "pipe",
});
return proc;
}

async function waitForServer(
proc: ChildProcess & { stdout: Readable; stderr: Readable },
args: { port: number; basename?: string }
Expand Down
90 changes: 90 additions & 0 deletions integration/vite-deno-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { expect } from "@playwright/test";
import dedent from "dedent";

import type { Files } from "./helpers/vite.js";
import { test, viteConfig } from "./helpers/vite.js";

const files: Files = async ({ port }) => ({
"vite.config.ts": dedent`
export default {
${await viteConfig.server({ port })}
plugins: [
remix(),
],
}
`,
"app/routes/_index.tsx": `
import { Form, useLoaderData } from "@remix-run/react";
import {
json,
type ActionFunctionArgs,
type LoaderFunctionArgs,
} from "@remix-run/server-runtime";
const key = "__my-key__";
export async function loader({ context }: LoaderFunctionArgs) {
const MY_KV = await Deno.openKv("test");
const { value } = await MY_KV.get<string>([key]);
return json({ value, extra: context.extra });
}
export async function action({ request }: ActionFunctionArgs) {
const MY_KV = await Deno.openKv("test");
if (request.method === "POST") {
const formData = await request.formData();
const value = formData.get("value") as string;
await MY_KV.set([key], value);
return null;
}
if (request.method === "DELETE") {
await MY_KV.delete([key]);
return null;
}
throw new Error(\`Method not supported: "\${request.method}"\`);
}
export default function Index() {
const { value } = useLoaderData<typeof loader>();
return (
<div>
<h1>Welcome to Remix</h1>
{value ? (
<>
<p data-text>Value: {value}</p>
<Form method="DELETE">
<button>Delete</button>
</Form>
</>
) : (
<>
<p data-text>No value</p>
<Form method="POST">
<label htmlFor="value">Set value:</label>
<input type="text" name="value" id="value" required />
<br />
<button>Save</button>
</Form>
</>
)}
</div>
);
}
`,
});

test("vite dev", async ({ page, viteDevDeno }) => {
let { port } = await viteDevDeno(files, "vite-deno-template");
await page.goto(`http://localhost:${port}/`, {
waitUntil: "networkidle",
});
await expect(page.locator("[data-text]")).toHaveText("No value");

await page.getByLabel("Set value:").fill("my-value");
await page.getByRole("button").click();
await expect(page.locator("[data-text]")).toHaveText("Value: my-value");
expect(page.errors).toEqual([]);
});
5 changes: 1 addition & 4 deletions templates/deno/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ deno task dev

This starts your app in development mode, rebuilding assets on file changes.

The server used for development is located in `server/development.ts`. It is an
express server running Vite in middleware mode.

## Production

First, build your app for production:
Expand All @@ -57,7 +54,7 @@ Then run the app in production mode:
deno task start
```

The server used for production is located in `server/production.ts`. It is
The server used for production is located in `server.production.ts`. It is
served by `deno serve` for maximum performance.

## Deployment
Expand Down
Loading

0 comments on commit 1228943

Please sign in to comment.