UNPKG

@api-platform/client-generator

Version:

Generate apps built with Next, Nuxt, Quasar, React, React Native, Vue or Vuetify for any API documented using Hydra or OpenAPI

332 lines (290 loc) 8.75 kB
import handlebars from "handlebars"; import hbh_comparison from "handlebars-helpers/lib/comparison.js"; import hbh_array from "handlebars-helpers/lib/array.js"; import hbh_string from "handlebars-helpers/lib/string.js"; import { sprintf } from "sprintf-js"; import BaseGenerator from "./BaseGenerator.js"; export default class extends BaseGenerator { constructor(params) { super(params); this.registerTemplates("vue-common/", [ // error "error/SubmissionError.js", // mixins "mixins/CreateMixin.js", "mixins/ListMixin.js", "mixins/NotificationMixin.js", "mixins/ShowMixin.js", "mixins/UpdateMixin.js", // services "services/api.js", "services/foo.js", // modules "store/modules/crud.js", "store/modules/notifications.js", // utils "utils/dates.js", "utils/fetch.js", "utils/hydra.js", // validators "validators/date.js", ]); handlebars.registerHelper("compare", hbh_comparison.compare); handlebars.registerHelper("ifEven", hbh_comparison.ifEven); handlebars.registerHelper("ifOdd", hbh_comparison.ifOdd); handlebars.registerHelper("isArray", hbh_array.isArray); handlebars.registerHelper("inArray", hbh_array.inArray); handlebars.registerHelper("forEach", hbh_array.forEach); handlebars.registerHelper("lowercase", hbh_string.lowercase); this.registerSwitchHelper(); } registerSwitchHelper() { /* https://github.com/wycats/handlebars.js/issues/927#issuecomment-318640459 {{#switch state}} {{#case "page1" "page2"}}page 1 or 2{{/case}} {{#case "page3"}}page3{{/case}} {{#case "page4"}}page4{{/case}} {{#case "page5"}} {{#switch s}} {{#case "3"}}s = 3{{/case}} {{#case "2"}}s = 2{{/case}} {{#case "1"}}s = 1{{/case}} {{#default}}unknown{{/default}} {{/switch}} {{/case}} {{#default}}page0{{/default}} {{/switch}} */ handlebars.__switch_stack__ = []; handlebars.registerHelper("switch", function (value, options) { handlebars.__switch_stack__.push({ switch_match: false, switch_value: value, }); let html = options.fn(this); handlebars.__switch_stack__.pop(); return html; }); handlebars.registerHelper("case", function (value, options) { let args = Array.from(arguments); options = args.pop(); let caseValues = args; let stack = handlebars.__switch_stack__[handlebars.__switch_stack__.length - 1]; if (stack.switch_match || caseValues.indexOf(stack.switch_value) === -1) { return ""; } else { stack.switch_match = true; return options.fn(this); } }); handlebars.registerHelper("default", function (options) { let stack = handlebars.__switch_stack__[handlebars.__switch_stack__.length - 1]; if (!stack.switch_match) { return options.fn(this); } }); } getContextForResource(resource, params) { const lc = resource.title.toLowerCase(); const titleUcFirst = resource.title.charAt(0).toUpperCase() + resource.title.slice(1); const fields = this.parseFields(resource); const formFields = this.buildFields(resource.writableFields); const dateTypes = ["time", "date", "dateTime"]; const formContainsDate = formFields.some((e) => dateTypes.includes(e.type)); const parameters = []; params.forEach((p) => { const param = fields.find((field) => field.name === p.variable); if (!param) { p.name = p.variable; parameters.push(p); } else { param.multiple = p.multiple; parameters.push(param); } }); const paramsHaveRefs = parameters.some( (e) => e.type === "text" && e.reference ); const labels = this.commonLabelTexts(); return { title: resource.title, name: resource.name, lc, uc: resource.title.toUpperCase(), fields, dateTypes, paramsHaveRefs, parameters, formFields, formContainsDate, hydraPrefix: this.hydraPrefix, titleUcFirst, labels, }; } generate(api, resource, dir) { return resource.getParameters().then((params) => { params = params.map((param) => ({ ...param, ...this.getHtmlInputTypeFromField(param), })); params = this.cleanupParams(params); this.generateFiles(api, resource, dir, params); }); } // eslint-disable-next-line no-unused-vars generateFiles(api, resource, dir, params) { // Create directories // These directories may already exist [ `${dir}/config`, `${dir}/error`, `${dir}/mixins`, `${dir}/router`, `${dir}/services`, `${dir}/store/modules`, `${dir}/utils`, `${dir}/validators`, ].forEach((dir) => this.createDir(dir, false)); // error this.createFile( "error/SubmissionError.js", `${dir}/error/SubmissionError.js`, {}, false ); // mixins [ "mixins/Create%s.js", "mixins/List%s.js", "mixins/Notification%s.js", "mixins/Show%s.js", "mixins/Update%s.js", ].forEach((pattern) => this.createFile( sprintf(`${pattern}`, "Mixin"), sprintf(`${dir}/${pattern}`, "Mixin"), {}, false ) ); // stores ["crud.js", "notifications.js"].forEach((file) => this.createFile( `store/modules/${file}`, `${dir}/store/modules/${file}`, { hydraPrefix: this.hydraPrefix }, false ) ); // services this.createFile("services/api.js", `${dir}/services/api.js`, {}, false); this.createFileFromPattern( "services/%s.js", dir, resource.title.toLowerCase(), { name: resource.name } ); // validators this.createFile( "validators/date.js", `${dir}/validators/date.js`, { hydraPrefix: this.hydraPrefix }, false ); // utils ["dates.js", "fetch.js", "hydra.js"].forEach((file) => this.createFile(`utils/${file}`, `${dir}/utils/${file}`, {}, false) ); this.createEntrypoint(api.entrypoint, `${dir}/config/entrypoint.js`); } cleanupParams(params) { const stats = {}; const result = []; params.forEach((p) => { let key = p.variable.endsWith("[]") ? p.variable.slice(0, -2) : p.variable; if (!stats[key]) { stats[key] = 0; } stats[key] += 1; }); params.forEach((p) => { if (p.variable.endsWith("[exists]")) { return; // removed for the moment, it can help to add null option to select } if (p.variable.startsWith("order[")) { return; // removed for the moment, it can help to sorting data } if (!stats[p.variable] && p.variable.endsWith("[]")) { if (stats[p.variable.slice(0, -2)] === 1) { result.push(p); } } else { if (stats[p.variable] === 2) { p.multiple = true; } result.push(p); } }); return result; } contextLabelTexts(formFields, fields) { let texts = []; formFields.forEach((x) => texts.push(x.name)); // forms fields.forEach((x) => texts.push(x.name)); // for show, too return [...new Set(texts)]; } commonLabelTexts() { return { submit: "Submit", reset: "Reset", delete: "Delete", edit: "Edit", confirmDelete: "Are you sure you want to delete this item?", noresults: "No results", close: "Close", cancel: "Cancel", updated: "Updated", field: "Field", value: "Value", filters: "Filters", filter: "Filter", unavail: "Data unavailable", loading: "Loading...", deleted: "Deleted", numValidation: "Please, insert a value bigger than zero!", stringValidation: "Please type something", required: "Field is required", recPerPage: "Records per page:", }; } parseFields(resource) { const fields = [ ...resource.writableFields, ...resource.readableFields, ].reduce((list, field) => { if (list[field.name]) { return list; } const isReferences = field.reference && field.maxCardinality !== 1; const isEmbeddeds = field.embedded && field.maxCardinality !== 1; return { ...list, [field.name]: { ...field, readonly: false, isReferences, isEmbeddeds, isRelations: isEmbeddeds || isReferences, }, }; }, {}); return Object.values(fields); } }