Sin descripción
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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.Config = void 0;
  4. const _ = require("lodash");
  5. const clc = require("colorette");
  6. const fs = require("fs-extra");
  7. const path = require("path");
  8. const cjson = require("cjson");
  9. const detectProjectRoot_1 = require("./detectProjectRoot");
  10. const error_1 = require("./error");
  11. const fsutils = require("./fsutils");
  12. const prompt_1 = require("./prompt");
  13. const projectPath_1 = require("./projectPath");
  14. const utils = require("./utils");
  15. const firebaseConfigValidate_1 = require("./firebaseConfigValidate");
  16. const logger_1 = require("./logger");
  17. const loadCJSON_1 = require("./loadCJSON");
  18. const parseBoltRules = require("./parseBoltRules");
  19. class Config {
  20. constructor(src, options = {}) {
  21. var _a;
  22. this.data = {};
  23. this.defaults = {};
  24. this.notes = {};
  25. this.options = options;
  26. this.projectDir = this.options.projectDir || (0, detectProjectRoot_1.detectProjectRoot)(this.options);
  27. this._src = src;
  28. if (this._src.firebase) {
  29. this.defaults.project = this._src.firebase;
  30. utils.logWarning(clc.bold('"firebase"') +
  31. " key in firebase.json is deprecated. Run " +
  32. clc.bold("firebase use --add") +
  33. " instead");
  34. }
  35. if ((_a = this._src) === null || _a === void 0 ? void 0 : _a.rules) {
  36. this._src.database = Object.assign(Object.assign({}, this._src.database), { rules: this._src.rules });
  37. }
  38. Config.MATERIALIZE_TARGETS.forEach((target) => {
  39. if (_.get(this._src, target)) {
  40. _.set(this.data, target, this.materialize(target));
  41. }
  42. });
  43. if (this.projectDir && fsutils.dirExistsSync(this.path(Config.DEFAULT_FUNCTIONS_SOURCE))) {
  44. if (Array.isArray(this.get("functions"))) {
  45. if (!this.get("functions.[0].source")) {
  46. this.set("functions.[0].source", Config.DEFAULT_FUNCTIONS_SOURCE);
  47. }
  48. }
  49. else {
  50. if (!this.get("functions.source")) {
  51. this.set("functions.source", Config.DEFAULT_FUNCTIONS_SOURCE);
  52. }
  53. }
  54. }
  55. }
  56. materialize(target) {
  57. const val = _.get(this._src, target);
  58. if (typeof val === "string") {
  59. let out = this.parseFile(target, val);
  60. const segments = target.split(".");
  61. const lastSegment = segments[segments.length - 1];
  62. if (Object.keys(out).length === 1 && out[lastSegment]) {
  63. out = out[lastSegment];
  64. }
  65. return out;
  66. }
  67. else if (val !== null && typeof val === "object") {
  68. return val;
  69. }
  70. throw new error_1.FirebaseError('Parse Error: "' + target + '" must be object or import path', {
  71. exit: 1,
  72. });
  73. }
  74. parseFile(target, filePath) {
  75. const fullPath = (0, projectPath_1.resolveProjectPath)(this.options, filePath);
  76. const ext = path.extname(filePath);
  77. if (!fsutils.fileExistsSync(fullPath)) {
  78. throw new error_1.FirebaseError("Parse Error: Imported file " + filePath + " does not exist", {
  79. exit: 1,
  80. });
  81. }
  82. switch (ext) {
  83. case ".json":
  84. if (target === "database") {
  85. this.notes.databaseRules = "json";
  86. }
  87. else if (target === "database.rules") {
  88. this.notes.databaseRulesFile = filePath;
  89. try {
  90. return fs.readFileSync(fullPath, "utf8");
  91. }
  92. catch (e) {
  93. if (e.code === "ENOENT") {
  94. throw new error_1.FirebaseError(`File not found: ${fullPath}`, { original: e });
  95. }
  96. throw e;
  97. }
  98. }
  99. return (0, loadCJSON_1.loadCJSON)(fullPath);
  100. case ".bolt":
  101. if (target === "database") {
  102. this.notes.databaseRules = "bolt";
  103. }
  104. return parseBoltRules(fullPath);
  105. default:
  106. throw new error_1.FirebaseError("Parse Error: " + filePath + " is not of a supported config file type", { exit: 1 });
  107. }
  108. }
  109. get src() {
  110. return this._src;
  111. }
  112. get(key, fallback) {
  113. return _.get(this.data, key, fallback);
  114. }
  115. set(key, value) {
  116. _.set(this._src, key, value);
  117. return _.set(this.data, key, value);
  118. }
  119. has(key) {
  120. return _.has(this.data, key);
  121. }
  122. path(pathName) {
  123. const outPath = path.normalize(path.join(this.projectDir, pathName));
  124. if (path.relative(this.projectDir, outPath).includes("..")) {
  125. throw new error_1.FirebaseError(clc.bold(pathName) + " is outside of project directory", { exit: 1 });
  126. }
  127. return outPath;
  128. }
  129. readProjectFile(p, options = {}) {
  130. options = options || {};
  131. try {
  132. const content = fs.readFileSync(this.path(p), "utf8");
  133. if (options.json) {
  134. return JSON.parse(content);
  135. }
  136. return content;
  137. }
  138. catch (e) {
  139. if (options.fallback) {
  140. return options.fallback;
  141. }
  142. if (e.code === "ENOENT") {
  143. throw new error_1.FirebaseError(`File not found: ${this.path(p)}`, { original: e });
  144. }
  145. throw e;
  146. }
  147. }
  148. writeProjectFile(p, content) {
  149. if (typeof content !== "string") {
  150. content = JSON.stringify(content, null, 2) + "\n";
  151. }
  152. fs.ensureFileSync(this.path(p));
  153. fs.writeFileSync(this.path(p), content, "utf8");
  154. }
  155. projectFileExists(p) {
  156. return fs.existsSync(this.path(p));
  157. }
  158. deleteProjectFile(p) {
  159. fs.removeSync(this.path(p));
  160. }
  161. askWriteProjectFile(p, content, force) {
  162. const writeTo = this.path(p);
  163. let next;
  164. if (fsutils.fileExistsSync(writeTo) && !force) {
  165. next = (0, prompt_1.promptOnce)({
  166. type: "confirm",
  167. message: "File " + clc.underline(p) + " already exists. Overwrite?",
  168. default: false,
  169. });
  170. }
  171. else {
  172. next = Promise.resolve(true);
  173. }
  174. return next.then((result) => {
  175. if (result) {
  176. this.writeProjectFile(p, content);
  177. utils.logSuccess("Wrote " + clc.bold(p));
  178. }
  179. else {
  180. utils.logBullet("Skipping write of " + clc.bold(p));
  181. }
  182. });
  183. }
  184. static load(options, allowMissing) {
  185. const pd = (0, detectProjectRoot_1.detectProjectRoot)(options);
  186. const filename = options.configPath || Config.FILENAME;
  187. if (pd) {
  188. try {
  189. const filePath = path.resolve(pd, path.basename(filename));
  190. const data = cjson.load(filePath);
  191. const validator = (0, firebaseConfigValidate_1.getValidator)();
  192. const valid = validator(data);
  193. if (!valid && validator.errors) {
  194. for (const e of validator.errors) {
  195. logger_1.logger.debug((0, firebaseConfigValidate_1.getErrorMessage)(e));
  196. }
  197. }
  198. return new Config(data, options);
  199. }
  200. catch (e) {
  201. throw new error_1.FirebaseError(`There was an error loading ${filename}:\n\n` + e.message, {
  202. exit: 1,
  203. });
  204. }
  205. }
  206. if (allowMissing) {
  207. return null;
  208. }
  209. throw new error_1.FirebaseError("Not in a Firebase app directory (could not locate firebase.json)", {
  210. exit: 1,
  211. });
  212. }
  213. }
  214. exports.Config = Config;
  215. Config.DEFAULT_FUNCTIONS_SOURCE = "functions";
  216. Config.FILENAME = "firebase.json";
  217. Config.MATERIALIZE_TARGETS = [
  218. "database",
  219. "emulators",
  220. "extensions",
  221. "firestore",
  222. "functions",
  223. "hosting",
  224. "storage",
  225. "remoteconfig",
  226. ];