Spaces:
Running
Running
| import { readFileSync } from 'node:fs'; | |
| import { join } from 'node:path'; | |
| import { fileURLToPath } from 'node:url'; | |
| const REPO_ROOT = fileURLToPath(new URL('..', import.meta.url)); | |
| const SEMVER_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/; | |
| const CHANGELOG_BASE_URL = 'https://github.com/MisonL/visual-journal'; | |
| function readText(path) { | |
| return readFileSync(join(REPO_ROOT, path), 'utf8'); | |
| } | |
| function readJson(path) { | |
| return JSON.parse(readText(path)); | |
| } | |
| function escapeRegExp(value) { | |
| return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); | |
| } | |
| function readReadmeBadgeVersion(readme) { | |
| const match = readme.match(/!\[Version\]\(https:\/\/img\.shields\.io\/badge\/version-(.+?)-blue\)/); | |
| return match?.[1]; | |
| } | |
| function addCheck(checks, name, ok, details = {}) { | |
| checks.push({ name, status: ok ? 'pass' : 'fail', ...details }); | |
| } | |
| function buildChecks() { | |
| const packageJson = readJson('package.json'); | |
| const packageLock = readJson('package-lock.json'); | |
| const readme = readText('README.md'); | |
| const changelog = readText('CHANGELOG.md'); | |
| const version = packageJson.version; | |
| const checks = []; | |
| addCheck(checks, 'package-version-semver', SEMVER_PATTERN.test(version), { version }); | |
| addCheck(checks, 'package-lock-root-version', packageLock.version === version, { | |
| expected: version, | |
| actual: packageLock.version | |
| }); | |
| addCheck(checks, 'package-lock-package-version', packageLock.packages?.['']?.version === version, { | |
| expected: version, | |
| actual: packageLock.packages?.['']?.version | |
| }); | |
| addCheck(checks, 'readme-version-badge', readReadmeBadgeVersion(readme) === version, { | |
| expected: version, | |
| actual: readReadmeBadgeVersion(readme) || null | |
| }); | |
| addCheck(checks, 'changelog-unreleased-section', /^## \[未发布\]$/m.test(changelog)); | |
| addCheck(checks, 'changelog-current-version-section', hasChangelogVersionSection(changelog, version), { version }); | |
| addCheck(checks, 'changelog-unreleased-link', hasUnreleasedLink(changelog, version), { version }); | |
| addCheck(checks, 'changelog-current-version-link', hasVersionLink(changelog, version), { version }); | |
| return checks; | |
| } | |
| function hasChangelogVersionSection(changelog, version) { | |
| return new RegExp(`^## \\[${escapeRegExp(version)}\\] - \\d{4}-\\d{2}-\\d{2}$`, 'm').test(changelog); | |
| } | |
| function hasUnreleasedLink(changelog, version) { | |
| const expected = `[未发布]: ${CHANGELOG_BASE_URL}/compare/v${version}...HEAD`; | |
| return changelog.split(/\r?\n/).includes(expected); | |
| } | |
| function hasVersionLink(changelog, version) { | |
| return new RegExp(`^\\[${escapeRegExp(version)}\\]: ${escapeRegExp(CHANGELOG_BASE_URL)}/compare/.+v${escapeRegExp(version)}$`, 'm').test(changelog); | |
| } | |
| function main() { | |
| const checks = buildChecks(); | |
| const ok = checks.every((check) => check.status === 'pass'); | |
| console.log(JSON.stringify({ ok, checks }, null, 2)); | |
| if (!ok) process.exit(1); | |
| } | |
| main(); | |