From b2d0235f823d4d428dd40041d4db8616216c3182 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 17 Oct 2025 18:29:49 +0000 Subject: [PATCH 1/7] feat(init): split hosting setup into askQuestions and actuate Refactors the hosting feature's `doSetup` function into `askQuestions` and `actuate` functions, following the pattern of other features like `firestore` and `database`. This change separates user interaction from the actuation of changes, improving modularity and maintainability. --- npm-shrinkwrap.json | 221 ++++++++++++----------------- package.json | 1 + src/init/features/hosting/index.ts | 151 +++++++++++--------- src/init/features/index.ts | 5 +- src/init/index.ts | 6 +- 5 files changed, 185 insertions(+), 199 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 6100aa1f889..9cf8bcd26e0 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -153,6 +153,7 @@ "firebase": "^9.16.0", "firebase-admin": "^11.5.0", "firebase-functions": "^4.3.1", + "globals": "^16.4.0", "google-discovery-to-swagger": "^2.1.0", "googleapis": "^105.0.0", "mocha": "^11.7.1", @@ -560,6 +561,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.0.tgz", "integrity": "sha512-reM4+U7B9ss148rh2n1Qs9ASS+w94irYXga7c2jaQv9RVzpS7Mv1a9rnYYwuDa45G+DkORt9g6An2k/V4d9LbQ==", "dev": true, + "peer": true, "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", @@ -914,6 +916,16 @@ } } }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/traverse/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -967,7 +979,8 @@ "version": "0.3.3", "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.3.3.tgz", "integrity": "sha512-JrvHOx9q0yvKEby0bK8qzGTVw6K+yEg8enxDWb2IwNKr5XZxRrBb+GNIqoAIP7yXyhRg5jcENWmdHmtnAT87vA==", - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/@electric-sql/pglite-tools": { "version": "0.2.8", @@ -1602,6 +1615,7 @@ "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.9.13.tgz", "integrity": "sha512-GfiI1JxJ7ecluEmDjPzseRXk/PX31hS7+tjgBopL7XjB2hLUdR+0FTMXy2Q3/hXezypDvU6or7gVFizDESrkXw==", "dev": true, + "peer": true, "dependencies": { "@firebase/component": "0.6.4", "@firebase/logger": "0.4.0", @@ -1671,6 +1685,7 @@ "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.13.tgz", "integrity": "sha512-j6ANZaWjeVy5zg6X7uiqh6lM6o3n3LD1+/SJFNs9V781xyryyZWXe+tmnWNWPkP086QfJoNkWN9pMQRqSG4vMg==", "dev": true, + "peer": true, "dependencies": { "@firebase/app": "0.9.13", "@firebase/component": "0.6.4", @@ -1689,7 +1704,8 @@ "version": "0.9.0", "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz", "integrity": "sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@firebase/app/node_modules/tslib": { "version": "2.6.2", @@ -2225,6 +2241,7 @@ "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.3.tgz", "integrity": "sha512-DY02CRhOZwpzO36fHpuVysz6JZrscPiBXD0fXp6qSrL9oNOx5KWICKdR95C0lSITzxp0TZosVyHqzatE8JbcjA==", "dev": true, + "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -4936,17 +4953,6 @@ "integrity": "sha512-AWzMJsDEsXj6dH+7rxe6RzLtNkW2tGqmJkjIaga76xeQORglf6VcMX5Xwv/jvZ/rfpXdFO0YAREWJEssAC6HWw==", "dev": true }, - "node_modules/@types/markdown-it": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.1.tgz", - "integrity": "sha512-4NpsnpYl2Gt1ljyBGrKMxFYAYvpqbnnkgP/i/g+NLpjEUa3obn1XJCur9YbEXKDAkaXqsR1LbDnGEJ0MmKFxfg==", - "dev": true, - "optional": true, - "dependencies": { - "@types/linkify-it": "^5", - "@types/mdurl": "^2" - } - }, "node_modules/@types/marked-terminal": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/@types/marked-terminal/-/marked-terminal-6.1.1.tgz", @@ -5070,6 +5076,7 @@ "version": "18.19.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.1.tgz", "integrity": "sha512-mZJ9V11gG5Vp0Ox2oERpeFDl+JvCwK24PGy76vVY/UgBtjwJWc5rYBThFxmbnYOm9UPZNm6wEl/sxHt2SU7x9A==", + "peer": true, "dependencies": { "undici-types": "~5.26.4" } @@ -5551,6 +5558,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.51.0.tgz", "integrity": "sha512-fEV0R9gGmfpDeRzJXn+fGQKcl0inIeYobmmUWijZh9zA7bxJ8clPhV9up2ZQzATxAiFAECqPQyMDB4o4B81AaA==", "dev": true, + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "5.51.0", "@typescript-eslint/types": "5.51.0", @@ -5845,6 +5853,7 @@ "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6395,6 +6404,7 @@ "resolved": "https://registry.npmjs.org/astro/-/astro-2.2.3.tgz", "integrity": "sha512-Pd67ZBoYxqeyHCZ0UpdmDZYNgcs7JTwc0NMzUScrH4y2hjSY4S8iwmNUtd9pf65gkxMpEbqfvQj06kLzgi4HZg==", "dev": true, + "peer": true, "dependencies": { "@astrojs/compiler": "^1.3.1", "@astrojs/language-server": "^0.28.3", @@ -7219,6 +7229,7 @@ "url": "https://tidelift.com/funding/github/npm/browserslist" } ], + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001400", "electron-to-chromium": "^1.4.251", @@ -7612,6 +7623,7 @@ "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", "dev": true, + "peer": true, "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", @@ -7706,6 +7718,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "peer": true, "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -8769,7 +8782,8 @@ "version": "0.0.1107588", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1107588.tgz", "integrity": "sha512-yIR+pG9x65Xko7bErCUSQaDLrO/P1p3JUzEk7JCU4DowPcGHkTGUGQapcfcLc4qj0UaALwZ+cr0riFgiqpixcg==", - "dev": true + "dev": true, + "peer": true }, "node_modules/dezalgo": { "version": "1.0.4", @@ -8929,27 +8943,6 @@ "node": ">= 0.8" } }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", @@ -9160,6 +9153,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -9227,6 +9221,7 @@ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -9930,6 +9925,7 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "license": "MIT", + "peer": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -11104,12 +11100,16 @@ } }, "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/globalyzer": { @@ -13713,7 +13713,6 @@ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dev": true, - "peer": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -13929,24 +13928,6 @@ "node": ">=8" } }, - "node_modules/markdown-it": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", - "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", - "dev": true, - "optional": true, - "dependencies": { - "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.0", - "mdurl": "^2.0.0", - "punycode.js": "^2.3.1", - "uc.micro": "^2.1.0" - }, - "bin": { - "markdown-it": "bin/markdown-it.mjs" - } - }, "node_modules/markdown-it-anchor": { "version": "8.6.7", "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", @@ -13972,6 +13953,7 @@ "version": "13.0.2", "resolved": "https://registry.npmjs.org/marked/-/marked-13.0.2.tgz", "integrity": "sha512-J6CPjP8pS5sgrRqxVRvkCIkZ6MFdRIjDkwUwgJ9nL2fbmM6qGQeB2C16hi8Cc9BOzj6xXzy0jyi0iPIfnMHYzA==", + "peer": true, "bin": { "marked": "bin/marked.js" }, @@ -17007,6 +16989,7 @@ "version": "8.12.0", "resolved": "https://registry.npmjs.org/pg/-/pg-8.12.0.tgz", "integrity": "sha512-A+LHUSnwnxrnL/tZ+OLfqR1SxLN3c/pgDztZ47Rpbsd4jUytsTtwQo/TLPRzPJMp/1pbhYVhH9cuSZLAajNfjQ==", + "peer": true, "dependencies": { "pg-connection-string": "^2.6.4", "pg-pool": "^3.6.2", @@ -17348,6 +17331,7 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", "dev": true, + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -18686,7 +18670,6 @@ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", "dev": true, - "peer": true, "dependencies": { "loose-envify": "^1.1.0" } @@ -20522,6 +20505,7 @@ "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -21234,6 +21218,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-4.2.1.tgz", "integrity": "sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==", "dev": true, + "peer": true, "dependencies": { "esbuild": "^0.17.5", "postcss": "^8.4.21", @@ -21925,6 +21910,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz", "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -22240,6 +22226,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.0.tgz", "integrity": "sha512-reM4+U7B9ss148rh2n1Qs9ASS+w94irYXga7c2jaQv9RVzpS7Mv1a9rnYYwuDa45G+DkORt9g6An2k/V4d9LbQ==", "dev": true, + "peer": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", @@ -22501,6 +22488,12 @@ "ms": "2.1.2" } }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -22548,7 +22541,8 @@ "@electric-sql/pglite": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.3.3.tgz", - "integrity": "sha512-JrvHOx9q0yvKEby0bK8qzGTVw6K+yEg8enxDWb2IwNKr5XZxRrBb+GNIqoAIP7yXyhRg5jcENWmdHmtnAT87vA==" + "integrity": "sha512-JrvHOx9q0yvKEby0bK8qzGTVw6K+yEg8enxDWb2IwNKr5XZxRrBb+GNIqoAIP7yXyhRg5jcENWmdHmtnAT87vA==", + "peer": true }, "@electric-sql/pglite-tools": { "version": "0.2.8", @@ -22922,6 +22916,7 @@ "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.9.13.tgz", "integrity": "sha512-GfiI1JxJ7ecluEmDjPzseRXk/PX31hS7+tjgBopL7XjB2hLUdR+0FTMXy2Q3/hXezypDvU6or7gVFizDESrkXw==", "dev": true, + "peer": true, "requires": { "@firebase/component": "0.6.4", "@firebase/logger": "0.4.0", @@ -22997,6 +22992,7 @@ "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.13.tgz", "integrity": "sha512-j6ANZaWjeVy5zg6X7uiqh6lM6o3n3LD1+/SJFNs9V781xyryyZWXe+tmnWNWPkP086QfJoNkWN9pMQRqSG4vMg==", "dev": true, + "peer": true, "requires": { "@firebase/app": "0.9.13", "@firebase/component": "0.6.4", @@ -23017,7 +23013,8 @@ "version": "0.9.0", "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz", "integrity": "sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==", - "dev": true + "dev": true, + "peer": true }, "@firebase/auth": { "version": "0.23.2", @@ -23525,6 +23522,7 @@ "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.3.tgz", "integrity": "sha512-DY02CRhOZwpzO36fHpuVysz6JZrscPiBXD0fXp6qSrL9oNOx5KWICKdR95C0lSITzxp0TZosVyHqzatE8JbcjA==", "dev": true, + "peer": true, "requires": { "tslib": "^2.1.0" }, @@ -25466,8 +25464,7 @@ "dev": true }, "@types/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "version": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", "dev": true, "optional": true @@ -25489,17 +25486,6 @@ "integrity": "sha512-AWzMJsDEsXj6dH+7rxe6RzLtNkW2tGqmJkjIaga76xeQORglf6VcMX5Xwv/jvZ/rfpXdFO0YAREWJEssAC6HWw==", "dev": true }, - "@types/markdown-it": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.1.tgz", - "integrity": "sha512-4NpsnpYl2Gt1ljyBGrKMxFYAYvpqbnnkgP/i/g+NLpjEUa3obn1XJCur9YbEXKDAkaXqsR1LbDnGEJ0MmKFxfg==", - "dev": true, - "optional": true, - "requires": { - "@types/linkify-it": "^5", - "@types/mdurl": "^2" - } - }, "@types/marked-terminal": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/@types/marked-terminal/-/marked-terminal-6.1.1.tgz", @@ -25536,8 +25522,7 @@ } }, "@types/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "version": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", "dev": true, "optional": true @@ -25613,6 +25598,7 @@ "version": "18.19.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.1.tgz", "integrity": "sha512-mZJ9V11gG5Vp0Ox2oERpeFDl+JvCwK24PGy76vVY/UgBtjwJWc5rYBThFxmbnYOm9UPZNm6wEl/sxHt2SU7x9A==", + "peer": true, "requires": { "undici-types": "~5.26.4" } @@ -26040,6 +26026,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.51.0.tgz", "integrity": "sha512-fEV0R9gGmfpDeRzJXn+fGQKcl0inIeYobmmUWijZh9zA7bxJ8clPhV9up2ZQzATxAiFAECqPQyMDB4o4B81AaA==", "dev": true, + "peer": true, "requires": { "@typescript-eslint/scope-manager": "5.51.0", "@typescript-eslint/types": "5.51.0", @@ -26240,7 +26227,8 @@ "acorn": { "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==" + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "peer": true }, "acorn-jsx": { "version": "5.3.2", @@ -26625,6 +26613,7 @@ "resolved": "https://registry.npmjs.org/astro/-/astro-2.2.3.tgz", "integrity": "sha512-Pd67ZBoYxqeyHCZ0UpdmDZYNgcs7JTwc0NMzUScrH4y2hjSY4S8iwmNUtd9pf65gkxMpEbqfvQj06kLzgi4HZg==", "dev": true, + "peer": true, "requires": { "@astrojs/compiler": "^1.3.1", "@astrojs/language-server": "^0.28.3", @@ -27208,6 +27197,7 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, + "peer": true, "requires": { "caniuse-lite": "^1.0.30001400", "electron-to-chromium": "^1.4.251", @@ -27476,6 +27466,7 @@ "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", "dev": true, + "peer": true, "requires": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", @@ -27543,6 +27534,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "peer": true, "requires": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -28319,7 +28311,8 @@ "version": "0.0.1107588", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1107588.tgz", "integrity": "sha512-yIR+pG9x65Xko7bErCUSQaDLrO/P1p3JUzEk7JCU4DowPcGHkTGUGQapcfcLc4qj0UaALwZ+cr0riFgiqpixcg==", - "dev": true + "dev": true, + "peer": true }, "dezalgo": { "version": "1.0.4", @@ -28460,26 +28453,6 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, - "encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "optional": true, - "requires": { - "iconv-lite": "^0.6.2" - }, - "dependencies": { - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - } - } - }, "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", @@ -28496,8 +28469,7 @@ "optional": true }, "entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "version": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true, "optional": true @@ -28638,6 +28610,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, + "peer": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -28875,6 +28848,7 @@ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, + "peer": true, "requires": {} }, "eslint-plugin-brikke": { @@ -29169,6 +29143,7 @@ "version": "4.21.2", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "peer": true, "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -30025,9 +30000,9 @@ } }, "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", "dev": true }, "globalyzer": { @@ -31856,8 +31831,7 @@ "dev": true }, "linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "version": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", "dev": true, "optional": true, @@ -32052,7 +32026,6 @@ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dev": true, - "peer": true, "requires": { "js-tokens": "^3.0.0 || ^4.0.0" } @@ -32229,21 +32202,6 @@ "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==", "dev": true }, - "markdown-it": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", - "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", - "dev": true, - "optional": true, - "requires": { - "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.0", - "mdurl": "^2.0.0", - "punycode.js": "^2.3.1", - "uc.micro": "^2.1.0" - } - }, "markdown-it-anchor": { "version": "8.6.7", "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", @@ -32261,7 +32219,8 @@ "marked": { "version": "13.0.2", "resolved": "https://registry.npmjs.org/marked/-/marked-13.0.2.tgz", - "integrity": "sha512-J6CPjP8pS5sgrRqxVRvkCIkZ6MFdRIjDkwUwgJ9nL2fbmM6qGQeB2C16hi8Cc9BOzj6xXzy0jyi0iPIfnMHYzA==" + "integrity": "sha512-J6CPjP8pS5sgrRqxVRvkCIkZ6MFdRIjDkwUwgJ9nL2fbmM6qGQeB2C16hi8Cc9BOzj6xXzy0jyi0iPIfnMHYzA==", + "peer": true }, "marked-terminal": { "version": "7.1.0", @@ -32461,8 +32420,7 @@ } }, "mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "version": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", "dev": true, "optional": true @@ -34403,6 +34361,7 @@ "version": "8.12.0", "resolved": "https://registry.npmjs.org/pg/-/pg-8.12.0.tgz", "integrity": "sha512-A+LHUSnwnxrnL/tZ+OLfqR1SxLN3c/pgDztZ47Rpbsd4jUytsTtwQo/TLPRzPJMp/1pbhYVhH9cuSZLAajNfjQ==", + "peer": true, "requires": { "pg-cloudflare": "^1.1.1", "pg-connection-string": "^2.6.4", @@ -34640,7 +34599,8 @@ "version": "3.2.5", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", - "dev": true + "dev": true, + "peer": true }, "prettier-linter-helpers": { "version": "1.0.0", @@ -34952,8 +34912,7 @@ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" }, "punycode.js": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "version": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", "dev": true, "optional": true @@ -35620,7 +35579,6 @@ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", "dev": true, - "peer": true, "requires": { "loose-envify": "^1.1.0" } @@ -37029,7 +36987,8 @@ "typescript": { "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "peer": true }, "typescript-json-schema": { "version": "0.65.1", @@ -37541,6 +37500,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-4.2.1.tgz", "integrity": "sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==", "dev": true, + "peer": true, "requires": { "esbuild": "^0.17.5", "fsevents": "~2.3.2", @@ -38025,7 +37985,8 @@ "zod": { "version": "3.24.3", "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz", - "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==" + "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==", + "peer": true }, "zod-to-json-schema": { "version": "3.24.5", diff --git a/package.json b/package.json index fdb630e361b..51aaab663e8 100644 --- a/package.json +++ b/package.json @@ -243,6 +243,7 @@ "firebase": "^9.16.0", "firebase-admin": "^11.5.0", "firebase-functions": "^4.3.1", + "globals": "^16.4.0", "google-discovery-to-swagger": "^2.1.0", "googleapis": "^105.0.0", "mocha": "^11.7.1", diff --git a/src/init/features/hosting/index.ts b/src/init/features/hosting/index.ts index b3fc0568e05..8545cb1bee0 100644 --- a/src/init/features/hosting/index.ts +++ b/src/init/features/hosting/index.ts @@ -14,17 +14,17 @@ import { Options } from "../../../options"; import { last, logSuccess } from "../../../utils"; import { interactiveCreateHostingSite } from "../../../hosting/interactive"; import { readTemplateSync } from "../../../templates"; +import { FirebaseError } from "../../../error"; const INDEX_TEMPLATE = readTemplateSync("init/hosting/index.html"); const MISSING_TEMPLATE = readTemplateSync("init/hosting/404.html"); const DEFAULT_IGNORES = ["firebase.json", "**/.*", "**/node_modules/**"]; -/** - * Does the setup steps for Firebase Hosting. - * WARNING: #6527 - `options` may not have all the things you think it does. - */ -export async function doSetup(setup: any, config: any, options: Options): Promise { - setup.hosting = {}; +// TODO: come up with a better way to type this +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export async function askQuestions(setup: any, config: any, options: Options): Promise { + setup.featureInfo = setup.featureInfo || {}; + setup.featureInfo.hosting = {}; // There's a path where we can set up Hosting without a project, so if // if setup.projectId is empty, we don't do any checking for a Hosting site. @@ -40,22 +40,10 @@ export async function doSetup(setup: any, config: any, options: Options): Promis } if (!hasHostingSite) { - // N.B. During prompt migration this did not pass options object, so there is no support - // for force or nonInteractive; there possibly should be. - const confirmCreate = await confirm({ + setup.featureInfo.hosting.createSite = await confirm({ message: "A Firebase Hosting site is required to deploy. Would you like to create one now?", default: true, }); - if (confirmCreate) { - const createOptions = { - projectId: setup.projectId, - nonInteractive: options.nonInteractive, - }; - const newSite = await interactiveCreateHostingSite("", "", createOptions); - logger.info(); - logSuccess(`Firebase Hosting site ${last(newSite.name.split("/"))} created!`); - logger.info(); - } } } @@ -66,40 +54,40 @@ export async function doSetup(setup: any, config: any, options: Options): Promis if (experiments.isEnabled("webframeworks")) { if (discoveredFramework) { const name = WebFrameworks[discoveredFramework.framework].name; - setup.hosting.useDiscoveredFramework ??= await confirm({ + setup.featureInfo.hosting.useDiscoveredFramework ??= await confirm({ message: `Detected an existing ${name} codebase in the current directory, should we use this?`, default: true, }); } - if (setup.hosting.useDiscoveredFramework) { - setup.hosting.source = "."; - setup.hosting.useWebFrameworks = true; + if (setup.featureInfo.hosting.useDiscoveredFramework) { + setup.featureInfo.hosting.source = "."; + setup.featureInfo.hosting.useWebFrameworks = true; } else { - setup.hosting.useWebFrameworks = await confirm( - `Do you want to use a web framework? (${clc.bold("experimental")})`, + setup.featureInfo.hosting.useWebFrameworks = await confirm( + `Do you want to use a web framework? (${clc.bold("experimental")})` ); } } - if (setup.hosting.useWebFrameworks) { - setup.hosting.source ??= await input({ + if (setup.featureInfo.hosting.useWebFrameworks) { + setup.featureInfo.hosting.source ??= await input({ message: "What folder would you like to use for your web application's root directory?", default: "hosting", }); - if (setup.hosting.source !== ".") delete setup.hosting.useDiscoveredFramework; - discoveredFramework = await discover(join(config.projectDir, setup.hosting.source)); + if (setup.featureInfo.hosting.source !== ".") delete setup.featureInfo.hosting.useDiscoveredFramework; + discoveredFramework = await discover(join(config.projectDir, setup.featureInfo.hosting.source)); if (discoveredFramework) { const name = WebFrameworks[discoveredFramework.framework].name; - setup.hosting.useDiscoveredFramework ??= await confirm({ - message: `Detected an existing ${name} codebase in ${setup.hosting.source}, should we use this?`, + setup.featureInfo.hosting.useDiscoveredFramework ??= await confirm({ + message: `Detected an existing ${name} codebase in ${setup.featureInfo.hosting.source}, should we use this?`, default: true, }); } - if (setup.hosting.useDiscoveredFramework && discoveredFramework) { - setup.hosting.webFramework = discoveredFramework.framework; + if (setup.featureInfo.hosting.useDiscoveredFramework && discoveredFramework) { + setup.featureInfo.hosting.webFramework = discoveredFramework.framework; } else { const choices: { name: string; value: string }[] = []; for (const value in WebFrameworks) { @@ -110,84 +98,113 @@ export async function doSetup(setup: any, config: any, options: Options): Promis } const defaultChoice = choices.find( - ({ value }) => value === discoveredFramework?.framework, + ({ value }) => value === discoveredFramework?.framework )?.value; - setup.hosting.whichFramework = - setup.hosting.whichFramework || + setup.featureInfo.hosting.whichFramework = + setup.featureInfo.hosting.whichFramework || (await select({ message: "Please choose the framework:", default: defaultChoice, choices, })); - - if (discoveredFramework) rmSync(setup.hosting.source, { recursive: true }); - await WebFrameworks[setup.hosting.whichFramework].init!(setup, config); } - setup.hosting.region = - setup.hosting.region || + setup.featureInfo.hosting.region = + setup.featureInfo.hosting.region || (await select({ message: "In which region would you like to host server-side content, if applicable?", default: DEFAULT_REGION, choices: ALLOWED_SSR_REGIONS.filter((region) => region.recommended), })); - - setup.config.hosting = { - source: setup.hosting.source, - // TODO swap out for framework ignores - ignore: DEFAULT_IGNORES, - frameworksBackend: { - region: setup.hosting.region, - }, - }; } else { logger.info(); logger.info( - `Your ${clc.bold("public")} directory is the folder (relative to your project directory) that`, + `Your ${clc.bold("public")} directory is the folder (relative to your project directory) that` ); logger.info( - `will contain Hosting assets to be uploaded with ${clc.bold("firebase deploy")}. If you`, + `will contain Hosting assets to be uploaded with ${clc.bold("firebase deploy")}. If you` ); logger.info("have a build process for your assets, use your build's output directory."); logger.info(); - setup.hosting.public = - setup.hosting.public || + setup.featureInfo.hosting.public = + setup.featureInfo.hosting.public || (await input({ message: "What do you want to use as your public directory?", default: "public", })); - setup.hosting.spa = - setup.hosting.spa || + setup.featureInfo.hosting.spa = + setup.featureInfo.hosting.spa || (await confirm("Configure as a single-page app (rewrite all urls to /index.html)?")); + } - setup.config.hosting = { - public: setup.hosting.public, - ignore: DEFAULT_IGNORES, + setup.featureInfo.hosting.github = + setup.featureInfo.hosting.github || + (await confirm("Set up automatic builds and deploys with GitHub?")); +} + +// TODO: come up with a better way to type this +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export async function actuate(setup: any, config: any, options: Options): Promise { + const hostingInfo = setup.featureInfo?.hosting; + if (!hostingInfo) { + throw new FirebaseError( + "Could not find hosting info into in setup.featureInfo.hosting. This should not happen.", + { exit: 2 } + ); + } + + if (hostingInfo.createSite) { + const createOptions = { + projectId: setup.projectId, + nonInteractive: options.nonInteractive, }; + const newSite = await interactiveCreateHostingSite("", "", createOptions); + logger.info(); + logSuccess(`Firebase Hosting site ${last(newSite.name.split("/"))} created!`); + logger.info(); } - setup.hosting.github = - setup.hosting.github || (await confirm("Set up automatic builds and deploys with GitHub?")); + const discoveredFramework = experiments.isEnabled("webframeworks") + ? await discover(config.projectDir, false) + : undefined; + + if (hostingInfo.useWebFrameworks) { + if (!hostingInfo.useDiscoveredFramework) { + if (discoveredFramework) rmSync(hostingInfo.source, { recursive: true }); + await WebFrameworks[hostingInfo.whichFramework].init!(setup, config); + } + setup.config.hosting = { + source: hostingInfo.source, + // TODO swap out for framework ignores + ignore: DEFAULT_IGNORES, + frameworksBackend: { + region: hostingInfo.region, + }, + }; + } else { + setup.config.hosting = { + public: hostingInfo.public, + ignore: DEFAULT_IGNORES, + }; - if (!setup.hosting.useWebFrameworks) { - if (setup.hosting.spa) { + if (hostingInfo.spa) { setup.config.hosting.rewrites = [{ source: "**", destination: "/index.html" }]; } else { // SPA doesn't need a 404 page since everything is index.html - await config.askWriteProjectFile(`${setup.hosting.public}/404.html`, MISSING_TEMPLATE); + await config.askWriteProjectFile(`${hostingInfo.public}/404.html`, MISSING_TEMPLATE); } const c = new Client({ urlPrefix: "https://www.gstatic.com", auth: false }); const response = await c.get<{ current: { version: string } }>("/firebasejs/releases.json"); await config.askWriteProjectFile( - `${setup.hosting.public}/index.html`, - INDEX_TEMPLATE.replace(/{{VERSION}}/g, response.body.current.version), + `${hostingInfo.public}/index.html`, + INDEX_TEMPLATE.replace(/{{VERSION}}/g, response.body.current.version) ); } - if (setup.hosting.github) { + if (hostingInfo.github) { return initGitHub(setup); } } diff --git a/src/init/features/index.ts b/src/init/features/index.ts index f5b5d34de50..ac7bdceb780 100644 --- a/src/init/features/index.ts +++ b/src/init/features/index.ts @@ -10,7 +10,10 @@ export { actuate as firestoreActuate, } from "./firestore"; export { doSetup as functions } from "./functions"; -export { doSetup as hosting } from "./hosting"; +export { + askQuestions as hostingAskQuestions, + actuate as hostingActuate, +} from "./hosting"; export { askQuestions as storageAskQuestions, RequiredInfo as StorageInfo, diff --git a/src/init/index.ts b/src/init/index.ts index a3067f9946b..5d5f4883ab4 100644 --- a/src/init/index.ts +++ b/src/init/index.ts @@ -79,7 +79,11 @@ const featuresList: Feature[] = [ actuate: features.dataconnectSdkActuate, }, { name: "functions", doSetup: features.functions }, - { name: "hosting", doSetup: features.hosting }, + { + name: "hosting", + askQuestions: features.hostingAskQuestions, + actuate: features.hostingActuate, + }, { name: "storage", askQuestions: features.storageAskQuestions, From 774b4e0476f9e8ef0137704fa8a45641c5955adb Mon Sep 17 00:00:00 2001 From: Joe Hanley Date: Mon, 20 Oct 2025 11:48:43 -0700 Subject: [PATCH 2/7] revert shrinkwrap --- npm-shrinkwrap.json | 221 ++++++++++++++++++++++++++------------------ 1 file changed, 130 insertions(+), 91 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 9cf8bcd26e0..6100aa1f889 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -153,7 +153,6 @@ "firebase": "^9.16.0", "firebase-admin": "^11.5.0", "firebase-functions": "^4.3.1", - "globals": "^16.4.0", "google-discovery-to-swagger": "^2.1.0", "googleapis": "^105.0.0", "mocha": "^11.7.1", @@ -561,7 +560,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.0.tgz", "integrity": "sha512-reM4+U7B9ss148rh2n1Qs9ASS+w94irYXga7c2jaQv9RVzpS7Mv1a9rnYYwuDa45G+DkORt9g6An2k/V4d9LbQ==", "dev": true, - "peer": true, "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", @@ -916,16 +914,6 @@ } } }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/traverse/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -979,8 +967,7 @@ "version": "0.3.3", "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.3.3.tgz", "integrity": "sha512-JrvHOx9q0yvKEby0bK8qzGTVw6K+yEg8enxDWb2IwNKr5XZxRrBb+GNIqoAIP7yXyhRg5jcENWmdHmtnAT87vA==", - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/@electric-sql/pglite-tools": { "version": "0.2.8", @@ -1615,7 +1602,6 @@ "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.9.13.tgz", "integrity": "sha512-GfiI1JxJ7ecluEmDjPzseRXk/PX31hS7+tjgBopL7XjB2hLUdR+0FTMXy2Q3/hXezypDvU6or7gVFizDESrkXw==", "dev": true, - "peer": true, "dependencies": { "@firebase/component": "0.6.4", "@firebase/logger": "0.4.0", @@ -1685,7 +1671,6 @@ "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.13.tgz", "integrity": "sha512-j6ANZaWjeVy5zg6X7uiqh6lM6o3n3LD1+/SJFNs9V781xyryyZWXe+tmnWNWPkP086QfJoNkWN9pMQRqSG4vMg==", "dev": true, - "peer": true, "dependencies": { "@firebase/app": "0.9.13", "@firebase/component": "0.6.4", @@ -1704,8 +1689,7 @@ "version": "0.9.0", "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz", "integrity": "sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@firebase/app/node_modules/tslib": { "version": "2.6.2", @@ -2241,7 +2225,6 @@ "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.3.tgz", "integrity": "sha512-DY02CRhOZwpzO36fHpuVysz6JZrscPiBXD0fXp6qSrL9oNOx5KWICKdR95C0lSITzxp0TZosVyHqzatE8JbcjA==", "dev": true, - "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -4953,6 +4936,17 @@ "integrity": "sha512-AWzMJsDEsXj6dH+7rxe6RzLtNkW2tGqmJkjIaga76xeQORglf6VcMX5Xwv/jvZ/rfpXdFO0YAREWJEssAC6HWw==", "dev": true }, + "node_modules/@types/markdown-it": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.1.tgz", + "integrity": "sha512-4NpsnpYl2Gt1ljyBGrKMxFYAYvpqbnnkgP/i/g+NLpjEUa3obn1XJCur9YbEXKDAkaXqsR1LbDnGEJ0MmKFxfg==", + "dev": true, + "optional": true, + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, "node_modules/@types/marked-terminal": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/@types/marked-terminal/-/marked-terminal-6.1.1.tgz", @@ -5076,7 +5070,6 @@ "version": "18.19.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.1.tgz", "integrity": "sha512-mZJ9V11gG5Vp0Ox2oERpeFDl+JvCwK24PGy76vVY/UgBtjwJWc5rYBThFxmbnYOm9UPZNm6wEl/sxHt2SU7x9A==", - "peer": true, "dependencies": { "undici-types": "~5.26.4" } @@ -5558,7 +5551,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.51.0.tgz", "integrity": "sha512-fEV0R9gGmfpDeRzJXn+fGQKcl0inIeYobmmUWijZh9zA7bxJ8clPhV9up2ZQzATxAiFAECqPQyMDB4o4B81AaA==", "dev": true, - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "5.51.0", "@typescript-eslint/types": "5.51.0", @@ -5853,7 +5845,6 @@ "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6404,7 +6395,6 @@ "resolved": "https://registry.npmjs.org/astro/-/astro-2.2.3.tgz", "integrity": "sha512-Pd67ZBoYxqeyHCZ0UpdmDZYNgcs7JTwc0NMzUScrH4y2hjSY4S8iwmNUtd9pf65gkxMpEbqfvQj06kLzgi4HZg==", "dev": true, - "peer": true, "dependencies": { "@astrojs/compiler": "^1.3.1", "@astrojs/language-server": "^0.28.3", @@ -7229,7 +7219,6 @@ "url": "https://tidelift.com/funding/github/npm/browserslist" } ], - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001400", "electron-to-chromium": "^1.4.251", @@ -7623,7 +7612,6 @@ "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", "dev": true, - "peer": true, "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", @@ -7718,7 +7706,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "peer": true, "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -8782,8 +8769,7 @@ "version": "0.0.1107588", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1107588.tgz", "integrity": "sha512-yIR+pG9x65Xko7bErCUSQaDLrO/P1p3JUzEk7JCU4DowPcGHkTGUGQapcfcLc4qj0UaALwZ+cr0riFgiqpixcg==", - "dev": true, - "peer": true + "dev": true }, "node_modules/dezalgo": { "version": "1.0.4", @@ -8943,6 +8929,27 @@ "node": ">= 0.8" } }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", @@ -9153,7 +9160,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -9221,7 +9227,6 @@ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, - "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -9925,7 +9930,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -11100,16 +11104,12 @@ } }, "node_modules/globals": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", - "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, - "license": "MIT", "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, "node_modules/globalyzer": { @@ -13713,6 +13713,7 @@ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dev": true, + "peer": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -13928,6 +13929,24 @@ "node": ">=8" } }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, + "optional": true, + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, "node_modules/markdown-it-anchor": { "version": "8.6.7", "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", @@ -13953,7 +13972,6 @@ "version": "13.0.2", "resolved": "https://registry.npmjs.org/marked/-/marked-13.0.2.tgz", "integrity": "sha512-J6CPjP8pS5sgrRqxVRvkCIkZ6MFdRIjDkwUwgJ9nL2fbmM6qGQeB2C16hi8Cc9BOzj6xXzy0jyi0iPIfnMHYzA==", - "peer": true, "bin": { "marked": "bin/marked.js" }, @@ -16989,7 +17007,6 @@ "version": "8.12.0", "resolved": "https://registry.npmjs.org/pg/-/pg-8.12.0.tgz", "integrity": "sha512-A+LHUSnwnxrnL/tZ+OLfqR1SxLN3c/pgDztZ47Rpbsd4jUytsTtwQo/TLPRzPJMp/1pbhYVhH9cuSZLAajNfjQ==", - "peer": true, "dependencies": { "pg-connection-string": "^2.6.4", "pg-pool": "^3.6.2", @@ -17331,7 +17348,6 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", "dev": true, - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -18670,6 +18686,7 @@ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", "dev": true, + "peer": true, "dependencies": { "loose-envify": "^1.1.0" } @@ -20505,7 +20522,6 @@ "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -21218,7 +21234,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-4.2.1.tgz", "integrity": "sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==", "dev": true, - "peer": true, "dependencies": { "esbuild": "^0.17.5", "postcss": "^8.4.21", @@ -21910,7 +21925,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz", "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -22226,7 +22240,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.0.tgz", "integrity": "sha512-reM4+U7B9ss148rh2n1Qs9ASS+w94irYXga7c2jaQv9RVzpS7Mv1a9rnYYwuDa45G+DkORt9g6An2k/V4d9LbQ==", "dev": true, - "peer": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", @@ -22488,12 +22501,6 @@ "ms": "2.1.2" } }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -22541,8 +22548,7 @@ "@electric-sql/pglite": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.3.3.tgz", - "integrity": "sha512-JrvHOx9q0yvKEby0bK8qzGTVw6K+yEg8enxDWb2IwNKr5XZxRrBb+GNIqoAIP7yXyhRg5jcENWmdHmtnAT87vA==", - "peer": true + "integrity": "sha512-JrvHOx9q0yvKEby0bK8qzGTVw6K+yEg8enxDWb2IwNKr5XZxRrBb+GNIqoAIP7yXyhRg5jcENWmdHmtnAT87vA==" }, "@electric-sql/pglite-tools": { "version": "0.2.8", @@ -22916,7 +22922,6 @@ "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.9.13.tgz", "integrity": "sha512-GfiI1JxJ7ecluEmDjPzseRXk/PX31hS7+tjgBopL7XjB2hLUdR+0FTMXy2Q3/hXezypDvU6or7gVFizDESrkXw==", "dev": true, - "peer": true, "requires": { "@firebase/component": "0.6.4", "@firebase/logger": "0.4.0", @@ -22992,7 +22997,6 @@ "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.13.tgz", "integrity": "sha512-j6ANZaWjeVy5zg6X7uiqh6lM6o3n3LD1+/SJFNs9V781xyryyZWXe+tmnWNWPkP086QfJoNkWN9pMQRqSG4vMg==", "dev": true, - "peer": true, "requires": { "@firebase/app": "0.9.13", "@firebase/component": "0.6.4", @@ -23013,8 +23017,7 @@ "version": "0.9.0", "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz", "integrity": "sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==", - "dev": true, - "peer": true + "dev": true }, "@firebase/auth": { "version": "0.23.2", @@ -23522,7 +23525,6 @@ "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.3.tgz", "integrity": "sha512-DY02CRhOZwpzO36fHpuVysz6JZrscPiBXD0fXp6qSrL9oNOx5KWICKdR95C0lSITzxp0TZosVyHqzatE8JbcjA==", "dev": true, - "peer": true, "requires": { "tslib": "^2.1.0" }, @@ -25464,7 +25466,8 @@ "dev": true }, "@types/linkify-it": { - "version": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", "dev": true, "optional": true @@ -25486,6 +25489,17 @@ "integrity": "sha512-AWzMJsDEsXj6dH+7rxe6RzLtNkW2tGqmJkjIaga76xeQORglf6VcMX5Xwv/jvZ/rfpXdFO0YAREWJEssAC6HWw==", "dev": true }, + "@types/markdown-it": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.1.tgz", + "integrity": "sha512-4NpsnpYl2Gt1ljyBGrKMxFYAYvpqbnnkgP/i/g+NLpjEUa3obn1XJCur9YbEXKDAkaXqsR1LbDnGEJ0MmKFxfg==", + "dev": true, + "optional": true, + "requires": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, "@types/marked-terminal": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/@types/marked-terminal/-/marked-terminal-6.1.1.tgz", @@ -25522,7 +25536,8 @@ } }, "@types/mdurl": { - "version": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", "dev": true, "optional": true @@ -25598,7 +25613,6 @@ "version": "18.19.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.1.tgz", "integrity": "sha512-mZJ9V11gG5Vp0Ox2oERpeFDl+JvCwK24PGy76vVY/UgBtjwJWc5rYBThFxmbnYOm9UPZNm6wEl/sxHt2SU7x9A==", - "peer": true, "requires": { "undici-types": "~5.26.4" } @@ -26026,7 +26040,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.51.0.tgz", "integrity": "sha512-fEV0R9gGmfpDeRzJXn+fGQKcl0inIeYobmmUWijZh9zA7bxJ8clPhV9up2ZQzATxAiFAECqPQyMDB4o4B81AaA==", "dev": true, - "peer": true, "requires": { "@typescript-eslint/scope-manager": "5.51.0", "@typescript-eslint/types": "5.51.0", @@ -26227,8 +26240,7 @@ "acorn": { "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "peer": true + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==" }, "acorn-jsx": { "version": "5.3.2", @@ -26613,7 +26625,6 @@ "resolved": "https://registry.npmjs.org/astro/-/astro-2.2.3.tgz", "integrity": "sha512-Pd67ZBoYxqeyHCZ0UpdmDZYNgcs7JTwc0NMzUScrH4y2hjSY4S8iwmNUtd9pf65gkxMpEbqfvQj06kLzgi4HZg==", "dev": true, - "peer": true, "requires": { "@astrojs/compiler": "^1.3.1", "@astrojs/language-server": "^0.28.3", @@ -27197,7 +27208,6 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, - "peer": true, "requires": { "caniuse-lite": "^1.0.30001400", "electron-to-chromium": "^1.4.251", @@ -27466,7 +27476,6 @@ "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", "dev": true, - "peer": true, "requires": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", @@ -27534,7 +27543,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "peer": true, "requires": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -28311,8 +28319,7 @@ "version": "0.0.1107588", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1107588.tgz", "integrity": "sha512-yIR+pG9x65Xko7bErCUSQaDLrO/P1p3JUzEk7JCU4DowPcGHkTGUGQapcfcLc4qj0UaALwZ+cr0riFgiqpixcg==", - "dev": true, - "peer": true + "dev": true }, "dezalgo": { "version": "1.0.4", @@ -28453,6 +28460,26 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "requires": { + "iconv-lite": "^0.6.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", @@ -28469,7 +28496,8 @@ "optional": true }, "entities": { - "version": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true, "optional": true @@ -28610,7 +28638,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, - "peer": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -28848,7 +28875,6 @@ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, - "peer": true, "requires": {} }, "eslint-plugin-brikke": { @@ -29143,7 +29169,6 @@ "version": "4.21.2", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "peer": true, "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -30000,9 +30025,9 @@ } }, "globals": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", - "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, "globalyzer": { @@ -31831,7 +31856,8 @@ "dev": true }, "linkify-it": { - "version": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", "dev": true, "optional": true, @@ -32026,6 +32052,7 @@ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dev": true, + "peer": true, "requires": { "js-tokens": "^3.0.0 || ^4.0.0" } @@ -32202,6 +32229,21 @@ "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==", "dev": true }, + "markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, + "optional": true, + "requires": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + } + }, "markdown-it-anchor": { "version": "8.6.7", "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", @@ -32219,8 +32261,7 @@ "marked": { "version": "13.0.2", "resolved": "https://registry.npmjs.org/marked/-/marked-13.0.2.tgz", - "integrity": "sha512-J6CPjP8pS5sgrRqxVRvkCIkZ6MFdRIjDkwUwgJ9nL2fbmM6qGQeB2C16hi8Cc9BOzj6xXzy0jyi0iPIfnMHYzA==", - "peer": true + "integrity": "sha512-J6CPjP8pS5sgrRqxVRvkCIkZ6MFdRIjDkwUwgJ9nL2fbmM6qGQeB2C16hi8Cc9BOzj6xXzy0jyi0iPIfnMHYzA==" }, "marked-terminal": { "version": "7.1.0", @@ -32420,7 +32461,8 @@ } }, "mdurl": { - "version": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", "dev": true, "optional": true @@ -34361,7 +34403,6 @@ "version": "8.12.0", "resolved": "https://registry.npmjs.org/pg/-/pg-8.12.0.tgz", "integrity": "sha512-A+LHUSnwnxrnL/tZ+OLfqR1SxLN3c/pgDztZ47Rpbsd4jUytsTtwQo/TLPRzPJMp/1pbhYVhH9cuSZLAajNfjQ==", - "peer": true, "requires": { "pg-cloudflare": "^1.1.1", "pg-connection-string": "^2.6.4", @@ -34599,8 +34640,7 @@ "version": "3.2.5", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", - "dev": true, - "peer": true + "dev": true }, "prettier-linter-helpers": { "version": "1.0.0", @@ -34912,7 +34952,8 @@ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" }, "punycode.js": { - "version": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", "dev": true, "optional": true @@ -35579,6 +35620,7 @@ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", "dev": true, + "peer": true, "requires": { "loose-envify": "^1.1.0" } @@ -36987,8 +37029,7 @@ "typescript": { "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "peer": true + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" }, "typescript-json-schema": { "version": "0.65.1", @@ -37500,7 +37541,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-4.2.1.tgz", "integrity": "sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==", "dev": true, - "peer": true, "requires": { "esbuild": "^0.17.5", "fsevents": "~2.3.2", @@ -37985,8 +38025,7 @@ "zod": { "version": "3.24.3", "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz", - "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==", - "peer": true + "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==" }, "zod-to-json-schema": { "version": "3.24.5", From 8d734a0561902274678a0e0a5d10065c5642e06a Mon Sep 17 00:00:00 2001 From: Joe Hanley Date: Mon, 20 Oct 2025 12:14:10 -0700 Subject: [PATCH 3/7] revert shrinkwrap --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 51aaab663e8..fdb630e361b 100644 --- a/package.json +++ b/package.json @@ -243,7 +243,6 @@ "firebase": "^9.16.0", "firebase-admin": "^11.5.0", "firebase-functions": "^4.3.1", - "globals": "^16.4.0", "google-discovery-to-swagger": "^2.1.0", "googleapis": "^105.0.0", "mocha": "^11.7.1", From 8afcd5936c384d373945e2b76d1dc15b01afab74 Mon Sep 17 00:00:00 2001 From: Joe Hanley Date: Tue, 21 Oct 2025 12:04:43 -0700 Subject: [PATCH 4/7] Substanial cleanup and simplification --- src/commands/deploy.ts | 21 +-- src/commands/hosting-sites-create.ts | 14 +- src/frameworks/angular/index.ts | 2 +- src/frameworks/flutter/index.ts | 2 +- src/frameworks/next/index.ts | 2 +- src/frameworks/nuxt/index.ts | 2 +- src/frameworks/vite/index.ts | 7 +- src/hosting/interactive.ts | 36 +++-- src/init/features/hosting/index.ts | 193 ++++++++++++++------------- src/init/features/index.ts | 1 + src/init/index.ts | 1 + 11 files changed, 148 insertions(+), 133 deletions(-) diff --git a/src/commands/deploy.ts b/src/commands/deploy.ts index 5b3e036138b..53fe96092e9 100644 --- a/src/commands/deploy.ts +++ b/src/commands/deploy.ts @@ -10,8 +10,10 @@ import { requireHostingSite } from "../requireHostingSite"; import { errNoDefaultSite } from "../getDefaultHostingSite"; import { FirebaseError } from "../error"; import { bold } from "colorette"; -import { interactiveCreateHostingSite } from "../hosting/interactive"; +import { pickHostingSiteName } from "../hosting/interactive"; import { logBullet } from "../utils"; +import { createSite } from "../hosting/api"; +import { Options } from "../options"; // in order of least time-consuming to most time-consuming export const VALID_DEPLOY_TARGETS = [ @@ -98,26 +100,26 @@ export const command = new Command("deploy") "In order to provide better validation, this may still enable APIs on the target project", ) .before(requireConfig) - .before((options) => { + .before((options: Options) => { options.filteredTargets = filterTargets(options, VALID_DEPLOY_TARGETS); const permissions = options.filteredTargets.reduce((perms: string[], target: string) => { return perms.concat(TARGET_PERMISSIONS[target]); }, []); return requirePermissions(options, permissions); }) - .before((options) => { + .before((options: Options) => { if (options.filteredTargets.includes("functions")) { - return checkServiceAccountIam(options.project); + return checkServiceAccountIam(options.project!); } }) - .before(async (options) => { + .before(async (options: Options) => { // only fetch the default instance for hosting or database deploys if (options.filteredTargets.includes("database")) { await requireDatabaseInstance(options); } if (options.filteredTargets.includes("hosting")) { - let createSite = false; + let shouldCreateSite = false; try { await requireHostingSite(options); } catch (err: unknown) { @@ -128,10 +130,10 @@ export const command = new Command("deploy") if (isPermissionError) { throw err; } else if (err === errNoDefaultSite) { - createSite = true; + shouldCreateSite = true; } } - if (!createSite) { + if (!shouldCreateSite) { return; } if (options.nonInteractive) { @@ -142,7 +144,8 @@ export const command = new Command("deploy") ); } logBullet("No Hosting site detected."); - await interactiveCreateHostingSite("", "", options); + const siteId = await pickHostingSiteName("", options); + await createSite(options.project!, siteId); } }) .before(checkValidTargetFilters) diff --git a/src/commands/hosting-sites-create.ts b/src/commands/hosting-sites-create.ts index e2e5bfc9f8f..2def56e8ed3 100644 --- a/src/commands/hosting-sites-create.ts +++ b/src/commands/hosting-sites-create.ts @@ -1,13 +1,13 @@ import { bold } from "colorette"; import { Command } from "../command"; -import { interactiveCreateHostingSite } from "../hosting/interactive"; -import { last, logLabeledSuccess } from "../utils"; +import { pickHostingSiteName } from "../hosting/interactive"; +import { logLabeledSuccess } from "../utils"; import { logger } from "../logger"; import { needProjectId } from "../projectUtils"; import { Options } from "../options"; import { requirePermissions } from "../requirePermissions"; -import { Site } from "../hosting/api"; +import { createSite, Site } from "../hosting/api"; import { FirebaseError } from "../error"; const LOG_TAG = "hosting:sites"; @@ -16,16 +16,16 @@ export const command = new Command("hosting:sites:create [siteId]") .description("create a Firebase Hosting site") .option("--app ", "specify an existing Firebase Web App ID") .before(requirePermissions, ["firebasehosting.sites.update"]) - .action(async (siteId: string, options: Options & { app: string }): Promise => { + .action(async (siteId: string | undefined, options: Options & { app: string }): Promise => { const projectId = needProjectId(options); const appId = options.app; if (options.nonInteractive && !siteId) { - throw new FirebaseError(`${bold(siteId)} is required in a non-interactive environment`); + throw new FirebaseError(`${bold("siteId")} is required in a non-interactive environment`); } - const site = await interactiveCreateHostingSite(siteId, appId, options); - siteId = last(site.name.split("/")); + siteId = await pickHostingSiteName(siteId ?? "", options); + const site = await createSite(projectId, siteId, appId); logger.info(); logLabeledSuccess( diff --git a/src/frameworks/angular/index.ts b/src/frameworks/angular/index.ts index b2528a8e3ee..9921119f7d0 100644 --- a/src/frameworks/angular/index.ts +++ b/src/frameworks/angular/index.ts @@ -47,7 +47,7 @@ export async function discover(dir: string): Promise { export function init(setup: any, config: any) { execSync( - `npx --yes -p @angular/cli@"${supportedRange}" ng new ${setup.projectId} --directory ${setup.hosting.source} --skip-git`, + `npx --yes -p @angular/cli@"${supportedRange}" ng new ${setup.projectId} --directory ${setup.featureInfo.hosting.source} --skip-git`, { stdio: "inherit", cwd: config.projectDir, diff --git a/src/frameworks/flutter/index.ts b/src/frameworks/flutter/index.ts index 3ab873842e2..2f011eb780a 100644 --- a/src/frameworks/flutter/index.ts +++ b/src/frameworks/flutter/index.ts @@ -36,7 +36,7 @@ export function init(setup: any, config: any) { `--project-name=${projectName}`, "--overwrite", "--platforms=web", - setup.hosting.source, + setup.featureInfo.hosting.source, ], { stdio: "inherit", cwd: config.projectDir }, ); diff --git a/src/frameworks/next/index.ts b/src/frameworks/next/index.ts index b1becc11ded..b381fbba157 100644 --- a/src/frameworks/next/index.ts +++ b/src/frameworks/next/index.ts @@ -387,7 +387,7 @@ export async function init(setup: any, config: any) { }); execSync( `npx --yes create-next-app@"${supportedRange}" -e hello-world ` + - `${setup.hosting.source} --use-npm --${language}`, + `${setup.featureInfo.hosting.source} --use-npm --${language}`, { stdio: "inherit", cwd: config.projectDir }, ); } diff --git a/src/frameworks/nuxt/index.ts b/src/frameworks/nuxt/index.ts index b478b365701..887de68354b 100644 --- a/src/frameworks/nuxt/index.ts +++ b/src/frameworks/nuxt/index.ts @@ -116,7 +116,7 @@ export async function getConfig(cwd: string): Promise { * Utility method used during project initialization. */ export function init(setup: any, config: any) { - execSync(`npx --yes nuxi@"${supportedRange}" init ${setup.hosting.source}`, { + execSync(`npx --yes nuxi@"${supportedRange}" init ${setup.featureInfo.hosting.source}`, { stdio: "inherit", cwd: config.projectDir, }); diff --git a/src/frameworks/vite/index.ts b/src/frameworks/vite/index.ts index f99d2ae968a..509b1a9744f 100644 --- a/src/frameworks/vite/index.ts +++ b/src/frameworks/vite/index.ts @@ -34,13 +34,16 @@ export async function init(setup: any, config: any, baseTemplate: string = "vani ], }); execSync( - `npm create vite@"${supportedRange}" ${setup.hosting.source} --yes -- --template ${template}`, + `npm create vite@"${supportedRange}" ${setup.featureInfo.hosting.source} --yes -- --template ${template}`, { stdio: "inherit", cwd: config.projectDir, }, ); - execSync(`npm install`, { stdio: "inherit", cwd: join(config.projectDir, setup.hosting.source) }); + execSync(`npm install`, { + stdio: "inherit", + cwd: join(config.projectDir, setup.featureInfo.hosting.source), + }); } export const viteDiscoverWithNpmDependency = (dep: string) => async (dir: string) => diff --git a/src/hosting/interactive.ts b/src/hosting/interactive.ts index a141088fa23..eb2b675abba 100644 --- a/src/hosting/interactive.ts +++ b/src/hosting/interactive.ts @@ -1,7 +1,7 @@ import { FirebaseError } from "../error"; import { logWarning } from "../utils"; import { needProjectId, needProjectNumber } from "../projectUtils"; -import { Site, createSite } from "./api"; +import { createSite } from "./api"; import { input } from "../prompt"; const nameSuggestion = new RegExp("try something like `(.+)`"); @@ -11,17 +11,16 @@ const prompt = 'We recommend using letters, numbers, and hyphens (e.g. "{project-id}-{random-hash}"):'; /** - * Interactively prompt to create a Hosting site. + * Interactively prompt to name a Hosting site. */ -export async function interactiveCreateHostingSite( +export async function pickHostingSiteName( siteId: string, - appId: string, options: { projectId?: string; nonInteractive?: boolean }, -): Promise { +): Promise { const projectId = needProjectId(options); const projectNumber = await needProjectNumber(options); let id = siteId; - let newSite: Site | undefined; + let nameConfirmed: boolean = false; let suggestion: string | undefined; // If we were given an ID, we're going to start with that, so don't check the project ID. @@ -35,7 +34,7 @@ export async function interactiveCreateHostingSite( } } - while (!newSite) { + while (!nameConfirmed) { if (!id || suggestion) { id = await input({ message: prompt, @@ -43,26 +42,18 @@ export async function interactiveCreateHostingSite( default: suggestion, }); } - try { - newSite = await createSite(projectNumber, id, appId); - } catch (err: unknown) { - if (!(err instanceof FirebaseError)) { - throw err; - } - if (options.nonInteractive) { - throw err; - } - - id = ""; // Clear so the prompt comes back. - suggestion = getSuggestionFromError(err); - } + const attempt = await trySiteID(projectNumber, id); + nameConfirmed = attempt.available; + suggestion = attempt.suggestion; + if (!nameConfirmed) id = ""; // Clear so the prompt comes back. } - return newSite; + return id; } async function trySiteID( projectNumber: string, id: string, + nonInteractive = false, ): Promise<{ available: boolean; suggestion?: string }> { try { await createSite(projectNumber, id, "", true); @@ -71,6 +62,9 @@ async function trySiteID( if (!(err instanceof FirebaseError)) { throw err; } + if (nonInteractive) { + throw err; + } const suggestion = getSuggestionFromError(err); return { available: false, suggestion }; } diff --git a/src/init/features/hosting/index.ts b/src/init/features/hosting/index.ts index 8545cb1bee0..611dc1c04e5 100644 --- a/src/init/features/hosting/index.ts +++ b/src/init/features/hosting/index.ts @@ -1,5 +1,5 @@ import * as clc from "colorette"; -import { rmSync } from "node:fs"; +import { existsSync, rmSync } from "node:fs"; import { join } from "path"; import { Client } from "../../../apiv2"; @@ -11,18 +11,32 @@ import { ALLOWED_SSR_REGIONS, DEFAULT_REGION } from "../../../frameworks/constan import * as experiments from "../../../experiments"; import { errNoDefaultSite, getDefaultHostingSite } from "../../../getDefaultHostingSite"; import { Options } from "../../../options"; -import { last, logSuccess } from "../../../utils"; -import { interactiveCreateHostingSite } from "../../../hosting/interactive"; +import { logSuccess } from "../../../utils"; +import { pickHostingSiteName } from "../../../hosting/interactive"; import { readTemplateSync } from "../../../templates"; import { FirebaseError } from "../../../error"; +import { Setup } from "../.."; +import { Config } from "../../../config"; +import { createSite } from "../../../hosting/api"; const INDEX_TEMPLATE = readTemplateSync("init/hosting/index.html"); const MISSING_TEMPLATE = readTemplateSync("init/hosting/404.html"); const DEFAULT_IGNORES = ["firebase.json", "**/.*", "**/node_modules/**"]; +export interface RequiredInfo { + newSiteId?: string; + source?: string; + useWebFrameworks?: boolean; + useDiscoveredFramework?: boolean; + webFramework?: string; + region?: string; + public?: string; + spa?: boolean; +} + // TODO: come up with a better way to type this // eslint-disable-next-line @typescript-eslint/no-explicit-any -export async function askQuestions(setup: any, config: any, options: Options): Promise { +export async function askQuestions(setup: Setup, config: Config, options: Options): Promise { setup.featureInfo = setup.featureInfo || {}; setup.featureInfo.hosting = {}; @@ -39,56 +53,65 @@ export async function askQuestions(setup: any, config: any, options: Options): P hasHostingSite = false; } - if (!hasHostingSite) { - setup.featureInfo.hosting.createSite = await confirm({ + if ( + !hasHostingSite && + (await confirm({ message: "A Firebase Hosting site is required to deploy. Would you like to create one now?", default: true, - }); + })) + ) { + const createOptions = { + projectId: setup.projectId, + nonInteractive: options.nonInteractive, + }; + setup.featureInfo.hosting.newSiteId = await pickHostingSiteName("", createOptions); } } - let discoveredFramework = experiments.isEnabled("webframeworks") - ? await discover(config.projectDir, false) - : undefined; - if (experiments.isEnabled("webframeworks")) { - if (discoveredFramework) { - const name = WebFrameworks[discoveredFramework.framework].name; - setup.featureInfo.hosting.useDiscoveredFramework ??= await confirm({ - message: `Detected an existing ${name} codebase in the current directory, should we use this?`, + let discoveredFramework = experiments.isEnabled("webframeworks") + ? await discover(config.projectDir, false) + : undefined; + // First, if we're in a framework directory, ask to use that. + if ( + discoveredFramework && + (await confirm({ + message: `Detected an existing ${WebFrameworks[discoveredFramework.framework].name} codebase in the current directory, do you want to use this?`, default: true, - }); - } - if (setup.featureInfo.hosting.useDiscoveredFramework) { + })) + ) { setup.featureInfo.hosting.source = "."; setup.featureInfo.hosting.useWebFrameworks = true; + setup.featureInfo.hosting.useDiscoveredFramework = true; + setup.featureInfo.hosting.webFramework = discoveredFramework.framework; + // Otherwise, just ask if they want to use web frameworks. } else { setup.featureInfo.hosting.useWebFrameworks = await confirm( - `Do you want to use a web framework? (${clc.bold("experimental")})` + `Do you want to use a web framework? (${clc.bold("experimental")})`, ); } - } - - if (setup.featureInfo.hosting.useWebFrameworks) { - setup.featureInfo.hosting.source ??= await input({ - message: "What folder would you like to use for your web application's root directory?", - default: "hosting", - }); + // If they say yes, ask for source directory if its not already known + if (setup.featureInfo.hosting.useWebFrameworks) { + setup.featureInfo.hosting.source ??= await input({ + message: "What folder would you like to use for your web application's root directory?", + default: "hosting", + }); - if (setup.featureInfo.hosting.source !== ".") delete setup.featureInfo.hosting.useDiscoveredFramework; - discoveredFramework = await discover(join(config.projectDir, setup.featureInfo.hosting.source)); + discoveredFramework = await discover( + join(config.projectDir, setup.featureInfo.hosting.source), + ); - if (discoveredFramework) { - const name = WebFrameworks[discoveredFramework.framework].name; - setup.featureInfo.hosting.useDiscoveredFramework ??= await confirm({ - message: `Detected an existing ${name} codebase in ${setup.featureInfo.hosting.source}, should we use this?`, - default: true, - }); - } + if (discoveredFramework) { + const name = WebFrameworks[discoveredFramework.framework].name; + setup.featureInfo.hosting.useDiscoveredFramework ??= await confirm({ + message: `Detected an existing ${name} codebase in ${setup.featureInfo.hosting.source}, should we use this?`, + default: true, + }); + if (setup.featureInfo.hosting.useDiscoveredFramework) + setup.featureInfo.hosting.webFramework = discoveredFramework.framework; + } - if (setup.featureInfo.hosting.useDiscoveredFramework && discoveredFramework) { - setup.featureInfo.hosting.webFramework = discoveredFramework.framework; - } else { + // If it is not known already, ask what framework to use. const choices: { name: string; value: string }[] = []; for (const value in WebFrameworks) { if (WebFrameworks[value]) { @@ -98,82 +121,71 @@ export async function askQuestions(setup: any, config: any, options: Options): P } const defaultChoice = choices.find( - ({ value }) => value === discoveredFramework?.framework + ({ value }) => value === discoveredFramework?.framework, )?.value; - setup.featureInfo.hosting.whichFramework = - setup.featureInfo.hosting.whichFramework || + setup.featureInfo.hosting.webFramework ??= await select({ + message: "Please choose the framework:", + default: defaultChoice, + choices, + }); + + setup.featureInfo.hosting.region = + setup.featureInfo.hosting.region || (await select({ - message: "Please choose the framework:", - default: defaultChoice, - choices, + message: "In which region would you like to host server-side content, if applicable?", + default: DEFAULT_REGION, + choices: ALLOWED_SSR_REGIONS.filter((region) => region.recommended), })); } - - setup.featureInfo.hosting.region = - setup.featureInfo.hosting.region || - (await select({ - message: "In which region would you like to host server-side content, if applicable?", - default: DEFAULT_REGION, - choices: ALLOWED_SSR_REGIONS.filter((region) => region.recommended), - })); } else { logger.info(); logger.info( - `Your ${clc.bold("public")} directory is the folder (relative to your project directory) that` + `Your ${clc.bold("public")} directory is the folder (relative to your project directory) that`, ); logger.info( - `will contain Hosting assets to be uploaded with ${clc.bold("firebase deploy")}. If you` + `will contain Hosting assets to be uploaded with ${clc.bold("firebase deploy")}. If you`, ); logger.info("have a build process for your assets, use your build's output directory."); logger.info(); - setup.featureInfo.hosting.public = - setup.featureInfo.hosting.public || - (await input({ - message: "What do you want to use as your public directory?", - default: "public", - })); - setup.featureInfo.hosting.spa = - setup.featureInfo.hosting.spa || - (await confirm("Configure as a single-page app (rewrite all urls to /index.html)?")); + setup.featureInfo.hosting.public ??= await input({ + message: "What do you want to use as your public directory?", + default: "public", + }); + setup.featureInfo.hosting.spa ??= await confirm( + "Configure as a single-page app (rewrite all urls to /index.html)?", + ); + } + // GitHub Action set up is still structured as doSetup + if (await confirm("Set up automatic builds and deploys with GitHub?")) { + return initGitHub(setup); } - - setup.featureInfo.hosting.github = - setup.featureInfo.hosting.github || - (await confirm("Set up automatic builds and deploys with GitHub?")); } // TODO: come up with a better way to type this // eslint-disable-next-line @typescript-eslint/no-explicit-any -export async function actuate(setup: any, config: any, options: Options): Promise { +export async function actuate(setup: Setup, config: Config, options: Options): Promise { const hostingInfo = setup.featureInfo?.hosting; if (!hostingInfo) { throw new FirebaseError( - "Could not find hosting info into in setup.featureInfo.hosting. This should not happen.", - { exit: 2 } + "Could not find hosting info in setup.featureInfo.hosting. This should not happen.", + { exit: 2 }, ); } - if (hostingInfo.createSite) { - const createOptions = { - projectId: setup.projectId, - nonInteractive: options.nonInteractive, - }; - const newSite = await interactiveCreateHostingSite("", "", createOptions); + if (hostingInfo.newSiteId && setup.projectId) { + await createSite(setup.projectId, hostingInfo.newSiteId); logger.info(); - logSuccess(`Firebase Hosting site ${last(newSite.name.split("/"))} created!`); + logSuccess(`Firebase Hosting site ${hostingInfo.newSiteId} created!`); logger.info(); } - const discoveredFramework = experiments.isEnabled("webframeworks") - ? await discover(config.projectDir, false) - : undefined; - - if (hostingInfo.useWebFrameworks) { + if (hostingInfo.webFramework) { if (!hostingInfo.useDiscoveredFramework) { - if (discoveredFramework) rmSync(hostingInfo.source, { recursive: true }); - await WebFrameworks[hostingInfo.whichFramework].init!(setup, config); + if (hostingInfo.source && existsSync(hostingInfo.source)) + rmSync(hostingInfo.source, { recursive: true }); + await WebFrameworks[hostingInfo.webFramework].init!(setup, config); } setup.config.hosting = { source: hostingInfo.source, @@ -193,18 +205,19 @@ export async function actuate(setup: any, config: any, options: Options): Promis setup.config.hosting.rewrites = [{ source: "**", destination: "/index.html" }]; } else { // SPA doesn't need a 404 page since everything is index.html - await config.askWriteProjectFile(`${hostingInfo.public}/404.html`, MISSING_TEMPLATE); + await config.askWriteProjectFile( + `${hostingInfo.public}/404.html`, + MISSING_TEMPLATE, + !!options.force, + ); } const c = new Client({ urlPrefix: "https://www.gstatic.com", auth: false }); const response = await c.get<{ current: { version: string } }>("/firebasejs/releases.json"); await config.askWriteProjectFile( `${hostingInfo.public}/index.html`, - INDEX_TEMPLATE.replace(/{{VERSION}}/g, response.body.current.version) + INDEX_TEMPLATE.replace(/{{VERSION}}/g, response.body.current.version), + !!options.force, ); } - - if (hostingInfo.github) { - return initGitHub(setup); - } } diff --git a/src/init/features/index.ts b/src/init/features/index.ts index ac7bdceb780..b7bf711730f 100644 --- a/src/init/features/index.ts +++ b/src/init/features/index.ts @@ -13,6 +13,7 @@ export { doSetup as functions } from "./functions"; export { askQuestions as hostingAskQuestions, actuate as hostingActuate, + RequiredInfo as HostingInfo, } from "./hosting"; export { askQuestions as storageAskQuestions, diff --git a/src/init/index.ts b/src/init/index.ts index 5d5f4883ab4..25f858b2e8d 100644 --- a/src/init/index.ts +++ b/src/init/index.ts @@ -39,6 +39,7 @@ export interface SetupInfo { storage?: features.StorageInfo; apptesting?: features.ApptestingInfo; ailogic?: features.AiLogicInfo; + hosting?: features.HostingInfo; } interface Feature { From bedcacd6212b0c74944bb2b4c09baed62f711cbe Mon Sep 17 00:00:00 2001 From: Joe Hanley Date: Tue, 21 Oct 2025 12:08:40 -0700 Subject: [PATCH 5/7] Apply suggestion from @gemini-code-assist[bot] Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- src/hosting/interactive.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hosting/interactive.ts b/src/hosting/interactive.ts index eb2b675abba..50c507b6182 100644 --- a/src/hosting/interactive.ts +++ b/src/hosting/interactive.ts @@ -42,7 +42,7 @@ export async function pickHostingSiteName( default: suggestion, }); } - const attempt = await trySiteID(projectNumber, id); + const attempt = await trySiteID(projectNumber, id, options.nonInteractive); nameConfirmed = attempt.available; suggestion = attempt.suggestion; if (!nameConfirmed) id = ""; // Clear so the prompt comes back. From 41f24bbd44fb2d8c01019c882ce2decad4a33bff Mon Sep 17 00:00:00 2001 From: Joe Hanley Date: Tue, 21 Oct 2025 12:12:00 -0700 Subject: [PATCH 6/7] Apply suggestion from @gemini-code-assist[bot] Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- src/init/features/hosting/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/init/features/hosting/index.ts b/src/init/features/hosting/index.ts index 611dc1c04e5..5db896fd96e 100644 --- a/src/init/features/hosting/index.ts +++ b/src/init/features/hosting/index.ts @@ -183,8 +183,9 @@ export async function actuate(setup: Setup, config: Config, options: Options): P if (hostingInfo.webFramework) { if (!hostingInfo.useDiscoveredFramework) { - if (hostingInfo.source && existsSync(hostingInfo.source)) + if (hostingInfo.source && existsSync(hostingInfo.source)) { rmSync(hostingInfo.source, { recursive: true }); + } await WebFrameworks[hostingInfo.webFramework].init!(setup, config); } setup.config.hosting = { From 79d9d97200bb95dfd0f96296a745007869a7cc7c Mon Sep 17 00:00:00 2001 From: Joe Hanley Date: Tue, 21 Oct 2025 14:24:42 -0700 Subject: [PATCH 7/7] fix test --- src/frameworks/flutter/index.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frameworks/flutter/index.spec.ts b/src/frameworks/flutter/index.spec.ts index 5214866315c..0af6107dd8c 100644 --- a/src/frameworks/flutter/index.spec.ts +++ b/src/frameworks/flutter/index.spec.ts @@ -139,7 +139,7 @@ describe("Flutter", () => { const stub = sandbox.stub(crossSpawn, "sync").returns(process as any); - const result = init({ projectId, hosting: { source } }, { projectDir }); + const result = init({ projectId, featureInfo: { hosting: { source } } }, { projectDir }); expect(await result).to.eql(undefined); sinon.assert.calledWith(