1

Intention:

I want to use Google's TypeScript style guide gts in my Firebase Functions project.

Expected Result:

The command firebase deploy --only functions should still successfully deploy my functions without errors after running npx gts init.

Actual Result:

Deployment of my functions fails even after fixing numerous obvious issues.

Steps to reproduce:

  1. Initialise the firebase functions project by running firebase init functions in an empty folder.
  2. Add relevant functions code to the src/ directory.
  3. Run the "deploy" script, or run firebase deploy --only functions from a terminal.
    • Result: functions deploy without any issues.
  4. Run npx gts init in the functions/ directory to initialise gts.
  5. Run the "deploy" script, or run firebase deploy --only functions from a terminal.
    • Result: deployment fails:
      • Error: There was an error reading functions\package.json:
        functions\lib\index.js does not exist, can't deploy Cloud Functions
  6. Fix directory mismatch.
    • Result: deployment still fails with same error.
  7. Fix location of "main".
    • Result: deployment fails:
      • Error: npm.cmd: not found.
  8. Fix npm.cmd: not found by removing .cmd from scripts.
    • Result: deployment fails:
      • Error: tsc: not found

Below I've listed the project structure and contents of relevant project files at some of the key stages from the above list.
My apologies if it seems verbose, but I thought it best to try and cover most of the potentially relevant info.

After initialising the Firebase functions project, but before initializing gts:

Project has standard Firebase Functions structure.

my-project
 +- functions/     # Directory containing all your functions code
      |
      +- lib/
      |   |
      |   +- index.js  # Built/transpiled JavaScript code
      |   |
      |   +- index.js.map # Source map for debugging
      |
      +- src/     # Directory containing TypeScript source
      |   |
      |   +- index.ts  # main source file for your Cloud Functions code
      |
      +- .eslintrc.js # Optional file if you enabled ESLint
      |
      +- package.json  # npm package file describing your Cloud Functions code
      |
      +- tsconfig.dev.json # Optional file that references .eslintrc.js
      |
      +- tsconfig.json

.eslintrc.js

module.exports = {
  root: true,
  env: {
    es6: true,
    node: true,
  },
  extends: [
    "eslint:recommended",
    "plugin:import/errors",
    "plugin:import/warnings",
    "plugin:import/typescript",
    "google",
    "plugin:@typescript-eslint/recommended",
  ],
  parser: "@typescript-eslint/parser",
  parserOptions: {
    project: ["tsconfig.json", "tsconfig.dev.json"],
    sourceType: "module",
  },
  ignorePatterns: [
    "/lib/**/*", // Ignore built files.
  ],
  plugins: [
    "@typescript-eslint",
    "import",
  ],
  rules: {
    "quotes": ["error", "double"],
    "import/no-unresolved": 0,
    "indent": ["error", 2],
  },
};

package.json

{
  "name": "functions",
  "scripts": {
    "lint": "eslint --ext .js,.ts .",
    "build": "tsc",
    "build:watch": "tsc --watch",
    "serve": "npm run build && firebase emulators:start --only functions",
    "shell": "npm run build && firebase functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log"
  },
  "engines": {
    "node": "16"
  },
  "main": "lib/index.js",
  "dependencies": {
    "firebase-admin": "^11.5.0",
    "firebase-functions": "^4.2.0"
  },
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^5.12.0",
    "@typescript-eslint/parser": "^5.12.0",
    "eslint": "^8.9.0",
    "eslint-config-google": "^0.14.0",
    "eslint-plugin-import": "^2.25.4",
    "firebase-functions-test": "^3.0.0",
    "typescript": "^4.9.0"
  },
  "private": true
}

tsconfig.dev.json

{
  "include": [
    ".eslintrc.js"
  ]
}

tsconfig.json

{
  "compilerOptions": {
    "module": "commonjs",
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "outDir": "lib",
    "sourceMap": true,
    "strict": true,
    "target": "es2017"
  },
  "compileOnSave": true,
  "include": [
    "src"
  ]
}

In the above project state the "deploy" script completes successfully. The functions are deployed without any errors.

After initializing gts:

Project structure.

my-project
 +- functions/     # Directory containing all your functions code
      |
      +- lib/
      |   |
      |   +- index.js  # Built/transpiled JavaScript code
      |   |
      |   +- index.js.map # Source map for debugging
      |
      +- src/     # Directory containing TypeScript source
      |   |
      |   +- index.ts  # main source file for your Cloud Functions code
      |
      +- .editorconfig
      |
      +- .eslintignore
      |
      +- .eslintrc.js # Optional file if you enabled ESLint
      |
      +- .eslintrc.json
      |
      +- .prettierrc.js
      |
      +- package.json  # npm package file describing your Cloud Functions code
      |
      +- tsconfig.dev.json # Optional file that references .eslintrc.js
      |
      +- tsconfig.json

.editorconfig

root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
insert_final_newline = true

.eslintignore

build/

.eslintrc.js

No change.

.eslintrc.json

{
  "extends": "./node_modules/gts/"
}

.prettierrc.js

module.exports = {
  ...require('gts/.prettierrc.json')
}

package.json

{
  "name": "functions",
  "scripts": {
    "lint": "gts lint",
    "build": "tsc",
    "build:watch": "tsc --watch",
    "serve": "npm run build && firebase emulators:start --only functions",
    "shell": "npm run build && firebase functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log",
    "clean": "gts clean",
    "compile": "tsc",
    "fix": "gts fix",
    "prepare": "npm.cmd run compile",
    "pretest": "npm.cmd run compile",
    "posttest": "npm.cmd run lint"
  },
  "engines": {
    "node": "16"
  },
  "main": "lib/index.js",
  "dependencies": {
    "firebase-admin": "^11.5.0",
    "firebase-functions": "^4.2.0"
  },
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^5.12.0",
    "@typescript-eslint/parser": "^5.12.0",
    "eslint": "^8.9.0",
    "eslint-config-google": "^0.14.0",
    "eslint-plugin-import": "^2.25.4",
    "firebase-functions-test": "^3.0.0",
    "typescript": "~4.7.0",
    "gts": "^3.1.1",
    "@types/node": "^14.11.2"
  },
  "private": true
}

tsconfig.dev.json

No change.

tsconfig.json

{
  "extends": "./node_modules/gts/tsconfig-google.json",
  "compilerOptions": {
    "rootDir": ".",
    "outDir": "build"
  },
  "include": [
    "src/**/*.ts",
    "test/**/*.ts"
  ]
}

In the above project state the "deploy" script fails with the error:

Error: There was an error reading functions\package.json:

 functions\lib\index.js does not exist, can't deploy Cloud Functions

This error is because by default Firebase functions sets the "outDir" in tsconfig.json to "lib", but initialising gts changes the "outDir" to "build".
I'm not sure how I would go about changing the "deploy" script to expect the "outDir" to be "build", so I instead reverted the "outDir" to be "lib".

After reverting to "outDir": "lib" in tsconfig.json:

tsconfig.json

{
  "extends": "./node_modules/gts/tsconfig-google.json",
  "compilerOptions": {
    "rootDir": ".",
    "outDir": "lib"
  },
  "include": [
    "src/**/*.ts",
    "test/**/*.ts"
  ]
}

In this project state the "deploy" script again fails with the error:

Error: There was an error reading functions\package.json:

 functions\lib\index.js does not exist, can't deploy Cloud Functions

This time the error is because index.js gets built to the directory functions\lib\src\ not functions\lib\.
I fixed this issue by changing "main": "lib/index.js" in package.json to "main": "lib/src/index.js".

After setting "main": "lib/src/index.js" in package.json:

package.json

{
  "name": "functions",
  "scripts": {
    "lint": "gts lint",
    "build": "tsc",
    "build:watch": "tsc --watch",
    "serve": "npm run build && firebase emulators:start --only functions",
    "shell": "npm run build && firebase functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log",
    "clean": "gts clean",
    "compile": "tsc",
    "fix": "gts fix",
    "prepare": "npm.cmd run compile",
    "pretest": "npm.cmd run compile",
    "posttest": "npm.cmd run lint"
  },
  "engines": {
    "node": "16"
  },
  "main": "lib/src/index.js",
  "dependencies": {
    "firebase-admin": "^11.5.0",
    "firebase-functions": "^4.2.0"
  },
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^5.12.0",
    "@typescript-eslint/parser": "^5.12.0",
    "eslint": "^8.9.0",
    "eslint-config-google": "^0.14.0",
    "eslint-plugin-import": "^2.25.4",
    "firebase-functions-test": "^3.0.0",
    "typescript": "~4.7.0",
    "gts": "^3.1.1",
    "@types/node": "^14.11.2"
  },
  "private": true
}

In this project state the "deploy" script fails as shown below:

i  deploying functions
Running command: npm --prefix "$RESOURCE_DIR" run lint

> lint
> gts lint

version: 16

Running command: npm --prefix "$RESOURCE_DIR" run build

> build
> tsc

+  functions: Finished running predeploy script.
i  functions: preparing codebase default for deployment
i  functions: ensuring required API cloudfunctions.googleapis.com is enabled...
i  functions: ensuring required API cloudbuild.googleapis.com is enabled...
i  artifactregistry: ensuring required API artifactregistry.googleapis.com is enabled...
+  functions: required API cloudbuild.googleapis.com is enabled
+  functions: required API cloudfunctions.googleapis.com is enabled
+  artifactregistry: required API artifactregistry.googleapis.com is enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged D:\Projects\my-project\functions (127.84 KB) for uploading
i  functions: ensuring required API identitytoolkit.googleapis.com is enabled...
+  functions: required API identitytoolkit.googleapis.com is enabled
+  functions: functions folder uploaded successfully
i  functions: updating Node.js 16 function userBeforeCreate(us-central1)...
i  functions: updating Node.js 16 function userOnCreate(us-central1)...
Build failed: > prepare
> npm.cmd run compile

sh: 1: npm.cmd: not found
npm ERR! code 127
npm ERR! path /workspace
npm ERR! command failed
npm ERR! command sh -c -- npm.cmd run compile

npm ERR! A complete log of this run can be found in:
npm ERR!     /www-data-home/.npm/_logs/2023-05-05T09_23_49_048Z-debug-0.log; Error ID: beaf8772
Build failed: > prepare
> npm.cmd run compile

sh: 1: npm.cmd: not found
npm ERR! code 127
npm ERR! path /workspace
npm ERR! command failed
npm ERR! command sh -c -- npm.cmd run compile

npm ERR! A complete log of this run can be found in:
npm ERR!     /www-data-home/.npm/_logs/2023-05-05T09_24_40_038Z-debug-0.log; Error ID: beaf8772

Functions deploy had errors with the following functions:
        userBeforeCreate(us-central1)
        userOnCreate(us-central1)
i  functions: cleaning up build files...
Error: There was an error deploying functions:
- Error Failed to update function userBeforeCreate in region us-central1
- Error Failed to update function userOnCreate in region us-central1

Process finished with exit code 2

And here's one of the Google Cloud logs mentioned (they're both essentially identical)[Note: ids etc have been xxxxx hidden]:

{
  "protoPayload": {
    "@type": "type.googleapis.com/google.cloud.audit.AuditLog",
    "status": {
      "code": 3,
      "message": "Build failed: > prepare\n> npm.cmd run compile\n\nsh: 1: npm.cmd: not found\nnpm ERR! code 127\nnpm ERR! path /workspace\nnpm ERR! command failed\nnpm ERR! command sh -c -- npm.cmd run compile\n\nnpm ERR! A complete log of this run can be found in:\nnpm ERR!     /www-data-home/.npm/_logs/2023-05-05T09_10_41_708Z-debug-0.log; Error ID: beaf8772"
    },
    "authenticationInfo": {},
    "serviceName": "cloudfunctions.googleapis.com",
    "methodName": "google.cloud.functions.v1.CloudFunctionsService.UpdateFunction",
    "resourceName": "projects/my-project/locations/us-central1/functions/userBeforeCreate"
  },
  "insertId": "xxxxx",
  "resource": {
    "type": "cloud_function",
    "labels": {
      "region": "us-central1",
      "function_name": "userBeforeCreate",
      "project_id": "my-project"
    }
  },
  "timestamp": "2023-05-05T09:10:59.793425Z",
  "severity": "ERROR",
  "logName": "projects/my-project/logs/cloudaudit.googleapis.com%2Factivity",
  "operation": {
    "id": "operations/xxxxx",
    "producer": "cloudfunctions.googleapis.com",
    "last": true
  },
  "receiveTimestamp": "2023-05-05T09:11:00.085118556Z"
}

To address the npm.cmd: not found issue, I then tried removing .cmd from the "prepare", "pretest" & "posttest" scripts in package.json.

After removing .cmd from the "prepare", "pretest" & "posttest" scripts:

package.json

{
  "name": "functions",
  "scripts": {
    "lint": "gts lint",
    "build": "tsc",
    "build:watch": "tsc --watch",
    "serve": "npm run build && firebase emulators:start --only functions",
    "shell": "npm run build && firebase functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log",
    "clean": "gts clean",
    "compile": "tsc",
    "fix": "gts fix",
    "prepare": "npm run compile",
    "pretest": "npm run compile",
    "posttest": "npm run lint"
  },
  "engines": {
    "node": "16"
  },
  "main": "lib/src/index.js",
  "dependencies": {
    "firebase-admin": "^11.5.0",
    "firebase-functions": "^4.2.0"
  },
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^5.12.0",
    "@typescript-eslint/parser": "^5.12.0",
    "eslint": "^8.9.0",
    "eslint-config-google": "^0.14.0",
    "eslint-plugin-import": "^2.25.4",
    "firebase-functions-test": "^3.0.0",
    "typescript": "~4.7.0",
    "gts": "^3.1.1",
    "@types/node": "^14.11.2"
  },
  "private": true
}

In this project state the "deploy" script fails as shown below:

i  deploying functions
Running command: npm --prefix "$RESOURCE_DIR" run lint

> lint
> gts lint

version: 16

Running command: npm --prefix "$RESOURCE_DIR" run build

> build
> tsc

+  functions: Finished running predeploy script.
i  functions: preparing codebase default for deployment
i  functions: ensuring required API cloudfunctions.googleapis.com is enabled...
i  functions: ensuring required API cloudbuild.googleapis.com is enabled...
i  artifactregistry: ensuring required API artifactregistry.googleapis.com is enabled...
+  functions: required API cloudfunctions.googleapis.com is enabled
+  artifactregistry: required API artifactregistry.googleapis.com is enabled
+  functions: required API cloudbuild.googleapis.com is enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged D:\Projects\my-project\functions (127.96 KB) for uploading
i  functions: ensuring required API identitytoolkit.googleapis.com is enabled...
+  functions: required API identitytoolkit.googleapis.com is enabled
+  functions: functions folder uploaded successfully
i  functions: updating Node.js 16 function userBeforeCreate(us-central1)...
i  functions: updating Node.js 16 function userOnCreate(us-central1)...
Build failed: > prepare
> npm run compile


> compile
> tsc

sh: 1: tsc: not found
npm ERR! code 127
npm ERR! path /workspace
npm ERR! command failed
npm ERR! command sh -c -- npm run compile

npm ERR! A complete log of this run can be found in:
npm ERR!     /www-data-home/.npm/_logs/2023-05-05T10_46_49_310Z-debug-0.log; Error ID: beaf8772
Build failed: > prepare
> npm run compile


> compile
> tsc

sh: 1: tsc: not found
npm ERR! code 127
npm ERR! path /workspace
npm ERR! command failed
npm ERR! command sh -c -- npm run compile

npm ERR! A complete log of this run can be found in:
npm ERR!     /www-data-home/.npm/_logs/2023-05-05T10_47_18_266Z-debug-0.log; Error ID: beaf8772

Functions deploy had errors with the following functions:
        userBeforeCreate(us-central1)
        userOnCreate(us-central1)
i  functions: cleaning up build files...
Error: There was an error deploying functions:
- Error Failed to update function userBeforeCreate in region us-central1
- Error Failed to update function userOnCreate in region us-central1

Process finished with exit code 2

I don't know how to fix the issue from here, so any assistance would be most appreciated.

Sound Conception
  • 5,263
  • 5
  • 30
  • 47

1 Answers1

0

I've manage to replicate your issue. Here are the steps on what I did:

  1. Created a project in a clear folder named "FirebaseGTS" using the code you provided "firebase init functions"
  2. Fill the necessary information needed. E.g (existing project, Project ID)

Then

Language: TypeScript

Do you want to use ESLint to catch probable bugs and enforce style ? : "NO"

It will automatically create the following:

Wrote functions/package.json
Wrote functions/tsconfig.json
Wrote functions/src/index.ts
Wrote functions/.gitignore

Next

Install dependencies with npm now ? : YES this adds all the packages needed.

  1. I used firebase deploy --only functions

Result: Deployed without any issues.

  1. Now here i installed the npx gts init inside the function folder as you mentioned.

Package.json does not exist. Generate ? : YES

"Auto generation"

Writes package.json
Writes ./tsconfig.json
Writes ./.eslintrc.json
Writes ./.eslintignore
Writes ./.prettierrc.js
Writes ./.editorconfig

Result: added packages, and audited packages needed.

Note: Dont Overwrite the devDependency since you have the lastest which is v4.9.0 rather than v4.7.0

  1. Inside the function folder, i just did firebase deploy --only functions

Result: Deploy Complete!

DominicT
  • 388
  • 1
  • 9