UNPKG

wakeb-starter-cli

Version:

A powerful CLI tool for generating CRUD modules, common modules, and components with Vue 3 form schemas, featuring intelligent field detection and automatic schema generation

293 lines (250 loc) 9.93 kB
import { capitalize } from "./stringUtils.js"; export function generateSchemaFromPayload( payloadText, crudName, helpModels, helpEnums ) { const lines = payloadText .split(/[\n,]/) .map((line) => line.trim()) .filter((line) => line.length > 0); const fieldMap = { text: "createTextField", textarea: "createTextAreaField", select: "createSelectField", image: "createImageInput", file: "createImageInput", number: "createNumberField", password: "createPasswordField", checkbox: "createCheckBoxField", radio: "createRadioButtonField", radiobutton: "createRadioButtonField", phone: "createPhoneField", datetime: "createDateTimeField", date: "createDateTimeField", time: "createDateTimeField", combobox: "createComboBoxField", combo: "createComboBoxField", button: "createButton", otp: "createOtpInput", captcha: "createCaptchaField", editor: "createEditorField", }; const usedFields = new Set(); const schemaEntries = lines.map((line) => { const [rawKey, rawType] = line.split(":").map((s) => s.trim()); const key = rawKey.replace("[", ".").replace("]", ""); const type = fieldMap[rawType?.toLowerCase()] || "createTextField"; usedFields.add(type); const isSelect = type === "createSelectField"; const isDateTime = type === "createDateTimeField"; const isNumber = type === "createNumberField"; const isCheckbox = type === "createCheckBoxField"; const isRadio = type === "createRadioButtonField"; const isPhone = type === "createPhoneField"; const isComboBox = type === "createComboBoxField"; const isPassword = type === "createPasswordField"; const isOtp = type === "createOtpInput"; const isCaptcha = type === "createCaptchaField"; const isEditor = type === "createEditorField"; const isButton = type === "createButton"; // Check if key ends with "ids" for multiple select const isMultipleSelect = isSelect && key.endsWith("ids"); // Find matching model or enum for select fields let matchType = null; let matchName = null; if (isSelect) { // Check for model match first const modelMatch = helpModels.find((m) => { const modelName = m.includes(".") ? m.split(".").pop() : m; return ( key.includes(modelName.slice(0, -1)) || key.replace("_id", "") === modelName.slice(0, -1) || key.replace("_ids", "") === modelName.slice(0, -1) ); }); if (modelMatch) { matchType = "model"; matchName = modelMatch; } else { // Check for enum match - improved logic const enumMatch = helpEnums.find((e) => { const enumName = e.includes(".") ? e.split(".").pop() : e; return ( key === enumName || key.includes(enumName) || key.replace("_id", "") === enumName || key.replace("_ids", "") === enumName || // Additional check for exact match enumName === key ); }); if (enumMatch) { matchType = "enum"; matchName = enumMatch; } } } // Generate label based on key with specific rules let label = key; // Rule 1: If has dot, replace with underscore if (key.includes(".")) label = key.replace(/\./g, "_"); // Rule 2: If ends with _id or _ids, remove it else if (key.endsWith("_id")) label = key.replace("_id", ""); else if (key.endsWith("_ids")) label = key.replace("_ids", ""); // Rule 3: If has underscore, keep as is else if (key.includes("_")) label = key; let updateKeyLine = ""; let optionsLine = ""; let itemLines = ""; let valueLine = ""; let extraProps = ""; // Set default value based on field type if (isSelect || isComboBox || isRadio) valueLine = isMultipleSelect ? "value: []," : "value: null,"; else if (isCheckbox) valueLine = "value: false,"; else if (isNumber) valueLine = "value: 0,"; else if (isDateTime) valueLine = "value: null,"; else if (isOtp) valueLine = "value: '',"; else if (isButton) valueLine = ""; else valueLine = 'value: "",'; // Handle select field with model match if (isSelect && matchType === "model") { const modelBase = matchName.includes(".") ? matchName.split(".").pop() : matchName; const baseKey = modelBase.slice(0, -1); // For model matches, override label with the base key if (key.endsWith("_id")) label = key.replace("_id", ""); else if (key.endsWith("_ids")) label = key.replace("_ids", ""); updateKeyLine = `updateKey: "${baseKey}.id",`; // Extract the last part for options const optionsKey = matchName.includes(".") ? matchName.split(".").pop() : matchName; optionsLine = `options: computed(() => enumsStore.state["${optionsKey}"]),`; itemLines = `itemTitle: "name", itemValue: "id",`; } else if (isSelect && matchType === "enum") { const enumBase = matchName.includes(".") ? matchName.split(".").pop() : matchName; // For enum matches, use the enum name as label label = enumBase.replace("user_", "").replace(/_/g, "_"); // Extract the last part for options const optionsKey = matchName.includes(".") ? matchName.split(".").pop() : matchName; optionsLine = `options: computed(() => enumsStore.state["${optionsKey}"]),`; itemLines = `itemTitle: "label", itemValue: "value",`; } else if (isSelect && (key.endsWith("_id") || key.endsWith("_ids"))) { // Handle select fields that end with _id or _ids but don't match models/enums const baseKey = key.replace("_id", "").replace("_ids", ""); label = baseKey; updateKeyLine = `updateKey: "${baseKey}.id",`; optionsLine = `options: computed(() => enumsStore.state["${baseKey}s"]),`; itemLines = `itemTitle: "title", itemValue: "id",`; } else if (isSelect) { // Handle select fields that don't end with _id/_ids and don't match models/enums // This is for cases like employee_type:select label = key; optionsLine = `options: computed(() => enumsStore.state["${key}"]),`; itemLines = `itemTitle: "label", itemValue: "value",`; } else if (isComboBox) { // Handle combo box fields optionsLine = `options: computed(() => enumsStore.state["${key}"]),`; itemLines = `itemTitle: "label", itemValue: "value",`; } else if (isRadio) { // Handle radio button fields optionsLine = `options: computed(() => enumsStore.state["${key}"]),`; itemLines = `itemTitle: "label", itemValue: "value",`; } // Handle special properties based on field type and key patterns if ( key.endsWith(".ar") && (type === "createTextField" || type === "createTextAreaField") ) { extraProps = `isArabicOnly: true,`; } else if ( key.endsWith(".en") && (type === "createTextField" || type === "createTextAreaField") ) { extraProps = `isEnglishOnly: true,`; } else if (type === "createImageInput") { extraProps = `required: true, maxSize: "30 MB",`; } else if (isDateTime) { // Handle datetime specific props if (rawType?.toLowerCase() === "date") extraProps = `dateOnly: true,`; else if (rawType?.toLowerCase() === "time") extraProps = `enableTimePicker: true,`; } else if (isNumber) extraProps = `min: 0,`; else if (isOtp) extraProps = `length: 6,`; else if (isCaptcha) extraProps = `length: 5,`; else if (isEditor) extraProps = `direction: "ltr",`; else if (isButton) extraProps = `click: () => {},`; // Add isMultiple for select fields ending with "ids" if (isMultipleSelect) { extraProps = extraProps ? `${extraProps}\n isMultiple: true,` : `isMultiple: true,`; } // Build the field configuration const fieldConfig = []; if (!isButton) { fieldConfig.push(`t,`); } fieldConfig.push(`key: "${key}",`); fieldConfig.push(`label: "${label}",`); if (updateKeyLine) fieldConfig.push(updateKeyLine); if (!isButton) fieldConfig.push(`inputStyle: isView ? "viewMode" : "flat",`); fieldConfig.push(`cols: { md: 6, lg: 6 },`); if (valueLine) fieldConfig.push(valueLine); if (extraProps) fieldConfig.push(extraProps); if (itemLines) fieldConfig.push(itemLines); if (optionsLine) fieldConfig.push(optionsLine); return `${type}({ ${fieldConfig.join("\n ")} })`; }); // Generate imports const imports = `import { ${[...usedFields].join( ", " )} } from '@/utils/fieldUtils'`; // Generate the final schema code const schemaCode = `${imports} import { ref, computed } from 'vue' import i18n from '@/utils/i18n' import { useEnumsStore } from '@/stores/enums' export const use${capitalize( crudName )}Schema = ({ isCreate = false, isView = false }) => { const t = i18n.global.t const enumsStore = useEnumsStore() const schema = ref([ ${schemaEntries.join(",\n ")}, ]) return schema }`; return schemaCode; } export function generateBasicSchema(crudName) { // Generate the basic schema code with empty array const schemaCode = `import { ref, computed } from "vue"; import i18n from "@/utils/i18n"; export const use${capitalize( crudName )}Schema = ({ isCreate = false, isView = false }) => { const t = i18n.global.t; const schema = ref([]); return schema; };`; return schemaCode; }