@dumbbell/stsc
TypeScript icon, indicating that this package has built-in type declarations

2.0.15 • Public • Published

Dumbbell

低代码二次开发工具,将阿里低代码引擎的 schema,转换为 react 函数组件并且可进行二次开发的源码,源码将脱离低代码引擎成为 ”干净的“ react 项目。

项目代码是基于 rush+pnpm 的多包项目,请参考:

存在的意义

低代码引擎导出的源码存在如下问题:

  • 导出的源码,绝大部分代码对于二次开发来说都是都是黑盒
  • 导出的源码时基于 class 的 react 组件且 react 版本是 16.x
  • 导出的源码项目,使用 yarn 或者 pnpm 初始化会报错,只能使用 npm
  • 要运行导出的源码项目,就必须依赖低代码引擎相关的依赖,并不是真正意义上的“源码项目”

使用方法

目前处于开发阶段功能还不稳定,此期间会频发布

$ npx stsc

配置文件

import { resolve } from 'node:path';
import { defineConfig } from '../packages/stsc/src';

export default defineConfig({
  schemaFile: './lc-config/schema.json',
  assetsFile: './lc-config/assets.json',
  entryHtmlFile: './index.html',
});
  • 从 scheam
[
  {
    "componentName": "Page",
    "props": {
      "ref": "outerView",
      "style": {
        "height": "100%"
      }
    },
    "state": {
      "text": {
        "type": "JSExpression",
        "value": "\"outer\""
      },
      "isShowDialog": {
        "type": "JSExpression",
        "value": "false"
      }
    },
    "lifeCycles": {
      "componentDidMount": {
        "type": "JSFunction",
        "value": "function componentDidMount() {\n  console.log('did mount');const {} = state;\n}"
      },
      "componentWillUnmount": {
        "type": "JSFunction",
        "value": "function componentWillUnmount() {\n  console.log('will unmount');\n}"
      }
    },
    "methods": {
      "onClick": {
        "type": "JSFunction",
        "value": "function onClick() {\n  this.setState({\n    isShowDialog: true\n  });\n}"
      },
      "closeDialog": {
        "type": "JSFunction",
        "value": "function closeDialog() {\n  this.setState({\n    isShowDialog: false\n  });\n}"
      }
    },
    "children": [
      {
        "componentName": "Button",
        "props": {
          "type": "primary",
          "children": "主按钮",
          "htmlType": "button",
          "size": "middle",
          "shape": "default",
          "block": false,
          "danger": false,
          "ghost": false,
          "disabled": false,
          "onClick": {
            "type": "JSExpression",
            "value": "onClick"
          }
        }
      },
      {
        "componentName": "Modal",
        "props": {
          "visible": {
            "type": "JSExpression",
            "value": "isShowDialog"
          },
          "onCancle": {
            "type": "JSExpression",
            "value": "closeDialog"
          }
        }
      },
      {
        "componentName": "Progress",
        "props": {
          "percent": 20,
          "status": "active",
          "type": "line",
          "showInfo": false,
          "steps": 0,
          "strokeLinecap": "round",
          "strokeWidth": 0
        }
      }
    ]
  }
]
  • 到源码

component.tsx

import React, { useEffect, forwardRef, useImperativeHandle } from 'react';
import { Button, Progress, Modal } from 'antd';
import { useInternalReducer } from './use-internal-reducer';

export const Page = forwardRef((props: any, ref) => {
  const { style } = props;
  const [state, setState] = useInternalReducer();
  const { text, isShowDialog } = state;

  function onClick() {
    setState({
      isShowDialog: true,
    });
  }

  function closeDialog() {
    setState({
      isShowDialog: false,
    });
  }

  useEffect(() => {
    const componentDidMount = () => {
      console.log('did mount');
      const {} = state;
    };

    componentDidMount();
    return () => {};
  }, []);

  useEffect(() => {
    const componentWillUnmount = () => {
      console.log('will unmount');
    };

    return () => {
      componentWillUnmount();
    };
  }, []);

  useImperativeHandle(ref, () => {});

  return (
    <div style={style}>
      <Button
        type="primary"
        children="主按钮"
        htmlType="button"
        size="middle"
        shape="default"
        block={false}
        danger={false}
        ghost={false}
        disabled={false}
        onClick={onClick}
      />
      <Modal visible={isShowDialog} onCancel={closeDialog} />
      <Progress percent={20} status="active" type="line" showInfo={false} steps={0} strokeLinecap="round" strokeWidth={0} />
    </div>
  );
});

export default Page;

use-internal-state.ts

import { useReducer } from 'react';

const initialState = { text: 'outer', isShowDialog: false };

const reducer = (state, action) => {
  const { type, ...resetState } = action;
  switch (type) {
    case 'update':
      return { ...state, ...resetState };
    default:
      throw '未定义此类型的操作';
  }
};
export const useInternalReducer = () => {
  const [state, dispatch] = useReducer(reducer, initialState);
  return [state, (newState) => dispatch({ type: 'update', ...newState })];
};

index.tsx

import React, { useRef } from 'react';
import Page from './component';

export default (props) => {
  const ref = useRef();
  return <Page ref={ref} style={{ height: '100%' }} {...props} />;
};

测试

使用Vitest作为测试框架,可以查看测试用例了解功能:

$ cd packages/stsc
$ rushx test

示例

示例代码目录 example,可以在此项目进行调试。

待办列表

  • 加载 stsc 的配置文件

    • √ 嗅探 stsc.config.ts
    • √ 加载 schema.json
    • √ 注册 assets.json
      • √ 解析远端assets.packages并可配置是否自动下载依赖
      • √ 解析本地assets.packages
  • √ 将 schema.state 解析为 useReducer

  • √ 将 schema.methods 解析为函数组件中的方法

  • √ 解析 schema.children

  • √ 转换 componentDidMount

  • √ 解析 schema.props

    • √ 根节点 props,处理 ref
    • √ 非根节点 props
    • √ 解析 JSONValue 类型属性
    • √ 解析 JSSlot 类型属性
    • √ 解析 JSExpression 类型属性
    • √ 解析 JSFunction 类型属性
    • 递归解析以上属性类型(嵌套)
  • schema.lifeCycles 解析为 useEffect

    • √ 转换 componentWillUnmount
    • √ 转换 componentWillMount
    • 转换 shouldComponentUpdate
    • 转换 componentDidUpdate
  • 解析 schema.dataSource

    • √ 并行请求(async)、串行请求(sync)
    • 是否是初始化是请求数据
    • fetch 初始超时
    • 所有请求完成之后调用钩子
    • 请求失败调用 error 钩子
  • 解析 schema.css

  • 基于 schema.chunk 拆分 component 代码

  • 完善脚手架项目管理流程

  • 完善代码 lint 及格式化

Package Sidebar

Install

npm i @dumbbell/stsc

Weekly Downloads

1

Version

2.0.15

License

MIT

Unpacked Size

64.3 kB

Total Files

36

Last publish

Collaborators

  • fantienan