Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ts to md #68

Merged
merged 4 commits into from
Nov 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions packages/vantui-doc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
"version": "1.2.2",
"description": "",
"scripts": {
"dev": "node synch.js && npx vant-cli dev",
"dev": "node ./scripts/ts-to-md && npx vant-cli dev",
"test:coverage": "open test/coverage/index.html",
"build": "node synch.js && sh ./build.sh",
"build": "node ./scripts/ts-to-md && sh ./build.sh",
"release": "npx gh-pages -d site",
"docs-ts": "node ./synch.js"
"docs-ts": "node ./scripts/ts-to-md"
},
"author": "sanshao",
"license": "MIT",
Expand All @@ -18,8 +18,11 @@
"devDependencies": {
"@vant/cli": "^3.9.0",
"@vue/compiler-sfc": "^3.0.0",
"ast-to-markdown": "^1.0.0",
"cross-env": "^7.0.3",
"glob": "^7.2.0",
"markdown-to-ast": "^6.0.3",
"prettier": "^2.4.1",
"vue": "^3.0.0"
},
"browserslist": [
Expand Down
137 changes: 137 additions & 0 deletions packages/vantui-doc/scripts/ts-to-md.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
const fs = require('fs')
const path = require('path')
const glob = require('glob')
const parser = require('./utils/ts-parser')
const markdownToAst = require('markdown-to-ast')
const astToMarkdown = require('ast-to-markdown')
const ora = require('../node_modules/ora/index')

const GITHUB_TYPESHS = `https://github.com/AntmJS/vantui/tree/main/packages/vantui/types`
const READMES_PATH = `${path.resolve(process.cwd(), './src/**/README.md')}`
const spinner = ora(`文档 API 同步开始`)

glob(READMES_PATH,
function (err, path_) {
path_.map((item) => {
const componentName = item.split('/').reverse()[1]
let content = fs.readFileSync(item, 'utf-8')
spinner.start(`${componentName}文档 API 同步中...`)

if (content) {
content = removeOldTable(content)
}

if (
fs.existsSync(`../vantui/types/${componentName}.d.ts`) &&
componentName !== 'index'
) {
let tsInfo = fs.readFileSync(
`../vantui/types/${componentName}.d.ts`,
'utf-8',
)
const res = parser(tsInfo)

fs.writeFileSync(item, content + createMd(res, componentName))
spinner.stop(`${componentName}文档 API 同步完成`)
}
})

spinner.succeed(`文档 API 同步完成`)
},
)

function createMd(obj, compName) {
let mdRes = ``
for (const Dkey in obj) {
const item = obj[Dkey]
if (!Object.keys(item).length) continue
mdRes +=
`### ${item['title'] && typeof item['title'] === 'string'
? item['title']
: Dkey
}` +
` [[详情]](${GITHUB_TYPESHS}/${compName}.d.ts)
`
mdRes += `${item['description'] || ''}
`
let header = `| 参数 | 说明 | 类型 | 默认值 | 必填 |
| --- | --- | --- | --- | --- |
`
let key = ['self', 'description', 'value', 'default', 'require']
if (!Dkey.includes('Props')) {
header = `| 参数 | 说明 | 类型 |
| --- | --- | --- |
`
key = ['self', 'description', 'value']
}

if (Dkey.includes('Instance')) {
header = `| 方法 | 说明 | 类型 |
| --- | --- | --- |
`
key = ['self', 'description', 'value']
}
mdRes += header
Object.keys(item).map((_key) => {
if (typeof item[_key] === 'object' && item[_key]) {
key.forEach((k) => {
if (k === 'self') {
mdRes += `| ${_key} `
} else {
let con = item[_key][k]
if (k === 'value') {
con = con
.replace(/[\n]+/g, '<br/>')
.replace(/\s(?!=\/)/g, '&nbsp;')
}
if (con && k === 'value') {
con = `_${con}_`
} else if (con && k === 'require') {
con = '`' + con + '`'
} else if (!con) {
con = `-`
}
mdRes += `| ` + `${con} `.replace(/\|/g, '&brvbar;')
}
})

mdRes += `|
`
}
})
mdRes += `\n`
}
return mdRes
}

function removeOldTable(md) {
let ast = markdownToAst.parse(md)
let shouldRmoveIndex
let firstTableIndex
ast.children.forEach((as, index) => {
if (as.type === 'Table' && firstTableIndex === undefined) {
firstTableIndex = index
}
})

for (let index = firstTableIndex; index >= 0; index--) {
if (ast.children[index].type === 'Header') {
shouldRmoveIndex = index
break
}
}
// 处理第一版ts的展示
if (shouldRmoveIndex === undefined) {
ast.children.forEach((as, index) => {
if (as.type === 'Header' &&
as.raw === '### TS信息' &&
shouldRmoveIndex === undefined
) {
shouldRmoveIndex = index
}
})
}

ast.children = ast.children.slice(0, shouldRmoveIndex)
return astToMarkdown(ast)
}
203 changes: 203 additions & 0 deletions packages/vantui-doc/scripts/utils/ts-parser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
const Prettier = require('prettier')
/** 收集d.ts暴露第一层级的属性和注释 */
module.exports = function main(tsStr) {
tsStr = Prettier.format(tsStr, { semi: false, parser: 'typescript' })
const { TOKENS } = getAllTokens(tsStr)
const res = parseTokens(TOKENS)

return res
}

function getAllTokens(str) {
let TOKENS = []
let token = ''
for (let i = 0; i < str.length; i++) {
let curItem = str.charAt(i)
if (curItem === ' ' || curItem === '\n') {
if (token) TOKENS.push(token.replace(/[\s]+/, ''))
TOKENS.push(curItem)
token = ''
} else {
token += curItem
}
}

TOKENS = TOKENS.slice(TOKENS.indexOf('export'), TOKENS.length)
TOKENS = TOKENS.slice(0, TOKENS.indexOf('declare'))

return { TOKENS }
}

function parseTokens(TOKENS) {
const res = {}
let value = ''
let count = 0
let exportName = ''
let attrName = ''
let attrNamePrev = ''
let InBrackets = 0
let commentsPenddingStr = ''
let commentsStr = ''
let STATUS = {
EXPORT_1: 'EXPORT_WHAT',
EXPORT_2: 'EXPORT_GET_WHAT',
EXPORT_3: 'EXPORTING',
}
let status = STATUS.EXPORT_1

for (let i = 0; i < TOKENS.length; i++) {
let curToken = TOKENS[i]
let nextToken = getRealTokenNext(1, TOKENS.slice(i + 1, TOKENS.length - 1))
let next2Token = getRealTokenNext(2, TOKENS.slice(i + 1, TOKENS.length - 1))

if (curToken === '/**' || commentsPenddingStr) {
commentsPenddingStr += curToken
}

if (curToken === 'export' && ['interface', 'type'].includes(nextToken)) {
if (!res[next2Token]) {
exportName = next2Token
res[exportName] = {
...parseComments(commentsStr),
}
commentsStr = ''
}
status = STATUS.EXPORT_2
}

if (curToken.includes('}') || curToken.includes('})')) {
count--
if (count === 0) {
status = STATUS.EXPORT_1
if (attrName && exportName) res[exportName][attrName]['value'] = formatTsValue(value)
value = ''
attrNamePrev = ''
attrName = ''
InBrackets = 0
exportName = ''
}
}

if (exportName && count !== 0) {
if (
curToken.includes(':') &&
count < 2 &&
!curToken.includes('(') &&
InBrackets === 0
) {
attrNamePrev = attrName
attrName = curToken.replace(':', '').replace('?', '')
if (!res[exportName][attrName]) {
res[exportName][attrName] = {
...parseComments(commentsStr),
}
commentsStr = ''
}
if (curToken.includes('?')) {
res[exportName][attrName]['require'] = 'false'
} else {
res[exportName][attrName]['require'] = 'true'
}
if (attrNamePrev) {
if (!res[exportName]) res[exportName] = {}
if (!res[exportName][attrNamePrev]) res[exportName][attrNamePrev] = {}
res[exportName][attrNamePrev]['value'] = formatTsValue(value)
value = ''
}
} else if (status === STATUS.EXPORT_3 && !commentsPenddingStr) {
value += `${curToken}`
}
}
if (curToken === '*/') {
commentsStr = commentsPenddingStr + curToken
commentsPenddingStr = ''
}
const InBracketsNum1 = countStrNumInToken('(', curToken)
const InBracketsNum2 = countStrNumInToken(')', curToken)

if (InBracketsNum1 > 0) {
InBrackets += InBracketsNum1
}
if (InBracketsNum2) {
InBrackets -= InBracketsNum2
}

if (curToken.includes('{') && status === STATUS.EXPORT_2) {
status = STATUS.EXPORT_3
}

if (curToken.includes('{') || curToken.includes('({')) {
count++
}
}

return res
}

function countStrNumInToken(str, token) {
let res = 0
for (let i = 0; i < token.length; i++) {
if (token.charAt(i) === str) {
res++
}
}

return res
}

function getRealTokenNext(nextNum, lastTokens) {
let res = ''
let next_i = 0
for (let a = 0; a < lastTokens.length; a++) {
if (lastTokens[a] !== ' ' && lastTokens[a] !== '\n') {
next_i++
if (next_i === nextNum) {
res = lastTokens[a]
break
}
}
}

return res
}

function parseComments(comments = '') {
let res = {}
const arr = comments
.split('\n')
.filter((item) => item.includes('@'))
.map((item) => item.replace(/^[\s]+/g, ''))
.map((item) => item.replace('* ', ''))
.map((item) => item.replace('@', ''))
.map((item) => item.replace(/[\s]+/, '##'))

arr.forEach((item) => {
const cons = item.split('##')
res[cons[0]] = cons[1]
})

return res
}

function formatTsValue(tsValue) {
let res = Prettier.format(
`type temp = {
attr:${tsValue}
}
// END`,
{
parser: 'typescript',
semi: false,
printWidth: 48,
},
).replace('type temp = {\n', '')
.replace('attr: ', '')
.replace(
`}
// END`,
'',
)

return res
}

29 changes: 0 additions & 29 deletions packages/vantui-doc/synch.js

This file was deleted.

Loading