From 539bb904a7b2ebaf843ccab27eacc37a0937e734 Mon Sep 17 00:00:00 2001 From: Jared Palmer Date: Sat, 11 Dec 2021 14:12:22 -0800 Subject: [PATCH 1/2] Add kitchen sink example --- examples/kitchen-sink/.gitignore | 13 ++ examples/kitchen-sink/.prettierignore | 9 + examples/kitchen-sink/.prettierrc | 4 + examples/kitchen-sink/README.md | 29 +++ examples/kitchen-sink/apps/admin/.eslintrc.js | 1 + examples/kitchen-sink/apps/admin/index.html | 12 + examples/kitchen-sink/apps/admin/package.json | 26 +++ examples/kitchen-sink/apps/admin/src/App.css | 42 ++++ examples/kitchen-sink/apps/admin/src/App.tsx | 43 ++++ .../kitchen-sink/apps/admin/src/index.css | 8 + examples/kitchen-sink/apps/admin/src/logo.svg | 7 + examples/kitchen-sink/apps/admin/src/main.tsx | 11 + .../kitchen-sink/apps/admin/tsconfig.json | 9 + .../kitchen-sink/apps/admin/vite.config.ts | 6 + examples/kitchen-sink/apps/api/.eslintrc.js | 1 + examples/kitchen-sink/apps/api/package.json | 36 +++ .../apps/api/src/__tests__/server.test.ts | 22 ++ .../apps/api/src/__tests__/tsconfig.json | 4 + examples/kitchen-sink/apps/api/src/index.ts | 9 + examples/kitchen-sink/apps/api/src/server.ts | 22 ++ examples/kitchen-sink/apps/api/tsconfig.json | 11 + examples/kitchen-sink/apps/blog/README.md | 41 ++++ .../apps/blog/app/entry.client.tsx | 4 + .../apps/blog/app/entry.server.tsx | 21 ++ examples/kitchen-sink/apps/blog/app/root.tsx | 178 +++++++++++++++ .../apps/blog/app/routes/demos/about.tsx | 44 ++++ .../blog/app/routes/demos/about/index.tsx | 17 ++ .../apps/blog/app/routes/demos/about/whoa.tsx | 20 ++ .../apps/blog/app/routes/demos/actions.tsx | 101 ++++++++ .../apps/blog/app/routes/demos/correct.tsx | 3 + .../apps/blog/app/routes/demos/params.tsx | 43 ++++ .../apps/blog/app/routes/demos/params/$id.tsx | 110 +++++++++ .../blog/app/routes/demos/params/index.tsx | 40 ++++ .../apps/blog/app/routes/index.tsx | 102 +++++++++ .../apps/blog/app/styles/dark.css | 7 + .../apps/blog/app/styles/demos/about.css | 26 +++ .../apps/blog/app/styles/global.css | 216 ++++++++++++++++++ examples/kitchen-sink/apps/blog/package.json | 31 +++ .../kitchen-sink/apps/blog/public/favicon.ico | Bin 0 -> 16958 bytes .../kitchen-sink/apps/blog/remix.config.js | 10 + .../kitchen-sink/apps/blog/remix.env.d.ts | 2 + examples/kitchen-sink/apps/blog/tsconfig.json | 19 ++ .../kitchen-sink/apps/storefront/.eslintrc | 3 + .../apps/storefront/next-env.d.ts | 3 + .../apps/storefront/next.config.js | 4 + .../kitchen-sink/apps/storefront/package.json | 28 +++ .../apps/storefront/src/pages/index.tsx | 12 + .../apps/storefront/tsconfig.json | 9 + examples/kitchen-sink/package.json | 44 ++++ .../kitchen-sink/packages/logger/.eslintrc.js | 1 + .../kitchen-sink/packages/logger/package.json | 26 +++ .../packages/logger/src/__tests__/log.test.ts | 10 + .../logger/src/__tests__/tsconfig.json | 4 + .../kitchen-sink/packages/logger/src/index.ts | 3 + .../packages/logger/tsconfig.json | 11 + .../kitchen-sink/packages/scripts/README.md | 3 + .../packages/scripts/eslint-preset.js | 8 + .../packages/scripts/jest/node/jest-preset.js | 13 ++ .../packages/scripts/jest/root/jest-preset.js | 20 ++ .../packages/scripts/package.json | 37 +++ .../kitchen-sink/packages/tsconfig/README.md | 3 + .../kitchen-sink/packages/tsconfig/base.json | 19 ++ .../packages/tsconfig/nextjs.json | 20 ++ .../packages/tsconfig/node12.json | 11 + .../packages/tsconfig/node14.json | 14 ++ .../packages/tsconfig/package.json | 17 ++ .../packages/tsconfig/react-library.json | 14 ++ .../kitchen-sink/packages/tsconfig/vite.json | 19 ++ .../kitchen-sink/packages/ui/.eslintrc.js | 1 + .../kitchen-sink/packages/ui/package.json | 30 +++ .../packages/ui/src/__tests__/index.test.tsx | 11 + .../packages/ui/src/__tests__/tsconfig.json | 7 + .../kitchen-sink/packages/ui/src/index.tsx | 23 ++ .../kitchen-sink/packages/ui/tsconfig.json | 13 ++ 74 files changed, 1801 insertions(+) create mode 100644 examples/kitchen-sink/.gitignore create mode 100644 examples/kitchen-sink/.prettierignore create mode 100644 examples/kitchen-sink/.prettierrc create mode 100644 examples/kitchen-sink/README.md create mode 100644 examples/kitchen-sink/apps/admin/.eslintrc.js create mode 100644 examples/kitchen-sink/apps/admin/index.html create mode 100644 examples/kitchen-sink/apps/admin/package.json create mode 100644 examples/kitchen-sink/apps/admin/src/App.css create mode 100644 examples/kitchen-sink/apps/admin/src/App.tsx create mode 100644 examples/kitchen-sink/apps/admin/src/index.css create mode 100644 examples/kitchen-sink/apps/admin/src/logo.svg create mode 100644 examples/kitchen-sink/apps/admin/src/main.tsx create mode 100644 examples/kitchen-sink/apps/admin/tsconfig.json create mode 100644 examples/kitchen-sink/apps/admin/vite.config.ts create mode 100644 examples/kitchen-sink/apps/api/.eslintrc.js create mode 100644 examples/kitchen-sink/apps/api/package.json create mode 100644 examples/kitchen-sink/apps/api/src/__tests__/server.test.ts create mode 100644 examples/kitchen-sink/apps/api/src/__tests__/tsconfig.json create mode 100644 examples/kitchen-sink/apps/api/src/index.ts create mode 100644 examples/kitchen-sink/apps/api/src/server.ts create mode 100644 examples/kitchen-sink/apps/api/tsconfig.json create mode 100644 examples/kitchen-sink/apps/blog/README.md create mode 100644 examples/kitchen-sink/apps/blog/app/entry.client.tsx create mode 100644 examples/kitchen-sink/apps/blog/app/entry.server.tsx create mode 100644 examples/kitchen-sink/apps/blog/app/root.tsx create mode 100644 examples/kitchen-sink/apps/blog/app/routes/demos/about.tsx create mode 100644 examples/kitchen-sink/apps/blog/app/routes/demos/about/index.tsx create mode 100644 examples/kitchen-sink/apps/blog/app/routes/demos/about/whoa.tsx create mode 100644 examples/kitchen-sink/apps/blog/app/routes/demos/actions.tsx create mode 100644 examples/kitchen-sink/apps/blog/app/routes/demos/correct.tsx create mode 100644 examples/kitchen-sink/apps/blog/app/routes/demos/params.tsx create mode 100644 examples/kitchen-sink/apps/blog/app/routes/demos/params/$id.tsx create mode 100644 examples/kitchen-sink/apps/blog/app/routes/demos/params/index.tsx create mode 100644 examples/kitchen-sink/apps/blog/app/routes/index.tsx create mode 100644 examples/kitchen-sink/apps/blog/app/styles/dark.css create mode 100644 examples/kitchen-sink/apps/blog/app/styles/demos/about.css create mode 100644 examples/kitchen-sink/apps/blog/app/styles/global.css create mode 100644 examples/kitchen-sink/apps/blog/package.json create mode 100644 examples/kitchen-sink/apps/blog/public/favicon.ico create mode 100644 examples/kitchen-sink/apps/blog/remix.config.js create mode 100644 examples/kitchen-sink/apps/blog/remix.env.d.ts create mode 100644 examples/kitchen-sink/apps/blog/tsconfig.json create mode 100644 examples/kitchen-sink/apps/storefront/.eslintrc create mode 100644 examples/kitchen-sink/apps/storefront/next-env.d.ts create mode 100644 examples/kitchen-sink/apps/storefront/next.config.js create mode 100644 examples/kitchen-sink/apps/storefront/package.json create mode 100644 examples/kitchen-sink/apps/storefront/src/pages/index.tsx create mode 100644 examples/kitchen-sink/apps/storefront/tsconfig.json create mode 100755 examples/kitchen-sink/package.json create mode 100644 examples/kitchen-sink/packages/logger/.eslintrc.js create mode 100644 examples/kitchen-sink/packages/logger/package.json create mode 100644 examples/kitchen-sink/packages/logger/src/__tests__/log.test.ts create mode 100644 examples/kitchen-sink/packages/logger/src/__tests__/tsconfig.json create mode 100644 examples/kitchen-sink/packages/logger/src/index.ts create mode 100644 examples/kitchen-sink/packages/logger/tsconfig.json create mode 100644 examples/kitchen-sink/packages/scripts/README.md create mode 100644 examples/kitchen-sink/packages/scripts/eslint-preset.js create mode 100644 examples/kitchen-sink/packages/scripts/jest/node/jest-preset.js create mode 100644 examples/kitchen-sink/packages/scripts/jest/root/jest-preset.js create mode 100644 examples/kitchen-sink/packages/scripts/package.json create mode 100644 examples/kitchen-sink/packages/tsconfig/README.md create mode 100644 examples/kitchen-sink/packages/tsconfig/base.json create mode 100644 examples/kitchen-sink/packages/tsconfig/nextjs.json create mode 100644 examples/kitchen-sink/packages/tsconfig/node12.json create mode 100644 examples/kitchen-sink/packages/tsconfig/node14.json create mode 100644 examples/kitchen-sink/packages/tsconfig/package.json create mode 100644 examples/kitchen-sink/packages/tsconfig/react-library.json create mode 100644 examples/kitchen-sink/packages/tsconfig/vite.json create mode 100644 examples/kitchen-sink/packages/ui/.eslintrc.js create mode 100644 examples/kitchen-sink/packages/ui/package.json create mode 100644 examples/kitchen-sink/packages/ui/src/__tests__/index.test.tsx create mode 100644 examples/kitchen-sink/packages/ui/src/__tests__/tsconfig.json create mode 100644 examples/kitchen-sink/packages/ui/src/index.tsx create mode 100644 examples/kitchen-sink/packages/ui/tsconfig.json diff --git a/examples/kitchen-sink/.gitignore b/examples/kitchen-sink/.gitignore new file mode 100644 index 0000000000000..e741956869dcb --- /dev/null +++ b/examples/kitchen-sink/.gitignore @@ -0,0 +1,13 @@ +.DS_Store +node_modules +.turbo +*.log +.next +dist +dist-ssr +*.local +.env +.cache +server/dist +public/dist +.turbo \ No newline at end of file diff --git a/examples/kitchen-sink/.prettierignore b/examples/kitchen-sink/.prettierignore new file mode 100644 index 0000000000000..e428cb710ed5f --- /dev/null +++ b/examples/kitchen-sink/.prettierignore @@ -0,0 +1,9 @@ +node_modules +.next +build +dist +*.tsbuildinfo +*.gitignore +*.svg +*.lock +*.npmignore \ No newline at end of file diff --git a/examples/kitchen-sink/.prettierrc b/examples/kitchen-sink/.prettierrc new file mode 100644 index 0000000000000..dc6958febb993 --- /dev/null +++ b/examples/kitchen-sink/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": false, + "semi": true +} diff --git a/examples/kitchen-sink/README.md b/examples/kitchen-sink/README.md new file mode 100644 index 0000000000000..78083f98daa7e --- /dev/null +++ b/examples/kitchen-sink/README.md @@ -0,0 +1,29 @@ +# `turborepo` kitchen sink starter + +This is an official Yarn v1 starter Turborepo with multiple meta-frameworks all working in harmony and sharing packages. + +## What's inside? + +This Turborepo includes the following packages and apps: + +### Apps and Packages + +- `api`: an [Express](https://expressjs.com/) server +- `storefront`: a [Next.js](https://nextjs.org) app +- `admin`: a [Vite](https://vitejs.dev/) single page app +- `blog`: a [Remix](https://remix.run/) blog +- `logger`: isomorphic logger (a small wrapper around console.log) +- `ui`: a dummy React UI library (which contains a single `` component) +- `scripts`: Jest and eslint configurations +- `tsconfig`: tsconfig.json;s used throughout the monorepo + +Each package and app is 100% [Typescript](https://www.typescriptlang.org/). + +### Utilities + +This turborepo has some additional tools already setup for you: + +- [Typescript](https://www.typescriptlang.org/) for static type checking +- [ESLint](https://eslint.org/) for code linting +- [Jest](https://jestjs.io) test runner for all things JavaScript +- [Prettier](https://prettier.io) for code formatting diff --git a/examples/kitchen-sink/apps/admin/.eslintrc.js b/examples/kitchen-sink/apps/admin/.eslintrc.js new file mode 100644 index 0000000000000..7821ec22c9512 --- /dev/null +++ b/examples/kitchen-sink/apps/admin/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require("scripts/eslint-preset"); diff --git a/examples/kitchen-sink/apps/admin/index.html b/examples/kitchen-sink/apps/admin/index.html new file mode 100644 index 0000000000000..ec41bbddb0108 --- /dev/null +++ b/examples/kitchen-sink/apps/admin/index.html @@ -0,0 +1,12 @@ + + + + + + Vite App + + +
+ + + diff --git a/examples/kitchen-sink/apps/admin/package.json b/examples/kitchen-sink/apps/admin/package.json new file mode 100644 index 0000000000000..25ec88f6c6e69 --- /dev/null +++ b/examples/kitchen-sink/apps/admin/package.json @@ -0,0 +1,26 @@ +{ + "name": "admin", + "private": true, + "version": "0.0.0", + "scripts": { + "dev": "vite --port 3001 --clearScreen false", + "build": "vite build", + "deploy": "vercel deploy dist --team=turborepo --confirm", + "lint": "TIMING=1 eslint src/**/*.tsx --fix && tsc --noEmit", + "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist" + }, + "dependencies": { + "react": "^17.0.2", + "react-dom": "^17.0.2", + "ui": "*" + }, + "devDependencies": { + "@types/react": "^17.0.0", + "@types/react-dom": "^17.0.0", + "@vitejs/plugin-react-refresh": "^1.3.6", + "scripts": "*", + "tsconfig": "*", + "typescript": "^4.2.4", + "vite": "^2.6.14" + } +} diff --git a/examples/kitchen-sink/apps/admin/src/App.css b/examples/kitchen-sink/apps/admin/src/App.css new file mode 100644 index 0000000000000..8da3fde63d9e7 --- /dev/null +++ b/examples/kitchen-sink/apps/admin/src/App.css @@ -0,0 +1,42 @@ +.App { + text-align: center; +} + +.App-logo { + height: 40vmin; + pointer-events: none; +} + +@media (prefers-reduced-motion: no-preference) { + .App-logo { + animation: App-logo-spin infinite 20s linear; + } +} + +.App-header { + background-color: #282c34; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); + color: white; +} + +.App-link { + color: #61dafb; +} + +@keyframes App-logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +button { + font-size: calc(10px + 2vmin); +} diff --git a/examples/kitchen-sink/apps/admin/src/App.tsx b/examples/kitchen-sink/apps/admin/src/App.tsx new file mode 100644 index 0000000000000..6321d2c4c10e2 --- /dev/null +++ b/examples/kitchen-sink/apps/admin/src/App.tsx @@ -0,0 +1,43 @@ +import * as React from "react"; +import logo from "./logo.svg"; +import "./App.css"; +import { CounterButton } from "ui"; + +function App() { + return ( +
+
+ logo +

Turborepo Admin

+

This app is powered by Vite.

+

+ +

+

+ Edit App.tsx and save to test HMR updates. +

+

+ + Learn React + + {" | "} + + Vite Docs + +

+
+
+ ); +} + +export default App; diff --git a/examples/kitchen-sink/apps/admin/src/index.css b/examples/kitchen-sink/apps/admin/src/index.css new file mode 100644 index 0000000000000..001838951c916 --- /dev/null +++ b/examples/kitchen-sink/apps/admin/src/index.css @@ -0,0 +1,8 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", + "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} diff --git a/examples/kitchen-sink/apps/admin/src/logo.svg b/examples/kitchen-sink/apps/admin/src/logo.svg new file mode 100644 index 0000000000000..6b60c1042f58d --- /dev/null +++ b/examples/kitchen-sink/apps/admin/src/logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/examples/kitchen-sink/apps/admin/src/main.tsx b/examples/kitchen-sink/apps/admin/src/main.tsx new file mode 100644 index 0000000000000..794ecaf978a5a --- /dev/null +++ b/examples/kitchen-sink/apps/admin/src/main.tsx @@ -0,0 +1,11 @@ +import * as React from "react"; +import * as ReactDOM from "react-dom"; +import "./index.css"; +import App from "./App"; + +ReactDOM.render( + + + , + document.getElementById("root") +); diff --git a/examples/kitchen-sink/apps/admin/tsconfig.json b/examples/kitchen-sink/apps/admin/tsconfig.json new file mode 100644 index 0000000000000..585415b6f9f9c --- /dev/null +++ b/examples/kitchen-sink/apps/admin/tsconfig.json @@ -0,0 +1,9 @@ +{ + "exclude": ["node_modules"], + "extends": "tsconfig/vite.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "include": ["src"] +} diff --git a/examples/kitchen-sink/apps/admin/vite.config.ts b/examples/kitchen-sink/apps/admin/vite.config.ts new file mode 100644 index 0000000000000..b098eb9aec82c --- /dev/null +++ b/examples/kitchen-sink/apps/admin/vite.config.ts @@ -0,0 +1,6 @@ +import reactRefresh from "@vitejs/plugin-react-refresh"; +import { defineConfig } from "vite"; + +export default defineConfig({ + plugins: [reactRefresh()], +}); diff --git a/examples/kitchen-sink/apps/api/.eslintrc.js b/examples/kitchen-sink/apps/api/.eslintrc.js new file mode 100644 index 0000000000000..7821ec22c9512 --- /dev/null +++ b/examples/kitchen-sink/apps/api/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require("scripts/eslint-preset"); diff --git a/examples/kitchen-sink/apps/api/package.json b/examples/kitchen-sink/apps/api/package.json new file mode 100644 index 0000000000000..728ca74a648eb --- /dev/null +++ b/examples/kitchen-sink/apps/api/package.json @@ -0,0 +1,36 @@ +{ + "name": "api", + "version": "0.0.0", + "private": true, + "scripts": { + "test": "jest --detectOpenHandles", + "dev": "nodemon --exec \"node -r esbuild-register ./src/index.ts\" -e .ts", + "start": "node -r esbuild-register ./src/index.ts", + "build": "tsc", + "lint": "tsc --noEmit && TIMING=1 eslint src --fix", + "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist" + }, + "devDependencies": { + "scripts": "*", + "tsconfig": "*", + "@types/body-parser": "^1.19.0", + "@types/cors": "^2.8.10", + "@types/express": "^4.17.12", + "@types/morgan": "^1.9.2", + "@types/node": "^15.12.2", + "@types/supertest": "^2.0.11", + "esbuild-register": "^2.5.0", + "supertest": "^6.1.3", + "typescript": "^4.2.4" + }, + "jest": { + "preset": "scripts/jest/node" + }, + "dependencies": { + "logger": "*", + "body-parser": "^1.19.0", + "cors": "^2.8.5", + "express": "^4.17.1", + "morgan": "^1.10.0" + } +} diff --git a/examples/kitchen-sink/apps/api/src/__tests__/server.test.ts b/examples/kitchen-sink/apps/api/src/__tests__/server.test.ts new file mode 100644 index 0000000000000..c0c9806f8a9d7 --- /dev/null +++ b/examples/kitchen-sink/apps/api/src/__tests__/server.test.ts @@ -0,0 +1,22 @@ +import supertest from "supertest"; +import { createServer } from "../server"; + +describe("server", () => { + it("health check returns 200", async () => { + await supertest(createServer()) + .get("/healthz") + .expect(200) + .then((res) => { + expect(res.body.ok).toBe(true); + }); + }); + + it("message endpoint says hello", async () => { + await supertest(createServer()) + .get("/message/jared") + .expect(200) + .then((res) => { + expect(res.body).toEqual({ message: "hello jared" }); + }); + }); +}); diff --git a/examples/kitchen-sink/apps/api/src/__tests__/tsconfig.json b/examples/kitchen-sink/apps/api/src/__tests__/tsconfig.json new file mode 100644 index 0000000000000..bf65be62f2d5c --- /dev/null +++ b/examples/kitchen-sink/apps/api/src/__tests__/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": [".", "../."] +} diff --git a/examples/kitchen-sink/apps/api/src/index.ts b/examples/kitchen-sink/apps/api/src/index.ts new file mode 100644 index 0000000000000..8e7010f3ee954 --- /dev/null +++ b/examples/kitchen-sink/apps/api/src/index.ts @@ -0,0 +1,9 @@ +import { createServer } from "./server"; +import { log } from "logger"; + +const port = process.env.PORT || 5000; +const server = createServer(); + +server.listen(port, () => { + log(`api running on ${port}`); +}); diff --git a/examples/kitchen-sink/apps/api/src/server.ts b/examples/kitchen-sink/apps/api/src/server.ts new file mode 100644 index 0000000000000..70ccf0b76b72c --- /dev/null +++ b/examples/kitchen-sink/apps/api/src/server.ts @@ -0,0 +1,22 @@ +import { json, urlencoded } from "body-parser"; +import express from "express"; +import morgan from "morgan"; +import cors from "cors"; + +export const createServer = () => { + const app = express(); + app + .disable("x-powered-by") + .use(morgan("dev")) + .use(urlencoded({ extended: true })) + .use(json()) + .use(cors()) + .get("/message/:name", (req, res) => { + return res.json({ message: `hello ${req.params.name}` }); + }) + .get("/healthz", (req, res) => { + return res.json({ ok: true }); + }); + + return app; +}; diff --git a/examples/kitchen-sink/apps/api/tsconfig.json b/examples/kitchen-sink/apps/api/tsconfig.json new file mode 100644 index 0000000000000..74f3de4613c10 --- /dev/null +++ b/examples/kitchen-sink/apps/api/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "lib": ["ES2015"], + "module": "CommonJS", + "outDir": "./dist", + "rootDir": "./src" + }, + "exclude": ["node_modules"], + "extends": "tsconfig/base.json", + "include": ["src"] +} diff --git a/examples/kitchen-sink/apps/blog/README.md b/examples/kitchen-sink/apps/blog/README.md new file mode 100644 index 0000000000000..7a200946bf15a --- /dev/null +++ b/examples/kitchen-sink/apps/blog/README.md @@ -0,0 +1,41 @@ +# Welcome to Remix! + +- [Remix Docs](https://remix.run/docs) + +## Fly Setup + +1. [Install Fly](https://fly.io/docs/getting-started/installing-flyctl/) + +2. Sign up and log in to Fly + + ```sh + flyctl auth signup + ``` + +3. Setup Fly. It might ask if you want to deploy, say no since you haven't built the app yet. + + ```sh + flyctl launch + ``` + +## Development + +From your terminal: + +```sh +npm run dev +``` + +This starts your app in development mode, rebuilding assets on file changes. + +## Deployment + +If you've followed the setup instructions already, all you need to do is run this: + +```sh +npm run deploy +``` + +You can run `flyctl info` to get the url and ip address of your server. + +Check out the [fly docs](https://fly.io/docs/getting-started/node/) for more information. diff --git a/examples/kitchen-sink/apps/blog/app/entry.client.tsx b/examples/kitchen-sink/apps/blog/app/entry.client.tsx new file mode 100644 index 0000000000000..a19979b25278a --- /dev/null +++ b/examples/kitchen-sink/apps/blog/app/entry.client.tsx @@ -0,0 +1,4 @@ +import { hydrate } from "react-dom"; +import { RemixBrowser } from "remix"; + +hydrate(, document); diff --git a/examples/kitchen-sink/apps/blog/app/entry.server.tsx b/examples/kitchen-sink/apps/blog/app/entry.server.tsx new file mode 100644 index 0000000000000..d2051de0ea187 --- /dev/null +++ b/examples/kitchen-sink/apps/blog/app/entry.server.tsx @@ -0,0 +1,21 @@ +import { renderToString } from "react-dom/server"; +import { RemixServer } from "remix"; +import type { EntryContext } from "remix"; + +export default function handleRequest( + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + remixContext: EntryContext +) { + let markup = renderToString( + + ); + + responseHeaders.set("Content-Type", "text/html"); + + return new Response("" + markup, { + status: responseStatusCode, + headers: responseHeaders + }); +} diff --git a/examples/kitchen-sink/apps/blog/app/root.tsx b/examples/kitchen-sink/apps/blog/app/root.tsx new file mode 100644 index 0000000000000..10673c9bd9fcd --- /dev/null +++ b/examples/kitchen-sink/apps/blog/app/root.tsx @@ -0,0 +1,178 @@ +import { + Link, + Links, + LiveReload, + Meta, + Outlet, + Scripts, + ScrollRestoration, + useCatch +} from "remix"; +import type { LinksFunction } from "remix"; + +import globalStylesUrl from "~/styles/global.css"; +import darkStylesUrl from "~/styles/dark.css"; + +// https://remix.run/api/app#links +export let links: LinksFunction = () => { + return [ + { rel: "stylesheet", href: globalStylesUrl }, + { + rel: "stylesheet", + href: darkStylesUrl, + media: "(prefers-color-scheme: dark)" + } + ]; +}; + +// https://remix.run/api/conventions#default-export +// https://remix.run/api/conventions#route-filenames +export default function App() { + return ( + + + + + + ); +} + +// https://remix.run/docs/en/v1/api/conventions#errorboundary +export function ErrorBoundary({ error }: { error: Error }) { + console.error(error); + return ( + + +
+

There was an error

+

{error.message}

+
+

+ Hey, developer, you should replace this with what you want your + users to see. +

+
+
+
+ ); +} + +// https://remix.run/docs/en/v1/api/conventions#catchboundary +export function CatchBoundary() { + let caught = useCatch(); + + let message; + switch (caught.status) { + case 401: + message = ( +

+ Oops! Looks like you tried to visit a page that you do not have access + to. +

+ ); + break; + case 404: + message = ( +

Oops! Looks like you tried to visit a page that does not exist.

+ ); + break; + + default: + throw new Error(caught.data || caught.statusText); + } + + return ( + + +

+ {caught.status}: {caught.statusText} +

+ {message} +
+
+ ); +} + +function Document({ + children, + title +}: { + children: React.ReactNode; + title?: string; +}) { + return ( + + + + + {title ? {title} : null} + + + + + {children} + + + {process.env.NODE_ENV === "development" && } + + + ); +} + +function Layout({ children }: { children: React.ReactNode }) { + return ( +
+
+
+ + + + +
+
+
+
{children}
+
+
+
+

© You!

+
+
+
+ ); +} + +function RemixLogo() { + return ( + + Remix Logo + + + + + + + ); +} diff --git a/examples/kitchen-sink/apps/blog/app/routes/demos/about.tsx b/examples/kitchen-sink/apps/blog/app/routes/demos/about.tsx new file mode 100644 index 0000000000000..e44e8279ea3d2 --- /dev/null +++ b/examples/kitchen-sink/apps/blog/app/routes/demos/about.tsx @@ -0,0 +1,44 @@ +import { Outlet } from "remix"; +import type { MetaFunction, LinksFunction } from "remix"; + +import stylesUrl from "~/styles/demos/about.css"; + +export let meta: MetaFunction = () => { + return { + title: "About Remix" + }; +}; + +export let links: LinksFunction = () => { + return [{ rel: "stylesheet", href: stylesUrl }]; +}; + +export default function Index() { + return ( +
+
+

About Us

+

+ Ok, so this page isn't really about us, but we did want to + show you a few more things Remix can do. +

+

+ Did you notice that things look a little different on this page? The + CSS that we import in the route file and include in its{" "} + links export is only included on this route and its + children. +

+

+ Wait a sec...its children? To understand what we mean by + this,{" "} + + read all about nested routes in the docs + + . +

+
+ +
+
+ ); +} diff --git a/examples/kitchen-sink/apps/blog/app/routes/demos/about/index.tsx b/examples/kitchen-sink/apps/blog/app/routes/demos/about/index.tsx new file mode 100644 index 0000000000000..4b6deb1bae313 --- /dev/null +++ b/examples/kitchen-sink/apps/blog/app/routes/demos/about/index.tsx @@ -0,0 +1,17 @@ +import { Link } from "remix"; + +export default function AboutIndex() { + return ( +
+

+ You are looking at the index route for the /about URL + segment, but there are nested routes as well! +

+

+ + Check out one of them here. + +

+
+ ); +} diff --git a/examples/kitchen-sink/apps/blog/app/routes/demos/about/whoa.tsx b/examples/kitchen-sink/apps/blog/app/routes/demos/about/whoa.tsx new file mode 100644 index 0000000000000..644a1bedc92b2 --- /dev/null +++ b/examples/kitchen-sink/apps/blog/app/routes/demos/about/whoa.tsx @@ -0,0 +1,20 @@ +import { Link } from "remix"; + +export default function AboutIndex() { + return ( +
+

+ Whoa, this is a nested route! We render the /about layout + route component, and its Outlet renders our route + component. 🤯 +

+

+ + + Go back to the /about index. + + +

+
+ ); +} diff --git a/examples/kitchen-sink/apps/blog/app/routes/demos/actions.tsx b/examples/kitchen-sink/apps/blog/app/routes/demos/actions.tsx new file mode 100644 index 0000000000000..910c706a666a2 --- /dev/null +++ b/examples/kitchen-sink/apps/blog/app/routes/demos/actions.tsx @@ -0,0 +1,101 @@ +import { useEffect, useRef } from "react"; +import type { ActionFunction } from "remix"; +import { Form, json, useActionData, redirect } from "remix"; + +export function meta() { + return { title: "Actions Demo" }; +} + +// When your form sends a POST, the action is called on the server. +// - https://remix.run/api/conventions#action +// - https://remix.run/guides/data-updates +export let action: ActionFunction = async ({ request }) => { + let formData = await request.formData(); + let answer = formData.get("answer"); + + // Typical action workflows start with validating the form data that just came + // over the network. Clientside validation is fine, but you definitely need it + // server side. If there's a problem, return the the data and the component + // can render it. + if (typeof answer !== "string") { + return json("Come on, at least try!", { status: 400 }); + } + + if (answer !== "egg") { + return json(`Sorry, ${answer} is not right.`, { status: 400 }); + } + + // Finally, if the data is valid, you'll typically write to a database or send or + // email or log the user in, etc. It's recommended to redirect after a + // successful action, even if it's to the same place so that non-JavaScript workflows + // from the browser doesn't repost the data if the user clicks back. + return redirect("/demos/correct"); +}; + +export default function ActionsDemo() { + // https://remix.run/api/remix#useactiondata + let actionMessage = useActionData(); + let answerRef = useRef(null); + + // This form works without JavaScript, but when we have JavaScript we can make + // the experience better by selecting the input on wrong answers! Go ahead, disable + // JavaScript in your browser and see what happens. + useEffect(() => { + if (actionMessage && answerRef.current) { + answerRef.current.select(); + } + }, [actionMessage]); + + return ( +
+
+

Actions!

+

+ This form submission will send a post request that we handle in our + `action` export. Any route can export an action to handle data + mutations. +

+
+

Post an Action

+

+ What is more useful when it is broken? +

+ +
+ +
+ {actionMessage ? ( +

+ {actionMessage} +

+ ) : null} +
+
+ + +
+ ); +} diff --git a/examples/kitchen-sink/apps/blog/app/routes/demos/correct.tsx b/examples/kitchen-sink/apps/blog/app/routes/demos/correct.tsx new file mode 100644 index 0000000000000..162ebeb787383 --- /dev/null +++ b/examples/kitchen-sink/apps/blog/app/routes/demos/correct.tsx @@ -0,0 +1,3 @@ +export default function NiceWork() { + return

You got it right!

; +} diff --git a/examples/kitchen-sink/apps/blog/app/routes/demos/params.tsx b/examples/kitchen-sink/apps/blog/app/routes/demos/params.tsx new file mode 100644 index 0000000000000..e491c34dee0d5 --- /dev/null +++ b/examples/kitchen-sink/apps/blog/app/routes/demos/params.tsx @@ -0,0 +1,43 @@ +import { useCatch, Link, json, useLoaderData, Outlet } from "remix"; + +export function meta() { + return { title: "Boundaries Demo" }; +} + +export default function Boundaries() { + return ( +
+
+ +
+ + +
+ ); +} diff --git a/examples/kitchen-sink/apps/blog/app/routes/demos/params/$id.tsx b/examples/kitchen-sink/apps/blog/app/routes/demos/params/$id.tsx new file mode 100644 index 0000000000000..c913791ace1e4 --- /dev/null +++ b/examples/kitchen-sink/apps/blog/app/routes/demos/params/$id.tsx @@ -0,0 +1,110 @@ +import { useCatch, Link, json, useLoaderData } from "remix"; +import type { LoaderFunction, MetaFunction } from "remix"; + +// The `$` in route filenames becomes a pattern that's parsed from the URL and +// passed to your loaders so you can look up data. +// - https://remix.run/api/conventions#loader-params +export let loader: LoaderFunction = async ({ params }) => { + // pretend like we're using params.id to look something up in the db + + if (params.id === "this-record-does-not-exist") { + // If the record doesn't exist we can't render the route normally, so + // instead we throw a 404 reponse to stop running code here and show the + // user the catch boundary. + throw new Response("Not Found", { status: 404 }); + } + + // now pretend like the record exists but the user just isn't authorized to + // see it. + if (params.id === "shh-its-a-secret") { + // Again, we can't render the component if the user isn't authorized. You + // can even put data in the response that might help the user rectify the + // issue! Like emailing the webmaster for access to the page. (Oh, right, + // `json` is just a Response helper that makes it easier to send JSON + // responses). + throw json({ webmasterEmail: "hello@remix.run" }, { status: 401 }); + } + + // Sometimes your code just blows up and you never anticipated it. Remix will + // automatically catch it and send the UI to the error boundary. + if (params.id === "kaboom") { + lol(); + } + + // but otherwise the record was found, user has access, so we can do whatever + // else we needed to in the loader and return the data. (This is boring, we're + // just gonna return the params.id). + return { param: params.id }; +}; + +export default function ParamDemo() { + let data = useLoaderData(); + return ( +

+ The param is {data.param} +

+ ); +} + +// https://remix.run/api/conventions#catchboundary +// https://remix.run/api/remix#usecatch +// https://remix.run/api/guides/not-found +export function CatchBoundary() { + let caught = useCatch(); + + let message: React.ReactNode; + switch (caught.status) { + case 401: + message = ( +

+ Looks like you tried to visit a page that you do not have access to. + Maybe ask the webmaster ({caught.data.webmasterEmail}) for access. +

+ ); + case 404: + message = ( +

Looks like you tried to visit a page that does not exist.

+ ); + default: + message = ( +

+ There was a problem with your request! +
+ {caught.status} {caught.statusText} +

+ ); + } + + return ( + <> +

Oops!

+

{message}

+

+ (Isn't it cool that the user gets to stay in context and try a different + link in the parts of the UI that didn't blow up?) +

+ + ); +} + +// https://remix.run/api/conventions#errorboundary +// https://remix.run/api/guides/not-found +export function ErrorBoundary({ error }: { error: Error }) { + console.error(error); + return ( + <> +

Error!

+

{error.message}

+

+ (Isn't it cool that the user gets to stay in context and try a different + link in the parts of the UI that didn't blow up?) +

+ + ); +} + +export let meta: MetaFunction = ({ data }) => { + return { + title: data ? `Param: ${data.param}` : "Oops...", + }; +}; diff --git a/examples/kitchen-sink/apps/blog/app/routes/demos/params/index.tsx b/examples/kitchen-sink/apps/blog/app/routes/demos/params/index.tsx new file mode 100644 index 0000000000000..c59e28a1a71b3 --- /dev/null +++ b/examples/kitchen-sink/apps/blog/app/routes/demos/params/index.tsx @@ -0,0 +1,40 @@ +import { useCatch, Link, json, useLoaderData, Outlet } from "remix"; +import type { LoaderFunction } from "remix"; + +export default function Boundaries() { + return ( + <> +

Params

+

+ When you name a route segment with $ like{" "} + routes/users/$userId.js, the $ segment will be parsed from + the URL and sent to your loaders and actions by the same name. +

+

Errors

+

+ When a route throws and error in it's action, loader, or component, + Remix automatically catches it, won't even try to render the component, + but it will render the route's ErrorBoundary instead. If the route + doesn't have one, it will bubble up to the routes above it until it hits + the root. +

+

So be as granular as you want with your error handling.

+

Not Found

+

+ (and other{" "} + + client errors + + ) +

+

+ Loaders and Actions can throw a Response instead of an + error and Remix will render the CatchBoundary instead of the component. + This is great when loading data from a database isn't found. As soon as + you know you can't render the component normally, throw a 404 response + and send your app into the catch boundary. Just like error boundaries, + catch boundaries bubble, too. +

+ + ); +} diff --git a/examples/kitchen-sink/apps/blog/app/routes/index.tsx b/examples/kitchen-sink/apps/blog/app/routes/index.tsx new file mode 100644 index 0000000000000..49099e001a0f1 --- /dev/null +++ b/examples/kitchen-sink/apps/blog/app/routes/index.tsx @@ -0,0 +1,102 @@ +import type { MetaFunction, LoaderFunction } from "remix"; +import { useLoaderData, json, Link } from "remix"; +import { CounterButton } from "ui"; + +type IndexData = { + resources: Array<{ name: string; url: string }>; + demos: Array<{ name: string; to: string }>; +}; + +// Loaders provide data to components and are only ever called on the server, so +// you can connect to a database or run any server side code you want right next +// to the component that renders it. +// https://remix.run/api/conventions#loader +export let loader: LoaderFunction = () => { + let data: IndexData = { + resources: [ + { + name: "Remix Docs", + url: "https://remix.run/docs", + }, + { + name: "React Router Docs", + url: "https://reactrouter.com/docs", + }, + { + name: "Remix Discord", + url: "https://discord.gg/VBePs6d", + }, + ], + demos: [ + { + to: "demos/actions", + name: "Actions", + }, + { + to: "demos/about", + name: "Nested Routes, CSS loading/unloading", + }, + { + to: "demos/params", + name: "URL Params and Error Boundaries", + }, + ], + }; + + // https://remix.run/api/remix#json + return json(data); +}; + +// https://remix.run/api/conventions#meta +export let meta: MetaFunction = () => { + return { + title: "Remix Starter", + description: "Welcome to remix!", + }; +}; + +// https://remix.run/guides/routing#index-routes +export default function Index() { + let data = useLoaderData(); + + return ( +
+
+

Welcome to Remix!

+

We're stoked that you're here. 🥳

+

+ Feel free to take a look around the code to see how Remix does things, + it might be a bit different than what you’re used to. When you're + ready to dive deeper, we've got plenty of resources to get you + up-and-running quickly. +

+

+ Check out all the demos in this starter, and then just delete the{" "} + app/routes/demos and app/styles/demos{" "} + folders when you're ready to turn this into your next project. +

+
+ + +
+ ); +} diff --git a/examples/kitchen-sink/apps/blog/app/styles/dark.css b/examples/kitchen-sink/apps/blog/app/styles/dark.css new file mode 100644 index 0000000000000..81b5196761cc4 --- /dev/null +++ b/examples/kitchen-sink/apps/blog/app/styles/dark.css @@ -0,0 +1,7 @@ +:root { + --color-foreground: hsl(0, 0%, 100%); + --color-background: hsl(0, 0%, 7%); + --color-links: hsl(213, 100%, 73%); + --color-links-hover: hsl(213, 100%, 80%); + --color-border: hsl(0, 0%, 25%); +} diff --git a/examples/kitchen-sink/apps/blog/app/styles/demos/about.css b/examples/kitchen-sink/apps/blog/app/styles/demos/about.css new file mode 100644 index 0000000000000..35fc3b90b6e9c --- /dev/null +++ b/examples/kitchen-sink/apps/blog/app/styles/demos/about.css @@ -0,0 +1,26 @@ +/* + * Whoa whoa whoa, wait a sec...why are we overriding global CSS selectors? + * Isn't that kind of scary? How do we know this won't have side effects? + * + * In Remix, CSS that is included in a route file will *only* show up on that + * route (and for nested routes, its children). When the user navigates away + * from that route the CSS files linked from those routes will be automatically + * unloaded, making your styles much easier to predict and control. + * + * Read more about styling routes in the docs: + * https://remix.run/guides/styling + */ + +:root { + --color-foreground: hsl(0, 0%, 7%); + --color-background: hsl(56, 100%, 50%); + --color-links: hsl(345, 56%, 39%); + --color-links-hover: hsl(345, 51%, 49%); + --color-border: rgb(184, 173, 20); + --font-body: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, + Liberation Mono, Courier New, monospace; +} + +.about__intro { + max-width: 500px; +} diff --git a/examples/kitchen-sink/apps/blog/app/styles/global.css b/examples/kitchen-sink/apps/blog/app/styles/global.css new file mode 100644 index 0000000000000..149f4b60674c6 --- /dev/null +++ b/examples/kitchen-sink/apps/blog/app/styles/global.css @@ -0,0 +1,216 @@ +/* + * You can just delete everything here or keep whatever you like, it's just a + * quick baseline! + */ +:root { + --color-foreground: hsl(0, 0%, 7%); + --color-background: hsl(0, 0%, 100%); + --color-links: hsl(213, 100%, 52%); + --color-links-hover: hsl(213, 100%, 43%); + --color-border: hsl(0, 0%, 82%); + --font-body: -apple-system, "Segoe UI", Helvetica Neue, Helvetica, Roboto, + Arial, sans-serif, system-ui, "Apple Color Emoji", "Segoe UI Emoji"; +} + +html { + box-sizing: border-box; +} + +*, +*::before, +*::after { + box-sizing: inherit; +} + +:-moz-focusring { + outline: auto; +} + +:focus { + outline: var(--color-links) solid 2px; + outline-offset: 2px; +} + +html, +body { + padding: 0; + margin: 0; + background-color: var(--color-background); + color: var(--color-foreground); +} + +body { + font-family: var(--font-body); + line-height: 1.5; +} + +a { + color: var(--color-links); + text-decoration: none; +} + +a:hover { + color: var(--color-links-hover); + text-decoration: underline; +} + +hr { + display: block; + height: 1px; + border: 0; + background-color: var(--color-border); + margin-top: 2rem; + margin-bottom: 2rem; +} + +input:where([type="text"]), +input:where([type="search"]) { + display: block; + border: 1px solid var(--color-border); + width: 100%; + font: inherit; + line-height: 1; + height: calc(1ch + 1.5em); + padding-right: 0.5em; + padding-left: 0.5em; + background-color: hsl(0 0% 100% / 20%); + color: var(--color-foreground); +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +.container { + --gutter: 16px; + width: 1024px; + max-width: calc(100% - var(--gutter) * 2); + margin-right: auto; + margin-left: auto; +} + +.remix-app { + display: flex; + flex-direction: column; + min-height: 100vh; + min-height: calc(100vh - env(safe-area-inset-bottom)); +} + +.remix-app > * { + width: 100%; +} + +.remix-app__header { + padding-top: 1rem; + padding-bottom: 1rem; + border-bottom: 1px solid var(--color-border); +} + +.remix-app__header-content { + display: flex; + justify-content: space-between; + align-items: center; +} + +.remix-app__header-home-link { + width: 106px; + height: 30px; + color: var(--color-foreground); +} + +.remix-app__header-nav ul { + list-style: none; + margin: 0; + display: flex; + align-items: center; + gap: 1.5em; +} + +.remix-app__header-nav li { + font-weight: bold; +} + +.remix-app__main { + flex: 1 1 100%; +} + +.remix-app__footer { + padding-top: 1rem; + padding-bottom: 1rem; + border-top: 1px solid var(--color-border); +} + +.remix-app__footer-content { + display: flex; + justify-content: center; + align-items: center; +} + +.remix__page { + --gap: 1rem; + --space: 2rem; + display: grid; + grid-auto-rows: min-content; + gap: var(--gap); + padding-top: var(--space); + padding-bottom: var(--space); +} + +@media print, screen and (min-width: 640px) { + .remix__page { + --gap: 2rem; + grid-auto-rows: unset; + grid-template-columns: repeat(2, 1fr); + } +} + +@media screen and (min-width: 1024px) { + .remix__page { + --gap: 4rem; + } +} + +.remix__page > main > :first-child { + margin-top: 0; +} + +.remix__page > main > :last-child { + margin-bottom: 0; +} + +.remix__page > aside { + margin: 0; + padding: 1.5ch 2ch; + border: solid 1px var(--color-border); + border-radius: 0.5rem; +} + +.remix__page > aside > :first-child { + margin-top: 0; +} + +.remix__page > aside > :last-child { + margin-bottom: 0; +} + +.remix__form { + display: flex; + flex-direction: column; + gap: 1rem; + padding: 1rem; + border: 1px solid var(--color-border); + border-radius: 0.5rem; +} + +.remix__form > * { + margin-top: 0; + margin-bottom: 0; +} diff --git a/examples/kitchen-sink/apps/blog/package.json b/examples/kitchen-sink/apps/blog/package.json new file mode 100644 index 0000000000000..114d5f0a8db7c --- /dev/null +++ b/examples/kitchen-sink/apps/blog/package.json @@ -0,0 +1,31 @@ +{ + "private": true, + "version": "0.0.0", + "name": "blog", + "scripts": { + "build": "remix build", + "dev": "remix dev", + "postinstall": "remix setup node", + "deploy": "flyctl deploy", + "start": "remix-serve build" + }, + "dependencies": { + "@remix-run/react": "^1.0.6", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "remix": "^1.0.6", + "ui": "*", + "@remix-run/serve": "^1.0.6" + }, + "devDependencies": { + "@remix-run/dev": "^1.0.6", + "@types/react": "^17.0.24", + "@types/react-dom": "^17.0.9", + "tsconfig": "*", + "typescript": "^4.2.4" + }, + "engines": { + "node": ">=14" + }, + "sideEffects": false +} diff --git a/examples/kitchen-sink/apps/blog/public/favicon.ico b/examples/kitchen-sink/apps/blog/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..8830cf6821b354114848e6354889b8ecf6d2bc61 GIT binary patch literal 16958 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 EKbET!0{{R3 literal 0 HcmV?d00001 diff --git a/examples/kitchen-sink/apps/blog/remix.config.js b/examples/kitchen-sink/apps/blog/remix.config.js new file mode 100644 index 0000000000000..49e85fc9f581f --- /dev/null +++ b/examples/kitchen-sink/apps/blog/remix.config.js @@ -0,0 +1,10 @@ +/** + * @type {import('@remix-run/dev/config').AppConfig} + */ +module.exports = { + appDirectory: "app", + browserBuildDirectory: "public/dist", + publicPath: "/dist/", + serverBuildDirectory: "dist", + devServerPort: 8002, +}; diff --git a/examples/kitchen-sink/apps/blog/remix.env.d.ts b/examples/kitchen-sink/apps/blog/remix.env.d.ts new file mode 100644 index 0000000000000..72e2affe311f2 --- /dev/null +++ b/examples/kitchen-sink/apps/blog/remix.env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/examples/kitchen-sink/apps/blog/tsconfig.json b/examples/kitchen-sink/apps/blog/tsconfig.json new file mode 100644 index 0000000000000..f2e9b81e019d7 --- /dev/null +++ b/examples/kitchen-sink/apps/blog/tsconfig.json @@ -0,0 +1,19 @@ +{ + "include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"], + "extends": "tsconfig/base.json", + "compilerOptions": { + "lib": ["DOM", "DOM.Iterable", "ES2019"], + "esModuleInterop": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "resolveJsonModule": true, + "target": "ES2019", + "strict": true, + "paths": { + "~/*": ["./app/*"] + }, + + // Remix takes care of building everything in `remix build`. + "noEmit": true + } +} diff --git a/examples/kitchen-sink/apps/storefront/.eslintrc b/examples/kitchen-sink/apps/storefront/.eslintrc new file mode 100644 index 0000000000000..15b1ed91acf66 --- /dev/null +++ b/examples/kitchen-sink/apps/storefront/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "next" +} diff --git a/examples/kitchen-sink/apps/storefront/next-env.d.ts b/examples/kitchen-sink/apps/storefront/next-env.d.ts new file mode 100644 index 0000000000000..c6643fda12ff6 --- /dev/null +++ b/examples/kitchen-sink/apps/storefront/next-env.d.ts @@ -0,0 +1,3 @@ +/// +/// +/// diff --git a/examples/kitchen-sink/apps/storefront/next.config.js b/examples/kitchen-sink/apps/storefront/next.config.js new file mode 100644 index 0000000000000..3278a65e15348 --- /dev/null +++ b/examples/kitchen-sink/apps/storefront/next.config.js @@ -0,0 +1,4 @@ +// next.config.js +const withTM = require("next-transpile-modules")(["logger"]); + +module.exports = withTM({}); diff --git a/examples/kitchen-sink/apps/storefront/package.json b/examples/kitchen-sink/apps/storefront/package.json new file mode 100644 index 0000000000000..7f524aa306e2e --- /dev/null +++ b/examples/kitchen-sink/apps/storefront/package.json @@ -0,0 +1,28 @@ +{ + "name": "storefront", + "version": "0.0.0", + "private": true, + "scripts": { + "build": "next build", + "start": "next start ", + "dev": "next dev -p 3002", + "lint": "TIMING=1 next lint", + "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next" + }, + "dependencies": { + "logger": "*", + "next": "^11.0.1", + "next-transpile-modules": "^8.0.0", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "ui": "*" + }, + "devDependencies": { + "eslint-config-next": "^11.0.1", + "@types/react": "^17.0.13", + "@types/react-dom": "^17.0.8", + "scripts": "*", + "tsconfig": "*", + "typescript": "^4.2.4" + } +} diff --git a/examples/kitchen-sink/apps/storefront/src/pages/index.tsx b/examples/kitchen-sink/apps/storefront/src/pages/index.tsx new file mode 100644 index 0000000000000..79bc173853331 --- /dev/null +++ b/examples/kitchen-sink/apps/storefront/src/pages/index.tsx @@ -0,0 +1,12 @@ +import { log } from "logger"; +import { CounterButton } from "ui"; + +export default function Store() { + log("Hey! This is Home."); + return ( +
+

Store

+ +
+ ); +} diff --git a/examples/kitchen-sink/apps/storefront/tsconfig.json b/examples/kitchen-sink/apps/storefront/tsconfig.json new file mode 100644 index 0000000000000..fcbeaa748019a --- /dev/null +++ b/examples/kitchen-sink/apps/storefront/tsconfig.json @@ -0,0 +1,9 @@ +{ + "exclude": ["node_modules"], + "extends": "tsconfig/nextjs.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "include": ["src", "next-env.d.ts"] +} diff --git a/examples/kitchen-sink/package.json b/examples/kitchen-sink/package.json new file mode 100755 index 0000000000000..08558f03bb643 --- /dev/null +++ b/examples/kitchen-sink/package.json @@ -0,0 +1,44 @@ +{ + "name": "turborepo", + "version": "0.0.0", + "private": true, + "workspaces": [ + "packages/*" + ], + "scripts": { + "build": "turbo run build", + "dev": "turbo run dev --no-cache --parallel --continue", + "lint": "turbo run lint", + "test": "turbo run test", + "clean": "turbo run clean && rm -rf node_modules" + }, + "devDependencies": { + "turbo": "latest" + }, + "turbo": { + "pipeline": { + "build": { + "outputs": [ + "dist/**", + ".next/**", + "public/dist/**" + ], + "dependsOn": [ + "^build" + ] + }, + "test": { + "outputs": [ + "coverage/**" + ], + "dependsOn": [] + }, + "lint": { + "outputs": [] + }, + "dev": { + "cache": "false" + } + } + } +} diff --git a/examples/kitchen-sink/packages/logger/.eslintrc.js b/examples/kitchen-sink/packages/logger/.eslintrc.js new file mode 100644 index 0000000000000..7821ec22c9512 --- /dev/null +++ b/examples/kitchen-sink/packages/logger/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require("scripts/eslint-preset"); diff --git a/examples/kitchen-sink/packages/logger/package.json b/examples/kitchen-sink/packages/logger/package.json new file mode 100644 index 0000000000000..ff694ed6e1ceb --- /dev/null +++ b/examples/kitchen-sink/packages/logger/package.json @@ -0,0 +1,26 @@ +{ + "name": "logger", + "version": "0.0.0", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "source": "./src/index.ts", + "private": true, + "files": [ + "dist/**" + ], + "scripts": { + "test": "jest", + "dev": "tsc -w", + "build": "tsc", + "lint": "TIMING=1 eslint src --fix", + "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist" + }, + "devDependencies": { + "scripts": "*", + "tsconfig": "*", + "typescript": "^4.2.4" + }, + "jest": { + "preset": "scripts/jest/node" + } +} diff --git a/examples/kitchen-sink/packages/logger/src/__tests__/log.test.ts b/examples/kitchen-sink/packages/logger/src/__tests__/log.test.ts new file mode 100644 index 0000000000000..fa597a863b164 --- /dev/null +++ b/examples/kitchen-sink/packages/logger/src/__tests__/log.test.ts @@ -0,0 +1,10 @@ +import { log } from ".."; + +jest.spyOn(global.console, "log"); + +describe("logger", () => { + it("prints a message", () => { + log("hello"); + expect(console.log).toBeCalled(); + }); +}); diff --git a/examples/kitchen-sink/packages/logger/src/__tests__/tsconfig.json b/examples/kitchen-sink/packages/logger/src/__tests__/tsconfig.json new file mode 100644 index 0000000000000..bf65be62f2d5c --- /dev/null +++ b/examples/kitchen-sink/packages/logger/src/__tests__/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": [".", "../."] +} diff --git a/examples/kitchen-sink/packages/logger/src/index.ts b/examples/kitchen-sink/packages/logger/src/index.ts new file mode 100644 index 0000000000000..e472eb34fe16c --- /dev/null +++ b/examples/kitchen-sink/packages/logger/src/index.ts @@ -0,0 +1,3 @@ +export const log = (str: any) => { + console.log("logger: " + str); +}; diff --git a/examples/kitchen-sink/packages/logger/tsconfig.json b/examples/kitchen-sink/packages/logger/tsconfig.json new file mode 100644 index 0000000000000..74f3de4613c10 --- /dev/null +++ b/examples/kitchen-sink/packages/logger/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "lib": ["ES2015"], + "module": "CommonJS", + "outDir": "./dist", + "rootDir": "./src" + }, + "exclude": ["node_modules"], + "extends": "tsconfig/base.json", + "include": ["src"] +} diff --git a/examples/kitchen-sink/packages/scripts/README.md b/examples/kitchen-sink/packages/scripts/README.md new file mode 100644 index 0000000000000..14ac5c0fbd05c --- /dev/null +++ b/examples/kitchen-sink/packages/scripts/README.md @@ -0,0 +1,3 @@ +# `scripts` + +These are internal repo scripts for Turborepo until we can fully self-compile. diff --git a/examples/kitchen-sink/packages/scripts/eslint-preset.js b/examples/kitchen-sink/packages/scripts/eslint-preset.js new file mode 100644 index 0000000000000..aafa50e3d2cd8 --- /dev/null +++ b/examples/kitchen-sink/packages/scripts/eslint-preset.js @@ -0,0 +1,8 @@ +module.exports = { + extends: ["react-app"], + settings: { + react: { + version: "detect", + }, + }, +}; diff --git a/examples/kitchen-sink/packages/scripts/jest/node/jest-preset.js b/examples/kitchen-sink/packages/scripts/jest/node/jest-preset.js new file mode 100644 index 0000000000000..b6c259304707f --- /dev/null +++ b/examples/kitchen-sink/packages/scripts/jest/node/jest-preset.js @@ -0,0 +1,13 @@ +module.exports = { + roots: [""], + transform: { + "^.+\\.tsx?$": "ts-jest", + }, + moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], + modulePathIgnorePatterns: [ + "/test/__fixtures__", + "/node_modules", + "/dist", + ], + preset: "ts-jest", +}; diff --git a/examples/kitchen-sink/packages/scripts/jest/root/jest-preset.js b/examples/kitchen-sink/packages/scripts/jest/root/jest-preset.js new file mode 100644 index 0000000000000..cffae9f3306aa --- /dev/null +++ b/examples/kitchen-sink/packages/scripts/jest/root/jest-preset.js @@ -0,0 +1,20 @@ +module.exports = { + transform: { + ".(ts|tsx)$": "ts-jest", + ".(js|jsx)$": "babel-jest", // jest's default + }, + transformIgnorePatterns: ["[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$"], + moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], + projects: [ + "/packages/*", + "/playground/*", + "/apps/*", + ], + coverageDirectory: "/coverage/", + collectCoverageFrom: ["/packages/*/src/**/*.{ts,tsx}"], + testURL: "http://localhost/", + moduleNameMapper: { + ".json$": "identity-obj-proxy", + }, + moduleDirectories: ["node_modules"], +}; diff --git a/examples/kitchen-sink/packages/scripts/package.json b/examples/kitchen-sink/packages/scripts/package.json new file mode 100644 index 0000000000000..c08c59c5c0284 --- /dev/null +++ b/examples/kitchen-sink/packages/scripts/package.json @@ -0,0 +1,37 @@ +{ + "name": "scripts", + "version": "0.0.0", + "license": "BUSL-1.1", + "main": "index.js", + "private": true, + "bin": { + "jest": "./node_modules/.bin/jest", + "eslint": "./node_modules/.bin/eslint" + }, + "files": [ + "jest", + "eslint-preset.js" + ], + "scripts": { + "clean": "rm -rf .turbo && rm -rf node_modules" + }, + "dependencies": { + "@typescript-eslint/eslint-plugin": "^4.0.0", + "@typescript-eslint/parser": "^4.0.0", + "babel-eslint": "^10.0.0", + "eslint": "^7.5.0", + "eslint-config-react-app": "^6.0.0", + "eslint-plugin-flowtype": "^5.2.0", + "eslint-plugin-import": "^2.22.0", + "eslint-plugin-jest": "23.13.1", + "eslint-plugin-jsx-a11y": "^6.3.1", + "eslint-plugin-react": "^7.20.3", + "eslint-plugin-react-hooks": "^4.0.8", + "jest": "^26.6.3", + "ts-jest": "^26.5.0", + "typescript": "^4.2.4" + }, + "devDependencies": { + "@types/jest": "^26.0.22" + } +} diff --git a/examples/kitchen-sink/packages/tsconfig/README.md b/examples/kitchen-sink/packages/tsconfig/README.md new file mode 100644 index 0000000000000..ad972d4ca8d49 --- /dev/null +++ b/examples/kitchen-sink/packages/tsconfig/README.md @@ -0,0 +1,3 @@ +# `tsconfig` + +This is the Turborepo shared `tsconfig.json` from which all sother `tsconfig.json`'s inherit from. Making changes to this may have unintended consequences. diff --git a/examples/kitchen-sink/packages/tsconfig/base.json b/examples/kitchen-sink/packages/tsconfig/base.json new file mode 100644 index 0000000000000..5b57072b63c0e --- /dev/null +++ b/examples/kitchen-sink/packages/tsconfig/base.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Default", + "compilerOptions": { + "composite": false, + "declaration": true, + "declarationMap": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "inlineSources": false, + "isolatedModules": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "preserveWatchOutput": true, + "skipLibCheck": true, + "strict": true + }, + "exclude": ["node_modules"] +} diff --git a/examples/kitchen-sink/packages/tsconfig/nextjs.json b/examples/kitchen-sink/packages/tsconfig/nextjs.json new file mode 100644 index 0000000000000..29fa727408dbe --- /dev/null +++ b/examples/kitchen-sink/packages/tsconfig/nextjs.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Next.js", + "extends": "./base.json", + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "noEmit": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "rootDir": "src" + }, + "include": ["src", "next-env.d.ts"], + "exclude": ["node_modules"] +} diff --git a/examples/kitchen-sink/packages/tsconfig/node12.json b/examples/kitchen-sink/packages/tsconfig/node12.json new file mode 100644 index 0000000000000..e538e9f3e53a1 --- /dev/null +++ b/examples/kitchen-sink/packages/tsconfig/node12.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Node 12", + "extends": "./base.json", + "compilerOptions": { + "lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string"], + "module": "commonjs", + "target": "es2019", + "strict": true + } +} diff --git a/examples/kitchen-sink/packages/tsconfig/node14.json b/examples/kitchen-sink/packages/tsconfig/node14.json new file mode 100644 index 0000000000000..3f5f130f25094 --- /dev/null +++ b/examples/kitchen-sink/packages/tsconfig/node14.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Node 14", + "extends": "./base.json", + "compilerOptions": { + "lib": ["es2020"], + "module": "commonjs", + "target": "es2020", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + } +} diff --git a/examples/kitchen-sink/packages/tsconfig/package.json b/examples/kitchen-sink/packages/tsconfig/package.json new file mode 100644 index 0000000000000..b908105754b7e --- /dev/null +++ b/examples/kitchen-sink/packages/tsconfig/package.json @@ -0,0 +1,17 @@ +{ + "name": "tsconfig", + "version": "0.0.0", + "license": "BUSL-1.1", + "main": "index.js", + "publishConfig": { + "access": "public" + }, + "files": [ + "base.json", + "base.json", + "vite.json", + "node12.json", + "node14.json", + "nextjs.json" + ] +} diff --git a/examples/kitchen-sink/packages/tsconfig/react-library.json b/examples/kitchen-sink/packages/tsconfig/react-library.json new file mode 100644 index 0000000000000..7c2ece9d1625c --- /dev/null +++ b/examples/kitchen-sink/packages/tsconfig/react-library.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "React Library", + "extends": "./base.json", + "include": ["src"], + "exclude": ["node_modules"], + "compilerOptions": { + "lib": ["ES2015"], + "module": "ESNext", + "rootDir": "src", + "outDir": "dist", + "jsx": "react" + } +} diff --git a/examples/kitchen-sink/packages/tsconfig/vite.json b/examples/kitchen-sink/packages/tsconfig/vite.json new file mode 100644 index 0000000000000..1cf8032f7b3f3 --- /dev/null +++ b/examples/kitchen-sink/packages/tsconfig/vite.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "./base.json", + "Display": "Vite", + "compilerOptions": { + "target": "ESNext", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "types": ["vite/client"], + "allowJs": false, + "skipLibCheck": false, + "esModuleInterop": false, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react" + } +} diff --git a/examples/kitchen-sink/packages/ui/.eslintrc.js b/examples/kitchen-sink/packages/ui/.eslintrc.js new file mode 100644 index 0000000000000..7821ec22c9512 --- /dev/null +++ b/examples/kitchen-sink/packages/ui/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require("scripts/eslint-preset"); diff --git a/examples/kitchen-sink/packages/ui/package.json b/examples/kitchen-sink/packages/ui/package.json new file mode 100644 index 0000000000000..1910e3caf3d4f --- /dev/null +++ b/examples/kitchen-sink/packages/ui/package.json @@ -0,0 +1,30 @@ +{ + "name": "ui", + "version": "0.0.0", + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "sideEffects": false, + "private": true, + "license": "MIT", + "files": [ + "dist/**" + ], + "scripts": { + "test": "jest", + "build": "tsup src/index.tsx --format esm,cjs --dts --external react", + "dev": "tsup src/index.tsx --format esm,cjs --watch --dts --external react", + "lint": "TIMING=1 eslint src --fix", + "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist" + }, + "devDependencies": { + "@types/react": "^17.0.13", + "@types/react-dom": "^17.0.8", + "react": "^17.0.2", + "tsup": "^5.10.1", + "typescript": "^4.2.4" + }, + "jest": { + "preset": "scripts/jest/node" + } +} diff --git a/examples/kitchen-sink/packages/ui/src/__tests__/index.test.tsx b/examples/kitchen-sink/packages/ui/src/__tests__/index.test.tsx new file mode 100644 index 0000000000000..a853a2b79fc49 --- /dev/null +++ b/examples/kitchen-sink/packages/ui/src/__tests__/index.test.tsx @@ -0,0 +1,11 @@ +import * as React from "react"; +import * as ReactDOM from "react-dom"; +import { CounterButton } from "../index"; + +describe("CounterButton", () => { + it("renders without crashing", () => { + const div = document.createElement("div"); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); + }); +}); diff --git a/examples/kitchen-sink/packages/ui/src/__tests__/tsconfig.json b/examples/kitchen-sink/packages/ui/src/__tests__/tsconfig.json new file mode 100644 index 0000000000000..10f19c15fbdf0 --- /dev/null +++ b/examples/kitchen-sink/packages/ui/src/__tests__/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "include": [".", "../."], + "compilerOptions": { + "jsx": "react" + } +} diff --git a/examples/kitchen-sink/packages/ui/src/index.tsx b/examples/kitchen-sink/packages/ui/src/index.tsx new file mode 100644 index 0000000000000..cacfb90e12054 --- /dev/null +++ b/examples/kitchen-sink/packages/ui/src/index.tsx @@ -0,0 +1,23 @@ +import * as React from "react"; + +export const CounterButton = () => { + const [count, setCount] = React.useState(0); + return ( +
+

+ This is component iadasdfsdfdffsfsds from ui +

+

+ +

+
+ ); +}; diff --git a/examples/kitchen-sink/packages/ui/tsconfig.json b/examples/kitchen-sink/packages/ui/tsconfig.json new file mode 100644 index 0000000000000..f81264a1bdf26 --- /dev/null +++ b/examples/kitchen-sink/packages/ui/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "lib": ["ES2015", "DOM"], + "target": "ES6", + "module": "ESNext", + "outDir": "./dist", + "rootDir": "./src" + }, + "exclude": ["node_modules", "src/__test__/**/*"], + "extends": "tsconfig/base.json", + "include": ["src"] +} From d25c5e20c98938a751e1d990196809aa86ab36e0 Mon Sep 17 00:00:00 2001 From: Jared Palmer Date: Sun, 12 Dec 2021 10:30:34 -0500 Subject: [PATCH 2/2] Update remix for vercel --- examples/kitchen-sink/apps/blog/.gitignore | 8 +++++ examples/kitchen-sink/apps/blog/README.md | 35 ++++++++----------- examples/kitchen-sink/apps/blog/api/index.js | 5 +++ examples/kitchen-sink/apps/blog/package.json | 1 - .../kitchen-sink/apps/blog/remix.config.js | 7 ++-- examples/kitchen-sink/apps/blog/tsconfig.json | 1 - examples/kitchen-sink/apps/blog/vercel.json | 7 ++++ examples/kitchen-sink/package.json | 3 +- 8 files changed, 39 insertions(+), 28 deletions(-) create mode 100644 examples/kitchen-sink/apps/blog/.gitignore create mode 100644 examples/kitchen-sink/apps/blog/api/index.js create mode 100644 examples/kitchen-sink/apps/blog/vercel.json diff --git a/examples/kitchen-sink/apps/blog/.gitignore b/examples/kitchen-sink/apps/blog/.gitignore new file mode 100644 index 0000000000000..0f43d8bdff171 --- /dev/null +++ b/examples/kitchen-sink/apps/blog/.gitignore @@ -0,0 +1,8 @@ +node_modules + +.cache +.vercel +.output + +public/build +api/build diff --git a/examples/kitchen-sink/apps/blog/README.md b/examples/kitchen-sink/apps/blog/README.md index 7a200946bf15a..944936b342976 100644 --- a/examples/kitchen-sink/apps/blog/README.md +++ b/examples/kitchen-sink/apps/blog/README.md @@ -2,40 +2,33 @@ - [Remix Docs](https://remix.run/docs) -## Fly Setup - -1. [Install Fly](https://fly.io/docs/getting-started/installing-flyctl/) +## Deployment -2. Sign up and log in to Fly +After having run the `create-remix` command and selected "Vercel" as a deployment target, you only need to [import your Git repository](https://vercel.com/new) into Vercel, and it will be deployed. - ```sh - flyctl auth signup - ``` +If you'd like to avoid using a Git repository, you can also deploy the directory by running [Vercel CLI](https://vercel.com/cli): -3. Setup Fly. It might ask if you want to deploy, say no since you haven't built the app yet. +```sh +npm i -g vercel +vercel +``` - ```sh - flyctl launch - ``` +It is generally recommended to use a Git repository, because future commits will then automatically be deployed by Vercel, through its [Git Integration](https://vercel.com/docs/concepts/git). ## Development -From your terminal: +To run your Remix app locally, make sure your project's local dependencies are installed: ```sh -npm run dev +npm install ``` -This starts your app in development mode, rebuilding assets on file changes. - -## Deployment - -If you've followed the setup instructions already, all you need to do is run this: +Afterwards, start the Remix development server like so: ```sh -npm run deploy +npm run dev ``` -You can run `flyctl info` to get the url and ip address of your server. +Open up [http://localhost:3000](http://localhost:3000) and you should be ready to go! -Check out the [fly docs](https://fly.io/docs/getting-started/node/) for more information. +If you're used to using the `vercel dev` command provided by [Vercel CLI](https://vercel.com/cli) instead, you can also use that, but it's not needed. diff --git a/examples/kitchen-sink/apps/blog/api/index.js b/examples/kitchen-sink/apps/blog/api/index.js new file mode 100644 index 0000000000000..84af2ea8345fd --- /dev/null +++ b/examples/kitchen-sink/apps/blog/api/index.js @@ -0,0 +1,5 @@ +const { createRequestHandler } = require("@remix-run/vercel"); + +module.exports = createRequestHandler({ + build: require("./build"), +}); diff --git a/examples/kitchen-sink/apps/blog/package.json b/examples/kitchen-sink/apps/blog/package.json index 114d5f0a8db7c..0c2391ac9c01d 100644 --- a/examples/kitchen-sink/apps/blog/package.json +++ b/examples/kitchen-sink/apps/blog/package.json @@ -6,7 +6,6 @@ "build": "remix build", "dev": "remix dev", "postinstall": "remix setup node", - "deploy": "flyctl deploy", "start": "remix-serve build" }, "dependencies": { diff --git a/examples/kitchen-sink/apps/blog/remix.config.js b/examples/kitchen-sink/apps/blog/remix.config.js index 49e85fc9f581f..36dfe42033f08 100644 --- a/examples/kitchen-sink/apps/blog/remix.config.js +++ b/examples/kitchen-sink/apps/blog/remix.config.js @@ -3,8 +3,7 @@ */ module.exports = { appDirectory: "app", - browserBuildDirectory: "public/dist", - publicPath: "/dist/", - serverBuildDirectory: "dist", - devServerPort: 8002, + browserBuildDirectory: "public/build", + publicPath: "/build/", + serverBuildDirectory: "api/build", }; diff --git a/examples/kitchen-sink/apps/blog/tsconfig.json b/examples/kitchen-sink/apps/blog/tsconfig.json index f2e9b81e019d7..705bb4a240a7f 100644 --- a/examples/kitchen-sink/apps/blog/tsconfig.json +++ b/examples/kitchen-sink/apps/blog/tsconfig.json @@ -1,6 +1,5 @@ { "include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"], - "extends": "tsconfig/base.json", "compilerOptions": { "lib": ["DOM", "DOM.Iterable", "ES2019"], "esModuleInterop": true, diff --git a/examples/kitchen-sink/apps/blog/vercel.json b/examples/kitchen-sink/apps/blog/vercel.json new file mode 100644 index 0000000000000..dcfba92eda559 --- /dev/null +++ b/examples/kitchen-sink/apps/blog/vercel.json @@ -0,0 +1,7 @@ +{ + "build": { + "env": { + "ENABLE_FILE_SYSTEM_API": "1" + } + } +} diff --git a/examples/kitchen-sink/package.json b/examples/kitchen-sink/package.json index 08558f03bb643..94292bdb87138 100755 --- a/examples/kitchen-sink/package.json +++ b/examples/kitchen-sink/package.json @@ -3,7 +3,8 @@ "version": "0.0.0", "private": true, "workspaces": [ - "packages/*" + "packages/*", + "apps/*" ], "scripts": { "build": "turbo run build",