diff --git a/package.json b/package.json index da56da7cb9..76d6120b36 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "generate:themes-dev": "node scripts/generate-themes-dev.js", "generate:file:taro": "node scripts/taro/generate-nutui-taro.js", "generate:file:taro:pages": "node scripts/taro/generate-taro-pages.js", + "generate:props_json": "PROPS_JSON=true node scripts/build-comments-to-dts.mjs", "lint": "eslint ./src/packages/*/", "lint:fix": "eslint --fix ./src/packages", "prepare": "husky && npm run generate:file && npm run generate:file:taro && npm run generate:file:taro:pages", diff --git a/scripts/build-comments-to-dts.mjs b/scripts/build-comments-to-dts.mjs index ac54b0a62e..a7d2b03bf2 100644 --- a/scripts/build-comments-to-dts.mjs +++ b/scripts/build-comments-to-dts.mjs @@ -1,25 +1,27 @@ /** - * 向生成的组件类型文件中注入注释 + * 通过在其他脚本 import codeShift 方法,可以向生成的组件类型文件中增加 JSDoc + * 通过npm scripts 触发,可在 src/types 下生成 props.json 文件 */ import * as path from 'path' +import { dirname } from 'path' import fse from 'fs-extra' import j from 'jscodeshift' import markdownit from 'markdown-it' import { fileURLToPath } from 'url' -import { dirname } from 'path' const __filename = fileURLToPath(import.meta.url) const __dirname = dirname(__filename) -const dist = 'release/h5/dist' + +const PROPS_JSON = process.env.PROPS_JSON /** * 通过 cofnig.json 获取所有组件的数据 */ function readAllComponents() { const config = JSON.parse(fse.readFileSync(path.join(__dirname, '../src/config.json')).toString()) - const components = config.nav.reduce(function (accumulator, currentValue) { + const components = config.nav.reduce(function(accumulator, currentValue) { currentValue.packages.forEach((pkg) => { - if (pkg.exclude || pkg.version !== '2.0.0') { + if (pkg.exclude) { return } accumulator.push(pkg) @@ -52,7 +54,7 @@ function extractPropsTable(doc) { token.type == 'heading_open' && token.tag == 'h3' && sources[index + 1].type == 'inline' && - sources[index + 1].content === 'Props' + sources[index + 1].content.toLowerCase() == 'props' ) { const componentName = sources[index - 2].content let startIndex = index + 3 @@ -99,95 +101,91 @@ function markdownTable2Json(table) { return rows } -function addComments(dtsPath, propsTable, componentName) { - const source = fse.readFileSync(dtsPath).toString() - - function findInTable(identifierName) { - const [info] = propsTable.filter( - (t) => t[0].replace(/'/g, '') === identifierName - ) - return info - } - - const transform = (file, api) => { - const j = api.jscodeshift.withParser('ts') - return j(file.source) - .find(j.TSInterfaceDeclaration, { - id: { - name: componentName + 'Props', - type: 'Identifier', - }, - }) - .forEach((path) => { - path.value?.body?.body?.forEach((item) => { - if (!item.key) return - const info = findInTable(item.key.name) - if (!info) return - item['comments'] = [ - j.commentBlock(`*\n* ${info[1]}\n* @default ${info[3]}\n`), - ] - }) - }) - .toSource() - } - const result = transform({ source }, { jscodeshift: j }) - if (result) { - fse.writeFileSync(dtsPath, result) - } -} - -function getDtsPath(key, outDir) { - // Tabs.Tabpane -> tabpane - let name - if (key === 'Tabs.Tabpane') { - name = 'tabpane' - } else { - name = key.toLowerCase().replace('.', '') - } - const file = path.join(__dirname, `../${outDir}/es/packages`, name, name + '.d.ts') - return file -} - -function getComponentName(key) { - // Tabs.Tabpane -> tabpane - let name = key - if (key === 'Tabs.Tabpane') { - name = 'TabPane' - } - return name.replace('.', '') -} - /** * step 1: 从 config.json 中读取组件列表,迭代 * step a: 读取组件的 doc.md 或 doc.taro.md * step b: 提取文档中的 Props 相关的表格,并转换为 JSON 数据 * step c: 添加注释 */ -export function codeShift(env) { +export function codeShift(platform) { const components = readAllComponents() + const componentsProps = {} components.forEach((component) => { const { name } = component const componentDocumentPath = path.join( __dirname, '../src/packages', name.toLowerCase(), - env === 'taro' ? 'doc.taro.md' : 'doc.md' + platform === 'taro' ? 'doc.taro.md' : 'doc.md', ) if (fse.pathExistsSync(componentDocumentPath)) { const tables = extractPropsTable( - readComponentDocument(componentDocumentPath) + readComponentDocument(componentDocumentPath), ) Object.keys(tables).forEach((key) => { - const dtsPath = getDtsPath(key, env !== 'taro' ? dist : dist.replace('h5', env)) - if (fse.pathExistsSync(dtsPath)) { - const table = markdownTable2Json(tables[key]) - addComments(dtsPath, table, getComponentName(key)) - } else { - console.warn(name + ' dts file does not exist') - } + const table = markdownTable2Json(tables[key]) + componentsProps[key.toLowerCase()] = table.reduce((acc, [key, desc, types, defaultValue]) => { + acc[key] = { + desc: desc, + types: types, + defaultValue: defaultValue, + } + return acc + }, {}) }) } else { // console.warn(name + ' document file does not exist') } + if (!PROPS_JSON) { + if (!component.exportEmpty) addJSDoc(componentsProps, name, platform) + } }) + if (PROPS_JSON) { + const jsonContent = JSON.stringify(componentsProps, ' ', 2) + fse.writeFileSync(path.join(__dirname, '../src/types', `props.json`), jsonContent) + } } + +function addJSDoc(propsJson, componentName, platform) { + const transform = (file, api) => { + const j = api.jscodeshift.withParser('ts') + const ast = j(file.source) + function addComment(item) { + if (!item.key) return + const description = propsJson[componentName.toLowerCase()][item.key.name] + if (!description) return + item['comments'] = [ + j.commentBlock(`*\n* ${description['desc']}\n`), + ] + } + ast + .find(j.TSTypeAliasDeclaration).forEach((path) => { + const annotationTypes = path.value.typeAnnotation.types + if (!annotationTypes) return + const typeLiteral = annotationTypes[annotationTypes.length - 1] + if (!typeLiteral.members) return + typeLiteral.members.forEach((item) => { + addComment(item) + }) + }) + ast.find(j.TSInterfaceDeclaration, { + id: { + name: `Base${componentName}`, + type: 'Identifier', + }, + }) + .forEach((path) => { + path.value?.body?.body?.forEach((item) => { + addComment(item) + }) + }) + + return ast.toSource() + } + const baseType = path.join(__dirname, `../release/${platform || 'h5'}/dist/es/types/spec/${componentName.toLowerCase()}/base.d.ts`) + const source = fse.readFileSync(baseType, { encoding: 'utf8' }) + const result = transform({ source }, { jscodeshift: j }) + fse.writeFileSync(baseType, result) +} + +PROPS_JSON && codeShift('h5') \ No newline at end of file diff --git a/scripts/build-taro.mjs b/scripts/build-taro.mjs index a044bc4806..4c15518f03 100644 --- a/scripts/build-taro.mjs +++ b/scripts/build-taro.mjs @@ -59,7 +59,6 @@ const transform = (file, api, replace) => { return } const dir = join(__dirname, alias.replace('@/', '../src/')) - console.log(alias, file) if (file.path) { path.node.source.value = relativePath(dir, file.path)?.replace( '.taro', diff --git a/src/config.json b/src/config.json index 00e38083f4..c765dfd161 100644 --- a/src/config.json +++ b/src/config.json @@ -394,6 +394,7 @@ "show": false, "taro": true, "author": "Alex.hxy", + "exportEmpty": true, "dd": true }, { diff --git a/src/packages/datepickerview/doc.en-US.md b/src/packages/datepickerview/doc.en-US.md index 03ab86f457..0977b8e90f 100644 --- a/src/packages/datepickerview/doc.en-US.md +++ b/src/packages/datepickerview/doc.en-US.md @@ -24,7 +24,7 @@ import { DatePickerView } from '@nutui/nutui' ::: -## DatePicker +## DatePickerView ### Props diff --git a/src/packages/datepickerview/doc.md b/src/packages/datepickerview/doc.md index fa1e52b734..87bbaeff71 100644 --- a/src/packages/datepickerview/doc.md +++ b/src/packages/datepickerview/doc.md @@ -28,7 +28,7 @@ import { DatePickerView } from '@nutui/nutui' ::: -## DatePicker +## DatePickerView ### Props diff --git a/src/packages/datepickerview/doc.taro.md b/src/packages/datepickerview/doc.taro.md index e68ed5b298..d446f2f035 100644 --- a/src/packages/datepickerview/doc.taro.md +++ b/src/packages/datepickerview/doc.taro.md @@ -28,7 +28,7 @@ import { DatePickerView } from '@nutui/nutui-taro' ::: -## DatePicker +## DatePickerView ### Props diff --git a/src/packages/datepickerview/doc.zh-TW.md b/src/packages/datepickerview/doc.zh-TW.md index e24427b108..98b91b537a 100644 --- a/src/packages/datepickerview/doc.zh-TW.md +++ b/src/packages/datepickerview/doc.zh-TW.md @@ -28,7 +28,7 @@ import { DatePickerView } from '@nutui/nutui' ::: -## DatePicker +## DatePickerView ### Props diff --git a/src/packages/rate/doc.taro.md b/src/packages/rate/doc.taro.md index 1709373b14..792c5cbc42 100644 --- a/src/packages/rate/doc.taro.md +++ b/src/packages/rate/doc.taro.md @@ -108,7 +108,7 @@ import { Rate } from '@nutui/nutui-react-taro' ## Rate -## Props +### Props | 属性 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | diff --git a/src/types/index.ts b/src/types/index.ts index 9a7860a89b..64f56f1fd5 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -158,6 +158,10 @@ export * from './spec/picker/base' export * from './spec/picker/h5' export * from './spec/picker/taro' +export * from './spec/pickerview/base' +export * from './spec/pickerview/h5' +export * from './spec/pickerview/taro' + export * from './spec/noticebar/base' export * from './spec/noticebar/h5' export * from './spec/noticebar/taro' @@ -344,6 +348,6 @@ export * from './spec/audio/base' export * from './spec/audio/h5' export * from './spec/audio/taro' -export * from './spec/step/base' -export * from './spec/step/h5' -export * from './spec/step/taro' +export * from './spec/steps/base' +export * from './spec/steps/h5' +export * from './spec/steps/taro' diff --git a/src/types/spec/picker/base.ts b/src/types/spec/picker/base.ts index 68528623f6..2d36f753b2 100644 --- a/src/types/spec/picker/base.ts +++ b/src/types/spec/picker/base.ts @@ -58,14 +58,3 @@ export interface BasePicker extends BaseProps { onChange?: (args0: PickerOnChangeCallbackParameter) => void children?: any } - -export interface BasePickerView extends BaseProps { - setRefs?: (ref: any) => any - options: PickerOptions[] - value?: PickerValue[] - defaultValue?: PickerValue[] - threeDimensional?: boolean - duration?: number | string - renderLabel: (item: PickerOption) => React.ReactNode - onChange?: (arg0: PickerOnChangeCallbackParameter) => void -} diff --git a/src/types/spec/picker/h5.ts b/src/types/spec/picker/h5.ts index 0b07aaca39..4228a4d177 100644 --- a/src/types/spec/picker/h5.ts +++ b/src/types/spec/picker/h5.ts @@ -1,6 +1,6 @@ -import { BasePicker, BasePickerView, BasePickerRoller } from './base' +import { BasePicker, BasePickerRoller } from './base' import { WebPopupProps } from '../popup/h5' export interface WebPickerProps extends BasePicker {} -export interface WebPickerViewProps extends BasePickerView {} + export interface WebPickerRollerProps extends BasePickerRoller {} diff --git a/src/types/spec/picker/taro.ts b/src/types/spec/picker/taro.ts index f1fcb10f7e..b0ea8ae9c4 100644 --- a/src/types/spec/picker/taro.ts +++ b/src/types/spec/picker/taro.ts @@ -1,6 +1,5 @@ -import { BasePicker, BasePickerRoller, BasePickerView } from './base' +import { BasePicker, BasePickerRoller } from './base' import { TaroPopupProps } from '../popup/taro' export interface TaroPickerProps extends BasePicker {} -export interface TaroPickerViewProps extends BasePickerView {} export interface TaroPickerRollerProps extends BasePickerRoller {} diff --git a/src/types/spec/pickerview/base.ts b/src/types/spec/pickerview/base.ts new file mode 100644 index 0000000000..f060d4e896 --- /dev/null +++ b/src/types/spec/pickerview/base.ts @@ -0,0 +1,18 @@ +import { BaseProps } from '../../base/props' +import { + PickerOnChangeCallbackParameter, + PickerOption, + PickerOptions, + PickerValue, +} from '../picker/base' + +export interface BasePickerView extends BaseProps { + setRefs?: (ref: any) => any + options: PickerOptions[] + value?: PickerValue[] + defaultValue?: PickerValue[] + threeDimensional?: boolean + duration?: number | string + renderLabel: (item: PickerOption) => React.ReactNode + onChange?: (arg0: PickerOnChangeCallbackParameter) => void +} diff --git a/src/types/spec/pickerview/h5.ts b/src/types/spec/pickerview/h5.ts new file mode 100644 index 0000000000..64bbce42f5 --- /dev/null +++ b/src/types/spec/pickerview/h5.ts @@ -0,0 +1,3 @@ +import { BasePickerView } from './base' + +export interface WebPickerViewProps extends BasePickerView {} diff --git a/src/types/spec/pickerview/taro.ts b/src/types/spec/pickerview/taro.ts new file mode 100644 index 0000000000..fb8dbfda99 --- /dev/null +++ b/src/types/spec/pickerview/taro.ts @@ -0,0 +1,3 @@ +import { BasePickerView } from './base' + +export interface TaroPickerViewProps extends BasePickerView {} diff --git a/src/types/spec/step/base.ts b/src/types/spec/steps/base.ts similarity index 100% rename from src/types/spec/step/base.ts rename to src/types/spec/steps/base.ts diff --git a/src/types/spec/step/h5.ts b/src/types/spec/steps/h5.ts similarity index 100% rename from src/types/spec/step/h5.ts rename to src/types/spec/steps/h5.ts diff --git a/src/types/spec/step/taro.ts b/src/types/spec/steps/taro.ts similarity index 100% rename from src/types/spec/step/taro.ts rename to src/types/spec/steps/taro.ts