index.ts 7.72 KB
import { join } from 'path';
import { pathExists, ensureFile, writeFile } from 'fs-extra';
import { NodeItemConfigType } from '/@/views/rule/designer/types/node';
import { camelCase, upperFirst, snakeCase } from 'lodash-es';
import { components } from './components';
type GroupNodeType = { [key: string]: NodeItemConfigType[] };

const RULE_CHAIN_FILE_PATH = join(process.cwd(), '/src/views/rule/designer');

const list: NodeItemConfigType[] = components;

const getCategoryConfigName = (name: string) => {
  return `${upperFirst(camelCase(name))}CategoryConfig`;
};

const getNodeConfigName = (name: string) => {
  return `${upperFirst(camelCase(name))}Config`;
};

const getCagegoryEnumName = (name: string) => {
  return `${upperFirst(name.toLowerCase())}CategoryComponentEnum`;
};

const getEnumKeyName = (name: string) => {
  return snakeCase(name).toUpperCase();
};

const createFile = async (fileName: string, fileContent?: string, replace = false) => {
  const path = join(RULE_CHAIN_FILE_PATH, './packages', fileName);

  const flag = await pathExists(path);

  if (flag && !replace) return false;

  await ensureFile(path);

  fileContent && (await writeFile(path, fileContent, { encoding: 'utf-8' }));
};

const groupByType = () => {
  const group: { [key: string]: NodeItemConfigType[] } = {};

  list.forEach((item) => {
    if (!group[item.type]) group[item.type] = [];
    group[item.type].push(item);
  });

  return group;
};

const generateCategoryEnumFile = async (data: GroupNodeType) => {
  const defaultContent = `
export enum EntryCategoryComponentEnum {
  INPUT = 'Input',
}
  `;

  const fileContent = Object.keys(data).reduce((prev, next) => {
    const enumName = getCagegoryEnumName(next);

    const enumKeys = data[next].map((item) => getEnumKeyName(item.name));

    const content = `export enum ${enumName} {
        ${enumKeys.map((name) => `${name} = '${upperFirst(camelCase(name))}'`)}
    }`;

    return `${prev} \n ${content}`;
  }, defaultContent);

  createFile('../enum/category.ts', fileContent, true);
  return fileContent;
};

const generateRuleNodeEnumFile = async (data: GroupNodeType) => {
  const categoryKeys = Object.keys(data).map((type) => type.toUpperCase());
  const filePath = join(RULE_CHAIN_FILE_PATH, './packages/index.type.ts');
  const fileContent = `
export enum RuleNodeTypeEnum {
  ${categoryKeys.map((item) => `${item} = '${item}'`).join(',\n')}
}  
  `;

  await writeFile(filePath, fileContent, {
    encoding: 'utf-8',
  });

  return fileContent;
};

const generateCategoryIndexFile = async (type: string, data: NodeItemConfigType[]) => {
  const getComponentsName = data.map((temp) => `${upperFirst(camelCase(temp.name))}`);
  const importContent = getComponentsName.map(
    (name) => `import { ${name}Config } from './${name}';\n`
  );

  const components = getComponentsName.map((item) => `${item}Config`);

  const content = `import type { CategoryConfigType, NodeItemConfigType } from '../../types/node';
import { RuleNodeTypeEnum } from '../index.type';
${importContent.join('')}

export const ${getCategoryConfigName(type)}: CategoryConfigType = {
  category: RuleNodeTypeEnum.${type.toUpperCase()},
  title: '${type}',
  icon: 'tabler:circuit-ground',
  description: '使用配置条件筛选传入消息',
};

export const ${upperFirst(type.toLowerCase())}Components: NodeItemConfigType[] = [${components}];
`;

  createFile(`./${upperFirst(type.toLowerCase())}/index.ts`, content, true);

  return content;
};

const generateNodeIndexFile = async (type: string, data: NodeItemConfigType) => {
  const categoryEnumName = getCagegoryEnumName(type);

  const nodeConfigName = getNodeConfigName(data.name);

  const content = `
  import { ${categoryEnumName} } from '../../../enum/category';
  import { useCreateNodeKey } from '../../../hook/useCreateNodeKey';
  import type { NodeItemConfigType } from '../../../types/node';
  import { RuleNodeTypeEnum } from '../../index.type';

  const keys = useCreateNodeKey(${categoryEnumName}.${getEnumKeyName(data.name)});

  export interface ${upperFirst(camelCase(data.name))}DataType {
    someConfiguration?: Recordable
  }

  export const ${nodeConfigName}: NodeItemConfigType = {
    ...keys,
    clazz: '${data.clazz}',
    categoryType: RuleNodeTypeEnum.${type.toUpperCase()},
    name: '${data.name}',
    configurationDescriptor: ${JSON.stringify(data.configurationDescriptor, null, 2)}
  };
  `;

  createFile(
    `./${upperFirst(type.toLowerCase())}/${upperFirst(camelCase(data.name))}/index.ts`,
    content
  );

  return content;
};

const generateNodeVueTemplateFile = async (type: string, data: NodeItemConfigType) => {
  const content = `
  <script lang="ts" setup>
    import type { CreateModalDefineExposeType } from '../../../types';
    import { BasicForm, useForm } from '/@/components/Form';
    import { formSchemas } from './create.config';
    import { NodeData } from '../../../types/node';

    defineProps<{
      config: NodeData;
    }>();

    const [register, { validate, getFieldsValue, setFieldsValue, resetFields }] = useForm({
      schemas: formSchemas,
      showActionButtonGroup: false,
    });

    const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => {
      await validate();
      const value = getFieldsValue() || {};
      return value;
    };

    const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => {
      resetFields();
      setFieldsValue(value);
    };

    defineExpose({
      setFieldsValue: setValue,
      getFieldsValue: getValue,
    } as CreateModalDefineExposeType);
  </script>

  <template>
    <BasicForm @register="register" />
  </template>
  `;

  createFile(
    `./${upperFirst(type.toLowerCase())}/${upperFirst(camelCase(data.name))}/create.vue`,
    content
  );
  return content;
};

const generateNodeVueConfigFile = async (type: string, data: NodeItemConfigType) => {
  const content = `
  import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node';
  import { FormSchema } from '/@/components/Form';

  export const formSchemas: FormSchema[] = [
    {
      field: NodeBindDataFieldEnum.NAME,
      component: 'Input',
      label: NodeBindDataFieldNameEnum.NAME,
    }
  ];
  `;

  createFile(
    `./${upperFirst(type.toLowerCase())}/${upperFirst(camelCase(data.name))}/create.config.ts`,
    content
  );
  return content;
};

const generateNodeConfigFile = async (type: string, data: NodeItemConfigType) => {
  const nodeConfigName = getNodeConfigName(data.name);
  const categoryConfigName = getCategoryConfigName(type);
  const content = `
  import { cloneDeep } from 'lodash-es';
  import { PublicNodeItemClass } from '../../../types/node';
  import type {
    CategoryConfigType,
    CreateComponentType,
    NodeItemConfigType,
  } from '../../../types/node';
  import { ${categoryConfigName} } from '..';
  import { ${nodeConfigName} } from '.';

  export class Config extends PublicNodeItemClass implements CreateComponentType {
    public config: NodeItemConfigType = cloneDeep(${nodeConfigName});

    public categoryConfig: CategoryConfigType = cloneDeep(${categoryConfigName});

    constructor() {
      super();
    }
  }
  `;

  createFile(
    `./${upperFirst(type.toLowerCase())}/${upperFirst(camelCase(data.name))}/config.ts`,
    content
  );
  return content;
};

const bootstrap = async () => {
  const groupData = groupByType();
  await generateRuleNodeEnumFile(groupData);
  await generateCategoryEnumFile(groupData);
  for (const type of Object.keys(groupData)) {
    const item = groupData[type];
    await generateCategoryIndexFile(type, item);
    for (const temp of item) {
      await generateNodeConfigFile(type, temp);
      await generateNodeIndexFile(type, temp);
      await generateNodeVueConfigFile(type, temp);
      await generateNodeVueTemplateFile(type, temp);
    }
  }
};

bootstrap();