diff --git a/README.md b/README.md index 6058080..85a2c1a 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,16 @@ This is a quest for the ideal Typescript monorepo setup. -My current projects are based on Node, Next.js, and Firebase, so that is what I -am focussing on. +My current projects are based on Node.js, Next.js, and Firebase, so that is what +I am focussing on. If you use different platforms, this can still be a great starting point, as it should be easy to discard any packages that you have no use for. The monorepo approach by itself is largely independent of the chosen technology stack. This is meant as a best-effort approach given the tooling that is available, so -I expect this code to change as the ecosystem around Typescript and Javascript -continue to evolve. +expect this repository to change as the ecosystem around Typescript and +Javascript continue to evolve. Contributions are welcome within the scope of this example, but I doubt there will ever be a one-size-fits-all solution, so this code should be viewed as @@ -27,22 +27,21 @@ opinionated. - [Features](#features) - [Install](#install) - [Usage](#usage) -- [Workspace packages](#workspace-packages) +- [Workspace](#workspace) - [Packages](#packages) - [Apps](#apps) - [Services](#services) - [Deployment](#deployment) +- [Running Firebase using Emulators](#running-firebase-using-emulators) - [Using NPM instead of PNPM](#using-npm-instead-of-pnpm) - [Using Yarn instead of PNPM](#using-yarn-instead-of-pnpm) - [The "built packages" strategy](#the-built-packages-strategy) - [Convert path aliases](#convert-path-aliases) - - [Write ESM without import extensions](#write-esm-without-import-extensions) + - [Write ESM without import file extensions](#write-esm-without-import-file-extensions) - [Tree shaking](#tree-shaking) - - [](#) - [The "internal packages" strategy](#the-internal-packages-strategy) - [Live code changes from internal packages](#live-code-changes-from-internal-packages) - [Deploying to Firebase](#deploying-to-firebase) -- [VSCode settings](#vscode-settings) @@ -80,16 +79,28 @@ If you prefer to use a different package manager, that should not be a problem. See [using NPM](#using-npm-instead-of-pnpm) or [using Yarn](#using-yarn-instead-of-pnpm) for more info. +> NOTE that at the moment, PNPM is the only package manager for which +> [isolate-package](https://github.com/0x80/isolate-package/) will generate a +> compatible lockfile for deployment. For other package managers the lockfile is +> omitted and therefor deployments are not deterministic. + ## Usage -Run `pnpm dev`. This will: +Run `npx turbo dev` + +This will: + +- Build the dependencies of the `web` app and start its dev server +- Build the `api` and `fns` backend services and their dependencies +- Start the Firebase emulators. See + [running Firebase emulators](#running-firebase-emulators) for more info -- Build the dependencies of the web app and start its dev server -- Build the backend services and their dependencies, and - [isolate](#deploying-to-firebase) the output -- Start the backend emulators +The web app should become available on http://localhost:3000 and the emulators +UI on http://localhost:4000. -## Workspace packages +More info can be found in the README files of the various packages. + +## Workspace ### Packages @@ -100,30 +111,47 @@ Run `pnpm dev`. This will: ### Apps -- [web](./apps/web) A Next.js based web application. +- [web](./apps/web) A Next.js based web application configured to use Tailwind + CSS and ShadCN components. ### Services -- [fns](./services/fns) Cloud functions that execute on document writes, pubsub - events etc. This package shows how to set up the firebase.json config if you - would like to use isolate-package as part of your predeploy script. The - downside of this is that a watch task on your build does not result in live - code updates when running the emulator, because the results would then still - need to be isolated. -- [api](./services/api) A 2nd gen (Cloud Run based) API endpoint, using Express. - This package shows how to use +- [fns](./services/fns) Various Firebase functions that execute on document + writes, pubsub events etc. This package shows how to use [isolate-package] + explicitly as part of the predeploy phase. +- [api](./services/api) A 2nd gen Firebase function (based on Cloud Run) serving + as an API endpoint, using Express. This package shows how to use [firebase-tools-with-isolate](https://github.com/0x80/firebase-tools-with-isolate) - in order to have the isolation process integrated as part of the - `firebase deploy` command. This does not interfere in any way and allows you - to use a build watch task to keep live code updates flowing to the emulator. + to have the isolation integrated as part of the `firebase deploy` command. In + addition it illustrates how to use secrets. ## Deployment -Deployment instructions can be found in the individual packages: +I consider deployment a bit out-of-scope for this demo. + +For deployment to Firebase you will have to set up and configure an actual +project, but it is not required to run this demo since it can use the emulators. +Additional info about the use of +[isolate-package](https://github.com/0x80/isolate-package) (used by fns) and +[firestore-tools-with-isolate](https://github.com/0x80/firebase-tools-with-isolate) +(used by api) can be found in the instructions of each package. + +## Running Firebase using Emulators + +Throughout this repository we use the a Firebase demo project called +`demo-mono-ts` which allows us to run emulators for the different components +like database without actually creating any Firebase projects or resources. + +To make this work we pass the `--project` flag when starting the emulator. You +can use any name that starts with `demo-`. + +When passing configuration to initializeApp you can use any non-empty string for +the API keys as you can see in +[apps/web/.env.development](apps/web/.env.development) -- [web](./apps/web/README.md) -- [fns](./services/fns/README.md#deployment) -- [api](./services/api/README.md#deployment) +Currently, if you want to make use of Firebase secrets, +[you need to make sure they are also available in .env or .env.local](https://github.com/firebase/firebase-tools/issues/5520) +for the emulators to work. ## Using NPM instead of PNPM @@ -278,7 +306,33 @@ upload a self-contained package that can be treated similarly to an NPM package, by installing its dependencies and executing the main entry. This repo includes a solution based on -[isolate-package](https://github.com/0x80/isolate-package/) and I encourage you -to look at that and maybe read the -[accompanying article](https://thijs-koerselman.medium.com/deploy-to-firebase-without-the-hacks-e685de39025e) -to understand what it does and why it is needed. +[isolate-package](https://github.com/0x80/isolate-package/). I wrote this +[article](https://thijs-koerselman.medium.com/deploy-to-firebase-without-the-hacks-e685de39025e) +explaining what it does and why it is needed. + +You might notice `@google-cloud/functions-framework` as a dependency in the +service package even though it is not being used in code imports. It is +currently required for Firebase to be able to deploy a PNPM workspace. Without +it you will get an error asking you to install the dependency. I don't quite +understand how the two are related, but it works. + +## Running Firebase Emulators + +For Firebase Functions each service (api and fns) start separate emulators on +port 5001 and 5002. The backend serviced (using the firebase-admin api) connect +to emulators by setting various environment variables. + +I have stored these in `.env` files in the respective service packages. Normally +you would want to store them in a file that is not part of the repository like +`.env.local` but by placing them in `.env` I prevent having to give instructions +for setting them up just for running the demo. + +### Secrets + +The api services uses a secret for DEMO_API_KEY. In order to make secrets work +with the emulator you currently have to add the secret to `.secret.local` and +also an `.env` or `.env.local` file. See +[this issue](https://github.com/firebase/firebase-tools/issues/5520) for more +info. I have place it in `.env` which is part of the repo, so you don't have to +set anything up, but .env.local is where you would put it normally, because that +file is not checked into git. diff --git a/apps/web/.env.development b/apps/web/.env.development new file mode 100644 index 0000000..feb9bb8 --- /dev/null +++ b/apps/web/.env.development @@ -0,0 +1,9 @@ +NEXT_PUBLIC_FIREBASE_API_KEY=any +NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=demo-mono-ts.firebaseapp.com +NEXT_PUBLIC_FIREBASE_PROJECT_ID=demo-mono-ts +NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=demo-mono-ts.appspot.com +NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=12344567890 +NEXT_PUBLIC_FIREBASE_APP_ID=any +NEXT_PUBLIC_DEMO_API_KEY=any +NEXT_PUBLIC_DEMO_API_ENDPOINT="http://localhost:5002/demo-mono-ts/europe-west3/api/v1" +NEXT_PUBLIC_USE_EMULATORS=true diff --git a/apps/web/README.md b/apps/web/README.md index 4b2a659..8d50317 100644 --- a/apps/web/README.md +++ b/apps/web/README.md @@ -1,19 +1,14 @@ -This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). - -## Getting Started - -First, run the development server: - -```bash -pnpm dev -``` +This is a [Next.js](https://nextjs.org/) project bootstrapped with +[`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). ## Deploy on Vercel -Because this app uses Firebase, you'll need to create a project there first. -Then go to project overview => Settings => General => Your Apps => Register web -app => Add firebase SDK => Use Npm => ... and copy the different values to -env variables in a `.env.local` file like so: +Because this app uses Firebase, and we do not want to have to create actual +Firebase resources just to run this code we use a so-called "demo" project. Any +project name starting with `demo-` passed to the emulators will make them run +without ever talking to real cloud resources. + +variables in a `.env.development` file like so: ```bash NEXT_PUBLIC_FIREBASE_API_KEY= @@ -32,9 +27,19 @@ first deploy that. The value for `NEXT_PUBLIC_DEMO_API_ENDPOINT` should be `"http://localhost:5002/mono-ts/europe-west3/api/v1"` if you use the local -firebase emulator. Otherwise it should point to your firebase function instance in the -correct region. +firebase emulator. Otherwise it should point to your firebase function instance +in the correct region. + +The easiest way to deploy your Next.js app is to use the +[Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) +from the creators of Next.js. + +Check out our +[Next.js deployment documentation](https://nextjs.org/docs/deployment) for more +details. -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. +## Run dev server -Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. +```bash +pnpm dev +``` diff --git a/apps/web/app/components/key-value-list.tsx b/apps/web/app/components/key-value-list.tsx index 5d3abb6..217eec7 100644 --- a/apps/web/app/components/key-value-list.tsx +++ b/apps/web/app/components/key-value-list.tsx @@ -14,7 +14,7 @@ export default function KeyValueList(props: { labels: Array<[string, string]>; }) { const rows = props.labels.map(([key, label]) => ( - + {label} diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index 249b760..4560fe5 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -1,6 +1,7 @@ "use client"; import { areWeThereYet } from "@mono/common"; +import { CardDescription } from "~/components/ui/card.jsx"; import { add, multiply, reset } from "~/lib/api.js"; import CardWithAction from "./components/card-with-action.jsx"; import { CounterView } from "./components/counter-view.jsx"; @@ -8,16 +9,23 @@ import { CounterView } from "./components/counter-view.jsx"; export default function Home() { return (
-

A quest for the ideal TS monorepo setup

+

A quest for the ideal TS monorepo

+ > + + To test live updates to the backend services while the emulator is + running, you can update code in `services/api/src/v1/handlers.ts`. + For example change the multiplication `counter.data.value * n` to + `counter.data.value * n * 10` + + reset() }} > diff --git a/apps/web/lib/firebase.ts b/apps/web/lib/firebase.ts index f23d7c0..228f5a2 100644 --- a/apps/web/lib/firebase.ts +++ b/apps/web/lib/firebase.ts @@ -1,5 +1,6 @@ import { initializeApp } from "firebase/app"; -import { getFirestore } from "firebase/firestore"; +import { connectAuthEmulator, getAuth } from "firebase/auth"; +import { connectFirestoreEmulator, getFirestore } from "firebase/firestore"; export const firebaseConfig = { apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, @@ -12,3 +13,9 @@ export const firebaseConfig = { export const app = initializeApp(firebaseConfig); export const db = getFirestore(app); +export const auth = getAuth(app); + +if (process.env.NEXT_PUBLIC_USE_EMULATORS) { + connectFirestoreEmulator(db, "127.0.0.1", 8080); + connectAuthEmulator(auth, "http://127.0.0.1:9099"); +} diff --git a/package.json b/package.json index f677671..fcd14f3 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "format": "prettier --write ." }, "devDependencies": { + "concurrently": "^8.2.2", "prettier": "^3.1.0", "prettier-plugin-jsdoc": "^1.1.1", "turbo": "^1.10.16", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cddcc3f..c84fad1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: devDependencies: + concurrently: + specifier: ^8.2.2 + version: 8.2.2 prettier: specifier: ^3.1.0 version: 3.1.0 @@ -410,7 +413,6 @@ packages: engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.0 - dev: false /@babel/types@7.23.3: resolution: {integrity: sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==} @@ -4220,6 +4222,22 @@ packages: /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + /concurrently@8.2.2: + resolution: {integrity: sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==} + engines: {node: ^14.13.0 || >=16.0.0} + hasBin: true + dependencies: + chalk: 4.1.2 + date-fns: 2.30.0 + lodash: 4.17.21 + rxjs: 7.8.1 + shell-quote: 1.8.1 + spawn-command: 0.0.2 + supports-color: 8.1.1 + tree-kill: 1.2.2 + yargs: 17.7.2 + dev: true + /config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} dependencies: @@ -4376,6 +4394,13 @@ packages: engines: {node: '>= 14'} dev: true + /date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} + dependencies: + '@babel/runtime': 7.23.4 + dev: true + /debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -9532,7 +9557,6 @@ packages: /regenerator-runtime@0.14.0: resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==} - dev: false /regexp.prototype.flags@1.5.1: resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==} @@ -9911,6 +9935,10 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + /shell-quote@1.8.1: + resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + dev: true + /side-channel@1.0.4: resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} dependencies: @@ -10013,6 +10041,10 @@ packages: whatwg-url: 7.1.0 dev: true + /spawn-command@0.0.2: + resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} + dev: true + /spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} dependencies: @@ -10341,6 +10373,13 @@ packages: dependencies: has-flag: 4.0.0 + /supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + dependencies: + has-flag: 4.0.0 + dev: true + /supports-hyperlinks@2.3.0: resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} engines: {node: '>=8'} diff --git a/services/api/.env b/services/api/.env new file mode 100644 index 0000000..f4b42d7 --- /dev/null +++ b/services/api/.env @@ -0,0 +1,2 @@ +DEMO_API_KEY="any" +FIRESTORE_EMULATOR_HOST="localhost:8080" diff --git a/services/api/.firebaserc b/services/api/.firebaserc index b5606d3..c09072e 100644 --- a/services/api/.firebaserc +++ b/services/api/.firebaserc @@ -1,5 +1,5 @@ { "projects": { - "default": "mono-ts" + "default": "demo-mono-ts" } } diff --git a/services/api/.secret.local b/services/api/.secret.local new file mode 100644 index 0000000..6cf9b24 --- /dev/null +++ b/services/api/.secret.local @@ -0,0 +1 @@ +DEMO_API_KEY=any diff --git a/services/api/README.md b/services/api/README.md index 2bbc6fb..f3fa719 100644 --- a/services/api/README.md +++ b/services/api/README.md @@ -5,24 +5,50 @@ The external API is deployed as a 2nd gen Firebase function. ## Deploy In order to deploy this, first create a Firebase project and set the name in -`.firebaserc`. +`.firebaserc`, or call `npx firebase use my-project-name`. -Then run `pnpm deploy` or `npx firebase deploy` to deploy -the api function. +Then run `npx firebase deploy` to deploy the api function. The first time you deploy the function, Firebase will notice that a secret is being used but not set yet. The CLI will ask you for the value, you can enter -some random string, but you'll need to match it with the nextapp `.env.local` +some random string, but you'll need to match it with the apps/web `.env.local` setting for `NEXT_PUBLIC_DEMO_API_KEY`. ## Parameters -Gen2 function runtime parameters can either be [defined via env variables or -secrets](https://firebase.google.com/docs/functions/config-env?gen=2nd#params). - - - -- [Deploy](#deploy) -- [Parameters](#parameters) - - +Gen2 function runtime parameters can either be +[defined via env variables or secrets](https://firebase.google.com/docs/functions/config-env?gen=2nd#params). + +## Firebase Config + +The `firebase.json` file in this packages contains: + +```json +"functions": { + "source": ".", + "runtime": "nodejs20", + "isolate": true, + "predeploy": ["turbo build"], + "codebase": "api" +}, +``` + +This looks a little different from the configuration in +[services/fns/firebase.json](../fns/README.md#firebase-config) and that is +because this package uses +[firebase-tools-with-isolate](https://github.com/0x80/firebase-tools-with-isolate) +instead of [isolate-package](https://github.com/0x80/isolate-package) directly. + +Setting the `isolate` field to true is an opt-in for isolate to execute. Any +configuration is still picked up from the `isolate.config.json` and +`tsconfig.json` files. + +The big advantage of this approach over the one in +[services/fns](../fns/README.md#firebase-config) is that you can keep the +configuration the same as a standard non-monorepo setup, and the isolation +process happens auto-magically as part of the `firebase deploy` command. What is +even more exciting; If you have an emulator running, and build code using a +watch task, you will get live updates in the emulator! 🌈✨ + +The `codebase` field is only required if you want to deploy to Firebase +Functions from more than one package. It acts as a namespace. diff --git a/services/api/firebase.json b/services/api/firebase.json index 9dfc4ad..3e61b7e 100644 --- a/services/api/firebase.json +++ b/services/api/firebase.json @@ -7,6 +7,9 @@ "codebase": "api" }, "emulators": { + "ui": { + "enabled": false + }, "functions": { "port": 5002 } diff --git a/services/api/package.json b/services/api/package.json index 866664d..bb60a22 100644 --- a/services/api/package.json +++ b/services/api/package.json @@ -11,9 +11,11 @@ "scripts": { "type-check": "tsc --noEmit", "build": "tsup-node", + "build:watch": "tsup-node --watch", + "emulate": "firebase emulators:start --project demo-mono-ts --only functions", + "dev": "concurrently \"npm:build:watch\" \"npm:emulate\"", "lint": "eslint \"**/*.ts*\"", - "deploy": "firebase deploy --only functions", - "emulate": "turbo build && isolate && firebase emulators:start --only functions" + "deploy": "firebase deploy --project demo-mono-ts --only functions" }, "license": "MIT", "dependencies": { diff --git a/services/api/tsconfig.json b/services/api/tsconfig.json index 42aa50c..ab04d2e 100644 --- a/services/api/tsconfig.json +++ b/services/api/tsconfig.json @@ -1,8 +1,8 @@ { "extends": "@mono/tsconfig/base.json", "compilerOptions": { - "module": "NodeNext", - "moduleResolution": "NodeNext", + "module": "nodenext", + "moduleResolution": "nodenext", "outDir": "dist", "rootDir": "src", "lib": ["esnext"], diff --git a/services/fns/README.md b/services/fns/README.md index 08ec9ce..2f564b9 100644 --- a/services/fns/README.md +++ b/services/fns/README.md @@ -5,8 +5,34 @@ storage and database rules. ## Deploy -In order to deploy this, first create a Firebase project and set the name in `.firebaserc`. +In order to deploy this, first create a Firebase project and set the name in +`.firebaserc`, or call `npx firebase use my-project-name`. -Then run `pnpm deploy` or `npx firebase deploy` to deploy the functions and the firestore indexes and rules. +Then run `npx firebase deploy` to deploy the api function. -If you only want to deploy functions you can use `npx firebase deploy --functions` +## Firebase Config + +The `firebase.json` file in this packages contains: + +```json +"functions": { + "source": "./isolate", + "runtime": "nodejs20", + "predeploy": ["turbo build", "isolate"], + "codebase": "fns" +}, +``` + +This looks a little different from the configuration in +[services/api/firebase.json](../api/README.md#firebase-config) and that is +because this package uses +[isolate-package](https://github.com/0x80/isolate-package) directly instead of +the integrated approach with +[firebase-tools-with-isolate](https://github.com/0x80/firebase-tools-with-isolate). + +This setup is here mainly to showcase the alternative, but Ii is not recommended +and I might remove it from this demo once I get the impression that +firebase-tools-with-isolate is working for everyone. + +The `codebase` field is only required if you want to deploy to Firebase +Functions from more than one package. It acts as a namespace. diff --git a/services/fns/package.json b/services/fns/package.json index f4364d3..08b4247 100644 --- a/services/fns/package.json +++ b/services/fns/package.json @@ -12,9 +12,10 @@ "scripts": { "type-check": "tsc --noEmit", "build": "tsup-node", + "dev": "concurrently \"npm:build:watch\" \"npm:emulate\"", "lint": "eslint \"**/*.ts*\"", "deploy": "firebase deploy", - "emulate": "turbo build && isolate && firebase emulators:start", + "emulate": "turbo build && isolate && firebase emulators:start --project demo-mono-ts", "db:get-indexes": "firebase firestore:indexes > firestore.indexes.json" }, "license": "MIT", diff --git a/services/fns/tsconfig.json b/services/fns/tsconfig.json index 42aa50c..ab04d2e 100644 --- a/services/fns/tsconfig.json +++ b/services/fns/tsconfig.json @@ -1,8 +1,8 @@ { "extends": "@mono/tsconfig/base.json", "compilerOptions": { - "module": "NodeNext", - "moduleResolution": "NodeNext", + "module": "nodenext", + "moduleResolution": "nodenext", "outDir": "dist", "rootDir": "src", "lib": ["esnext"],