feat: add changelog page

This commit is contained in:
Justineo
2021-09-25 15:17:57 +08:00
parent 28459eaa42
commit 9dc42b82cf
18 changed files with 761 additions and 104 deletions

108
one/build/changelog.js Normal file
View File

@@ -0,0 +1,108 @@
import { readFileSync } from 'fs'
import cheerio from 'cheerio'
import { render } from './page'
const VERSION_RE = /^(\d+\.\d+\.\d+(?:-[a-z]+(?:\.\d+)?)?)(?:\s+"([^"]+)")?$/i
function getVersion (title = '') {
const [, version, codeName] = title.trim().match(VERSION_RE) || []
if (!version) {
return null
}
return [version, codeName]
}
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)
}
function extract (html) {
const changelog = []
const $ = cheerio.load(html)
const $versions = $('h2')
$versions.each((_, el) => {
const $version = $(el)
const [version, codeName] = getVersion($(el).text()) || []
const versionLog = {
version,
codeName,
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()
versionLog.changeset.push({
type,
tags,
content: $change.html().replace(/^\n+|\n+$/g, '')
})
})
})
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)
}

View File

@@ -1,10 +1,11 @@
import { statSync } from 'fs'
import { statSync, writeFileSync } from 'fs'
import { resolve, relative, extname, basename, sep } from 'path'
import readdirpSync from 'recursive-readdir-sync'
import rimraf from 'rimraf'
import { copyFileSync, replaceExtSync } from './util'
import { renderDocToPage } from './page'
import { get, removeFile } from './deps'
import { getChangelogData } from './changelog'
const DOCS_DIR = resolve(__dirname, '../docs')
const PAGES_DIR = resolve(__dirname, '../../pages')
@@ -18,17 +19,28 @@ export function generatePages (file, stats) {
rimraf.sync(resolve(__dirname, './deps.json'))
console.log('Regenerating all files...')
handleFile(DOCS_DIR)
handleChangelog()
console.log('...done.')
} else {
handleFile(file, stats)
}
}
function handleChangelog () {
const changelogData = getChangelogData()
writeFileSync(
resolve(ASSETS_DIR, 'data', 'changelog.json'),
JSON.stringify(changelogData, null, 2)
)
}
function handleFile (file, stats) {
let segments = relative(DOCS_DIR, file).split(sep)
if (segments.some(segment => {
return segment.startsWith('_') || segment.startsWith('.')
})) {
if (
segments.some(segment => {
return segment.startsWith('_') || segment.startsWith('.')
})
) {
return
}
@@ -74,9 +86,10 @@ function handleFile (file, stats) {
default: {
let relDest = relative(DOCS_DIR, file)
let dest = relDest.split(sep).indexOf('demo') === -1
? resolve(PAGES_DIR, relDest)
: resolve(DEMOS_DIR, relDest)
let dest =
relDest.split(sep).indexOf('demo') === -1
? resolve(PAGES_DIR, relDest)
: resolve(DEMOS_DIR, relDest)
if (remove) {
rimraf.sync(dest)
console.log(`[${relDest}] removed.`)

View File

@@ -3,11 +3,11 @@ import vfile from 'vfile'
import remark from 'remark'
import slug from 'remark-slug'
import frontmatter from 'remark-frontmatter'
import highlight from 'remark-highlight.js'
import shortcodes from 'remark-shortcodes'
import remarkToRehype from 'remark-rehype'
import raw from 'rehype-raw'
import html from 'rehype-stringify'
import highlight from 'rehype-highlight'
import etpl from 'etpl'
import { readFileSync, writeFileSync, replaceExtSync } from './util'
import demo from './remark-demo'
@@ -17,9 +17,10 @@ import custom from './remark-custom'
import extractFrontmatter from './remark-extract-frontmatter'
import rehypePreviewImg from './rehype-preview-img'
import rehypeLink from './rehype-link'
import rehypeScoped from './rehype-scoped'
import rehypeDemo from './rehype-demo'
import rehypePre from './rehype-pre'
import { add } from './deps'
import lowlight from 'lowlight'
import { vue } from './language'
const DOCS_DIR = resolve(__dirname, '../docs')
@@ -28,8 +29,6 @@ const PAGE_TPL = readFileSync(resolve(__dirname, '../templates/page.etpl'))
const renderPage = etpl.compile(PAGE_TPL)
lowlight.registerLanguage('vue', vue)
const md = remark()
.use(custom)
.use(details)
@@ -38,13 +37,15 @@ const md = remark()
.use(shortcodes)
.use(demo)
.use(extractFrontmatter)
.use(highlight)
.use(slug)
.use(remarkToRehype, { allowDangerousHTML: true })
.use(raw)
.use(rehypePreviewImg)
.use(rehypeLink)
.use(rehypeScoped)
.use(rehypeDemo)
.use(highlight, { languages: { vue } })
.use(rehypePre)
.use(html, { allowDangerousHTML: true })
export function render (contents, path, data = {}) {
@@ -59,7 +60,13 @@ export function renderDocToPage (file) {
let src = resolve(DOCS_DIR, file)
let dest = resolve(PAGES_DIR, replaceExtSync(file, 'vue'))
let { contents, data } = renderFile(src, dest)
let { demos = {}, components = {}, meta = {}, deps = {}, hasAlert = false } = data
let {
demos = {},
components = {},
meta = {},
deps = {},
hasAlert = false
} = data
Object.keys(deps || {}).forEach(dep => {
add({ [dep]: { [src]: true } })
@@ -69,7 +76,12 @@ export function renderDocToPage (file) {
let componentList = Object.keys(components)
let demoList = Object.keys(demos)
let result = renderPage({
content: (contents || '').replace(/\n{3,}/g, '\n\n'),
content: (contents || '')
.replace(/\n{3,}/g, '\n\n')
.replace(/\{/g, '{')
.replace(/\}/g, '}')
.replace(/v-pre="true"/g, 'v-pre')
.replace(/data-markdown="true"/g, 'data-markdown'),
demos: demoList.map(name => {
return {
name,

View File

@@ -24,16 +24,10 @@ export default function attacher () {
{
slot: 'source'
},
h(
'div',
{
'v-pre': true
},
{
type: 'raw',
value: code
}
)
{
type: 'raw',
value: code
}
),
h(
'template',

11
one/build/rehype-pre.js Normal file
View File

@@ -0,0 +1,11 @@
import visit from 'unist-util-visit'
export default function attacher () {
return tree => {
visit(tree, 'element', ({ tagName, properties }) => {
if (tagName === 'pre') {
properties['v-pre'] = true
}
})
}
}

View File

@@ -0,0 +1,13 @@
import visit from 'unist-util-visit'
const RE_DEMO = /^one-demo-[a-f0-9]+/i
export default function attacher () {
return tree => {
visit(tree, 'element', ({ tagName, properties }, _, { type }) => {
if (type === 'root' && !RE_DEMO.test(tagName)) {
properties['data-markdown'] = true
}
})
}
}

View File

@@ -1,4 +1,4 @@
import tokenizer from './customBlock'
import tokenizer from './custom-block'
import visit from 'unist-util-visit'
import { render } from './page'

View File

@@ -1,4 +1,4 @@
import tokenizer from './refBlock'
import tokenizer from './ref-block'
import visit from 'unist-util-visit'
import remove from 'unist-util-remove'
import { render } from './page'
@@ -35,7 +35,7 @@ export default function attacher () {
visit(tree, 'linkReference', (node, index, parent) => {
let { identifier } = node
let [match, id] = identifier.match(RE_REF)
let [match, id] = identifier.match(RE_REF) || []
if (!match || !id || !data.refs[id]) {
return
}