Skip to content
Merged
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
56 changes: 56 additions & 0 deletions .moon/tasks/tag-react-router.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
$schema: "https://moonrepo.dev/schemas/tasks.json"

fileGroups:
react-router-config:
- react-router.config.ts
- vite.config.ts
react-router-app:
- app/**/*
react-router-public:
- public/**/*
react-router-target:
- target/react-router/**/*
react-router-build:
- build/**/*

tasks:
react-router-dev:
preset: server
command: yarn dev
inputs:
- "@group(react-router-config)"
- "@group(react-router-app)"
- "@group(react-router-public)"
react-router-build:
env:
PROJECT_ROOT: $projectRoot
deps:
- ~:react-router-typecheck
- ~:react-router-react-router-build
script: >-
$workspaceRoot/etc/scripts/actions/copy-react-router-build.ts
inputs:
- "@group(react-router-build)"
outputs:
- "@group(react-router-target)"
react-router-react-router-build:
command: yarn build
inputs:
- "@group(react-router-config)"
- "@group(react-router-app)"
- "@group(react-router-public)"
outputs:
- "@group(react-router-build)"
react-router-start-csr:
preset: server
deps:
- ~:react-router-build
options:
runDepsInParallel: false
command: yarn serve ./build/client
react-router-typecheck:
command: yarn typecheck
inputs:
- "@group(react-router-config)"
- "@group(react-router-app)"
- tsconfig.json
3 changes: 1 addition & 2 deletions .moon/workspace.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,12 @@ projects:
- "etc/*"

vcs:
manager: git
defaultBranch: main
provider: github
syncHooks: true
hooks:
pre-commit:
- moon scripts:check
- yarn lint-staged

generator:
templates:
Expand Down
4 changes: 4 additions & 0 deletions apps/ext-e2e-test-app/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.react-router
build
node_modules
README.md
7 changes: 7 additions & 0 deletions apps/ext-e2e-test-app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.DS_Store
.env
/node_modules/

# React Router
/.react-router/
/build/
22 changes: 22 additions & 0 deletions apps/ext-e2e-test-app/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FROM node:20-alpine AS development-dependencies-env
COPY . /app
WORKDIR /app
RUN npm ci

FROM node:20-alpine AS production-dependencies-env
COPY ./package.json package-lock.json /app/
WORKDIR /app
RUN npm ci --omit=dev

FROM node:20-alpine AS build-env
COPY . /app/
COPY --from=development-dependencies-env /app/node_modules /app/node_modules
WORKDIR /app
RUN npm run build

FROM node:20-alpine
COPY ./package.json package-lock.json /app/
COPY --from=production-dependencies-env /app/node_modules /app/node_modules
COPY --from=build-env /app/build /app/build
WORKDIR /app
CMD ["npm", "run", "start"]
4 changes: 4 additions & 0 deletions apps/ext-e2e-test-app/app/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
body {
margin: 0;
padding: 0;
}
61 changes: 61 additions & 0 deletions apps/ext-e2e-test-app/app/root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {
isRouteErrorResponse,
Links,
Meta,
Outlet,
Scripts,
ScrollRestoration,
} from "react-router";

import type { Route } from "./+types/root";

export function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta />
<Links />
</head>
<body>
{children}
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}

export default function App() {
return <Outlet />;
}

export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
let message = "Oops!";
let details = "An unexpected error occurred.";
let stack: string | undefined;

if (isRouteErrorResponse(error)) {
message = error.status === 404 ? "404" : "Error";
details =
error.status === 404
? "The requested page could not be found."
: error.statusText || details;
} else if (import.meta.env.DEV && error && error instanceof Error) {
details = error.message;
stack = error.stack;
}

return (
<div>
<div>{message}</div>
<div>{details}</div>
{stack && (
<pre>
<code>{stack}</code>
</pre>
)}
</div>
);
}
5 changes: 5 additions & 0 deletions apps/ext-e2e-test-app/app/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { index, type RouteConfig } from "@react-router/dev/routes";

export default [
index("routes/home.tsx"),
] satisfies RouteConfig;
13 changes: 13 additions & 0 deletions apps/ext-e2e-test-app/app/routes/home.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { Route } from "./+types/home";

export function meta(_args: Route.MetaArgs) {
return [
{
title: "Home",
},
];
}

export default function Home() {
return <div>Home</div>;
}
3 changes: 3 additions & 0 deletions apps/ext-e2e-test-app/app/welcome/welcome.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function Welcome() {
return <div>Welcome</div>;
}
6 changes: 6 additions & 0 deletions apps/ext-e2e-test-app/moon.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
$schema: "https://moonrepo.dev/schemas/project.json"
language: typescript

tags:
- react-router

32 changes: 32 additions & 0 deletions apps/ext-e2e-test-app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "ext-e2e-test-app",
"private": true,
"type": "module",
"packageManager": "yarn@4.8.0",
"scripts": {
"build": "react-router build",
"dev": "react-router dev",
"start": "react-router-serve ./build/server/index.js",
"typecheck": "react-router typegen && tsc"
},
"dependencies": {
"@react-router/node": "7.10.1",
"@react-router/serve": "7.10.1",
"isbot": "^5.1.31",
"react": "^19.2.3",
"react-dom": "^19.2.3",
"react-router": "7.10.1"
},
"devDependencies": {
"@react-router/dev": "7.10.1",
"@tailwindcss/vite": "^4.1.13",
"@types/node": "^22",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"serve": "^14.2.5",
"tailwindcss": "^4.1.13",
"typescript": "^5.9.2",
"vite": "^7.1.7",
"vite-tsconfig-paths": "^5.1.4"
}
}
Binary file added apps/ext-e2e-test-app/public/favicon.ico
Binary file not shown.
7 changes: 7 additions & 0 deletions apps/ext-e2e-test-app/react-router.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { Config } from "@react-router/dev/config";

export default {
// Config options...
// Server-side render by default, to enable SPA mode set this to `false`
ssr: false,
} satisfies Config;
35 changes: 35 additions & 0 deletions apps/ext-e2e-test-app/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"extends": "../../tsconfig.options.json",
"include": [
"**/*",
"**/.server/**/*",
"**/.client/**/*",
".react-router/types/**/*"
],
"compilerOptions": {
"lib": [
"DOM",
"DOM.Iterable",
"ES2022"
],
"types": [
"node",
"vite/client"
],
"moduleResolution": "bundler",
"jsx": "react-jsx",
"rootDirs": [
".",
"./.react-router/types"
],
"baseUrl": ".",
"paths": {
"~/*": [
"./app/*"
]
},
"noEmit": true,
"resolveJsonModule": true,
"outDir": "../../.moon/cache/types/apps/ext-e2e-test-app"
}
}
12 changes: 12 additions & 0 deletions apps/ext-e2e-test-app/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { reactRouter } from "@react-router/dev/vite";
import tailwindcss from "@tailwindcss/vite";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";

export default defineConfig({
plugins: [
tailwindcss(),
reactRouter(),
tsconfigPaths(),
],
});
3 changes: 0 additions & 3 deletions apps/ext-e2e/.swcrc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,3 @@
"type": "es6"
}
}



8 changes: 7 additions & 1 deletion apps/ext-e2e/moon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,15 @@ dependsOn:
- core-server
tasks:
test:
options:
runDepsInParallel: false
deps:
- ~:playwright-test

playwright-test:
options:
runDepsInParallel: false
deps:
- m2:build
- m3:build
- ~:playwright-test
- ext-e2e-test-app:react-router-build
32 changes: 23 additions & 9 deletions apps/ext-e2e/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ export default defineConfig<ExtContextOptions>({
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
workers: 1,
reporter: [
[
"html",
{
outputFolder: "target/playwright/playwright-report",
open: "never",
},
],
],
Expand All @@ -27,22 +28,35 @@ export default defineConfig<ExtContextOptions>({
external: [],
},

projects: [
webServer: [
{
name: "m3",
use: {
...devices["Desktop Chrome"],
channel: "chromium",
extTarget: "m3",
command: "moon run ext-e2e-test-app:react-router-start-csr",
url: "http://localhost:3000",
timeout: 30000,
reuseExistingServer: !process.env.CI,
env: {
// biome-ignore lint/style/useNamingConvention: The issue is that process.env.NODE_OPTIONS = "--require @swc-node/register" is inherited by the webServer child process, causing Yarn to fail.
NODE_OPTIONS: "",
},
},
],

projects: [
{
name: "m2",
name: "m3",
use: {
...devices["Desktop Chrome"],
channel: "chromium",
extTarget: "m2",
extTarget: "m3",
},
},
// {
// name: "m2",
// use: {
// ...devices["Desktop Chrome"],
// channel: "chromium",
// extTarget: "m2",
// },
// },
],
});
6 changes: 4 additions & 2 deletions apps/ext-e2e/src/fixtures/ext-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ export const test = extContextTest.extend<
const playwrightPage = new PlaywrightPage(page);
await use(playwrightPage);
},
// biome-ignore lint/correctness/noEmptyPattern: Playwright requires object destructuring pattern
mcpClientPage: async ({}, use) => {
mcpClientPage: async ({ context }, use) => {
// Depend on context to ensure mcpClientPage teardown happens before context.close()
void context;
const mcpClientPage = new McpClientPageObject();
await use(mcpClientPage);
await mcpClientPage.disconnect();
},
});

Expand Down
Loading