No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

config.js 8.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.hostingConfig = exports.normalize = exports.resolveTargets = exports.validate = exports.extract = exports.filterExcept = exports.filterOnly = void 0;
  4. const colorette_1 = require("colorette");
  5. const utils_1 = require("../utils");
  6. const error_1 = require("../error");
  7. const functional_1 = require("../functional");
  8. const fsutils_1 = require("../fsutils");
  9. const projectPath_1 = require("../projectPath");
  10. const path = require("node:path");
  11. const experiments = require("../experiments");
  12. const logger_1 = require("../logger");
  13. function matchingConfigs(configs, targets, assertMatches) {
  14. const matches = [];
  15. const [hasSite, hasTarget] = (0, functional_1.partition)(configs, (c) => "site" in c);
  16. for (const target of targets) {
  17. const siteMatch = hasSite.find((c) => c.site === target);
  18. const targetMatch = hasTarget.find((c) => c.target === target);
  19. if (siteMatch) {
  20. matches.push(siteMatch);
  21. }
  22. else if (targetMatch) {
  23. matches.push(targetMatch);
  24. }
  25. else if (assertMatches) {
  26. throw new error_1.FirebaseError(`Hosting site or target ${(0, colorette_1.bold)(target)} not detected in firebase.json`);
  27. }
  28. }
  29. return matches;
  30. }
  31. function filterOnly(configs, onlyString) {
  32. if (!onlyString) {
  33. return configs;
  34. }
  35. let onlyTargets = onlyString.split(",");
  36. if (onlyTargets.includes("hosting")) {
  37. return configs;
  38. }
  39. onlyTargets = onlyTargets
  40. .filter((target) => target.startsWith("hosting:"))
  41. .map((target) => target.replace("hosting:", ""));
  42. return matchingConfigs(configs, onlyTargets, true);
  43. }
  44. exports.filterOnly = filterOnly;
  45. function filterExcept(configs, exceptOption) {
  46. if (!exceptOption) {
  47. return configs;
  48. }
  49. const exceptTargets = exceptOption.split(",");
  50. if (exceptTargets.includes("hosting")) {
  51. return [];
  52. }
  53. const exceptValues = exceptTargets
  54. .filter((t) => t.startsWith("hosting:"))
  55. .map((t) => t.replace("hosting:", ""));
  56. const toReject = matchingConfigs(configs, exceptValues, false);
  57. return configs.filter((c) => !toReject.find((r) => c.site === r.site && c.target === r.target));
  58. }
  59. exports.filterExcept = filterExcept;
  60. function extract(options) {
  61. const config = options.config.src;
  62. if (!config.hosting) {
  63. return [];
  64. }
  65. const assertOneTarget = (config) => {
  66. if (config.target && config.site) {
  67. throw new error_1.FirebaseError(`Hosting configs should only include either "site" or "target", not both.`);
  68. }
  69. };
  70. if (!Array.isArray(config.hosting)) {
  71. const res = (0, utils_1.cloneDeep)(config.hosting);
  72. if (!res.target && !res.site) {
  73. res.site = options.site;
  74. }
  75. assertOneTarget(res);
  76. return [res];
  77. }
  78. else {
  79. config.hosting.forEach(assertOneTarget);
  80. return (0, utils_1.cloneDeep)(config.hosting);
  81. }
  82. }
  83. exports.extract = extract;
  84. function validate(configs, options) {
  85. for (const config of configs) {
  86. validateOne(config, options);
  87. }
  88. }
  89. exports.validate = validate;
  90. function validateOne(config, options) {
  91. var _a, _b, _c, _d;
  92. const hasAnyStaticRewrites = !!((_a = config.rewrites) === null || _a === void 0 ? void 0 : _a.find((rw) => "destination" in rw));
  93. const hasAnyDynamicRewrites = !!((_b = config.rewrites) === null || _b === void 0 ? void 0 : _b.find((rw) => !("destination" in rw)));
  94. const hasAnyRedirects = !!((_c = config.redirects) === null || _c === void 0 ? void 0 : _c.length);
  95. if (config.source && config.public) {
  96. throw new error_1.FirebaseError('Can only specify "source" or "public" in a Hosting config, not both');
  97. }
  98. const root = experiments.isEnabled("webframeworks")
  99. ? config.source || config.public
  100. : config.public;
  101. const orSource = experiments.isEnabled("webframeworks") ? ' or "source"' : "";
  102. if (!root && hasAnyStaticRewrites) {
  103. throw new error_1.FirebaseError(`Must supply a "public"${orSource} directory when using "destination" rewrites.`);
  104. }
  105. if (!root && !hasAnyDynamicRewrites && !hasAnyRedirects) {
  106. throw new error_1.FirebaseError(`Must supply a "public"${orSource} directory or at least one rewrite or redirect in each "hosting" config.`);
  107. }
  108. if (root && !(0, fsutils_1.dirExistsSync)((0, projectPath_1.resolveProjectPath)(options, root))) {
  109. logger_1.logger.debug(`Specified "${config.source ? "source" : "public"}" directory "${root}" does not exist; Deploy to Hosting site "${config.site || config.target || ""}" may fail or be empty.`);
  110. }
  111. const regionWithoutFunction = (rewrite) => typeof rewrite.region === "string" && typeof rewrite.function !== "string";
  112. const violation = (_d = config.rewrites) === null || _d === void 0 ? void 0 : _d.find(regionWithoutFunction);
  113. if (violation) {
  114. throw new error_1.FirebaseError("Rewrites only support 'region' as a top-level field when 'function' is set as a string");
  115. }
  116. if (config.i18n) {
  117. if (!root) {
  118. throw new error_1.FirebaseError(`Must supply a "public"${orSource} directory when using "i18n" configuration.`);
  119. }
  120. if (!config.i18n.root) {
  121. throw new error_1.FirebaseError('Must supply a "root" in "i18n" config.');
  122. }
  123. const i18nPath = path.join(root, config.i18n.root);
  124. if (!(0, fsutils_1.dirExistsSync)((0, projectPath_1.resolveProjectPath)(options, i18nPath))) {
  125. (0, utils_1.logLabeledWarning)("hosting", `Couldn't find specified i18n root directory ${(0, colorette_1.bold)(config.i18n.root)} in public directory ${(0, colorette_1.bold)(root)}`);
  126. }
  127. }
  128. }
  129. function resolveTargets(configs, options) {
  130. return configs.map((config) => {
  131. const newConfig = (0, utils_1.cloneDeep)(config);
  132. if (config.site) {
  133. return newConfig;
  134. }
  135. if (!config.target) {
  136. throw new error_1.FirebaseError("Assertion failed: resolving hosting target of a site with no site name " +
  137. "or target name. This should have caused an error earlier", { exit: 2 });
  138. }
  139. if (!options.project) {
  140. throw new error_1.FirebaseError("Assertion failed: options.project is not set. Commands depending on hosting.config should use requireProject", { exit: 2 });
  141. }
  142. const matchingTargets = options.rc.requireTarget(options.project, "hosting", config.target);
  143. if (matchingTargets.length > 1) {
  144. throw new error_1.FirebaseError(`Hosting target ${(0, colorette_1.bold)(config.target)} is linked to multiple sites, ` +
  145. `but only one is permitted. ` +
  146. `To clear, run:\n\n ${(0, colorette_1.bold)(`firebase target:clear hosting ${config.target}`)}`);
  147. }
  148. newConfig.site = matchingTargets[0];
  149. return newConfig;
  150. });
  151. }
  152. exports.resolveTargets = resolveTargets;
  153. function isLegacyFunctionsRewrite(rewrite) {
  154. return "function" in rewrite && typeof rewrite.function === "string";
  155. }
  156. function normalize(configs) {
  157. var _a;
  158. for (const config of configs) {
  159. config.rewrites = (_a = config.rewrites) === null || _a === void 0 ? void 0 : _a.map((rewrite) => {
  160. if (!("function" in rewrite)) {
  161. return rewrite;
  162. }
  163. if (isLegacyFunctionsRewrite(rewrite)) {
  164. const modern = Object.assign(Object.assign({}, rewrite), { function: {
  165. functionId: rewrite.function,
  166. } });
  167. delete modern.region;
  168. if ("region" in rewrite && typeof rewrite.region === "string") {
  169. modern.function.region = rewrite.region;
  170. }
  171. if (rewrite.region) {
  172. modern.function.region = rewrite.region;
  173. }
  174. return modern;
  175. }
  176. return rewrite;
  177. });
  178. }
  179. }
  180. exports.normalize = normalize;
  181. function hostingConfig(options) {
  182. if (!options.normalizedHostingConfig) {
  183. let configs = extract(options);
  184. configs = filterOnly(configs, options.only);
  185. configs = filterExcept(configs, options.except);
  186. normalize(configs);
  187. validate(configs, options);
  188. const resolved = resolveTargets(configs, options);
  189. options.normalizedHostingConfig = resolved;
  190. }
  191. return options.normalizedHostingConfig;
  192. }
  193. exports.hostingConfig = hostingConfig;