1
0

release-notes.mjs 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. import { readFileSync, writeFileSync } from "node:fs"
  2. import { createRequire } from "node:module"
  3. import { dirname, resolve } from "node:path"
  4. import { pathToFileURL, fileURLToPath } from "node:url"
  5. import ts from "typescript"
  6. const require = createRequire(import.meta.url)
  7. const root = resolve(dirname(fileURLToPath(import.meta.url)), "..")
  8. function optionalIconvLite() {
  9. try {
  10. return require("iconv-lite")
  11. } catch {
  12. return null
  13. }
  14. }
  15. function repairLikelyMojibake(text) {
  16. if (!/[锛銆涓绔榛勯噾瀹]/.test(text)) return text
  17. const iconv = optionalIconvLite()
  18. if (!iconv) return text
  19. try {
  20. const repaired = iconv.decode(iconv.encode(text, "gbk"), "utf8")
  21. return repaired.includes("�") ? text : repaired
  22. } catch {
  23. return text
  24. }
  25. }
  26. async function loadChangelog(rootDir = root) {
  27. const source = readFileSync(resolve(rootDir, "src/lib/changelog.ts"), "utf8")
  28. const compiled = ts.transpileModule(source, {
  29. compilerOptions: {
  30. module: ts.ModuleKind.ES2022,
  31. target: ts.ScriptTarget.ES2022,
  32. },
  33. }).outputText
  34. const moduleUrl = `data:text/javascript;base64,${Buffer.from(compiled).toString("base64")}`
  35. return import(moduleUrl)
  36. }
  37. export function formatReleaseNotes(entry) {
  38. const zhItems = entry?.highlights?.zh ?? []
  39. if (!entry || zhItems.length === 0) return ""
  40. return zhItems
  41. .map((item, index) => `${index + 1}. ${repairLikelyMojibake(item)}`)
  42. .join("\n")
  43. }
  44. export async function buildReleaseNotes(version, rootDir = root) {
  45. const changelog = await loadChangelog(rootDir)
  46. const entry = changelog.currentVersionChangelog(version)[0]
  47. const notes = formatReleaseNotes(entry)
  48. return notes || `QMAI ${version} 发布版本`
  49. }
  50. export async function buildCurrentReleaseNotes(rootDir = root) {
  51. const pkg = JSON.parse(readFileSync(resolve(rootDir, "package.json"), "utf8"))
  52. return buildReleaseNotes(pkg.version, rootDir)
  53. }
  54. if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
  55. const args = process.argv.slice(2)
  56. const outIndex = args.indexOf("--out")
  57. const outPath = outIndex >= 0 ? args[outIndex + 1] : ""
  58. const version = args.find((arg, index) => index !== outIndex && index !== outIndex + 1)
  59. const notes = version ? await buildReleaseNotes(version) : await buildCurrentReleaseNotes()
  60. if (outPath) {
  61. writeFileSync(outPath, notes, "utf8")
  62. } else {
  63. process.stdout.write(notes)
  64. }
  65. }