simondotm / nx-firebase

Firebase plugin for Nx Monorepos
https://www.npmjs.com/package/@simondotm/nx-firebase
MIT License
175 stars 31 forks source link

WIP: Suggestions for executors #127

Closed Schmale97 closed 10 months ago

Schmale97 commented 1 year ago

This is a PR with some suggestions for some executors to improve firebase's cli within nx. I have these changes currently as a workspace plugin that I have been playing around with and noticed a few issues pop-up which I think these executors help to fix ( #92 and #40 ). I have copied the local plugin into these repo quickly to gauge interest in including these in this plugin.

There are 3 executors:

My project stucture is:

apps/
> example/
>> functions/
>> app/
>> firebase/

The project.json I have in the functions folder is

{
  "name": "example-functions",
  "$schema": "../../../node_modules/nx/schemas/project-schema.json",
  "sourceRoot": "apps/example/functions/src",
  "projectType": "application",
  "targets": {
    "build": {
      "executor": "@nx/esbuild:esbuild",
      "outputs": ["{options.outputPath}"],
      "options": {
        "project": "apps/example/functions/package.json",
        "outputPath": "dist/apps/example/functions",
        "main": "apps/example/functions/src/index.ts",
        "tsConfig": "apps/example/functions/tsconfig.app.json",
        "assets": ["apps/example/functions/.env.*"],
        "bundle": true,
        "minify": true,
        "generatePackageJson": true,
        "platform": "node",
        "esbuildOptions": {
          "splitting": true
        }
      },
      "configurations": {
        "watch": {
          "watch": true,
          "minify": false
        }
      }
    },
    "lint": {
      "executor": "@nx/linter:eslint",
      "options": {
        "lintFilePatterns": ["apps/example/functions/**/*.ts"]
      },
      "outputs": ["{options.outputFile}"]
    },
    "test": {
      "executor": "@nx/jest:jest",
      "outputs": ["{workspaceRoot}/coverage/apps/example/functions"],
      "options": {
        "jestConfig": "apps/example/functions/jest.config.ts",
        "passWithNoTests": true
      }
    },
    "copy-local-files": {
      "executor": "@workspace-plugin/firebase:copy-local-files",
      "inputs": ["{projectRoot}/.(secret|env).local"]
    }
  },
  "tags": []
}

and the project json I have for the firebase folder is:

{
  "name": "example-firebase",
  "$schema": "../../../node_modules/nx/schemas/project-schema.json",
  "projectType": "application",
  "targets": {
    "cli": {
      "executor": "@workspace-plugin/firebase:cli",
      "options": {
        "config": "firebase.example.json"
      }
    },
    "deploy": {
      "executor": "@workspace-plugin/firebase:cli",
      "options": {
        "command": "deploy",
        "config": "firebase.example.json",
        "non-interactive": true,
        "force": true
      },
      "dependsOn": [
        { "projects": ["example-functions"], "target": "build" }
      ],
      "configurations": {
        "dev": {
          "project": "example-dev"
        },
        "prd": {
          "project": "example-prd"
        }
      }
    },
    "emulators:start": {
      "executor": "@workspace-plugin/firebase:cli",
      "dependsOn": [
        { "projects": ["example-functions"], "target": "build" },
        {
          "projects": ["example-functions"],
          "target": "copy-local-files"
        }
      ],
      "options": {
        "command": "emulators:start",
        "config": "firebase.example.json"
      },
      "configurations": {
        "dev": {
          "project": "example-dev"
        },
        "prd": {
          "project": "example-prd"
        }
      }
    },
    "emulators:exec": {
      "executor": "@workspace-plugin/firebase:cli",
      "dependsOn": [
        { "projects": ["example-functions"], "target": "build" },
        {
          "projects": ["example-functions"],
          "target": "copy-local-files"
        }
      ],
      "options": {
        "command": "emulators:exec",
        "config": "firebase.example.json"
      },
      "configurations": {
        "dev": {
          "project": "example-dev"
        },
        "prd": {
          "project": "example-prd"
        }
      }
    },
    "emulators:web": {
      "executor": "@workspace-plugin/firebase:cli",
      "dependsOn": [
        { "projects": ["example-functions"], "target": "build" },
        {
          "projects": ["example-functions"],
          "target": "copy-local-files"
        }
      ],
      "options": {
        "command": "emulators:exec",
        "config": "firebase.example.json"
      },
      "configurations": {
        "dev": {
          "project": "example-dev",
          "_": ["npx nx run example-app:serve:dev"]
        },
        "prd": {
          "project": "example-prd",
          "_": ["npx nx run example-app:serve:prd"]
        }
      }
    },
    "serve": {
      "executor": "nx:run-commands",
      "options": {
        "commands": [
          "npx nx run example-functions:build:watch",
          "npx nx run example-firebase:emulators:start"
        ],
        "parallel": true
      },
      "configurations": {
        "dev": {
          "commands": [
            "npx nx run example-functions:build:watch",
            "npx nx run example-firebase:emulators:start:dev"
          ]
        },
        "prd": {
          "commands": [
            "npx nx run example-functions:build:watch",
            "npx nx run example-firebase:emulators:start:prd"
          ]
        },
        "dev:web": {
          "commands": [
            "npx nx run example-functions:build:watch",
            "npx nx run example-firebase:emulators:web:dev"
          ]
        },
        "prd:web": {
          "commands": [
            "npx nx run example-functions:build:watch",
            "npx nx run example-firebase:emulators:web:pre"
          ]
        }
      }
    },
    "emulators:kill": {
      "executor": "@workspace-plugin/firebase:kill-emulator",
      "options": {
        "config": "firebase.example.json"
      }
    }
  },
  "tags": [],
  "implicitDependencies": ["example-app", "example-functions"]
}

I have shared these jsons just to demonstrate how I am using the executors in the pr.

Again, this is just what I have found to be useful so far and am still working through a few things:

  1. As soon as you use "run-commands" the interactivity breaks so selecting options for the command runs breaks and killing the emulators is no longer clean - this is demonstrated in the serve command (but this is the only way I have got the build:watch to work)
  2. My pubsub emulators never seem to get a clean shutdown
  3. I don't currently have a good way of connecting the frontend to the emulators based on if the emulators are running (I think this is a limitation of the firebase cli so don't know if it should be addressed here)

Let me know what you think

simondotm commented 11 months ago

Hi @Schmale97 , thanks for your PR. It's great to have submissions & experiementations, and I can see how such solutions might be useful for customising a specific workflow/workspace, but for a generic plugin like this one, I'm not sure it adds value over what we already have:

The copy-local-files executor you have is an interesting workaround, but I believe we can achieve the same outcome by modifying the .nxignore files with exclusion rules and add local secret files to the build executor assets array - I just havent had time to investigate this better.

For your custom NX_REACT_APP_STORAGE_EMULATOR type env vars, I can see why you took that approach. For me, I have added an environment.dev.ts file to my FE project (Angular in my case), and set the appropriate emulator client app vars from that. eg.

  providers: [
    {
      provide: AUTH_EMULATOR,
      useValue: environment.localEmulation ? ['http://localhost:9099'] : undefined,
    },
    {
      provide: FIRESTORE_EMULATOR,
      useValue: environment.localEmulation ? ['localhost', 8080] : undefined,
    },
    {
      provide: DATABASE_EMULATOR, // i.e., Realtime Database
      useValue: environment.localEmulation ? ['localhost', 9000] : undefined,
    },
    {
      provide: FUNCTIONS_EMULATOR,
      useValue: environment.localEmulation ? ['http://localhost', 5001] : undefined,
    },
  ],
simondotm commented 10 months ago

I'm going to close this PR for now, but I welcome & will review future contributions.