diff options
| -rw-r--r-- | bench/install/.eslintrc.js | 4 | ||||
| -rw-r--r-- | bench/install/.gitignore | 10 | ||||
| -rw-r--r-- | bench/install/README.md | 18 | ||||
| -rw-r--r-- | bench/install/app/entry.client.tsx | 18 | ||||
| -rw-r--r-- | bench/install/app/entry.server.tsx | 101 | ||||
| -rw-r--r-- | bench/install/app/root.tsx | 20 | ||||
| -rw-r--r-- | bench/install/app/routes/_index.tsx | 30 | ||||
| -rw-r--r-- | bench/install/package.json | 31 | ||||
| -rw-r--r-- | bench/install/public/favicon.ico | bin | 0 -> 16958 bytes | |||
| -rw-r--r-- | bench/install/remix.config.js | 14 | ||||
| -rw-r--r-- | bench/install/remix.env.d.ts | 2 | ||||
| -rw-r--r-- | bench/install/tsconfig.json | 22 | ||||
| -rw-r--r-- | package.json | 2 | 
13 files changed, 271 insertions, 1 deletions
| diff --git a/bench/install/.eslintrc.js b/bench/install/.eslintrc.js new file mode 100644 index 000000000..2061cd226 --- /dev/null +++ b/bench/install/.eslintrc.js @@ -0,0 +1,4 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { +  extends: ["@remix-run/eslint-config", "@remix-run/eslint-config/node"], +}; diff --git a/bench/install/.gitignore b/bench/install/.gitignore new file mode 100644 index 000000000..eff95a8ce --- /dev/null +++ b/bench/install/.gitignore @@ -0,0 +1,10 @@ +node_modules + +/.cache +/build +/public/build +.env +package-lock.json +yarn.lock +pnpm-lock.yaml +bun.lockb
\ No newline at end of file diff --git a/bench/install/README.md b/bench/install/README.md new file mode 100644 index 000000000..e9b617995 --- /dev/null +++ b/bench/install/README.md @@ -0,0 +1,18 @@ +# `install` benchmark + +Requires [`hyperfine`](https://github.com/sharkdp/hyperfine) + +``` +$ hyperfine --prepare 'rm -rf node_modules' --warmup 1 --runs 3 'bun install' 'pnpm install' 'yarn' 'npm install' +``` + +To check that the app is working as expected: + +``` +$ bun run dev +$ npm run dev +$ yarn dev +$ pnpm dev +``` + +Then visit [http://localhost:3000](http://localhost:3000). diff --git a/bench/install/app/entry.client.tsx b/bench/install/app/entry.client.tsx new file mode 100644 index 000000000..186cd9344 --- /dev/null +++ b/bench/install/app/entry.client.tsx @@ -0,0 +1,18 @@ +/** + * By default, Remix will handle hydrating your app on the client for you. + * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨ + * For more information, see https://remix.run/docs/en/main/file-conventions/entry.client + */ + +import { RemixBrowser } from "@remix-run/react"; +import { startTransition, StrictMode } from "react"; +import { hydrateRoot } from "react-dom/client"; + +startTransition(() => { +  hydrateRoot( +    document, +    <StrictMode> +      <RemixBrowser /> +    </StrictMode>, +  ); +}); diff --git a/bench/install/app/entry.server.tsx b/bench/install/app/entry.server.tsx new file mode 100644 index 000000000..fbea6220e --- /dev/null +++ b/bench/install/app/entry.server.tsx @@ -0,0 +1,101 @@ +/** + * By default, Remix will handle generating the HTTP Response for you. + * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨ + * For more information, see https://remix.run/docs/en/main/file-conventions/entry.server + */ + +import { PassThrough } from "node:stream"; +import type { EntryContext } from "@remix-run/node"; +import { Response } from "@remix-run/node"; +import { RemixServer } from "@remix-run/react"; +import isbot from "isbot"; +import { renderToPipeableStream } from "react-dom/server"; + +const ABORT_DELAY = 5_000; + +export default function handleRequest( +  request: Request, +  responseStatusCode: number, +  responseHeaders: Headers, +  remixContext: EntryContext, +) { +  return isbot(request.headers.get("user-agent")) +    ? handleBotRequest(request, responseStatusCode, responseHeaders, remixContext) +    : handleBrowserRequest(request, responseStatusCode, responseHeaders, remixContext); +} + +function handleBotRequest( +  request: Request, +  responseStatusCode: number, +  responseHeaders: Headers, +  remixContext: EntryContext, +) { +  return new Promise((resolve, reject) => { +    const { pipe, abort } = renderToPipeableStream( +      <RemixServer context={remixContext} url={request.url} abortDelay={ABORT_DELAY} />, +      { +        onAllReady() { +          const body = new PassThrough(); + +          responseHeaders.set("Content-Type", "text/html"); + +          resolve( +            new Response(body, { +              headers: responseHeaders, +              status: responseStatusCode, +            }), +          ); + +          pipe(body); +        }, +        onShellError(error: unknown) { +          reject(error); +        }, +        onError(error: unknown) { +          responseStatusCode = 500; +          console.error(error); +        }, +      }, +    ); + +    setTimeout(abort, ABORT_DELAY); +  }); +} + +function handleBrowserRequest( +  request: Request, +  responseStatusCode: number, +  responseHeaders: Headers, +  remixContext: EntryContext, +) { +  return new Promise((resolve, reject) => { +    const { pipe, abort } = renderToPipeableStream( +      <RemixServer context={remixContext} url={request.url} abortDelay={ABORT_DELAY} />, +      { +        onShellReady() { +          const body = new PassThrough(); + +          responseHeaders.set("Content-Type", "text/html"); + +          resolve( +            new Response(body, { +              headers: responseHeaders, +              status: responseStatusCode, +            }), +          ); + +          pipe(body); +        }, +        onShellError(error: unknown) { +          reject(error); +        }, +        onError(error: unknown) { +          console.error(error); +          responseStatusCode = 500; +        }, +      }, +    ); + +    setTimeout(abort, ABORT_DELAY); +  }); +} diff --git a/bench/install/app/root.tsx b/bench/install/app/root.tsx new file mode 100644 index 000000000..4d0236fb2 --- /dev/null +++ b/bench/install/app/root.tsx @@ -0,0 +1,20 @@ +import { Links, LiveReload, 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 /> +        <LiveReload /> +      </body> +    </html> +  ); +} diff --git a/bench/install/app/routes/_index.tsx b/bench/install/app/routes/_index.tsx new file mode 100644 index 000000000..92fccd4ad --- /dev/null +++ b/bench/install/app/routes/_index.tsx @@ -0,0 +1,30 @@ +import type { V2_MetaFunction } from "@remix-run/node"; + +export const meta: V2_MetaFunction = () => { +  return [{ title: "New Remix App" }]; +}; + +export default function Index() { +  return ( +    <div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}> +      <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> +  ); +} diff --git a/bench/install/package.json b/bench/install/package.json new file mode 100644 index 000000000..02bb8e92f --- /dev/null +++ b/bench/install/package.json @@ -0,0 +1,31 @@ +{ +  "private": true, +  "sideEffects": false, +  "scripts": { +    "build": "remix build", +    "dev": "remix dev", +    "start": "remix-serve build", +    "typecheck": "tsc", +    "clean": "rm -rf node_modules", +    "bench": "hyperfine --prepare 'rm -rf node_modules' --warmup 1 --runs 3 'bun install' 'pnpm install' 'yarn' 'npm install'" +  }, +  "dependencies": { +    "@remix-run/node": "^1.15.0", +    "@remix-run/react": "^1.15.0", +    "@remix-run/serve": "^1.15.0", +    "isbot": "^3.6.5", +    "react": "^18.2.0", +    "react-dom": "^18.2.0" +  }, +  "devDependencies": { +    "@remix-run/dev": "^1.15.0", +    "@remix-run/eslint-config": "^1.15.0", +    "@types/react": "^18.0.25", +    "@types/react-dom": "^18.0.8", +    "eslint": "^8.27.0", +    "typescript": "^4.8.4" +  }, +  "engines": { +    "node": ">=14" +  } +} diff --git a/bench/install/public/favicon.ico b/bench/install/public/favicon.icoBinary files differ new file mode 100644 index 000000000..8830cf682 --- /dev/null +++ b/bench/install/public/favicon.ico diff --git a/bench/install/remix.config.js b/bench/install/remix.config.js new file mode 100644 index 000000000..a1a396661 --- /dev/null +++ b/bench/install/remix.config.js @@ -0,0 +1,14 @@ +/** @type {import('@remix-run/dev').AppConfig} */ +module.exports = { +  ignoredRouteFiles: ["**/.*"], +  // appDirectory: "app", +  // assetsBuildDirectory: "public/build", +  // serverBuildPath: "build/index.js", +  // publicPath: "/build/", +  future: { +    v2_errorBoundary: true, +    v2_meta: true, +    v2_normalizeFormMethod: true, +    v2_routeConvention: true, +  }, +}; diff --git a/bench/install/remix.env.d.ts b/bench/install/remix.env.d.ts new file mode 100644 index 000000000..dcf8c45e1 --- /dev/null +++ b/bench/install/remix.env.d.ts @@ -0,0 +1,2 @@ +/// <reference types="@remix-run/dev" /> +/// <reference types="@remix-run/node" /> diff --git a/bench/install/tsconfig.json b/bench/install/tsconfig.json new file mode 100644 index 000000000..20f8a386a --- /dev/null +++ b/bench/install/tsconfig.json @@ -0,0 +1,22 @@ +{ +  "include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"], +  "compilerOptions": { +    "lib": ["DOM", "DOM.Iterable", "ES2019"], +    "isolatedModules": true, +    "esModuleInterop": true, +    "jsx": "react-jsx", +    "moduleResolution": "node", +    "resolveJsonModule": true, +    "target": "ES2019", +    "strict": true, +    "allowJs": true, +    "forceConsistentCasingInFileNames": true, +    "baseUrl": ".", +    "paths": { +      "~/*": ["./app/*"] +    }, + +    // Remix takes care of building everything in `remix build`. +    "noEmit": true +  } +} diff --git a/package.json b/package.json index e488635c2..477f237e7 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@      "build-fallback": "esbuild --target=esnext  --bundle src/fallback.ts --format=iife --platform=browser --minify > src/fallback.out.js",      "postinstall": "bash .scripts/postinstall.sh",      "typecheck": "tsc --noEmit", -    "fmt": "prettier --write  './test/**/*.{mjs,ts,tsx,js,jsx}' './src/*.{mjs,ts,tsx,js,jsx}' './src/*/*.{mjs,ts,tsx,js,jsx}' './src/*/*/*.{mjs,ts,tsx,js,jsx}' './bench/*.{mjs,ts,tsx,js,jsx}' --config .prettierrc.cjs", +    "fmt": "prettier --write  './test/**/*.{mjs,ts,tsx,js,jsx}' './src/*.{mjs,ts,tsx,js,jsx}' './src/*/*.{mjs,ts,tsx,js,jsx}' './src/*/*/*.{mjs,ts,tsx,js,jsx}' './bench/**/*.{mjs,ts,tsx,js,jsx}' --config .prettierrc.cjs",      "lint": "eslint './**/*.d.ts' --cache",      "lint:fix": "eslint './**/*.d.ts' --cache --fix"    }, | 
