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
29 changes: 17 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,21 @@ Contributions and suggestions are welcome within the scope of this example, but
I doubt there ever will be a one-size-fits-all solution, so this code should be
viewed as opinionated.

I ended up basing a lot of things on the
[Turborepo starter](https://turbo.build/repo/docs/getting-started/create-new),
and I recommend reading
[their monorepo handbook](https://turbo.build/repo/docs/handbook).

## Features

- [Turborepo](https://turbo.build/) to orchestrate the build process and
dependencies
- A traditional "built package" with multiple entry points as well as the
["internal packages"](#the-internal-packages-strategy) strategy referencing
- Showing a traditional "built package" with multiple entry points as well as
the ["internal package"](#the-internal-packages-strategy) strategy referencing
Typescript code directly
- Multiple separate Firebase Functions deployments, using
- Multiple isolated Firebase deployments, using
[isolate-package](https://github.com/0x80/isolate-package/)
- Firebase monorepo support with live code updates in the emulators using
- Firebase emulators with live code updates using
[firebase-tools-with-isolate](https://github.com/0x80/firebase-tools-with-isolate)
- A web app based on Next.js with [ShadCN](https://ui.shadcn.com/) and
[Tailwind CSS](https://tailwindcss.com/)
Expand All @@ -68,8 +73,10 @@ viewed as opinionated.

In the main branch of this repo, packages are managed with PNPM, but if you
prefer to use a different package manager, there is
[a branch using NPM](https://github.com/0x80/mono-ts/tree/use-npm) and
[a branch using Yarn v1](https://github.com/0x80/mono-ts/tree/use-yarn-classic)
[a branch using NPM](https://github.com/0x80/mono-ts/tree/use-npm),
[a branch using classic Yarn (v1)](https://github.com/0x80/mono-ts/tree/use-yarn-classic),
and
[a branch using modern Yarn (v4)](https://github.com/0x80/mono-ts/tree/use-yarn-modern)

I encourage anyone to give PNPM a try if you haven't already.

Expand All @@ -90,9 +97,7 @@ To get started quickly run `npx turbo dev` from the root.
This will:

- Build the `web` app and start its dev server
- Build the `api` and `fns` backend services and starts their Firebase
emulators. See [running Firebase emulators](#running-firebase-emulators) for
more info.
- Build the `api` and `fns` backend services and start their emulators.

The web app should become available on http://localhost:3000 and the emulators
UI on http://localhost:4000.
Expand All @@ -101,8 +106,8 @@ Alternatively, you can start the emulators and dev server separately. It makes
the console output more readable and preserves coloring:

- In `apps/web` run `pnpm dev`
- In `services/fns` run `pnpm emulate`
- In `services/api` run `pnpm emulate`
- In `services/fns` run `pnpm dev`
- In `services/api` run `pnpm dev`

Additional information can be found in the README files of the various packages.

Expand Down Expand Up @@ -236,7 +241,7 @@ But, as always, there are also some disadvantages you should be aware of:
including the Next.js app.

For testing and comparison, mono-ts uses the internal packages approach for
`@mono/common` and a traditional built approach for `@mono/backend`. Both are
`@repo/common` and a traditional built approach for `@repo/backend`. Both are
compatible with `isolate-package` for deploying to Firebase.

## Live code changes from internal packages
Expand Down
2 changes: 1 addition & 1 deletion apps/web/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const { defineConfig } = require("eslint-define-config");

module.exports = defineConfig({
root: true,
extends: ["@mono/custom", "next/core-web-vitals"],
extends: ["@repo/custom", "next/core-web-vitals"],
overrides: [
{
files: ["*.mjs"],
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/components/counter-view.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable react/no-unescaped-entities */
import { Counter } from "@mono/common";
import { Counter } from "@repo/common";
import { doc } from "firebase/firestore";
import { useTypedDocument } from "~/lib/firestore.js";
import { refs } from "~/refs.js";
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/components/key-value-list.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FsTimestamp, isDefined } from "@mono/common";
import { FsTimestamp, isDefined } from "@repo/common";
import { Timestamp } from "firebase/firestore";
import {
Table,
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { areWeThereYet } from "@mono/common";
import { areWeThereYet } from "@repo/common";
import { CardDescription } from "~/components/ui/card.jsx";
import { add, multiply, reset } from "~/lib/api.js";
import CardWithAction from "./components/card-with-action.jsx";
Expand Down
8 changes: 3 additions & 5 deletions apps/web/next.config.mjs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
// @ts-check

/** @type {import('next').NextConfig} */
/** @type {import("next").NextConfig} */
const nextConfig = {
transpilePackages: ["@mono/common"],
transpilePackages: ["@repo/common"],

/**
* This is currently required to make Next.js work with ESM style imports.
*/
/** This is currently required to make Next.js work with ESM style imports. */
webpack(config) {
config.resolve.extensionAlias = {
".js": [".js", ".ts"],
Expand Down
4 changes: 2 additions & 2 deletions apps/web/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@mono/web",
"name": "@repo/web",
"version": "0.0.0",
"private": true,
"type": "module",
Expand All @@ -10,7 +10,7 @@
"lint": "next lint"
},
"dependencies": {
"@mono/common": "workspace:*",
"@repo/common": "workspace:*",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-slot": "^1.0.2",
"@types/node": "^20.10.4",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"url": "https://github.com/0x80/mono-ts"
},
"scripts": {
"type-check": "turbo run type-check",
"type:check": "turbo run type:check",
"build": "turbo run build",
"clean": "turbo run clean",
"dev": "turbo run dev",
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/.eslintrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"root": true,
"extends": ["@mono/custom"]
"extends": ["@repo/custom"]
}
21 changes: 14 additions & 7 deletions packages/backend/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@mono/backend",
"name": "@repo/backend",
"version": "0.0.0",
"type": "module",
"files": [
Expand All @@ -11,27 +11,34 @@
"./firebase": "./dist/firebase.js"
},
"scripts": {
"build": "tsup && tsc --emitDeclarationOnly",
"clean": "rm -rf dist && rm tsconfig.tsbuildinfo",
"type-check": "tsc --noEmit",
"bundle": "tsup-node",
"bundle:watch": "tsup-node --watch",
"type:check": "tsc --noEmit",
"type:gen": "tsc --emitDeclarationOnly",
"type:gen:watch": "tsc --emitDeclarationOnly --watch",
"build": "run-p bundle type:gen",
"dev": "run-p bundle:watch type:gen:watch",
"clean": "del dist tsconfig.tsbuildinfo",
"test": "vitest",
"coverage": "vitest run --coverage ",
"lint": "eslint \"**/*.ts*\""
},
"license": "MIT",
"dependencies": {
"@mono/common": "workspace:*",
"@repo/common": "workspace:*",
"@sindresorhus/is": "^6.1.0",
"firebase-admin": "^11",
"firebase-functions": "^4.5.0",
"lodash-es": "^4.17.21",
"ts-is-present": "^1.2.2"
},
"devDependencies": {
"@mono/eslint-config-custom": "workspace:*",
"@mono/tsconfig": "workspace:*",
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*",
"@types/lodash-es": "^4.17.12",
"@types/node": "^20.10.4",
"del-cli": "^5.1.0",
"npm-run-all": "^4.1.5",
"tsup": "^8.0.1",
"typescript": "^5.3.3",
"vitest": "^1.0.4"
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"extends": "@mono/tsconfig/library.json",
"extends": "@repo/typescript-config/library.json",
"compilerOptions": {
"target": "esnext",
"rootDir": "src",
Expand Down
2 changes: 1 addition & 1 deletion packages/common/.eslintrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"root": true,
"extends": ["@mono/custom"]
"extends": ["@repo/custom"]
}
7 changes: 4 additions & 3 deletions packages/common/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@mono/common",
"name": "@repo/common",
"version": "0.0.0",
"type": "module",
"types": "./src/index.ts",
Expand All @@ -10,6 +10,7 @@
"src"
],
"scripts": {
"?build": "This package is links directly to its source files",
"type-check": "tsc --noEmit",
"test": "vitest",
"coverage": "vitest run --coverage ",
Expand All @@ -21,8 +22,8 @@
"lodash-es": "^4.17.21"
},
"devDependencies": {
"@mono/eslint-config-custom": "workspace:*",
"@mono/tsconfig": "workspace:*",
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*",
"eslint-plugin-require-extensions": "^0.1.3",
"prettier": "^3.1.1",
"typescript": "^5.3.3",
Expand Down
6 changes: 2 additions & 4 deletions packages/common/src/are-we-there-yet.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
/**
* A function to test if code changes propagate to the running web app
*/
/** A function to test if code changes propagate to the running web app */
export function areWeThereYet() {
return "This text is coming from a function in the @mono/common package which is linked using the internal packages strategy. Change this text to test live updates while the dev server is running.";
return "This text is coming from a function in the @repo/common package which is linked using the internal packages strategy. Change this text to test live updates while the dev server is running.";
}
2 changes: 1 addition & 1 deletion packages/common/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"extends": "@mono/tsconfig/library.json",
"extends": "@repo/typescript-config/library.json",
"compilerOptions": {
"target": "esnext",
"rootDir": "src",
Expand Down
File renamed without changes.
34 changes: 34 additions & 0 deletions packages/eslint-config/library.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const { resolve } = require("node:path");

const project = resolve(process.cwd(), "tsconfig.json");

/** @type {import("eslint").Linter.Config} */
module.exports = {
extends: ["eslint:recommended", "prettier", "eslint-config-turbo"],
plugins: ["only-warn"],
globals: {
React: true,
JSX: true,
},
env: {
node: true,
},
settings: {
"import/resolver": {
typescript: {
project,
},
},
},
ignorePatterns: [
// Ignore dotfiles
".*.js",
"node_modules/",
"dist/",
],
overrides: [
{
files: ["*.js?(x)", "*.ts?(x)"],
},
],
};
35 changes: 35 additions & 0 deletions packages/eslint-config/next.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const { resolve } = require("node:path");

const project = resolve(process.cwd(), "tsconfig.json");

/** @type {import("eslint").Linter.Config} */
module.exports = {
extends: [
"eslint:recommended",
"prettier",
require.resolve("@vercel/style-guide/eslint/next"),
"eslint-config-turbo",
],
globals: {
React: true,
JSX: true,
},
env: {
node: true,
browser: true,
},
plugins: ["only-warn"],
settings: {
"import/resolver": {
typescript: {
project,
},
},
},
ignorePatterns: [
// Ignore dotfiles
".*.js",
"node_modules/",
],
overrides: [{ files: ["*.js?(x)", "*.ts?(x)"] }],
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@mono/eslint-config-custom",
"name": "@repo/eslint-config",
"version": "0.0.0",
"main": "index.js",
"license": "MIT",
Expand Down
43 changes: 43 additions & 0 deletions packages/eslint-config/react-internal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const { resolve } = require("node:path");

const project = resolve(process.cwd(), "tsconfig.json");

/*
* This is a custom ESLint configuration for use with
* internal (bundled by their consumer) libraries
* that utilize React.
*
* This config extends the Vercel Engineering Style Guide.
* For more information, see https://github.com/vercel/style-guide
*
*/

/** @type {import("eslint").Linter.Config} */
module.exports = {
extends: ["eslint:recommended", "prettier", "eslint-config-turbo"],
plugins: ["only-warn"],
globals: {
React: true,
JSX: true,
},
env: {
browser: true,
},
settings: {
"import/resolver": {
typescript: {
project,
},
},
},
ignorePatterns: [
// Ignore dotfiles
".*.js",
"node_modules/",
"dist/",
],
overrides: [
// Force ESLint to detect .tsx files
{ files: ["*.js?(x)", "*.ts?(x)"] },
],
};
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@mono/tsconfig",
"name": "@repo/typescript-config",
"version": "0.0.0",
"private": true,
"license": "MIT",
Expand Down
Loading