kdx
Version:
kintone CLI for development & deployment, with Developer Experience
220 lines (200 loc) • 7.6 kB
text/typescript
// Copyright (c) 2020 Shellyl_N and Authors
// license: MIT
// https://github.com/shellyln
import { promisify } from 'util';
import * as fs from 'fs';
import * as path from 'path';
import { escapeString } from './index';
const readFile = promisify(fs.readFile);
const getRawFields = (interfaceComment: string, interfaceName: string, fields: object, nestLv: number) => {
let code = '';
let subTableCode = '';
const indent = ''.padEnd((nestLv + 1) * 4);
const indent2 = ''.padEnd((nestLv + 2) * 4);
const keys = Object.keys(fields);
OUTER: for (const k of keys) {
let isArray = false;
let typeName = 'any';
let decorators = '';
const field = fields[k];
switch (field.type) {
case 'STATUS':
typeName = 'string';
break;
case 'CATEGORY':
typeName = 'string';
break;
case 'NUMBER':
decorators += `@match(/^[+-]?(?:[1-9][0-9]*|0)(?:\\.[0-9]+)?(?:[Ee][+-]?[0-9]+)?$/)`;
typeName = 'string';
break;
case 'SINGLE_LINE_TEXT':
typeName = 'string';
break;
case 'MULTI_LINE_TEXT':
typeName = 'string';
break;
case 'RICH_TEXT':
typeName = 'string';
break;
case 'LINK':
typeName = 'string';
break;
case 'DATE':
decorators += `@stereotype('lcdate')`;
typeName = 'string';
break;
case 'DATETIME':
decorators += `@stereotype('lcdatetime')`;
typeName = 'string';
break;
case 'TIME':
decorators += `@match(/^[0-2]\\d:[0-5]\\d$/)`;
typeName = 'string';
break;
case 'RADIO_BUTTON': case 'DROP_DOWN':
typeName = Object.keys(field.options)
.map(x => `'${escapeString(x)}'`).join(' | ');
break;
case 'CHECK_BOX': case 'MULTI_SELECT':
isArray = true;
typeName = `(${Object.keys(field.options)
.map(x => `'${escapeString(x)}'`).join(' | ')})[${field.required ? ', 1..' : ''}]`;
break;
case 'USER_SELECT':
isArray = true;
typeName = `Array<{code: string, name: string}${field.required ? ', 1..' : ''}>`;
break;
case 'GROUP_SELECT':
isArray = true;
typeName = `Array<{code: string, name: string}${field.required ? ', 1..' : ''}>`;
break;
case 'ORGANIZATION_SELECT':
isArray = true;
typeName = `Array<{code: string, name: string}${field.required ? ', 1..' : ''}>`;
break;
case 'SUBTABLE':
{
isArray = true;
typeName = field.code[0].toUpperCase() + field.code.slice(1);
const sub = getRawFields(field.label, typeName, field.fields, nestLv);
subTableCode += sub.subTableCode + sub.code;
typeName = `${typeName}[${field.required ? ', 1..' : ''}]`;
}
break;
case 'CALC':
typeName = 'string';
break;
case 'FILE':
isArray = true;
typeName = `Array<{contentType: string, fileKey: string, name: string, size: string}>`;
break;
case 'STATUS_ASSIGNEE':
isArray = true;
typeName = `Array<{code: string, name: string}>`;
break;
// system fields
case 'RECORD_NUMBER':
typeName = 'string';
break;
case 'CREATED_TIME':
decorators += `@stereotype('lcdatetime')`;
typeName = 'string';
break;
case 'CREATOR':
typeName = `{code: string, name: string}`;
break;
case 'UPDATED_TIME':
decorators += `@stereotype('lcdatetime')`;
typeName = 'string';
break;
case 'MODIFIER':
typeName = `{code: string, name: string}`;
break;
case '__ID__':
typeName = 'string';
break;
case '__REVISION__':
typeName = 'string';
break;
// UI elements (no record fields)
case 'REFERENCE_TABLE':
case 'GROUP':
default:
continue OUTER;
}
if (field.maxValue) {
let v = '';
if (field.type === 'NUMBER') {
v = field.maxValue;
} else {
v = `'${escapeString(field.maxValue)}'`;
}
decorators += `${decorators ? ' ' : ''}@maxValue(${v})`;
}
if (field.minValue) {
let v = '';
if (field.type === 'NUMBER') {
v = field.minValue;
} else {
v = `'${escapeString(field.minValue)}'`;
}
decorators += `${decorators ? ' ' : ''}@minValue(${v})`;
}
if (field.maxLength) {
let v = '';
v = field.maxLength;
decorators += `${decorators ? ' ' : ''}@maxLength(${v})`;
}
if (field.minLength) {
let v = '';
v = field.minLength;
decorators += `${decorators ? ' ' : ''}@minLength(${v})`;
}
code += `${code ? '\n' : ''}${
indent}/** ${field.label} */\n${indent}@meta({fieldType:'${escapeString(field.type)}'})\n${
indent}${/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(field.code) ? field.code : `'${escapeString(field.code)}'`}${
(isArray || field.required) ? '' : '?'}: {\n${
indent2}type: '${escapeString(field.type)}';\n${
indent2}${decorators ? decorators + '\n' + indent2 : ''}value: ${typeName};\n${
field.type === 'SINGLE_LINE_TEXT' || field.type === 'NUMBER' ?
`${indent2}lookup?: boolean | 'UPDATE';\n` :
''}${
indent2}error?: string;\n${
indent2}disabled?: boolean;\n${
indent}};\n`;
}
code = `\n\n${interfaceComment ? `/** ${interfaceComment} */\n` : ''}export interface ${interfaceName} {\n${code}}\n\n`;
return ({subTableCode, code});
};
const appendSystemFields = (fields: any) => {
if (! fields.$id) {
fields.$id = {
type: "__ID__",
code: "$id",
label: "$id",
enabled: false,
required: false,
};
}
if (! fields.$revision) {
fields.$revision = {
type: "__REVISION__",
code: "$revision",
label: "$revision",
enabled: false,
required: false,
};
}
return fields;
}
export const generateRawAppSchema = async (projectDir: string, appName: string) => {
// app.json
const metaApp = JSON.parse(await readFile(path.join(projectDir, 'meta/apps', appName, 'app.json'), { encoding: 'utf8' }));
// form/fields.json
const metaFields = JSON.parse(await readFile(path.join(projectDir, 'meta/apps', appName, 'form/fields.json'), { encoding: 'utf8' }));
const fields = metaFields.properties;
appendSystemFields(fields);
const { subTableCode, code } = getRawFields(metaApp.name, 'RawApp', fields, 0);
return '// @ts-nocheck\n\n' + subTableCode + code;
};