[READ-ONLY] Mirror of https://github.com/danielroe/uppt. A composite GitHub Action that turns conventional commits into a draft release PR, tags the PR on merge, and stages publishing to npm via OIDC
github-action
npm
npmjs
oidc
staged-publish
trusted-publishing
1// Parse the `--json` output of `npm pack` / `pnpm pack`.
2//
3// `npm pack --json` emits an array of pack records with a bare
4// `filename`; `pnpm pack --json` emits a single object whose
5// `filename` is an absolute path. Both tools run lifecycle scripts
6// (`prepare`, `prepack`) before emitting their JSON, and tools like
7// unbuild print to stdout (e.g. `[INFO] ...`), so the captured stdout
8// is a mix of script output followed by a single trailing JSON value.
9// We locate that trailing value rather than parsing the whole buffer,
10// and normalise filenames to basenames so the step output matches
11// what `actions/upload-artifact` puts in the artifact (and what
12// `publish.ts` resolves under `TARBALL_DIR`).
13
14import { basename } from 'node:path'
15
16export function extractTrailingJson (stdout: string): unknown {
17 const trimmed = stdout.replace(/\s+$/, '')
18 if (!trimmed) {
19 throw new Error('Pack tool produced no output on stdout')
20 }
21 const last = trimmed[trimmed.length - 1]
22 if (last !== '}' && last !== ']') {
23 throw new Error(`Pack tool stdout did not end with a JSON value:\n${stdout}`)
24 }
25 const open = last === '}' ? '{' : '['
26 const candidates: number[] = []
27 for (let i = 0; i < trimmed.length; i++) {
28 if (trimmed[i] === open && (i === 0 || trimmed[i - 1] === '\n')) {
29 candidates.push(i)
30 }
31 }
32 for (let i = candidates.length - 1; i >= 0; i--) {
33 try {
34 return JSON.parse(trimmed.slice(candidates[i]!))
35 }
36 catch {}
37 }
38 throw new Error(`Could not find a JSON value in pack tool stdout:\n${stdout}`)
39}
40
41export function parseFilenames (stdout: string): string[] {
42 const data = extractTrailingJson(stdout)
43 const records = Array.isArray(data) ? data : [data]
44 const filenames: string[] = []
45 for (const entry of records) {
46 if (!entry || typeof entry !== 'object' || !('filename' in entry)) {
47 throw new Error(`Unexpected pack JSON entry without 'filename': ${JSON.stringify(entry)}`)
48 }
49 const filename = (entry as { filename: unknown }).filename
50 if (typeof filename !== 'string' || !filename.endsWith('.tgz')) {
51 throw new Error(`Unexpected pack JSON 'filename': ${JSON.stringify(filename)}`)
52 }
53 filenames.push(basename(filename))
54 }
55 if (!filenames.length) {
56 throw new Error('Pack tool produced JSON with no tarball filenames')
57 }
58 return filenames
59}