feat(create-turbo): apply official-starter transform

This commit is contained in:
Turbobot
2025-02-08 13:41:40 -08:00
committed by ryan
parent 9fe2818df8
commit c158dda08f
48 changed files with 5621 additions and 24 deletions
+5
View File
@@ -0,0 +1,5 @@
/** @type {import("eslint").Linter.Config} */
module.exports = {
root: true,
extends: ["@repo/eslint-config/server.js"],
};
+47
View File
@@ -0,0 +1,47 @@
FROM node:18-alpine AS base
# The web Dockerfile is copy-pasted into our main docs at /docs/handbook/deploying-with-docker.
# Make sure you update this Dockerfile, the Dockerfile in the web workspace and copy that over to Dockerfile in the docs.
FROM base AS builder
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk update
RUN apk add --no-cache libc6-compat
# Set working directory
WORKDIR /app
RUN yarn global add turbo
COPY . .
RUN turbo prune api --docker
# Add lockfile and package.json's of isolated subworkspace
FROM base AS installer
RUN apk update
RUN apk add --no-cache libc6-compat
WORKDIR /app
# First install dependencies (as they change less often)
COPY --from=builder /app/out/json/ .
RUN yarn install
# Build the project and its dependencies
COPY --from=builder /app/out/full/ .
# Uncomment and use build args to enable remote caching
# ARG TURBO_TEAM
# ENV TURBO_TEAM=$TURBO_TEAM
# ARG TURBO_TOKEN
# ENV TURBO_TOKEN=$TURBO_TOKEN
RUN yarn turbo build
FROM base AS runner
WORKDIR /app
# Don't run production as root
RUN addgroup --system --gid 1001 expressjs
RUN adduser --system --uid 1001 expressjs
USER expressjs
COPY --from=installer /app .
CMD node apps/api/dist/index.js
+42
View File
@@ -0,0 +1,42 @@
{
"name": "api",
"version": "0.0.0",
"private": true,
"scripts": {
"build": "tsc",
"clean": "rm -rf dist",
"dev": "nodemon --exec \"node -r esbuild-register ./src/index.ts\" -e .ts",
"lint": "tsc --noEmit && eslint \"src/**/*.ts*\" --max-warnings 0",
"start": "node -r esbuild-register ./src/index.ts",
"test": "jest --detectOpenHandles"
},
"jest": {
"preset": "@repo/jest-presets/node"
},
"dependencies": {
"@repo/logger": "*",
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.18.3",
"morgan": "^1.10.0"
},
"devDependencies": {
"@jest/globals": "^29.7.0",
"@repo/eslint-config": "*",
"@repo/jest-presets": "*",
"@repo/typescript-config": "*",
"@types/body-parser": "^1.19.5",
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/morgan": "^1.9.9",
"@types/node": "^20.11.24",
"@types/supertest": "^6.0.2",
"esbuild": "^0.20.1",
"esbuild-register": "^3.5.0",
"eslint": "^8.57.0",
"jest": "^29.7.0",
"nodemon": "^3.1.0",
"supertest": "^6.3.3",
"typescript": "5.5.4"
}
}
@@ -0,0 +1,23 @@
import supertest from "supertest";
import { describe, it, expect } from "@jest/globals";
import { createServer } from "../server";
describe("server", () => {
it("status check returns 200", async () => {
await supertest(createServer())
.get("/status")
.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.message).toBe("hello jared");
});
});
});
+9
View File
@@ -0,0 +1,9 @@
import { createServer } from "./server";
import { log } from "@repo/logger";
const port = process.env.PORT || 3001;
const server = createServer();
server.listen(port, () => {
log(`api running on ${port}`);
});
+22
View File
@@ -0,0 +1,22 @@
import { json, urlencoded } from "body-parser";
import express, { type Express } from "express";
import morgan from "morgan";
import cors from "cors";
export const createServer = (): Express => {
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("/status", (_, res) => {
return res.json({ ok: true });
});
return app;
};
+10
View File
@@ -0,0 +1,10 @@
{
"extends": "@repo/typescript-config/base.json",
"compilerOptions": {
"lib": ["ES2015"],
"outDir": "./dist",
"rootDir": "./src"
},
"exclude": ["node_modules"],
"include": ["src"]
}
+9
View File
@@ -0,0 +1,9 @@
/** @type {import("eslint").Linter.Config} */
module.exports = {
root: true,
extends: ["@repo/eslint-config/next.js"],
parser: "@typescript-eslint/parser",
parserOptions: {
project: true,
},
};
+52
View File
@@ -0,0 +1,52 @@
FROM node:18-alpine AS base
# This Dockerfile is copy-pasted into our main docs at /docs/handbook/deploying-with-docker.
# Make sure you update both files!
FROM base AS builder
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk update
RUN apk add --no-cache libc6-compat
# Set working directory
WORKDIR /app
RUN yarn global add turbo
COPY . .
RUN turbo prune web --docker
# Add lockfile and package.json's of isolated subworkspace
FROM base AS installer
RUN apk update
RUN apk add --no-cache libc6-compat
WORKDIR /app
# First install the dependencies (as they change less often)
COPY --from=builder /app/out/json/ .
RUN yarn install
# Build the project
COPY --from=builder /app/out/full/ .
# Uncomment and use build args to enable remote caching
# ARG TURBO_TEAM
# ENV TURBO_TEAM=$TURBO_TEAM
# ARG TURBO_TOKEN
# ENV TURBO_TOKEN=$TURBO_TOKEN
RUN yarn turbo build
FROM base AS runner
WORKDIR /app
# Don't run production as root
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
USER nextjs
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/standalone ./
COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static
COPY --from=installer --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public
CMD node apps/web/server.js
+30
View File
@@ -0,0 +1,30 @@
## Getting Started
First, run the development server:
```bash
yarn dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn/foundations/about-nextjs) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_source=github.com&utm_medium=referral&utm_campaign=turborepo-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
+5
View File
@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
+10
View File
@@ -0,0 +1,10 @@
const path = require("path");
module.exports = {
reactStrictMode: true,
transpilePackages: ["@repo/ui"],
output: "standalone",
experimental: {
outputFileTracingRoot: path.join(__dirname, "../../"),
},
};
+27
View File
@@ -0,0 +1,27 @@
{
"name": "web",
"version": "0.0.0",
"private": true,
"scripts": {
"build": "next build",
"dev": "next dev",
"lint": "eslint . --max-warnings 0",
"start": "next start"
},
"dependencies": {
"next": "^14.1.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"@repo/ui": "*"
},
"devDependencies": {
"@next/eslint-plugin-next": "^14.1.1",
"@types/node": "^20.11.24",
"@types/react": "^18.2.61",
"@types/react-dom": "^18.2.19",
"eslint": "^8.57.0",
"@repo/eslint-config": "*",
"@repo/typescript-config": "*",
"typescript": "5.5.4"
}
}
+7
View File
@@ -0,0 +1,7 @@
This public directory can be used to store any static content, see:
https://nextjs.org/docs/basic-features/static-file-serving
To remove this directory, also remove the following line from the web `Dockerfile`:
COPY --from=installer --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public
+11
View File
@@ -0,0 +1,11 @@
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
+67
View File
@@ -0,0 +1,67 @@
"use client";
import { useEffect, useState, ChangeEvent, FormEvent } from "react";
import { Button } from "@repo/ui/button";
const API_HOST = process.env.NEXT_PUBLIC_API_HOST || "http://localhost:3001";
export default function Web() {
const [name, setName] = useState<string>("");
const [response, setResponse] = useState<{ message: string } | null>(null);
const [error, setError] = useState<string | undefined>();
useEffect(() => {
setResponse(null);
setError(undefined);
}, [name]);
const onChange = (e: ChangeEvent<HTMLInputElement>) =>
setName(e.target.value);
const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
try {
const result = await fetch(`${API_HOST}/message/${name}`);
const response = await result.json();
setResponse(response);
} catch (err) {
console.error(err);
setError("Unable to fetch response");
}
};
const onReset = () => {
setName("");
};
return (
<div>
<h1>Web</h1>
<form onSubmit={onSubmit}>
<label htmlFor="name">Name </label>
<input
type="text"
name="name"
id="name"
value={name}
onChange={onChange}
></input>
<Button type="submit">Submit</Button>
</form>
{error && (
<div>
<h3>Error</h3>
<p>{error}</p>
</div>
)}
{response && (
<div>
<h3>Greeting</h3>
<p>{response.message}</p>
<Button onClick={onReset}>Reset</Button>
</div>
)}
</div>
);
}
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "@repo/typescript-config/nextjs.json",
"compilerOptions": {
"plugins": [{ "name": "next" }]
},
"include": ["next-env.d.ts", "next.config.js", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}