827e1a8ecf
Mechanical one-shot pass of pnpm format over src/, tests/, tools/, and root configs. No functional changes; build and type-check verified.
73 lines
2.2 KiB
JavaScript
73 lines
2.2 KiB
JavaScript
#!/usr/bin/env node
|
|
import { readFileSync, writeFileSync } from 'node:fs'
|
|
import { resolve } from 'node:path'
|
|
|
|
const SECTION_BY_TYPE = {
|
|
added: 'Added',
|
|
changed: 'Changed',
|
|
deprecated: 'Deprecated',
|
|
removed: 'Removed',
|
|
fixed: 'Fixed',
|
|
security: 'Security',
|
|
}
|
|
|
|
function usage() {
|
|
console.error(
|
|
[
|
|
'Usage:',
|
|
' pnpm changelog:add -- --type fixed --message "Fix thing"',
|
|
'',
|
|
`Types: ${Object.keys(SECTION_BY_TYPE).join(', ')}`,
|
|
].join('\n')
|
|
)
|
|
process.exit(1)
|
|
}
|
|
|
|
function argValue(name) {
|
|
const index = process.argv.indexOf(`--${name}`)
|
|
if (index === -1) return null
|
|
return process.argv[index + 1] ?? null
|
|
}
|
|
|
|
const type = argValue('type')?.toLowerCase()
|
|
const message = argValue('message')?.trim()
|
|
|
|
if (!type || !message || !SECTION_BY_TYPE[type]) {
|
|
usage()
|
|
}
|
|
|
|
const section = SECTION_BY_TYPE[type]
|
|
const changelogPath = resolve('CHANGELOG.md')
|
|
let changelog = readFileSync(changelogPath, 'utf8')
|
|
|
|
if (!/^## \[Unreleased\]/m.test(changelog)) {
|
|
changelog = changelog.replace(/(\r?\n)(## \[[^\r\n]+\])/, `$1## [Unreleased]$1$1$2`)
|
|
}
|
|
|
|
// JS regex has no \z anchor, so match to the next version heading or true
|
|
// end-of-input ($ with nothing following — \n-tolerant under the m flag).
|
|
const unreleasedMatch = changelog.match(/^## \[Unreleased\][\s\S]*?(?=^## \[|$(?![\s\S]))/m)
|
|
if (!unreleasedMatch) {
|
|
throw new Error('Could not find or create an Unreleased section.')
|
|
}
|
|
|
|
const unreleased = unreleasedMatch[0]
|
|
const lineEnding = changelog.includes('\r\n') ? '\r\n' : '\n'
|
|
const bullet = `- ${message}`
|
|
|
|
let nextUnreleased
|
|
const sectionRegex = new RegExp(`(^### ${section}\\s*)([\\s\\S]*?)(?=^### |$(?![\\s\\S]))`, 'm')
|
|
const sectionMatch = unreleased.match(sectionRegex)
|
|
|
|
if (sectionMatch) {
|
|
const body = sectionMatch[2].trimEnd()
|
|
const replacement = `${sectionMatch[1]}${body ? `${body}${lineEnding}` : ''}${bullet}${lineEnding}${lineEnding}`
|
|
nextUnreleased = unreleased.replace(sectionRegex, replacement)
|
|
} else {
|
|
const insert = `${lineEnding}### ${section}${lineEnding}${lineEnding}${bullet}${lineEnding}`
|
|
nextUnreleased = unreleased.trimEnd() + insert + lineEnding
|
|
}
|
|
|
|
changelog = changelog.replace(unreleased, nextUnreleased)
|
|
writeFileSync(changelogPath, changelog)
|