UNPKG

stackpress

Version:

Incept is a content management framework.

502 lines (501 loc) 16.3 kB
import { VariableDeclarationKind } from 'ts-morph'; export default function generate(directory, registry) { for (const model of registry.model.values()) { model.columns.forEach(column => column.field.method === 'relation' ? generateRelation(directory, model, column) : column.field.method === 'fieldset' ? generateFieldset(directory, model, column) : column.field.component && ['Checkbox', 'Switch'].indexOf(column.field.component) !== -1 ? generateBoolean(directory, model, column) : generateField(directory, model, column)); } for (const fieldset of registry.fieldset.values()) { fieldset.columns.forEach(column => column.field.method === 'fieldset' ? generateFieldset(directory, fieldset, column) : column.field.component && ['Checkbox', 'Switch'].indexOf(column.field.component) !== -1 ? generateBoolean(directory, fieldset, column) : generateField(directory, fieldset, column)); } } export function generateRelation(directory, model, column) { if (typeof column.field.component !== 'string') return; const path = `${model.name}/components/fields/${column.title}Field.tsx`; const source = directory.createSourceFile(path, '', { overwrite: true }); source.addImportDeclaration({ isTypeOnly: true, moduleSpecifier: 'stackpress/view/client', namedImports: ['FieldProps', 'ControlProps'] }); source.addImportDeclaration({ moduleSpecifier: 'mustache', defaultImport: 'mustache' }); source.addImportDeclaration({ moduleSpecifier: 'r22n', namedImports: ['useLanguage'] }); source.addImportDeclaration({ moduleSpecifier: 'frui/form/Control', defaultImport: 'Control' }); source.addImportDeclaration({ moduleSpecifier: `frui/field/${column.field.component}`, defaultImport: column.field.component }); source.addFunction({ isExported: true, name: `${column.title}Field`, parameters: [ { name: 'props', type: 'FieldProps' } ], statements: (` //props const { className, name = '${column.name}${column.multiple ? '[]' : ''}', value, change, error = false } = props; //render return ( <${column.field.component} name={name} className={className} error={error} defaultValue={value} searchable={true} onQuery={async (query, update) => { const response = await fetch(\`${column.field.attributes.search?.includes('?') ? column.field.attributes.search + '&q=${query}' : column.field.attributes.search + '?q=${query}'}\`); const json = await response.json(); const options = json.results.map(row => ({ label: mustache.render('${column.field.attributes.template}', row), value: row.${column.field.attributes.id} })); update(options); }} /> ); `) }); source.addFunction({ isExported: true, name: `${column.title}FieldControl`, parameters: [ { name: 'props', type: 'ControlProps' } ], statements: (` //props const { className, name, value, change, error } = props; //hooks const { _ } = useLanguage(); //determine label const label = ${column.required && !column.multiple ? `\`\${_('${column.label}')}*\`;` : `_('${column.label}');`} //render return ( <Control label={label} error={error} className={className}> <${column.title}Field error={!!error} name={name} value={value} change={change} /> </Control> ); `) }); } export function generateFieldset(directory, model, column) { if (!column.fieldset) return; const path = `${model.name}/components/fields/${column.title}Field.tsx`; const source = directory.createSourceFile(path, '', { overwrite: true }); const fieldset = column.fieldset; source.addImportDeclaration({ isTypeOnly: true, moduleSpecifier: 'frui/element/Fieldset', namedImports: ['FieldsProps', 'FieldsetProps'] }); source.addImportDeclaration({ isTypeOnly: true, moduleSpecifier: 'stackpress/view/client', namedImports: ['ControlProps'] }); source.addImportDeclaration({ isTypeOnly: true, moduleSpecifier: `../../../${fieldset.name}/types`, namedImports: [`${column.title}Input`] }); source.addImportDeclaration({ moduleSpecifier: 'r22n', namedImports: ['useLanguage'] }); source.addImportDeclaration({ moduleSpecifier: 'frui/form/Control', defaultImport: 'Control' }); source.addImportDeclaration({ moduleSpecifier: 'frui/form/Fieldset', defaultImport: 'make' }); fieldset.fields.forEach(column => { if (typeof column.field.component !== 'string') return; if (column.field.method === 'fieldset') { source.addImportDeclaration({ moduleSpecifier: `../../../${fieldset.name}/components/fields/${column.title}Field`, namedImports: [`${column.title}FieldsetControl`] }); return; } source.addImportDeclaration({ moduleSpecifier: `../../../${fieldset.name}/components/fields/${column.title}Field`, namedImports: [`${column.title}FieldControl`] }); }); source.addTypeAlias({ isExported: true, name: `${column.title}Config`, type: `{ type?: string, values?: (${column.title}Input|undefined)[], index: number, set: (values: (${column.title}Input|undefined)[]) => void }` }); source.addTypeAlias({ isExported: true, name: `${column.title}FieldsetProps`, type: `FieldsetProps<${column.title}Input>` }); source.addFunction({ isExported: true, name: `use${column.title}Fieldset`, parameters: [ { name: 'config', type: `${column.title}Config` } ], statements: (` //props const { values, index, set } = config; //handlers const handlers = { update: (value: string) => { const newValues = [ ...(values || []) ] newValues[index] = value; set(newValues); }, remove: () => { const newValues = [ ...(values || []) ]; newValues[index] = undefined; set(newValues); } }; return { handlers }; `) }); source.addFunction({ isExported: true, name: `${column.title}Fields`, parameters: [ { name: 'props', type: `FieldsProps<${column.title}Input>` } ], statements: (` const { name, config, values, index, error, set, limit } = props; const { _ } = useLanguage(); //handlers const { handlers } = use${column.title}Fieldset({ values, index, set }); //variables const prefix = !limit && name ? \`\${name}[\${index}]\` : limit && name ? \`\${name}\` : undefined; const value = values ? values[index]: undefined; const border = error ? 'theme-bc-error' : 'theme-bc-bd2'; return ( <div className={\`\${border} border relative px-my-10\`}> {!limit ? ( <header className="theme-bg-bg1 flex items-center px-p-10"> <h6 className="flex-grow"> {_('${fieldset.singular} %s', index + 1)} </h6> <a onClick={handlers.remove} className="theme-error px-fs-20 cursor-pointer" > &times; </a> </header> ) : !config.required ? ( <header className="absolute px-r-10 px-t-5"> <a onClick={handlers.remove} className="theme-error px-fs-20 cursor-pointer" > &times; </a> </header> ) : null} <main className="px-p-10"> ${fieldset.fields.map(column => { return column.field.method === 'fieldset' ? (` <${column.title}FieldsetControl className="px-py-10" name={prefix ? \`\${prefix}[${column.name}]\` : undefined} value={value.${column.name}} /> `) : column.field.component ? (` <${column.title}FieldControl className="px-py-10" name={prefix ? \`\${prefix}[${column.name}]\` : undefined} value={value.${column.name}} /> `) : false; }).filter(Boolean).join('\n')} </main> </div> ); `) }); source.addVariableStatement({ declarationKind: VariableDeclarationKind.Const, declarations: [{ name: 'Fieldset', initializer: `make<${column.title}Input>(${column.title}Fields)`, }] }); source.addFunction({ isExported: true, name: `${column.title}Fieldset`, parameters: [ { name: 'props', type: `${column.title}FieldsetProps` } ], statements: (` //config gets passed straight to the fields const config = { required: ${column.required ? 'true' : 'false'} }; const add = 'Add ${fieldset.singular}'; const limit = ${column.multiple ? '0' : '1'}; const defaults = ${JSON.stringify(fieldset.defaults)}; const value = ${column.multiple ? 'props.value || []' : column.required ? 'props.value || [defaults]' : 'props.value || []'} //render return ( <Fieldset {...props} add={add} limit={limit} config={config} value={value} emptyValue={defaults} /> ); `) }); source.addFunction({ isExported: true, name: `${column.title}FieldsetControl`, parameters: [ { name: 'props', type: 'ControlProps' } ], statements: (` //props const { className, name, value, change, error } = props; //hooks const { _ } = useLanguage(); //determine label const label = ${column.required && !column.multiple ? `\`\${_('${column.label}')}*\`;` : `_('${column.label}');`} //render return ( <Control label={label} error={error} className={className}> <${column.title}Fieldset error={!!error} name={name} value={value} /> </Control> ); `) }); } export function generateBoolean(directory, fieldset, column) { if (typeof column.field.component !== 'string') return; const path = `${fieldset.name}/components/fields/${column.title}Field.tsx`; const source = directory.createSourceFile(path, '', { overwrite: true }); source.addImportDeclaration({ isTypeOnly: true, moduleSpecifier: 'stackpress/view/client', namedImports: ['FieldProps', 'ControlProps'] }); source.addImportDeclaration({ moduleSpecifier: 'r22n', namedImports: ['useLanguage'] }); source.addImportDeclaration({ moduleSpecifier: 'frui/form/Control', defaultImport: 'Control' }); source.addImportDeclaration({ moduleSpecifier: `frui/field/${column.field.component}`, defaultImport: column.field.component }); source.addFunction({ isExported: true, name: `${column.title}Field`, parameters: [ { name: 'props', type: 'FieldProps' } ], statements: (` //props const { className, name = '${column.name}${column.multiple ? '[]' : ''}', value, change, error = false } = props; const attributes = ${JSON.stringify(column.field.attributes)}; //render return ( <${column.field.component} {...attributes} name={name} className={className} error={error} defaultValue="1" defaultChecked={!!value} onUpdate={value => change && change('${column.name}${column.multiple ? '[]' : ''}', value)} /> ); `) }); source.addFunction({ isExported: true, name: `${column.title}FieldControl`, parameters: [ { name: 'props', type: 'ControlProps' } ], statements: (` //props const { className, name, value, change, error } = props; //hooks const { _ } = useLanguage(); //determine label const label = ${column.required && !column.multiple ? `\`\${_('${column.label}')}*\`;` : `_('${column.label}');`} //render return ( <Control label={label} error={error} className={className}> <input type="hidden" name="${column.name}" value="0" /> <${column.title}Field error={!!error} name={name} value={value} change={change} /> </Control> ); `) }); } export function generateField(directory, fieldset, column) { if (typeof column.field.component !== 'string') return; const path = `${fieldset.name}/components/fields/${column.title}Field.tsx`; const source = directory.createSourceFile(path, '', { overwrite: true }); source.addImportDeclaration({ isTypeOnly: true, moduleSpecifier: 'stackpress/view/client', namedImports: ['FieldProps', 'ControlProps'] }); source.addImportDeclaration({ moduleSpecifier: 'r22n', namedImports: ['useLanguage'] }); source.addImportDeclaration({ moduleSpecifier: 'frui/form/Control', defaultImport: 'Control' }); source.addImportDeclaration({ moduleSpecifier: `frui/field/${column.field.component}`, defaultImport: column.field.component }); source.addFunction({ isExported: true, name: `${column.title}Field`, parameters: [ { name: 'props', type: 'FieldProps' } ], statements: (` //props const { className, name = '${column.name}${column.multiple ? '[]' : ''}', value, change, error = false } = props; const attributes = ${JSON.stringify(column.field.attributes)}; //render return ( <${column.field.component} {...attributes} name={name} className={className} error={error} defaultValue={value} onUpdate={value => change && change('${column.name}${column.multiple ? '[]' : ''}', value)} /> ); `) }); source.addFunction({ isExported: true, name: `${column.title}FieldControl`, parameters: [ { name: 'props', type: 'ControlProps' } ], statements: (` //props const { className, name, value, change, error } = props; //hooks const { _ } = useLanguage(); //determine label const label = ${column.required && !column.multiple ? `\`\${_('${column.label}')}*\`;` : `_('${column.label}');`} //render return ( <Control label={label} error={error} className={className}> <${column.title}Field error={!!error} name={name} value={value} change={change} /> </Control> ); `) }); }