63 lines
1.7 KiB
TypeScript
63 lines
1.7 KiB
TypeScript
import * as fs from 'node:fs';
|
|
import { unified } from 'unified';
|
|
import remarkParse from 'remark-parse';
|
|
import { Heading, List, Node } from 'mdast';
|
|
import { toString } from 'mdast-util-to-string';
|
|
|
|
export class Release {
|
|
public readonly releaseName: string;
|
|
public readonly categories: ReleaseCategory[];
|
|
|
|
constructor(releaseName: string, categories: ReleaseCategory[] = []) {
|
|
this.releaseName = releaseName;
|
|
this.categories = [...categories];
|
|
}
|
|
}
|
|
|
|
export class ReleaseCategory {
|
|
public readonly categoryName: string;
|
|
public readonly items: string[];
|
|
|
|
constructor(categoryName: string, items: string[] = []) {
|
|
this.categoryName = categoryName;
|
|
this.items = [...items];
|
|
}
|
|
}
|
|
|
|
function isHeading(node: Node): node is Heading {
|
|
return node.type === 'heading';
|
|
}
|
|
|
|
function isList(node: Node): node is List {
|
|
return node.type === 'list';
|
|
}
|
|
|
|
export function parseChangeLog(path: string): Release[] {
|
|
const input = fs.readFileSync(path, { encoding: 'utf8' });
|
|
const processor = unified().use(remarkParse);
|
|
|
|
const releases: Release[] = [];
|
|
const root = processor.parse(input);
|
|
|
|
let release: Release | null = null;
|
|
let category: ReleaseCategory | null = null;
|
|
for (const it of root.children) {
|
|
if (isHeading(it) && it.depth === 2) {
|
|
// リリース
|
|
release = new Release(toString(it));
|
|
releases.push(release);
|
|
} else if (isHeading(it) && it.depth === 3 && release) {
|
|
// リリース配下のカテゴリ
|
|
category = new ReleaseCategory(toString(it));
|
|
release.categories.push(category);
|
|
} else if (isList(it) && category) {
|
|
for (const listItem of it.children) {
|
|
// カテゴリ配下のリスト項目
|
|
category.items.push(toString(listItem));
|
|
}
|
|
}
|
|
}
|
|
|
|
return releases;
|
|
}
|