2021-09-25 15:17:57 +08:00
|
|
|
import { readFileSync } from 'fs'
|
|
|
|
import cheerio from 'cheerio'
|
|
|
|
import { render } from './page'
|
2022-01-17 15:58:23 +08:00
|
|
|
const VERSION_RE = /^(\d+\.\d+\.\d+(?:-[a-z]+(?:\.\d+)?)?)(?:\s+"([^"]+)")?(?:\s+\((\d{4}-\d{2}-\d{2})\))?$/i
|
2021-09-25 15:17:57 +08:00
|
|
|
function getVersion (title = '') {
|
2022-01-17 15:58:23 +08:00
|
|
|
const [, version, codeName, date] = title.trim().match(VERSION_RE) || []
|
2021-09-25 15:17:57 +08:00
|
|
|
if (!version) {
|
|
|
|
return null
|
|
|
|
}
|
2022-01-17 15:58:23 +08:00
|
|
|
return [version, codeName, date]
|
2021-09-25 15:17:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const TYPE_MAP = {
|
|
|
|
'โ ๏ธ': 'breaking',
|
|
|
|
'๐ก': 'feature',
|
|
|
|
'๐': 'bugfix',
|
|
|
|
'๐งช': 'experimental'
|
|
|
|
}
|
|
|
|
const TYPE_KEYS = Object.keys(TYPE_MAP)
|
|
|
|
function getChangeType (title) {
|
|
|
|
const t = title.trim()
|
|
|
|
const key = TYPE_KEYS.find(key => t.includes(key))
|
|
|
|
if (!key) {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
return TYPE_MAP[key]
|
|
|
|
}
|
|
|
|
|
|
|
|
const TAG_RE = /#([^\s]+)$/
|
|
|
|
function getTags (comment) {
|
|
|
|
return comment
|
|
|
|
.trim()
|
|
|
|
.split(/\s+/)
|
|
|
|
.map(token => {
|
|
|
|
const [, tag] = token.match(TAG_RE) || []
|
|
|
|
return tag
|
|
|
|
})
|
|
|
|
.filter(tag => !!tag)
|
|
|
|
}
|
|
|
|
|
2021-09-25 23:32:02 +08:00
|
|
|
const EDIT_TYPE_RE = /^\[([+-^])\]/
|
|
|
|
const EDIT_TYPE_MAP = {
|
|
|
|
'-': 'remove',
|
|
|
|
'+': 'add',
|
|
|
|
'^': 'modify'
|
|
|
|
}
|
|
|
|
function getEditType (text) {
|
|
|
|
const [, sign] = text.match(EDIT_TYPE_RE) || []
|
|
|
|
if (sign) {
|
|
|
|
return EDIT_TYPE_MAP[sign]
|
|
|
|
}
|
|
|
|
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
|
|
|
function trim (text) {
|
|
|
|
return text.trim().replace(/^\n+|\n+$/g, '')
|
|
|
|
}
|
|
|
|
|
2021-09-25 15:17:57 +08:00
|
|
|
function extract (html) {
|
|
|
|
const changelog = []
|
|
|
|
|
|
|
|
const $ = cheerio.load(html)
|
|
|
|
const $versions = $('h2')
|
|
|
|
|
|
|
|
$versions.each((_, el) => {
|
|
|
|
const $version = $(el)
|
2022-01-17 15:58:23 +08:00
|
|
|
const [version, codeName, date] = getVersion($(el).text()) || []
|
2021-09-25 15:17:57 +08:00
|
|
|
const versionLog = {
|
|
|
|
version,
|
|
|
|
codeName,
|
2022-01-17 15:58:23 +08:00
|
|
|
date,
|
2021-09-25 15:17:57 +08:00
|
|
|
changeset: []
|
|
|
|
}
|
|
|
|
|
|
|
|
const $types = $version.nextUntil('h2', 'h3')
|
|
|
|
|
|
|
|
if ($types.length === 0) {
|
|
|
|
throw new Error(`No change type found for version ${version}`)
|
|
|
|
}
|
|
|
|
|
|
|
|
let type
|
|
|
|
$types.each((_, el) => {
|
|
|
|
const $type = $(el)
|
|
|
|
type = getChangeType($type.text())
|
|
|
|
|
|
|
|
const $changeset = $type.next('ul').children()
|
|
|
|
|
|
|
|
if ($changeset.length === 0) {
|
|
|
|
throw new Error(`No changeset found for version ${version}`)
|
|
|
|
}
|
|
|
|
|
|
|
|
$changeset.each((_, el) => {
|
|
|
|
const $change = $(el)
|
|
|
|
const tags = $change
|
|
|
|
.contents()
|
|
|
|
.toArray()
|
|
|
|
.map(el => {
|
|
|
|
if (el.type === 'comment') {
|
|
|
|
return getTags(el.data)
|
|
|
|
}
|
|
|
|
return []
|
|
|
|
})
|
|
|
|
.reduce((all, current) => all.concat(current), [])
|
|
|
|
|
|
|
|
$change.contents().filter((_, el) => el.type === 'comment').remove()
|
2021-09-25 23:32:02 +08:00
|
|
|
$change.find('*').contents().filter((_, el) => el.type === 'comment').remove()
|
|
|
|
|
|
|
|
const $container = trim($change.html()).startsWith('<p>') ? $change.children('p').first() : $change
|
|
|
|
|
|
|
|
let html = trim($container.html())
|
|
|
|
const editType = getEditType($container.text())
|
|
|
|
html = html.replace(EDIT_TYPE_RE, '')
|
|
|
|
|
|
|
|
$container.html(html)
|
2021-09-25 15:17:57 +08:00
|
|
|
|
|
|
|
versionLog.changeset.push({
|
|
|
|
type,
|
|
|
|
tags,
|
2021-09-25 23:32:02 +08:00
|
|
|
editType,
|
|
|
|
content: trim($change.html())
|
2021-09-25 15:17:57 +08:00
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
changelog.push(versionLog)
|
|
|
|
})
|
|
|
|
return changelog
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getChangelogData () {
|
|
|
|
const changelogPath = require.resolve('veui/CHANGELOG.md')
|
|
|
|
const raw = readFileSync(changelogPath, 'utf8')
|
|
|
|
const { contents } = render(raw, changelogPath)
|
|
|
|
return extract(contents)
|
|
|
|
}
|