feat: 增加 material-parser 包

This commit is contained in:
gengyang.gy 2021-12-29 20:52:36 +08:00 committed by lihao.ylh
parent a52fcafc45
commit a802418d65
210 changed files with 19452 additions and 0 deletions

View 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

View 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

View File

@ -0,0 +1,5 @@
{
"plugins": [
"@ali/lowcode-test-mate/plugin/index.ts"
]
}

View 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;

View 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));
})();

View 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/**',
],
};

View 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"
}
}

View 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"
}
}
}
}
}

View 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

View 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);
}
})();

View 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();

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View File

@ -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;

View 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);
};

View File

@ -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);
}
}

View 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;

View File

@ -0,0 +1,3 @@
export default function preProcessHandler(documentation: any, path: any) {
documentation.set('meta', path.__meta);
}

View File

@ -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');

View File

@ -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);
}

View 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;
}

View File

@ -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'
);
}

View File

@ -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;

View 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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View 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;
}

View File

@ -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;
}

View 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;
}

View File

@ -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;
}

View 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);
}

View 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);
}

View File

@ -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;
}

View File

@ -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;
}

View 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;
}

View File

@ -0,0 +1,7 @@
export default function getRoot(path: any) {
let root = path.parent;
while (root.parent) {
root = root.parent;
}
return root;
}

View 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;

View 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;
}

View 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,
};
}

View 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;
}

View File

@ -0,0 +1,11 @@
{
"compilerOptions": {
"jsx": "react",
"target": "es6",
"module": "commonjs",
"allowSyntheticDefaultImports": true,
"allowJs": true
},
"include": ["**/*"],
"exclude": [""]
}

View 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;
}

View 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;

View File

@ -0,0 +1,9 @@
/**
*
*/
export enum ChannelType {
/** 本地 */
LOCAL = 'local',
/** 在线 */
ONLINE = 'online',
}

View File

@ -0,0 +1,4 @@
/**
* DSL类型
*/
export type DSLType = 'react' | 'rax';

View File

@ -0,0 +1,14 @@
import { ComponentMeta } from '../core';
/**
*
* @interface IAccesser
*/
export interface IAccesser {
/**
*
* @returns {Promise<IMaterialinSchema>}
* @memberof IAccesser
*/
access(): Promise<ComponentMeta[]>;
}

View File

@ -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 文件对象
}>;

View 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;
};
}

View 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;
}

View File

@ -0,0 +1,117 @@
import { Expand } from './Basic';
import { DSLType } from './DSLType';
/**
*
* @interface IMaterializeCommonOptions
*/
export interface IMaterializeCommonOptions {
/**
* accesser=online 使 npm clienttnpmcnpmyarnnpm
*/
npmClient?: string;
/**
* dsl类型'react' | 'rax'
*/
dslType?: DSLType;
}
/**
*
* @interface IMaterializeOnlineOptions
*/
export interface IMaterializeLocalOptions extends IMaterializeCommonOptions {
/**
*
* localonline线 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 {
/**
*
* localonline线 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;
}
>;

View 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;
}

View 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';

View 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;
}

View 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;
}

View 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"
}
}
}
}
}

View File

@ -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",
},
]
`;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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();
});

View 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;

View File

@ -0,0 +1,5 @@
{
"name": "dts-component",
"version": "1.2.3",
"main": "./src/index.jsx"
}

File diff suppressed because it is too large Load Diff

View 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;

View 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>&nbsp;<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 };

View 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;
}
}

View 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;

View File

@ -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,
};

View File

@ -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;

View File

@ -0,0 +1,6 @@
import AIMakeBlank from './index.js';
import manifest from './manifest.js';
export default { origin: AIMakeBlank, manifest };

View 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))));

View File

@ -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":""}]}

View File

@ -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":[]}

View File

@ -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;
}

View File

@ -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,
};

View File

@ -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;

View File

@ -0,0 +1,6 @@
import AIMakeIcon from './index.js';
import manifest from './manifest.js';
export default { origin: AIMakeIcon, manifest };

View 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;

View File

@ -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":"{}"}]}

View File

@ -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":[]}

View File

@ -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,
};

View File

@ -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;

View File

@ -0,0 +1,6 @@
import AIMakeImage from './index.js';
import manifest from './manifest.js';
export default { origin: AIMakeImage, manifest };

View 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);

View File

@ -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":"{}"}]}

View File

@ -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":[]}

View File

@ -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,
};

View 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;

View File

@ -0,0 +1,6 @@
import AIMakeLink from './index.js';
import manifest from './manifest.js';
export default { origin: AIMakeLink, manifest };

View 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))));

View File

@ -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":"{}"}]}

View File

@ -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":[]}

View File

@ -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,
};

View File

@ -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;

View File

@ -0,0 +1,6 @@
import AIMakePlaceholder from './index.js';
import manifest from './manifest.js';
export default { origin: AIMakePlaceholder, manifest };

View File

@ -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));

View File

@ -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