diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ebb9d9b3e..6786c6311 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -115,7 +115,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: 'temurin' - java-version: '11' + java-version: '21' - name: node_modules cache id: node_modules_cache uses: actions/cache@v3 @@ -183,7 +183,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: 'temurin' - java-version: '11' + java-version: '21' - name: Test headless run: npm run test:chrome-headless # Tests are flaky on Windows diff --git a/docs/deploy/getting-started.md b/docs/deploy/getting-started.md index 5e3cf180d..1c701adc0 100644 --- a/docs/deploy/getting-started.md +++ b/docs/deploy/getting-started.md @@ -97,7 +97,7 @@ Setting `functionsNodeVersion` and `functionsRuntimeOptions` in your `angular.js "deploy": { "builder": "@angular/fire:deploy", "options": { - "functionsNodeVersion": 12, + "functionsNodeVersion": 22, "functionsRuntimeOptions": { "memory": "2GB", "timeoutSeconds": 10, diff --git a/package-lock.json b/package-lock.json index 45a587ed3..149335d97 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@angular/fire", - "version": "20.0.0", + "version": "20.0.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@angular/fire", - "version": "20.0.0", + "version": "20.0.2", "license": "MIT", "dependencies": { "@angular-devkit/architect": "~0.2000.0", @@ -21,7 +21,8 @@ "@schematics/angular": "^20.0.0", "esbuild": "^0.24.0", "firebase": "^11.8.0", - "firebase-functions": "^6.1.0", + "firebase-admin": "^13.0.0", + "firebase-functions": "^6.1.1", "fs-extra": "^8.0.1", "fuzzy": "^0.1.3", "husky": "^4.2.5", @@ -4775,8 +4776,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.1.1.tgz", "integrity": "sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@firebase/ai": { "version": "1.3.0", @@ -5392,7 +5392,6 @@ "integrity": "sha512-ZxOdH8Wr01hBDvKCQfMWqwUcfNcN3JY19k1LtS1fTFhEyorYPLsbWN+VxIRL46pOYGHTPkU3Or5HbT/SLQM5nA==", "license": "Apache-2.0", "optional": true, - "peer": true, "dependencies": { "@opentelemetry/api": "^1.3.0", "fast-deep-equal": "^3.1.1", @@ -5410,7 +5409,6 @@ "integrity": "sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==", "license": "Apache-2.0", "optional": true, - "peer": true, "dependencies": { "arrify": "^2.0.0", "extend": "^3.0.2" @@ -5425,7 +5423,6 @@ "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==", "license": "Apache-2.0", "optional": true, - "peer": true, "engines": { "node": ">=14.0.0" } @@ -5436,7 +5433,6 @@ "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==", "license": "Apache-2.0", "optional": true, - "peer": true, "engines": { "node": ">=14" } @@ -5447,7 +5443,6 @@ "integrity": "sha512-7/5LRgykyOfQENcm6hDKP8SX/u9XxE5YOiWOkgkwcoO+cG8xT/cyOvp9wwN3IxfdYgpHs8CE7Nq2PKX2lNaEXw==", "license": "Apache-2.0", "optional": true, - "peer": true, "dependencies": { "@google-cloud/paginator": "^5.0.0", "@google-cloud/projectify": "^4.0.0", @@ -5475,7 +5470,6 @@ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "license": "MIT", "optional": true, - "peer": true, "bin": { "uuid": "dist/bin/uuid" } @@ -5791,7 +5785,6 @@ "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", "license": "MIT", "optional": true, - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/js-sdsl" @@ -6658,7 +6651,6 @@ "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", "optional": true, - "peer": true, "engines": { "node": ">=8.0.0" } @@ -7508,7 +7500,6 @@ "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "license": "MIT", "optional": true, - "peer": true, "engines": { "node": ">= 10" } @@ -7562,8 +7553,7 @@ "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/@types/connect": { "version": "3.4.38", @@ -7731,7 +7721,6 @@ "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.9.tgz", "integrity": "sha512-uoe+GxEuHbvy12OUQct2X9JenKM3qAscquYymuQN4fMWG9DBQtykrQEFcAbVACF7qaLw9BePSodUL0kquqBJpQ==", "license": "MIT", - "peer": true, "dependencies": { "@types/ms": "*", "@types/node": "*" @@ -7759,8 +7748,7 @@ "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/@types/mime": { "version": "1.3.5", @@ -7772,8 +7760,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/node": { "version": "12.12.41", @@ -8036,8 +8023,7 @@ "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/@types/triple-beam": { "version": "1.3.5", @@ -8505,7 +8491,6 @@ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "event-target-shim": "^5.0.0" }, @@ -8897,7 +8882,6 @@ "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", "license": "MIT", "optional": true, - "peer": true, "engines": { "node": ">=8" } @@ -8924,7 +8908,6 @@ "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "retry": "0.13.1" } @@ -8934,8 +8917,7 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/autoprefixer": { "version": "10.4.21", @@ -9139,7 +9121,6 @@ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.0.tgz", "integrity": "sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==", "license": "MIT", - "peer": true, "engines": { "node": "*" } @@ -9309,8 +9290,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/buffer-from": { "version": "1.1.2", @@ -9855,7 +9835,6 @@ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -10772,7 +10751,6 @@ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "license": "MIT", "optional": true, - "peer": true, "engines": { "node": ">=0.4.0" } @@ -10969,7 +10947,6 @@ "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "end-of-stream": "^1.4.1", "inherits": "^2.0.3", @@ -10989,7 +10966,6 @@ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "safe-buffer": "^5.0.1" } @@ -11067,7 +11043,6 @@ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "once": "^1.4.0" } @@ -11867,7 +11842,6 @@ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "license": "MIT", "optional": true, - "peer": true, "engines": { "node": ">=6" } @@ -11982,7 +11956,6 @@ "resolved": "https://registry.npmjs.org/farmhash-modern/-/farmhash-modern-1.1.0.tgz", "integrity": "sha512-6ypT4XfgqJk/F3Yuv4SX26I3doUjt0GTG4a+JgWxXQpxXzTBq8fPUeGHfcYMMDPHJHm3yPOSjaeBwBGAHWXCdA==", "license": "MIT", - "peer": true, "engines": { "node": ">=18.0.0" } @@ -12065,7 +12038,6 @@ ], "license": "MIT", "optional": true, - "peer": true, "dependencies": { "strnum": "^1.1.1" }, @@ -12317,7 +12289,6 @@ "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-13.4.0.tgz", "integrity": "sha512-Y8DcyKK+4pl4B93ooiy1G8qvdyRMkcNFfBSh+8rbVcw4cW8dgG0VXCCTp5NUwub8sn9vSPsOwpb9tE2OuFmcfQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@fastify/busboy": "^3.0.0", "@firebase/database-compat": "^2.0.0", @@ -12343,7 +12314,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.24.tgz", "integrity": "sha512-w9CZGm9RDjzTh/D+hFwlBJ3ziUaVw7oufKA3vOFSOZlzmW9AkZnfjPb+DLnrV6qtgL/LNmP0/2zBNCFHL3F0ng==", "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -12467,7 +12437,6 @@ "integrity": "sha512-XHIrMD0NpDrNM/Ckf7XJiBbLl57KEhT3+i3yY+eWm+cqYZJQTZrKo8Y8AWKnuV5GT4scfuUGt9LzNoIx3dU1nQ==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -12595,8 +12564,7 @@ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/functions-have-names": { "version": "1.2.3", @@ -12621,7 +12589,6 @@ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", @@ -12642,7 +12609,6 @@ "https://github.com/sponsors/ctavan" ], "license": "MIT", - "peer": true, "bin": { "uuid": "dist/bin/uuid" } @@ -12652,7 +12618,6 @@ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", "license": "Apache-2.0", - "peer": true, "dependencies": { "gaxios": "^6.1.1", "google-logging-utils": "^0.0.2", @@ -13086,7 +13051,6 @@ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", "license": "Apache-2.0", - "peer": true, "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", @@ -13105,7 +13069,6 @@ "integrity": "sha512-V6eky/xz2mcKfAd1Ioxyd6nmA61gao3n01C+YeuIwu3vzM9EDR6wcVzMSIbLMDXWeoi9SHYctXuKYC5uJUT3eQ==", "license": "Apache-2.0", "optional": true, - "peer": true, "dependencies": { "@grpc/grpc-js": "^1.10.9", "@grpc/proto-loader": "^0.7.13", @@ -13130,7 +13093,6 @@ "integrity": "sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==", "license": "Apache-2.0", "optional": true, - "peer": true, "dependencies": { "@grpc/proto-loader": "^0.7.13", "@js-sdsl/ordered-map": "^4.4.2" @@ -13149,7 +13111,6 @@ ], "license": "MIT", "optional": true, - "peer": true, "bin": { "uuid": "dist/bin/uuid" } @@ -13159,7 +13120,6 @@ "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=14" } @@ -13194,7 +13154,6 @@ "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", "license": "MIT", - "peer": true, "dependencies": { "gaxios": "^6.0.0", "jws": "^4.0.0" @@ -13424,8 +13383,7 @@ } ], "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/html-escaper": { "version": "2.0.2", @@ -14746,7 +14704,6 @@ "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/panva" } @@ -14795,7 +14752,6 @@ "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", "license": "MIT", - "peer": true, "dependencies": { "bignumber.js": "^9.0.0" } @@ -14904,7 +14860,6 @@ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", "license": "MIT", - "peer": true, "dependencies": { "jws": "^3.2.2", "lodash.includes": "^4.3.0", @@ -14927,7 +14882,6 @@ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", "license": "MIT", - "peer": true, "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", @@ -14939,7 +14893,6 @@ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", "license": "MIT", - "peer": true, "dependencies": { "jwa": "^1.4.1", "safe-buffer": "^5.0.1" @@ -14950,7 +14903,6 @@ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", "license": "MIT", - "peer": true, "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", @@ -14962,7 +14914,6 @@ "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.2.0.tgz", "integrity": "sha512-PwchfHcQK/5PSydeKCs1ylNym0w/SSv8a62DgHJ//7x2ZclCoinlsjAfDxAAbpoTPybOum/Jgy+vkvMmKz89Ww==", "license": "MIT", - "peer": true, "dependencies": { "@types/express": "^4.17.20", "@types/jsonwebtoken": "^9.0.4", @@ -14980,7 +14931,6 @@ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", "license": "MIT", - "peer": true, "dependencies": { "jwa": "^2.0.0", "safe-buffer": "^5.0.1" @@ -15578,8 +15528,7 @@ "node_modules/limiter": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", - "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==", - "peer": true + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" }, "node_modules/lines-and-columns": { "version": "1.2.4", @@ -15821,8 +15770,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/lodash.debounce": { "version": "4.0.8", @@ -15835,43 +15783,37 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/lodash.isnumber": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/lodash.isstring": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/lodash.merge": { "version": "4.6.2", @@ -15884,8 +15826,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/lodash.template": { "version": "4.5.0", @@ -16213,7 +16154,6 @@ "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz", "integrity": "sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==", "license": "MIT", - "peer": true, "dependencies": { "lodash.clonedeep": "^4.5.0", "lru-cache": "6.0.0" @@ -16224,7 +16164,6 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "license": "ISC", - "peer": true, "dependencies": { "yallist": "^4.0.0" }, @@ -16236,8 +16175,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/magic-string": { "version": "0.30.17", @@ -16589,7 +16527,6 @@ "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", "license": "MIT", "optional": true, - "peer": true, "bin": { "mime": "cli.js" }, @@ -17763,7 +17700,6 @@ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "license": "MIT", - "peer": true, "dependencies": { "whatwg-url": "^5.0.0" }, @@ -18145,7 +18081,6 @@ "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", "license": "MIT", "optional": true, - "peer": true, "engines": { "node": ">= 6" } @@ -19056,7 +18991,6 @@ "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", "license": "Apache-2.0", "optional": true, - "peer": true, "dependencies": { "protobufjs": "^7.2.5" }, @@ -19840,7 +19774,6 @@ "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "@types/request": "^2.48.8", "extend": "^3.0.2", @@ -19856,7 +19789,6 @@ "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "@types/caseless": "*", "@types/node": "*", @@ -21217,7 +21149,6 @@ "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "stubs": "^3.0.0" } @@ -21227,8 +21158,7 @@ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/streamroller": { "version": "3.1.5", @@ -21435,16 +21365,14 @@ } ], "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/stubs": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/supports-color": { "version": "7.2.0", @@ -21588,7 +21516,6 @@ "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", "license": "Apache-2.0", "optional": true, - "peer": true, "dependencies": { "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0", @@ -21606,7 +21533,6 @@ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "debug": "4" }, @@ -21620,7 +21546,6 @@ "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "@tootallnate/once": "2", "agent-base": "6", @@ -21636,7 +21561,6 @@ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "agent-base": "6", "debug": "4" @@ -21655,7 +21579,6 @@ ], "license": "MIT", "optional": true, - "peer": true, "bin": { "uuid": "dist/bin/uuid" } @@ -21891,8 +21814,7 @@ "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/tree-dump": { "version": "1.0.3", @@ -22647,7 +22569,6 @@ "https://github.com/sponsors/ctavan" ], "license": "MIT", - "peer": true, "bin": { "uuid": "dist/esm/bin/uuid" } @@ -22743,8 +22664,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause", - "peer": true + "license": "BSD-2-Clause" }, "node_modules/webpack": { "version": "5.99.8", @@ -23170,7 +23090,6 @@ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "license": "MIT", - "peer": true, "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" diff --git a/package.json b/package.json index dbfd2b852..b57ae332f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@angular/fire", - "version": "20.0.0", + "version": "20.0.2", "description": "Angular + Firebase = ❤️", "private": true, "scripts": { @@ -58,7 +58,8 @@ "@schematics/angular": "^20.0.0", "esbuild": "^0.24.0", "firebase": "^11.8.0", - "firebase-functions": "^6.1.0", + "firebase-admin": "^13.0.0", + "firebase-functions": "^6.1.1", "fs-extra": "^8.0.1", "fuzzy": "^0.1.3", "husky": "^4.2.5", diff --git a/site/src/get-started/deploying.md b/site/src/get-started/deploying.md index b0e9011b4..f5c4886a7 100644 --- a/site/src/get-started/deploying.md +++ b/site/src/get-started/deploying.md @@ -98,13 +98,13 @@ To customize the deployment flow, you can use the configuration files you're alr ### Configuring Cloud Functions -Setting `functionsNodeVersion` and `functionsRuntimeOptions` in your `angular.json` allow you to custimze the version of Node.js Cloud Functions is running and run-time settings like timeout, VPC connectors, and memory. +Setting `functionsNodeVersion` and `functionsRuntimeOptions` in your `angular.json` allow you to customize the version of Node.js Cloud Functions is running and run-time settings like timeout, VPC connectors, and memory. ```json "deploy": { "builder": "@angular/fire:deploy", "options": { - "functionsNodeVersion": 12, + "functionsNodeVersion": 22, "functionsRuntimeOptions": { "memory": "2GB", "timeoutSeconds": 10, diff --git a/src/package.json b/src/package.json index 4f0bc9082..1cce5ce94 100644 --- a/src/package.json +++ b/src/package.json @@ -26,10 +26,9 @@ "@angular/common": "^20.0.0", "@angular/core": "^20.0.0", "@angular/platform-browser": "^20.0.0", - "@angular/platform-browser-dynamic": "^20.0.0", "@angular/platform-server": "^20.0.0", "rxjs": "~7.8.0", - "firebase-tools": "^14.0.0" + "firebase-tools": "^14.0.0 || ^15.0.0" }, "peerDependenciesMeta": { "firebase-tools": { "optional": true }, diff --git a/src/schematics/deploy/functions-templates.jasmine.ts b/src/schematics/deploy/functions-templates.jasmine.ts new file mode 100644 index 000000000..b6bf4c148 --- /dev/null +++ b/src/schematics/deploy/functions-templates.jasmine.ts @@ -0,0 +1,20 @@ +import { DEFAULT_NODE_VERSION, defaultFunction, defaultPackage } from './functions-templates.js'; +import 'jasmine'; + +describe('functions templates', () => { + describe('defaultFunction', () => { + it('requires the firebase-functions/v1 subpath, where the v1 API lives on firebase-functions 6', () => { + const generated = defaultFunction('dist/app', {}, undefined); + expect(generated).toContain(`require('firebase-functions/v1')`); + expect(generated).not.toContain(`require('firebase-functions')`); + }); + }); + + describe('defaultPackage', () => { + it('defaults engines.node to a runtime Cloud Functions still accepts', () => { + const generated = defaultPackage({}, {}, {}); + expect(generated.engines.node).toBe(DEFAULT_NODE_VERSION.toString()); + expect(DEFAULT_NODE_VERSION).toBe(22); + }); + }); +}); diff --git a/src/schematics/deploy/functions-templates.ts b/src/schematics/deploy/functions-templates.ts index 4005675c9..b7af2a3b1 100644 --- a/src/schematics/deploy/functions-templates.ts +++ b/src/schematics/deploy/functions-templates.ts @@ -1,6 +1,9 @@ import { DeployBuilderOptions } from './actions'; -export const DEFAULT_NODE_VERSION = 14; +// Must be a runtime Cloud Functions still accepts, and must satisfy the engines of @angular/core +// (the function runs the Angular server) and of firebase-admin. The `dockerfile` template below +// also drops it into `FROM node:-slim`, so it picks the base image on the Cloud Run path. +export const DEFAULT_NODE_VERSION = 22; export const DEFAULT_FUNCTION_NAME = 'ssr'; const DEFAULT_FUNCTION_REGION = 'us-central1'; @@ -30,14 +33,16 @@ export const defaultPackage = ( private: true }); +// `.region()` lives on the /v1 subpath since firebase-functions 6. Requiring the package root +// instead still resolves, and fails only when the deployed function serves its first request. export const defaultFunction = ( path: string, options: DeployBuilderOptions, functionName: string|undefined, -) => `const functions = require('firebase-functions'); +) => `const functions = require('firebase-functions/v1'); // Increase readability in Cloud Logging -require("firebase-functions/lib/logger/compat"); +require("firebase-functions/logger/compat"); const expressApp = require('./${path}/main').app(); @@ -55,7 +60,7 @@ export const functionGen2 = ( ) => `const { onRequest } = require('firebase-functions/v2/https'); // Increase readability in Cloud Logging -require("firebase-functions/lib/logger/compat"); +require("firebase-functions/logger/compat"); const expressApp = require('./${path}/main').app(); diff --git a/src/schematics/firebaseTools.ts b/src/schematics/firebaseTools.ts index 2d8c394b1..3d8136763 100644 --- a/src/schematics/firebaseTools.ts +++ b/src/schematics/firebaseTools.ts @@ -45,7 +45,7 @@ export const getFirebaseTools = () => globalThis.firebaseTools ? const version = firebaseTools.cli.version(); console.log(`Using firebase-tools version ${version}`); if (semverCompare(version, '14.0.0') === -1) { - console.error('firebase-tools version 13.0.0+ is required, please upgrade and run again'); + console.error('firebase-tools version 14.0.0+ is required, please upgrade and run again'); return Promise.reject(); } return firebaseTools; diff --git a/src/schematics/setup/index.ts b/src/schematics/setup/index.ts index 06e29f789..3d40ed132 100644 --- a/src/schematics/setup/index.ts +++ b/src/schematics/setup/index.ts @@ -18,6 +18,12 @@ import { } from '../utils'; import { appPrompt, featuresPrompt, projectPrompt, userPrompt } from './prompts'; +// FirebaseOptions keys — apps.sdkconfig responses include management-API extras that initializeApp() rejects. +const firebaseOptionsKeys = [ + 'apiKey', 'authDomain', 'databaseURL', 'projectId', 'storageBucket', + 'messagingSenderId', 'appId', 'measurementId', 'recaptchaSiteKey', +]; + export interface SetupConfig extends DeployOptions { firebaseProject: FirebaseProject, firebaseApp?: FirebaseApp, @@ -92,8 +98,11 @@ export const ngAddSetupProject = ( firebaseApp = await appPrompt(firebaseProject, undefined, { projectRoot }); const result = await firebaseTools.apps.sdkconfig('web', firebaseApp.appId, { nonInteractive: true, projectRoot }); - sdkConfig = result.sdkConfig; - delete sdkConfig.locationId; + sdkConfig = Object.fromEntries( + firebaseOptionsKeys + .filter(key => result.sdkConfig[key] !== undefined) + .map(key => [key, result.sdkConfig[key]]) + ); setupConfig.sdkConfig = sdkConfig; setupConfig.firebaseApp = firebaseApp; // set up data connect locally if data connect hasn't already been initialized. diff --git a/src/schematics/versions.json b/src/schematics/versions.json index 9477d49a4..176a5b8ea 100644 --- a/src/schematics/versions.json +++ b/src/schematics/versions.json @@ -2,7 +2,7 @@ "peerDependencies": { }, "firebaseFunctionsDependencies": { - "firebase-admin": { "dev": false, "version": "0.0.0" }, - "firebase-functions": { "dev": false, "version": "0.0.0" } + "firebase-admin": { "dev": false, "version": "FIREBASE_ADMIN_VERSION" }, + "firebase-functions": { "dev": false, "version": "FIREBASE_FUNCTIONS_VERSION" } } } diff --git a/tools/build.ts b/tools/build.ts index c947ad765..d4daf50a6 100644 --- a/tools/build.ts +++ b/tools/build.ts @@ -1,6 +1,7 @@ import { spawn } from 'cross-spawn'; +import { readFileSync, readdirSync } from 'fs'; import { copy, writeFile } from 'fs-extra'; -import { join } from 'path'; +import { join, sep } from 'path'; import { keys as tsKeys } from 'ts-transformer-keys'; import * as esbuild from "esbuild"; @@ -274,16 +275,104 @@ async function replacePackageCoreVersion() { }); } +/** The placeholder a package's version is written as in src/schematics/versions.json, e.g. + * firebase-admin becomes FIREBASE_ADMIN_VERSION. That file is hand-written, so a placeholder + * spelled any other way is not a compile error: it only fails the build in the checks below. */ +const versionPlaceholder = (name: string) => `${name.toUpperCase().replace(/[^A-Z0-9]+/g, '_')}_VERSION`; + +/** Every compiled schematic bundle, as a glob for replace-in-file. */ +const compiledSchematicsGlob = () => `${dest('schematics').split(sep).join('/')}/**/*.js`; + +/** Every compiled schematic bundle, as absolute paths. */ +const compiledSchematicFiles = (directory: string): string[] => + readdirSync(directory, { withFileTypes: true }).flatMap(entry => { + const entryPath = join(directory, entry.name); + if (entry.isDirectory()) { return compiledSchematicFiles(entryPath); } + return entryPath.endsWith('.js') ? [entryPath] : []; + }); + +/** The schematics ship as esbuild bundles, so `import { firebaseFunctionsDependencies } from + * '../versions.json'` is inlined at compile time and the copied versions.json is never read at + * runtime. Rewriting only that copy leaves the placeholder baked into the emitted JavaScript. */ +async function writeSchematicVersionsIntoBundles(versions: Record) { + // eslint-disable-next-line @typescript-eslint/no-require-imports + const replace = require('replace-in-file'); + for (const [name, version] of Object.entries(versions)) { + if (!version) { + throw new Error(`No version resolved for ${name}, so the generated Cloud Functions manifest would not install. Add it to the root package.json.`); + } + const placeholder = versionPlaceholder(name); + const replacements = await replace({ + // Glob patterns need forward slashes even on Windows. + files: compiledSchematicsGlob(), + // (?![A-Z0-9_]) stops FIREBASE_ADMIN_VERSION from also matching the start of a misspelled + // FIREBASE_ADMIN_VERSIONS, which would replace part of it and leave "^13.0.0S" behind. + from: new RegExp(`${placeholder}(?![A-Z0-9_])`, 'g'), + to: version, + // When the glob matches no files at all, return an empty result instead of rejecting, so the + // failure comes out of the check below with a message that says where we looked. + allowEmptyPaths: true, + }); + if (!replacements.some(replacement => replacement.hasChanged)) { + throw new Error(`No ${placeholder} placeholder found in the compiled schematics. Check that src/schematics/versions.json spells it exactly that way, and that the build is reading ${compiledSchematicsGlob()}.`); + } + } +} + +/** A compiled bundle that still contains one or more of our placeholders. */ +interface BundleWithPlaceholders { + file: string; + placeholders: string[]; +} + +/** Fails the build if any of our version placeholders is still present in a compiled bundle. + * Checks only the placeholders named in `packageNames`, because a blanket search for anything + * ending in _VERSION also hits SEMVER_SPEC_VERSION and TERM_PROGRAM_VERSION from bundled + * libraries, which are not ours and are supposed to be there. */ +function assertNoVersionPlaceholdersRemain(packageNames: string[]) { + const files = compiledSchematicFiles(dest('schematics')); + if (files.length === 0) { + throw new Error(`No compiled schematics found under ${dest('schematics')}, so no version placeholder could have been replaced.`); + } + const ourPlaceholders = packageNames.map(versionPlaceholder); + const bundlesWithPlaceholders: BundleWithPlaceholders[] = files + .map(file => { + const contents = readFileSync(file, 'utf8'); + return { file, placeholders: ourPlaceholders.filter(placeholder => contents.includes(placeholder)) }; + }) + .filter(bundle => bundle.placeholders.length > 0); + if (bundlesWithPlaceholders.length > 0) { + const offendingBundles = bundlesWithPlaceholders + .map(bundle => ` ${bundle.file}: ${bundle.placeholders.join(', ')}`) + .join('\n'); + throw new Error(`Unreplaced version placeholders in the compiled schematics:\n${offendingBundles}\nThe generated manifests would ship a placeholder instead of a version.`); + } +} + +/** Resolves the versions src/schematics/versions.json asks for from the root package.json, then + * writes them both into the copied versions.json and into the compiled bundles that inlined them. */ async function replaceSchematicVersions() { const root = await rootPackage; const packagesPath = dest('schematics', 'versions.json'); const dependencies = await import(packagesPath); - Object.keys(dependencies.peerDependencies).forEach(name => { - dependencies.peerDependencies[name].version = root.dependencies[name] || root.devDependencies[name]; - }); - Object.keys(dependencies.firebaseFunctionsDependencies).forEach(name => { - dependencies.firebaseFunctionsDependencies[name].version = root.dependencies[name] || root.devDependencies[name]; + // A missing version is dropped by JSON.stringify, so the package silently vanishes from the + // manifests the schematics generate. Fail the build instead. + const resolveVersion = (name: string): string => { + const version = root.dependencies[name] || root.devDependencies[name]; + if (!version) { + throw new Error(`No version resolved for ${name} in the root package.json, so the schematics would generate manifests without it.`); + } + return version; + }; + const resolved: Record = {}; + [dependencies.peerDependencies, dependencies.firebaseFunctionsDependencies].forEach(block => { + Object.keys(block).forEach(name => { + block[name].version = resolveVersion(name); + resolved[name] = block[name].version; + }); }); + await writeSchematicVersionsIntoBundles(resolved); + assertNoVersionPlaceholdersRemain(Object.keys(resolved)); return writeFile(packagesPath, JSON.stringify(dependencies, null, 2)); }