mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-06-03 14:11:10 +00:00
feat: 增加 material-parser 包
This commit is contained in:
parent
a52fcafc45
commit
a802418d65
370
packages/material-parser/CHANGELOG.md
Normal file
370
packages/material-parser/CHANGELOG.md
Normal file
@ -0,0 +1,370 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
<a name="1.0.22"></a>
|
||||
## [1.0.22](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.21...@ali/lowcode-material-parser@1.0.22) (2020-11-16)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="1.0.21"></a>
|
||||
## [1.0.21](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.20...@ali/lowcode-material-parser@1.0.21) (2020-11-10)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="1.0.20"></a>
|
||||
## [1.0.20](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.19...@ali/lowcode-material-parser@1.0.20) (2020-11-10)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="1.0.19"></a>
|
||||
## [1.0.19](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.18...@ali/lowcode-material-parser@1.0.19) (2020-11-05)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="1.0.18"></a>
|
||||
## [1.0.18](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.17...@ali/lowcode-material-parser@1.0.18) (2020-11-05)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="1.0.17"></a>
|
||||
## [1.0.17](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.16...@ali/lowcode-material-parser@1.0.17) (2020-11-05)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="1.0.16"></a>
|
||||
## [1.0.16](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.15...@ali/lowcode-material-parser@1.0.16) (2020-11-04)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="1.0.15"></a>
|
||||
## [1.0.15](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.13...@ali/lowcode-material-parser@1.0.15) (2020-11-04)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="1.0.14"></a>
|
||||
## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.13...@ali/lowcode-material-parser@1.0.14) (2020-11-04)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="1.0.13"></a>
|
||||
## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.12...@ali/lowcode-material-parser@1.0.13) (2020-11-02)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="1.0.12"></a>
|
||||
## [1.0.12](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.11...@ali/lowcode-material-parser@1.0.12) (2020-10-20)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* support params & returns of func propType ([0e46e49](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/0e46e49))
|
||||
* use parseJsDoc to parse propType docblock ([0b80be6](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/0b80be6))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.0.11"></a>
|
||||
## [1.0.11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.10...@ali/lowcode-material-parser@1.0.11) (2020-10-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix typescript related bugs, including the following: ([d4c45d2](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/d4c45d2))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.0.10"></a>
|
||||
## [1.0.10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.9...@ali/lowcode-material-parser@1.0.10) (2020-09-29)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="1.0.9"></a>
|
||||
## [1.0.9](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.8...@ali/lowcode-material-parser@1.0.9) (2020-09-28)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="1.0.8"></a>
|
||||
## [1.0.8](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.8-0...@ali/lowcode-material-parser@1.0.8) (2020-09-28)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="1.0.8-0"></a>
|
||||
## [1.0.8-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.7-0...@ali/lowcode-material-parser@1.0.8-0) (2020-09-09)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="1.0.7-0"></a>
|
||||
## [1.0.7-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.6-0...@ali/lowcode-material-parser@1.0.7-0) (2020-09-02)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="1.0.6-0"></a>
|
||||
## [1.0.6-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.5-0...@ali/lowcode-material-parser@1.0.6-0) (2020-09-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add root field to material parser options ([c6724e9](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/c6724e9))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.0.5-0"></a>
|
||||
## [1.0.5-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.4-0...@ali/lowcode-material-parser@1.0.5-0) (2020-08-20)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="1.0.4-0"></a>
|
||||
## [1.0.4-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.3-0...@ali/lowcode-material-parser@1.0.4-0) (2020-08-20)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="1.0.3-0"></a>
|
||||
## [1.0.3-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.2-0...@ali/lowcode-material-parser@1.0.3-0) (2020-08-20)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="1.0.2-0"></a>
|
||||
## [1.0.2-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.1-0...@ali/lowcode-material-parser@1.0.2-0) (2020-08-20)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="1.0.1-0"></a>
|
||||
## [1.0.1-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.0...@ali/lowcode-material-parser@1.0.1-0) (2020-08-20)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="1.0.0"></a>
|
||||
# [1.0.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@0.15.0...@ali/lowcode-material-parser@1.0.0) (2020-08-17)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="0.15.0"></a>
|
||||
# [0.15.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@0.14.0...@ali/lowcode-material-parser@0.15.0) (2020-08-17)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="0.14.0"></a>
|
||||
# [0.14.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@0.12.0...@ali/lowcode-material-parser@0.14.0) (2020-08-17)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="0.13.0"></a>
|
||||
# [0.13.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@0.12.0...@ali/lowcode-material-parser@0.13.0) (2020-08-17)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="0.12.0"></a>
|
||||
# [0.12.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@0.11.0...@ali/lowcode-material-parser@0.12.0) (2020-08-16)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="0.11.0"></a>
|
||||
# [0.11.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@0.10.0...@ali/lowcode-material-parser@0.11.0) (2020-08-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* support typescript & dynamic parsing in material parser ([6168ef5](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/6168ef5))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="0.10.0"></a>
|
||||
# [0.10.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@0.9.3...@ali/lowcode-material-parser@0.10.0) (2020-08-14)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="0.9.2"></a>
|
||||
## [0.9.2](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@0.9.1...@ali/lowcode-material-parser@0.9.2) (2020-05-07)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="0.9.0"></a>
|
||||
# [0.9.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@0.8.4...@ali/lowcode-material-parser@0.9.0) (2020-03-31)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [material-parser]fix bug of main field & remove useless debugger ([8fde0ec](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/8fde0ec))
|
||||
* fix bug of build errors ([770a1b6](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/770a1b6))
|
||||
* fix bug of missing types in material-parser ([9ce0a73](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/9ce0a73))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* 🎸 support parsing fusion source code ([5895cf1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/5895cf1))
|
||||
* support localizing ([e1faa84](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/e1faa84))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="0.8.4"></a>
|
||||
## [0.8.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@0.8.3...@ali/lowcode-material-parser@0.8.4) (2020-03-30)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="0.8.3"></a>
|
||||
## [0.8.3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@0.8.2...@ali/lowcode-material-parser@0.8.3) (2020-03-30)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-material-parser
|
||||
|
||||
<a name="0.8.2"></a>
|
||||
## 0.8.2 (2020-03-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 fix bug of transforming type ([ebbe58d](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/ebbe58d))
|
||||
* 🐛 fix bug of validate schema ([3f97523](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/3f97523))
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* 💡 refactor with react-docgen ([64c9daa](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/64c9daa))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* complete component protocol json schema & validate method ([3df360d](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/3df360d))
|
||||
* immigrate aimake materialin ([44ac85f](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/44ac85f))
|
||||
* import react-docgen to parse propTypes ([6e66168](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/6e66168))
|
||||
* remove -p tslint.json for test ([6d013e1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/6d013e1))
|
||||
* remove useless codes & modify generator ([dcd1b33](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/dcd1b33))
|
||||
* support multiple exported components ([db1b6de](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/db1b6de))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* 🧨 use react-docgen to replace parser
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="0.8.1"></a>
|
||||
## 0.8.1 (2020-03-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 fix bug of transforming type ([ebbe58d](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/ebbe58d))
|
||||
* 🐛 fix bug of validate schema ([3f97523](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/3f97523))
|
||||
* 🐛 fix bug of transforming type ([ebbe58d](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/ebbe58df70f047f4b5fe367ac4b4a08de8a65e5d))
|
||||
* 🐛 fix bug of validate schema ([3f97523](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/3f975232c7cd551bc9c74962095dcc9b127af489))
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* 💡 refactor with react-docgen ([64c9daa](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/64c9daa))
|
||||
* 💡 refactor with react-docgen ([64c9daa](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/64c9daa1f451fdfeab2777e4beefc5d5e1890ba1))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* complete component protocol json schema & validate method ([3df360d](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/3df360d))
|
||||
* immigrate aimake materialin ([44ac85f](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/44ac85f))
|
||||
* import react-docgen to parse propTypes ([6e66168](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/6e66168))
|
||||
* remove -p tslint.json for test ([6d013e1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/6d013e1))
|
||||
* remove useless codes & modify generator ([dcd1b33](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/dcd1b33))
|
||||
* support multiple exported components ([db1b6de](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/db1b6de))
|
||||
* complete component protocol json schema & validate method ([3df360d](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/3df360de85d425b2926ea50ff26a8df27ec36a78))
|
||||
* immigrate aimake materialin ([44ac85f](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/44ac85f8a6a35bcd50f2e2b74a022e3cebe3cdef))
|
||||
* import react-docgen to parse propTypes ([6e66168](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/6e661686e4693e69279c496f3be1dd173703c55e))
|
||||
* remove -p tslint.json for test ([6d013e1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/6d013e18f93bad5647cb9ea0a497336f64e1459a))
|
||||
* remove useless codes & modify generator ([dcd1b33](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/dcd1b33d3bf8bdf5577dcc980608d9eac8d99372))
|
||||
* support multiple exported components ([db1b6de](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/db1b6deaca256b0d107fe607de6cd0fc90517a9c))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* 🧨 use react-docgen to replace parser
|
||||
16
packages/material-parser/README.md
Normal file
16
packages/material-parser/README.md
Normal file
@ -0,0 +1,16 @@
|
||||
# @ali/lowcode-material-parser
|
||||
|
||||
> 入料模块
|
||||
|
||||
本模块负责物料接入,能自动扫描、解析源码组件,并最终产出一份符合 **[《中后台搭建组件描述协议》](https://yuque.antfin-inc.com/rt656r/spec/pbeaqx)** 的 **JSON Schema**。
|
||||
|
||||
详见[文档](https://yuque.antfin-inc.com/ali-lowcode/docs/tyktrt)。
|
||||
|
||||
## demo
|
||||
|
||||
```shell
|
||||
cd demo
|
||||
node index.js
|
||||
```
|
||||
|
||||
## API
|
||||
5
packages/material-parser/build.test.json
Normal file
5
packages/material-parser/build.test.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"plugins": [
|
||||
"@ali/lowcode-test-mate/plugin/index.ts"
|
||||
]
|
||||
}
|
||||
81
packages/material-parser/demo/component.jsx
Normal file
81
packages/material-parser/demo/component.jsx
Normal file
@ -0,0 +1,81 @@
|
||||
/* eslint-disable react/forbid-prop-types,react/no-unused-prop-types */
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import './main.scss';
|
||||
|
||||
class Demo extends React.Component {
|
||||
render() {
|
||||
return <div> Test </div>;
|
||||
}
|
||||
}
|
||||
|
||||
Demo.propTypes = {
|
||||
optionalArray: PropTypes.array,
|
||||
optionalBool: PropTypes.bool,
|
||||
optionalFunc: PropTypes.func,
|
||||
optionalNumber: PropTypes.number,
|
||||
optionalObject: PropTypes.object,
|
||||
optionalString: PropTypes.string,
|
||||
optionalSymbol: PropTypes.symbol,
|
||||
|
||||
// Anything that can be rendered: numbers, strings, elements or an array
|
||||
// (or fragment) containing these types.
|
||||
optionalNode: PropTypes.node,
|
||||
|
||||
// A React element (ie. <MyComponent />).
|
||||
optionalElement: PropTypes.element,
|
||||
|
||||
// A React element type (ie. MyComponent).
|
||||
optionalElementType: PropTypes.elementType,
|
||||
|
||||
// You can also declare that a prop is an instance of a class. This uses
|
||||
// JS's instanceof operator.
|
||||
optionalMessage: PropTypes.instanceOf(Demo),
|
||||
|
||||
// You can ensure that your prop is limited to specific values by treating
|
||||
// it as an enum.
|
||||
optionalEnum: PropTypes.oneOf(['News', 'Photos']),
|
||||
|
||||
// An object that could be one of many types
|
||||
optionalUnion: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
PropTypes.instanceOf(Demo),
|
||||
]),
|
||||
|
||||
// An array of a certain type
|
||||
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
|
||||
|
||||
// An object with property values of a certain type
|
||||
optionalObjectOf: PropTypes.objectOf(PropTypes.number),
|
||||
|
||||
// You can chain any of the above with `isRequired` to make sure a warning
|
||||
// is shown if the prop isn't provided.
|
||||
|
||||
// An object taking on a particular shape
|
||||
optionalObjectWithShape: PropTypes.shape({
|
||||
optionalProperty: PropTypes.string,
|
||||
requiredProperty: PropTypes.number.isRequired,
|
||||
}),
|
||||
|
||||
optionalObjectWithShape2: PropTypes.shape({
|
||||
optionalProperty: PropTypes.string,
|
||||
requiredProperty: PropTypes.number.isRequired,
|
||||
}).isRequired,
|
||||
|
||||
// An object with warnings on extra properties
|
||||
optionalObjectWithStrictShape: PropTypes.exact({
|
||||
optionalProperty: PropTypes.string,
|
||||
requiredProperty: PropTypes.number.isRequired,
|
||||
}),
|
||||
|
||||
requiredFunc: PropTypes.func.isRequired,
|
||||
|
||||
// A value of any data type
|
||||
requiredAny: PropTypes.any.isRequired,
|
||||
};
|
||||
|
||||
Demo.defaultProps = {};
|
||||
|
||||
export default Demo;
|
||||
11
packages/material-parser/demo/index.js
Normal file
11
packages/material-parser/demo/index.js
Normal file
@ -0,0 +1,11 @@
|
||||
const parse = require('../lib').default;
|
||||
|
||||
(async () => {
|
||||
const options = {
|
||||
entry: './component.jsx',
|
||||
accesser: 'local',
|
||||
};
|
||||
|
||||
const actual = await parse(options);
|
||||
console.log(JSON.stringify(actual, null, 2));
|
||||
})();
|
||||
20
packages/material-parser/jest.config.js
Normal file
20
packages/material-parser/jest.config.js
Normal file
@ -0,0 +1,20 @@
|
||||
|
||||
module.exports = {
|
||||
transform: {
|
||||
'^.+\\.(ts|tsx|js|jsx)$': 'ts-jest',
|
||||
},
|
||||
testEnvironment: 'node',
|
||||
// testMatch: ['(/tests?/.*(test))\\.[jt]s$'],
|
||||
testTimeout: 1000000,
|
||||
testPathIgnorePatterns: [
|
||||
'/node_modules/',
|
||||
'test/fixtures',
|
||||
],
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
|
||||
collectCoverage: true,
|
||||
collectCoverageFrom: [
|
||||
'src/**/*.{ts,tsx}',
|
||||
'!**/node_modules/**',
|
||||
'!**/vendor/**',
|
||||
],
|
||||
};
|
||||
65
packages/material-parser/package.json
Normal file
65
packages/material-parser/package.json
Normal file
@ -0,0 +1,65 @@
|
||||
{
|
||||
"name": "@ali/lowcode-material-parser",
|
||||
"version": "1.0.45",
|
||||
"description": "material parser for Ali lowCode engine",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
"files": [
|
||||
"lib",
|
||||
"schemas"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@babel/runtime": "^7.11.2",
|
||||
"@types/debug": "^4.1.5",
|
||||
"@types/fs-extra": "^8.0.1",
|
||||
"@types/jest": "^26.0.18",
|
||||
"@types/js-yaml": "^3.12.2",
|
||||
"@types/lodash": "^4.14.149",
|
||||
"@types/node": "^14.6.0",
|
||||
"@types/prop-types": "^15.7.3",
|
||||
"copy-webpack-plugin": "^9.1.0",
|
||||
"copyfiles": "^2.4.1",
|
||||
"jest": "^26.6.3",
|
||||
"js-yaml": "^3.13.1",
|
||||
"json-schema-to-typescript": "^8.2.0",
|
||||
"ts-jest": "^26.0.0",
|
||||
"ts-loader": "^9.2.6",
|
||||
"ts-node": "^8.10.2",
|
||||
"tslib": "^1.11.1",
|
||||
"webpack": "^5.64.1",
|
||||
"webpack-cli": "^4.9.1",
|
||||
"webpack-node-externals": "^3.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"prebuild": "npm run schema && npm run bundle",
|
||||
"postbuild": "copyfiles -u 1 \"src/**/*.json\" lib/",
|
||||
"prepublishOnly": "npm run build",
|
||||
"test": "jest --verbose",
|
||||
"test:snapshot": "jest --updateSnapshot --verbose",
|
||||
"schema": "node ./scripts/transform.js",
|
||||
"bundle": "webpack"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^6.12.0",
|
||||
"ast-types": "^0.13.3",
|
||||
"cross-spawn-promise": "^0.10.2",
|
||||
"debug": "^4.1.1",
|
||||
"find-config": "^1.0.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"lodash": "^4.17.15",
|
||||
"parse-prop-types": "^0.3.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-docgen": "5.3.0",
|
||||
"react-docgen-typescript": "^1.16.5",
|
||||
"safe-eval": "^0.4.1",
|
||||
"short-uuid": "^3.1.1",
|
||||
"ts-polyfill": "^3.8.1-rc",
|
||||
"typescript": "3.9.4",
|
||||
"vm2": "^3.9.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npm.alibaba-inc.com"
|
||||
}
|
||||
}
|
||||
537
packages/material-parser/schemas/schema.json
Normal file
537
packages/material-parser/schemas/schema.json
Normal file
@ -0,0 +1,537 @@
|
||||
{
|
||||
"$id": "@ali/low-code-component-protocol-schema",
|
||||
"description": "json schema for low code component protocol",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/BasicSection"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/PropsSection"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/ConfigureSection"
|
||||
}
|
||||
],
|
||||
"definitions": {
|
||||
"BasicSection": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"componentName": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"docUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"screenshot": {
|
||||
"type": "string"
|
||||
},
|
||||
"icon": {
|
||||
"type": "string"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"devMode": {
|
||||
"enum": [
|
||||
"proCode",
|
||||
"lowCode"
|
||||
]
|
||||
},
|
||||
"npm": {
|
||||
"$ref": "#/definitions/Npm"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"componentName",
|
||||
"title",
|
||||
"npm",
|
||||
"docUrl",
|
||||
"screenshot"
|
||||
]
|
||||
},
|
||||
"PropsSection": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"props": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"propType": {
|
||||
"$ref": "#/definitions/PropType"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"defaultValue": {}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"propType"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ConfigureSection": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"configure": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"props": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ConfigureProp"
|
||||
}
|
||||
},
|
||||
"styles": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
},
|
||||
"events": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
},
|
||||
"component": {
|
||||
"$ref": "#/definitions/ConfigureComponent"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Npm": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"package": {
|
||||
"type": "string"
|
||||
},
|
||||
"exportName": {
|
||||
"type": "string"
|
||||
},
|
||||
"subName": {
|
||||
"type": "string"
|
||||
},
|
||||
"main": {
|
||||
"type": "string"
|
||||
},
|
||||
"destructuring": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"package",
|
||||
"exportName",
|
||||
"subName",
|
||||
"main",
|
||||
"destructuring",
|
||||
"version"
|
||||
]
|
||||
},
|
||||
"PropType": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/BasicType"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/RequiredType"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/ComplexType"
|
||||
}
|
||||
]
|
||||
},
|
||||
"BasicType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"array",
|
||||
"bool",
|
||||
"func",
|
||||
"number",
|
||||
"object",
|
||||
"string",
|
||||
"node",
|
||||
"element",
|
||||
"any"
|
||||
]
|
||||
},
|
||||
"RequiredType": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"$ref": "#/definitions/BasicType"
|
||||
},
|
||||
"isRequired": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"ComplexType": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/OneOf"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/OneOfType"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/ArrayOf"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/ObjectOf"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/Shape"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/Exact"
|
||||
}
|
||||
]
|
||||
},
|
||||
"OneOf": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"oneOf"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"isRequired": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"OneOfType": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"oneOfType"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/PropType"
|
||||
}
|
||||
},
|
||||
"isRequired": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ArrayOf": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"arrayOf"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/definitions/PropType"
|
||||
},
|
||||
"isRequired": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ObjectOf": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"objectOf"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/definitions/PropType"
|
||||
},
|
||||
"isRequired": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Shape": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"shape"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"propType": {
|
||||
"$ref": "#/definitions/PropType"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"isRequired": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ShapeItem": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"propType"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"propType": {
|
||||
"$ref": "#/definitions/PropType"
|
||||
},
|
||||
"isRequired": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Exact": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"exact"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"propType": {
|
||||
"$ref": "#/definitions/PropType"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"isRequired": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ConfigureProp": {
|
||||
"type": "object",
|
||||
"allOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"extraProps": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ConfigureFieldProp"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/ConfigureGroupProp"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ConfigureFieldProp": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"field"
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"setter": {
|
||||
"$ref": "#/definitions/ConfigureFieldSetter"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ConfigureFieldSetter": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"componentName"
|
||||
],
|
||||
"properties": {
|
||||
"componentName": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"List",
|
||||
"Object",
|
||||
"Function",
|
||||
"Node",
|
||||
"Mixin",
|
||||
"Expression",
|
||||
"Switch",
|
||||
"Number",
|
||||
"Input",
|
||||
"TextArea",
|
||||
"Date",
|
||||
"DateYear",
|
||||
"DateMonth",
|
||||
"DateRange",
|
||||
"ColorPicker",
|
||||
"CodeEditor",
|
||||
"Select",
|
||||
"RadioGroup"
|
||||
]
|
||||
},
|
||||
"props": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ConfigureGroupProp": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type",
|
||||
"items"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"group"
|
||||
]
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ConfigureProp"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ConfigureComponent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"isContainer": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isModal": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"descriptor": {
|
||||
"type": "string"
|
||||
},
|
||||
"nestingRule": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"childWhitelist": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"parentWhitelist": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"descendantBlacklist": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"ancestorWhitelist": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"isNullNode": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isLayout": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
351
packages/material-parser/schemas/schema.yml
Normal file
351
packages/material-parser/schemas/schema.yml
Normal file
@ -0,0 +1,351 @@
|
||||
$id: "@ali/low-code-component-protocol-schema"
|
||||
description: json schema for low code component protocol
|
||||
allOf:
|
||||
- $ref: "#/definitions/BasicSection"
|
||||
- $ref: "#/definitions/PropsSection"
|
||||
- $ref: "#/definitions/ConfigureSection"
|
||||
definitions:
|
||||
BasicSection:
|
||||
type: object
|
||||
properties:
|
||||
componentName:
|
||||
type: string
|
||||
title:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
docUrl:
|
||||
type: string
|
||||
screenshot:
|
||||
type: string
|
||||
icon:
|
||||
type: string
|
||||
tags:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
devMode:
|
||||
enum:
|
||||
- proCode
|
||||
- lowCode
|
||||
npm:
|
||||
$ref: "#/definitions/Npm"
|
||||
required:
|
||||
- componentName
|
||||
- title
|
||||
- npm
|
||||
PropsSection:
|
||||
type: object
|
||||
required:
|
||||
- props
|
||||
properties:
|
||||
props:
|
||||
type: array
|
||||
items:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
propType:
|
||||
$ref: "#/definitions/PropType"
|
||||
description:
|
||||
type: string
|
||||
defaultValue: {}
|
||||
required:
|
||||
- name
|
||||
- propType
|
||||
ConfigureSection:
|
||||
type: object
|
||||
properties:
|
||||
configure:
|
||||
type: object
|
||||
properties:
|
||||
props:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/definitions/ConfigureProp"
|
||||
styles:
|
||||
type: object
|
||||
properties: {}
|
||||
events:
|
||||
type: object
|
||||
properties: {}
|
||||
component:
|
||||
$ref: "#/definitions/ConfigureComponent"
|
||||
Npm:
|
||||
type: object
|
||||
properties:
|
||||
package:
|
||||
type: string
|
||||
exportName:
|
||||
type: string
|
||||
subName:
|
||||
type: string
|
||||
main:
|
||||
type: string
|
||||
destructuring:
|
||||
type: boolean
|
||||
version:
|
||||
type: string
|
||||
required:
|
||||
- package
|
||||
- exportName
|
||||
- subName
|
||||
- main
|
||||
- destructuring
|
||||
- version
|
||||
PropType:
|
||||
oneOf:
|
||||
- $ref: "#/definitions/BasicType"
|
||||
- $ref: "#/definitions/RequiredType"
|
||||
- $ref: "#/definitions/ComplexType"
|
||||
BasicType:
|
||||
type: string
|
||||
enum:
|
||||
- array
|
||||
- bool
|
||||
- func
|
||||
- number
|
||||
- object
|
||||
- string
|
||||
- node
|
||||
- element
|
||||
- any
|
||||
RequiredType:
|
||||
type: object
|
||||
properties:
|
||||
type:
|
||||
$ref: "#/definitions/BasicType"
|
||||
isRequired:
|
||||
type: boolean
|
||||
additionalProperties: false
|
||||
required:
|
||||
- type
|
||||
ComplexType:
|
||||
oneOf:
|
||||
- $ref: "#/definitions/OneOf"
|
||||
- $ref: "#/definitions/OneOfType"
|
||||
- $ref: "#/definitions/ArrayOf"
|
||||
- $ref: "#/definitions/ObjectOf"
|
||||
- $ref: "#/definitions/Shape"
|
||||
- $ref: "#/definitions/Exact"
|
||||
OneOf:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
- value
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- oneOf
|
||||
value:
|
||||
type: array
|
||||
items:
|
||||
oneOf:
|
||||
- type: string
|
||||
- type: number
|
||||
- type: boolean
|
||||
isRequired:
|
||||
type: boolean
|
||||
OneOfType:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
- value
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- oneOfType
|
||||
value:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/definitions/PropType"
|
||||
isRequired:
|
||||
type: boolean
|
||||
ArrayOf:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
- value
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- arrayOf
|
||||
value:
|
||||
$ref: "#/definitions/PropType"
|
||||
isRequired:
|
||||
type: boolean
|
||||
ObjectOf:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
- value
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- objectOf
|
||||
value:
|
||||
$ref: "#/definitions/PropType"
|
||||
isRequired:
|
||||
type: boolean
|
||||
Shape:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
- value
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- shape
|
||||
value:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
propType:
|
||||
$ref: "#/definitions/PropType"
|
||||
additionalProperties: false
|
||||
isRequired:
|
||||
type: boolean
|
||||
ShapeItem:
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
- propType
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
propType:
|
||||
$ref: "#/definitions/PropType"
|
||||
isRequired:
|
||||
type: boolean
|
||||
additionalProperties: false
|
||||
Exact:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
- value
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- exact
|
||||
value:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
propType:
|
||||
$ref: "#/definitions/PropType"
|
||||
additionalProperties: false
|
||||
isRequired:
|
||||
type: boolean
|
||||
ConfigureProp:
|
||||
type: object
|
||||
allOf:
|
||||
- type: object
|
||||
properties:
|
||||
title:
|
||||
type: string
|
||||
extraProps:
|
||||
type: object
|
||||
properties: {}
|
||||
- oneOf:
|
||||
- $ref: "#/definitions/ConfigureFieldProp"
|
||||
- $ref: "#/definitions/ConfigureGroupProp"
|
||||
ConfigureFieldProp:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- field
|
||||
name:
|
||||
type: string
|
||||
setter:
|
||||
$ref: "#/definitions/ConfigureFieldSetter"
|
||||
ConfigureFieldSetter:
|
||||
type: object
|
||||
required:
|
||||
- componentName
|
||||
properties:
|
||||
componentName:
|
||||
type: string
|
||||
enum:
|
||||
- List
|
||||
- Object
|
||||
- Function
|
||||
- Node
|
||||
- Mixin
|
||||
- Expression
|
||||
- Switch
|
||||
- Number
|
||||
- Input
|
||||
- TextArea
|
||||
- Date
|
||||
- DateYear
|
||||
- DateMonth
|
||||
- DateRange
|
||||
- ColorPicker
|
||||
- CodeEditor
|
||||
- Select
|
||||
- RadioGroup
|
||||
props:
|
||||
type: object
|
||||
properties: {} # 暂未校验每个控件的props,待控件入料后获取真实属性
|
||||
ConfigureGroupProp:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
- items
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- group
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/definitions/ConfigureProp"
|
||||
ConfigureComponent:
|
||||
type: object
|
||||
properties:
|
||||
isContainer:
|
||||
type: boolean
|
||||
isModal:
|
||||
type: boolean
|
||||
descriptor:
|
||||
type: string
|
||||
nestingRule:
|
||||
type: object
|
||||
properties:
|
||||
childWhitelist:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
parentWhitelist:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
descendantBlacklist:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
ancestorWhitelist:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
isNullNode:
|
||||
type: boolean
|
||||
isLayout:
|
||||
type: boolean
|
||||
27
packages/material-parser/scripts/transform.js
Normal file
27
packages/material-parser/scripts/transform.js
Normal file
@ -0,0 +1,27 @@
|
||||
const yaml = require('js-yaml');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const Ajv = require('ajv');
|
||||
const { compile } = require('json-schema-to-typescript');
|
||||
|
||||
const ajv = new Ajv();
|
||||
|
||||
const YamlPath = path.resolve(__dirname, '../schemas/schema.yml');
|
||||
const JsonPath = path.resolve(__dirname, '../src/validate/schema.json');
|
||||
const tsPath = path.resolve(__dirname, '../src/core/schema/types.ts');
|
||||
// Get document, or throw exception on error
|
||||
|
||||
(async function () {
|
||||
try {
|
||||
const schema = yaml.load(fs.readFileSync(YamlPath, 'utf8'));
|
||||
ajv.compile(schema);
|
||||
fs.writeFileSync(JsonPath, JSON.stringify(schema, null, 2), 'utf-8');
|
||||
console.log('yaml file is successfully transformed into json');
|
||||
const ts = await compile(schema, 'ComponentMeta');
|
||||
fs.writeFileSync(tsPath, ts);
|
||||
console.log('schema.d.ts is successfully generated');
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
process.exit(1);
|
||||
}
|
||||
})();
|
||||
10
packages/material-parser/src/core/index.ts
Normal file
10
packages/material-parser/src/core/index.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import _debug from 'debug';
|
||||
|
||||
export * from './schema/types';
|
||||
|
||||
/**
|
||||
* Dev helper
|
||||
*/
|
||||
export const debug = _debug('lowcode:mat');
|
||||
export const enableDebug = () => _debug.enable('lowcode:*');
|
||||
export const disableDebug = () => _debug.disable();
|
||||
163
packages/material-parser/src/core/schema/types.ts
Normal file
163
packages/material-parser/src/core/schema/types.ts
Normal file
@ -0,0 +1,163 @@
|
||||
/**
|
||||
* This file was automatically generated by json-schema-to-typescript.
|
||||
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
|
||||
* and run json-schema-to-typescript to regenerate this file.
|
||||
*/
|
||||
|
||||
/**
|
||||
* json schema for low code component protocol
|
||||
*/
|
||||
export type ComponentMeta = BasicSection & PropsSection & ConfigureSection;
|
||||
export type PropType = BasicType | RequiredType | ComplexType;
|
||||
export type BasicType = 'array' | 'bool' | 'func' | 'number' | 'object' | 'string' | 'node' | 'element' | 'any';
|
||||
export type ComplexType = OneOf | OneOfType | ArrayOf | ObjectOf | Shape | Exact;
|
||||
export type ConfigureProp = {
|
||||
title?: string;
|
||||
extraProps?: {
|
||||
[k: string]: any;
|
||||
};
|
||||
[k: string]: any;
|
||||
} & (ConfigureFieldProp | ConfigureGroupProp);
|
||||
|
||||
export interface BasicSection {
|
||||
componentName: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
docUrl?: string;
|
||||
screenshot?: string;
|
||||
icon?: string;
|
||||
tags?: string[];
|
||||
devMode?: 'proCode' | 'lowCode';
|
||||
npm: Npm;
|
||||
[k: string]: any;
|
||||
}
|
||||
export interface Npm {
|
||||
package: string;
|
||||
exportName: string;
|
||||
subName: string;
|
||||
main: string;
|
||||
destructuring: boolean;
|
||||
version: string;
|
||||
[k: string]: any;
|
||||
}
|
||||
export interface PropsSection {
|
||||
props: {
|
||||
name: string;
|
||||
propType: PropType;
|
||||
description?: string;
|
||||
defaultValue?: any;
|
||||
[k: string]: any;
|
||||
}[];
|
||||
[k: string]: any;
|
||||
}
|
||||
export interface RequiredType {
|
||||
type: BasicType;
|
||||
isRequired?: boolean;
|
||||
}
|
||||
export interface OneOf {
|
||||
type: 'oneOf';
|
||||
value: (string | number | boolean)[];
|
||||
isRequired?: boolean;
|
||||
[k: string]: any;
|
||||
}
|
||||
export interface OneOfType {
|
||||
type: 'oneOfType';
|
||||
value: PropType[];
|
||||
isRequired?: boolean;
|
||||
[k: string]: any;
|
||||
}
|
||||
export interface ArrayOf {
|
||||
type: 'arrayOf';
|
||||
value: PropType;
|
||||
isRequired?: boolean;
|
||||
[k: string]: any;
|
||||
}
|
||||
export interface ObjectOf {
|
||||
type: 'objectOf';
|
||||
value: PropType;
|
||||
isRequired?: boolean;
|
||||
[k: string]: any;
|
||||
}
|
||||
export interface Shape {
|
||||
type: 'shape';
|
||||
value: {
|
||||
name?: string;
|
||||
propType?: PropType;
|
||||
}[];
|
||||
isRequired?: boolean;
|
||||
[k: string]: any;
|
||||
}
|
||||
export interface Exact {
|
||||
type: 'exact';
|
||||
value: {
|
||||
name?: string;
|
||||
propType?: PropType;
|
||||
}[];
|
||||
isRequired?: boolean;
|
||||
[k: string]: any;
|
||||
}
|
||||
export interface ConfigureSection {
|
||||
configure?: {
|
||||
props?: ConfigureProp[];
|
||||
styles?: {
|
||||
[k: string]: any;
|
||||
};
|
||||
events?: {
|
||||
[k: string]: any;
|
||||
};
|
||||
component?: ConfigureComponent;
|
||||
[k: string]: any;
|
||||
};
|
||||
[k: string]: any;
|
||||
}
|
||||
export interface ConfigureFieldProp {
|
||||
type: 'field';
|
||||
name?: string;
|
||||
setter?: ConfigureFieldSetter;
|
||||
[k: string]: any;
|
||||
}
|
||||
export interface ConfigureFieldSetter {
|
||||
componentName:
|
||||
| 'List'
|
||||
| 'Object'
|
||||
| 'Function'
|
||||
| 'Node'
|
||||
| 'Mixin'
|
||||
| 'Expression'
|
||||
| 'Switch'
|
||||
| 'Number'
|
||||
| 'Input'
|
||||
| 'TextArea'
|
||||
| 'Date'
|
||||
| 'DateYear'
|
||||
| 'DateMonth'
|
||||
| 'DateRange'
|
||||
| 'ColorPicker'
|
||||
| 'CodeEditor'
|
||||
| 'Select'
|
||||
| 'RadioGroup';
|
||||
props?: {
|
||||
[k: string]: any;
|
||||
};
|
||||
[k: string]: any;
|
||||
}
|
||||
export interface ConfigureGroupProp {
|
||||
type: 'group';
|
||||
items: ConfigureProp[];
|
||||
[k: string]: any;
|
||||
}
|
||||
export interface ConfigureComponent {
|
||||
isContainer?: boolean;
|
||||
isModal?: boolean;
|
||||
descriptor?: string;
|
||||
nestingRule?: {
|
||||
childWhitelist?: string[];
|
||||
parentWhitelist?: string[];
|
||||
descendantBlacklist?: string[];
|
||||
ancestorWhitelist?: string[];
|
||||
[k: string]: any;
|
||||
};
|
||||
isNullNode?: boolean;
|
||||
isLayout?: boolean;
|
||||
[k: string]: any;
|
||||
}
|
||||
65
packages/material-parser/src/generate.ts
Normal file
65
packages/material-parser/src/generate.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import * as path from 'path';
|
||||
import { debug, ComponentMeta } from './core';
|
||||
import { IMaterialParsedModel, IMaterialScanModel, IInternalMaterializeOptions } from './types';
|
||||
|
||||
const log = debug.extend('gen');
|
||||
|
||||
export default async function (
|
||||
matScanModel: IMaterialScanModel,
|
||||
matParsedModels: IMaterialParsedModel[],
|
||||
options: IInternalMaterializeOptions,
|
||||
): Promise<ComponentMeta[]> {
|
||||
const containerList = [];
|
||||
for (const matParsedModel of matParsedModels) {
|
||||
// 默认排除掉 defaultExportName 为空的组件
|
||||
if (!matParsedModel.componentName) {
|
||||
log('skip');
|
||||
continue;
|
||||
}
|
||||
// 组装 manifest
|
||||
const manifest: any = await genManifest(matScanModel, matParsedModel, options);
|
||||
|
||||
containerList.push(manifest);
|
||||
}
|
||||
|
||||
return containerList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 manifest
|
||||
*
|
||||
* @param {IMaterialParsedModel} matParsedModel
|
||||
* @returns {Promise<
|
||||
* manifestObj: ComponentMeta, // 组件描述
|
||||
* >}
|
||||
* @memberof LocalGenerator
|
||||
*/
|
||||
export async function genManifest(
|
||||
matScanModel: IMaterialScanModel,
|
||||
matParsedModel: IMaterialParsedModel,
|
||||
options: IInternalMaterializeOptions,
|
||||
): Promise<ComponentMeta> {
|
||||
const manifestObj: Partial<ComponentMeta> = {
|
||||
componentName: matParsedModel.componentName,
|
||||
title: matScanModel.pkgName,
|
||||
docUrl: '',
|
||||
screenshot: '',
|
||||
devMode: 'proCode', // 需要入料的组件都是源码模式,低代码组件在平台上即可直接生成描述
|
||||
npm: {
|
||||
package: matScanModel.pkgName,
|
||||
version: matScanModel.pkgVersion,
|
||||
exportName: matParsedModel.meta?.exportName || matParsedModel.componentName,
|
||||
main:
|
||||
options.root && path.isAbsolute(matScanModel.mainFilePath)
|
||||
? path.relative(options.root, matScanModel.mainFilePath)
|
||||
: matScanModel.mainFilePath,
|
||||
destructuring: matParsedModel.meta?.exportName !== 'default',
|
||||
subName: matParsedModel.meta?.subName || '',
|
||||
},
|
||||
};
|
||||
|
||||
// 填充 props
|
||||
manifestObj.props = matParsedModel.props;
|
||||
// 执行扩展点
|
||||
return manifestObj as ComponentMeta;
|
||||
}
|
||||
70
packages/material-parser/src/index.ts
Normal file
70
packages/material-parser/src/index.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import 'ts-polyfill';
|
||||
import { remove, lstatSync } from 'fs-extra';
|
||||
import { join, isAbsolute } from 'path';
|
||||
import {
|
||||
IMaterializeOptions,
|
||||
IMaterializeLocalOptions,
|
||||
IMaterializeOnlineOptions,
|
||||
IInternalMaterializeOptions,
|
||||
} from './types';
|
||||
import { ComponentMeta } from './core';
|
||||
import scan from './scan';
|
||||
import generate from './generate';
|
||||
import parse from './parse';
|
||||
import localize from './localize';
|
||||
|
||||
export { default as validate } from './validate';
|
||||
export { default as schema } from './validate/schema.json';
|
||||
|
||||
export * from './types';
|
||||
|
||||
export default async function (options: IMaterializeOptions): Promise<ComponentMeta[]> {
|
||||
const { accesser = 'local', dslType } = options;
|
||||
|
||||
let { entry = '' } = options;
|
||||
const internalOptions: IInternalMaterializeOptions = {
|
||||
...options,
|
||||
accesser,
|
||||
entry: options.entry || '',
|
||||
root: (options as IMaterializeLocalOptions)?.root || '',
|
||||
} as IInternalMaterializeOptions;
|
||||
|
||||
if (accesser === 'local') {
|
||||
const { root } = options as IMaterializeLocalOptions;
|
||||
internalOptions.root = root;
|
||||
if (!root) {
|
||||
const stats = lstatSync(entry);
|
||||
if (stats.isDirectory()) {
|
||||
internalOptions.root = entry;
|
||||
} else {
|
||||
internalOptions.root = process.cwd();
|
||||
}
|
||||
} else if (!isAbsolute(entry)) {
|
||||
internalOptions.entry = join(root, entry);
|
||||
}
|
||||
}
|
||||
|
||||
let workDir = internalOptions.root || '';
|
||||
let moduleDir = '';
|
||||
if (accesser === 'online') {
|
||||
const result = await localize(internalOptions as IMaterializeOnlineOptions);
|
||||
workDir = result.workDir;
|
||||
moduleDir = result.moduleDir;
|
||||
internalOptions.entry = result.entry ? join(moduleDir, result.entry) : moduleDir;
|
||||
internalOptions.root = moduleDir;
|
||||
}
|
||||
const scanedModel = await scan(internalOptions);
|
||||
const parsedModel = await parse({
|
||||
...scanedModel,
|
||||
dslType,
|
||||
accesser,
|
||||
npmClient: internalOptions.npmClient,
|
||||
workDir,
|
||||
moduleDir,
|
||||
});
|
||||
const result = await generate(scanedModel, parsedModel, internalOptions);
|
||||
if (workDir && accesser === 'online') {
|
||||
await remove(workDir);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
126
packages/material-parser/src/localize.ts
Normal file
126
packages/material-parser/src/localize.ts
Normal file
@ -0,0 +1,126 @@
|
||||
import spawn from 'cross-spawn-promise';
|
||||
import { ensureDir, ensureFile, writeFile } from 'fs-extra';
|
||||
import { join, resolve } from 'path';
|
||||
import uuid from 'short-uuid';
|
||||
import { debug } from './core';
|
||||
import { IMaterializeOnlineOptions, IMaterializeOnlinePackageAndVersionOptions } from './types';
|
||||
|
||||
const log = debug.extend('localize');
|
||||
|
||||
/**
|
||||
* 创建组件包
|
||||
*
|
||||
* @private
|
||||
* @param {{
|
||||
* pkgName: string;
|
||||
* pkgVersion: string;
|
||||
* }} params
|
||||
* @returns {Promise<void>}
|
||||
* @memberof OnlineAccesser
|
||||
*/
|
||||
export async function createFakePackage(params: {
|
||||
workDir: string;
|
||||
pkgName: string;
|
||||
pkgVersion: string;
|
||||
npmClient?: string;
|
||||
}): Promise<void> {
|
||||
// 创建临时组件包
|
||||
const { workDir } = params;
|
||||
const pkgJsonFilePath = join(workDir, 'package.json');
|
||||
await ensureFile(pkgJsonFilePath);
|
||||
await writeFile(
|
||||
pkgJsonFilePath,
|
||||
JSON.stringify(
|
||||
{
|
||||
name: params.pkgName,
|
||||
version: params.pkgVersion || '0.0.0',
|
||||
dependencies: {
|
||||
[params.pkgName]: params.pkgVersion || 'latest',
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
'parse-prop-types': '^0.3.0',
|
||||
typesync: 'latest',
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
|
||||
// 安装依赖
|
||||
const npmClient = params.npmClient || 'tnpm';
|
||||
await spawn(npmClient, ['i'], { stdio: 'inherit', cwd: workDir } as any);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建临时目录
|
||||
*
|
||||
* @private
|
||||
* @returns {Promise<string>} 返回临时文件夹路径
|
||||
* @memberof LocalGenerator
|
||||
*/
|
||||
export async function createworkDir(tempDir?: string): Promise<string> {
|
||||
const workDirName = uuid.generate();
|
||||
const workDir = resolve(tempDir || '../../node_modules/.temp/', workDirName);
|
||||
await ensureDir(workDir);
|
||||
log('create temp dir successfully', workDir);
|
||||
return workDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分离物料组件名称和版本号
|
||||
*
|
||||
* @private
|
||||
* @param {string} pkgNameWithVersion
|
||||
* @returns {{ [key: string]: any }}
|
||||
* @memberof OnlineAccesser
|
||||
*/
|
||||
export function getPkgNameAndVersion(pkgNameWithVersion: string): { [key: string]: any } {
|
||||
const matches = pkgNameWithVersion.match(/(@[^/]+)$/);
|
||||
if (!matches) {
|
||||
return {
|
||||
name: pkgNameWithVersion,
|
||||
};
|
||||
}
|
||||
const name = pkgNameWithVersion.replace(matches[0], '');
|
||||
return {
|
||||
version: matches[0].slice(1),
|
||||
name,
|
||||
};
|
||||
}
|
||||
|
||||
// 将问题转化为本地物料化场景
|
||||
export default async function localize(options: IMaterializeOnlineOptions): Promise<{
|
||||
workDir: string;
|
||||
moduleDir: string;
|
||||
entry?: string;
|
||||
}> {
|
||||
// 创建临时目录
|
||||
const workDir = await createworkDir(options.tempDir);
|
||||
await ensureDir(workDir);
|
||||
let { name, version = 'latest' } = options as IMaterializeOnlinePackageAndVersionOptions;
|
||||
if (!name) {
|
||||
const pkgNameAndVersion = getPkgNameAndVersion(options.entry);
|
||||
name = pkgNameAndVersion.name;
|
||||
version = pkgNameAndVersion.version;
|
||||
}
|
||||
// 创建组件包
|
||||
await createFakePackage({
|
||||
pkgName: name,
|
||||
pkgVersion: version,
|
||||
workDir,
|
||||
npmClient: options.npmClient,
|
||||
});
|
||||
|
||||
const result = {
|
||||
workDir,
|
||||
moduleDir: join(workDir, 'node_modules', name),
|
||||
entry: undefined,
|
||||
};
|
||||
|
||||
if ((options as IMaterializeOnlinePackageAndVersionOptions)?.name) {
|
||||
result.entry = options.entry;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
121
packages/material-parser/src/parse/dynamic/index.ts
Normal file
121
packages/material-parser/src/parse/dynamic/index.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import { isEmpty } from 'lodash';
|
||||
// import * as path from 'path';
|
||||
// @ts-ignore
|
||||
import parsePropTypes from 'parse-prop-types';
|
||||
import PropTypes from 'prop-types';
|
||||
import { transformItem } from '../transform';
|
||||
import requireInSandbox from './requireInSandbox';
|
||||
|
||||
export interface IComponentInfo {
|
||||
component: any;
|
||||
meta: {
|
||||
exportName: string;
|
||||
subName?: string;
|
||||
};
|
||||
}
|
||||
|
||||
const reservedKeys = [
|
||||
'propTypes',
|
||||
'defaultProps',
|
||||
'name',
|
||||
'arguments',
|
||||
'caller',
|
||||
'length',
|
||||
'contextTypes',
|
||||
'displayName',
|
||||
'__esModule',
|
||||
'version',
|
||||
];
|
||||
|
||||
function getKeys(com: any) {
|
||||
const keys = Object.keys(com).filter(x => {
|
||||
return !reservedKeys.includes(x) && !x.startsWith('_');
|
||||
});
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
function isComponent(obj: any) {
|
||||
return (
|
||||
typeof obj === 'function' &&
|
||||
(Object.prototype.hasOwnProperty.call(obj, 'propTypes') ||
|
||||
Object.prototype.hasOwnProperty.call(obj, 'defaultProps'))
|
||||
);
|
||||
}
|
||||
|
||||
export default function (filePath: string) {
|
||||
// const { filePath } = arg;
|
||||
// const modulePath = path.resolve(workDir, 'node_modules', 'parse-prop-types');
|
||||
// const parsePropTypes = require(modulePath).default;
|
||||
if (!filePath) return [];
|
||||
const Com = requireInSandbox(filePath, PropTypes);
|
||||
const components: IComponentInfo[] = [];
|
||||
let index = 0;
|
||||
|
||||
if (Com.__esModule) {
|
||||
const keys = getKeys(Com);
|
||||
keys.forEach(k => {
|
||||
if (isComponent(Com[k])) {
|
||||
components.push({
|
||||
component: Com[k],
|
||||
meta: {
|
||||
exportName: k,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (isComponent(Com)) {
|
||||
components.push({
|
||||
component: Com,
|
||||
meta: {
|
||||
exportName: 'default',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// dps
|
||||
while (index < components.length) {
|
||||
const item = components[index++];
|
||||
|
||||
const keys = getKeys(item.component);
|
||||
const subs = keys
|
||||
.filter(k => isComponent(item.component[k]))
|
||||
.map(k => ({
|
||||
component: item.component[k],
|
||||
meta: {
|
||||
...item.meta,
|
||||
subName: k,
|
||||
},
|
||||
}));
|
||||
if (subs.length) {
|
||||
components.splice(index, 0, ...subs);
|
||||
}
|
||||
}
|
||||
|
||||
const result = components.reduce((acc: any, { meta, component }) => {
|
||||
const componentInfo = parsePropTypes(component);
|
||||
if (!isEmpty(componentInfo)) {
|
||||
const props = Object.keys(componentInfo).reduce((acc2: any[], name) => {
|
||||
try {
|
||||
const item: any = transformItem(name, componentInfo[name]);
|
||||
acc2.push(item);
|
||||
} catch (e) {
|
||||
// TODO
|
||||
}
|
||||
return acc2;
|
||||
}, []);
|
||||
|
||||
return [
|
||||
...acc,
|
||||
{
|
||||
meta,
|
||||
props,
|
||||
componentName: meta.subName || meta.exportName || component.displayName,
|
||||
},
|
||||
];
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
import { readFileSync } from 'fs-extra';
|
||||
import { NodeVM } from 'vm2';
|
||||
// import PropTypes from 'prop-types';
|
||||
|
||||
const cssPattern = /\.(css|scss|sass|less)$/;
|
||||
function requireInSandbox(filePath: string, PropTypes: any) {
|
||||
const vm = new NodeVM({
|
||||
sandbox: {},
|
||||
sourceExtensions: ['js', 'css', 'scss', 'sass', 'less'],
|
||||
compiler: (code, filename) => {
|
||||
if (filename.match(cssPattern)) {
|
||||
return `
|
||||
const handler = {
|
||||
get() {
|
||||
return new Proxy({}, handler);
|
||||
},
|
||||
};
|
||||
const proxiedObject = new Proxy({}, handler);
|
||||
module.exports = proxiedObject;
|
||||
`;
|
||||
} else {
|
||||
return code;
|
||||
}
|
||||
},
|
||||
require: {
|
||||
external: true,
|
||||
context: 'sandbox',
|
||||
mock: {
|
||||
'prop-types': PropTypes,
|
||||
},
|
||||
},
|
||||
});
|
||||
const fileContent = readFileSync(filePath, { encoding: 'utf8' });
|
||||
return vm.run(fileContent, filePath);
|
||||
}
|
||||
|
||||
export default requireInSandbox;
|
||||
80
packages/material-parser/src/parse/index.ts
Normal file
80
packages/material-parser/src/parse/index.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import parseDynamic from './dynamic';
|
||||
import parseJS from './js';
|
||||
import parseTS from './ts';
|
||||
import { install, installPeerAndDevDeps, syncTypeModules, installTypeDTS } from '../utils';
|
||||
import { IMaterialScanModel, DSLType } from '../types';
|
||||
import { debug } from '../core';
|
||||
|
||||
const log = debug.extend('parse');
|
||||
|
||||
export interface IParseArgs extends IMaterialScanModel {
|
||||
accesser?: 'online' | 'local';
|
||||
dslType?: DSLType;
|
||||
npmClient?: string;
|
||||
workDir: string;
|
||||
moduleDir: string;
|
||||
typingsFileAbsolutePath?: string;
|
||||
mainFileAbsolutePath: string;
|
||||
moduleFileAbsolutePath?: string;
|
||||
}
|
||||
|
||||
export function isTSLike(str) {
|
||||
return str.endsWith('ts') || str.endsWith('tsx');
|
||||
}
|
||||
|
||||
export default async (args: IParseArgs) => {
|
||||
const {
|
||||
typingsFileAbsolutePath,
|
||||
mainFileAbsolutePath,
|
||||
moduleFileAbsolutePath = mainFileAbsolutePath,
|
||||
useEntry = false,
|
||||
} = args;
|
||||
if (args.accesser === 'local') {
|
||||
if (isTSLike(mainFileAbsolutePath)) {
|
||||
await install(args);
|
||||
// in case the developer forgets to install types
|
||||
await installTypeDTS(args);
|
||||
return parseTS(mainFileAbsolutePath, args);
|
||||
} else if (typingsFileAbsolutePath) {
|
||||
await installTypeDTS(args);
|
||||
return parseTS(typingsFileAbsolutePath, args);
|
||||
} else {
|
||||
try {
|
||||
return parseJS(moduleFileAbsolutePath || mainFileAbsolutePath);
|
||||
} catch (e) {
|
||||
log(e);
|
||||
await install(args);
|
||||
const info = parseDynamic(mainFileAbsolutePath);
|
||||
if (!info || !info.length) {
|
||||
throw Error();
|
||||
}
|
||||
return info;
|
||||
}
|
||||
}
|
||||
} else if (args.accesser === 'online') {
|
||||
// ts
|
||||
const entryPath = useEntry ? mainFileAbsolutePath : typingsFileAbsolutePath;
|
||||
if (entryPath && isTSLike(entryPath)) {
|
||||
await syncTypeModules(args);
|
||||
await install(args);
|
||||
await installTypeDTS(args);
|
||||
await installPeerAndDevDeps(args);
|
||||
return parseTS(entryPath, args);
|
||||
}
|
||||
// js
|
||||
try {
|
||||
// try dynamic parsing first
|
||||
await installPeerAndDevDeps(args);
|
||||
const info = parseDynamic(mainFileAbsolutePath);
|
||||
if (!info || !info.length) {
|
||||
throw Error();
|
||||
}
|
||||
return info;
|
||||
} catch (e) {
|
||||
log(e);
|
||||
// if error, use static js parsing instead
|
||||
return parseJS(moduleFileAbsolutePath || mainFileAbsolutePath);
|
||||
}
|
||||
}
|
||||
return parseJS(moduleFileAbsolutePath || mainFileAbsolutePath);
|
||||
};
|
||||
@ -0,0 +1,138 @@
|
||||
import getComposedPath from '../utils/getComposedPath';
|
||||
import evaluate from '../utils/evaluate';
|
||||
|
||||
const { namedTypes: t, NodePath, visit } = require('ast-types');
|
||||
type NodePathType = typeof NodePath;
|
||||
const {
|
||||
getPropertyName,
|
||||
isReactComponentClass,
|
||||
getMemberValuePath,
|
||||
isReactForwardRefCall,
|
||||
printValue,
|
||||
resolveToValue,
|
||||
} = require('react-docgen').utils;
|
||||
const resolveFunctionDefinitionToReturnValue = require('react-docgen/dist/utils/resolveFunctionDefinitionToReturnValue');
|
||||
|
||||
function getDefaultValue(path: NodePathType) {
|
||||
let { node } = path;
|
||||
let defaultValue;
|
||||
if (t.Literal.check(node)) {
|
||||
defaultValue = node.raw;
|
||||
} else {
|
||||
if (t.AssignmentPattern.check(path.node)) {
|
||||
path = resolveToValue(path.get('right'));
|
||||
} else {
|
||||
path = resolveToValue(path);
|
||||
}
|
||||
if (t.ImportDeclaration.check(path.node)) {
|
||||
defaultValue = node.name;
|
||||
} else {
|
||||
node = path.node;
|
||||
try {
|
||||
const result = evaluate(path);
|
||||
if (result.confident) {
|
||||
defaultValue = result.value;
|
||||
}
|
||||
} catch (e) {
|
||||
// log(e);
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
if (typeof defaultValue !== 'undefined') {
|
||||
return {
|
||||
value: defaultValue,
|
||||
computed:
|
||||
t.CallExpression.check(node) || t.MemberExpression.check(node) || t.Identifier.check(node),
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function getStatelessPropsPath(componentDefinition: any) {
|
||||
const value = resolveToValue(componentDefinition);
|
||||
if (isReactForwardRefCall(value)) {
|
||||
const inner = resolveToValue(value.get('arguments', 0));
|
||||
return inner.get('params', 0);
|
||||
}
|
||||
return value.get('params', 0);
|
||||
}
|
||||
|
||||
function getDefaultPropsPath(componentDefinition: any) {
|
||||
let defaultPropsPath = getMemberValuePath(componentDefinition, 'defaultProps');
|
||||
if (!defaultPropsPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
defaultPropsPath = resolveToValue(defaultPropsPath);
|
||||
if (!defaultPropsPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (t.FunctionExpression.check(defaultPropsPath.node)) {
|
||||
// Find the value that is returned from the function and process it if it is
|
||||
// an object literal.
|
||||
const returnValue = resolveFunctionDefinitionToReturnValue(defaultPropsPath);
|
||||
if (returnValue && t.ObjectExpression.check(returnValue.node)) {
|
||||
defaultPropsPath = returnValue;
|
||||
}
|
||||
}
|
||||
return defaultPropsPath;
|
||||
}
|
||||
|
||||
function getDefaultValuesFromProps(properties: any[], documentation: any, isStateless: boolean) {
|
||||
properties
|
||||
// Don't evaluate property if component is functional and the node is not an AssignmentPattern
|
||||
.filter(
|
||||
(propertyPath) => !isStateless || t.AssignmentPattern.check(propertyPath.get('value').node),
|
||||
)
|
||||
.forEach((propertyPath) => {
|
||||
if (t.Property.check(propertyPath.node)) {
|
||||
const propName = getPropertyName(propertyPath);
|
||||
if (!propName) return;
|
||||
|
||||
const propDescriptor = documentation.getPropDescriptor(propName);
|
||||
const defaultValue = getDefaultValue(
|
||||
isStateless ? propertyPath.get('value', 'right') : propertyPath.get('value'),
|
||||
);
|
||||
if (defaultValue) {
|
||||
propDescriptor.defaultValue = defaultValue;
|
||||
}
|
||||
} else if (t.SpreadElement.check(propertyPath.node)) {
|
||||
const resolvedValuePath = resolveToValue(propertyPath.get('argument'));
|
||||
if (t.ObjectExpression.check(resolvedValuePath.node)) {
|
||||
getDefaultValuesFromProps(
|
||||
resolvedValuePath.get('properties'),
|
||||
documentation,
|
||||
isStateless,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default function defaultPropsHandler(documentation: any, componentDefinition: any) {
|
||||
let statelessProps = null;
|
||||
let defaultPropsPath = getDefaultPropsPath(componentDefinition);
|
||||
/**
|
||||
* function, lazy, memo, forwardRef etc components can resolve default props as well
|
||||
*/
|
||||
if (!isReactComponentClass(componentDefinition)) {
|
||||
statelessProps = getStatelessPropsPath(componentDefinition);
|
||||
}
|
||||
|
||||
// Do both statelessProps and defaultProps if both are available so defaultProps can override
|
||||
if (statelessProps && t.ObjectPattern.check(statelessProps.node)) {
|
||||
getDefaultValuesFromProps(statelessProps.get('properties'), documentation, true);
|
||||
}
|
||||
if (defaultPropsPath && !t.ObjectExpression.check(defaultPropsPath.node)) {
|
||||
const composedPath = getComposedPath(documentation, 'defaultProps', defaultPropsPath);
|
||||
if (composedPath) {
|
||||
defaultPropsPath = composedPath;
|
||||
}
|
||||
}
|
||||
if (defaultPropsPath && t.ObjectExpression.check(defaultPropsPath.node)) {
|
||||
getDefaultValuesFromProps(defaultPropsPath.get('properties'), documentation, false);
|
||||
}
|
||||
}
|
||||
22
packages/material-parser/src/parse/js/handlers/index.ts
Normal file
22
packages/material-parser/src/parse/js/handlers/index.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { propTypeHandler, contextTypeHandler, childContextTypeHandler } from './propTypeHandler';
|
||||
import defaultPropsHandler from './defaultPropsHandler';
|
||||
import preProcessHandler from './preProcessHandler';
|
||||
import propTypeJsDocHandler from './propTypeJsDocHandler';
|
||||
|
||||
const { handlers } = require('react-docgen');
|
||||
|
||||
const defaultHandlers = [
|
||||
preProcessHandler,
|
||||
handlers.propTypeCompositionHandler,
|
||||
propTypeHandler,
|
||||
contextTypeHandler,
|
||||
childContextTypeHandler,
|
||||
handlers.propDocBlockHandler,
|
||||
propTypeJsDocHandler,
|
||||
defaultPropsHandler,
|
||||
handlers.componentDocblockHandler,
|
||||
handlers.displayNameHandler,
|
||||
handlers.componentMethodsJsDocHandler,
|
||||
];
|
||||
|
||||
export default defaultHandlers;
|
||||
@ -0,0 +1,3 @@
|
||||
export default function preProcessHandler(documentation: any, path: any) {
|
||||
documentation.set('meta', path.__meta);
|
||||
}
|
||||
@ -0,0 +1,139 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import { namedTypes as t, visit } from 'ast-types';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import getRoot from '../utils/getRoot';
|
||||
import findJSFilePath from '../utils/findJSFilePath';
|
||||
import getComposedPath from '../utils/getComposedPath';
|
||||
const buildParser = require('react-docgen/dist/babelParser').default;
|
||||
|
||||
const {
|
||||
resolveToValue,
|
||||
isExportsOrModuleAssignment,
|
||||
getPropType,
|
||||
getPropertyName,
|
||||
getMemberValuePath,
|
||||
isReactModuleName,
|
||||
printValue,
|
||||
resolveToModule,
|
||||
} = require('react-docgen').utils;
|
||||
|
||||
const expressionTo = require('react-docgen/dist/utils/expressionTo');
|
||||
const isRequiredPropType = require('react-docgen/dist/utils/isRequiredPropType')
|
||||
.default;
|
||||
|
||||
function isPropTypesExpression(path: any) {
|
||||
const moduleName = resolveToModule(path);
|
||||
if (moduleName) {
|
||||
return isReactModuleName(moduleName) || moduleName === 'ReactPropTypes';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function amendPropTypes(getDescriptor: any, path: any, documentation, propName: string) {
|
||||
if (!t.ObjectExpression.check(path.node)) {
|
||||
const propTypesPath = getComposedPath(documentation, propName, path);
|
||||
if (!propTypesPath) {
|
||||
return;
|
||||
} else {
|
||||
path.replace(propTypesPath.node);
|
||||
}
|
||||
}
|
||||
|
||||
path.get('properties').each((propertyPath: any) => {
|
||||
switch (propertyPath.node.type) {
|
||||
// @ts-ignore
|
||||
case t.Property.name: {
|
||||
const propName = getPropertyName(propertyPath);
|
||||
if (!propName) return;
|
||||
|
||||
const propDescriptor = getDescriptor(propName);
|
||||
const valuePath = propertyPath.get('value');
|
||||
const type = getPropType(valuePath);
|
||||
|
||||
if (type) {
|
||||
propDescriptor.type = type;
|
||||
propDescriptor.required =
|
||||
type.name !== 'custom' && isRequiredPropType(valuePath);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// @ts-ignore
|
||||
case t.SpreadElement.name: {
|
||||
const resolvedValuePath = resolveToValue(propertyPath.get('argument'));
|
||||
switch (resolvedValuePath.node.type) {
|
||||
// @ts-ignore
|
||||
case t.ObjectExpression.name: // normal object literal
|
||||
amendPropTypes(getDescriptor, resolvedValuePath, documentation, propName);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getDefinePropertyValuePath(nodePath: any, propName: string) {
|
||||
const program = getRoot(nodePath);
|
||||
let resultPath = nodePath;
|
||||
if (!nodePath.node.id) return;
|
||||
const componentName = nodePath.node.id.name;
|
||||
|
||||
visit(program, {
|
||||
visitCallExpression(path) {
|
||||
const args = path.get('arguments');
|
||||
const argsNodeList = args.value;
|
||||
if (
|
||||
argsNodeList.length === 3 &&
|
||||
t.Identifier.check(argsNodeList[0]) &&
|
||||
argsNodeList[0].name === componentName &&
|
||||
t.Literal.check(argsNodeList[1]) &&
|
||||
argsNodeList[1].value === propName
|
||||
) {
|
||||
resultPath = args.get(2);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
});
|
||||
return resultPath;
|
||||
}
|
||||
|
||||
function getPropTypeHandler(propName: string) {
|
||||
return function (documentation: any, path: any) {
|
||||
let propTypesPath = getMemberValuePath(path, propName);
|
||||
if (!propTypesPath) {
|
||||
propTypesPath = getDefinePropertyValuePath(path, propName);
|
||||
if (!propTypesPath) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
propTypesPath = resolveToValue(propTypesPath);
|
||||
if (!propTypesPath) {
|
||||
return;
|
||||
}
|
||||
let getDescriptor;
|
||||
switch (propName) {
|
||||
case 'childContextTypes':
|
||||
getDescriptor = documentation.getChildContextDescriptor;
|
||||
break;
|
||||
case 'contextTypes':
|
||||
getDescriptor = documentation.getContextDescriptor;
|
||||
break;
|
||||
default:
|
||||
getDescriptor = documentation.getPropDescriptor;
|
||||
}
|
||||
amendPropTypes(getDescriptor.bind(documentation), propTypesPath, documentation, propName);
|
||||
};
|
||||
}
|
||||
|
||||
export const propTypeHandler = getPropTypeHandler('propTypes');
|
||||
export const contextTypeHandler = getPropTypeHandler('contextTypes');
|
||||
export const childContextTypeHandler = getPropTypeHandler('childContextTypes');
|
||||
@ -0,0 +1,64 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import { set, get } from 'lodash';
|
||||
import { debug } from '../../../core';
|
||||
|
||||
const log = debug.extend('parse:js');
|
||||
|
||||
const parseJsDoc = require('react-docgen/dist/utils/parseJsDoc').default;
|
||||
const { getMemberValuePath, resolveToValue } = require('react-docgen').utils;
|
||||
|
||||
function getType(type = 'void') {
|
||||
const typeOfType = typeof type;
|
||||
if (typeOfType === 'string') {
|
||||
return typeOfType;
|
||||
} else if (typeOfType === 'object') {
|
||||
return get(type, 'name', 'void');
|
||||
}
|
||||
return 'void';
|
||||
}
|
||||
|
||||
function generateRaw(params = [], returns = { type: 'void' }): string {
|
||||
const raw = `(${params.filter(x => !!x).map(x => `${x.name}: ${getType(x.type)}`).join(', ')}) => ${returns ? getType(returns.type) : 'void'}`;
|
||||
return raw;
|
||||
}
|
||||
|
||||
function resolveDocumentation(documentation) {
|
||||
documentation._props.forEach(propDescriptor => {
|
||||
const { description } = propDescriptor;
|
||||
if (description.includes('@') && propDescriptor?.type?.name === 'func') {
|
||||
const jsDoc = parseJsDoc(description);
|
||||
propDescriptor.description = jsDoc.description;
|
||||
if (jsDoc.params) {
|
||||
set(propDescriptor, ['type', 'params'], jsDoc.params);
|
||||
}
|
||||
if (jsDoc.returns) {
|
||||
set(propDescriptor, ['type', 'returns'], jsDoc.returns);
|
||||
}
|
||||
try {
|
||||
const raw = generateRaw(jsDoc.params, jsDoc.returns);
|
||||
if (raw) {
|
||||
set(propDescriptor, ['type', 'raw'], raw);
|
||||
}
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract info from the propType jsdoc blocks. Must be run after
|
||||
* propDocBlockHandler.
|
||||
*/
|
||||
export default function propTypeJsDocHandler(documentation, path) {
|
||||
let propTypesPath = getMemberValuePath(path, 'propTypes');
|
||||
if (!propTypesPath) {
|
||||
return;
|
||||
}
|
||||
propTypesPath = resolveToValue(propTypesPath);
|
||||
if (!propTypesPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
resolveDocumentation(documentation);
|
||||
}
|
||||
42
packages/material-parser/src/parse/js/index.ts
Normal file
42
packages/material-parser/src/parse/js/index.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { transformItem } from '../transform';
|
||||
import { IMaterialParsedModel } from '../../types';
|
||||
import { loadFile } from '../../utils';
|
||||
import resolver from './resolver';
|
||||
import handlers from './handlers';
|
||||
|
||||
const reactDocs = require('react-docgen');
|
||||
|
||||
export default function parse(filePath: string): IMaterialParsedModel[] {
|
||||
if (!filePath) return [];
|
||||
const fileContent = loadFile(filePath);
|
||||
const result = reactDocs.parse(
|
||||
fileContent,
|
||||
(ast: any) => {
|
||||
ast.__path = filePath;
|
||||
return resolver(ast);
|
||||
},
|
||||
handlers,
|
||||
{
|
||||
filename: filePath,
|
||||
},
|
||||
);
|
||||
const coms = result.reduce((res: any[], info: any) => {
|
||||
if (!info || !info.props) return res;
|
||||
const props = Object.keys(info.props).reduce((acc: any[], name) => {
|
||||
try {
|
||||
const item: any = transformItem(name, info.props[name]);
|
||||
acc.push(item);
|
||||
} catch (e) {
|
||||
// TODO
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
res.push({
|
||||
componentName: info.displayName,
|
||||
props,
|
||||
meta: info.meta || {},
|
||||
});
|
||||
return res;
|
||||
}, []);
|
||||
return coms;
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
export default function checkIsIIFE(path: any) {
|
||||
return (
|
||||
path.value &&
|
||||
path.value.callee &&
|
||||
path.value.callee.type === 'FunctionExpression' &&
|
||||
path.node.type === 'CallExpression'
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
import { namedTypes as t } from 'ast-types';
|
||||
import isReactComponentStaticMember from './isReactComponentStaticMember';
|
||||
|
||||
const { match } = require('react-docgen').utils;
|
||||
const { traverseShallow } = require('react-docgen/dist/utils/traverse');
|
||||
|
||||
function findAssignedMethods(scope: any, idPath: any) {
|
||||
const results: any[] = [];
|
||||
|
||||
if (!t.Identifier.check(idPath.node)) {
|
||||
return results;
|
||||
}
|
||||
|
||||
const { name } = idPath.node;
|
||||
// const idScope = idPath.scope.lookup(idPath.node.name);
|
||||
|
||||
traverseShallow(scope.path, {
|
||||
visitAssignmentExpression(path: any) {
|
||||
const { node } = path;
|
||||
if (
|
||||
match(node.left, {
|
||||
type: 'MemberExpression',
|
||||
object: { type: 'Identifier', name },
|
||||
})
|
||||
// && path.scope.lookup(name) === idScope
|
||||
) {
|
||||
results.push(path);
|
||||
return false;
|
||||
}
|
||||
return this.traverse(path);
|
||||
},
|
||||
});
|
||||
|
||||
return results.filter((x) => !isReactComponentStaticMember(x.get('left')));
|
||||
}
|
||||
|
||||
export default findAssignedMethods;
|
||||
393
packages/material-parser/src/parse/js/resolver/index.ts
Normal file
393
packages/material-parser/src/parse/js/resolver/index.ts
Normal file
@ -0,0 +1,393 @@
|
||||
import { namedTypes as t, visit } from 'ast-types';
|
||||
import { uniqBy } from 'lodash';
|
||||
import checkIsIIFE from './checkIsIIFE';
|
||||
import resolveHOC from './resolveHOC';
|
||||
import resolveIIFE from './resolveIIFE';
|
||||
import resolveImport from './resolveImport';
|
||||
import resolveTranspiledClass from './resolveTranspiledClass';
|
||||
import isStaticMethod from './isStaticMethod';
|
||||
import findAssignedMethods from './findAssignedMethods';
|
||||
import resolveExportDeclaration from './resolveExportDeclaration';
|
||||
import makeProxy from '../utils/makeProxy';
|
||||
import { get, set, has, ICache } from '../utils/cache';
|
||||
import getName from '../utils/getName';
|
||||
import getRoot from '../utils/getRoot';
|
||||
|
||||
const expressionTo = require('react-docgen/dist/utils/expressionTo');
|
||||
|
||||
const {
|
||||
isExportsOrModuleAssignment,
|
||||
isReactComponentClass,
|
||||
isReactCreateClassCall,
|
||||
isReactForwardRefCall,
|
||||
isStatelessComponent,
|
||||
normalizeClassDefinition,
|
||||
resolveToValue,
|
||||
getMemberValuePath,
|
||||
} = require('react-docgen').utils;
|
||||
|
||||
function ignore() {
|
||||
return false;
|
||||
}
|
||||
|
||||
function isComponentDefinition(path: any) {
|
||||
return (
|
||||
isReactCreateClassCall(path) ||
|
||||
isReactComponentClass(path) ||
|
||||
isStatelessComponent(path) ||
|
||||
isReactForwardRefCall(path)
|
||||
);
|
||||
}
|
||||
|
||||
function resolveDefinition(definition: any) {
|
||||
if (isReactCreateClassCall(definition)) {
|
||||
// return argument
|
||||
const resolvedPath = resolveToValue(definition.get('arguments', 0));
|
||||
if (t.ObjectExpression.check(resolvedPath.node)) {
|
||||
return resolvedPath;
|
||||
}
|
||||
} else if (isReactComponentClass(definition)) {
|
||||
normalizeClassDefinition(definition);
|
||||
return definition;
|
||||
} else if (isStatelessComponent(definition) || isReactForwardRefCall(definition)) {
|
||||
return definition;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getDefinition(definition: any, cache: ICache = {}): any {
|
||||
const { __meta: exportMeta = {} } = definition;
|
||||
if (checkIsIIFE(definition)) {
|
||||
definition = resolveToValue(resolveIIFE(definition));
|
||||
if (!isComponentDefinition(definition)) {
|
||||
definition = resolveTranspiledClass(definition);
|
||||
}
|
||||
} else {
|
||||
definition = resolveToValue(resolveHOC(definition));
|
||||
if (isComponentDefinition(definition)) {
|
||||
definition = makeProxy(definition, {
|
||||
__meta: exportMeta,
|
||||
});
|
||||
return definition;
|
||||
}
|
||||
if (checkIsIIFE(definition)) {
|
||||
definition = resolveToValue(resolveIIFE(definition));
|
||||
if (!isComponentDefinition(definition)) {
|
||||
definition = resolveTranspiledClass(definition);
|
||||
}
|
||||
} else if (t.SequenceExpression.check(definition.node)) {
|
||||
const classNameNode = definition.parent.get('id').node;
|
||||
const localNames: string[] = [];
|
||||
let { node } = definition.get('expressions', 0);
|
||||
while (t.AssignmentExpression.check(node)) {
|
||||
// @ts-ignore
|
||||
const { name } = node.left;
|
||||
if (name) {
|
||||
localNames.push(name);
|
||||
}
|
||||
node = node.right;
|
||||
}
|
||||
definition.get('expressions').each((x: any) => {
|
||||
if (!x.name) return;
|
||||
if (t.AssignmentExpression.check(x.node) && t.MemberExpression.check(x.node.left)) {
|
||||
const objectName = x.node.left.object.name;
|
||||
if (localNames.includes(objectName)) {
|
||||
x.get('left', 'object').replace(classNameNode);
|
||||
}
|
||||
}
|
||||
});
|
||||
definition = getDefinition(resolveToValue(definition.get('expressions').get(0)), cache);
|
||||
} else {
|
||||
return resolveImport(definition, (ast: any, sourcePath: string, importMeta, mode) => {
|
||||
let result;
|
||||
if (has('ast-export', ast.__path)) {
|
||||
result = get('ast-export', ast.__path);
|
||||
} else {
|
||||
result = findAllExportedComponentDefinition(ast);
|
||||
set('ast-export', ast.__path, result);
|
||||
}
|
||||
|
||||
const exportList: any[] = [];
|
||||
const importList: any[] = [];
|
||||
result.forEach((def: any) => {
|
||||
const { __meta: meta = {} } = def;
|
||||
let { exportName } = meta;
|
||||
for (const item of importMeta) {
|
||||
if (exportName === item.importedName) {
|
||||
exportName = item.localName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (exportName) {
|
||||
importList.push(makeProxy(def, { __meta: { exportName } }));
|
||||
}
|
||||
|
||||
const nextMeta: any = {
|
||||
exportName,
|
||||
};
|
||||
|
||||
if (exportName === exportMeta.localName) {
|
||||
nextMeta.exportName = exportMeta.exportName;
|
||||
} else if (mode === 'import') {
|
||||
// } else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (exportMeta.subName) {
|
||||
nextMeta.subName = exportMeta.subName;
|
||||
} else if (meta.subName) {
|
||||
nextMeta.subName = meta.subName;
|
||||
}
|
||||
exportList.push(makeProxy(def, { __meta: nextMeta }));
|
||||
});
|
||||
cache[sourcePath] = importList;
|
||||
|
||||
// result = result.filter((x) => !x.__shouldDelete);
|
||||
return exportList;
|
||||
});
|
||||
}
|
||||
}
|
||||
if (definition && (!definition.__meta || Object.keys(definition.__meta).length === 0)) {
|
||||
definition.__meta = exportMeta;
|
||||
}
|
||||
return definition;
|
||||
}
|
||||
|
||||
export interface IMethodsPath {
|
||||
subName: string;
|
||||
localName: string;
|
||||
value: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract all flow types for the methods of a react component. Doesn't
|
||||
* return any react specific lifecycle methods.
|
||||
*/
|
||||
function getSubComponents(path: any, scope: any, cache: ICache) {
|
||||
// Extract all methods from the class or object.
|
||||
let methodPaths = [];
|
||||
if (isReactComponentClass(path)) {
|
||||
methodPaths = path.get('body', 'body').filter(isStaticMethod);
|
||||
methodPaths = [...methodPaths, ...findAssignedMethods(scope || path.scope, path.get('id'))];
|
||||
} else if (t.ObjectExpression.check(path.node)) {
|
||||
methodPaths = path.get('properties').filter(isStaticMethod);
|
||||
methodPaths = [...methodPaths, ...findAssignedMethods(scope || path.scope, path.get('id'))];
|
||||
// Add the statics object properties.
|
||||
const statics = getMemberValuePath(path, 'statics');
|
||||
if (statics) {
|
||||
statics.get('properties').each((p: any) => {
|
||||
if (isStaticMethod(p)) {
|
||||
p.node.static = true;
|
||||
methodPaths.push(p);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (
|
||||
t.VariableDeclarator.check(path.parent.node) &&
|
||||
path.parent.node.init === path.node &&
|
||||
t.Identifier.check(path.parent.node.id)
|
||||
) {
|
||||
methodPaths = findAssignedMethods(scope || path.parent.scope, path.parent.get('id'));
|
||||
} else if (
|
||||
t.AssignmentExpression.check(path.parent.node) &&
|
||||
path.parent.node.right === path.node &&
|
||||
t.Identifier.check(path.parent.node.left)
|
||||
) {
|
||||
methodPaths = findAssignedMethods(scope || path.parent.scope, path.parent.get('left'));
|
||||
} else if (t.FunctionDeclaration.check(path.node)) {
|
||||
methodPaths = findAssignedMethods(scope || path.parent.scope, path.get('id'));
|
||||
} else if (t.ArrowFunctionExpression.check(path.node)) {
|
||||
methodPaths = findAssignedMethods(scope || path.parent.scope, path.parent.get('id'));
|
||||
}
|
||||
|
||||
return (
|
||||
methodPaths
|
||||
.map((x: any) => {
|
||||
if (t.ClassProperty.check(x.node)) {
|
||||
return {
|
||||
value: x.get('value'),
|
||||
subName: x.node.key.name,
|
||||
localName: getName(x.get('value')),
|
||||
};
|
||||
}
|
||||
return {
|
||||
value: x,
|
||||
subName: x.node.left.property.name,
|
||||
localName: getName(x.get('right')),
|
||||
};
|
||||
})
|
||||
.map(({ subName, localName, value }: IMethodsPath) => ({
|
||||
subName,
|
||||
localName,
|
||||
value: resolveToValue(value),
|
||||
}))
|
||||
.map(({ subName, localName, value }: IMethodsPath) => {
|
||||
let def = getDefinition(
|
||||
makeProxy(value, {
|
||||
__meta: {
|
||||
localName,
|
||||
subName,
|
||||
exportName: path.__meta && path.__meta.exportName,
|
||||
},
|
||||
}),
|
||||
cache,
|
||||
);
|
||||
if (!Array.isArray(def)) {
|
||||
def = [def];
|
||||
}
|
||||
return {
|
||||
subName,
|
||||
localName,
|
||||
value: def.flatMap((x: any) => x).filter((x: any) => isComponentDefinition(x)),
|
||||
};
|
||||
})
|
||||
.map(({ subName, localName, value }: IMethodsPath) => {
|
||||
return value.map((x: any) => ({
|
||||
subName,
|
||||
localName,
|
||||
value: x,
|
||||
}));
|
||||
})
|
||||
// @ts-ignore
|
||||
.flatMap((x: any) => x)
|
||||
.map(({ subName, value }: IMethodsPath) => {
|
||||
const __meta = {
|
||||
subName,
|
||||
exportName: path.__meta && path.__meta.exportName,
|
||||
};
|
||||
return makeProxy(value, { __meta });
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an AST, this function tries to find the exported component definition.
|
||||
*
|
||||
* The component definition is either the ObjectExpression passed to
|
||||
* `React.createClass` or a `class` definition extending `React.Component` or
|
||||
* having a `render()` method.
|
||||
*
|
||||
* If a definition is part of the following statements, it is considered to be
|
||||
* exported:
|
||||
*
|
||||
* modules.exports = Definition;
|
||||
* exports.foo = Definition;
|
||||
* export default Definition;
|
||||
* export var Definition = ...;
|
||||
*/
|
||||
export default function findAllExportedComponentDefinition(ast: any) {
|
||||
const components: any[] = [];
|
||||
const cache: ICache = {};
|
||||
let programScope: any;
|
||||
|
||||
function exportDeclaration(path: any) {
|
||||
const definitions = resolveExportDeclaration(path)
|
||||
.reduce((acc: any[], definition: any) => {
|
||||
if (isComponentDefinition(definition)) {
|
||||
acc.push(definition);
|
||||
} else {
|
||||
definition = getDefinition(definition, cache);
|
||||
if (!Array.isArray(definition)) {
|
||||
definition = [definition];
|
||||
}
|
||||
definition.forEach((def: any) => {
|
||||
if (isComponentDefinition(def)) {
|
||||
acc.push(def);
|
||||
}
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
}, [])
|
||||
.map((definition: any) => {
|
||||
const { __meta: meta } = definition;
|
||||
const def = resolveDefinition(definition);
|
||||
return makeProxy(def, { __meta: meta });
|
||||
});
|
||||
|
||||
if (definitions.length === 0) {
|
||||
return false;
|
||||
}
|
||||
definitions.forEach((definition: any) => {
|
||||
if (definition && components.indexOf(definition) === -1) {
|
||||
components.push(definition);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
visit(ast, {
|
||||
visitProgram(path) {
|
||||
programScope = path.scope;
|
||||
return this.traverse(path);
|
||||
},
|
||||
visitFunctionDeclaration: ignore,
|
||||
visitFunctionExpression: ignore,
|
||||
visitClassDeclaration: ignore,
|
||||
visitClassExpression: ignore,
|
||||
visitIfStatement: ignore,
|
||||
visitWithStatement: ignore,
|
||||
visitSwitchStatement: ignore,
|
||||
visitWhileStatement: ignore,
|
||||
visitDoWhileStatement: ignore,
|
||||
visitForStatement: ignore,
|
||||
visitForInStatement: ignore,
|
||||
visitForOfStatement: ignore,
|
||||
visitImportDeclaration: ignore,
|
||||
|
||||
visitExportNamedDeclaration: exportDeclaration,
|
||||
visitExportDefaultDeclaration: exportDeclaration,
|
||||
visitExportAllDeclaration(path) {
|
||||
components.push(...resolveImport(path, findAllExportedComponentDefinition));
|
||||
return false;
|
||||
},
|
||||
|
||||
visitAssignmentExpression(path: any) {
|
||||
// Ignore anything that is not `exports.X = ...;` or
|
||||
// `module.exports = ...;`
|
||||
if (!isExportsOrModuleAssignment(path)) {
|
||||
return false;
|
||||
}
|
||||
const arr = expressionTo.Array(path.get('left'));
|
||||
const meta: any = {
|
||||
exportName: arr[1] === 'exports' ? 'default' : arr[1],
|
||||
};
|
||||
// Resolve the value of the right hand side. It should resolve to a call
|
||||
// expression, something like React.createClass
|
||||
path = resolveToValue(path.get('right'));
|
||||
if (!isComponentDefinition(path)) {
|
||||
path = getDefinition(path, cache);
|
||||
}
|
||||
|
||||
if (!Array.isArray(path)) {
|
||||
path = [path];
|
||||
}
|
||||
|
||||
const definitions = path.map(resolveDefinition);
|
||||
|
||||
definitions.forEach((definition: any) => {
|
||||
if (definition && components.indexOf(definition) === -1) {
|
||||
// if (definition.__meta) {
|
||||
definition = makeProxy(definition, {
|
||||
__meta: meta,
|
||||
});
|
||||
// }
|
||||
components.push(definition);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
const result = components.reduce((acc, item) => {
|
||||
let subModuleDefinitions = [];
|
||||
subModuleDefinitions = getSubComponents(item, programScope, cache);
|
||||
return [...acc, item, ...subModuleDefinitions];
|
||||
}, []);
|
||||
|
||||
const res = uniqBy(result, (x: any) => {
|
||||
return `${getRoot(x)?.node?.__path}/${x.__meta.exportName}/${x.__meta.subName}`;
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
import { namedTypes as t } from 'ast-types';
|
||||
|
||||
const { getPropertyName } = require('react-docgen').utils;
|
||||
|
||||
const reactStaticMembers = ['propTypes', 'defaultProps', 'contextTypes'];
|
||||
export default function isReactComponentStaticMember(methodPath: any) {
|
||||
let name;
|
||||
if (t.MemberExpression.check(methodPath.node)) {
|
||||
name = methodPath.node.property.name;
|
||||
} else {
|
||||
name = getPropertyName(methodPath);
|
||||
}
|
||||
return !!name && reactStaticMembers.indexOf(name) !== -1;
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
import { namedTypes as t } from 'ast-types';
|
||||
import isReactComponentStaticMember from './isReactComponentStaticMember';
|
||||
|
||||
const { isReactComponentMethod } = require('react-docgen').utils;
|
||||
|
||||
/**
|
||||
* judge if static method
|
||||
*/
|
||||
function isStaticMethod(path: any) {
|
||||
const isProbablyStaticMethod = t.ClassProperty.check(path.node) && path.node.static === true;
|
||||
|
||||
return isProbablyStaticMethod && !isReactComponentStaticMember(path) && !isReactComponentMethod(path);
|
||||
}
|
||||
|
||||
export default isStaticMethod;
|
||||
@ -0,0 +1,53 @@
|
||||
import { namedTypes as t } from 'ast-types';
|
||||
import makeProxy from '../utils/makeProxy';
|
||||
import getName from '../utils/getName';
|
||||
|
||||
export default function resolveExportDeclaration(path: any) {
|
||||
const definitions = [];
|
||||
if (path.node.default || t.ExportDefaultDeclaration.check(path.node)) {
|
||||
const def = path.get('declaration');
|
||||
const meta: { [name: string]: string } = {
|
||||
exportName: 'default',
|
||||
localName: getName(def),
|
||||
};
|
||||
|
||||
definitions.push(makeProxy(def, { __meta: meta }));
|
||||
} else if (path.node.declaration) {
|
||||
if (t.VariableDeclaration.check(path.node.declaration)) {
|
||||
path.get('declaration', 'declarations').each((declarator: any) => {
|
||||
definitions.push(
|
||||
makeProxy(declarator, {
|
||||
__meta: {
|
||||
exportName: declarator.get('id').node.name,
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
} else {
|
||||
const def = path.get('declaration');
|
||||
definitions.push(
|
||||
makeProxy(def, {
|
||||
__meta: {
|
||||
exportName: 'default',
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
} else if (path.node.specifiers) {
|
||||
path.get('specifiers').each((specifier: any) => {
|
||||
const def = specifier.node.id ? specifier.get('id') : specifier.get('local');
|
||||
const exportName = specifier.get('exported').node.name;
|
||||
const localName = def.get('local').node.name;
|
||||
|
||||
definitions.push(
|
||||
makeProxy(def, {
|
||||
__meta: {
|
||||
exportName,
|
||||
localName,
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
return definitions;
|
||||
}
|
||||
42
packages/material-parser/src/parse/js/resolver/resolveHOC.ts
Normal file
42
packages/material-parser/src/parse/js/resolver/resolveHOC.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { namedTypes as t } from 'ast-types';
|
||||
|
||||
const { isReactCreateClassCall, isReactForwardRefCall } = require('react-docgen').utils;
|
||||
|
||||
/**
|
||||
* If the path is a call expression, it recursively resolves to the
|
||||
* rightmost argument, stopping if it finds a React.createClass call expression
|
||||
*
|
||||
* Else the path itself is returned.
|
||||
*/
|
||||
export default function resolveHOC(path: any): any {
|
||||
const { node } = path;
|
||||
if (
|
||||
t.CallExpression.check(node) &&
|
||||
!isReactCreateClassCall(path) &&
|
||||
!isReactForwardRefCall(path)
|
||||
) {
|
||||
if (node.arguments.length) {
|
||||
const inner = path.get('arguments', 0);
|
||||
|
||||
// If the first argument is one of these types then the component might be the last argument
|
||||
// If there are all identifiers then we cannot figure out exactly and have to assume it is the first
|
||||
if (
|
||||
node.arguments.length > 1 &&
|
||||
(t.Literal.check(inner.node) ||
|
||||
t.ObjectExpression.check(inner.node) ||
|
||||
t.ArrayExpression.check(inner.node) ||
|
||||
t.SpreadElement.check(inner.node))
|
||||
) {
|
||||
return resolveHOC(
|
||||
// resolveToValue(path.get('arguments', node.arguments.length - 1)),
|
||||
path.get('arguments', node.arguments.length - 1),
|
||||
);
|
||||
}
|
||||
|
||||
// return resolveHOC(resolveToValue(inner));
|
||||
return resolveHOC(inner);
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
import checkIsIIFE from './checkIsIIFE';
|
||||
|
||||
const resolveFunctionDefinitionToReturnValue = require('react-docgen/dist/utils/resolveFunctionDefinitionToReturnValue')
|
||||
.default;
|
||||
/**
|
||||
* If the path is a call expression, it recursively resolves to the
|
||||
* rightmost argument, stopping if it finds a React.createClass call expression
|
||||
*
|
||||
* Else the path itself is returned.
|
||||
*/
|
||||
export default function resolveIIFE(path: any) {
|
||||
if (!checkIsIIFE(path)) {
|
||||
return path;
|
||||
}
|
||||
const returnValue = resolveFunctionDefinitionToReturnValue(
|
||||
path.get('callee'),
|
||||
);
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
178
packages/material-parser/src/parse/js/resolver/resolveImport.ts
Normal file
178
packages/material-parser/src/parse/js/resolver/resolveImport.ts
Normal file
@ -0,0 +1,178 @@
|
||||
import { namedTypes as t } from 'ast-types';
|
||||
import fs from 'fs';
|
||||
import p from 'path';
|
||||
import getRoot from '../utils/getRoot';
|
||||
|
||||
const { resolveToModule, resolveToValue, match } = require('react-docgen').utils;
|
||||
|
||||
export function isImportLike(path) {
|
||||
const { node } = path;
|
||||
return (
|
||||
t.ImportDeclaration.check(node) ||
|
||||
t.ExportAllDeclaration.check(node) ||
|
||||
t.ExportNamedDeclaration.check(node)
|
||||
);
|
||||
}
|
||||
|
||||
export function isRequireLike(path: any) {
|
||||
if (
|
||||
t.CallExpression.check(path.node) &&
|
||||
t.Identifier.check(path.get('callee').node) &&
|
||||
path.get('callee').node.name === 'require' &&
|
||||
t.Literal.check(path.get('arguments', 0)?.node)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function resolveToImport(initialPath) {
|
||||
const pathBuffer = [initialPath];
|
||||
|
||||
while (pathBuffer.length) {
|
||||
let path = pathBuffer.shift();
|
||||
const node = path.node;
|
||||
switch (node.type) {
|
||||
case 'VariableDeclarator':
|
||||
if (node.init) {
|
||||
pathBuffer.unshift(path.get('init'));
|
||||
}
|
||||
break;
|
||||
case 'CallExpression': {
|
||||
if (match(node.callee, { type: 'Identifier', name: 'require' })) {
|
||||
return path;
|
||||
}
|
||||
const paths = [path.get('callee')];
|
||||
const argumentsPath = path.get('arguments');
|
||||
for (let index = 0; index < argumentsPath.value.length; index++) {
|
||||
paths.push(argumentsPath.get(index));
|
||||
}
|
||||
pathBuffer.unshift(...paths);
|
||||
}
|
||||
case 'Identifier':
|
||||
case 'JSXIdentifier': {
|
||||
const valuePath = resolveToValue(path);
|
||||
if (valuePath !== path) {
|
||||
pathBuffer.unshift(valuePath);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'ImportDeclaration':
|
||||
return path;
|
||||
case 'MemberExpression':
|
||||
while (path && t.MemberExpression.check(path.node)) {
|
||||
path = path.get('object');
|
||||
}
|
||||
if (path) {
|
||||
pathBuffer.unshift(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function getPath(path: any, name: any) {
|
||||
const root = getRoot(path).node;
|
||||
if (!root) return;
|
||||
let { __path } = root;
|
||||
__path = p.dirname(__path);
|
||||
// is directory
|
||||
if (fs.existsSync(p.resolve(__path, name))) {
|
||||
name += '/index';
|
||||
}
|
||||
const suffix = suffixes.find((suf) => {
|
||||
return fs.existsSync(p.resolve(__path, name + suf));
|
||||
});
|
||||
if (!suffix) return;
|
||||
return p.resolve(__path, name + suffix);
|
||||
}
|
||||
|
||||
const buildParser = require('react-docgen/dist/babelParser').default;
|
||||
|
||||
const suffixes = ['.js', '.jsx', '.ts', '.tsx'];
|
||||
|
||||
const cache: {
|
||||
[name: string]: any;
|
||||
} = {};
|
||||
|
||||
export default function resolveImport(path: any, callback: any) {
|
||||
let name;
|
||||
let mode: 'import' | 'require' = 'import';
|
||||
|
||||
let importPath;
|
||||
if (path.name === 'local') {
|
||||
name = path.parentPath.parentPath.parentPath.node.source.value;
|
||||
importPath = path;
|
||||
} else {
|
||||
importPath = resolveToImport(path);
|
||||
if (!importPath) {
|
||||
return path;
|
||||
}
|
||||
if (isImportLike(importPath)) {
|
||||
name = importPath.node.source.value;
|
||||
} else if (isRequireLike(importPath)) {
|
||||
const moduleName = resolveToModule(importPath);
|
||||
if (typeof moduleName === 'string') {
|
||||
mode = 'require';
|
||||
name = moduleName;
|
||||
}
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
if (name) {
|
||||
const __path = getPath(path, name);
|
||||
if (!__path) return path;
|
||||
let ast;
|
||||
if (!cache[__path]) {
|
||||
const fileContent = fs.readFileSync(__path, 'utf8');
|
||||
const parser = buildParser({ filename: __path });
|
||||
ast = parser.parse(fileContent);
|
||||
ast.__src = fileContent;
|
||||
ast.__path = __path;
|
||||
cache[__path] = ast;
|
||||
} else {
|
||||
ast = cache[__path];
|
||||
}
|
||||
|
||||
const importMeta: any[] = [];
|
||||
if (mode === 'import') {
|
||||
if (t.ImportDeclaration.check(importPath.node)) {
|
||||
// @ts-ignore
|
||||
const specifiers = importPath.get('specifiers');
|
||||
specifiers.each((spec: any) => {
|
||||
const { node } = spec;
|
||||
importMeta.push({
|
||||
localName: node.local.name,
|
||||
importedName: node.imported ? node.imported.name : 'default',
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const idPath = importPath.parentPath.get('id');
|
||||
if (t.Identifier.check(idPath.node)) {
|
||||
importMeta.push({
|
||||
localName: 'default',
|
||||
importedName: idPath.node.name,
|
||||
});
|
||||
} else if (t.ObjectPattern.check(path.node)) {
|
||||
path.get('properties').each((propertyPath) => {
|
||||
const keyPath = propertyPath.get('key');
|
||||
const valuePath = propertyPath.get('value');
|
||||
if (t.Identifier.check(keyPath.node) && t.Identifier.check(valuePath.node)) {
|
||||
importMeta.push({
|
||||
localName: keyPath.node.name,
|
||||
importedName: valuePath.node.name,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return callback(ast, __path, importMeta, mode);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
import { builders, NodePath, visit } from 'ast-types';
|
||||
/**
|
||||
* If the path is a call expression, it recursively resolves to the
|
||||
* rightmost argument, stopping if it finds a React.createClass call expression
|
||||
*
|
||||
* Else the path itself is returned.
|
||||
*/
|
||||
export default function resolveTranspiledClass(path: any) {
|
||||
let classPath = path;
|
||||
visit(path, {
|
||||
visitFunctionDeclaration(arg) {
|
||||
classPath = new NodePath(
|
||||
builders.functionDeclaration(
|
||||
// @ts-ignore
|
||||
arg.node.id || 'Default',
|
||||
[],
|
||||
builders.blockStatement([
|
||||
builders.returnStatement(
|
||||
builders.jsxElement(
|
||||
builders.jsxOpeningElement(builders.jsxIdentifier('div'), [], true),
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
path.parent,
|
||||
);
|
||||
return false;
|
||||
},
|
||||
});
|
||||
return classPath;
|
||||
}
|
||||
18
packages/material-parser/src/parse/js/utils/cache.ts
Normal file
18
packages/material-parser/src/parse/js/utils/cache.ts
Normal file
@ -0,0 +1,18 @@
|
||||
export interface ICache {
|
||||
[name: string]: any;
|
||||
}
|
||||
|
||||
const cache: ICache = {};
|
||||
|
||||
export function set(scope: string, name: string, value: any) {
|
||||
cache[scope] = cache[scope] || {};
|
||||
cache[scope][name] = value;
|
||||
}
|
||||
|
||||
export function get(scope: string, name: string) {
|
||||
return (cache[scope] || {})[name];
|
||||
}
|
||||
|
||||
export function has(scope: string, name: string) {
|
||||
return cache[scope] && Object.prototype.hasOwnProperty.call(cache[scope], name);
|
||||
}
|
||||
103
packages/material-parser/src/parse/js/utils/evaluate.ts
Normal file
103
packages/material-parser/src/parse/js/utils/evaluate.ts
Normal file
@ -0,0 +1,103 @@
|
||||
const { namedTypes: t } = require('ast-types');
|
||||
|
||||
const { resolveToValue } = require('react-docgen').utils;
|
||||
|
||||
function isInfinity(path) {
|
||||
return t.Identifier.check(path.node) && path.node.name === 'Infinity';
|
||||
}
|
||||
|
||||
function wrapValue(value, confident = true) {
|
||||
return {
|
||||
value,
|
||||
confident,
|
||||
};
|
||||
}
|
||||
|
||||
export default function evaluate(path: any) {
|
||||
if (t.UnaryExpression.check(path.node)) {
|
||||
if (path.node.operator === 'void') {
|
||||
return wrapValue(undefined);
|
||||
}
|
||||
|
||||
const argument = path.get('argument');
|
||||
|
||||
const result = evaluate(argument);
|
||||
if (!result.confident) {
|
||||
return wrapValue(undefined, false);
|
||||
}
|
||||
|
||||
const arg = result.value;
|
||||
if (arg === undefined) {
|
||||
return wrapValue(undefined);
|
||||
}
|
||||
|
||||
switch (path.node.operator) {
|
||||
case '!':
|
||||
return wrapValue(!arg);
|
||||
case '+':
|
||||
return wrapValue(+arg);
|
||||
case '-':
|
||||
return wrapValue(-arg);
|
||||
case '~':
|
||||
return wrapValue(~arg);
|
||||
case 'typeof':
|
||||
return wrapValue(typeof arg);
|
||||
}
|
||||
}
|
||||
|
||||
if (t.Identifier.check(path.node)) {
|
||||
const valuePath = resolveToValue(path);
|
||||
if (isInfinity(valuePath)) {
|
||||
return wrapValue(undefined);
|
||||
}
|
||||
return evaluate(valuePath);
|
||||
}
|
||||
|
||||
if (t.Literal.check(path.node)) {
|
||||
return wrapValue(path.node.value);
|
||||
}
|
||||
|
||||
if (t.ObjectExpression.check(path.node)) {
|
||||
const returnValue = {};
|
||||
path.get('properties').each((propertyPath) => {
|
||||
const { confident, value } = evaluate(propertyPath.get('value'));
|
||||
if (!confident) {
|
||||
return;
|
||||
}
|
||||
const keyPath = propertyPath.get('key');
|
||||
let key;
|
||||
if (keyPath.node.computed) {
|
||||
const result = evaluate(keyPath);
|
||||
if (!result.confident) {
|
||||
return;
|
||||
}
|
||||
key = result.value;
|
||||
} else {
|
||||
key = keyPath.node.name;
|
||||
}
|
||||
returnValue[key] = value;
|
||||
});
|
||||
|
||||
return wrapValue(returnValue);
|
||||
}
|
||||
|
||||
if (t.ArrayExpression.check(path.node)) {
|
||||
const value = [];
|
||||
let isValid = true;
|
||||
path.get('elements').each((x) => {
|
||||
if (!isValid) return;
|
||||
const result = evaluate(x);
|
||||
if (!result.confident) {
|
||||
isValid = false;
|
||||
} else {
|
||||
value.push(result.value);
|
||||
}
|
||||
});
|
||||
|
||||
if (isValid) {
|
||||
return wrapValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
return wrapValue(undefined, false);
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
import fs from 'fs';
|
||||
|
||||
const suffixes = ['js', 'jsx'];
|
||||
export default function findJSFilePath(fileBasePath: string): string {
|
||||
let filePath;
|
||||
for (const suffix of suffixes) {
|
||||
const fp = `${fileBasePath}.${suffix}`;
|
||||
if (fs.existsSync(fp)) {
|
||||
filePath = fp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return filePath;
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
import { namedTypes as t, visit } from 'ast-types';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import getRoot from './getRoot';
|
||||
import findJSFilePath from './findJSFilePath';
|
||||
|
||||
const buildParser = require('react-docgen/dist/babelParser').default;
|
||||
const expressionTo = require('react-docgen/dist/utils/expressionTo');
|
||||
|
||||
const {
|
||||
resolveToValue,
|
||||
isExportsOrModuleAssignment,
|
||||
} = require('react-docgen').utils;
|
||||
|
||||
export default function getComposedPropTypesPath(documentation, propName, p) {
|
||||
const composes: string[] = Array.from(documentation._composes);
|
||||
let _path = null;
|
||||
const root = getRoot(p).node;
|
||||
for (const compose of composes) {
|
||||
const composePath = findJSFilePath(path.resolve(path.dirname(root.__path), compose));
|
||||
if (!composePath) continue;
|
||||
|
||||
const fileContent = fs.readFileSync(composePath, 'utf8');
|
||||
const parser = buildParser({ filename: composePath });
|
||||
const ast = parser.parse(fileContent);
|
||||
|
||||
visit(ast, {
|
||||
visitAssignmentExpression(path: any) {
|
||||
// Ignore anything that is not `exports.X = ...;` or
|
||||
// `module.exports = ...;`
|
||||
if (!isExportsOrModuleAssignment(path)) {
|
||||
return false;
|
||||
}
|
||||
const arr = expressionTo.Array(path.get('left'));
|
||||
if (!(arr[0] === 'exports' && arr[1] === propName)) return false;
|
||||
|
||||
// Resolve the value of the right hand side. It should resolve to a call
|
||||
// expression, something like React.createClass
|
||||
path = resolveToValue(path.get('right'));
|
||||
_path = path;
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
if (_path) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return _path;
|
||||
}
|
||||
13
packages/material-parser/src/parse/js/utils/getName.ts
Normal file
13
packages/material-parser/src/parse/js/utils/getName.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { namedTypes as t } from 'ast-types';
|
||||
|
||||
export default function (def: any) {
|
||||
let name = '';
|
||||
if (def.node.name) {
|
||||
name = def.node.name;
|
||||
// hoc
|
||||
} else if (t.CallExpression.check(def.node)) {
|
||||
if (def.node.arguments && def.node.arguments.length && t.Identifier.check(def.get('arguments', 0).node)) name = def.get('arguments', 0).node.name;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
7
packages/material-parser/src/parse/js/utils/getRoot.ts
Normal file
7
packages/material-parser/src/parse/js/utils/getRoot.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export default function getRoot(path: any) {
|
||||
let root = path.parent;
|
||||
while (root.parent) {
|
||||
root = root.parent;
|
||||
}
|
||||
return root;
|
||||
}
|
||||
23
packages/material-parser/src/parse/js/utils/makeProxy.ts
Normal file
23
packages/material-parser/src/parse/js/utils/makeProxy.ts
Normal file
@ -0,0 +1,23 @@
|
||||
function makeProxy(target: { [name: string]: any }, meta: any = {}): any {
|
||||
if (target.__isProxy) {
|
||||
const value = target.__getRaw();
|
||||
const rawMeta = target.__getMeta();
|
||||
return makeProxy(value, Object.assign({}, rawMeta, meta));
|
||||
}
|
||||
return new Proxy(target, {
|
||||
get: (obj, prop: string | number) => {
|
||||
if (prop === '__isProxy') return true;
|
||||
if (prop === '__getRaw') return () => target;
|
||||
if (prop === '__getMeta') return () => meta;
|
||||
return Object.prototype.hasOwnProperty.call(meta, prop) ? meta[prop] : obj[prop];
|
||||
},
|
||||
has: (obj, prop) => {
|
||||
return (
|
||||
Object.prototype.hasOwnProperty.call(obj, prop) ||
|
||||
Object.prototype.hasOwnProperty.call(meta, prop)
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export default makeProxy;
|
||||
306
packages/material-parser/src/parse/transform.ts
Normal file
306
packages/material-parser/src/parse/transform.ts
Normal file
@ -0,0 +1,306 @@
|
||||
import { omit, pick, isNil, uniq } from 'lodash';
|
||||
import { safeEval, isEvaluable } from '../utils';
|
||||
import { debug } from '../core';
|
||||
|
||||
const log = debug.extend('parse:transform');
|
||||
|
||||
export function transformType(itemType: any) {
|
||||
if (typeof itemType === 'string') return itemType;
|
||||
const {
|
||||
name,
|
||||
elements,
|
||||
value = elements,
|
||||
computed,
|
||||
required,
|
||||
type,
|
||||
raw,
|
||||
params,
|
||||
returns,
|
||||
} = itemType;
|
||||
if (computed !== undefined && value) {
|
||||
return safeEval(value);
|
||||
}
|
||||
const result: any = {
|
||||
type: name,
|
||||
};
|
||||
if (required) {
|
||||
result.isRequired = required;
|
||||
}
|
||||
switch (name) {
|
||||
case 'number':
|
||||
case 'string':
|
||||
case 'bool':
|
||||
case 'any':
|
||||
case 'symbol':
|
||||
case 'object':
|
||||
case 'null':
|
||||
case 'array':
|
||||
case 'element':
|
||||
case 'node':
|
||||
case 'void':
|
||||
break;
|
||||
case 'func':
|
||||
if (params) {
|
||||
result.params = params.map((x) => {
|
||||
const res: any = {
|
||||
name: x.name,
|
||||
propType: transformType(x.type || x.propType),
|
||||
};
|
||||
if (x.description) {
|
||||
res.description = x.description;
|
||||
}
|
||||
return res;
|
||||
});
|
||||
}
|
||||
if (returns) {
|
||||
result.returns = {
|
||||
propType: transformType(returns.type || returns.propType),
|
||||
};
|
||||
}
|
||||
if (raw) {
|
||||
result.raw = raw;
|
||||
}
|
||||
break;
|
||||
case 'literal': {
|
||||
result.type = 'oneOf';
|
||||
try {
|
||||
const literalValue = safeEval(value);
|
||||
result.value = [literalValue];
|
||||
} catch (e) {
|
||||
result.value = [raw];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'enum':
|
||||
case 'oneOf':
|
||||
result.type = 'oneOf';
|
||||
result.value = value.map(transformType);
|
||||
break;
|
||||
case 'tuple':
|
||||
result.type = 'tuple';
|
||||
result.value = value.map(transformType);
|
||||
break;
|
||||
case 'union': {
|
||||
if (itemType.raw) {
|
||||
if (itemType.raw.match(/ReactNode$/)) {
|
||||
result.type = 'node';
|
||||
break;
|
||||
} else if (itemType.raw.match(/Element$/)) {
|
||||
result.type = 'element';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line no-fallthrough
|
||||
case 'oneOfType':
|
||||
result.type = 'oneOfType';
|
||||
result.value = value.map(transformType);
|
||||
break;
|
||||
case 'boolean':
|
||||
result.type = 'bool';
|
||||
break;
|
||||
case 'Function':
|
||||
result.type = 'func';
|
||||
break;
|
||||
case 'unknown':
|
||||
result.type = 'any';
|
||||
break;
|
||||
case 'Array':
|
||||
case 'arrayOf': {
|
||||
result.type = 'arrayOf';
|
||||
let _itemType = transformType(value[0]);
|
||||
if (typeof _itemType === 'object') {
|
||||
_itemType = omit(_itemType, ['isRequired']);
|
||||
}
|
||||
|
||||
result.value = _itemType;
|
||||
break;
|
||||
}
|
||||
case 'signature': {
|
||||
if (typeof type === 'string') {
|
||||
result.type = type;
|
||||
break;
|
||||
}
|
||||
result.type = 'shape';
|
||||
const properties = type?.signature?.properties || itemType?.signature?.properties || [];
|
||||
if (properties.length === 0) {
|
||||
if (raw?.includes('=>')) {
|
||||
result.type = 'func';
|
||||
result.raw = raw;
|
||||
} else {
|
||||
result.type = 'object';
|
||||
}
|
||||
} else if (properties.length === 1 && typeof properties[0].key === 'object') {
|
||||
const v = transformType(properties[0].value);
|
||||
if (v === 'any') {
|
||||
result.type = 'object';
|
||||
} else if (typeof v === 'string') {
|
||||
result.value = v;
|
||||
result.type = 'objectOf';
|
||||
} else if (typeof v?.type === 'string') {
|
||||
result.value = v.type;
|
||||
result.type = 'objectOf';
|
||||
} else {
|
||||
result.type = 'object';
|
||||
}
|
||||
} else if (properties.length === 1 && properties[0].key === '__call') {
|
||||
result.type = 'func';
|
||||
} else {
|
||||
result.value = properties
|
||||
.filter((item: any) => typeof item.key !== 'object')
|
||||
.map((prop: any) => {
|
||||
const { key } = prop;
|
||||
const typeItem = {
|
||||
...omit(prop.value, 'name'),
|
||||
type: prop.value.type || {},
|
||||
};
|
||||
typeItem.type = {
|
||||
...typeItem.type,
|
||||
...pick(prop.value, ['name', 'value']),
|
||||
};
|
||||
return transformItem(key, typeItem);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'objectOf':
|
||||
case 'instanceOf':
|
||||
result.value = transformType(value);
|
||||
break;
|
||||
case 'exact':
|
||||
case 'shape':
|
||||
result.value = Object.keys(value).map((n) => {
|
||||
const { name: _name, ...others } = value[n];
|
||||
return transformItem(n, {
|
||||
...others,
|
||||
type: {
|
||||
name: _name,
|
||||
},
|
||||
});
|
||||
});
|
||||
break;
|
||||
case (name.match(/ReactNode$/) || {}).input:
|
||||
result.type = 'node';
|
||||
break;
|
||||
case (name.match(/JSX\.Element$/) || {}).input:
|
||||
result.type = 'element';
|
||||
break;
|
||||
default:
|
||||
result.type = 'object';
|
||||
break;
|
||||
}
|
||||
if (Object.keys(result).length === 1) {
|
||||
return result.type;
|
||||
}
|
||||
if (result?.type === 'oneOfType') {
|
||||
return combineOneOfValues(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function combineOneOfValues(propType) {
|
||||
if (propType.type !== 'oneOfType') {
|
||||
return propType;
|
||||
}
|
||||
const newValue = [];
|
||||
let oneOfItem = null;
|
||||
let firstBooleanIndex = -1;
|
||||
propType.value.forEach((item) => {
|
||||
if (item?.type === 'oneOf') {
|
||||
if (!oneOfItem) {
|
||||
oneOfItem = {
|
||||
type: 'oneOf',
|
||||
value: [],
|
||||
};
|
||||
}
|
||||
if (item.value.includes(true) || item.value.includes(false)) {
|
||||
if (firstBooleanIndex !== -1) {
|
||||
oneOfItem.value.splice(firstBooleanIndex, 1);
|
||||
newValue.push('bool');
|
||||
} else {
|
||||
firstBooleanIndex = oneOfItem.value.length;
|
||||
oneOfItem.value = oneOfItem.value.concat(item.value);
|
||||
}
|
||||
} else {
|
||||
oneOfItem.value = oneOfItem.value.concat(item.value);
|
||||
}
|
||||
} else {
|
||||
newValue.push(item);
|
||||
}
|
||||
});
|
||||
let result = propType;
|
||||
const oneOfItemLength = oneOfItem?.value?.length;
|
||||
if (oneOfItemLength) {
|
||||
newValue.push(oneOfItem);
|
||||
}
|
||||
if (firstBooleanIndex !== -1 || oneOfItemLength) {
|
||||
result = {
|
||||
...propType,
|
||||
value: newValue,
|
||||
};
|
||||
}
|
||||
if (result.value.length === 1 && result.value[0]?.type === 'oneOf') {
|
||||
result = {
|
||||
...result,
|
||||
type: 'oneOf',
|
||||
value: result.value[0].value,
|
||||
};
|
||||
}
|
||||
result.value = uniq(result.value);
|
||||
return result;
|
||||
}
|
||||
|
||||
export function transformItem(name: string, item: any) {
|
||||
const {
|
||||
description,
|
||||
flowType,
|
||||
tsType,
|
||||
type = tsType || flowType,
|
||||
optional,
|
||||
required = optional,
|
||||
defaultValue,
|
||||
...others
|
||||
} = item;
|
||||
const result: any = {
|
||||
name,
|
||||
};
|
||||
|
||||
if (type) {
|
||||
result.propType = transformType({
|
||||
...type,
|
||||
...omit(others, ['name']),
|
||||
required: !!required,
|
||||
});
|
||||
}
|
||||
if (description) {
|
||||
if (description.includes('\n')) {
|
||||
result.description = description.split('\n')[0];
|
||||
} else {
|
||||
result.description = description;
|
||||
}
|
||||
}
|
||||
if (!isNil(defaultValue) && typeof defaultValue === 'object' && isEvaluable(defaultValue)) {
|
||||
if (defaultValue === null) {
|
||||
result.defaultValue = defaultValue;
|
||||
} else {
|
||||
// if ('computed' in defaultValue) {
|
||||
// val = val.value;
|
||||
try {
|
||||
const value = safeEval(defaultValue.value);
|
||||
if (isEvaluable(value)) {
|
||||
result.defaultValue = value;
|
||||
}
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
}
|
||||
// else {
|
||||
// result.defaultValue = defaultValue.value;
|
||||
// }
|
||||
}
|
||||
if (result.propType === undefined) {
|
||||
delete result.propType;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
56
packages/material-parser/src/parse/ts/generateDTS.ts
Normal file
56
packages/material-parser/src/parse/ts/generateDTS.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import * as path from 'path';
|
||||
import { writeFileSync, pathExistsSync, ensureDirSync, copySync } from 'fs-extra';
|
||||
import { loadFile } from '../../utils';
|
||||
|
||||
import { debug } from '../../core';
|
||||
|
||||
const log = debug.extend('parse:ts:generate_dts');
|
||||
|
||||
/**
|
||||
* Generate alias dts file by removing some needless interfaces.
|
||||
* Replace original file at present, which will cause type pollution, looking for better solution
|
||||
* @param {string} workDir - the dir containing the module to be parsed
|
||||
* @returns {string} - the path of generated xxx.d.ts
|
||||
*/
|
||||
export default function generateDTS({
|
||||
workDir,
|
||||
dslType = 'react',
|
||||
}: {
|
||||
workDir: string;
|
||||
dslType?: string;
|
||||
}): {
|
||||
originalTypePath: string;
|
||||
newTypePath: string;
|
||||
} {
|
||||
const typeDir = path.join(workDir, 'node_modules', `@types/${dslType}`);
|
||||
const typePath = path.join(typeDir, 'index.d.ts');
|
||||
const fileContent = loadFile(typePath);
|
||||
// const materialParserTypeDir = path.join(workDir, `node_modules/material-parser-types/${type}`);
|
||||
// ensureDirSync(materialParserTypeDir);
|
||||
const materialParserTypeDir = typeDir;
|
||||
const newTypePath = path.join(materialParserTypeDir, 'index.d.ts');
|
||||
// if (!pathExistsSync(newTypePath)) {
|
||||
// copySync(
|
||||
// path.join(typeDir, 'global.d.ts'),
|
||||
// path.join(materialParserTypeDir, 'global.d.ts'),
|
||||
// );
|
||||
let newContent = fileContent.replace(
|
||||
/(?<=interface HTMLAttributes[^e]+)(extends[^}]+)/,
|
||||
`{
|
||||
style?: CSSProperties;
|
||||
className?: string;
|
||||
`,
|
||||
);
|
||||
newContent = newContent.replace(/(?<=interface IntrinsicElements {)([^}]+)/, '');
|
||||
newContent = newContent.replace(/type LibraryManagedAttributes[^;]+;/, '');
|
||||
writeFileSync(newTypePath, newContent);
|
||||
log('generate dts', newTypePath);
|
||||
// } else {
|
||||
// log('found dts', newTypePath);
|
||||
// }
|
||||
|
||||
return {
|
||||
originalTypePath: typePath,
|
||||
newTypePath,
|
||||
};
|
||||
}
|
||||
611
packages/material-parser/src/parse/ts/index.ts
Normal file
611
packages/material-parser/src/parse/ts/index.ts
Normal file
@ -0,0 +1,611 @@
|
||||
import * as path from 'path';
|
||||
import { Parser, ComponentDoc } from 'react-docgen-typescript';
|
||||
import ts, { SymbolFlags, TypeFlags, SyntaxKind } from 'typescript';
|
||||
import { isEmpty, isEqual } from 'lodash';
|
||||
import { existsSync, readFileSync } from 'fs-extra';
|
||||
import findConfig from 'find-config';
|
||||
import { debug } from '../../core';
|
||||
import { Json } from '../../types';
|
||||
import { transformItem } from '../transform';
|
||||
import generateDTS from './generateDTS';
|
||||
import { IParseArgs } from '../index';
|
||||
|
||||
const log = debug.extend('parse:ts');
|
||||
|
||||
type ExtendedType = ts.Type & {
|
||||
id: string;
|
||||
typeArguments: any[];
|
||||
};
|
||||
|
||||
function getNextParentIds(parentIds: number[], type: ts.Type) {
|
||||
// @ts-ignore
|
||||
const id = type?.symbol?.id;
|
||||
if (id) {
|
||||
return [...parentIds, id];
|
||||
}
|
||||
return parentIds;
|
||||
}
|
||||
|
||||
function getSymbolName(symbol: ts.Symbol) {
|
||||
// @ts-ignore
|
||||
const prefix: string = symbol?.parent && getSymbolName(symbol.parent);
|
||||
const name = symbol.getName();
|
||||
if (prefix && prefix.length <= 20) {
|
||||
return `${prefix}.${name}`;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
function getFunctionParams(parameters: any[] = [], checker, parentIds, type) {
|
||||
return parameters.map((node) => {
|
||||
const typeObject = checker.getTypeOfSymbolAtLocation(node.symbol, node.symbol.valueDeclaration);
|
||||
const v = getDocgenTypeHelper(checker, typeObject, false, getNextParentIds(parentIds, type));
|
||||
const name = node.symbol.escapedName;
|
||||
return {
|
||||
name,
|
||||
propType: v,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function getFunctionReturns(node: any, checker, parentIds, type) {
|
||||
if (!node) return {};
|
||||
const propType = getDocgenTypeHelper(
|
||||
checker,
|
||||
node.type,
|
||||
false,
|
||||
getNextParentIds(parentIds, type),
|
||||
);
|
||||
return {
|
||||
propType,
|
||||
};
|
||||
}
|
||||
|
||||
const blacklistNames = [
|
||||
'prototype',
|
||||
'getDerivedStateFromProps',
|
||||
'propTypes',
|
||||
'defaultProps',
|
||||
'contextTypes',
|
||||
'displayName',
|
||||
'contextType',
|
||||
'Provider',
|
||||
'Consumer',
|
||||
];
|
||||
|
||||
const blacklistPatterns = [
|
||||
/^HTML/,
|
||||
/^React\./,
|
||||
/^Object$/,
|
||||
/^Date$/,
|
||||
/^Promise$/,
|
||||
/^XML/,
|
||||
/^Function$/,
|
||||
];
|
||||
|
||||
// function hasTooManyTypes(type) {
|
||||
// return type?.types?.length >= 20;
|
||||
// }
|
||||
|
||||
function isComplexType(type) {
|
||||
let isAliasSymbol = false;
|
||||
let symbol = type?.symbol;
|
||||
if (!symbol) {
|
||||
symbol = type?.aliasSymbol;
|
||||
isAliasSymbol = true;
|
||||
}
|
||||
if (!symbol) return false;
|
||||
if (isAliasSymbol) {
|
||||
return false;
|
||||
}
|
||||
const name = getSymbolName(symbol);
|
||||
if (blacklistPatterns.some((patt) => patt.test(name))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getDocgenTypeHelper(
|
||||
checker: ts.TypeChecker,
|
||||
type: ts.Type,
|
||||
skipRequired = false,
|
||||
parentIds: number[] = [],
|
||||
isRequired = false,
|
||||
): any {
|
||||
function isTuple(_type: ts.Type) {
|
||||
// @ts-ignore use internal methods
|
||||
return checker.isArrayLikeType(_type) && !checker.isArrayType(_type);
|
||||
}
|
||||
let required: boolean;
|
||||
if (isRequired !== undefined) {
|
||||
required = isRequired;
|
||||
} else {
|
||||
required = !(type.flags & SymbolFlags.Optional) || isRequired;
|
||||
}
|
||||
|
||||
function makeResult(typeInfo: Json) {
|
||||
if (skipRequired) {
|
||||
return {
|
||||
raw: checker.typeToString(type),
|
||||
...typeInfo,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
required,
|
||||
raw: checker.typeToString(type),
|
||||
...typeInfo,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getShapeFromArray(symbolArr: ts.Symbol[], _type: ts.Type) {
|
||||
const shape: Array<{
|
||||
key:
|
||||
| {
|
||||
name: string;
|
||||
}
|
||||
| string;
|
||||
value: any;
|
||||
}> = symbolArr.map((prop) => {
|
||||
const propType = checker.getTypeOfSymbolAtLocation(
|
||||
prop,
|
||||
// @ts-ignore
|
||||
prop.valueDeclaration || (prop.declarations && prop.declarations[0]) || {},
|
||||
);
|
||||
return {
|
||||
key: prop.getName(),
|
||||
|
||||
value: getDocgenTypeHelper(
|
||||
checker,
|
||||
propType,
|
||||
false,
|
||||
// @ts-ignore
|
||||
getNextParentIds(parentIds, _type),
|
||||
// @ts-ignore
|
||||
!prop?.valueDeclaration?.questionToken,
|
||||
),
|
||||
};
|
||||
});
|
||||
// @ts-ignore use internal methods
|
||||
if (checker.isArrayLikeType(_type)) {
|
||||
return shape;
|
||||
}
|
||||
if (_type.getStringIndexType()) {
|
||||
// @ts-ignore use internal methods
|
||||
if (!_type.stringIndexInfo) {
|
||||
return shape;
|
||||
}
|
||||
shape.push({
|
||||
key: {
|
||||
name: 'string',
|
||||
},
|
||||
value: getDocgenTypeHelper(
|
||||
checker,
|
||||
// @ts-ignore use internal methods
|
||||
_type.stringIndexInfo.type,
|
||||
false,
|
||||
getNextParentIds(parentIds, _type),
|
||||
),
|
||||
});
|
||||
} else if (_type.getNumberIndexType()) {
|
||||
// @ts-ignore use internal methods
|
||||
if (!_type.numberIndexInfo) {
|
||||
return shape;
|
||||
}
|
||||
shape.push({
|
||||
key: {
|
||||
name: 'number',
|
||||
},
|
||||
|
||||
value: getDocgenTypeHelper(
|
||||
checker,
|
||||
// @ts-ignore use internal methods
|
||||
_type.numberIndexInfo.type,
|
||||
false,
|
||||
getNextParentIds(parentIds, _type),
|
||||
),
|
||||
});
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
function getShape(_type: ts.Type) {
|
||||
const { symbol } = _type;
|
||||
if (symbol && symbol.members) {
|
||||
// @ts-ignore
|
||||
const props: ts.Symbol[] = Array.from(symbol.members.values());
|
||||
// if (props.length >= 20) {
|
||||
// throw new Error('too many props');
|
||||
// }
|
||||
return getShapeFromArray(
|
||||
props.filter((prop) => prop.getName() !== '__index'),
|
||||
_type,
|
||||
);
|
||||
} else {
|
||||
// @ts-ignore
|
||||
const args = _type.resolvedTypeArguments || [];
|
||||
const props = checker.getPropertiesOfType(_type);
|
||||
// if (props.length >= 20) {
|
||||
// throw new Error('too many props');
|
||||
// }
|
||||
const shape = getShapeFromArray(props.slice(0, args.length), _type);
|
||||
return shape;
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
if (type?.kind === SyntaxKind.VoidExpression) {
|
||||
return makeResult({
|
||||
name: 'void',
|
||||
raw: 'void',
|
||||
});
|
||||
}
|
||||
|
||||
const pattern = /^__global\.(.+)$/;
|
||||
// @ts-ignore
|
||||
if (parentIds.includes(type?.symbol?.id)) {
|
||||
return makeResult({
|
||||
name: 'object', // checker.typeToString(type),
|
||||
});
|
||||
}
|
||||
if (type.symbol) {
|
||||
const symbolName = getSymbolName(type.symbol);
|
||||
if (symbolName) {
|
||||
const matches = pattern.exec(symbolName);
|
||||
if (matches) {
|
||||
return makeResult({
|
||||
name: matches[1],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type.flags & TypeFlags.Number) {
|
||||
return makeResult({
|
||||
name: 'number',
|
||||
});
|
||||
} else if (type.flags & TypeFlags.String) {
|
||||
return makeResult({
|
||||
name: 'string',
|
||||
});
|
||||
} else if (type.flags & TypeFlags.NumberLiteral) {
|
||||
return makeResult({
|
||||
name: 'literal',
|
||||
// @ts-ignore
|
||||
value: type.value,
|
||||
});
|
||||
} else if (type.flags & TypeFlags.Literal) {
|
||||
return makeResult({
|
||||
name: 'literal',
|
||||
value: checker.typeToString(type),
|
||||
});
|
||||
} else if (type.symbol?.flags & SymbolFlags.Enum) {
|
||||
return makeResult({
|
||||
name: 'union',
|
||||
// @ts-ignore
|
||||
value: type.types.map((t) => t.value),
|
||||
});
|
||||
// @ts-ignore
|
||||
} else if (type.flags & TypeFlags.DisjointDomains) {
|
||||
return makeResult({
|
||||
name: checker.typeToString(type),
|
||||
});
|
||||
} else if (type.flags & TypeFlags.Any) {
|
||||
return makeResult({
|
||||
name: 'any',
|
||||
});
|
||||
} else if (type.flags & TypeFlags.Union && !isComplexType(type)) {
|
||||
return makeResult({
|
||||
name: 'union',
|
||||
// @ts-ignore
|
||||
value: type.types.map((t) =>
|
||||
getDocgenTypeHelper(checker, t, true, getNextParentIds(parentIds, type)),
|
||||
),
|
||||
});
|
||||
} else if (isComplexType(type)) {
|
||||
return makeResult({
|
||||
name: getSymbolName(type?.symbol || type?.aliasSymbol),
|
||||
});
|
||||
} else if (type.flags & (TypeFlags.Object | TypeFlags.Intersection)) {
|
||||
if (isTuple(type)) {
|
||||
try {
|
||||
const props = getShape(type);
|
||||
return makeResult({
|
||||
name: 'tuple',
|
||||
value: props.map((p) => p.value),
|
||||
});
|
||||
} catch (e) {
|
||||
return makeResult({
|
||||
name: 'object',
|
||||
});
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
} else if (checker.isArrayType(type)) {
|
||||
return makeResult({
|
||||
name: 'Array',
|
||||
// @ts-ignore
|
||||
elements: [
|
||||
getDocgenTypeHelper(
|
||||
checker,
|
||||
(type as ExtendedType).typeArguments[0],
|
||||
false,
|
||||
getNextParentIds(parentIds, type),
|
||||
),
|
||||
],
|
||||
});
|
||||
// @ts-ignore
|
||||
} else if (type?.symbol?.valueDeclaration?.parameters?.length) {
|
||||
return makeResult({
|
||||
name: 'func',
|
||||
params: getFunctionParams(
|
||||
// @ts-ignore
|
||||
type?.symbol?.valueDeclaration?.parameters,
|
||||
checker,
|
||||
parentIds,
|
||||
type,
|
||||
),
|
||||
returns: getFunctionReturns(
|
||||
checker.typeToTypeNode(type, type?.symbol?.valueDeclaration),
|
||||
checker,
|
||||
parentIds,
|
||||
type,
|
||||
),
|
||||
});
|
||||
} else if (
|
||||
// @ts-ignore
|
||||
type?.members?.get('__call')?.declarations[0]?.symbol?.declarations[0]?.parameters?.length
|
||||
) {
|
||||
return makeResult({
|
||||
name: 'func',
|
||||
params: getFunctionParams(
|
||||
// @ts-ignore
|
||||
type?.members?.get('__call')?.declarations[0]?.symbol?.declarations[0]?.parameters,
|
||||
checker,
|
||||
parentIds,
|
||||
type,
|
||||
),
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
const props = getShape(type);
|
||||
return makeResult({
|
||||
name: 'signature',
|
||||
type: {
|
||||
signature: {
|
||||
properties: props,
|
||||
},
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
return makeResult({
|
||||
name: 'object',
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return makeResult({
|
||||
name: 'object',
|
||||
});
|
||||
}
|
||||
}
|
||||
class MyParser extends Parser {
|
||||
getDocgenType(propType: ts.Type): any {
|
||||
const parentIds = [];
|
||||
// @ts-ignore
|
||||
const parentId = propType?.symbol?.parent?.id;
|
||||
if (parentId) {
|
||||
parentIds.push(parentId);
|
||||
}
|
||||
// @ts-ignore
|
||||
const result = getDocgenTypeHelper(this.checker, propType, true, parentIds);
|
||||
return result;
|
||||
}
|
||||
|
||||
// override the builtin method, to avoid the false positive
|
||||
public extractPropsFromTypeIfStatelessComponent(type: ts.Type): ts.Symbol | null {
|
||||
const callSignatures = type.getCallSignatures();
|
||||
|
||||
if (callSignatures.length) {
|
||||
// Could be a stateless component. Is a function, so the props object we're interested
|
||||
// in is the (only) parameter.
|
||||
|
||||
for (const sig of callSignatures) {
|
||||
const params = sig.getParameters();
|
||||
if (params.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const returnSymbol = this.checker.getReturnTypeOfSignature(sig);
|
||||
if (!returnSymbol) continue;
|
||||
const symbol = returnSymbol?.symbol;
|
||||
if (!symbol) continue;
|
||||
// @ts-ignore
|
||||
const typeString = this.checker.symbolToString(symbol);
|
||||
if (
|
||||
typeString.startsWith('ReactElement') ||
|
||||
typeString.startsWith('Element') ||
|
||||
typeString.startsWith('RaxElement')
|
||||
) {
|
||||
const propsParam = params[0];
|
||||
if (propsParam) {
|
||||
return propsParam;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const getCompilerOptions = (reactTypePath, originalReactTypePath) => {
|
||||
const options: any = {
|
||||
jsx: ts.JsxEmit.React,
|
||||
module: ts.ModuleKind.CommonJS,
|
||||
target: ts.ScriptTarget.Latest,
|
||||
allowSyntheticDefaultImports: true,
|
||||
};
|
||||
// if (reactTypePath) {
|
||||
// options.paths = {
|
||||
// react: [reactTypePath],
|
||||
// };
|
||||
// options.exclude = [path.dirname(originalReactTypePath)];
|
||||
// options.types = [];
|
||||
// options.skipLibCheck = true;
|
||||
// }
|
||||
return options;
|
||||
};
|
||||
|
||||
interface SymbolWithMeta extends ts.Symbol {
|
||||
meta?: {
|
||||
exportName: string;
|
||||
subName?: string;
|
||||
};
|
||||
}
|
||||
|
||||
function getComponentName(exportName, displayName) {
|
||||
if (displayName) {
|
||||
const firstCharCode = displayName.charCodeAt(0);
|
||||
if (firstCharCode >= 65 && firstCharCode <= 90) {
|
||||
return displayName || exportName;
|
||||
}
|
||||
}
|
||||
return exportName;
|
||||
}
|
||||
|
||||
const defaultTsConfigPath = path.resolve(__dirname, './tsconfig.json');
|
||||
|
||||
export default function parseTS(filePath: string, args: IParseArgs): ComponentDoc[] {
|
||||
if (!filePath) return [];
|
||||
|
||||
let basePath = args.moduleDir || args.workDir || path.dirname(filePath);
|
||||
let tsConfigPath = findConfig('tsconfig.json', { cwd: basePath }); // path.resolve(basePath, 'tsconfig.json')
|
||||
if (
|
||||
!tsConfigPath ||
|
||||
!existsSync(tsConfigPath) ||
|
||||
(args.accesser === 'online' && tsConfigPath === 'tsconfig.json')
|
||||
) {
|
||||
tsConfigPath = defaultTsConfigPath;
|
||||
} else {
|
||||
basePath = path.dirname(tsConfigPath);
|
||||
}
|
||||
|
||||
log('ts config path is', tsConfigPath);
|
||||
const { config, error } = ts.readConfigFile(tsConfigPath, (filename) =>
|
||||
readFileSync(filename, 'utf8'),
|
||||
);
|
||||
|
||||
if (error !== undefined) {
|
||||
const errorText = `Cannot load custom tsconfig.json from provided path: ${tsConfigPath}, with error code: ${error.code}, message: ${error.messageText}`;
|
||||
throw new Error(errorText);
|
||||
}
|
||||
|
||||
const { options, errors } = ts.parseJsonConfigFileContent(
|
||||
config,
|
||||
ts.sys,
|
||||
basePath,
|
||||
{},
|
||||
tsConfigPath,
|
||||
);
|
||||
|
||||
if (errors && errors.length) {
|
||||
throw errors[0];
|
||||
}
|
||||
log('ts config is', options);
|
||||
// const filePaths = Array.isArray(filePathOrPaths) ? filePathOrPaths : [filePathOrPaths];
|
||||
generateDTS(args);
|
||||
const program = ts.createProgram([filePath], options);
|
||||
|
||||
const parser = new MyParser(program, {});
|
||||
|
||||
const checker = program.getTypeChecker();
|
||||
|
||||
const result = [filePath]
|
||||
.map((fPath) => program.getSourceFile(fPath))
|
||||
.filter((sourceFile) => typeof sourceFile !== 'undefined')
|
||||
.reduce((docs: any[], sourceFile) => {
|
||||
const moduleSymbol = checker.getSymbolAtLocation(sourceFile as ts.Node);
|
||||
|
||||
if (!moduleSymbol) {
|
||||
return docs;
|
||||
}
|
||||
|
||||
const exportSymbols = checker.getExportsOfModule(moduleSymbol);
|
||||
|
||||
for (let index = 0; index < exportSymbols.length; index++) {
|
||||
const sym: SymbolWithMeta = exportSymbols[index];
|
||||
const name = sym.getName();
|
||||
if (blacklistNames.includes(name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// polyfill valueDeclaration
|
||||
sym.valueDeclaration =
|
||||
sym.valueDeclaration || (Array.isArray(sym.declarations) && sym.declarations[0]);
|
||||
|
||||
if (!sym.valueDeclaration) {
|
||||
continue;
|
||||
}
|
||||
const info = parser.getComponentInfo(sym, sourceFile);
|
||||
if (info === null) {
|
||||
continue;
|
||||
}
|
||||
const exportName = sym.meta && sym.meta.exportName;
|
||||
const meta = {
|
||||
subName: exportName ? name : '',
|
||||
exportName: exportName || name,
|
||||
};
|
||||
if (docs.find((x) => isEqual(x.meta, meta))) {
|
||||
continue;
|
||||
}
|
||||
docs.push({
|
||||
...info,
|
||||
meta,
|
||||
});
|
||||
// find sub components
|
||||
if (!!sym.declarations && sym.declarations.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const type = checker.getTypeOfSymbolAtLocation(
|
||||
sym,
|
||||
sym.valueDeclaration || sym.declarations[0],
|
||||
);
|
||||
Array.prototype.push.apply(
|
||||
exportSymbols,
|
||||
type.getProperties().map((x: SymbolWithMeta) => {
|
||||
x.meta = { exportName: name };
|
||||
return x;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return docs;
|
||||
}, []);
|
||||
const coms = result.reduce((res: any[], info: any) => {
|
||||
if (!info || !info.props || isEmpty(info.props)) return res;
|
||||
const props = Object.keys(info.props).reduce((acc: any[], name) => {
|
||||
// omit aria related properties temporarily
|
||||
if (name.startsWith('aria-')) {
|
||||
return acc;
|
||||
}
|
||||
try {
|
||||
const item: any = transformItem(name, info.props[name]);
|
||||
acc.push(item);
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
const exportName = info?.meta?.exportName;
|
||||
res.push({
|
||||
componentName: getComponentName(exportName, info.displayName),
|
||||
props,
|
||||
meta: info.meta || {},
|
||||
});
|
||||
return res;
|
||||
}, []);
|
||||
return coms;
|
||||
}
|
||||
11
packages/material-parser/src/parse/ts/tsconfig.json
Normal file
11
packages/material-parser/src/parse/ts/tsconfig.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"jsx": "react",
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"allowJs": true
|
||||
},
|
||||
"include": ["**/*"],
|
||||
"exclude": [""]
|
||||
}
|
||||
78
packages/material-parser/src/scan.ts
Normal file
78
packages/material-parser/src/scan.ts
Normal file
@ -0,0 +1,78 @@
|
||||
import {
|
||||
IInternalMaterializeOptions,
|
||||
IMaterializeOnlinePackageAndVersionOptions,
|
||||
IMaterialScanModel,
|
||||
} from './types';
|
||||
import { pathExists, lstatSync } from 'fs-extra';
|
||||
import { join, isAbsolute, resolve } from 'path';
|
||||
import { debug } from './core';
|
||||
import { resolvePkgJson } from './utils';
|
||||
|
||||
const log = debug.extend('mat');
|
||||
|
||||
export default async function scan(
|
||||
options: IInternalMaterializeOptions,
|
||||
): Promise<IMaterialScanModel> {
|
||||
const model: IMaterialScanModel = {
|
||||
pkgName: '',
|
||||
pkgVersion: '',
|
||||
mainFileAbsolutePath: '',
|
||||
mainFilePath: '',
|
||||
};
|
||||
log('options', options);
|
||||
// 入口文件路径
|
||||
const entryFilePath = options.entry;
|
||||
const stats = lstatSync(entryFilePath);
|
||||
if (
|
||||
(options.accesser === 'local' ||
|
||||
(options.accesser === 'online' &&
|
||||
(options as IMaterializeOnlinePackageAndVersionOptions).name &&
|
||||
options.entry)) &&
|
||||
stats.isFile()
|
||||
) {
|
||||
if (options.accesser === 'online') {
|
||||
model.useEntry = true;
|
||||
}
|
||||
if (isAbsolute(entryFilePath)) {
|
||||
model.mainFilePath = entryFilePath;
|
||||
model.mainFileAbsolutePath = entryFilePath;
|
||||
} else {
|
||||
model.mainFilePath = entryFilePath;
|
||||
model.mainFileAbsolutePath = resolve(entryFilePath);
|
||||
}
|
||||
}
|
||||
const pkgJsonPath = join(options.root, 'package.json');
|
||||
if (await pathExists(pkgJsonPath)) {
|
||||
const pkgJson = await resolvePkgJson(pkgJsonPath);
|
||||
model.pkgName = pkgJson.name;
|
||||
model.pkgVersion = pkgJson.version;
|
||||
if (pkgJson.module) {
|
||||
const moduleFileAbsolutePath = join(options.root, pkgJson.module);
|
||||
if (await pathExists(moduleFileAbsolutePath)) {
|
||||
model.moduleFilePath = pkgJson.module;
|
||||
model.moduleFileAbsolutePath = moduleFileAbsolutePath;
|
||||
}
|
||||
}
|
||||
model.mainFilePath = model.mainFilePath || pkgJson.main || './index.js';
|
||||
model.mainFileAbsolutePath = model.mainFileAbsolutePath || join(entryFilePath, pkgJson.main);
|
||||
const typingsPathCandidates = [
|
||||
pkgJson.typings,
|
||||
pkgJson.types,
|
||||
'./index.d.ts',
|
||||
'./lib/index.d.ts',
|
||||
];
|
||||
for (let i = 0; i < typingsPathCandidates.length; i++) {
|
||||
const typingsFilePath = typingsPathCandidates[i];
|
||||
if (!typingsFilePath) continue;
|
||||
const typingsFileAbsolutePath = join(options.root, typingsFilePath);
|
||||
if (await pathExists(typingsFileAbsolutePath)) {
|
||||
model.typingsFileAbsolutePath = typingsFileAbsolutePath;
|
||||
model.typingsFilePath = typingsFilePath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log('model', model);
|
||||
return model;
|
||||
}
|
||||
5
packages/material-parser/src/types/Basic.ts
Normal file
5
packages/material-parser/src/types/Basic.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface Json {
|
||||
[x: string]: string | number | boolean | Date | Json | JsonArray;
|
||||
}
|
||||
export type JsonArray = Array<string | number | boolean | Date | Json | JsonArray>;
|
||||
export type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
|
||||
9
packages/material-parser/src/types/ChannelType.ts
Normal file
9
packages/material-parser/src/types/ChannelType.ts
Normal file
@ -0,0 +1,9 @@
|
||||
/**
|
||||
* 物料接入渠道
|
||||
*/
|
||||
export enum ChannelType {
|
||||
/** 本地 */
|
||||
LOCAL = 'local',
|
||||
/** 在线 */
|
||||
ONLINE = 'online',
|
||||
}
|
||||
4
packages/material-parser/src/types/DSLType.ts
Normal file
4
packages/material-parser/src/types/DSLType.ts
Normal file
@ -0,0 +1,4 @@
|
||||
/**
|
||||
* DSL类型
|
||||
*/
|
||||
export type DSLType = 'react' | 'rax';
|
||||
14
packages/material-parser/src/types/IAccesser.ts
Normal file
14
packages/material-parser/src/types/IAccesser.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { ComponentMeta } from '../core';
|
||||
|
||||
/**
|
||||
* 接入器接口(用于定义物料化组件的接入渠道)
|
||||
* @interface IAccesser
|
||||
*/
|
||||
export interface IAccesser {
|
||||
/**
|
||||
* 接入
|
||||
* @returns {Promise<IMaterialinSchema>}
|
||||
* @memberof IAccesser
|
||||
*/
|
||||
access(): Promise<ComponentMeta[]>;
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
import { ComponentMeta } from '../core';
|
||||
/**
|
||||
* 扩展点:配置 manifest
|
||||
* (物料化场景)
|
||||
*/
|
||||
export type IExtensionConfigManifest = (params: {
|
||||
manifestObj: ComponentMeta; // manifest 配置对象
|
||||
manifestFilePath: string; // manifest 文件默认路径
|
||||
}) => Promise<{
|
||||
manifestJSON: string; // manifest 文件内容
|
||||
manifestFilePath: string; // manifest 文件路径
|
||||
manifestObj: ComponentMeta; // manifest 文件对象
|
||||
}>;
|
||||
22
packages/material-parser/src/types/IMaterialParsedModel.ts
Normal file
22
packages/material-parser/src/types/IMaterialParsedModel.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { PropsSection } from '../core';
|
||||
/**
|
||||
* 对应解析器分析出的一些关键信息
|
||||
*/
|
||||
export interface IPropType {
|
||||
name: string;
|
||||
type: string;
|
||||
value?: IPropTypes;
|
||||
required: boolean;
|
||||
}
|
||||
|
||||
export type IPropTypes = IPropType[];
|
||||
|
||||
export interface IMaterialParsedModel {
|
||||
// filePath: string;
|
||||
componentName: string;
|
||||
props?: PropsSection['props'];
|
||||
meta?: {
|
||||
exportName?: string;
|
||||
subName?: string;
|
||||
};
|
||||
}
|
||||
23
packages/material-parser/src/types/IMaterialScanModel.ts
Normal file
23
packages/material-parser/src/types/IMaterialScanModel.ts
Normal file
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 对应扫描阶段的产物
|
||||
*/
|
||||
export interface IMaterialScanModel {
|
||||
/** 当前包名 */
|
||||
pkgName: string;
|
||||
/** 当前包版本 */
|
||||
pkgVersion: string;
|
||||
/** 在ts场景下,使用entry */
|
||||
useEntry?: boolean;
|
||||
/** main文件相对路径 */
|
||||
mainFilePath: string;
|
||||
/** module文件相对路径 */
|
||||
moduleFilePath?: string;
|
||||
/** typings文件相对路径 */
|
||||
typingsFilePath?: string;
|
||||
/** main文件绝对路径 */
|
||||
mainFileAbsolutePath: string;
|
||||
/** module文件绝对路径 */
|
||||
moduleFileAbsolutePath?: string;
|
||||
/** typings文件绝对路径 */
|
||||
typingsFileAbsolutePath?: string;
|
||||
}
|
||||
117
packages/material-parser/src/types/IMaterializeOptions.ts
Normal file
117
packages/material-parser/src/types/IMaterializeOptions.ts
Normal file
@ -0,0 +1,117 @@
|
||||
import { Expand } from './Basic';
|
||||
import { DSLType } from './DSLType';
|
||||
/**
|
||||
* 通用入料配置项
|
||||
* @interface IMaterializeCommonOptions
|
||||
*/
|
||||
export interface IMaterializeCommonOptions {
|
||||
/**
|
||||
* 当 accesser=online 时,配置要使用的 npm client,如:tnpm、cnpm、yarn、npm
|
||||
*/
|
||||
npmClient?: string;
|
||||
/**
|
||||
* 当前dsl类型,可选值包括'react' | 'rax'
|
||||
*/
|
||||
dslType?: DSLType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 本地入料配置项
|
||||
* @interface IMaterializeOnlineOptions
|
||||
*/
|
||||
export interface IMaterializeLocalOptions extends IMaterializeCommonOptions {
|
||||
/**
|
||||
* 接入渠道
|
||||
* (local:表示本地物料工作台方式接入,online:表示在线 npm 包接入)
|
||||
* @type {('local' | 'online')}
|
||||
* @memberof IMaterializeOptions
|
||||
*/
|
||||
accesser: 'local';
|
||||
|
||||
/**
|
||||
* 组件文件(夹)路径或包名
|
||||
* 形如:
|
||||
* 本地路径:/usr/project/src/container/DemoMaterial
|
||||
* 包名:@ali/demo-material@0.0.1
|
||||
*/
|
||||
entry: string;
|
||||
|
||||
/**
|
||||
* 组件根目录,当entry为文件路径的时候,可以用root来指定根目录,当entry为文件夹时,root默认为entry
|
||||
* 形如:
|
||||
* 相对路径:./
|
||||
* 绝对路径:/usr/project/src/container/DemoMaterial
|
||||
*/
|
||||
root?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在线入料配置项
|
||||
* @interface IMaterializeOnlineOptions
|
||||
*/
|
||||
export interface IMaterializeOnlineCommonOptions {
|
||||
/**
|
||||
* 接入渠道
|
||||
* (local:表示本地物料工作台方式接入,online:表示在线 npm 包接入)
|
||||
* @type {('local' | 'online')}
|
||||
* @memberof IMaterializeOptions
|
||||
*/
|
||||
accesser: 'online';
|
||||
/**
|
||||
* 临时工作目录,用来存放下载的npm包,可为绝对路径或相对路径
|
||||
*/
|
||||
tempDir?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 只通过entry指定包名&版本号,无需内部路径
|
||||
*/
|
||||
export interface IMaterializeOnlineEntryOptions {
|
||||
/**
|
||||
* npm包名&版本号,此时无需指定内部路径,会从package.json自动解析
|
||||
* 形如:
|
||||
* 包名&版本号:@ali/demo-material@0.0.1
|
||||
*/
|
||||
entry: string;
|
||||
}
|
||||
|
||||
export interface IMaterializeOnlinePackageAndVersionOptions {
|
||||
/**
|
||||
* npm包内部相对路径
|
||||
* 形如:
|
||||
* 相对路径:lib/index.js
|
||||
*/
|
||||
entry?: string;
|
||||
|
||||
/**
|
||||
* npm包名
|
||||
* 形如:
|
||||
* react-color
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* npm包版本号
|
||||
* 形如:
|
||||
* latest/1.0.0/1.x.0
|
||||
* @default latest
|
||||
*/
|
||||
version?: string;
|
||||
}
|
||||
|
||||
export type IMaterializeOnlineOptions = Expand<
|
||||
IMaterializeCommonOptions &
|
||||
IMaterializeOnlineCommonOptions &
|
||||
(IMaterializeOnlineEntryOptions | IMaterializeOnlinePackageAndVersionOptions)
|
||||
>;
|
||||
/**
|
||||
* 入料配置项
|
||||
* @interface IMaterializeOptions
|
||||
*/
|
||||
export type IMaterializeOptions = Expand<IMaterializeLocalOptions | IMaterializeOnlineOptions>;
|
||||
|
||||
export type IInternalMaterializeOptions = Expand<
|
||||
IMaterializeOptions & {
|
||||
root: string;
|
||||
}
|
||||
>;
|
||||
14
packages/material-parser/src/types/Meta.ts
Normal file
14
packages/material-parser/src/types/Meta.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { Path } from 'ast-types';
|
||||
|
||||
export interface IFileMeta {
|
||||
src: string;
|
||||
path: string;
|
||||
exports: IDefinitionMeta[];
|
||||
}
|
||||
|
||||
export interface IDefinitionMeta {
|
||||
subDefinitions: IDefinitionMeta[];
|
||||
nodePath: typeof Path;
|
||||
exportName: string;
|
||||
id: string;
|
||||
}
|
||||
8
packages/material-parser/src/types/index.ts
Normal file
8
packages/material-parser/src/types/index.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export * from './ChannelType';
|
||||
export * from './DSLType';
|
||||
export * from './IAccesser';
|
||||
export * from './IExtensionConfigManifest';
|
||||
export * from './IMaterializeOptions';
|
||||
export * from './IMaterialScanModel';
|
||||
export * from './IMaterialParsedModel';
|
||||
export * from './Basic';
|
||||
129
packages/material-parser/src/utils.ts
Normal file
129
packages/material-parser/src/utils.ts
Normal file
@ -0,0 +1,129 @@
|
||||
import { pathExists, readFileSync, writeFile } from 'fs-extra';
|
||||
import { isPlainObject } from 'lodash';
|
||||
import originalSafeEval from 'safe-eval';
|
||||
import * as path from 'path';
|
||||
import spawn from 'cross-spawn-promise';
|
||||
import { DSLType } from './types';
|
||||
|
||||
export async function isNPMInstalled(args: {
|
||||
workDir: string;
|
||||
moduleDir: string;
|
||||
npmClient?: string;
|
||||
}) {
|
||||
return pathExists(path.join(args.workDir, 'node_modules'));
|
||||
}
|
||||
|
||||
export async function install(args: { workDir: string; moduleDir: string; npmClient?: string }) {
|
||||
if (await isNPMInstalled(args)) return;
|
||||
const { workDir, npmClient = 'tnpm' } = args;
|
||||
try {
|
||||
await spawn(npmClient, ['i'], { stdio: 'inherit', cwd: workDir } as any);
|
||||
} catch (e) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
export async function installModule(
|
||||
args: { workDir: string; moduleDir: string; npmClient?: string },
|
||||
name: string,
|
||||
) {
|
||||
const { workDir, npmClient = 'tnpm' } = args;
|
||||
try {
|
||||
await spawn(npmClient, ['i', name], { stdio: 'inherit', cwd: workDir } as any);
|
||||
} catch (e) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
export function installTypeDTS(args: {
|
||||
workDir: string;
|
||||
moduleDir: string;
|
||||
npmClient?: string;
|
||||
dslType?: DSLType;
|
||||
}) {
|
||||
return installModule(args, `@types/${args.dslType || 'react'}`);
|
||||
}
|
||||
|
||||
export async function installTypeScript(args: {
|
||||
workDir: string;
|
||||
moduleDir: string;
|
||||
npmClient?: string;
|
||||
}) {
|
||||
if (await pathExists(path.join(args.workDir, 'node_modules', '.bin', 'tsc'))) return;
|
||||
const { workDir, npmClient = 'tnpm' } = args;
|
||||
await spawn(npmClient, ['i', 'typescript'], { stdio: 'inherit', cwd: workDir } as any);
|
||||
}
|
||||
|
||||
export async function installPeerAndDevDeps(args: {
|
||||
workDir: string;
|
||||
moduleDir: string;
|
||||
npmClient?: string;
|
||||
}) {
|
||||
const { workDir, moduleDir, npmClient = 'tnpm' } = args;
|
||||
const modulePkgJsonPath = path.resolve(moduleDir, 'package.json');
|
||||
if (!(await pathExists(modulePkgJsonPath))) {
|
||||
return;
|
||||
}
|
||||
const pkgJsonPath = path.resolve(workDir, 'package.json');
|
||||
if (!(await pathExists(pkgJsonPath))) {
|
||||
return;
|
||||
}
|
||||
const modulePkgJson = await resolvePkgJson(modulePkgJsonPath);
|
||||
const pkgJson = await resolvePkgJson(pkgJsonPath);
|
||||
const { peerDependencies = {}, devDependencies = {} } = modulePkgJson;
|
||||
pkgJson.dependencies = pkgJson.dependencies || {};
|
||||
pkgJson.dependencies = {
|
||||
...pkgJson.dependencies,
|
||||
...peerDependencies,
|
||||
...devDependencies,
|
||||
};
|
||||
await writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
|
||||
await spawn(npmClient, ['i'], { stdio: 'inherit', cwd: workDir } as any);
|
||||
}
|
||||
|
||||
export async function syncTypeModules(args: {
|
||||
workDir: string;
|
||||
moduleDir: string;
|
||||
npmClient?: string;
|
||||
}) {
|
||||
const { workDir, moduleDir, npmClient = 'tnpm' } = args;
|
||||
const pkgJsonPath = path.resolve(moduleDir, 'package.json');
|
||||
if (!(await pathExists(pkgJsonPath))) {
|
||||
return;
|
||||
}
|
||||
await installModule(args, 'typesync');
|
||||
await spawn(npmClient.replace('m', 'x'), ['typesync'], { stdio: 'inherit', cwd: workDir } as any);
|
||||
}
|
||||
|
||||
export async function resolvePkgJson(pkgJsonPath: string): Promise<{ [k: string]: any }> {
|
||||
const content = await loadFile(pkgJsonPath);
|
||||
const json = JSON.parse(content);
|
||||
return json;
|
||||
}
|
||||
|
||||
export function loadFile(filePath: string): string {
|
||||
const content: string | Buffer = readFileSync(filePath);
|
||||
if (typeof content === 'string') {
|
||||
return content;
|
||||
}
|
||||
return content.toString();
|
||||
}
|
||||
|
||||
export function isPrimitive(val) {
|
||||
return !['object', 'function'].includes(typeof val) || val === null;
|
||||
}
|
||||
|
||||
export function isEvaluable(value) {
|
||||
if (isPrimitive(value)) return true;
|
||||
if (Array.isArray(value)) {
|
||||
return value.every(isEvaluable);
|
||||
} else if (isPlainObject(value)) {
|
||||
return Object.keys(value).every((key) => isEvaluable(value[key]));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function safeEval(value: any) {
|
||||
if (typeof value === 'string') return originalSafeEval(value);
|
||||
return value;
|
||||
}
|
||||
14
packages/material-parser/src/validate/index.ts
Normal file
14
packages/material-parser/src/validate/index.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import Ajv from 'ajv';
|
||||
import { Json } from '../types/Basic';
|
||||
import schema from './schema.json';
|
||||
|
||||
const ajv = new Ajv({ jsonPointers: true });
|
||||
const validate = ajv.compile(schema);
|
||||
|
||||
export default function validateSchema(json: Json) {
|
||||
if (validate(json) === false) {
|
||||
throw new Error(JSON.stringify(validate.errors, null, 2));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
548
packages/material-parser/src/validate/schema.json
Normal file
548
packages/material-parser/src/validate/schema.json
Normal file
@ -0,0 +1,548 @@
|
||||
{
|
||||
"$id": "@ali/low-code-component-protocol-schema",
|
||||
"description": "json schema for low code component protocol",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/BasicSection"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/PropsSection"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/ConfigureSection"
|
||||
}
|
||||
],
|
||||
"definitions": {
|
||||
"BasicSection": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"componentName": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"docUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"screenshot": {
|
||||
"type": "string"
|
||||
},
|
||||
"icon": {
|
||||
"type": "string"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"devMode": {
|
||||
"enum": [
|
||||
"proCode",
|
||||
"lowCode"
|
||||
]
|
||||
},
|
||||
"npm": {
|
||||
"$ref": "#/definitions/Npm"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"componentName",
|
||||
"title",
|
||||
"npm"
|
||||
]
|
||||
},
|
||||
"PropsSection": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"props"
|
||||
],
|
||||
"properties": {
|
||||
"props": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"propType": {
|
||||
"$ref": "#/definitions/PropType"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"defaultValue": {}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"propType"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ConfigureSection": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"configure": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"props": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ConfigureProp"
|
||||
}
|
||||
},
|
||||
"styles": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
},
|
||||
"events": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
},
|
||||
"component": {
|
||||
"$ref": "#/definitions/ConfigureComponent"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Npm": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"package": {
|
||||
"type": "string"
|
||||
},
|
||||
"exportName": {
|
||||
"type": "string"
|
||||
},
|
||||
"subName": {
|
||||
"type": "string"
|
||||
},
|
||||
"main": {
|
||||
"type": "string"
|
||||
},
|
||||
"destructuring": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"package",
|
||||
"exportName",
|
||||
"subName",
|
||||
"main",
|
||||
"destructuring",
|
||||
"version"
|
||||
]
|
||||
},
|
||||
"PropType": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/BasicType"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/RequiredType"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/ComplexType"
|
||||
}
|
||||
]
|
||||
},
|
||||
"BasicType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"array",
|
||||
"bool",
|
||||
"func",
|
||||
"number",
|
||||
"object",
|
||||
"string",
|
||||
"node",
|
||||
"element",
|
||||
"any"
|
||||
]
|
||||
},
|
||||
"RequiredType": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"$ref": "#/definitions/BasicType"
|
||||
},
|
||||
"isRequired": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"ComplexType": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/OneOf"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/OneOfType"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/ArrayOf"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/ObjectOf"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/Shape"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/Exact"
|
||||
}
|
||||
]
|
||||
},
|
||||
"OneOf": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"oneOf"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"isRequired": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"OneOfType": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"oneOfType"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/PropType"
|
||||
}
|
||||
},
|
||||
"isRequired": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ArrayOf": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"arrayOf"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/definitions/PropType"
|
||||
},
|
||||
"isRequired": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ObjectOf": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"objectOf"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/definitions/PropType"
|
||||
},
|
||||
"isRequired": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Shape": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"shape"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"propType": {
|
||||
"$ref": "#/definitions/PropType"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"isRequired": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ShapeItem": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"propType"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"propType": {
|
||||
"$ref": "#/definitions/PropType"
|
||||
},
|
||||
"isRequired": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Exact": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"exact"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"propType": {
|
||||
"$ref": "#/definitions/PropType"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"isRequired": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ConfigureProp": {
|
||||
"type": "object",
|
||||
"allOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"extraProps": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ConfigureFieldProp"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/ConfigureGroupProp"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ConfigureFieldProp": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"field"
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"setter": {
|
||||
"$ref": "#/definitions/ConfigureFieldSetter"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ConfigureFieldSetter": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"componentName"
|
||||
],
|
||||
"properties": {
|
||||
"componentName": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"List",
|
||||
"Object",
|
||||
"Function",
|
||||
"Node",
|
||||
"Mixin",
|
||||
"Expression",
|
||||
"Switch",
|
||||
"Number",
|
||||
"Input",
|
||||
"TextArea",
|
||||
"Date",
|
||||
"DateYear",
|
||||
"DateMonth",
|
||||
"DateRange",
|
||||
"ColorPicker",
|
||||
"CodeEditor",
|
||||
"Select",
|
||||
"RadioGroup"
|
||||
]
|
||||
},
|
||||
"props": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ConfigureGroupProp": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type",
|
||||
"items"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"group"
|
||||
]
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ConfigureProp"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ConfigureComponent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"isContainer": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isModal": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"descriptor": {
|
||||
"type": "string"
|
||||
},
|
||||
"nestingRule": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"childWhitelist": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"parentWhitelist": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"descendantBlacklist": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"ancestorWhitelist": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"isNullNode": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isLayout": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`materialize dynamic component by online 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"componentName": "BizAnchor",
|
||||
"devMode": "proCode",
|
||||
"docUrl": "",
|
||||
"npm": Object {
|
||||
"destructuring": false,
|
||||
"exportName": "default",
|
||||
"main": "lib/index.js",
|
||||
"package": "@alifd/biz-anchor",
|
||||
"subName": "",
|
||||
"version": "1.1.7",
|
||||
},
|
||||
"props": Array [
|
||||
Object {
|
||||
"defaultValue": false,
|
||||
"name": "noHash",
|
||||
},
|
||||
],
|
||||
"screenshot": "",
|
||||
"title": "@alifd/biz-anchor",
|
||||
},
|
||||
]
|
||||
`;
|
||||
1753
packages/material-parser/test/__snapshots__/index.test.ts.snap
Normal file
1753
packages/material-parser/test/__snapshots__/index.test.ts.snap
Normal file
File diff suppressed because it is too large
Load Diff
5780
packages/material-parser/test/__snapshots__/online.test.ts.snap
Normal file
5780
packages/material-parser/test/__snapshots__/online.test.ts.snap
Normal file
File diff suppressed because it is too large
Load Diff
14
packages/material-parser/test/dynamic.test.ts
Normal file
14
packages/material-parser/test/dynamic.test.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import parse from '../src';
|
||||
import { IMaterializeOptions } from '../src/types';
|
||||
|
||||
const dynamicComponent = '@alifd/biz-anchor@1.1.7';
|
||||
|
||||
test('materialize dynamic component by online', async () => {
|
||||
const options: IMaterializeOptions = {
|
||||
entry: dynamicComponent,
|
||||
accesser: 'online',
|
||||
};
|
||||
|
||||
const actual = await parse(options);
|
||||
expect(actual).toMatchSnapshot();
|
||||
});
|
||||
168
packages/material-parser/test/fixtures/dts-component/index.d.ts
vendored
Normal file
168
packages/material-parser/test/fixtures/dts-component/index.d.ts
vendored
Normal file
@ -0,0 +1,168 @@
|
||||
import * as React from 'react';
|
||||
|
||||
/**
|
||||
* 财鲸Layout属性配置
|
||||
*/
|
||||
export interface WhaleLayoutProps {
|
||||
/**
|
||||
* 是否是财鲸主站,该字段由后端 vm 中吐出在 window.appConfig 中
|
||||
* @defaultValue false
|
||||
*/
|
||||
isMainSite?: boolean;
|
||||
/**
|
||||
* 财鲸主站的 app 名称,该字段由后端 vm 中吐出在 window.appConfig 中
|
||||
* @defaultValue 'caijing'
|
||||
*/
|
||||
mainSiteAppName?: string;
|
||||
/**
|
||||
* 主站中文名,该字段由后端 vm 中吐出在 window.appConfig 中
|
||||
* @defaultValue '财鲸'
|
||||
*/
|
||||
mainSiteName?: string;
|
||||
/**
|
||||
* 主站英文名,该字段由后端 vm 中吐出在 window.appConfig 中
|
||||
* @defaultValue 'Caijing'
|
||||
*/
|
||||
mainSiteNameEn?: string;
|
||||
/**
|
||||
* 主站链接,该字段由后端 vm 中吐出在 window.appConfig 中
|
||||
* @defaultValue '//caijing.alibaba-inc.com'
|
||||
*/
|
||||
mainSiteLink?: string;
|
||||
/**
|
||||
* 子站点的应用名,该字段由后端 vm 中吐出在 window.appConfig 中
|
||||
*/
|
||||
subSiteAppName: string;
|
||||
/**
|
||||
* 子站点的名称,该字段由后端 vm 中吐出在 window.appConfig 中
|
||||
*/
|
||||
subSiteName: string;
|
||||
/**
|
||||
* 子站点的英文名称,该字段由后端 vm 中吐出在 window.appConfig 中
|
||||
*/
|
||||
subSiteNameEn: string;
|
||||
/**
|
||||
* 子站点的链接,该字段由后端 vm 中吐出在 window.appConfig 中
|
||||
*/
|
||||
subSiteLink: string;
|
||||
/**
|
||||
* 子站访问相关接口时,属于buc 跨域请求,需要把 ssoTicket 带上,该字段由后端 vm 中吐出在 window.appConfig 中
|
||||
*/
|
||||
ssoTicket: string;
|
||||
/**
|
||||
* 是否使用 ak/sk 方案(用于登录)
|
||||
* @defaultValue false
|
||||
*/
|
||||
aksk?: boolean;
|
||||
/**
|
||||
* 是否显示左侧导航
|
||||
* @defaultValue true
|
||||
*/
|
||||
sidebarVisible?: boolean;
|
||||
/**
|
||||
* 是否显示顶部菜单栏
|
||||
* @defaultValue true
|
||||
*/
|
||||
topbarVisible?: boolean;
|
||||
/**
|
||||
* 是否开启全屏
|
||||
* @defaultValue false
|
||||
*/
|
||||
isFullScreen?: boolean;
|
||||
/**
|
||||
* 当前选中的导航 key
|
||||
* @defaultValue 'home'
|
||||
*/
|
||||
navSelectedKeys?: string | string[];
|
||||
/**
|
||||
* history 对象
|
||||
* @defaultValue {}
|
||||
*/
|
||||
history?: string | {};
|
||||
/**
|
||||
* 左侧子导航数组
|
||||
* 参考:https://whale.fusion.alibaba-inc.com/73015/component/basic/nav#demo-api
|
||||
* @defaultValue []
|
||||
*/
|
||||
nav?: Array<{}>;
|
||||
/**
|
||||
* Next Nav 组件本身的配置
|
||||
* @defaultValue {}
|
||||
*/
|
||||
nextNavConfig?: {};
|
||||
/**
|
||||
* 切换语言的 Url
|
||||
* @defaultValue null
|
||||
*/
|
||||
setLanguageUrl?: string;
|
||||
/**
|
||||
* 顶部控件的扩展区渲染内容
|
||||
* @defaultValue null
|
||||
*/
|
||||
extra?: React.ReactNode;
|
||||
/**
|
||||
* 顶部导航站点名称后面的快速入口区域自定义渲染
|
||||
* @defaultValue null
|
||||
*/
|
||||
quickEntry?: React.ReactNode;
|
||||
/**
|
||||
* 水印配置项
|
||||
* 配置文档参考:{@link https://npm.alibaba-inc.com/package/@alife/waterMark}
|
||||
* @defaultValue null
|
||||
*/
|
||||
waterMarkOptions?: {};
|
||||
/**
|
||||
* 是否需要默认的登出行为
|
||||
* @defaultValue true
|
||||
*/
|
||||
needDefaultLogout?: boolean;
|
||||
/**
|
||||
* 点击登出按钮后的回调函数
|
||||
* @defaultValue null
|
||||
*/
|
||||
onLogoutClick?: () => void;
|
||||
/**
|
||||
* 侧边栏收起展开回调函数
|
||||
* @defaultValue null
|
||||
*/
|
||||
onToggleMenu?: () => void;
|
||||
/**
|
||||
* logo 的 style 覆盖
|
||||
* @defaultValue null
|
||||
*/
|
||||
logoStyle?: React.CSSProperties;
|
||||
/**
|
||||
* logo 点击事件,传入覆盖
|
||||
* @defaultValue null
|
||||
*/
|
||||
onLogoClick?: () => void;
|
||||
/**
|
||||
* 切换语言自定义事件
|
||||
*
|
||||
*/
|
||||
onToggleLang?: (nextConfig: Record<string, any>, appName: any) => void;
|
||||
/**
|
||||
* 语言切换器显示切换
|
||||
* @defaultValueValue true
|
||||
*/
|
||||
langVisible?: boolean;
|
||||
/**
|
||||
* 打开启用浏览器缩放警告
|
||||
* @defaultValue `true`
|
||||
*/
|
||||
scaleWarning?: boolean;
|
||||
/**
|
||||
* This event is fired whenever the application navigates to a new page.
|
||||
* @eventProperty
|
||||
*/
|
||||
test: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 财鲸Layout owner: 昔夜
|
||||
*
|
||||
* 查看组件文档: https://whale.fusion.alibaba-inc.com/73015/component/bizcomps/692
|
||||
*/
|
||||
declare class WhaleLayout extends React.Component<WhaleLayoutProps, any> { }
|
||||
|
||||
export default WhaleLayout;
|
||||
5
packages/material-parser/test/fixtures/dts-component/package.json
vendored
Normal file
5
packages/material-parser/test/fixtures/dts-component/package.json
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "dts-component",
|
||||
"version": "1.2.3",
|
||||
"main": "./src/index.jsx"
|
||||
}
|
||||
1264
packages/material-parser/test/fixtures/dts-component/src/data.js
vendored
Normal file
1264
packages/material-parser/test/fixtures/dts-component/src/data.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
27
packages/material-parser/test/fixtures/dts-component/src/i18n.js
vendored
Normal file
27
packages/material-parser/test/fixtures/dts-component/src/i18n.js
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* 组件内置的多语言文案
|
||||
*/
|
||||
|
||||
const bundles = {
|
||||
'zh-CN': {
|
||||
dataSource: [
|
||||
{
|
||||
label: '+86',
|
||||
value: '86',
|
||||
country: '中国',
|
||||
rule: /(?=(\d{4})+$)/g,
|
||||
},
|
||||
],
|
||||
},
|
||||
'en-US': {
|
||||
dataSource: [
|
||||
{
|
||||
label: '+86',
|
||||
value: '86',
|
||||
country: 'china',
|
||||
rule: /(?=(\d{4})+$)/g,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
export default bundles;
|
||||
142
packages/material-parser/test/fixtures/dts-component/src/index.jsx
vendored
Normal file
142
packages/material-parser/test/fixtures/dts-component/src/index.jsx
vendored
Normal file
@ -0,0 +1,142 @@
|
||||
import React from 'react';
|
||||
import { Select, Input } from '@alifd/next';
|
||||
import i18n from '@alife/whale-i18n';
|
||||
import classnames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import { getLanguage } from '@alife/whale-util';
|
||||
|
||||
import i18nBundles from './i18n';
|
||||
import countryDataSource from './data';
|
||||
|
||||
const lang = getLanguage();
|
||||
const isCN = ['ZH_CN', 'ZH-CN'].includes(lang && lang.toUpperCase());
|
||||
|
||||
class WhaleTelephone extends React.Component {
|
||||
static displayName = 'WhaleTelephone';
|
||||
|
||||
static propTypes = {
|
||||
style: PropTypes.object,
|
||||
className: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
value: PropTypes.object,
|
||||
onChange: PropTypes.func,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
style: {},
|
||||
className: '',
|
||||
disabled: false,
|
||||
onChange: undefined,
|
||||
value: {
|
||||
countryCode: '+86',
|
||||
phoneNumber: '',
|
||||
},
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
popupStyle: {},
|
||||
tmpSelectedItem: {},
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (!this.props.readOnly) {
|
||||
this.handleStyle();
|
||||
window.addEventListener('resize', this.resizeEventFunction);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (!this.props.readOnly) {
|
||||
window.removeEventListener('resize', this.resizeEventFunction);
|
||||
}
|
||||
}
|
||||
|
||||
resizeEventFunction = () => this.handleStyle();
|
||||
|
||||
handleStyle = () => {
|
||||
const { el } = this;
|
||||
const elWidth = el.offsetWidth;
|
||||
this.setState({
|
||||
popupStyle: {
|
||||
width: elWidth,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
selectChange=(val, actionType, tmpSelectedItem) => {
|
||||
const { value, onChange } = this.props;
|
||||
this.setState({ tmpSelectedItem });
|
||||
onChange && onChange(Object.assign({}, value, { countryCode: val }), tmpSelectedItem, 'select');
|
||||
};
|
||||
|
||||
inputChange = (val) => {
|
||||
const { value, onChange } = this.props;
|
||||
const { tmpSelectedItem } = this.state;
|
||||
onChange && onChange(Object.assign({}, value, { phoneNumber: val }), tmpSelectedItem, 'input');
|
||||
};
|
||||
|
||||
renderItem = (item) => {
|
||||
const { countryName, countryName_zh: countryNameZh, value } = item;
|
||||
return (
|
||||
<div>
|
||||
<span className="country-span">{!isCN ? countryName : countryNameZh}</span>
|
||||
<span className="code-span">{value}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
// i18nBundle 是 i18n 包装后额外提供的 prop,表示组件当前实际使用的文案
|
||||
const {
|
||||
style,
|
||||
className,
|
||||
disabled,
|
||||
value: { countryCode, phoneNumber },
|
||||
readOnly,
|
||||
} = this.props;
|
||||
// 排除掉不需要透传下去的参数,特别地适用于直接把大部分特殊控制外的参数透传给 Fusion 等组件的用法
|
||||
const cls = classnames({
|
||||
'whale-telephone': true,
|
||||
'whale-telephone-readonly': !!readOnly,
|
||||
[className]: className,
|
||||
});
|
||||
const { popupStyle } = this.state;
|
||||
|
||||
if (readOnly) {
|
||||
return (
|
||||
<div className={cls} style={style}>
|
||||
<span>{countryCode}</span> <span>{phoneNumber}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cls} style={style} ref={(ref) => { this.el = ref; }} data-name="WhaleTelephone">
|
||||
<Select
|
||||
className="whale-telephone-select"
|
||||
disabled={disabled}
|
||||
dataSource={countryDataSource}
|
||||
itemRender={this.renderItem}
|
||||
popupStyle={popupStyle}
|
||||
popupClassName="whale-telephone-select-popup"
|
||||
defaultValue={countryCode}
|
||||
onChange={this.selectChange}
|
||||
/>
|
||||
<Input
|
||||
className="whale-telephone-number-input"
|
||||
value={phoneNumber}
|
||||
disabled={disabled}
|
||||
onChange={this.inputChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 默认 export 提供了内置多语言文案的组件
|
||||
export default i18n(WhaleTelephone, i18nBundles);
|
||||
// 万一需有对组件进行扩展,为了避免 HOC 后无法直接继承,提供了导出组件内置多语言文案及未经 HOC 的组件
|
||||
export { i18nBundles, WhaleTelephone as Pure };
|
||||
40
packages/material-parser/test/fixtures/dts-component/src/main.scss
vendored
Normal file
40
packages/material-parser/test/fixtures/dts-component/src/main.scss
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
|
||||
@import 'scss/variable';
|
||||
|
||||
// put your code here
|
||||
|
||||
.whale-telephone {
|
||||
display: flex;
|
||||
.whale-telephone-select {
|
||||
min-width: 72px;
|
||||
width: 72px;
|
||||
.next-select-inner {
|
||||
min-width: 72px;
|
||||
}
|
||||
.next-input.next-medium {
|
||||
border-radius: 0;
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
.whale-telephone-number-input {
|
||||
flex-grow: 2;
|
||||
border-radius: 0;
|
||||
&.next-input.next-medium {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.whale-telephone-readonly {
|
||||
font-family: Roboto;
|
||||
color: #262626;
|
||||
font-size: 14px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
}
|
||||
|
||||
.whale-telephone-select-popup {
|
||||
.code-span {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
12
packages/material-parser/test/fixtures/dts-component/src/scss/variable.scss
vendored
Normal file
12
packages/material-parser/test/fixtures/dts-component/src/scss/variable.scss
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
////
|
||||
/// @module @alife/whale-telephone: 组件中文名
|
||||
/// @tag WhaleTelephone
|
||||
/// @category custom
|
||||
/// @family whale-telephone
|
||||
/// @varPrefix $whale-telephone
|
||||
/// @classPrefix whale-telephone
|
||||
////
|
||||
|
||||
/// backgroud
|
||||
/// @namespace statement/normal
|
||||
$whale-telephone-color: $color-brand1-1 !default;
|
||||
@ -0,0 +1,11 @@
|
||||
import component from './index.js';
|
||||
import amManifest from './amManifest.js';
|
||||
|
||||
const getComponent = function () {
|
||||
return Promise.resolve(component);
|
||||
};
|
||||
|
||||
export default {
|
||||
getComponent,
|
||||
manifest: amManifest,
|
||||
};
|
||||
@ -0,0 +1,144 @@
|
||||
/**
|
||||
* The template of manifest for AiMake studio.
|
||||
*/
|
||||
function _update(Nygma, node) {
|
||||
const attributes = node.get();
|
||||
const display = attributes.display;
|
||||
const flexDirection = attributes.flexDirection;
|
||||
const alignItems = attributes.alignItems;
|
||||
const justifyContent = attributes.justifyContent;
|
||||
const flexWrap = attributes.flexWrap;
|
||||
const isFlex = display === 'flex';
|
||||
node.set({
|
||||
display,
|
||||
flexDirection: isFlex ? flexDirection : undefined,
|
||||
alignItems: isFlex ? alignItems : undefined,
|
||||
justifyContent: isFlex ? justifyContent : undefined,
|
||||
flexWrap: isFlex ? flexWrap : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
const manifest = {
|
||||
// The name of current component.
|
||||
name: 'AIMakeBlank',
|
||||
// The description of current component.
|
||||
description: '空白卡片',
|
||||
// The coverimage's url of current component.
|
||||
coverImage: 'https://img.alicdn.com/tfs/TB1un9tqntYBeNjy1XdXXXXyVXa-366-124.png',
|
||||
// The category of current component in AiMake studio.
|
||||
// can be:
|
||||
// `分栏` or `文本` or `按钮` or `标签` or `标签页` or `表格` or `单选` or `分割线`
|
||||
// or `分页` or `复选` or `滑动条` or `进度条` or `卡片` or `开关` or `缺省状态`
|
||||
// or `日期选择` or `输入框` or `搜索框` or `图表` or `图片` or `下拉选择` or `表单行`
|
||||
// or `树控件` or `折叠面板` or `占位图`
|
||||
category: '布局',
|
||||
// card.blank
|
||||
// The preview list of current component in AiMake studio.
|
||||
// Each preset contains following keys:
|
||||
// - `alias`: string. required. The previewing component's name to display
|
||||
// - `thumbnail`: string. not required. The previewing component's thumbnail
|
||||
// - `customProps`: object. not required.
|
||||
// The previewing component's customize props, e.g. { [propName]: [propValue] }
|
||||
// - `colSpan`: number. not required. default 24 (1~24). The previewing component's size when to display
|
||||
presets: [{
|
||||
alias: '空白卡片',
|
||||
thumbnail: 'https://img.alicdn.com/tfs/TB1ucPNVsbpK1RjSZFyXXX_qFXa-198-120.png',
|
||||
colSpan: 12,
|
||||
customProps: {
|
||||
id: '',
|
||||
textAlign: 'left',
|
||||
padding: '12px',
|
||||
width: '100%',
|
||||
backgroundColor: '#FFF',
|
||||
},
|
||||
}],
|
||||
// Other settings of current component for AiMake studio.
|
||||
settings: {
|
||||
// The render type of current component in AiMake studio.
|
||||
// can be:
|
||||
// `element_inline` or `element_block` or `container`
|
||||
type: 'container',
|
||||
// The insert mode of current component in AiMake studio.
|
||||
// can be:
|
||||
// one or combine of `t` and `b` and `r` and `l`
|
||||
insertionModes: 'tbrlv',
|
||||
// The handle list of current component in AiMake studio.
|
||||
// can be:
|
||||
// an array contains one and more of ['cut', 'copy', 'paste', 'delete', 'duplicate']
|
||||
handles: ['cut', 'copy', 'paste', 'delete', 'duplicate'],
|
||||
// Whether the component can be actived.
|
||||
shouldActive: true,
|
||||
// Whether the component can be dragged.
|
||||
shouldDrag: true,
|
||||
lifeCycle: {
|
||||
didMount (props) {
|
||||
const Nygma = props.Nygma;
|
||||
const dragInstance = props.dragInstance;
|
||||
const Drager = dragInstance.NygmaNode;
|
||||
|
||||
_update(Nygma, Drager);
|
||||
},
|
||||
didUpdate (Nygma, node, args) {
|
||||
const newvalue = args[1];
|
||||
const oldvalue = args[2];
|
||||
|
||||
if (JSON.stringify(newvalue) !== JSON.stringify(oldvalue)) {
|
||||
_update(Nygma, node);
|
||||
}
|
||||
},
|
||||
},
|
||||
// The props of current component in AiMake studio.
|
||||
// Each property contains following keys:
|
||||
// - `name`: string. required. The property's name
|
||||
// - `label`: string. required. The property's name to display
|
||||
// - `renderer`: string. required. The property's editor. can be: (@冰骊)
|
||||
// - `defaultValue`: any. not required. The property's default value
|
||||
// - `params`: any. not required. The parameters for property's editor
|
||||
// - `placeholder`: string. not required. The placeholder for property's editor
|
||||
// - `hint`: string. not required. The hint for property's editor
|
||||
props: [{
|
||||
name: 'id',
|
||||
label: 'id',
|
||||
defaultValue: '',
|
||||
renderer: 'Input',
|
||||
}, {
|
||||
name: 'textAlign',
|
||||
label: '水平对齐',
|
||||
defaultValue: 'left',
|
||||
renderer: 'TextAlign',
|
||||
}, {
|
||||
name: 'margin',
|
||||
label: '外边距',
|
||||
renderer: 'Quadrant',
|
||||
}, {
|
||||
name: 'padding',
|
||||
label: '内边距',
|
||||
renderer: 'Quadrant',
|
||||
defaultValue: '12px',
|
||||
}, {
|
||||
name: 'width',
|
||||
label: '宽度',
|
||||
defaultValue: '100%',
|
||||
renderer: 'Width',
|
||||
}, {
|
||||
name: 'height',
|
||||
label: '高度',
|
||||
renderer: 'Height',
|
||||
defaultValue: undefined,
|
||||
}, {
|
||||
name: 'backgroundColor',
|
||||
label: '背景颜色',
|
||||
renderer: 'Color',
|
||||
defaultValue: '#FFF',
|
||||
}, {
|
||||
name: 'border',
|
||||
label: '边框',
|
||||
renderer: 'BarBorder',
|
||||
}, {
|
||||
name: 'display',
|
||||
label: '布局设置',
|
||||
renderer: 'FlexLayout',
|
||||
}],
|
||||
},
|
||||
};
|
||||
export default manifest;
|
||||
@ -0,0 +1,6 @@
|
||||
|
||||
import AIMakeBlank from './index.js';
|
||||
import manifest from './manifest.js';
|
||||
|
||||
export default { origin: AIMakeBlank, manifest };
|
||||
|
||||
74
packages/material-parser/test/fixtures/multiple-exported-component/es/basic/AIMakeBlank/index.js
vendored
Normal file
74
packages/material-parser/test/fixtures/multiple-exported-component/es/basic/AIMakeBlank/index.js
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
import _extends from "@babel/runtime/helpers/extends";
|
||||
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
||||
import _createClass from "@babel/runtime/helpers/createClass";
|
||||
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
|
||||
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
|
||||
import _inherits from "@babel/runtime/helpers/inherits";
|
||||
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import HOCBoxModelProps from '../utils/HOCBoxModelProps';
|
||||
import HOCLayoutProps from '../utils/HOCLayoutProps';
|
||||
import HOCBackgroundProps from '../utils/HOCBackgroundProps';
|
||||
import HOCFlexLayoutProps from '../utils/HOCFlexLayoutProps';
|
||||
|
||||
const AIMakeBlank =
|
||||
/* #__PURE__ */
|
||||
function (_Component) {
|
||||
_inherits(AIMakeBlank, _Component);
|
||||
|
||||
function AIMakeBlank() {
|
||||
_classCallCheck(this, AIMakeBlank);
|
||||
|
||||
return _possibleConstructorReturn(this, _getPrototypeOf(AIMakeBlank).apply(this, arguments));
|
||||
}
|
||||
|
||||
_createClass(AIMakeBlank, [{
|
||||
key: "render",
|
||||
value: function render() {
|
||||
const merged = {};
|
||||
const _this$props = this.props;
|
||||
const children = _this$props.children;
|
||||
const styleBoxModel = _this$props.styleBoxModel;
|
||||
const styleLayout = _this$props.styleLayout;
|
||||
const styleBackground = _this$props.styleBackground;
|
||||
const styleFlexLayout = _this$props.styleFlexLayout;
|
||||
const style = _this$props.style;
|
||||
const id = _this$props.id;
|
||||
const styles = { ...styleBoxModel,
|
||||
...styleLayout,
|
||||
...styleBackground,
|
||||
...styleFlexLayout,
|
||||
...style,
|
||||
};
|
||||
|
||||
if (id) {
|
||||
merged.id = id;
|
||||
}
|
||||
|
||||
return React.createElement("div", _extends({
|
||||
style: styles,
|
||||
}, merged), children);
|
||||
},
|
||||
}]);
|
||||
|
||||
return AIMakeBlank;
|
||||
}(Component);
|
||||
|
||||
_defineProperty(AIMakeBlank, "propTypes", {
|
||||
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
|
||||
styleBoxModel: PropTypes.object.isRequired,
|
||||
styleLayout: PropTypes.object.isRequired,
|
||||
styleBackground: PropTypes.object.isRequired,
|
||||
styleFlexLayout: PropTypes.object.isRequired,
|
||||
style: PropTypes.object,
|
||||
id: PropTypes.string,
|
||||
});
|
||||
|
||||
_defineProperty(AIMakeBlank, "defaultProps", {
|
||||
children: [],
|
||||
style: {},
|
||||
id: '',
|
||||
});
|
||||
|
||||
export default HOCBoxModelProps(HOCLayoutProps(HOCBackgroundProps(HOCFlexLayoutProps(AIMakeBlank))));
|
||||
@ -0,0 +1 @@
|
||||
{"componentName":"AIMakeBlank","title":"","docUrl":"","screenshot":"","npm":{"package":"@ali/lowcode-engine-material-parser","version":"0.1.0","exportName":"AIMakeBlank","main":"/Users/gengyang/code/frontend/low-code/ali-lowcode-engine/packages/material-parser/test/fixtures/multiple-exported-component/es/index.js","destructuring":false,"subName":""},"props":[{"name":"children","propType":"oneOfType","description":""},{"name":"styleBoxModel","propType":"object","description":""},{"name":"styleLayout","propType":"object","description":""},{"name":"styleBackground","propType":"object","description":""},{"name":"styleFlexLayout","propType":"object","description":""},{"name":"style","propType":"object","description":"","defaultValue":"{}"},{"name":"id","propType":"string","description":""}]}
|
||||
@ -0,0 +1 @@
|
||||
{"title":"multiple-exported-component","docUrl":"","screenshot":"","npm":{"package":"multiple-exported-component","version":"1.0.0","exportName":"","main":"/Users/gengyang/code/frontend/low-code/ali-lowcode-engine/packages/material-parser/test/fixtures/multiple-exported-component/es/index.js","destructuring":false,"subName":""},"props":[]}
|
||||
@ -0,0 +1,67 @@
|
||||
import _extends from "@babel/runtime/helpers/extends";
|
||||
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
|
||||
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
||||
import _createClass from "@babel/runtime/helpers/createClass";
|
||||
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
|
||||
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
|
||||
import _inherits from "@babel/runtime/helpers/inherits";
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types'; // 缓存已加载的字体文件
|
||||
|
||||
const customCache = new Set(); // 动态加载字体文件
|
||||
|
||||
export default function createFromIconfont(options) {
|
||||
const scriptUrl = options.scriptUrl;
|
||||
|
||||
if (typeof document !== 'undefined' && typeof window !== 'undefined' && typeof document.createElement === 'function' && typeof scriptUrl === 'string' && scriptUrl.length && !customCache.has(scriptUrl)) {
|
||||
const script = document.createElement('script');
|
||||
script.setAttribute('src', scriptUrl);
|
||||
script.setAttribute('data-namespace', scriptUrl);
|
||||
customCache.add(scriptUrl);
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
|
||||
const IconFont =
|
||||
/* #__PURE__ */
|
||||
function (_Component) {
|
||||
_inherits(IconFont, _Component);
|
||||
|
||||
function IconFont() {
|
||||
_classCallCheck(this, IconFont);
|
||||
|
||||
return _possibleConstructorReturn(this, _getPrototypeOf(IconFont).apply(this, arguments));
|
||||
}
|
||||
|
||||
_createClass(IconFont, [{
|
||||
key: "render",
|
||||
value: function render() {
|
||||
const _this$props = this.props;
|
||||
const type = _this$props.type;
|
||||
const restProps = _objectWithoutProperties(_this$props, ["type"]);
|
||||
|
||||
const innerSvgProps = {
|
||||
width: '1em',
|
||||
height: '1em',
|
||||
fill: 'currentColor',
|
||||
'aria-hidden': 'true',
|
||||
focusable: 'false',
|
||||
}; // 引用指定svg
|
||||
|
||||
const content = React.createElement("use", {
|
||||
xlinkHref: `#${type}`,
|
||||
});
|
||||
return React.createElement("i", _extends({}, restProps, {
|
||||
className: `iconfont ${type}`,
|
||||
}), React.createElement("svg", innerSvgProps, content));
|
||||
},
|
||||
}]);
|
||||
|
||||
return IconFont;
|
||||
}(Component);
|
||||
|
||||
IconFont.propTypes = {
|
||||
type: PropTypes.string.isRequired, // icon
|
||||
|
||||
};
|
||||
return IconFont;
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
import component from './index.js';
|
||||
import amManifest from './amManifest.js';
|
||||
import createFromIconfont from './IconFont';
|
||||
|
||||
const getComponent = function () {
|
||||
return Promise.resolve(component);
|
||||
};
|
||||
|
||||
export default {
|
||||
getComponent,
|
||||
manifest: amManifest,
|
||||
createFromIconfont,
|
||||
};
|
||||
@ -0,0 +1,82 @@
|
||||
/**
|
||||
* The template of manifest for AiMake studio.
|
||||
*/
|
||||
const manifest = {
|
||||
// The name of current component.
|
||||
name: 'AIMakeIcon',
|
||||
// The description of current component.
|
||||
description: '图标',
|
||||
// The coverimage's url of current component.
|
||||
coverImage: '',
|
||||
// The category of current component in AiMake studio.
|
||||
// can be:
|
||||
// `分栏` or `文本` or `按钮` or `标签` or `标签页` or `表格` or `单选` or `分割线`
|
||||
// or `分页` or `复选` or `滑动条` or `进度条` or `卡片` or `开关` or `缺省状态`
|
||||
// or `日期选择` or `输入框` or `搜索框` or `图表` or `图片` or `下拉选择` or `表单行`
|
||||
// or `树控件` or `折叠面板` or `占位图`
|
||||
category: 'AIMakeIcon',
|
||||
// The preview list of current component in AiMake studio.
|
||||
// Each preset contains following keys:
|
||||
// - `alias`: string. required. The previewing component's name to display
|
||||
// - `thumbnail`: string. not required. The previewing component's thumbnail
|
||||
// - `customProps`: object. not required.
|
||||
// The previewing component's customize props, e.g. { [propName]: [propValue] }
|
||||
// - `colSpan`: number. not required. default 24 (1~24). The previewing component's size when to display
|
||||
presets: [],
|
||||
// Other settings of current component for AiMake studio.
|
||||
settings: {
|
||||
// The render type of current component in AiMake studio.
|
||||
// can be:
|
||||
// `element_inline` or `element_block` or `container`
|
||||
type: 'element_inline',
|
||||
// The insert mode of current component in AiMake studio.
|
||||
// can be:
|
||||
// one or combine of `t` and `b` and `r` and `l`
|
||||
insertionModes: 'rl',
|
||||
// The handle list of current component in AiMake studio.
|
||||
// can be:
|
||||
// an array contains one and more of ['cut', 'copy', 'paste', 'delete', 'duplicate']
|
||||
handles: ['cut', 'copy', 'paste', 'delete', 'duplicate'],
|
||||
// Whether the component can be actived.
|
||||
shouldActive: true,
|
||||
// Whether the component can be dragged.
|
||||
shouldDrag: true,
|
||||
// The props of current component in AiMake studio.
|
||||
// Each property contains following keys:
|
||||
// - `name`: string. required. The property's name
|
||||
// - `label`: string. required. The property's name to display
|
||||
// - `renderer`: string. required. The property's editor. can be: (@冰骊)
|
||||
// - `defaultValue`: any. not required. The property's default value
|
||||
// - `params`: any. not required. The parameters for property's editor
|
||||
// - `placeholder`: string. not required. The placeholder for property's editor
|
||||
// - `hint`: string. not required. The hint for property's editor
|
||||
props: [{
|
||||
name: 'margin',
|
||||
label: '外边距',
|
||||
renderer: 'Quadrant',
|
||||
}, {
|
||||
name: 'color',
|
||||
label: '图标颜色',
|
||||
renderer: 'Color',
|
||||
defaultValue: '#333',
|
||||
}, {
|
||||
name: 'fontSize',
|
||||
label: '图标大小',
|
||||
renderer: 'FontSize',
|
||||
defaultValue: '16px',
|
||||
}, {
|
||||
name: 'display',
|
||||
label: '显示',
|
||||
defaultValue: 'inline-block',
|
||||
}, {
|
||||
name: 'className',
|
||||
label: '图标类型',
|
||||
defaultValue: 'iconfont',
|
||||
renderer: false,
|
||||
params: {
|
||||
placeholder: '请输入Iconfont名',
|
||||
},
|
||||
}],
|
||||
},
|
||||
};
|
||||
export default manifest;
|
||||
@ -0,0 +1,6 @@
|
||||
|
||||
import AIMakeIcon from './index.js';
|
||||
import manifest from './manifest.js';
|
||||
|
||||
export default { origin: AIMakeIcon, manifest };
|
||||
|
||||
71
packages/material-parser/test/fixtures/multiple-exported-component/es/basic/AIMakeIcon/index.js
vendored
Normal file
71
packages/material-parser/test/fixtures/multiple-exported-component/es/basic/AIMakeIcon/index.js
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
import _extends from "@babel/runtime/helpers/extends";
|
||||
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
|
||||
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
||||
import _createClass from "@babel/runtime/helpers/createClass";
|
||||
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
|
||||
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
|
||||
import _inherits from "@babel/runtime/helpers/inherits";
|
||||
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import createFromIconfont from './IconFont';
|
||||
|
||||
const AIMakeIcon =
|
||||
/* #__PURE__ */
|
||||
function (_Component) {
|
||||
_inherits(AIMakeIcon, _Component);
|
||||
|
||||
function AIMakeIcon() {
|
||||
_classCallCheck(this, AIMakeIcon);
|
||||
|
||||
return _possibleConstructorReturn(this, _getPrototypeOf(AIMakeIcon).apply(this, arguments));
|
||||
}
|
||||
|
||||
_createClass(AIMakeIcon, [{
|
||||
key: "render",
|
||||
value: function render() {
|
||||
const _this$props = this.props;
|
||||
const className = _this$props.className;
|
||||
const iconClassName = _this$props.iconClassName;
|
||||
const children = _this$props.children;
|
||||
const styleBoxModel = _this$props.styleBoxModel;
|
||||
const styleText = _this$props.styleText;
|
||||
const styleBackground = _this$props.styleBackground;
|
||||
const style = _this$props.style;
|
||||
const otherProps = _objectWithoutProperties(_this$props, ["className", "iconClassName", "children", "styleBoxModel", "styleText", "styleBackground", "style"]);
|
||||
|
||||
const styles = { ...styleBoxModel,
|
||||
...styleText,
|
||||
...styleBackground,
|
||||
...style,
|
||||
};
|
||||
return React.createElement("i", _extends({}, otherProps, {
|
||||
className: classNames(className, iconClassName),
|
||||
style: styles,
|
||||
}), children);
|
||||
},
|
||||
}]);
|
||||
|
||||
return AIMakeIcon;
|
||||
}(Component);
|
||||
|
||||
_defineProperty(AIMakeIcon, "propTypes", {
|
||||
className: PropTypes.string,
|
||||
iconClassName: PropTypes.string,
|
||||
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
|
||||
styleBoxModel: PropTypes.object.isRequired,
|
||||
styleText: PropTypes.object.isRequired,
|
||||
styleBackground: PropTypes.object.isRequired,
|
||||
style: PropTypes.object,
|
||||
});
|
||||
|
||||
_defineProperty(AIMakeIcon, "defaultProps", {
|
||||
className: '',
|
||||
iconClassName: 'iconfont',
|
||||
children: '',
|
||||
style: {},
|
||||
});
|
||||
|
||||
AIMakeIcon.createFromIconfont = createFromIconfont;
|
||||
export default AIMakeIcon;
|
||||
@ -0,0 +1 @@
|
||||
{"componentName":"AIMakeIcon","title":"","docUrl":"","screenshot":"","npm":{"package":"@ali/lowcode-engine-material-parser","version":"0.1.0","exportName":"AIMakeIcon","main":"/Users/gengyang/code/frontend/low-code/ali-lowcode-engine/packages/material-parser/test/fixtures/multiple-exported-component/es/index.js","destructuring":false,"subName":""},"props":[{"name":"className","propType":"string","description":""},{"name":"iconClassName","propType":"string","description":""},{"name":"children","propType":"oneOfType","description":""},{"name":"styleBoxModel","propType":"object","description":""},{"name":"styleText","propType":"object","description":""},{"name":"styleBackground","propType":"object","description":""},{"name":"style","propType":"object","description":"","defaultValue":"{}"}]}
|
||||
@ -0,0 +1 @@
|
||||
{"title":"multiple-exported-component","docUrl":"","screenshot":"","npm":{"package":"multiple-exported-component","version":"1.0.0","exportName":"","main":"/Users/gengyang/code/frontend/low-code/ali-lowcode-engine/packages/material-parser/test/fixtures/multiple-exported-component/es/index.js","destructuring":false,"subName":""},"props":[]}
|
||||
@ -0,0 +1,11 @@
|
||||
import component from './index.js';
|
||||
import amManifest from './amManifest.js';
|
||||
|
||||
const getComponent = function () {
|
||||
return Promise.resolve(component);
|
||||
};
|
||||
|
||||
export default {
|
||||
getComponent,
|
||||
manifest: amManifest,
|
||||
};
|
||||
@ -0,0 +1,86 @@
|
||||
/**
|
||||
* The template of manifest for AiMake studio.
|
||||
*/
|
||||
const manifest = {
|
||||
// The name of current component.
|
||||
name: 'AIMakeImage',
|
||||
// The description of current component.
|
||||
description: '图片',
|
||||
// The coverimage's url of current component.
|
||||
coverImage: 'https://img.alicdn.com/tfs/TB17gMFp1uSBuNjy1XcXXcYjFXa-172-120.png',
|
||||
// The category of current component in AiMake studio.
|
||||
// can be:
|
||||
// `分栏` or `文本` or `按钮` or `标签` or `标签页` or `表格` or `单选` or `分割线`
|
||||
// or `分页` or `复选` or `滑动条` or `进度条` or `卡片` or `开关` or `缺省状态`
|
||||
// or `日期选择` or `输入框` or `搜索框` or `图表` or `图片` or `下拉选择` or `表单行`
|
||||
// or `树控件` or `折叠面板` or `占位图`
|
||||
category: '线条图像',
|
||||
// The preview list of current component in AiMake studio.
|
||||
// Each preset contains following keys:
|
||||
// - `alias`: string. required. The previewing component's name to display
|
||||
// - `thumbnail`: string. not required. The previewing component's thumbnail
|
||||
// - `customProps`: object. not required.
|
||||
// The previewing component's customize props, e.g. { [propName]: [propValue] }
|
||||
// - `colSpan`: number. not required. default 24 (1~24). The previewing component's size when to display
|
||||
presets: [{
|
||||
alias: '图片',
|
||||
thumbnail: 'https://img.alicdn.com/tfs/TB17gMFp1uSBuNjy1XcXXcYjFXa-172-120.png',
|
||||
colSpan: 12,
|
||||
customProps: {
|
||||
width: '224px',
|
||||
height: '126px',
|
||||
src: 'https://img.alicdn.com/tfs/TB1RtEMGbSYBuNjSspfXXcZCpXa-448-252.png',
|
||||
},
|
||||
}],
|
||||
// Other settings of current component for AiMake studio.
|
||||
settings: {
|
||||
// The render type of current component in AiMake studio.
|
||||
// can be:
|
||||
// `element_inline` or `element_block` or `container`
|
||||
type: 'element_inline',
|
||||
// The insert mode of current component in AiMake studio.
|
||||
// can be:
|
||||
// one or combine of `t` and `b` and `r` and `l`
|
||||
insertionModes: 'rl',
|
||||
// The handle list of current component in AiMake studio.
|
||||
// can be:
|
||||
// an array contains one and more of ['cut', 'copy', 'paste', 'delete', 'duplicate']
|
||||
handles: ['cut', 'copy', 'paste', 'delete', 'duplicate'],
|
||||
// Whether the component can be actived.
|
||||
shouldActive: true,
|
||||
// Whether the component can be dragged.
|
||||
shouldDrag: true,
|
||||
// The props of current component in AiMake studio.
|
||||
// Each property contains following keys:
|
||||
// - `name`: string. required. The property's name
|
||||
// - `label`: string. required. The property's name to display
|
||||
// - `renderer`: string. required. The property's editor. can be: (@冰骊)
|
||||
// - `defaultValue`: any. not required. The property's default value
|
||||
// - `params`: any. not required. The parameters for property's editor
|
||||
// - `placeholder`: string. not required. The placeholder for property's editor
|
||||
// - `hint`: string. not required. The hint for property's editor
|
||||
props: [{
|
||||
name: 'margin',
|
||||
label: '外边距',
|
||||
renderer: 'Quadrant',
|
||||
}, {
|
||||
name: 'width',
|
||||
label: '宽度',
|
||||
defaultValue: '224px',
|
||||
renderer: 'Width',
|
||||
}, {
|
||||
name: 'height',
|
||||
label: '高度',
|
||||
defaultValue: '126px',
|
||||
renderer: 'Height',
|
||||
}, {
|
||||
name: 'src',
|
||||
label: '图片URL',
|
||||
renderer: 'Uploader',
|
||||
placeholder: 'eg: https://img.alicdn.com/tfs/TB1RtEMGbSYBuNjSspfXXcZCpXa-448-252.png',
|
||||
hint: '请填入图片的URL',
|
||||
defaultValue: 'https://img.alicdn.com/tfs/TB1RtEMGbSYBuNjSspfXXcZCpXa-448-252.png',
|
||||
}],
|
||||
},
|
||||
};
|
||||
export default manifest;
|
||||
@ -0,0 +1,6 @@
|
||||
|
||||
import AIMakeImage from './index.js';
|
||||
import manifest from './manifest.js';
|
||||
|
||||
export default { origin: AIMakeImage, manifest };
|
||||
|
||||
54
packages/material-parser/test/fixtures/multiple-exported-component/es/basic/AIMakeImage/index.js
vendored
Normal file
54
packages/material-parser/test/fixtures/multiple-exported-component/es/basic/AIMakeImage/index.js
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
import _extends from "@babel/runtime/helpers/extends";
|
||||
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
|
||||
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
||||
import _createClass from "@babel/runtime/helpers/createClass";
|
||||
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
|
||||
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
|
||||
import _inherits from "@babel/runtime/helpers/inherits";
|
||||
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import HOCBoxModelProps from '../utils/HOCBoxModelProps';
|
||||
|
||||
const AIMakeImage =
|
||||
/* #__PURE__ */
|
||||
function (_Component) {
|
||||
_inherits(AIMakeImage, _Component);
|
||||
|
||||
function AIMakeImage() {
|
||||
_classCallCheck(this, AIMakeImage);
|
||||
|
||||
return _possibleConstructorReturn(this, _getPrototypeOf(AIMakeImage).apply(this, arguments));
|
||||
}
|
||||
|
||||
_createClass(AIMakeImage, [{
|
||||
key: "render",
|
||||
value: function render() {
|
||||
const _this$props = this.props;
|
||||
const styleBoxModel = _this$props.styleBoxModel;
|
||||
const style = _this$props.style;
|
||||
const otherProps = _objectWithoutProperties(_this$props, ["styleBoxModel", "style"]);
|
||||
|
||||
const styles = { ...styleBoxModel,
|
||||
...style,
|
||||
};
|
||||
return React.createElement("img", _extends({}, otherProps, {
|
||||
style: styles,
|
||||
alt: "AIMakeImage",
|
||||
}));
|
||||
},
|
||||
}]);
|
||||
|
||||
return AIMakeImage;
|
||||
}(Component);
|
||||
|
||||
_defineProperty(AIMakeImage, "propTypes", {
|
||||
styleBoxModel: PropTypes.object.isRequired,
|
||||
style: PropTypes.object,
|
||||
});
|
||||
|
||||
_defineProperty(AIMakeImage, "defaultProps", {
|
||||
style: {},
|
||||
});
|
||||
|
||||
export default HOCBoxModelProps(AIMakeImage);
|
||||
@ -0,0 +1 @@
|
||||
{"componentName":"AIMakeImage","title":"","docUrl":"","screenshot":"","npm":{"package":"@ali/lowcode-engine-material-parser","version":"0.1.0","exportName":"AIMakeImage","main":"/Users/gengyang/code/frontend/low-code/ali-lowcode-engine/packages/material-parser/test/fixtures/multiple-exported-component/es/index.js","destructuring":false,"subName":""},"props":[{"name":"styleBoxModel","propType":"object","description":""},{"name":"style","propType":"object","description":"","defaultValue":"{}"}]}
|
||||
@ -0,0 +1 @@
|
||||
{"title":"multiple-exported-component","docUrl":"","screenshot":"","npm":{"package":"multiple-exported-component","version":"1.0.0","exportName":"","main":"/Users/gengyang/code/frontend/low-code/ali-lowcode-engine/packages/material-parser/test/fixtures/multiple-exported-component/es/index.js","destructuring":false,"subName":""},"props":[]}
|
||||
@ -0,0 +1,11 @@
|
||||
import component from './index.js';
|
||||
import amManifest from './amManifest.js';
|
||||
|
||||
const getComponent = function () {
|
||||
return Promise.resolve(component);
|
||||
};
|
||||
|
||||
export default {
|
||||
getComponent,
|
||||
manifest: amManifest,
|
||||
};
|
||||
102
packages/material-parser/test/fixtures/multiple-exported-component/es/basic/AIMakeLink/amManifest.js
vendored
Normal file
102
packages/material-parser/test/fixtures/multiple-exported-component/es/basic/AIMakeLink/amManifest.js
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
/**
|
||||
* The template of manifest for AiMake studio.
|
||||
*/
|
||||
const manifest = {
|
||||
// The name of current component.
|
||||
name: 'AIMakeLink',
|
||||
// The description of current component.
|
||||
description: '链接',
|
||||
// The coverimage's url of current component.
|
||||
coverImage: 'https://img.alicdn.com/tfs/TB1otbyVwTqK1RjSZPhXXXfOFXa-192-48.png',
|
||||
// The category of current component in AiMake studio.
|
||||
// can be:
|
||||
// `分栏` or `文本` or `按钮` or `标签` or `标签页` or `表格` or `单选` or `分割线`
|
||||
// or `分页` or `复选` or `滑动条` or `进度条` or `卡片` or `开关` or `缺省状态`
|
||||
// or `日期选择` or `输入框` or `搜索框` or `图表` or `图片` or `下拉选择` or `表单行`
|
||||
// or `树控件` or `折叠面板` or `占位图`
|
||||
category: '文本',
|
||||
// The preview list of current component in AiMake studio.
|
||||
// Each preset contains following keys:
|
||||
// - `alias`: string. required. The previewing component's name to display
|
||||
// - `thumbnail`: string. not required. The previewing component's thumbnail
|
||||
// - `customProps`: object. not required.
|
||||
// The previewing component's customize props, e.g. { [propName]: [propValue] }
|
||||
// - `colSpan`: number. not required. default 24 (1~24). The previewing component's size when to display
|
||||
presets: [{
|
||||
alias: '链接',
|
||||
thumbnail: 'https://img.alicdn.com/tfs/TB1otbyVwTqK1RjSZPhXXXfOFXa-192-48.png',
|
||||
colSpan: 12,
|
||||
customProps: {
|
||||
color: '#3788FF',
|
||||
fontSize: '12px',
|
||||
fontWeight: 'normal',
|
||||
href: '#',
|
||||
children: '链接',
|
||||
},
|
||||
}],
|
||||
// Other settings of current component for AiMake studio.
|
||||
settings: {
|
||||
// The render type of current component in AiMake studio.
|
||||
// can be:
|
||||
// `element_inline` or `element_block` or `container`
|
||||
type: 'element_inline',
|
||||
// The insert mode of current component in AiMake studio.
|
||||
// can be:
|
||||
// one or combine of `t` and `b` and `r` and `l`
|
||||
insertionModes: 'lrv',
|
||||
// The handle list of current component in AiMake studio.
|
||||
// can be:
|
||||
// an array contains one and more of ['cut', 'copy', 'paste', 'delete', 'duplicate']
|
||||
handles: ['cut', 'copy', 'paste', 'delete', 'duplicate'],
|
||||
// Whether the component can be actived.
|
||||
shouldActive: true,
|
||||
// Whether the component can be dragged.
|
||||
shouldDrag: true,
|
||||
// The props of current component in AiMake studio.
|
||||
// Each property contains following keys:
|
||||
// - `name`: string. required. The property's name
|
||||
// - `label`: string. required. The property's name to display
|
||||
// - `renderer`: string. required. The property's editor. can be: (@冰骊)
|
||||
// - `defaultValue`: any. not required. The property's default value
|
||||
// - `params`: any. not required. The parameters for property's editor
|
||||
// - `placeholder`: string. not required. The placeholder for property's editor
|
||||
// - `hint`: string. not required. The hint for property's editor
|
||||
props: [{
|
||||
name: 'margin',
|
||||
label: '外边距',
|
||||
renderer: 'Quadrant',
|
||||
}, {
|
||||
name: 'color',
|
||||
label: '文字颜色',
|
||||
renderer: 'Color',
|
||||
defaultValue: '#3788FF',
|
||||
}, {
|
||||
name: 'fontSize',
|
||||
label: '字号',
|
||||
renderer: 'FontSize',
|
||||
defaultValue: '12px',
|
||||
}, {
|
||||
name: 'fontWeight',
|
||||
label: '字重',
|
||||
renderer: 'FontWeight',
|
||||
defaultValue: 'normal',
|
||||
}, {
|
||||
name: 'lineHeight',
|
||||
label: '行高',
|
||||
defaultValue: undefined,
|
||||
renderer: 'LineHeight',
|
||||
}, {
|
||||
name: 'href',
|
||||
label: '链接URL',
|
||||
renderer: 'Input',
|
||||
placeholder: '请输入链接URL',
|
||||
defaultValue: '#',
|
||||
}, {
|
||||
name: 'children',
|
||||
label: '内容',
|
||||
defaultValue: '链接',
|
||||
renderer: 'TextArea',
|
||||
}],
|
||||
},
|
||||
};
|
||||
export default manifest;
|
||||
@ -0,0 +1,6 @@
|
||||
|
||||
import AIMakeLink from './index.js';
|
||||
import manifest from './manifest.js';
|
||||
|
||||
export default { origin: AIMakeLink, manifest };
|
||||
|
||||
73
packages/material-parser/test/fixtures/multiple-exported-component/es/basic/AIMakeLink/index.js
vendored
Normal file
73
packages/material-parser/test/fixtures/multiple-exported-component/es/basic/AIMakeLink/index.js
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
import _extends from "@babel/runtime/helpers/extends";
|
||||
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
|
||||
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
||||
import _createClass from "@babel/runtime/helpers/createClass";
|
||||
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
|
||||
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
|
||||
import _inherits from "@babel/runtime/helpers/inherits";
|
||||
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import HOCBoxModelProps from '../utils/HOCBoxModelProps';
|
||||
import HOCTextProps from '../utils/HOCTextProps';
|
||||
import HOCLayoutProps from '../utils/HOCLayoutProps';
|
||||
import HOCBackgroundProps from '../utils/HOCBackgroundProps';
|
||||
|
||||
const AIMakeLink =
|
||||
/* #__PURE__ */
|
||||
function (_Component) {
|
||||
_inherits(AIMakeLink, _Component);
|
||||
|
||||
function AIMakeLink() {
|
||||
_classCallCheck(this, AIMakeLink);
|
||||
|
||||
return _possibleConstructorReturn(this, _getPrototypeOf(AIMakeLink).apply(this, arguments));
|
||||
}
|
||||
|
||||
_createClass(AIMakeLink, [{
|
||||
key: "render",
|
||||
value: function render() {
|
||||
const _this$props = this.props;
|
||||
const children = _this$props.children;
|
||||
const styleBoxModel = _this$props.styleBoxModel;
|
||||
const styleText = _this$props.styleText;
|
||||
const styleLayout = _this$props.styleLayout;
|
||||
const styleBackground = _this$props.styleBackground;
|
||||
const style = _this$props.style;
|
||||
const otherProps = _objectWithoutProperties(_this$props, ["children", "styleBoxModel", "styleText", "styleLayout", "styleBackground", "style"]);
|
||||
|
||||
const styles = { ...styleBoxModel,
|
||||
...styleText,
|
||||
...styleLayout,
|
||||
...styleBackground,
|
||||
...style,
|
||||
};
|
||||
|
||||
if (typeof children !== 'string') {
|
||||
styles.display = 'inline-block';
|
||||
}
|
||||
|
||||
return React.createElement("a", _extends({}, otherProps, {
|
||||
style: styles,
|
||||
}), [children]);
|
||||
},
|
||||
}]);
|
||||
|
||||
return AIMakeLink;
|
||||
}(Component);
|
||||
|
||||
_defineProperty(AIMakeLink, "propTypes", {
|
||||
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
|
||||
styleBoxModel: PropTypes.object.isRequired,
|
||||
styleText: PropTypes.object.isRequired,
|
||||
styleLayout: PropTypes.object.isRequired,
|
||||
styleBackground: PropTypes.object.isRequired,
|
||||
style: PropTypes.object,
|
||||
});
|
||||
|
||||
_defineProperty(AIMakeLink, "defaultProps", {
|
||||
children: '',
|
||||
style: {},
|
||||
});
|
||||
|
||||
export default HOCBoxModelProps(HOCTextProps(HOCLayoutProps(HOCBackgroundProps(AIMakeLink))));
|
||||
@ -0,0 +1 @@
|
||||
{"componentName":"AIMakeLink","title":"","docUrl":"","screenshot":"","npm":{"package":"@ali/lowcode-engine-material-parser","version":"0.1.0","exportName":"AIMakeLink","main":"/Users/gengyang/code/frontend/low-code/ali-lowcode-engine/packages/material-parser/test/fixtures/multiple-exported-component/es/index.js","destructuring":false,"subName":""},"props":[{"name":"children","propType":"oneOfType","description":""},{"name":"styleBoxModel","propType":"object","description":""},{"name":"styleText","propType":"object","description":""},{"name":"styleLayout","propType":"object","description":""},{"name":"styleBackground","propType":"object","description":""},{"name":"style","propType":"object","description":"","defaultValue":"{}"}]}
|
||||
@ -0,0 +1 @@
|
||||
{"title":"multiple-exported-component","docUrl":"","screenshot":"","npm":{"package":"multiple-exported-component","version":"1.0.0","exportName":"","main":"/Users/gengyang/code/frontend/low-code/ali-lowcode-engine/packages/material-parser/test/fixtures/multiple-exported-component/es/index.js","destructuring":false,"subName":""},"props":[]}
|
||||
@ -0,0 +1,11 @@
|
||||
import component from './index.js';
|
||||
import amManifest from './amManifest.js';
|
||||
|
||||
const getComponent = function () {
|
||||
return Promise.resolve(component);
|
||||
};
|
||||
|
||||
export default {
|
||||
getComponent,
|
||||
manifest: amManifest,
|
||||
};
|
||||
@ -0,0 +1,102 @@
|
||||
/**
|
||||
* The template of manifest for AiMake studio.
|
||||
*/
|
||||
const manifest = {
|
||||
// The name of current component.
|
||||
name: 'AIMakePlaceholder',
|
||||
// The description of current component.
|
||||
description: '占位图',
|
||||
// The coverimage's url of current component.
|
||||
coverImage: 'https://img.alicdn.com/tfs/TB1RxDup3mTBuNjy1XbXXaMrVXa-362-120.png',
|
||||
// The category of current component in AiMake studio.
|
||||
// can be:
|
||||
// `分栏` or `文本` or `按钮` or `标签` or `标签页` or `表格` or `单选` or `分割线`
|
||||
// or `分页` or `复选` or `滑动条` or `进度条` or `卡片` or `开关` or `缺省状态`
|
||||
// or `日期选择` or `输入框` or `搜索框` or `图表` or `图片` or `下拉选择` or `表单行`
|
||||
// or `树控件` or `折叠面板` or `占位图`
|
||||
category: '占位图',
|
||||
// The preview list of current component in AiMake studio.
|
||||
// Each preset contains following keys:
|
||||
// - `alias`: string. required. The previewing component's name to display
|
||||
// - `thumbnail`: string. not required. The previewing component's thumbnail
|
||||
// - `customProps`: object. not required.
|
||||
// The previewing component's customize props, e.g. { [propName]: [propValue] }
|
||||
// - `colSpan`: number. not required. default 24 (1~24). The previewing component's size when to display
|
||||
presets: [{
|
||||
alias: '占位图',
|
||||
thumbnail: 'https://img.alicdn.com/tfs/TB1RxDup3mTBuNjy1XbXXaMrVXa-362-120.png',
|
||||
colSpan: 24,
|
||||
customProps: {
|
||||
width: '224px',
|
||||
height: '126px',
|
||||
backgroundColor: '#FFF6E0',
|
||||
textAlign: 'center',
|
||||
border: '1px dashed rgb(170, 170, 170)',
|
||||
children: '暂不支持此组件',
|
||||
},
|
||||
}],
|
||||
// Other settings of current component for AiMake studio.
|
||||
settings: {
|
||||
// The render type of current component in AiMake studio.
|
||||
// can be:
|
||||
// `element_inline` or `element_block` or `container`
|
||||
type: 'element_inline',
|
||||
// The insert mode of current component in AiMake studio.
|
||||
// can be:
|
||||
// one or combine of `t` and `b` and `r` and `l`
|
||||
insertionModes: 'lr',
|
||||
// The handle list of current component in AiMake studio.
|
||||
// can be:
|
||||
// an array contains one and more of ['cut', 'copy', 'paste', 'delete', 'duplicate']
|
||||
handles: ['cut', 'copy', 'paste', 'delete', 'duplicate'],
|
||||
// Whether the component can be actived.
|
||||
shouldActive: true,
|
||||
// Whether the component can be dragged.
|
||||
shouldDrag: true,
|
||||
// The props of current component in AiMake studio.
|
||||
// Each property contains following keys:
|
||||
// - `name`: string. required. The property's name
|
||||
// - `label`: string. required. The property's name to display
|
||||
// - `renderer`: string. required. The property's editor. can be: (@冰骊)
|
||||
// - `defaultValue`: any. not required. The property's default value
|
||||
// - `params`: any. not required. The parameters for property's editor
|
||||
// - `placeholder`: string. not required. The placeholder for property's editor
|
||||
// - `hint`: string. not required. The hint for property's editor
|
||||
props: [{
|
||||
name: 'margin',
|
||||
label: '外边距',
|
||||
renderer: 'Quadrant',
|
||||
}, {
|
||||
name: 'width',
|
||||
label: '宽度',
|
||||
defaultValue: '224px',
|
||||
renderer: 'Width',
|
||||
}, {
|
||||
name: 'height',
|
||||
label: '高度',
|
||||
defaultValue: '126px',
|
||||
renderer: 'Height',
|
||||
}, {
|
||||
name: 'backgroundColor',
|
||||
label: '背景色',
|
||||
defaultValue: '#FFF6E0',
|
||||
renderer: false,
|
||||
}, {
|
||||
name: 'textAlign',
|
||||
label: '对齐',
|
||||
defaultValue: 'center',
|
||||
renderer: false,
|
||||
}, {
|
||||
name: 'border',
|
||||
label: '边框',
|
||||
defaultValue: '1px dashed rgb(170, 170, 170)',
|
||||
renderer: false,
|
||||
}, {
|
||||
name: 'children',
|
||||
label: '内容',
|
||||
defaultValue: '暂不支持此组件',
|
||||
renderer: false,
|
||||
}],
|
||||
},
|
||||
};
|
||||
export default manifest;
|
||||
@ -0,0 +1,6 @@
|
||||
|
||||
import AIMakePlaceholder from './index.js';
|
||||
import manifest from './manifest.js';
|
||||
|
||||
export default { origin: AIMakePlaceholder, manifest };
|
||||
|
||||
@ -0,0 +1,65 @@
|
||||
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
||||
import _createClass from "@babel/runtime/helpers/createClass";
|
||||
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
|
||||
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
|
||||
import _inherits from "@babel/runtime/helpers/inherits";
|
||||
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import HOCBoxModelProps from '../utils/HOCBoxModelProps';
|
||||
import HOCLayoutProps from '../utils/HOCLayoutProps';
|
||||
|
||||
const AIMakePlaceholder =
|
||||
/* #__PURE__ */
|
||||
function (_Component) {
|
||||
_inherits(AIMakePlaceholder, _Component);
|
||||
|
||||
function AIMakePlaceholder() {
|
||||
_classCallCheck(this, AIMakePlaceholder);
|
||||
|
||||
return _possibleConstructorReturn(this, _getPrototypeOf(AIMakePlaceholder).apply(this, arguments));
|
||||
}
|
||||
|
||||
_createClass(AIMakePlaceholder, [{
|
||||
key: "render",
|
||||
value: function render() {
|
||||
const _this$props = this.props;
|
||||
const children = _this$props.children;
|
||||
const styleBoxModel = _this$props.styleBoxModel;
|
||||
const styleLayout = _this$props.styleLayout;
|
||||
const style = _this$props.style;
|
||||
const styles = { ...styleBoxModel,
|
||||
...styleLayout,
|
||||
...style,
|
||||
};
|
||||
const placeholderStyle = {
|
||||
display: 'inline-block',
|
||||
border: '1px dashed #aaa',
|
||||
lineHeight: styles.height,
|
||||
backgroundColor: '#F5E075',
|
||||
overflow: 'hidden',
|
||||
textAlign: 'center',
|
||||
...styles,
|
||||
};
|
||||
return React.createElement("div", {
|
||||
style: placeholderStyle,
|
||||
}, children);
|
||||
},
|
||||
}]);
|
||||
|
||||
return AIMakePlaceholder;
|
||||
}(Component);
|
||||
|
||||
_defineProperty(AIMakePlaceholder, "propTypes", {
|
||||
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
|
||||
styleBoxModel: PropTypes.object.isRequired,
|
||||
styleLayout: PropTypes.object.isRequired,
|
||||
style: PropTypes.object,
|
||||
});
|
||||
|
||||
_defineProperty(AIMakePlaceholder, "defaultProps", {
|
||||
children: '',
|
||||
style: {},
|
||||
});
|
||||
|
||||
export default HOCBoxModelProps(HOCLayoutProps(AIMakePlaceholder));
|
||||
@ -0,0 +1 @@
|
||||
{"componentName":"AIMakePlaceholder","title":"","docUrl":"","screenshot":"","npm":{"package":"@ali/lowcode-engine-material-parser","version":"0.1.0","exportName":"AIMakePlaceholder","main":"/Users/gengyang/code/frontend/low-code/ali-lowcode-engine/packages/material-parser/test/fixtures/multiple-exported-component/es/index.js","destructuring":false,"subName":""},"props":[{"name":"children","propType":"oneOfType","description":""},{"name":"styleBoxModel","propType":"object","description":""},{"name":"styleLayout","propType":"object","description":""},{"name":"style","propType":"object","description":"","defaultValue":"{}"}]}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user