/* eslint-disable max-depth */ const fs = require('fs-extra'); const path = require('path'); const marked = require('marked'); const get = require('lodash/get'); const isString = require('lodash/isString'); const capitalize = require('lodash/capitalize'); const COMPONENT_LIST = [ 'anchor', 'autoComplete', 'avatar', 'backtop', 'badge', 'banner', 'breadcrumb', 'button', 'calendar', 'card', 'carousel', 'cascader', 'checkbox', 'collapse', 'collapsible', 'configProvider', 'datePicker', 'descriptions', 'dropdown', 'empty', 'form', 'grid', 'iconButton', 'icon', 'input', 'inputNumber', 'image', 'layout', 'list', 'localeProvider', 'modal', 'navigation', 'notification', 'overflowList', 'pagination', 'popconfirm', 'popover', 'progress', 'radio', 'rating', 'scrollList', 'select', 'sideSheet', 'skeleton', 'slider', 'space', 'spin', 'steps', 'switch', 'table', 'tabs', 'tag', 'tagInput', 'timePicker', 'timeline', 'toast', 'tooltip', 'transfer', 'tree', 'treeSelect', 'typography', 'upload' ]; const TYPE_MAP = { feat: 'feature', perf: 'performance', doc: 'docs', styles: 'style', }; const SUPPORT_TYPES = new Set(['feat', 'feature', 'performance', 'style', 'new component', 'chore', 'docs', 'fix', 'refactor', 'breaking change', 'design token']); const UNKNOWN_COMPONENT_NAME = 'Other'; const UNKNOWN_TYPE_NAME = 'chore'; function resolve(dir) { return path.join(__dirname, dir); } function readFile(filename) { return fs.readFileSync(resolve(filename), { encoding: 'utf-8' }); } function getVersion(str) { const versionReg = /.*((\d{1,2}\.){2}\d{1,2}(-\w+\.\d+)?)/; let result = versionReg.exec(str); return get(result, '1', '').trim().toLowerCase(); } function getType(str) { const typeReg = /【([a-z\s]*)】|\[([a-z\s]*)\]/i; let result = typeReg.exec(str); let type; if (result) { type = result[1] || result[2]; } type = isString(type) && type.trim().toLowerCase(); if (!SUPPORT_TYPES.has(type)) { type = UNKNOWN_TYPE_NAME; } return type; } function getIndent(str) { return /(\s*).*/.exec(str)[1].length; } function getChangeLog(str) { const changeLogReg = /\s*-\s*(.*)/; const changeLog = changeLogReg.exec(str)[1]; const indent = getIndent(str); return [changeLog.trim(), indent]; } function getComponent(str) { const componentReg = new RegExp(`\\b(${COMPONENT_LIST.join('|')})\\b`, 'i'); let result = componentReg.exec(str); if (result) { return capitalize(result[1]); } else { return null; } } function formatData(data) { // feat => feature // perf => performance // doc => docs const newData = data.map(item => { const { type, component } = item; if (type in TYPE_MAP) { // console.log(type); item.type = TYPE_MAP[type]; } if (!SUPPORT_TYPES.has(item.type)) { console.warn('changelog not support type', item.type); } if (!component) { item.component = UNKNOWN_COMPONENT_NAME; } return item; }); return newData; } function getChangeLogList(rawMarkdown) { const lines = rawMarkdown.split('\n'); const output = []; let version, type, parentComponent, prevLevel, level, prevIndent; let parentIndent = 0; let intent2Level = new Map(); for (let line of lines) { // is version line, eg. #### 🎉 1.24.4 (2021-06-21) if (line.startsWith('####')) { version = getVersion(line); intent2Level = new Map(); continue; } // is type line, eg. - 【Fix】 if (/-\s*[【】\[\]]/.test(line)) { type = getType(line); prevIndent = 0; level = 0; prevLevel = 0; continue; } // is changelog line if (version && /\s*-\s*.*/.test(line)) { let [content, indent] = getChangeLog(line); let component = getComponent(content); // process level const _level = intent2Level.get(indent); // align to same value indent if (_level) { level = _level; prevIndent = indent; prevLevel = level; } else { if (indent > prevIndent) { level = ++prevLevel; } else if (indent < prevIndent) { level = --prevLevel; } else { level = prevLevel; } intent2Level.set(indent, level); } // process component is none if ((prevIndent && (indent > prevIndent)) || ((indent === prevIndent) && level !== 1)) { if (!component) { component = indent > parentIndent ? parentComponent : UNKNOWN_COMPONENT_NAME; } } // update parent parameter if (level === 1 || indent < prevIndent) { parentComponent = component; parentIndent = indent; } prevIndent = indent; // record prev indent const contentToHTML = marked(content).replace(/(

)|(<\/p>)/g, '').trim(); const currentChangeLog = { version, type, content: contentToHTML, component, level }; output.push(currentChangeLog); } } return formatData(output); } function main() { // eslint-disable-next-line no-unused-vars const [_, __, savePath] = process.argv; const output = '../static/changeLog.json'; try { const changelogZN = readFile('../content/start/changelog/index.md'); const changelogEN = readFile('../content/start/changelog/index-en-US.md'); const changelogZNList = getChangeLogList(changelogZN); const changelogENList = getChangeLogList(changelogEN); const changelogJSON = { 'zh-CN': changelogZNList, 'en-US': changelogENList, }; fs.writeFileSync(savePath || resolve(output), JSON.stringify(changelogJSON)); console.info(`🎉 save changeLog.json to ${savePath || resolve(output)}`); } catch (err) { console.error('changelog build error: ', err.message, err.stack); } } main();