├── __test__ ├── fixtures │ ├── defaultTypeMap.ts │ ├── extends │ │ ├── b.ts │ │ ├── a.ts │ │ └── interface.ts │ ├── escape.ts │ ├── basic.ts │ ├── propertySorter.ts │ ├── nest │ │ ├── components │ │ │ └── Blink │ │ │ │ └── interface.ts │ │ └── interface.ts │ └── function.ts ├── index.test.ts └── __snapshots__ │ └── index.test.ts.snap ├── jest.config.js ├── src ├── index.ts ├── util.ts ├── default.ts ├── interface.ts ├── generateMarkdown.ts └── generate.ts ├── examples ├── interface │ └── b.tsx ├── generateMarkdown.js ├── generate.js ├── readme.tsx ├── a.tsx └── demo.tsx ├── .prettierrc ├── tsconfig.json ├── .eslintrc.js ├── LICENSE ├── package.json ├── .gitignore └── README.md /__test__/fixtures/defaultTypeMap.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @title A 3 | */ 4 | export interface AProps { 5 | style: object; 6 | className: string; 7 | } 8 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: "ts-jest", 3 | testEnvironment: "node", 4 | collectCoverage: true, 5 | collectCoverageFrom: ["src/*.ts"], 6 | }; 7 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as generate } from './generate'; 2 | export { default as generateMarkdown } from './generateMarkdown'; 3 | export { Project } from 'ts-morph'; -------------------------------------------------------------------------------- /examples/interface/b.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @title B 3 | */ 4 | export interface BProps { 5 | /** 6 | * @zh 小尺寸 7 | * @en small size 8 | */ 9 | small?: boolean; 10 | } 11 | -------------------------------------------------------------------------------- /__test__/fixtures/extends/b.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @title B 3 | */ 4 | export interface BProps { 5 | /** 6 | * @zh 小尺寸 7 | * @en small size 8 | */ 9 | small?: boolean; 10 | } 11 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "semi": true, 4 | "singleQuote": true, 5 | "jsxBracketSameLine": false, 6 | "jsxSingleQuote": false, 7 | "printWidth": 100, 8 | "useTabs": false, 9 | "tabWidth": 2, 10 | "trailingComma": "es5" 11 | } 12 | -------------------------------------------------------------------------------- /examples/generateMarkdown.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const { generateMarkdown } = require("../lib"); 3 | 4 | const md = generateMarkdown(path.resolve(__dirname, "readme.tsx"), { 5 | sourceFilesPaths: ["**/*.ts", "**/*.tsx"], 6 | }); 7 | 8 | console.log(JSON.stringify(md, null, 2)); 9 | -------------------------------------------------------------------------------- /examples/generate.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { generateMarkdown } = require('../lib'); 3 | 4 | const schema = generateMarkdown(path.resolve(__dirname, 'a.tsx'), { 5 | sourceFilesPaths: ['**/*.ts', '**/*.tsx'], 6 | strictOrder: true, 7 | }); 8 | 9 | console.log(schema); 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "lib": ["es5", "es6"], 5 | "target": "es5", 6 | "noImplicitAny": false, 7 | "strict": true, 8 | "outDir": "lib", 9 | "jsx": "react", 10 | "declaration": true, 11 | "typeRoots": ["node_modules/@types"], 12 | "downlevelIteration": true 13 | }, 14 | "include": ["src/**/*.ts"] 15 | } 16 | -------------------------------------------------------------------------------- /__test__/fixtures/escape.ts: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react'; 2 | 3 | /** 4 | * @title A 5 | * 6 | * @zh 这是接口描述。 7 | * 8 | * @en This is interface description. 9 | */ 10 | export interface AProps { 11 | /** 12 | * @zh 尺寸 13 | * @en size 14 | */ 15 | size?: 'mini' | 'large' | 'default'; 16 | /** 17 | * @zh 获取数据函数 18 | * @en Function to fetch data 19 | */ 20 | fetchData?: () => Promise; 21 | } 22 | 23 | 24 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2021: true, 5 | node: true, 6 | }, 7 | extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 8 | parser: "@typescript-eslint/parser", 9 | parserOptions: { 10 | ecmaVersion: 12, 11 | sourceType: "module", 12 | }, 13 | plugins: ["@typescript-eslint"], 14 | rules: { 15 | "@typescript-eslint/ban-types": 0, 16 | "@typescript-eslint/no-var-requires": 0, 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /__test__/fixtures/extends/a.ts: -------------------------------------------------------------------------------- 1 | import { BProps } from "./b"; 2 | /** 3 | * @title A 4 | */ 5 | export interface AProps extends BProps { 6 | /** 7 | * @zh 是否禁用 8 | * @en Whether to disable 9 | */ 10 | disabled?: boolean; 11 | /** 12 | * @zh 动画 13 | * @en animation 14 | */ 15 | animation?: boolean | string; 16 | } 17 | 18 | export interface CProps { 19 | /** 20 | * @zh BB 21 | * @en bb 22 | */ 23 | bb?: boolean; 24 | } 25 | 26 | export type Option = { 27 | a: string; 28 | b: string; 29 | }; 30 | -------------------------------------------------------------------------------- /__test__/fixtures/basic.ts: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react'; 2 | 3 | /** 4 | * @title A 5 | * 6 | * @zh 7 | * 8 | * 这是接口描述。 9 | * 10 | * @en 11 | * 12 | * This is interface description. 13 | */ 14 | export interface AProps { 15 | /** 16 | * @zh 自定义操作项 17 | * @en this is action 18 | * @version 2.15.0 19 | */ 20 | action?: ReactNode; 21 | /** 22 | * @zh 是否可以关闭 23 | * @en Whether Alert can be closed 24 | * @defaultValue {} 25 | */ 26 | closable?: InnerProps; 27 | } 28 | 29 | export interface InnerProps { 30 | /** 31 | * @zh 位置 32 | * @en position 33 | */ 34 | position?: string; 35 | /** 36 | * @zh 尺寸 37 | * @en Size 38 | */ 39 | size?: string; 40 | } 41 | -------------------------------------------------------------------------------- /__test__/fixtures/propertySorter.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @title Button 3 | * @zh 按钮 4 | * @en Button 5 | */ 6 | type ButtonType = { 7 | /** 8 | * @zh 自定义文本渲染 9 | * @en Custom text render 10 | */ 11 | renderText?: () => string; 12 | /** 13 | * @zh 尺寸 14 | * @en Size 15 | * @defaultValue default 16 | */ 17 | size?: 'mini' | 'large' | 'default'; 18 | /** 19 | * @zh TabIndex 20 | * @en TabIndex 21 | */ 22 | tabIndex?: number; 23 | /** 24 | * @zh 只读 25 | * @en Readonly 26 | */ 27 | readonly?: boolean; 28 | /** 29 | * @zh 颜色 30 | * @en Color 31 | * @version 1.2.0 32 | */ 33 | color?: string; 34 | /** 35 | * @zh 禁用 36 | * @en Disabled 37 | */ 38 | disabled?: boolean; 39 | }; 40 | -------------------------------------------------------------------------------- /examples/readme.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | /** 4 | * @title Alert 5 | * 6 | * @zh 7 | * 8 | * 向用户显示警告的信息时,通过警告提示,展现需要关注的信息。 9 | * 10 | * @en 11 | * 12 | * Display warning information to the user. the Alert is used to display the information that needs attention. 13 | */ 14 | export interface AlertProps { 15 | /** 16 | * @zh 自定义操作项 17 | * @en this is action 18 | * @version 2.15.0 19 | */ 20 | action?: ReactNode; 21 | /** 22 | * @zh 是否可以关闭 23 | * @en Whether Alert can be closed 24 | * @defaultValue false 25 | */ 26 | closable?: InnerProps; 27 | } 28 | 29 | interface InnerProps { 30 | /** 31 | * @zh 位置 32 | * @en position 33 | */ 34 | position?: string; 35 | /** 36 | * @zh 尺寸 37 | * @en Size 38 | */ 39 | size?: string; 40 | } 41 | -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | export function toSingleLine(str = '', escapeChars = true): string { 2 | if (!str) { 3 | return ''; 4 | } 5 | let newStr = str.trim() 6 | .replace(/[\r\n\t]+/g, '') 7 | .replace(/[\x20]{2,}/g, '') 8 | if (escapeChars) { 9 | newStr = escape( 10 | newStr.replace(/\|/g, '\\|') 11 | ); 12 | } 13 | return newStr; 14 | } 15 | 16 | /** 17 | * Markdown will treat html tag like text as real tag when converted to html so we need to escape them 18 | * `` => should be escaped 19 | * `< void >` => don't need to be escaped 20 | * `() => void` => don't need to be escaped 21 | */ 22 | export function escape(str: string): string { 23 | if (!str || !/<[a-zA-Z]+[^>]*>/.test(str)) { 24 | return str; 25 | } 26 | return str.replace(//g, '>'); 27 | } 28 | -------------------------------------------------------------------------------- /__test__/fixtures/nest/components/Blink/interface.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @title Blink 3 | * 4 | * @zh 这是 Blink 接口描述。 5 | * @en This is interface description of Blink. 6 | * 7 | */ 8 | export interface BlinkProps { 9 | /** 10 | * @zh 数字 11 | * @en numbers 12 | */ 13 | numbers: number[]; 14 | /** 15 | * @zh 是否设置 flex 布局属性 16 | * @en Whether to set flex layout properties 17 | */ 18 | flex: boolean; 19 | /** 20 | * @zh 排序属性 21 | * @en sorter 22 | */ 23 | sorter: SorterProps; 24 | _privatePropThatShouldBeIgnored: ShouldBeIgnoredProps; 25 | } 26 | 27 | /** 28 | * @title Clink 29 | * 30 | * @zh 这是 Clink 接口描述。 31 | * @en This is interface description of Clink. 32 | * 33 | */ 34 | export interface ClinkProps { 35 | /** 36 | * @zh 排序属性 37 | * @en sorter 38 | */ 39 | sorter: SorterProps; 40 | } 41 | 42 | interface SorterProps { 43 | counter: number; 44 | } 45 | 46 | export interface ShouldBeIgnoredProps { 47 | fail: boolean; 48 | } 49 | -------------------------------------------------------------------------------- /__test__/fixtures/function.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @title Plus 3 | * @zh 两数相乘 4 | * @en Multiply two numbers 5 | * @returns Product of two numbers 6 | */ 7 | type Plus = ( 8 | /** 9 | * @en First number 10 | * @zh 被乘数 11 | */ 12 | a: number, 13 | /** 14 | * @en Second number 15 | * @zh 乘数 16 | * @defaultValue 1 17 | */ 18 | b: number 19 | ) => number; 20 | 21 | /** 22 | * @title Add 23 | * @en Add two numbers 24 | * @zh 两数相加 25 | * @returns Sum of two numbers 26 | * @version 1.0.0 27 | */ 28 | function Add( 29 | /** 30 | * @zh 被加数 31 | * @en First number 32 | */ 33 | a: number, 34 | /** 35 | * @zh 加数 36 | * @en Second number 37 | */ 38 | b = 5 39 | ) { 40 | return a + b; 41 | } 42 | 43 | type ButtonType = { 44 | size?: 'mini' | 'large' | 'default'; 45 | color?: string; 46 | }; 47 | 48 | /** 49 | * @title function with nest type 50 | */ 51 | function withNestType(buttonProps: Partial) { 52 | return null; 53 | } 54 | -------------------------------------------------------------------------------- /examples/a.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @title Plus 3 | * @zh 两数相乘 4 | * @en Multiply two numbers 5 | * @returns Product of two numbers 6 | */ 7 | type Plus = ( 8 | /** 9 | * @en First number 10 | * @zh 被乘数 11 | */ 12 | a: number, 13 | /** 14 | * @en Second number 15 | * @zh 乘数 16 | * @defaultValue 1 17 | */ 18 | b: number 19 | ) => number; 20 | 21 | /** 22 | * @title Add 23 | * @en Add two numbers 24 | * @zh 两数相加 25 | * @returns Sum of two numbers 26 | * @version 1.0.0 27 | */ 28 | function Add( 29 | /** 30 | * @zh 被加数 31 | * @en First number 32 | */ 33 | a: number, 34 | /** 35 | * @zh 加数 36 | * @en Second number 37 | */ 38 | b = 5 39 | ) { 40 | return a + b; 41 | } 42 | 43 | /** 44 | * @title Button 45 | * @zh 按钮 46 | * @en Button 47 | */ 48 | type ButtonType = { 49 | /** 50 | * @zh 尺寸 51 | * @en Size 52 | * @defaultValue default 53 | */ 54 | size?: 'mini' | 'large' | 'default'; 55 | /** 56 | * @zh 颜色 57 | * @en Color 58 | * @version 1.2.0 59 | */ 60 | color?: string; 61 | }; 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 PengJiyuan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/demo.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode, ReactElement } from "react"; 2 | import { AProps, CProps as DProps, Option } from "./a"; 3 | 4 | type ExtendType = Pick, 'animation' | 'bb'>; 5 | 6 | /** 7 | * @title Alert 8 | * @zh 9 | * 向用户显示警告的信息时,通过警告提示,展现需要关注的信息。 10 | * @en 11 | * Display warning information to the user. the Alert is used to display the information that needs attention. 12 | */ 13 | export interface AlertProps extends ExtendType { 14 | children: ReactNode; 15 | /** 16 | * @zh 自定义操作项 17 | * @en this is action 18 | * @version 2.15.0 19 | */ 20 | action: ReactElement 21 | /** 22 | * @zh 是否可以关闭 23 | * @en Whether Alert can be closed 24 | * @defaultValue false 25 | */ 26 | closable?: { 27 | a: boolean; 28 | b: string; 29 | }; 30 | /** 31 | * @zh 回调参数 32 | * @en Callback function 33 | */ 34 | callback?: (option: Option) => void; 35 | } 36 | 37 | interface InnerProps { 38 | /** 39 | * @zh 位置 40 | * @en position 41 | */ 42 | position?: string; 43 | /** 44 | * @zh 尺寸 45 | * @en Size 46 | */ 47 | size?: string; 48 | } 49 | 50 | /** 51 | * @title Test 52 | */ 53 | export type TestType = InnerProps & AlertProps; 54 | -------------------------------------------------------------------------------- /__test__/fixtures/extends/interface.ts: -------------------------------------------------------------------------------- 1 | import { ReactNode, ReactElement } from 'react'; 2 | import { AProps, CProps as DProps, Option } from './a'; 3 | 4 | type ExtendType = Pick, 'animation' | 'bb'>; 5 | 6 | /** 7 | * @title Alert 8 | * @zh 9 | * 向用户显示警告的信息时,通过警告提示,展现需要关注的信息。 10 | * @en 11 | * Display warning information to the user. the Alert is used to display the information that needs attention. 12 | */ 13 | export interface AlertProps extends ExtendType { 14 | children: ReactNode; 15 | /** 16 | * @zh 自定义操作项 17 | * @en this is action 18 | * @version 2.15.0 19 | */ 20 | action: ReactElement; 21 | /** 22 | * @zh 是否可以关闭 23 | * @en Whether Alert can be closed 24 | * @defaultValue false 25 | */ 26 | closable?: { 27 | a: boolean; 28 | b: string; 29 | }; 30 | /** 31 | * @zh 回调参数 32 | * @en Callback function 33 | */ 34 | callback?: (option: Option) => void; 35 | } 36 | 37 | interface InnerProps { 38 | /** 39 | * @zh 位置 40 | * @en position 41 | */ 42 | position?: string; 43 | /** 44 | * @zh 尺寸 45 | * @en Size 46 | */ 47 | size?: string; 48 | } 49 | 50 | /** 51 | * @title Test 52 | */ 53 | export type TestType = InnerProps & AlertProps; 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ts-document", 3 | "version": "0.8.0", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "types": "lib/index.d.ts", 7 | "scripts": { 8 | "prepublishOnly": "npm run test && npm run tsc", 9 | "tsc": "rm -rf lib/ && tsc", 10 | "tsc:watch": "tsc -w", 11 | "lint": "eslint src/ --fix --ext .ts,.tsx", 12 | "test": "jest" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/PengJiyuan/ts-document.git" 17 | }, 18 | "keywords": [ 19 | "docgen", 20 | "react", 21 | "typescript" 22 | ], 23 | "author": "PengJiyuan", 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/PengJiyuan/ts-document/issues" 27 | }, 28 | "homepage": "https://github.com/PengJiyuan/ts-document#readme", 29 | "peerDependencies": { 30 | "typescript": ">= 4.3.x" 31 | }, 32 | "devDependencies": { 33 | "@types/jest": "^27.4.1", 34 | "@types/node": "^15.12.5", 35 | "@types/react": "^17.0.11", 36 | "@typescript-eslint/eslint-plugin": "^4.28.2", 37 | "@typescript-eslint/parser": "^4.28.2", 38 | "eslint": "^7.30.0", 39 | "jest": "^27.0.6", 40 | "prettier": "^2.0.5", 41 | "react": "^17.0.2", 42 | "ts-jest": "^27.0.3", 43 | "typescript": "^4.3.5" 44 | }, 45 | "dependencies": { 46 | "ts-morph": "^11.0.0" 47 | }, 48 | "files": [ 49 | "lib" 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /src/default.ts: -------------------------------------------------------------------------------- 1 | import { MarkdownTableType, LinkFormatterParam } from './interface'; 2 | 3 | export const defaultLang = 'en'; 4 | 5 | export const defaultTypeMap = { 6 | className: { 7 | type: 'string \\| string[]', 8 | tags: [ 9 | { 10 | name: 'zh', 11 | value: '节点类名', 12 | }, 13 | { 14 | name: 'en', 15 | value: 'Additional css class', 16 | }, 17 | ], 18 | }, 19 | style: { 20 | type: 'CSSProperties', 21 | tags: [ 22 | { 23 | name: 'zh', 24 | value: '节点样式', 25 | }, 26 | { 27 | name: 'en', 28 | value: 'Additional style', 29 | }, 30 | ], 31 | }, 32 | }; 33 | 34 | export const defaultLinkFormatter = ({ typeName }: LinkFormatterParam): string => `#${typeName}`; 35 | 36 | export const defaultMarkdownTableSchema: Record< 37 | string, 38 | Array<{ 39 | title: string | Record; 40 | value: string | Record; 41 | }> 42 | > = { 43 | zh: [ 44 | { 45 | title: '参数名', 46 | value: 'name', 47 | }, 48 | { 49 | title: '描述', 50 | value: 'tag.zh', 51 | }, 52 | { 53 | title: '类型', 54 | value: 'type', 55 | }, 56 | { 57 | title: '默认值', 58 | value: { 59 | interface: 'tag.defaultValue', 60 | parameter: 'initializerText', 61 | }, 62 | }, 63 | { 64 | title: '版本', 65 | value: 'tag.version', 66 | }, 67 | ], 68 | en: [ 69 | { 70 | title: { 71 | interface: 'Property', 72 | parameter: 'Argument', 73 | }, 74 | value: 'name', 75 | }, 76 | { 77 | title: 'Description', 78 | value: 'tag.en', 79 | }, 80 | { 81 | title: 'Type', 82 | value: 'type', 83 | }, 84 | { 85 | title: 'DefaultValue', 86 | value: { 87 | interface: 'tag.defaultValue', 88 | parameter: 'initializerText', 89 | }, 90 | }, 91 | { 92 | title: 'Version', 93 | value: 'tag.version', 94 | }, 95 | ], 96 | }; 97 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | # IDE config 107 | .idea 108 | 109 | # dist files 110 | lib/ 111 | 112 | .idea 113 | -------------------------------------------------------------------------------- /__test__/fixtures/nest/interface.ts: -------------------------------------------------------------------------------- 1 | import { ReactNode, PropsWithChildren } from 'react'; 2 | import { BlinkProps, ClinkProps, ShouldBeIgnoredProps } from './components/Blink/interface'; 3 | 4 | export interface OptionInfo extends PropsWithChildren { 5 | valid: boolean; 6 | index: number; 7 | origin: 'children' | 'options' | 'userCreatedOptions' | 'userCreatingOption'; 8 | } 9 | 10 | /** 11 | * @title A 12 | * 13 | * @zh 这是接口描述。 14 | * @en This is interface description. 15 | * 16 | */ 17 | export interface AProps { 18 | /** 19 | * @zh 树选择数据属性 20 | * @en treeSelectDataType 21 | */ 22 | treeSelectDataType?: TreeSelectDataType; 23 | /** 24 | * @zh 点击回调 25 | * @en onClick 26 | */ 27 | onClick: (num: number) => void; 28 | /** 29 | * @zh 属性 B 30 | * @en BProps 31 | */ 32 | b: BlinkProps; 33 | /** 34 | * @zh 属性 baseB 35 | * @en baseBlink 36 | */ 37 | unionBlink: UnionBlinkProps; 38 | /** 39 | * @zh 操作项 40 | * @en action 41 | */ 42 | action?: ReactNode; 43 | /** 44 | * @zh 负责人 45 | * @en owner 46 | */ 47 | owner?: Partial; 48 | /** 49 | * @zh 尺寸 50 | * @en size 51 | */ 52 | size?: Size; 53 | /** 54 | * @zh 列配置 55 | * @en column settings 56 | * @defaultValue {} 57 | */ 58 | columns?: ColProps; 59 | /** 60 | * @zh 排序 61 | * @en sorter 62 | */ 63 | sorter: (a: SorterProps, b: SorterProps) => Promise; 64 | /** 65 | * @zh 方向 66 | * @en direction 67 | */ 68 | direction?: Direction; 69 | /** 70 | * @zh 选项 71 | * @en option 72 | */ 73 | option: OptionInfo; 74 | shouldBeIgnoreProps: ShouldBeIgnoredProps; 75 | } 76 | 77 | export interface ColProps { 78 | // width of the column 79 | width?: number; 80 | name?: string; 81 | onColumnCallback: (param: onColumnCallbackProps) => number; 82 | } 83 | 84 | type SizeObject = { [key in string]: number }; 85 | export type Size = number | string | SizeObject; 86 | 87 | enum Direction { 88 | LEFT, 89 | RIGHT, 90 | UP, 91 | DOWN, 92 | } 93 | 94 | interface SorterProps { 95 | counter: number; 96 | } 97 | 98 | interface onColumnCallbackProps { 99 | // index of column 100 | index: string; 101 | position: { x: number; y: number }; 102 | } 103 | 104 | interface Owner { 105 | name: string; 106 | age: number; 107 | } 108 | 109 | export type TreeSelectDataType = TreeDataType & { 110 | getInnerMethods: (inner: boolean) => InnerMethodsReturnType; 111 | }; 112 | 113 | export type TreeDataType = { 114 | key?: string; 115 | _index?: number; 116 | children: TreeDataType[]; 117 | loadMore: (data: TreeDataType) => void; 118 | }; 119 | 120 | interface InnerMethodsReturnType { 121 | text: string; 122 | } 123 | 124 | type UnionBlinkProps = ClinkProps & BlinkProps; 125 | -------------------------------------------------------------------------------- /src/interface.ts: -------------------------------------------------------------------------------- 1 | import { Project } from 'ts-morph'; 2 | 3 | // K-V pair parsed from jsDoc 4 | export type TagType = { 5 | name: string; 6 | value: string; 7 | }; 8 | 9 | // Schema parsed from Symbol 10 | export type PropertyType = { 11 | name: string; 12 | type: string; 13 | isOptional: boolean; 14 | tags: TagType[]; 15 | }; 16 | 17 | // Schema parsed from function declaration 18 | export type FunctionSchema = { 19 | tags: TagType[]; 20 | params: Array; 21 | returns: string; 22 | }; 23 | 24 | // Schema parsed from interface declaration 25 | export type InterfaceSchema = { 26 | tags: TagType[]; 27 | data: PropertyType[]; 28 | }; 29 | 30 | // Schema parsed from nested type declaration 31 | export type NestedTypeSchema = { 32 | tags: TagType[]; 33 | data: string; 34 | isNestedType: true; 35 | }; 36 | 37 | export interface LinkFormatterParam { 38 | typeName: string; 39 | jsDocTitle?: string; 40 | fullPath?: string; 41 | } 42 | 43 | export type LinkFormatter = (param: LinkFormatterParam) => string | undefined; 44 | 45 | export type SchemaList = Array<{ title: string; schema: Schema }>; 46 | 47 | // Collect of all schema type generated 48 | export type Schema = FunctionSchema | InterfaceSchema | NestedTypeSchema; 49 | 50 | // Table type in markdown generated 51 | export type MarkdownTableType = 'interface' | 'parameter'; 52 | 53 | export type DefaultTypeMapT = Record; 54 | 55 | export type GenerateConfig = { 56 | defaultTypeMap?: DefaultTypeMapT; 57 | sourceFilesPaths: string | string[]; 58 | /** 59 | * Whether to skip parsing documentation comment as property description 60 | */ 61 | strictComment?: boolean; 62 | /** 63 | * Generate schema in the order they appear in the document 64 | * When it's true, generate function will return Array<{ title: string; schema: Schema }> 65 | */ 66 | strictDeclarationOrder?: boolean; 67 | /** 68 | * The compare function to sort properties/arguments of schema 69 | */ 70 | propertySorter?: (a: PropertyType, b: typeof a) => number; 71 | /** 72 | * Custom project to use in generate function 73 | */ 74 | project?: Project; 75 | /** 76 | * Format function to generate link of the nested type 77 | */ 78 | linkFormatter?: LinkFormatter; 79 | /** 80 | * When parsing nested types, whether to ignore these nested types if they are defined in some files 81 | * When returning true, nested types must not be exported, but when false is returned, nested types may not be exported due to other reasons (such as the nested type has jsdoc @title, which needs to be manually exported) 82 | */ 83 | ignoreNestedType?: (definitionFilePath: string) => boolean; 84 | /* 85 | * Whether to escape characters for extracted type text. 86 | * E.g. `|` will be escaped to `\|`, `` will be escaped to `<Promise>`. 87 | */ 88 | escapeChars?: boolean; 89 | }; 90 | 91 | export type GenerateMarkdownConfig = GenerateConfig & { 92 | lang?: string; 93 | }; 94 | -------------------------------------------------------------------------------- /src/generateMarkdown.ts: -------------------------------------------------------------------------------- 1 | import generate from './generate'; 2 | import { defaultMarkdownTableSchema, defaultLang } from './default'; 3 | import { 4 | MarkdownTableType, 5 | FunctionSchema, 6 | GenerateMarkdownConfig, 7 | InterfaceSchema, 8 | NestedTypeSchema, 9 | PropertyType, 10 | Schema, 11 | } from './interface'; 12 | import { toSingleLine } from './util'; 13 | 14 | const BASE_TITLE_PREFIX = '###'; 15 | 16 | function generateMarkdown( 17 | file: string, 18 | config?: GenerateMarkdownConfig 19 | ): Record | string[] | undefined { 20 | const lang = config?.lang || defaultLang; 21 | const markdownSchema = defaultMarkdownTableSchema[lang]; 22 | 23 | if (!markdownSchema) { 24 | return; 25 | } 26 | 27 | const schemas = generate(file, config); 28 | 29 | if (!schemas) { 30 | return; 31 | } 32 | 33 | const getMarkdownTable = (data: PropertyType[], type: MarkdownTableType) => { 34 | const hasVersionTag = data.find((item) => item?.tags?.find((t) => t.name === 'version')); 35 | const tableColumns: Array<{ title: string; value: string }> = []; 36 | 37 | markdownSchema.forEach(({ title, value }) => { 38 | title = typeof title === 'object' ? title[type] : title; 39 | value = typeof value === 'object' ? value[type] : value; 40 | if (hasVersionTag || value !== 'tag.version') { 41 | tableColumns.push({ title, value }); 42 | } 43 | }); 44 | 45 | const tableHeader = `|${tableColumns.map(({ title }) => title).join('|')}| 46 | |${tableColumns.map(() => '---').join('|')}|`; 47 | 48 | const tableBody = data 49 | .map((schema) => { 50 | const requiredTextWord = lang === 'zh' ? '必填' : 'Required'; 51 | const requiredText = !schema.isOptional ? ` **(${requiredTextWord})**` : ''; 52 | const singleLineMarkdown = tableColumns 53 | .map((column) => { 54 | let field = column.value; 55 | 56 | // Field like tag.version 57 | const execResult = /tag\.(\w+)/.exec(field); 58 | if (execResult) { 59 | field = execResult[1]; 60 | const obj = schema.tags?.find((tag) => tag.name === field); 61 | const value = obj ? toSingleLine(obj.value) : '-'; 62 | return field === 'defaultValue' ? `\`${value}\`` : value; 63 | } 64 | 65 | const value = schema[field]; 66 | switch (field) { 67 | case 'type': { 68 | return `${value} ${requiredText}`; 69 | } 70 | case 'initializerText': { 71 | return value !== null ? `\`${value}\`` : '-'; 72 | } 73 | default: 74 | return value; 75 | } 76 | }) 77 | .join('|'); 78 | 79 | return `|${singleLineMarkdown}|`; 80 | }) 81 | .join('\n'); 82 | 83 | return `${tableHeader}\n${tableBody}`; 84 | }; 85 | 86 | const getMarkdownFromSchema = (title: string, schema: Schema): string => { 87 | const markdownTitle = `${BASE_TITLE_PREFIX} ${title}`; 88 | if ((schema as NestedTypeSchema).isNestedType) { 89 | const markdownBody = `\`\`\`js\n${(schema as NestedTypeSchema).data}\`\`\``; 90 | return `${markdownTitle}\n\n${markdownBody}`; 91 | } else { 92 | const dataForTable = (schema as InterfaceSchema).data || (schema as FunctionSchema).params; 93 | const tagMap: Record = {}; 94 | schema.tags.forEach(({ name, value }) => (tagMap[name] = value)); 95 | let description = tagMap[lang] || ''; 96 | let table = 97 | dataForTable && dataForTable.length 98 | ? getMarkdownTable( 99 | dataForTable, 100 | (schema as FunctionSchema).params ? 'parameter' : 'interface' 101 | ) 102 | : ''; 103 | 104 | // Function type 105 | const { params, returns: typeOfReturn } = schema as FunctionSchema; 106 | if (params) { 107 | const { version, returns } = tagMap; 108 | if (version) { 109 | description += `${description ? '\n\n' : ''}${BASE_TITLE_PREFIX}# Since\n${version}`; 110 | } 111 | description += `${ 112 | description ? '\n\n' : '' 113 | }${BASE_TITLE_PREFIX}# Returns\n\`${typeOfReturn}\`${returns ? `: ${returns}` : ''}`; 114 | table = `${BASE_TITLE_PREFIX}# Arguments\n${table}`; 115 | } 116 | 117 | return [markdownTitle, description, table].filter(Boolean).join('\n\n'); 118 | } 119 | }; 120 | 121 | if (config?.strictDeclarationOrder) { 122 | const markdownList: string[] = []; 123 | (schemas as Array<{ title: string; schema: Schema }>).forEach(({ title, schema }) => 124 | markdownList.push(getMarkdownFromSchema(title, schema)) 125 | ); 126 | return markdownList; 127 | } 128 | 129 | const markdownMap: Record = {}; 130 | Object.entries(schemas as Record).forEach(([title, schema]) => { 131 | markdownMap[title] = getMarkdownFromSchema(title, schema); 132 | }); 133 | return markdownMap; 134 | } 135 | 136 | export default generateMarkdown; 137 | -------------------------------------------------------------------------------- /__test__/index.test.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import { generate, generateMarkdown, Project } from '../src/index'; 3 | 4 | const pathFixtures = path.resolve(__dirname, 'fixtures'); 5 | const pathBasic = path.resolve(pathFixtures, 'basic.ts'); 6 | const pathExtends = path.resolve(pathFixtures, 'extends/interface.ts'); 7 | const pathDefaultMap = path.resolve(pathFixtures, 'defaultTypeMap.ts'); 8 | const pathFunction = path.resolve(pathFixtures, 'function.ts'); 9 | const pathPropertySorter = path.resolve(pathFixtures, 'propertySorter.ts'); 10 | const pathNest = path.resolve(pathFixtures, 'nest/interface.ts'); 11 | const pathEscape = path.resolve(pathFixtures, 'escape.ts'); 12 | 13 | const nestLinkFormatter = (options) => { 14 | const { typeName, jsDocTitle, fullPath } = options; 15 | const toHyphen = (str) => { 16 | return str 17 | .replace(/^\w/, (g) => g.toLowerCase()) 18 | .replace(/([A-Z])/g, (g) => `-${g[0].toLowerCase()}`); 19 | }; 20 | if (!jsDocTitle) { 21 | return `#${typeName}`; 22 | } 23 | const componentName = (fullPath || '').match(/components\/([^/]*)/)?.[1]; 24 | if (componentName) { 25 | return `${toHyphen(componentName)}#${jsDocTitle}`; 26 | } 27 | return ''; 28 | }; 29 | 30 | describe('generate', () => { 31 | it('basic', () => { 32 | const schema = generate(pathBasic, { 33 | sourceFilesPaths: './**/*.ts', 34 | }); 35 | expect(schema).toMatchSnapshot(); 36 | }); 37 | 38 | it('extends', () => { 39 | const schema = generate(pathExtends, { 40 | sourceFilesPaths: './**/*.ts', 41 | }); 42 | expect(schema).toMatchSnapshot(); 43 | }); 44 | 45 | it('defaultTypeMap', () => { 46 | const schema = generate(pathDefaultMap, { 47 | sourceFilesPaths: './**/*.ts', 48 | }); 49 | expect(schema).toMatchSnapshot(); 50 | }); 51 | 52 | it('function type', () => { 53 | const schema = generate(pathFunction, { 54 | sourceFilesPaths: './**/*.ts', 55 | }); 56 | expect(schema).toMatchSnapshot(); 57 | }); 58 | 59 | it('sort property', () => { 60 | const schema = generate(pathPropertySorter, { 61 | sourceFilesPaths: './**/*.ts', 62 | propertySorter: ({ type: typeA }, { type: typeB }) => { 63 | const getLevel = (type) => 64 | type === 'boolean' 65 | ? 0 66 | : type === 'number' 67 | ? 1 68 | : type === 'string' 69 | ? 2 70 | : /([^)]*)\s*=>/.test(type) 71 | ? 3 72 | : -1; 73 | return getLevel(typeA) - getLevel(typeB); 74 | }, 75 | }); 76 | expect(schema).toMatchSnapshot(); 77 | }); 78 | 79 | it('nested types', () => { 80 | const schema = generate(pathNest, { 81 | sourceFilesPaths: './**/*.ts', 82 | linkFormatter: nestLinkFormatter, 83 | }); 84 | expect(schema).toMatchSnapshot(); 85 | }); 86 | 87 | it('escape', () => { 88 | const schema = generate(pathEscape, { 89 | sourceFilesPaths: './**/*.ts', 90 | escapeChars: false 91 | }); 92 | expect(schema).toMatchSnapshot(); 93 | }); 94 | }); 95 | 96 | describe('generateMarkdown', () => { 97 | it('basic', () => { 98 | const markdownZh = generateMarkdown(pathBasic, { 99 | sourceFilesPaths: './**/*.ts', 100 | lang: 'zh', 101 | }); 102 | const markdownEn = generateMarkdown(pathBasic, { 103 | sourceFilesPaths: './**/*.ts', 104 | lang: 'en', 105 | }); 106 | expect(markdownZh).toMatchSnapshot(); 107 | expect(markdownEn).toMatchSnapshot(); 108 | }); 109 | 110 | it('extends', () => { 111 | const markdownZh = generateMarkdown(pathExtends, { 112 | sourceFilesPaths: './**/*.ts', 113 | lang: 'zh', 114 | }); 115 | const markdownEn = generateMarkdown(pathExtends, { 116 | sourceFilesPaths: './**/*.ts', 117 | lang: 'en', 118 | }); 119 | expect(markdownZh).toMatchSnapshot(); 120 | expect(markdownEn).toMatchSnapshot(); 121 | }); 122 | 123 | it('function type', () => { 124 | const markdownZh = generateMarkdown(pathFunction, { 125 | sourceFilesPaths: './**/*.ts', 126 | lang: 'zh', 127 | }); 128 | const markdownEn = generateMarkdown(pathFunction, { 129 | sourceFilesPaths: './**/*.ts', 130 | lang: 'en', 131 | strictDeclarationOrder: true, 132 | }); 133 | expect(markdownZh).toMatchSnapshot(); 134 | expect(markdownEn).toMatchSnapshot(); 135 | }); 136 | 137 | it('custom project', () => { 138 | const project = new Project({ 139 | compilerOptions: { 140 | jsx: 'react' as any, 141 | }, 142 | }); 143 | const markdownZh = generateMarkdown(pathFunction, { 144 | sourceFilesPaths: './**/*.ts', 145 | lang: 'zh', 146 | project, 147 | }); 148 | const markdownEn = generateMarkdown(pathFunction, { 149 | sourceFilesPaths: './**/*.ts', 150 | lang: 'en', 151 | strictDeclarationOrder: true, 152 | project, 153 | }); 154 | expect(markdownZh).toMatchSnapshot(); 155 | expect(markdownEn).toMatchSnapshot(); 156 | }); 157 | 158 | it('nested types', () => { 159 | const markdownZh = generateMarkdown(pathNest, { 160 | sourceFilesPaths: './**/*.ts', 161 | lang: 'zh', 162 | linkFormatter: nestLinkFormatter, 163 | }); 164 | const markdownEn = generateMarkdown(pathNest, { 165 | sourceFilesPaths: './**/*.ts', 166 | lang: 'en', 167 | linkFormatter: nestLinkFormatter, 168 | }); 169 | expect(markdownZh).toMatchSnapshot(); 170 | expect(markdownEn).toMatchSnapshot(); 171 | }); 172 | 173 | it('escape', () => { 174 | const markdownZh = generateMarkdown(pathEscape, { 175 | sourceFilesPaths: './**/*.ts', 176 | lang: 'zh', 177 | escapeChars: false 178 | 179 | }); 180 | const markdownEn = generateMarkdown(pathEscape, { 181 | sourceFilesPaths: './**/*.ts', 182 | lang: 'en', 183 | escapeChars: false 184 | }); 185 | expect(markdownZh).toMatchSnapshot(); 186 | expect(markdownEn).toMatchSnapshot(); 187 | }); 188 | }); 189 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ts-document 2 | 3 | Auto generate ts document schema by ts interface conform to the [TSDoc](https://tsdoc.org/). 4 | 5 | ## Highlight 6 | 7 | - Controllable parameter extraction, only the specified interface is extracted. 8 | - Automatically analyze extends relationships. 9 | - Support extract English and Chinese in one ts file. 10 | - Support generate markdown string directly. 11 | 12 | ## Usage 13 | 14 | ```bash 15 | npm i ts-document -D 16 | ``` 17 | 18 | ```js 19 | const { generate, generateMarkdown } = require('ts-document'); 20 | 21 | generate('interface.ts', config); 22 | 23 | generateMarkdown('interface.ts', config); 24 | ``` 25 | 26 | ### interface.ts 27 | 28 | ts-document will only extract interface and type with jsDoc tag `title`。 29 | 30 | ```ts 31 | import { ReactNode } from 'react'; 32 | 33 | /** 34 | * @title Alert 35 | * 36 | * @zh 37 | * 38 | * 向用户显示警告的信息时,通过警告提示,展现需要关注的信息。 39 | * 40 | * @en 41 | * 42 | * Display warning information to the user. the Alert is used to display the information that needs attention. 43 | */ 44 | export interface AlertProps { 45 | /** 46 | * @zh 自定义操作项 47 | * @en this is action 48 | * @version 2.15.0 49 | */ 50 | action?: ReactNode; 51 | /** 52 | * @zh 是否可以关闭 53 | * @en Whether Alert can be closed 54 | * @defaultValue false 55 | */ 56 | closable?: InnerProps; 57 | } 58 | 59 | interface InnerProps { 60 | /** 61 | * @zh 位置 62 | * @en position 63 | */ 64 | position?: string; 65 | /** 66 | * @zh 尺寸 67 | * @en Size 68 | */ 69 | size?: string; 70 | } 71 | ``` 72 | 73 | ## Generate jsDoc schema 74 | 75 | ```js 76 | const { generate } = require('ts-document'); 77 | 78 | generate('interface.ts'); 79 | ``` 80 | 81 | output 82 | 83 | ```json 84 | { 85 | "Alert": { 86 | "data": [ 87 | { 88 | "name": "action", 89 | "type": "ReactNode", 90 | "isOptional": true, 91 | "tags": [ 92 | { 93 | "name": "zh", 94 | "value": "自定义操作项" 95 | }, 96 | { 97 | "name": "en", 98 | "value": "this is action" 99 | }, 100 | { 101 | "name": "version", 102 | "value": "2.15.0" 103 | } 104 | ] 105 | }, 106 | { 107 | "name": "closable", 108 | "type": "InnerProps", 109 | "isOptional": true, 110 | "tags": [ 111 | { 112 | "name": "zh", 113 | "value": "是否可以关闭" 114 | }, 115 | { 116 | "name": "en", 117 | "value": "Whether Alert can be closed" 118 | }, 119 | { 120 | "name": "defaultValue", 121 | "value": "false" 122 | } 123 | ] 124 | } 125 | ], 126 | "tags": [ 127 | { 128 | "name": "title", 129 | "value": "Alert" 130 | }, 131 | { 132 | "name": "zh", 133 | "value": "向用户显示警告的信息时,通过警告提示,展现需要关注的信息。" 134 | }, 135 | { 136 | "name": "en", 137 | "value": "Display warning information to the user. the Alert is used to display the information that needs attention." 138 | } 139 | ] 140 | } 141 | } 142 | ``` 143 | 144 | ## Generate markdown document 145 | 146 | ```js 147 | const { generateMarkdown } = require('ts-document'); 148 | 149 | generateMarkdown('interface.ts'); 150 | ``` 151 | 152 | output 153 | 154 | ```json 155 | { 156 | "Alert": "### Alert\n\nDisplay warning information to the user. the Alert is used to display the information that needs attention.\n\n|Property|Description|Type|DefaultValue|Version|\n|---|---|---|---|---|\n|action|this is action|`ReactNode`|`-`|2.15.0|\n|closable|Whether Alert can be closed|`InnerProps`|`false`|-|" 157 | } 158 | ``` 159 | 160 | ## Config 161 | 162 | ### defaultTypeMap 163 | 164 | `Record` 165 | 166 | If no comments are extracted, will extracted from the `defaultTypeMap` automatically. 167 | 168 | ### sourceFilesPaths 169 | 170 | `string | string[]` 171 | 172 | See [ts-morph](https://ts-morph.com/setup/adding-source-files)。 173 | 174 | ### strictComment 175 | 176 | `boolean` 177 | 178 | Whether to skip parsing documentation comment like `/** Some comment **/` as property description if there is no tag like `@en` or `@zh`. 179 | 180 | ### strictDeclarationOrder 181 | 182 | `boolean` 183 | 184 | Generate schema in the order their declarations appear in the document. When it's true, `generate/generateMarkdown` will return an array list(`Array<{ title: string; schema: Schema }>`). 185 | 186 | ### propertySorter 187 | 188 | `(a: { name: string; type: string; isOptional: boolean; tags: Array<{ name: string; value: string }>; }, b: typeof a) => number` 189 | 190 | The compare function to sort properties/arguments of schema generated. 191 | 192 | ### lang 193 | 194 | `string` 195 | 196 | Only work in `generateMarkdown`, specify output language. 197 | 198 | ### project 199 | 200 | `Project` 201 | 202 | Custom project to use in `generate/generateMarkdown` function. See [ts-morph](https://ts-morph.com/setup/)。 203 | 204 | ### linkFormatter 205 | 206 | `({ typeName: string, jsDocTitle?: string, fullPath: string }) => string` 207 | 208 | Format function to generate link of nested types. 209 | 210 | ### ignoreNestedType 211 | 212 | When parsing nested types, whether to ignore these nested types if they are defined in some files. 213 | When returning true, nested types must not be exported, but when false is returned, 214 | nested types may not be exported due to other reasons (such as the nested type has jsdoc @title, which needs to be manually exported) 215 | 216 | ### escapeChars 217 | 218 | `boolean`: default to `true` 219 | 220 | Whether to escape characters for extracted type text. E.g. `|` will be escaped to `\|`, `` will be escaped to `<Promise>`. 221 | 222 | ## Who's using? 223 | 224 | [Arco Design](https://github.com/arco-design/arco-design) - A comprehensive React UI components library based on Arco Design. 225 | 226 | ## LICENSE 227 | 228 | [MIT](./LICENSE) © [PengJiyuan](https://github.com/PengJiyuan) 229 | -------------------------------------------------------------------------------- /src/generate.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Project, 3 | SourceFile, 4 | TypeChecker, 5 | Symbol, 6 | FunctionDeclaration, 7 | InterfaceDeclaration, 8 | TypeAliasDeclaration, 9 | Node, 10 | Type, 11 | ts, 12 | SyntaxKind, 13 | } from 'ts-morph'; 14 | import { 15 | PropertyType, 16 | GenerateConfig, 17 | DefaultTypeMapT, 18 | TagType, 19 | FunctionSchema, 20 | Schema, 21 | SchemaList, 22 | InterfaceSchema, 23 | NestedTypeSchema, 24 | LinkFormatter, 25 | } from './interface'; 26 | import { defaultTypeMap, defaultLinkFormatter } from './default'; 27 | import { toSingleLine } from './util'; 28 | 29 | const dummyProject = new Project({ useInMemoryFileSystem: true }); 30 | 31 | type DeclarationCanBeParsed = InterfaceDeclaration | TypeAliasDeclaration | FunctionDeclaration; 32 | 33 | type ExtractType = { 34 | name: string; 35 | type: string; 36 | isOptional: boolean; 37 | }; 38 | 39 | const KEYWORDS_TO_SKIP = ['Omit']; 40 | 41 | const TAG_NAMES_FOR_DESCRIPTION = ['zh', 'en']; 42 | 43 | const internalProject = new Project({ 44 | compilerOptions: { 45 | jsx: 'react' as any, 46 | }, 47 | }); 48 | 49 | const propertyRegex = /(\w+)\s{0,}([?]?)\s{0,}:(.*?);?$/s; 50 | 51 | let config: GenerateConfig; 52 | 53 | function setConfig(cfg: typeof config) { 54 | config = cfg; 55 | } 56 | 57 | // extract pure type 58 | function extractFromPropertyText(text: string): ExtractType | undefined { 59 | const regexResult = propertyRegex.exec(text); 60 | if (!regexResult) { 61 | return; 62 | } 63 | const name = regexResult[1]; 64 | const isOptional = regexResult[2] === '?'; 65 | const type = regexResult[3]; 66 | 67 | return { 68 | name, 69 | isOptional, 70 | type, 71 | }; 72 | } 73 | 74 | // Get key-value pairs from jsDoc of Declarations 75 | function getDeclarationTags(declaration: DeclarationCanBeParsed) { 76 | const tags: Array = []; 77 | const rawTags = declaration.getJsDocs()[0]?.getTags() || []; 78 | let title; 79 | 80 | for (const tag of rawTags) { 81 | const name = tag.getTagName(); 82 | const value = tag.getCommentText() || ''; 83 | 84 | if (name === 'title') { 85 | title = value; 86 | } 87 | 88 | tags.push({ name, value }); 89 | } 90 | 91 | return { 92 | title, 93 | tags, 94 | }; 95 | } 96 | 97 | // Get key-value pairs from jsDoc of Symbol 98 | function getSymbolTags(sym: Symbol, strictComment = false): TagType[] { 99 | const jsDocTags = sym.compilerSymbol.getJsDocTags(); 100 | const tags: TagType[] = jsDocTags.map((tag) => ({ 101 | name: tag.name, 102 | value: tag.text?.[0].text || '', 103 | })); 104 | 105 | // Try to extend property description from common comment 106 | if (!strictComment) { 107 | const [commonComment] = sym.compilerSymbol.getDocumentationComment(undefined); 108 | if (commonComment && commonComment.kind === 'text' && commonComment.text) { 109 | TAG_NAMES_FOR_DESCRIPTION.forEach((tagNameForDescription) => { 110 | if (!tags.find(({ name }) => name === tagNameForDescription)) { 111 | tags.push({ name: tagNameForDescription, value: commonComment.text }); 112 | } 113 | }); 114 | } 115 | } 116 | 117 | return tags; 118 | } 119 | 120 | function getSymbolByType(type: Type) { 121 | return type.getAliasSymbol() || type.getSymbol(); 122 | } 123 | 124 | function hasJSDocTitle( 125 | declaration: Node | DeclarationCanBeParsed, 126 | parsedNestedTypeSet: Set 127 | ) { 128 | const { title } = 129 | (declaration && 'getJsDocs' in declaration && getDeclarationTags(declaration)) || {}; 130 | if (title) { 131 | // Type with @title in JSDoc is used to format link but not dumped as nested types onto page 132 | parsedNestedTypeSet.add(declaration.getType()); 133 | return true; 134 | } 135 | return false; 136 | } 137 | 138 | // Check whether the type is our target that we want to continue parsing 139 | function isTarget(type: Type, parsedNestedTypeSet: Set) { 140 | // Has parsed before 141 | if (parsedNestedTypeSet.has(type)) { 142 | return false; 143 | } 144 | const declaration: any = getDeclarationBySymbol(getSymbolByType(type)); 145 | if (hasJSDocTitle(declaration, parsedNestedTypeSet)) { 146 | return false; 147 | } 148 | const defPath = declaration?.getSourceFile()?.getFilePath(); 149 | if ( 150 | // Types from node_modules 151 | defPath && config.ignoreNestedType!(defPath) 152 | ) { 153 | return false; 154 | } 155 | 156 | return ( 157 | type.isInterface() || type.isEnum() || type.isUnionOrIntersection() || isAliasDeclaration(type) 158 | ); 159 | } 160 | 161 | function isAliasDeclaration(type: Type) { 162 | // No type.isAlias method so do alias declaration check separately 163 | const aliasSymbol = type.getAliasSymbol(); 164 | if (aliasSymbol) { 165 | return getDeclarationBySymbol(aliasSymbol)?.getKind() === SyntaxKind.TypeAliasDeclaration; 166 | } 167 | return false; 168 | } 169 | 170 | function getDeclarationBySymbol(symbol?: Symbol) { 171 | return symbol?.getDeclarations()?.[0]; 172 | } 173 | 174 | function getDeclarationTextBySymbol(symbol?: Symbol) { 175 | const declaration = getDeclarationBySymbol(symbol); 176 | // Four spaces -> two spaces and add a new line at the end 177 | return `${(declaration?.print() || '').replace(/ {4}/g, ' '.repeat(2))}\n`; 178 | } 179 | 180 | /** 181 | * 182 | * @param declaration declaration to be parsed 183 | * @param nestedTypeList list to store the nested types to be displayed on the doc page 184 | * @param parsedNestedTypes set of parsed nested types to avoid infinite loop 185 | * @returns 186 | */ 187 | function dumpNestedTypes( 188 | declaration: Node | undefined, 189 | nestedTypeList: SchemaList, 190 | parsedNestedTypes: Set 191 | ) { 192 | if (declaration == null) { 193 | return; 194 | } 195 | if (hasJSDocTitle(declaration, parsedNestedTypes)) { 196 | return; 197 | } 198 | declaration.forEachDescendant((descendant) => { 199 | const typeOfIdentifier = descendant.getType(); 200 | const symbolOfIdentifier = getSymbolByType(typeOfIdentifier); 201 | const title = symbolOfIdentifier?.getName(); 202 | if ( 203 | // Only interested in type nodes that has title and matches our target check 204 | !Node.isTypeNode(descendant) || 205 | !title || 206 | KEYWORDS_TO_SKIP.includes(title) || 207 | !isTarget(typeOfIdentifier, parsedNestedTypes) 208 | ) { 209 | return; 210 | } 211 | parsedNestedTypes.add(typeOfIdentifier); 212 | const schema: NestedTypeSchema = { 213 | tags: [ 214 | { 215 | name: 'title', 216 | value: title, 217 | }, 218 | ], 219 | data: getDeclarationTextBySymbol(symbolOfIdentifier), 220 | isNestedType: true, 221 | } as NestedTypeSchema; 222 | nestedTypeList.push({ 223 | title, 224 | schema, 225 | }); 226 | if (typeOfIdentifier.isUnionOrIntersection()) { 227 | const method = typeOfIdentifier.isUnion() ? 'getUnionTypes' : 'getIntersectionTypes'; 228 | // Recursively iterate subTypes 229 | const subTypes = typeOfIdentifier[method]() || []; 230 | subTypes.forEach((subType) => { 231 | const subTypeSymbol = getSymbolByType(subType); 232 | if (subTypeSymbol && getDeclarationBySymbol(subTypeSymbol)) { 233 | dumpNestedTypes(getDeclarationBySymbol(subTypeSymbol), nestedTypeList, parsedNestedTypes); 234 | } 235 | }); 236 | } else if (typeOfIdentifier.isInterface()) { 237 | // Recursively iterate children properties 238 | (getDeclarationBySymbol(symbolOfIdentifier) as InterfaceDeclaration) 239 | .getProperties() 240 | .forEach((a) => { 241 | dumpNestedTypes(getDeclarationBySymbol(a.getSymbol()), nestedTypeList, parsedNestedTypes); 242 | }); 243 | } 244 | }); 245 | return nestedTypeList; 246 | } 247 | 248 | function getDisplayTypeWithLink( 249 | originTypeText: string, 250 | nestedTypeList: SchemaList, 251 | parsedNestedTypeSet: Set, 252 | linkFormatter: LinkFormatter 253 | ) { 254 | const sourceFile = dummyProject.createSourceFile( 255 | './dummy.ts', 256 | toSingleLine(originTypeText, config.escapeChars), 257 | { 258 | overwrite: true, 259 | } 260 | ); 261 | sourceFile.transform((traversal) => { 262 | const node = traversal.visitChildren(); 263 | 264 | if (ts.isIdentifier(node)) { 265 | const nodeTypeText = node.text; 266 | // Check identifiers in the type against parsed custom type definitions and replace with link info if necessary 267 | for (const parsedNestedType of parsedNestedTypeSet) { 268 | const typeName = getSymbolByType(parsedNestedType)?.getName(); 269 | if (!typeName || typeName !== nodeTypeText) { 270 | continue; 271 | } 272 | const matchedNestedType = nestedTypeList.find((item) => item.title === typeName); 273 | let link; 274 | if (matchedNestedType) { 275 | // Type that is available on the current page and doesn't have JSDoc @title 276 | link = linkFormatter({ typeName }); 277 | } else { 278 | const declaration: any = getDeclarationBySymbol(getSymbolByType(parsedNestedType)); 279 | const definitionPath = declaration?.getSourceFile()?.getFilePath(); 280 | const { title } = (declaration?.getJsDocs && getDeclarationTags(declaration)) || {}; 281 | // Has @title in JSDoc 282 | if (title) { 283 | link = linkFormatter({ typeName, jsDocTitle: title, fullPath: definitionPath }); 284 | } 285 | } 286 | // Only convert to link when link is available 287 | if (link) { 288 | return ts.factory.createIdentifier(`[${typeName}](${link})`); 289 | } 290 | } 291 | } 292 | return node; 293 | }); 294 | return sourceFile.getText(); 295 | } 296 | 297 | // Get Json schema of interface's property 298 | function getPropertySchema( 299 | sym: Symbol, 300 | defaultT: DefaultTypeMapT, 301 | strictComment = false, 302 | nestedTypeList: SchemaList, 303 | parsedNestedTypeSet: Set, 304 | linkFormatter: LinkFormatter 305 | ): PropertyType | null { 306 | const name = sym.getName(); 307 | const declaration = sym.getDeclarations()[0]; 308 | const typeText = declaration.getText(); 309 | const extract = extractFromPropertyText(typeText); 310 | 311 | if (!extract) { 312 | return null; 313 | } 314 | 315 | const tags = getSymbolTags(sym, strictComment); 316 | if (tags.find(({ name }) => name && TAG_NAMES_FOR_DESCRIPTION.indexOf(name) > -1)) { 317 | // Deeply analyze nested types 318 | dumpNestedTypes(declaration, nestedTypeList, parsedNestedTypeSet); 319 | 320 | const typeWithLink = getDisplayTypeWithLink( 321 | extract.type, 322 | nestedTypeList, 323 | parsedNestedTypeSet, 324 | linkFormatter 325 | ); 326 | 327 | return { 328 | name, 329 | type: typeWithLink, 330 | isOptional: extract.isOptional, 331 | tags, 332 | }; 333 | } 334 | 335 | return defaultT[name] 336 | ? { 337 | name, 338 | isOptional: extract.isOptional, 339 | ...defaultT[name], 340 | } 341 | : null; 342 | } 343 | 344 | // Get Json schema of Function 345 | function getFunctionSchema( 346 | declaration: FunctionDeclaration, 347 | strictComment = false, 348 | nestedTypeList: SchemaList, 349 | parsedNestedTypeSet: Set, 350 | linkFormatter: LinkFormatter 351 | ): Pick { 352 | return { 353 | params: declaration.getParameters().map((para) => { 354 | // Deeply analyze nested types 355 | dumpNestedTypes(para, nestedTypeList, parsedNestedTypeSet); 356 | 357 | const tags = getSymbolTags(para.getSymbol() as Symbol, strictComment); 358 | const typeWithLink = getDisplayTypeWithLink( 359 | para 360 | .getType() 361 | .getText() 362 | .replace(/import\([^)]+\)\./g, ''), 363 | nestedTypeList, 364 | parsedNestedTypeSet, 365 | linkFormatter 366 | ); 367 | 368 | return { 369 | tags, 370 | name: para.getName(), 371 | type: typeWithLink, 372 | isOptional: para.isOptional(), 373 | initializerText: 374 | para.getInitializer()?.getText() || 375 | tags.find(({ name }) => name === 'default' || name === 'defaultValue')?.value || 376 | null, 377 | }; 378 | }), 379 | returns: declaration.getReturnType().getText(), 380 | }; 381 | } 382 | 383 | function generateSchema(sourceFile: SourceFile, typeChecker: TypeChecker, config?: GenerateConfig) { 384 | const interfaces = sourceFile?.getInterfaces() || []; 385 | const typeAliases = sourceFile?.getTypeAliases() || []; 386 | const functions = sourceFile?.getFunctions() || []; 387 | const defaultT = config?.defaultTypeMap || defaultTypeMap; 388 | const strictComment = !!config?.strictComment; 389 | const propertySorter = config?.propertySorter; 390 | const linkFormatter = config?.linkFormatter || defaultLinkFormatter; 391 | 392 | const schemaMap: Record = {}; 393 | const schemaList: SchemaList = []; 394 | const nestedTypeList: SchemaList = []; 395 | const parsedNestedTypeSet = new Set(); 396 | 397 | [...interfaces, ...typeAliases, ...functions] 398 | .sort((declarationA, declarationB) => { 399 | return declarationA.getStartLineNumber() - declarationB.getStartLineNumber(); 400 | }) 401 | .forEach((declaration) => { 402 | const { title, tags } = getDeclarationTags(declaration); 403 | const dType = declaration.getKindName() as 404 | | 'InterfaceDeclaration' 405 | | 'FunctionDeclaration' 406 | | 'TypeAliasDeclaration'; 407 | 408 | if (!title) { 409 | return; 410 | } 411 | 412 | let schema: Schema; 413 | const typeNode = 414 | dType === 'FunctionDeclaration' 415 | ? (declaration as FunctionDeclaration) 416 | : dType === 'TypeAliasDeclaration' 417 | ? (declaration as TypeAliasDeclaration).getTypeNode() 418 | : null; 419 | 420 | // Function declaration 421 | if ( 422 | typeNode && 423 | ['FunctionDeclaration', 'FunctionType'].indexOf(typeNode.getKindName()) > -1 424 | ) { 425 | schema = { 426 | tags, 427 | ...getFunctionSchema( 428 | typeNode as FunctionDeclaration, 429 | strictComment, 430 | nestedTypeList, 431 | parsedNestedTypeSet, 432 | linkFormatter 433 | ), 434 | }; 435 | } 436 | // Interface declaration forbid extends 437 | else if ( 438 | dType === 'InterfaceDeclaration' && 439 | !!tags.find(({ name }) => name === 'notExtends') 440 | ) { 441 | const data: PropertyType[] = []; 442 | (declaration as InterfaceDeclaration).getProperties().forEach((a) => { 443 | const schema = getPropertySchema( 444 | a.getSymbol() as Symbol, 445 | defaultT, 446 | strictComment, 447 | nestedTypeList, 448 | parsedNestedTypeSet, 449 | linkFormatter 450 | ); 451 | schema && data.push(schema); 452 | }); 453 | schema = { tags, data }; 454 | } else { 455 | const data: PropertyType[] = []; 456 | typeChecker.getPropertiesOfType(declaration.getType()).forEach((a) => { 457 | const schema = getPropertySchema( 458 | a, 459 | defaultT, 460 | strictComment, 461 | nestedTypeList, 462 | parsedNestedTypeSet, 463 | linkFormatter 464 | ); 465 | schema && data.push(schema); 466 | }); 467 | schema = { tags, data }; 468 | } 469 | 470 | if (typeof propertySorter === 'function') { 471 | (schema as InterfaceSchema).data?.sort(propertySorter); 472 | (schema as FunctionSchema).params?.sort(propertySorter); 473 | } 474 | 475 | schemaList.push({ title, schema }); 476 | schemaMap[title] = schema; 477 | }); 478 | 479 | let list = schemaList; 480 | let map = schemaMap; 481 | if (nestedTypeList.length > 0) { 482 | list = [...schemaList, ...nestedTypeList]; 483 | map = { 484 | ...schemaMap, 485 | ...nestedTypeList.reduce((result, item) => { 486 | result[item.title] = item.schema; 487 | return result; 488 | }, {}), 489 | }; 490 | } 491 | return config?.strictDeclarationOrder ? list : map; 492 | } 493 | 494 | function generate( 495 | file: string, 496 | config?: GenerateConfig 497 | ): Record | Array<{ title: string; schema: Schema }> | undefined { 498 | config = { 499 | sourceFilesPaths: [], 500 | ignoreNestedType(definitionFilePath: string) { 501 | return definitionFilePath.includes('/node_modules/') 502 | }, 503 | escapeChars: true, 504 | ...config 505 | } 506 | 507 | setConfig(config); 508 | 509 | const project = config?.project || internalProject; 510 | 511 | if (config?.sourceFilesPaths) { 512 | project.addSourceFilesAtPaths(config?.sourceFilesPaths); 513 | } 514 | 515 | const typeChecker = project.getTypeChecker(); 516 | const sourceFile = project.getSourceFile(file); 517 | 518 | if (!sourceFile) { 519 | return; 520 | } 521 | 522 | return generateSchema(sourceFile, typeChecker, config); 523 | } 524 | 525 | export default generate; 526 | -------------------------------------------------------------------------------- /__test__/__snapshots__/index.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`generate basic 1`] = ` 4 | Object { 5 | "A": Object { 6 | "data": Array [ 7 | Object { 8 | "isOptional": true, 9 | "name": "action", 10 | "tags": Array [ 11 | Object { 12 | "name": "zh", 13 | "value": "自定义操作项", 14 | }, 15 | Object { 16 | "name": "en", 17 | "value": "this is action", 18 | }, 19 | Object { 20 | "name": "version", 21 | "value": "2.15.0", 22 | }, 23 | ], 24 | "type": "ReactNode", 25 | }, 26 | Object { 27 | "isOptional": true, 28 | "name": "closable", 29 | "tags": Array [ 30 | Object { 31 | "name": "zh", 32 | "value": "是否可以关闭", 33 | }, 34 | Object { 35 | "name": "en", 36 | "value": "Whether Alert can be closed", 37 | }, 38 | Object { 39 | "name": "defaultValue", 40 | "value": "{}", 41 | }, 42 | ], 43 | "type": "[InnerProps](#InnerProps)", 44 | }, 45 | ], 46 | "tags": Array [ 47 | Object { 48 | "name": "title", 49 | "value": "A", 50 | }, 51 | Object { 52 | "name": "zh", 53 | "value": "这是接口描述。", 54 | }, 55 | Object { 56 | "name": "en", 57 | "value": "This is interface description.", 58 | }, 59 | ], 60 | }, 61 | "InnerProps": Object { 62 | "data": "export interface InnerProps { 63 | /** 64 | * @zh 位置 65 | * @en position 66 | */ 67 | position?: string; 68 | /** 69 | * @zh 尺寸 70 | * @en Size 71 | */ 72 | size?: string; 73 | } 74 | ", 75 | "isNestedType": true, 76 | "tags": Array [ 77 | Object { 78 | "name": "title", 79 | "value": "InnerProps", 80 | }, 81 | ], 82 | }, 83 | } 84 | `; 85 | 86 | exports[`generate defaultTypeMap 1`] = ` 87 | Object { 88 | "A": Object { 89 | "data": Array [ 90 | Object { 91 | "isOptional": false, 92 | "name": "style", 93 | "tags": Array [ 94 | Object { 95 | "name": "zh", 96 | "value": "节点样式", 97 | }, 98 | Object { 99 | "name": "en", 100 | "value": "Additional style", 101 | }, 102 | ], 103 | "type": "CSSProperties", 104 | }, 105 | Object { 106 | "isOptional": false, 107 | "name": "className", 108 | "tags": Array [ 109 | Object { 110 | "name": "zh", 111 | "value": "节点类名", 112 | }, 113 | Object { 114 | "name": "en", 115 | "value": "Additional css class", 116 | }, 117 | ], 118 | "type": "string \\\\| string[]", 119 | }, 120 | ], 121 | "tags": Array [ 122 | Object { 123 | "name": "title", 124 | "value": "A", 125 | }, 126 | ], 127 | }, 128 | } 129 | `; 130 | 131 | exports[`generate escape 1`] = ` 132 | Object { 133 | "A": Object { 134 | "data": Array [ 135 | Object { 136 | "isOptional": true, 137 | "name": "size", 138 | "tags": Array [ 139 | Object { 140 | "name": "zh", 141 | "value": "尺寸", 142 | }, 143 | Object { 144 | "name": "en", 145 | "value": "size", 146 | }, 147 | ], 148 | "type": "'mini' | 'large' | 'default'", 149 | }, 150 | Object { 151 | "isOptional": true, 152 | "name": "fetchData", 153 | "tags": Array [ 154 | Object { 155 | "name": "zh", 156 | "value": "获取数据函数", 157 | }, 158 | Object { 159 | "name": "en", 160 | "value": "Function to fetch data", 161 | }, 162 | ], 163 | "type": "() => Promise", 164 | }, 165 | ], 166 | "tags": Array [ 167 | Object { 168 | "name": "title", 169 | "value": "A", 170 | }, 171 | Object { 172 | "name": "zh", 173 | "value": "这是接口描述。", 174 | }, 175 | Object { 176 | "name": "en", 177 | "value": "This is interface description.", 178 | }, 179 | ], 180 | }, 181 | } 182 | `; 183 | 184 | exports[`generate extends 1`] = ` 185 | Object { 186 | "Alert": Object { 187 | "data": Array [ 188 | Object { 189 | "isOptional": false, 190 | "name": "action", 191 | "tags": Array [ 192 | Object { 193 | "name": "zh", 194 | "value": "自定义操作项", 195 | }, 196 | Object { 197 | "name": "en", 198 | "value": "this is action", 199 | }, 200 | Object { 201 | "name": "version", 202 | "value": "2.15.0", 203 | }, 204 | ], 205 | "type": "ReactElement", 206 | }, 207 | Object { 208 | "isOptional": true, 209 | "name": "closable", 210 | "tags": Array [ 211 | Object { 212 | "name": "zh", 213 | "value": "是否可以关闭", 214 | }, 215 | Object { 216 | "name": "en", 217 | "value": "Whether Alert can be closed", 218 | }, 219 | Object { 220 | "name": "defaultValue", 221 | "value": "false", 222 | }, 223 | ], 224 | "type": "{a: boolean;b: string;}", 225 | }, 226 | Object { 227 | "isOptional": true, 228 | "name": "callback", 229 | "tags": Array [ 230 | Object { 231 | "name": "zh", 232 | "value": "回调参数", 233 | }, 234 | Object { 235 | "name": "en", 236 | "value": "Callback function", 237 | }, 238 | ], 239 | "type": "(option: [Option](#Option)) => void", 240 | }, 241 | Object { 242 | "isOptional": true, 243 | "name": "bb", 244 | "tags": Array [ 245 | Object { 246 | "name": "zh", 247 | "value": "BB", 248 | }, 249 | Object { 250 | "name": "en", 251 | "value": "bb", 252 | }, 253 | ], 254 | "type": "boolean", 255 | }, 256 | Object { 257 | "isOptional": true, 258 | "name": "animation", 259 | "tags": Array [ 260 | Object { 261 | "name": "zh", 262 | "value": "动画", 263 | }, 264 | Object { 265 | "name": "en", 266 | "value": "animation", 267 | }, 268 | ], 269 | "type": "boolean \\\\| string", 270 | }, 271 | ], 272 | "tags": Array [ 273 | Object { 274 | "name": "title", 275 | "value": "Alert", 276 | }, 277 | Object { 278 | "name": "zh", 279 | "value": "向用户显示警告的信息时,通过警告提示,展现需要关注的信息。", 280 | }, 281 | Object { 282 | "name": "en", 283 | "value": "Display warning information to the user. the Alert is used to display the information that needs attention.", 284 | }, 285 | ], 286 | }, 287 | "Option": Object { 288 | "data": "export type Option = { 289 | a: string; 290 | b: string; 291 | }; 292 | ", 293 | "isNestedType": true, 294 | "tags": Array [ 295 | Object { 296 | "name": "title", 297 | "value": "Option", 298 | }, 299 | ], 300 | }, 301 | "Test": Object { 302 | "data": Array [ 303 | Object { 304 | "isOptional": true, 305 | "name": "position", 306 | "tags": Array [ 307 | Object { 308 | "name": "zh", 309 | "value": "位置", 310 | }, 311 | Object { 312 | "name": "en", 313 | "value": "position", 314 | }, 315 | ], 316 | "type": "string", 317 | }, 318 | Object { 319 | "isOptional": true, 320 | "name": "size", 321 | "tags": Array [ 322 | Object { 323 | "name": "zh", 324 | "value": "尺寸", 325 | }, 326 | Object { 327 | "name": "en", 328 | "value": "Size", 329 | }, 330 | ], 331 | "type": "string", 332 | }, 333 | Object { 334 | "isOptional": false, 335 | "name": "action", 336 | "tags": Array [ 337 | Object { 338 | "name": "zh", 339 | "value": "自定义操作项", 340 | }, 341 | Object { 342 | "name": "en", 343 | "value": "this is action", 344 | }, 345 | Object { 346 | "name": "version", 347 | "value": "2.15.0", 348 | }, 349 | ], 350 | "type": "ReactElement", 351 | }, 352 | Object { 353 | "isOptional": true, 354 | "name": "closable", 355 | "tags": Array [ 356 | Object { 357 | "name": "zh", 358 | "value": "是否可以关闭", 359 | }, 360 | Object { 361 | "name": "en", 362 | "value": "Whether Alert can be closed", 363 | }, 364 | Object { 365 | "name": "defaultValue", 366 | "value": "false", 367 | }, 368 | ], 369 | "type": "{a: boolean;b: string;}", 370 | }, 371 | Object { 372 | "isOptional": true, 373 | "name": "callback", 374 | "tags": Array [ 375 | Object { 376 | "name": "zh", 377 | "value": "回调参数", 378 | }, 379 | Object { 380 | "name": "en", 381 | "value": "Callback function", 382 | }, 383 | ], 384 | "type": "(option: [Option](#Option)) => void", 385 | }, 386 | Object { 387 | "isOptional": true, 388 | "name": "bb", 389 | "tags": Array [ 390 | Object { 391 | "name": "zh", 392 | "value": "BB", 393 | }, 394 | Object { 395 | "name": "en", 396 | "value": "bb", 397 | }, 398 | ], 399 | "type": "boolean", 400 | }, 401 | Object { 402 | "isOptional": true, 403 | "name": "animation", 404 | "tags": Array [ 405 | Object { 406 | "name": "zh", 407 | "value": "动画", 408 | }, 409 | Object { 410 | "name": "en", 411 | "value": "animation", 412 | }, 413 | ], 414 | "type": "boolean \\\\| string", 415 | }, 416 | ], 417 | "tags": Array [ 418 | Object { 419 | "name": "title", 420 | "value": "Test", 421 | }, 422 | ], 423 | }, 424 | } 425 | `; 426 | 427 | exports[`generate function type 1`] = ` 428 | Object { 429 | "Add": Object { 430 | "params": Array [ 431 | Object { 432 | "initializerText": null, 433 | "isOptional": false, 434 | "name": "a", 435 | "tags": Array [ 436 | Object { 437 | "name": "zh", 438 | "value": "被加数", 439 | }, 440 | Object { 441 | "name": "en", 442 | "value": "First number", 443 | }, 444 | ], 445 | "type": "number", 446 | }, 447 | Object { 448 | "initializerText": "5", 449 | "isOptional": true, 450 | "name": "b", 451 | "tags": Array [ 452 | Object { 453 | "name": "zh", 454 | "value": "加数", 455 | }, 456 | Object { 457 | "name": "en", 458 | "value": "Second number", 459 | }, 460 | ], 461 | "type": "number", 462 | }, 463 | ], 464 | "returns": "number", 465 | "tags": Array [ 466 | Object { 467 | "name": "title", 468 | "value": "Add", 469 | }, 470 | Object { 471 | "name": "en", 472 | "value": "Add two numbers", 473 | }, 474 | Object { 475 | "name": "zh", 476 | "value": "两数相加", 477 | }, 478 | Object { 479 | "name": "returns", 480 | "value": "Sum of two numbers", 481 | }, 482 | Object { 483 | "name": "version", 484 | "value": "1.0.0", 485 | }, 486 | ], 487 | }, 488 | "ButtonType": Object { 489 | "data": "type ButtonType = { 490 | size?: 'mini' | 'large' | 'default'; 491 | color?: string; 492 | }; 493 | ", 494 | "isNestedType": true, 495 | "tags": Array [ 496 | Object { 497 | "name": "title", 498 | "value": "ButtonType", 499 | }, 500 | ], 501 | }, 502 | "Plus": Object { 503 | "params": Array [ 504 | Object { 505 | "initializerText": null, 506 | "isOptional": false, 507 | "name": "a", 508 | "tags": Array [ 509 | Object { 510 | "name": "en", 511 | "value": "First number", 512 | }, 513 | Object { 514 | "name": "zh", 515 | "value": "被乘数", 516 | }, 517 | ], 518 | "type": "number", 519 | }, 520 | Object { 521 | "initializerText": "1", 522 | "isOptional": false, 523 | "name": "b", 524 | "tags": Array [ 525 | Object { 526 | "name": "en", 527 | "value": "Second number", 528 | }, 529 | Object { 530 | "name": "zh", 531 | "value": "乘数", 532 | }, 533 | Object { 534 | "name": "defaultValue", 535 | "value": "1", 536 | }, 537 | ], 538 | "type": "number", 539 | }, 540 | ], 541 | "returns": "number", 542 | "tags": Array [ 543 | Object { 544 | "name": "title", 545 | "value": "Plus", 546 | }, 547 | Object { 548 | "name": "zh", 549 | "value": "两数相乘", 550 | }, 551 | Object { 552 | "name": "en", 553 | "value": "Multiply two numbers", 554 | }, 555 | Object { 556 | "name": "returns", 557 | "value": "Product of two numbers", 558 | }, 559 | ], 560 | }, 561 | "function with nest type": Object { 562 | "params": Array [ 563 | Object { 564 | "initializerText": null, 565 | "isOptional": false, 566 | "name": "buttonProps", 567 | "tags": Array [], 568 | "type": "Partial<[ButtonType](#ButtonType)>", 569 | }, 570 | ], 571 | "returns": "any", 572 | "tags": Array [ 573 | Object { 574 | "name": "title", 575 | "value": "function with nest type", 576 | }, 577 | ], 578 | }, 579 | } 580 | `; 581 | 582 | exports[`generate nested types 1`] = ` 583 | Object { 584 | "A": Object { 585 | "data": Array [ 586 | Object { 587 | "isOptional": true, 588 | "name": "treeSelectDataType", 589 | "tags": Array [ 590 | Object { 591 | "name": "zh", 592 | "value": "树选择数据属性", 593 | }, 594 | Object { 595 | "name": "en", 596 | "value": "treeSelectDataType", 597 | }, 598 | ], 599 | "type": "[TreeSelectDataType](#TreeSelectDataType)", 600 | }, 601 | Object { 602 | "isOptional": false, 603 | "name": "onClick", 604 | "tags": Array [ 605 | Object { 606 | "name": "zh", 607 | "value": "点击回调", 608 | }, 609 | Object { 610 | "name": "en", 611 | "value": "onClick", 612 | }, 613 | ], 614 | "type": "(num: number) => void", 615 | }, 616 | Object { 617 | "isOptional": false, 618 | "name": "b", 619 | "tags": Array [ 620 | Object { 621 | "name": "zh", 622 | "value": "属性 B", 623 | }, 624 | Object { 625 | "name": "en", 626 | "value": "BProps", 627 | }, 628 | ], 629 | "type": "[BlinkProps](blink#Blink)", 630 | }, 631 | Object { 632 | "isOptional": false, 633 | "name": "unionBlink", 634 | "tags": Array [ 635 | Object { 636 | "name": "zh", 637 | "value": "属性 baseB", 638 | }, 639 | Object { 640 | "name": "en", 641 | "value": "baseBlink", 642 | }, 643 | ], 644 | "type": "[UnionBlinkProps](#UnionBlinkProps)", 645 | }, 646 | Object { 647 | "isOptional": true, 648 | "name": "action", 649 | "tags": Array [ 650 | Object { 651 | "name": "zh", 652 | "value": "操作项", 653 | }, 654 | Object { 655 | "name": "en", 656 | "value": "action", 657 | }, 658 | ], 659 | "type": "ReactNode", 660 | }, 661 | Object { 662 | "isOptional": true, 663 | "name": "owner", 664 | "tags": Array [ 665 | Object { 666 | "name": "zh", 667 | "value": "负责人", 668 | }, 669 | Object { 670 | "name": "en", 671 | "value": "owner", 672 | }, 673 | ], 674 | "type": "Partial<[Owner](#Owner)>", 675 | }, 676 | Object { 677 | "isOptional": true, 678 | "name": "size", 679 | "tags": Array [ 680 | Object { 681 | "name": "zh", 682 | "value": "尺寸", 683 | }, 684 | Object { 685 | "name": "en", 686 | "value": "size", 687 | }, 688 | ], 689 | "type": "[Size](#Size)", 690 | }, 691 | Object { 692 | "isOptional": true, 693 | "name": "columns", 694 | "tags": Array [ 695 | Object { 696 | "name": "zh", 697 | "value": "列配置", 698 | }, 699 | Object { 700 | "name": "en", 701 | "value": "column settings", 702 | }, 703 | Object { 704 | "name": "defaultValue", 705 | "value": "{}", 706 | }, 707 | ], 708 | "type": "[ColProps](#ColProps)", 709 | }, 710 | Object { 711 | "isOptional": false, 712 | "name": "sorter", 713 | "tags": Array [ 714 | Object { 715 | "name": "zh", 716 | "value": "排序", 717 | }, 718 | Object { 719 | "name": "en", 720 | "value": "sorter", 721 | }, 722 | ], 723 | "type": "(a: [SorterProps](#SorterProps), b: [SorterProps](#SorterProps)) => Promise<void>", 724 | }, 725 | Object { 726 | "isOptional": true, 727 | "name": "direction", 728 | "tags": Array [ 729 | Object { 730 | "name": "zh", 731 | "value": "方向", 732 | }, 733 | Object { 734 | "name": "en", 735 | "value": "direction", 736 | }, 737 | ], 738 | "type": "[Direction](#Direction)", 739 | }, 740 | Object { 741 | "isOptional": false, 742 | "name": "option", 743 | "tags": Array [ 744 | Object { 745 | "name": "zh", 746 | "value": "选项", 747 | }, 748 | Object { 749 | "name": "en", 750 | "value": "option", 751 | }, 752 | ], 753 | "type": "[OptionInfo](#OptionInfo)", 754 | }, 755 | ], 756 | "tags": Array [ 757 | Object { 758 | "name": "title", 759 | "value": "A", 760 | }, 761 | Object { 762 | "name": "zh", 763 | "value": "这是接口描述。", 764 | }, 765 | Object { 766 | "name": "en", 767 | "value": "This is interface description.", 768 | }, 769 | ], 770 | }, 771 | "ColProps": Object { 772 | "data": "export interface ColProps { 773 | // width of the column 774 | width?: number; 775 | name?: string; 776 | onColumnCallback: (param: onColumnCallbackProps) => number; 777 | } 778 | ", 779 | "isNestedType": true, 780 | "tags": Array [ 781 | Object { 782 | "name": "title", 783 | "value": "ColProps", 784 | }, 785 | ], 786 | }, 787 | "Direction": Object { 788 | "data": "enum Direction { 789 | LEFT, 790 | RIGHT, 791 | UP, 792 | DOWN 793 | } 794 | ", 795 | "isNestedType": true, 796 | "tags": Array [ 797 | Object { 798 | "name": "title", 799 | "value": "Direction", 800 | }, 801 | ], 802 | }, 803 | "InnerMethodsReturnType": Object { 804 | "data": "interface InnerMethodsReturnType { 805 | text: string; 806 | } 807 | ", 808 | "isNestedType": true, 809 | "tags": Array [ 810 | Object { 811 | "name": "title", 812 | "value": "InnerMethodsReturnType", 813 | }, 814 | ], 815 | }, 816 | "OptionInfo": Object { 817 | "data": "export interface OptionInfo extends PropsWithChildren { 818 | valid: boolean; 819 | index: number; 820 | origin: 'children' | 'options' | 'userCreatedOptions' | 'userCreatingOption'; 821 | } 822 | ", 823 | "isNestedType": true, 824 | "tags": Array [ 825 | Object { 826 | "name": "title", 827 | "value": "OptionInfo", 828 | }, 829 | ], 830 | }, 831 | "Owner": Object { 832 | "data": "interface Owner { 833 | name: string; 834 | age: number; 835 | } 836 | ", 837 | "isNestedType": true, 838 | "tags": Array [ 839 | Object { 840 | "name": "title", 841 | "value": "Owner", 842 | }, 843 | ], 844 | }, 845 | "Size": Object { 846 | "data": "export type Size = number | string | SizeObject; 847 | ", 848 | "isNestedType": true, 849 | "tags": Array [ 850 | Object { 851 | "name": "title", 852 | "value": "Size", 853 | }, 854 | ], 855 | }, 856 | "SizeObject": Object { 857 | "data": "type SizeObject = { 858 | [key in string]: number; 859 | }; 860 | ", 861 | "isNestedType": true, 862 | "tags": Array [ 863 | Object { 864 | "name": "title", 865 | "value": "SizeObject", 866 | }, 867 | ], 868 | }, 869 | "SorterProps": Object { 870 | "data": "interface SorterProps { 871 | counter: number; 872 | } 873 | ", 874 | "isNestedType": true, 875 | "tags": Array [ 876 | Object { 877 | "name": "title", 878 | "value": "SorterProps", 879 | }, 880 | ], 881 | }, 882 | "TreeDataType": Object { 883 | "data": "export type TreeDataType = { 884 | key?: string; 885 | _index?: number; 886 | children: TreeDataType[]; 887 | loadMore: (data: TreeDataType) => void; 888 | }; 889 | ", 890 | "isNestedType": true, 891 | "tags": Array [ 892 | Object { 893 | "name": "title", 894 | "value": "TreeDataType", 895 | }, 896 | ], 897 | }, 898 | "TreeSelectDataType": Object { 899 | "data": "export type TreeSelectDataType = TreeDataType & { 900 | getInnerMethods: (inner: boolean) => InnerMethodsReturnType; 901 | }; 902 | ", 903 | "isNestedType": true, 904 | "tags": Array [ 905 | Object { 906 | "name": "title", 907 | "value": "TreeSelectDataType", 908 | }, 909 | ], 910 | }, 911 | "UnionBlinkProps": Object { 912 | "data": "type UnionBlinkProps = ClinkProps & BlinkProps; 913 | ", 914 | "isNestedType": true, 915 | "tags": Array [ 916 | Object { 917 | "name": "title", 918 | "value": "UnionBlinkProps", 919 | }, 920 | ], 921 | }, 922 | "onColumnCallbackProps": Object { 923 | "data": "interface onColumnCallbackProps { 924 | // index of column 925 | index: string; 926 | position: { 927 | x: number; 928 | y: number; 929 | }; 930 | } 931 | ", 932 | "isNestedType": true, 933 | "tags": Array [ 934 | Object { 935 | "name": "title", 936 | "value": "onColumnCallbackProps", 937 | }, 938 | ], 939 | }, 940 | } 941 | `; 942 | 943 | exports[`generate sort property 1`] = ` 944 | Object { 945 | "Button": Object { 946 | "data": Array [ 947 | Object { 948 | "isOptional": true, 949 | "name": "size", 950 | "tags": Array [ 951 | Object { 952 | "name": "zh", 953 | "value": "尺寸", 954 | }, 955 | Object { 956 | "name": "en", 957 | "value": "Size", 958 | }, 959 | Object { 960 | "name": "defaultValue", 961 | "value": "default", 962 | }, 963 | ], 964 | "type": "'mini' \\\\| 'large' \\\\| 'default'", 965 | }, 966 | Object { 967 | "isOptional": true, 968 | "name": "readonly", 969 | "tags": Array [ 970 | Object { 971 | "name": "zh", 972 | "value": "只读", 973 | }, 974 | Object { 975 | "name": "en", 976 | "value": "Readonly", 977 | }, 978 | ], 979 | "type": "boolean", 980 | }, 981 | Object { 982 | "isOptional": true, 983 | "name": "disabled", 984 | "tags": Array [ 985 | Object { 986 | "name": "zh", 987 | "value": "禁用", 988 | }, 989 | Object { 990 | "name": "en", 991 | "value": "Disabled", 992 | }, 993 | ], 994 | "type": "boolean", 995 | }, 996 | Object { 997 | "isOptional": true, 998 | "name": "tabIndex", 999 | "tags": Array [ 1000 | Object { 1001 | "name": "zh", 1002 | "value": "TabIndex", 1003 | }, 1004 | Object { 1005 | "name": "en", 1006 | "value": "TabIndex", 1007 | }, 1008 | ], 1009 | "type": "number", 1010 | }, 1011 | Object { 1012 | "isOptional": true, 1013 | "name": "color", 1014 | "tags": Array [ 1015 | Object { 1016 | "name": "zh", 1017 | "value": "颜色", 1018 | }, 1019 | Object { 1020 | "name": "en", 1021 | "value": "Color", 1022 | }, 1023 | Object { 1024 | "name": "version", 1025 | "value": "1.2.0", 1026 | }, 1027 | ], 1028 | "type": "string", 1029 | }, 1030 | Object { 1031 | "isOptional": true, 1032 | "name": "renderText", 1033 | "tags": Array [ 1034 | Object { 1035 | "name": "zh", 1036 | "value": "自定义文本渲染", 1037 | }, 1038 | Object { 1039 | "name": "en", 1040 | "value": "Custom text render", 1041 | }, 1042 | ], 1043 | "type": "() => string", 1044 | }, 1045 | ], 1046 | "tags": Array [ 1047 | Object { 1048 | "name": "title", 1049 | "value": "Button", 1050 | }, 1051 | Object { 1052 | "name": "zh", 1053 | "value": "按钮", 1054 | }, 1055 | Object { 1056 | "name": "en", 1057 | "value": "Button", 1058 | }, 1059 | ], 1060 | }, 1061 | } 1062 | `; 1063 | 1064 | exports[`generateMarkdown basic 1`] = ` 1065 | Object { 1066 | "A": "### A 1067 | 1068 | 这是接口描述。 1069 | 1070 | |参数名|描述|类型|默认值|版本| 1071 | |---|---|---|---|---| 1072 | |action|自定义操作项|ReactNode |\`-\`|2.15.0| 1073 | |closable|是否可以关闭|[InnerProps](#InnerProps) |\`{}\`|-|", 1074 | "InnerProps": "### InnerProps 1075 | 1076 | \`\`\`js 1077 | export interface InnerProps { 1078 | /** 1079 | * @zh 位置 1080 | * @en position 1081 | */ 1082 | position?: string; 1083 | /** 1084 | * @zh 尺寸 1085 | * @en Size 1086 | */ 1087 | size?: string; 1088 | } 1089 | \`\`\`", 1090 | } 1091 | `; 1092 | 1093 | exports[`generateMarkdown basic 2`] = ` 1094 | Object { 1095 | "A": "### A 1096 | 1097 | This is interface description. 1098 | 1099 | |Property|Description|Type|DefaultValue|Version| 1100 | |---|---|---|---|---| 1101 | |action|this is action|ReactNode |\`-\`|2.15.0| 1102 | |closable|Whether Alert can be closed|[InnerProps](#InnerProps) |\`{}\`|-|", 1103 | "InnerProps": "### InnerProps 1104 | 1105 | \`\`\`js 1106 | export interface InnerProps { 1107 | /** 1108 | * @zh 位置 1109 | * @en position 1110 | */ 1111 | position?: string; 1112 | /** 1113 | * @zh 尺寸 1114 | * @en Size 1115 | */ 1116 | size?: string; 1117 | } 1118 | \`\`\`", 1119 | } 1120 | `; 1121 | 1122 | exports[`generateMarkdown custom project 1`] = ` 1123 | Object { 1124 | "Add": "### Add 1125 | 1126 | 两数相加 1127 | 1128 | #### Since 1129 | 1.0.0 1130 | 1131 | #### Returns 1132 | \`number\`: Sum of two numbers 1133 | 1134 | #### Arguments 1135 | |参数名|描述|类型|默认值| 1136 | |---|---|---|---| 1137 | |a|被加数|number **(必填)**|-| 1138 | |b|加数|number |\`5\`|", 1139 | "ButtonType": "### ButtonType 1140 | 1141 | \`\`\`js 1142 | type ButtonType = { 1143 | size?: 'mini' | 'large' | 'default'; 1144 | color?: string; 1145 | }; 1146 | \`\`\`", 1147 | "Plus": "### Plus 1148 | 1149 | 两数相乘 1150 | 1151 | #### Returns 1152 | \`number\`: Product of two numbers 1153 | 1154 | #### Arguments 1155 | |参数名|描述|类型|默认值| 1156 | |---|---|---|---| 1157 | |a|被乘数|number **(必填)**|-| 1158 | |b|乘数|number **(必填)**|\`1\`|", 1159 | "function with nest type": "### function with nest type 1160 | 1161 | #### Returns 1162 | \`any\` 1163 | 1164 | #### Arguments 1165 | |参数名|描述|类型|默认值| 1166 | |---|---|---|---| 1167 | |buttonProps|-|Partial<[ButtonType](#ButtonType)> **(必填)**|-|", 1168 | } 1169 | `; 1170 | 1171 | exports[`generateMarkdown custom project 2`] = ` 1172 | Array [ 1173 | "### Plus 1174 | 1175 | Multiply two numbers 1176 | 1177 | #### Returns 1178 | \`number\`: Product of two numbers 1179 | 1180 | #### Arguments 1181 | |Argument|Description|Type|DefaultValue| 1182 | |---|---|---|---| 1183 | |a|First number|number **(Required)**|-| 1184 | |b|Second number|number **(Required)**|\`1\`|", 1185 | "### Add 1186 | 1187 | Add two numbers 1188 | 1189 | #### Since 1190 | 1.0.0 1191 | 1192 | #### Returns 1193 | \`number\`: Sum of two numbers 1194 | 1195 | #### Arguments 1196 | |Argument|Description|Type|DefaultValue| 1197 | |---|---|---|---| 1198 | |a|First number|number **(Required)**|-| 1199 | |b|Second number|number |\`5\`|", 1200 | "### function with nest type 1201 | 1202 | #### Returns 1203 | \`any\` 1204 | 1205 | #### Arguments 1206 | |Argument|Description|Type|DefaultValue| 1207 | |---|---|---|---| 1208 | |buttonProps|-|Partial<[ButtonType](#ButtonType)> **(Required)**|-|", 1209 | "### ButtonType 1210 | 1211 | \`\`\`js 1212 | type ButtonType = { 1213 | size?: 'mini' | 'large' | 'default'; 1214 | color?: string; 1215 | }; 1216 | \`\`\`", 1217 | ] 1218 | `; 1219 | 1220 | exports[`generateMarkdown escape 1`] = ` 1221 | Object { 1222 | "A": "### A 1223 | 1224 | 这是接口描述。 1225 | 1226 | |参数名|描述|类型|默认值| 1227 | |---|---|---|---| 1228 | |size|尺寸|'mini' | 'large' | 'default' |\`-\`| 1229 | |fetchData|获取数据函数|() => Promise |\`-\`|", 1230 | } 1231 | `; 1232 | 1233 | exports[`generateMarkdown escape 2`] = ` 1234 | Object { 1235 | "A": "### A 1236 | 1237 | This is interface description. 1238 | 1239 | |Property|Description|Type|DefaultValue| 1240 | |---|---|---|---| 1241 | |size|size|'mini' | 'large' | 'default' |\`-\`| 1242 | |fetchData|Function to fetch data|() => Promise |\`-\`|", 1243 | } 1244 | `; 1245 | 1246 | exports[`generateMarkdown extends 1`] = ` 1247 | Object { 1248 | "Alert": "### Alert 1249 | 1250 | 向用户显示警告的信息时,通过警告提示,展现需要关注的信息。 1251 | 1252 | |参数名|描述|类型|默认值|版本| 1253 | |---|---|---|---|---| 1254 | |action|自定义操作项|ReactElement **(必填)**|\`-\`|2.15.0| 1255 | |closable|是否可以关闭|{a: boolean;b: string;} |\`false\`|-| 1256 | |callback|回调参数|(option: [Option](#Option)) => void |\`-\`|-| 1257 | |bb|BB|boolean |\`-\`|-| 1258 | |animation|动画|boolean \\\\| string |\`-\`|-|", 1259 | "Option": "### Option 1260 | 1261 | \`\`\`js 1262 | export type Option = { 1263 | a: string; 1264 | b: string; 1265 | }; 1266 | \`\`\`", 1267 | "Test": "### Test 1268 | 1269 | |参数名|描述|类型|默认值|版本| 1270 | |---|---|---|---|---| 1271 | |position|位置|string |\`-\`|-| 1272 | |size|尺寸|string |\`-\`|-| 1273 | |action|自定义操作项|ReactElement **(必填)**|\`-\`|2.15.0| 1274 | |closable|是否可以关闭|{a: boolean;b: string;} |\`false\`|-| 1275 | |callback|回调参数|(option: [Option](#Option)) => void |\`-\`|-| 1276 | |bb|BB|boolean |\`-\`|-| 1277 | |animation|动画|boolean \\\\| string |\`-\`|-|", 1278 | } 1279 | `; 1280 | 1281 | exports[`generateMarkdown extends 2`] = ` 1282 | Object { 1283 | "Alert": "### Alert 1284 | 1285 | Display warning information to the user. the Alert is used to display the information that needs attention. 1286 | 1287 | |Property|Description|Type|DefaultValue|Version| 1288 | |---|---|---|---|---| 1289 | |action|this is action|ReactElement **(Required)**|\`-\`|2.15.0| 1290 | |closable|Whether Alert can be closed|{a: boolean;b: string;} |\`false\`|-| 1291 | |callback|Callback function|(option: [Option](#Option)) => void |\`-\`|-| 1292 | |bb|bb|boolean |\`-\`|-| 1293 | |animation|animation|boolean \\\\| string |\`-\`|-|", 1294 | "Option": "### Option 1295 | 1296 | \`\`\`js 1297 | export type Option = { 1298 | a: string; 1299 | b: string; 1300 | }; 1301 | \`\`\`", 1302 | "Test": "### Test 1303 | 1304 | |Property|Description|Type|DefaultValue|Version| 1305 | |---|---|---|---|---| 1306 | |position|position|string |\`-\`|-| 1307 | |size|Size|string |\`-\`|-| 1308 | |action|this is action|ReactElement **(Required)**|\`-\`|2.15.0| 1309 | |closable|Whether Alert can be closed|{a: boolean;b: string;} |\`false\`|-| 1310 | |callback|Callback function|(option: [Option](#Option)) => void |\`-\`|-| 1311 | |bb|bb|boolean |\`-\`|-| 1312 | |animation|animation|boolean \\\\| string |\`-\`|-|", 1313 | } 1314 | `; 1315 | 1316 | exports[`generateMarkdown function type 1`] = ` 1317 | Object { 1318 | "Add": "### Add 1319 | 1320 | 两数相加 1321 | 1322 | #### Since 1323 | 1.0.0 1324 | 1325 | #### Returns 1326 | \`number\`: Sum of two numbers 1327 | 1328 | #### Arguments 1329 | |参数名|描述|类型|默认值| 1330 | |---|---|---|---| 1331 | |a|被加数|number **(必填)**|-| 1332 | |b|加数|number |\`5\`|", 1333 | "ButtonType": "### ButtonType 1334 | 1335 | \`\`\`js 1336 | type ButtonType = { 1337 | size?: 'mini' | 'large' | 'default'; 1338 | color?: string; 1339 | }; 1340 | \`\`\`", 1341 | "Plus": "### Plus 1342 | 1343 | 两数相乘 1344 | 1345 | #### Returns 1346 | \`number\`: Product of two numbers 1347 | 1348 | #### Arguments 1349 | |参数名|描述|类型|默认值| 1350 | |---|---|---|---| 1351 | |a|被乘数|number **(必填)**|-| 1352 | |b|乘数|number **(必填)**|\`1\`|", 1353 | "function with nest type": "### function with nest type 1354 | 1355 | #### Returns 1356 | \`any\` 1357 | 1358 | #### Arguments 1359 | |参数名|描述|类型|默认值| 1360 | |---|---|---|---| 1361 | |buttonProps|-|Partial<[ButtonType](#ButtonType)> **(必填)**|-|", 1362 | } 1363 | `; 1364 | 1365 | exports[`generateMarkdown function type 2`] = ` 1366 | Array [ 1367 | "### Plus 1368 | 1369 | Multiply two numbers 1370 | 1371 | #### Returns 1372 | \`number\`: Product of two numbers 1373 | 1374 | #### Arguments 1375 | |Argument|Description|Type|DefaultValue| 1376 | |---|---|---|---| 1377 | |a|First number|number **(Required)**|-| 1378 | |b|Second number|number **(Required)**|\`1\`|", 1379 | "### Add 1380 | 1381 | Add two numbers 1382 | 1383 | #### Since 1384 | 1.0.0 1385 | 1386 | #### Returns 1387 | \`number\`: Sum of two numbers 1388 | 1389 | #### Arguments 1390 | |Argument|Description|Type|DefaultValue| 1391 | |---|---|---|---| 1392 | |a|First number|number **(Required)**|-| 1393 | |b|Second number|number |\`5\`|", 1394 | "### function with nest type 1395 | 1396 | #### Returns 1397 | \`any\` 1398 | 1399 | #### Arguments 1400 | |Argument|Description|Type|DefaultValue| 1401 | |---|---|---|---| 1402 | |buttonProps|-|Partial<[ButtonType](#ButtonType)> **(Required)**|-|", 1403 | "### ButtonType 1404 | 1405 | \`\`\`js 1406 | type ButtonType = { 1407 | size?: 'mini' | 'large' | 'default'; 1408 | color?: string; 1409 | }; 1410 | \`\`\`", 1411 | ] 1412 | `; 1413 | 1414 | exports[`generateMarkdown nested types 1`] = ` 1415 | Object { 1416 | "A": "### A 1417 | 1418 | 这是接口描述。 1419 | 1420 | |参数名|描述|类型|默认值| 1421 | |---|---|---|---| 1422 | |treeSelectDataType|树选择数据属性|[TreeSelectDataType](#TreeSelectDataType) |\`-\`| 1423 | |onClick|点击回调|(num: number) => void **(必填)**|\`-\`| 1424 | |b|属性 B|[BlinkProps](blink#Blink) **(必填)**|\`-\`| 1425 | |unionBlink|属性 baseB|[UnionBlinkProps](#UnionBlinkProps) **(必填)**|\`-\`| 1426 | |action|操作项|ReactNode |\`-\`| 1427 | |owner|负责人|Partial<[Owner](#Owner)> |\`-\`| 1428 | |size|尺寸|[Size](#Size) |\`-\`| 1429 | |columns|列配置|[ColProps](#ColProps) |\`{}\`| 1430 | |sorter|排序|(a: [SorterProps](#SorterProps), b: [SorterProps](#SorterProps)) => Promise<void> **(必填)**|\`-\`| 1431 | |direction|方向|[Direction](#Direction) |\`-\`| 1432 | |option|选项|[OptionInfo](#OptionInfo) **(必填)**|\`-\`|", 1433 | "ColProps": "### ColProps 1434 | 1435 | \`\`\`js 1436 | export interface ColProps { 1437 | // width of the column 1438 | width?: number; 1439 | name?: string; 1440 | onColumnCallback: (param: onColumnCallbackProps) => number; 1441 | } 1442 | \`\`\`", 1443 | "Direction": "### Direction 1444 | 1445 | \`\`\`js 1446 | enum Direction { 1447 | LEFT, 1448 | RIGHT, 1449 | UP, 1450 | DOWN 1451 | } 1452 | \`\`\`", 1453 | "InnerMethodsReturnType": "### InnerMethodsReturnType 1454 | 1455 | \`\`\`js 1456 | interface InnerMethodsReturnType { 1457 | text: string; 1458 | } 1459 | \`\`\`", 1460 | "OptionInfo": "### OptionInfo 1461 | 1462 | \`\`\`js 1463 | export interface OptionInfo extends PropsWithChildren { 1464 | valid: boolean; 1465 | index: number; 1466 | origin: 'children' | 'options' | 'userCreatedOptions' | 'userCreatingOption'; 1467 | } 1468 | \`\`\`", 1469 | "Owner": "### Owner 1470 | 1471 | \`\`\`js 1472 | interface Owner { 1473 | name: string; 1474 | age: number; 1475 | } 1476 | \`\`\`", 1477 | "Size": "### Size 1478 | 1479 | \`\`\`js 1480 | export type Size = number | string | SizeObject; 1481 | \`\`\`", 1482 | "SizeObject": "### SizeObject 1483 | 1484 | \`\`\`js 1485 | type SizeObject = { 1486 | [key in string]: number; 1487 | }; 1488 | \`\`\`", 1489 | "SorterProps": "### SorterProps 1490 | 1491 | \`\`\`js 1492 | interface SorterProps { 1493 | counter: number; 1494 | } 1495 | \`\`\`", 1496 | "TreeDataType": "### TreeDataType 1497 | 1498 | \`\`\`js 1499 | export type TreeDataType = { 1500 | key?: string; 1501 | _index?: number; 1502 | children: TreeDataType[]; 1503 | loadMore: (data: TreeDataType) => void; 1504 | }; 1505 | \`\`\`", 1506 | "TreeSelectDataType": "### TreeSelectDataType 1507 | 1508 | \`\`\`js 1509 | export type TreeSelectDataType = TreeDataType & { 1510 | getInnerMethods: (inner: boolean) => InnerMethodsReturnType; 1511 | }; 1512 | \`\`\`", 1513 | "UnionBlinkProps": "### UnionBlinkProps 1514 | 1515 | \`\`\`js 1516 | type UnionBlinkProps = ClinkProps & BlinkProps; 1517 | \`\`\`", 1518 | "onColumnCallbackProps": "### onColumnCallbackProps 1519 | 1520 | \`\`\`js 1521 | interface onColumnCallbackProps { 1522 | // index of column 1523 | index: string; 1524 | position: { 1525 | x: number; 1526 | y: number; 1527 | }; 1528 | } 1529 | \`\`\`", 1530 | } 1531 | `; 1532 | 1533 | exports[`generateMarkdown nested types 2`] = ` 1534 | Object { 1535 | "A": "### A 1536 | 1537 | This is interface description. 1538 | 1539 | |Property|Description|Type|DefaultValue| 1540 | |---|---|---|---| 1541 | |treeSelectDataType|treeSelectDataType|[TreeSelectDataType](#TreeSelectDataType) |\`-\`| 1542 | |onClick|onClick|(num: number) => void **(Required)**|\`-\`| 1543 | |b|BProps|[BlinkProps](blink#Blink) **(Required)**|\`-\`| 1544 | |unionBlink|baseBlink|[UnionBlinkProps](#UnionBlinkProps) **(Required)**|\`-\`| 1545 | |action|action|ReactNode |\`-\`| 1546 | |owner|owner|Partial<[Owner](#Owner)> |\`-\`| 1547 | |size|size|[Size](#Size) |\`-\`| 1548 | |columns|column settings|[ColProps](#ColProps) |\`{}\`| 1549 | |sorter|sorter|(a: [SorterProps](#SorterProps), b: [SorterProps](#SorterProps)) => Promise<void> **(Required)**|\`-\`| 1550 | |direction|direction|[Direction](#Direction) |\`-\`| 1551 | |option|option|[OptionInfo](#OptionInfo) **(Required)**|\`-\`|", 1552 | "ColProps": "### ColProps 1553 | 1554 | \`\`\`js 1555 | export interface ColProps { 1556 | // width of the column 1557 | width?: number; 1558 | name?: string; 1559 | onColumnCallback: (param: onColumnCallbackProps) => number; 1560 | } 1561 | \`\`\`", 1562 | "Direction": "### Direction 1563 | 1564 | \`\`\`js 1565 | enum Direction { 1566 | LEFT, 1567 | RIGHT, 1568 | UP, 1569 | DOWN 1570 | } 1571 | \`\`\`", 1572 | "InnerMethodsReturnType": "### InnerMethodsReturnType 1573 | 1574 | \`\`\`js 1575 | interface InnerMethodsReturnType { 1576 | text: string; 1577 | } 1578 | \`\`\`", 1579 | "OptionInfo": "### OptionInfo 1580 | 1581 | \`\`\`js 1582 | export interface OptionInfo extends PropsWithChildren { 1583 | valid: boolean; 1584 | index: number; 1585 | origin: 'children' | 'options' | 'userCreatedOptions' | 'userCreatingOption'; 1586 | } 1587 | \`\`\`", 1588 | "Owner": "### Owner 1589 | 1590 | \`\`\`js 1591 | interface Owner { 1592 | name: string; 1593 | age: number; 1594 | } 1595 | \`\`\`", 1596 | "Size": "### Size 1597 | 1598 | \`\`\`js 1599 | export type Size = number | string | SizeObject; 1600 | \`\`\`", 1601 | "SizeObject": "### SizeObject 1602 | 1603 | \`\`\`js 1604 | type SizeObject = { 1605 | [key in string]: number; 1606 | }; 1607 | \`\`\`", 1608 | "SorterProps": "### SorterProps 1609 | 1610 | \`\`\`js 1611 | interface SorterProps { 1612 | counter: number; 1613 | } 1614 | \`\`\`", 1615 | "TreeDataType": "### TreeDataType 1616 | 1617 | \`\`\`js 1618 | export type TreeDataType = { 1619 | key?: string; 1620 | _index?: number; 1621 | children: TreeDataType[]; 1622 | loadMore: (data: TreeDataType) => void; 1623 | }; 1624 | \`\`\`", 1625 | "TreeSelectDataType": "### TreeSelectDataType 1626 | 1627 | \`\`\`js 1628 | export type TreeSelectDataType = TreeDataType & { 1629 | getInnerMethods: (inner: boolean) => InnerMethodsReturnType; 1630 | }; 1631 | \`\`\`", 1632 | "UnionBlinkProps": "### UnionBlinkProps 1633 | 1634 | \`\`\`js 1635 | type UnionBlinkProps = ClinkProps & BlinkProps; 1636 | \`\`\`", 1637 | "onColumnCallbackProps": "### onColumnCallbackProps 1638 | 1639 | \`\`\`js 1640 | interface onColumnCallbackProps { 1641 | // index of column 1642 | index: string; 1643 | position: { 1644 | x: number; 1645 | y: number; 1646 | }; 1647 | } 1648 | \`\`\`", 1649 | } 1650 | `; 1651 | --------------------------------------------------------------------------------