|
36 | 36 |
|
37 | 37 | import { readdirSync, readFileSync, existsSync } from 'node:fs'; |
38 | 38 | import { join, basename, dirname } from 'node:path'; |
| 39 | +import { isEntrypoint } from './invoked-as.mjs'; |
39 | 40 |
|
40 | 41 | const ROOT = new URL('..', import.meta.url).pathname; |
41 | 42 | const AREAS_DIR = join(ROOT, 'docs/qa/platform-checklist/areas'); |
@@ -117,7 +118,7 @@ function isBlocked(it) { |
117 | 118 | } |
118 | 119 |
|
119 | 120 | // ── self-test ──────────────────────────────────────────────────────────────── |
120 | | -if (process.argv.includes('--self-test')) { |
| 121 | +function selfTest() { |
121 | 122 | const FIX = [ |
122 | 123 | { id: 'a.one', status: 'active', priority: 'P0', surface: 'browser', since: 'v16', source: ['packages/foo/bar.ts'] }, |
123 | 124 | { id: 'a.two', status: 'active', priority: 'P1', surface: 'api', since: 'v16.1', source: ['#3358'], blocked: { by: 'fixture', ref: '#1' } }, |
@@ -153,39 +154,49 @@ if (process.argv.includes('--self-test')) { |
153 | 154 | } |
154 | 155 |
|
155 | 156 | // ── CLI ────────────────────────────────────────────────────────────────────── |
156 | | -const args = process.argv.slice(2); |
157 | | -const json = args.includes('--json'); |
158 | | -const includeBlocked = args.includes('--include-blocked'); |
159 | | -const selector = args.find((a) => !a.startsWith('--')); |
160 | | - |
161 | | -if (!selector) { |
162 | | - console.error('usage: node scripts/checklist-select.mjs <selector> [--json] [--include-blocked]'); |
163 | | - console.error(' selectors: <id> | area:<a> | capability:<k> | priority:P0 | surface:api | since:vN | file:<path> | all'); |
164 | | - process.exit(2); |
165 | | -} |
166 | | -if (!existsSync(AREAS_DIR)) { |
167 | | - console.error(`checklist-select: ${AREAS_DIR} not found`); |
168 | | - process.exit(1); |
169 | | -} |
| 157 | +function main() { |
| 158 | + const args = process.argv.slice(2); |
| 159 | + const json = args.includes('--json'); |
| 160 | + const includeBlocked = args.includes('--include-blocked'); |
| 161 | + const selector = args.find((a) => !a.startsWith('--')); |
170 | 162 |
|
171 | | -const items = loadItems(); |
172 | | -const coverage = existsSync(COVERAGE) ? JSON.parse(readFileSync(COVERAGE, 'utf8')) : { metadataKinds: {} }; |
173 | | -let matched = selectItems(selector, items, coverage); |
174 | | -const droppedBlocked = includeBlocked ? [] : matched.filter(isBlocked); |
175 | | -if (!includeBlocked) matched = matched.filter((it) => !isBlocked(it)); |
| 163 | + if (!selector) { |
| 164 | + console.error('usage: node scripts/checklist-select.mjs <selector> [--json] [--include-blocked]'); |
| 165 | + console.error(' selectors: <id> | area:<a> | capability:<k> | priority:P0 | surface:api | since:vN | file:<path> | all'); |
| 166 | + process.exit(2); |
| 167 | + } |
| 168 | + if (!existsSync(AREAS_DIR)) { |
| 169 | + console.error(`checklist-select: ${AREAS_DIR} not found`); |
| 170 | + process.exit(1); |
| 171 | + } |
176 | 172 |
|
177 | | -if (json) { |
178 | | - process.stdout.write(JSON.stringify(matched.map((it) => ({ id: it.id, priority: it.priority, surface: it.surface, since: it.since, revision: it.revision })), null, 2) + '\n'); |
179 | | -} |
| 173 | + const items = loadItems(); |
| 174 | + const coverage = existsSync(COVERAGE) ? JSON.parse(readFileSync(COVERAGE, 'utf8')) : { metadataKinds: {} }; |
| 175 | + let matched = selectItems(selector, items, coverage); |
| 176 | + const droppedBlocked = includeBlocked ? [] : matched.filter(isBlocked); |
| 177 | + if (!includeBlocked) matched = matched.filter((it) => !isBlocked(it)); |
180 | 178 |
|
181 | | -console.error(`\nselector: ${selector} → ${matched.length} runnable item(s)${droppedBlocked.length ? ` (${droppedBlocked.length} blocked, hidden — pass --include-blocked)` : ''}\n`); |
182 | | -for (const it of matched) { |
183 | | - console.error(` ${it.priority} ${String(it.surface).padEnd(8)} ${it.id}${isBlocked(it) ? ' [BLOCKED]' : ''}`); |
184 | | -} |
185 | | -if (droppedBlocked.length) { |
186 | | - console.error(`\n hidden (blocked): ${droppedBlocked.map((i) => i.id).join(', ')}`); |
| 179 | + if (json) { |
| 180 | + process.stdout.write(JSON.stringify(matched.map((it) => ({ id: it.id, priority: it.priority, surface: it.surface, since: it.since, revision: it.revision })), null, 2) + '\n'); |
| 181 | + } |
| 182 | + |
| 183 | + console.error(`\nselector: ${selector} → ${matched.length} runnable item(s)${droppedBlocked.length ? ` (${droppedBlocked.length} blocked, hidden — pass --include-blocked)` : ''}\n`); |
| 184 | + for (const it of matched) { |
| 185 | + console.error(` ${it.priority} ${String(it.surface).padEnd(8)} ${it.id}${isBlocked(it) ? ' [BLOCKED]' : ''}`); |
| 186 | + } |
| 187 | + if (droppedBlocked.length) { |
| 188 | + console.error(`\n hidden (blocked): ${droppedBlocked.map((i) => i.id).join(', ')}`); |
| 189 | + } |
| 190 | + if (matched.length === 0) { |
| 191 | + console.error(' (nothing matched — check the selector; try `all` or `area:<name>`)'); |
| 192 | + process.exit(1); |
| 193 | + } |
187 | 194 | } |
188 | | -if (matched.length === 0) { |
189 | | - console.error(' (nothing matched — check the selector; try `all` or `area:<name>`)'); |
190 | | - process.exit(1); |
| 195 | + |
| 196 | +// The dispatch runs only when node ran THIS file. Imported for `selectItems` |
| 197 | +// (the skill's front half is a pure resolver), the old top-level CLI printed a |
| 198 | +// usage block to the importer's stderr and killed it with exit 2 mid-import. |
| 199 | +if (isEntrypoint(import.meta.url)) { |
| 200 | + if (process.argv.includes('--self-test')) selfTest(); |
| 201 | + main(); |
191 | 202 | } |
0 commit comments