UNPKG

@fireenjin/cli

Version:

A set of tools for speeding up production of full-stack Firebase projects.

265 lines (264 loc) 20.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); var fs = __importStar(require("fs")); var tiny_glob_1 = __importDefault(require("tiny-glob")); var pluralize = __importStar(require("pluralize")); var path_1 = __importDefault(require("path")); function renderToFile(templateName, location, dataFilter) { return __awaiter(this, void 0, void 0, function () { var _this = this; return __generator(this, function (_a) { return [2 /*return*/, new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () { var templateFile; var _this = this; return __generator(this, function (_a) { templateFile = "".concat(__dirname, "/../../templates/").concat(templateName, ".hbs"); try { if (fs.existsSync(process.cwd() + "/templates/".concat(templateName, ".hbs"))) { templateFile = process.cwd() + "/templates/".concat(templateName, ".hbs"); } else if (fs.existsSync(process.cwd() + "/".concat(templateName, ".hbs"))) { templateFile = path_1.default.join(process.cwd(), "../.fireenjin/templates", "/".concat(templateName, ".hbs")); } } catch (err) { console.error(err); } fs.readFile(templateFile, "utf8", function (_err, data) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { try { fs.writeFileSync(location, dataFilter(data)); resolve(data); } catch (err) { reject(err); } return [2 /*return*/]; }); }); }); return [2 /*return*/]; }); }); })]; }); }); } function capitalize(s) { if (typeof s !== "string") return ""; return s.charAt(0).toUpperCase() + s.slice(1); } function camelize(s) { if (typeof s !== "string") return ""; return s.charAt(0).toLowerCase() + s.slice(1); } function replaceModelName(data, modelName) { return data .replace(/{{pascalCase modelName}}/g, capitalize(modelName)) .replace(/{{camelCase modelName}}/g, modelName); } exports.default = (function (config) { return __awaiter(void 0, void 0, void 0, function () { var importStr, exportStr, endpointStr, skipResolvers, triggers, endpoints, models, endpointCount, schema, _i, _a, file, pathParts, triggerName, _b, _c, gqlType, _d, _e, field, modelName, modelName, modelName, modelName, modelName, _f, _g, modelName, e_1; var _h, _j; return __generator(this, function (_k) { switch (_k.label) { case 0: importStr = ""; exportStr = ""; endpointStr = ""; skipResolvers = []; triggers = []; endpoints = []; models = {}; endpointCount = 0; schema = require("".concat(process.cwd(), "/dist/graphql.schema.json")); // CHECK FOR CUSTOM RESOLVERS try { fs.readdir(((_h = config === null || config === void 0 ? void 0 : config.express) === null || _h === void 0 ? void 0 : _h.resolverDir) ? "".concat(process.cwd(), "/").concat((_j = config === null || config === void 0 ? void 0 : config.express) === null || _j === void 0 ? void 0 : _j.resolverDir) : "".concat(process.cwd(), "/dist/resolvers/"), function (err, files) { if (!err) files.forEach(function (file) { skipResolvers.push(file.split(".")[0].toLowerCase()); }); }); } catch (err) { console.log("error getting resolver..."); } _i = 0; return [4 /*yield*/, (0, tiny_glob_1.default)("./src/triggers/**/*.ts")]; case 1: _a = _k.sent(); _k.label = 2; case 2: if (!(_i < _a.length)) return [3 /*break*/, 4]; file = _a[_i]; pathParts = file.split(path_1.default.sep); triggerName = pathParts[pathParts.length - 1].split(".")[0]; importStr += "const ".concat(triggerName, " = require(\"./triggers/").concat(triggerName, "\");\n"); exportStr += " ".concat(triggerName, ": ").concat(triggerName, ".default,\n"); triggers.push(triggerName.toLowerCase()); _k.label = 3; case 3: _i++; return [3 /*break*/, 2]; case 4: // CREATE API ENDPOINTS FROM GRAPHQL SCHEMA for (_b = 0, _c = schema.__schema.types; _b < _c.length; _b++) { gqlType = _c[_b]; if (["Query", "Mutation"].indexOf(gqlType.name) === -1) continue; for (_d = 0, _e = gqlType.fields; _d < _e.length; _d++) { field = _e[_d]; if (endpoints.indexOf(field.name.toLowerCase) >= 0 || skipResolvers.indexOf(field.name.toLowerCase()) >= 0 || triggers.indexOf(field.name.toLowerCase()) >= 0) continue; if (gqlType.name === "Query" && field.type.kind === "OBJECT") { try { modelName = capitalize(pluralize.singular(field.name)); models[modelName] = true; endpointStr += "app.get(\"/".concat(camelize(modelName), "/:id\", async (req, res) => {\n const model = new ").concat(modelName, ".default();\n if (\n model.onAuth &&\n typeof model.onAuth === \"function\" &&\n !(await model.onAuth(\n \"find\",\n {\n id: req.params.id,\n },\n hookOptions\n ))\n )\n return res.status(400).send({\n message: \"Permission Denied!\"\n });\n const doc =\n model.onBeforeFind && typeof model.onBeforeFind === \"function\"\n ? await model.onBeforeFind(req.params.id, hookOptions)\n : await model.find(req.params.id);\n return res.send(\n await cleanData(\n model.onAfterFind && typeof model.onAfterFind === \"function\"\n ? await model.onAfterFind(doc, hookOptions)\n : doc\n , {with: req.query.with, include: req.query.include, exclude: req.query.exclude})\n );\n});\n"); } catch (e) { endpointCount--; console.log("Error creating ".concat(field.name, " trigger..."), e); } } else if (gqlType.name === "Query" && field.type.kind === "LIST") { try { modelName = capitalize(pluralize.singular(field.name)); models[modelName] = true; endpointStr += "app.get(\"/".concat(camelize(modelName), "\", async (req, res) => {\n const model = new ").concat(modelName, ".default();\n if (\n model.onAuth &&\n typeof model.onAuth === \"function\" &&\n !(await model.onAuth(\"list\", req.query ? req.query : {}, hookOptions))\n )\n return res.status(400).send({\n message: \"Permission Denied!\"\n });\n const docs =\n model.onBeforeList && typeof model.onBeforeList === \"function\"\n ? await model.onBeforeList(req.query, hookOptions)\n : await model.paginate(req.query);\n return res.send(\n await cleanData(\n model.onAfterList && typeof model.onAfterList === \"function\"\n ? await model.onAfterList(docs, hookOptions)\n : docs\n , {with: req.query.with, include: req.query.include, exclude: req.query.exclude})\n );\n});\n"); } catch (e) { endpointCount--; console.log("Error creating ".concat(field.name, " trigger..."), e); } } else if (gqlType.name === "Mutation" && field.name.startsWith("add")) { try { modelName = capitalize(field.name.slice(3)); models[modelName] = true; endpointStr += "app.post(\"/".concat(camelize(modelName), "\", async (req, res) => {\n const requestInput = typeof req.body === \"string\" ? JSON.parse(req.body) : req.body;\n if (typeof requestInput === \"string\") return res.status(400).send({\n message: \"Invalid doc data input!\"\n });\n const model = new ").concat(modelName, ".default();\n if (\n model.onAuth &&\n typeof model.onAuth === \"function\" &&\n !(await model.onAuth(\"add\", requestInput, hookOptions))\n )\n return res.status(400).send({\n message: \"Permission Denied!\"\n });\n const docData =\n model.onBeforeAdd && typeof model.onBeforeAdd === \"function\"\n ? await model.onBeforeAdd(requestInput, hookOptions)\n : model.onBeforeWrite && typeof model.onBeforeWrite === \"function\"\n ? await model.onBeforeWrite(requestInput, hookOptions)\n : requestInput;\n if (docData === false) {\n return res.status(400).send({\n message: \"No data for doc!\"\n });\n }\n\n const newDoc = await model.create(docData);\n\n return res.send(\n await cleanData(\n model.onAfterAdd && typeof model.onAfterAdd === \"function\"\n ? await model.onAfterAdd(newDoc, hookOptions)\n : model.onAfterWrite && typeof model.onAfterWrite === \"function\"\n ? await model.onAfterWrite(newDoc, hookOptions)\n : newDoc\n , {with: requestInput.with, include: requestInput.include, exclude: requestInput.exclude})\n );\n});\n"); } catch (e) { endpointCount--; console.log("Error creating ".concat(field.name, " trigger..."), e); } } else if (gqlType.name === "Mutation" && field.name.startsWith("edit")) { try { modelName = capitalize(field.name.slice(4)); models[modelName] = true; endpointStr += "app.post(\"/".concat(camelize(modelName), "/:id\", async (req, res) => {\n const requestInput = typeof req.body === \"string\" ? JSON.parse(req.body) : req.body;\n if (typeof requestInput === \"string\") return res.status(400).send({\n message: \"Invalid doc data input!\"\n });\n const model = new ").concat(modelName, ".default();\n if (\n model.onAuth &&\n typeof model.onAuth === \"function\" &&\n !(await model.onAuth(\"edit\", { ...requestInput, id: req.params.id }, hookOptions))\n )\n return res.status(400).send({\n message: \"Permission Denied!\"\n });\n const docData =\n model.onBeforeEdit && typeof model.onBeforeEdit === \"function\"\n ? await model.onBeforeEdit({ id: req.params.id, ...requestInput }, hookOptions)\n : model.onBeforeWrite && typeof model.onBeforeWrite === \"function\"\n ? await model.onBeforeWrite({ id: req.params.id, ...requestInput }, hookOptions)\n : data;\n if (docData === false) {\n return res.status(400).send({\n message: \"No data for doc!\"\n });\n }\n\n const doc = await model.update({ id: req.params.id, ...docData });\n \n return res.send(\n await cleanData(\n model.onAfterEdit && typeof model.onAfterEdit === \"function\"\n ? await model.onAfterEdit(doc, hookOptions)\n : model.onAfterWrite && typeof model.onAfterWrite === \"function\"\n ? await model.onAfterWrite(doc, hookOptions)\n : doc\n , {with: requestInput.with, include: requestInput.include, exclude: requestInput.exclude})\n );\n});\n"); } catch (e) { endpointCount--; console.log("Error creating ".concat(field.name, " trigger..."), e); } } else if (gqlType.name === "Mutation" && field.name.startsWith("delete")) { try { modelName = capitalize(field.name.slice(6)); models[modelName] = true; endpointStr += "app.delete(\"/".concat(camelize(modelName), "/:id\", async (req, res) => {\n const requestInput = typeof req.body === \"string\" ? JSON.parse(req.body) : req.body;\n if (typeof requestInput === \"string\") return res.status(400).send({\n message: \"Invalid doc data input!\"\n });\n const model = new ").concat(modelName, ".default();\n if (\n model.onAuth &&\n typeof model.onAuth === \"function\" &&\n !(await model.onAuth(\"list\", { id: req.params.id }, hookOptions))\n )\n return res.status(400).send({\n message: \"Permission Denied!\"\n });\n const modelBefore = await model.find(req.params.id);\n if (model.onBeforeDelete && typeof model.onBeforeDelete === \"function\") {\n const delData = await model.onBeforeDelete({\n id: req.params.id,\n ...modelBefore\n }, hookOptions);\n if (delData === false) {\n return res.status(400).send({\n message: \"No data for doc!\"\n });\n }\n }\n await model.delete(req.params.id);\n\n return res.send(\n await cleanData(\n model.onAfterDelete && typeof model.onAfterDelete === \"function\"\n ? await model.onAfterDelete({ id: req.params.id, ...modelBefore }, hookOptions)\n : { id: req.params.id, ...modelBefore }\n , {with: req.query.with, include: req.query.include, exclude: req.query.exclude})\n );\n});\n"); } catch (e) { endpointCount--; console.log("Error creating ".concat(field.name, " trigger..."), e); } } endpoints.push(field.name.toLowerCase()); } } for (_f = 0, _g = Object.keys(models); _f < _g.length; _f++) { modelName = _g[_f]; importStr += "const ".concat(modelName, " = require(\"./models/").concat(modelName, "/").concat(modelName, ".model\");\n"); } _k.label = 5; case 5: _k.trys.push([5, 7, , 8]); return [4 /*yield*/, renderToFile("firebaseFunctionsIndex", "./dist/index.js", function (data) { return data .replace(/{{imports}}/g, importStr) .replace(/{{exports}}/g, exportStr) .replace(/{{endpoints}}/g, endpointStr); })]; case 6: _k.sent(); return [3 /*break*/, 8]; case 7: e_1 = _k.sent(); console.log("Error rendering firebase functions index... ", e_1); return [3 /*break*/, 8]; case 8: console.log("Rendered Firebase Functions index file with ".concat(triggers.length + 1, " triggers and ").concat(endpointCount + endpoints.length, " api endpoints...")); return [2 /*return*/]; } }); }); });