@lucidcms/core
Version:
The core of the Lucid CMS. It's responsible for spinning up the API and serving the CMS.
1 lines • 17.1 kB
Source Map (JSON)
{"version":3,"file":"index.mjs","names":["JSONCF","DateTimeCF"],"sources":["../../../../../src/libs/collection/builders/field-builder/index.ts"],"sourcesContent":["import LucidError from \"../../../../utils/errors/lucid-error.js\";\nimport { translate } from \"../../../i18n/index.js\";\nimport type CustomField from \"../../custom-fields/custom-field.js\";\nimport CheckboxCustomField from \"../../custom-fields/fields/checkbox/custom-field.js\";\nimport CodeCustomField from \"../../custom-fields/fields/code/custom-field.js\";\nimport CollapsibleCustomField from \"../../custom-fields/fields/collapsible/custom-field.js\";\nimport ColorCustomField from \"../../custom-fields/fields/color/custom-field.js\";\nimport DateTimeCF from \"../../custom-fields/fields/datetime/custom-field.js\";\nimport JSONCF from \"../../custom-fields/fields/json/custom-field.js\";\nimport LinkCustomField from \"../../custom-fields/fields/link/custom-field.js\";\nimport MediaCustomField from \"../../custom-fields/fields/media/custom-field.js\";\nimport NumberCustomField from \"../../custom-fields/fields/number/custom-field.js\";\nimport RangeCustomField from \"../../custom-fields/fields/range/custom-field.js\";\nimport RelationCustomField from \"../../custom-fields/fields/relation/custom-field.js\";\nimport RepeaterCustomField from \"../../custom-fields/fields/repeater/custom-field.js\";\nimport RichTextCustomField from \"../../custom-fields/fields/rich-text/custom-field.js\";\nimport SectionCustomField from \"../../custom-fields/fields/section/custom-field.js\";\nimport SelectCustomField from \"../../custom-fields/fields/select/custom-field.js\";\nimport TabCustomField from \"../../custom-fields/fields/tab/custom-field.js\";\nimport TextCustomField from \"../../custom-fields/fields/text/custom-field.js\";\nimport TextareaCustomField from \"../../custom-fields/fields/textarea/custom-field.js\";\nimport UserCustomField from \"../../custom-fields/fields/user/custom-field.js\";\nimport registeredFields from \"../../custom-fields/registered-fields.js\";\nimport { isStorageMode } from \"../../custom-fields/storage/index.js\";\nimport type {\n\tCFConfig,\n\tCFProps,\n\tFieldTypes,\n\tTabFieldConfig,\n} from \"../../custom-fields/types.js\";\nimport normalizeFieldCopy from \"../../custom-fields/utils/normalize-field-copy.js\";\nimport type { FieldBuilderMeta } from \"./types.js\";\n\n/**\n * - `full` includes every field: tabs at the root and structural fields with\n * their children nested. Used for admin rendering.\n * - `persisted` only includes stored fields, nested by storage scope. Used for\n * schema inference and value formatting.\n * - `client` includes stored fields plus sections/collapsibles with their\n * children nested. Tabs are transparent. Used for client response shaping.\n */\ntype FieldTreeMode = \"full\" | \"persisted\" | \"client\";\n\ntype ContainerStackEntry = {\n\tkind: \"repeater\" | \"section\" | \"collapsible\";\n\tkey: string;\n};\n\ntype StructuralFieldConfig = CFConfig<\"section\"> | CFConfig<\"collapsible\">;\n\nconst isStructuralFieldType = (\n\ttype: FieldTypes,\n): type is \"section\" | \"collapsible\" => {\n\treturn type === \"section\" || type === \"collapsible\";\n};\n\nclass FieldBuilder {\n\tfields: Map<string, CustomField<FieldTypes>> = new Map();\n\trepeaterStack: string[] = [];\n\tcontainerStack: ContainerStackEntry[] = [];\n\tmeta: FieldBuilderMeta = {\n\t\tfieldKeys: [],\n\t\trepeaterDepth: {},\n\t};\n\tactiveTabKey: string | null = null;\n\tprivate cachedFieldTree: CFConfig<FieldTypes>[] | null = null;\n\tprivate cachedPersistedFieldTree: CFConfig<FieldTypes>[] | null = null;\n\tprivate cachedClientFieldTree: CFConfig<FieldTypes>[] | null = null;\n\n\tprotected invalidateFieldTreeCache() {\n\t\tthis.cachedFieldTree = null;\n\t\tthis.cachedPersistedFieldTree = null;\n\t\tthis.cachedClientFieldTree = null;\n\t}\n\n\tprivate registerField(key: string, field: CustomField<FieldTypes>) {\n\t\tnormalizeFieldCopy(field.config);\n\n\t\tif (field.type !== \"tab\") {\n\t\t\tfield.tabParent = this.activeTabKey;\n\t\t}\n\n\t\tconst container = this.containerStack[this.containerStack.length - 1];\n\t\tif (container && container.kind !== \"repeater\" && field.type !== \"tab\") {\n\t\t\tfield.structuralParent = container.key;\n\t\t}\n\n\t\tthis.fields.set(key, field);\n\t\tthis.meta.fieldKeys.push(key);\n\t\tthis.invalidateFieldTreeCache();\n\t\treturn this;\n\t}\n\n\t// Custom Fields\n\tpublic addRepeater(key: string, props?: CFProps<\"repeater\">) {\n\t\tthis.meta.repeaterDepth[key] = this.repeaterStack.length;\n\t\tthis.registerField(key, new RepeaterCustomField(key, props));\n\t\tthis.repeaterStack.push(key);\n\t\tthis.containerStack.push({ kind: \"repeater\", key });\n\t\treturn this;\n\t}\n\tpublic addSection(key: string, props?: CFProps<\"section\">) {\n\t\tthis.registerField(key, new SectionCustomField(key, props));\n\t\tthis.containerStack.push({ kind: \"section\", key });\n\t\treturn this;\n\t}\n\tpublic addCollapsible(key: string, props?: CFProps<\"collapsible\">) {\n\t\tthis.registerField(key, new CollapsibleCustomField(key, props));\n\t\tthis.containerStack.push({ kind: \"collapsible\", key });\n\t\treturn this;\n\t}\n\tpublic addTab(key: string, props?: CFProps<\"tab\">) {\n\t\t//* tabs restart the root grouping, so any dangling structural containers close\n\t\tthis.containerStack = [];\n\t\tthis.activeTabKey = key;\n\t\treturn this.registerField(key, new TabCustomField(key, props));\n\t}\n\tpublic addToTab(key: string) {\n\t\tconst field = this.fields.get(key);\n\t\tif (!field) return this;\n\n\t\tif (field.type !== \"tab\") {\n\t\t\tthrow new LucidError({\n\t\t\t\tmessage: translate(\"server:core.fields.tab.target.invalid\", {\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\ttype: field.type,\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t});\n\t\t}\n\n\t\tthis.containerStack = [];\n\t\tthis.activeTabKey = key;\n\t\treturn this;\n\t}\n\tpublic addText(key: string, props?: CFProps<\"text\">) {\n\t\treturn this.registerField(key, new TextCustomField(key, props));\n\t}\n\tpublic addRichText(key: string, props?: CFProps<\"rich-text\">) {\n\t\treturn this.registerField(key, new RichTextCustomField(key, props));\n\t}\n\tpublic addMedia(key: string, props?: CFProps<\"media\">) {\n\t\treturn this.registerField(key, new MediaCustomField(key, props));\n\t}\n\tpublic addRelation(key: string, props: CFProps<\"relation\">) {\n\t\treturn this.registerField(key, new RelationCustomField(key, props));\n\t}\n\tpublic addNumber(key: string, props?: CFProps<\"number\">) {\n\t\treturn this.registerField(key, new NumberCustomField(key, props));\n\t}\n\tpublic addRange(key: string, props?: CFProps<\"range\">) {\n\t\treturn this.registerField(key, new RangeCustomField(key, props));\n\t}\n\tpublic addCheckbox(key: string, props?: CFProps<\"checkbox\">) {\n\t\treturn this.registerField(key, new CheckboxCustomField(key, props));\n\t}\n\tpublic addSelect(key: string, props?: CFProps<\"select\">) {\n\t\treturn this.registerField(key, new SelectCustomField(key, props));\n\t}\n\tpublic addTextarea(key: string, props?: CFProps<\"textarea\">) {\n\t\treturn this.registerField(key, new TextareaCustomField(key, props));\n\t}\n\tpublic addJSON(key: string, props?: CFProps<\"json\">) {\n\t\treturn this.registerField(key, new JSONCF(key, props));\n\t}\n\tpublic addCode(key: string, props?: CFProps<\"code\">) {\n\t\treturn this.registerField(key, new CodeCustomField(key, props));\n\t}\n\tpublic addColor(key: string, props?: CFProps<\"color\">) {\n\t\treturn this.registerField(key, new ColorCustomField(key, props));\n\t}\n\tpublic addDateTime(key: string, props?: CFProps<\"datetime\">) {\n\t\treturn this.registerField(key, new DateTimeCF(key, props));\n\t}\n\tpublic addLink(key: string, props?: CFProps<\"link\">) {\n\t\treturn this.registerField(key, new LinkCustomField(key, props));\n\t}\n\tpublic addUser(key: string, props?: CFProps<\"user\">) {\n\t\treturn this.registerField(key, new UserCustomField(key, props));\n\t}\n\tpublic endRepeater() {\n\t\tconst key = this.repeaterStack.pop();\n\t\tif (!key) return this;\n\n\t\t//* close the repeater and any dangling structural containers inside it\n\t\twhile (this.containerStack.length > 0) {\n\t\t\tconst entry = this.containerStack.pop();\n\t\t\tif (entry?.kind === \"repeater\" && entry.key === key) break;\n\t\t}\n\n\t\tconst fields = Array.from(this.fields.values());\n\n\t\t// index of repeater that is being closed\n\t\tconst selectedRepeaterIndex = fields.findIndex(\n\t\t\t(field) => field.type === \"repeater\" && field.key === key,\n\t\t);\n\t\tif (selectedRepeaterIndex === -1) return this; // Repeater not found\n\n\t\t// only fields after this repeater\n\t\tconst fieldsAfter = fields.slice(selectedRepeaterIndex + 1);\n\t\tlet hasUpdatedTreeParent = false;\n\n\t\tfor (const field of fieldsAfter) {\n\t\t\tif (field.type === \"tab\" || field.treeParent) continue;\n\t\t\tfield.treeParent = key;\n\t\t\thasUpdatedTreeParent = true;\n\t\t}\n\n\t\tif (hasUpdatedTreeParent) {\n\t\t\tthis.invalidateFieldTreeCache();\n\t\t}\n\n\t\treturn this;\n\t}\n\tpublic endSection() {\n\t\tconst top = this.containerStack[this.containerStack.length - 1];\n\t\tif (top?.kind === \"section\") this.containerStack.pop();\n\t\treturn this;\n\t}\n\tpublic endCollapsible() {\n\t\tconst top = this.containerStack[this.containerStack.length - 1];\n\t\tif (top?.kind === \"collapsible\") this.containerStack.pop();\n\t\treturn this;\n\t}\n\t// Private Methods\n\tprivate nestFields(mode: FieldTreeMode): CFConfig<FieldTypes>[] {\n\t\tconst nestStructural = mode !== \"persisted\";\n\t\tconst fields = Array.from(this.fields.values()).filter((field) => {\n\t\t\tif (mode === \"full\") return true;\n\t\t\tif (\n\t\t\t\tisStorageMode(registeredFields[field.type].config.database, \"ignore\")\n\t\t\t) {\n\t\t\t\treturn mode === \"client\" && isStructuralFieldType(field.type);\n\t\t\t}\n\t\t\treturn true;\n\t\t});\n\n\t\tconst result: CFConfig<FieldTypes>[] = [];\n\t\tconst tabMap: Map<string, CFConfig<\"tab\">> = new Map();\n\t\tconst repeaterMap: Map<string, CFConfig<\"repeater\">> = new Map();\n\t\tconst structuralMap: Map<string, StructuralFieldConfig> = new Map();\n\n\t\tfor (const field of fields) {\n\t\t\tconst config = JSON.parse(JSON.stringify(field.config));\n\n\t\t\tif (field.type === \"tab\") {\n\t\t\t\tconst tab = config as CFConfig<\"tab\">;\n\t\t\t\ttabMap.set(field.key, tab);\n\t\t\t\tresult.push(tab);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// add repeaters/structural containers to their lookups\n\t\t\tif (field.type === \"repeater\")\n\t\t\t\trepeaterMap.set(field.key, config as CFConfig<\"repeater\">);\n\t\t\tif (nestStructural && isStructuralFieldType(field.type))\n\t\t\t\tstructuralMap.set(field.key, config as StructuralFieldConfig);\n\n\t\t\tif (nestStructural && field.structuralParent) {\n\t\t\t\tconst structural = structuralMap.get(field.structuralParent);\n\t\t\t\tif (structural) {\n\t\t\t\t\tstructural.fields.push(\n\t\t\t\t\t\tconfig as Exclude<CFConfig<FieldTypes>, TabFieldConfig>,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (field.treeParent) {\n\t\t\t\tconst repeater = repeaterMap.get(field.treeParent);\n\t\t\t\tif (repeater)\n\t\t\t\t\trepeater.fields.push(\n\t\t\t\t\t\tconfig as Exclude<CFConfig<FieldTypes>, TabFieldConfig>,\n\t\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (mode === \"full\" && field.tabParent) {\n\t\t\t\tconst tab = tabMap.get(field.tabParent);\n\t\t\t\tif (tab) {\n\t\t\t\t\ttab.fields.push(\n\t\t\t\t\t\tconfig as Exclude<CFConfig<FieldTypes>, TabFieldConfig>,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tresult.push(config);\n\t\t}\n\n\t\treturn result;\n\t}\n\t// Getters\n\tget fieldTree(): CFConfig<FieldTypes>[] {\n\t\tif (!this.cachedFieldTree) {\n\t\t\tthis.cachedFieldTree = this.nestFields(\"full\");\n\t\t}\n\n\t\treturn this.cachedFieldTree;\n\t}\n\tget persistedFieldTree(): CFConfig<FieldTypes>[] {\n\t\tif (!this.cachedPersistedFieldTree) {\n\t\t\tthis.cachedPersistedFieldTree = this.nestFields(\"persisted\");\n\t\t}\n\n\t\treturn this.cachedPersistedFieldTree;\n\t}\n\tget clientFieldTree(): CFConfig<FieldTypes>[] {\n\t\tif (!this.cachedClientFieldTree) {\n\t\t\tthis.cachedClientFieldTree = this.nestFields(\"client\");\n\t\t}\n\n\t\treturn this.cachedClientFieldTree;\n\t}\n\tget flatFields(): CFConfig<FieldTypes>[] {\n\t\tconst config: CFConfig<FieldTypes>[] = [];\n\t\tfor (const [_, value] of this.fields) {\n\t\t\tconfig.push(value.config);\n\t\t}\n\t\treturn config;\n\t}\n}\n\nexport default FieldBuilder;\n"],"mappings":"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"}