pipework 0.268.0 → 0.270.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/REFERENCE.md +48 -0
- package/dist/cli/commands/check-steps.d.ts +34 -0
- package/dist/cli/commands/check-steps.d.ts.map +1 -0
- package/dist/cli/commands/check-steps.js +60 -0
- package/dist/cli/commands/check-steps.js.map +1 -0
- package/dist/cli/commands/check.d.ts +43 -1
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +232 -133
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/lint.js +36 -1
- package/dist/cli/commands/lint.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +55 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/lint/doctrine.d.ts +2 -0
- package/dist/lint/doctrine.d.ts.map +1 -1
- package/dist/lint/doctrine.js +3 -1
- package/dist/lint/doctrine.js.map +1 -1
- package/dist/request/di/builder.d.ts +1 -0
- package/dist/request/di/builder.d.ts.map +1 -1
- package/dist/request/di/builder.js +1 -0
- package/dist/request/di/builder.js.map +1 -1
- package/dist/request/di/types.d.ts +2 -0
- package/dist/request/di/types.d.ts.map +1 -1
- package/dist/request/http/namespace.d.ts +1 -1
- package/dist/request/http/namespace.d.ts.map +1 -1
- package/dist/request/http/namespace.js +1 -1
- package/dist/request/http/namespace.js.map +1 -1
- package/dist/request/http/server.d.ts.map +1 -1
- package/dist/request/http/server.js +4 -2
- package/dist/request/http/server.js.map +1 -1
- package/dist/trace/cardinality/check.d.ts +5 -1
- package/dist/trace/cardinality/check.d.ts.map +1 -1
- package/dist/trace/cardinality/check.js +3 -1
- package/dist/trace/cardinality/check.js.map +1 -1
- package/dist/trace/check/coverage-check.d.ts +2 -0
- package/dist/trace/check/coverage-check.d.ts.map +1 -1
- package/dist/trace/check/coverage-check.js +3 -1
- package/dist/trace/check/coverage-check.js.map +1 -1
- package/dist/trace/check/flow-check.d.ts +2 -0
- package/dist/trace/check/flow-check.d.ts.map +1 -1
- package/dist/trace/check/flow-check.js +3 -1
- package/dist/trace/check/flow-check.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/check-steps.ts +98 -0
- package/src/cli/commands/check.ts +283 -147
- package/src/cli/commands/lint.ts +34 -1
- package/src/cli/index.ts +56 -2
- package/src/lint/doctrine.ts +5 -1
- package/src/request/di/builder.ts +2 -1
- package/src/request/di/types.ts +2 -0
- package/src/request/http/namespace.ts +1 -1
- package/src/request/http/server.ts +4 -2
- package/src/trace/cardinality/check.ts +9 -2
- package/src/trace/check/coverage-check.ts +5 -1
- package/src/trace/check/flow-check.ts +5 -1
package/src/cli/commands/lint.ts
CHANGED
|
@@ -2,7 +2,8 @@ import { spawnSync } from 'node:child_process'
|
|
|
2
2
|
import { dirname, relative } from 'node:path'
|
|
3
3
|
import { findConfig } from '../../core/config/discover.js'
|
|
4
4
|
import { error } from '../output.js'
|
|
5
|
-
import { builtinConfigArgs, lintTargets, loadLintIncludes, resolveEslintBin } from './check.js'
|
|
5
|
+
import { builtinConfigArgs, lintPopulation, lintTargets, loadLintIncludes, resolveEslintBin, resolveModulePaths } from './check.js'
|
|
6
|
+
import { parseShard } from './shard.js'
|
|
6
7
|
|
|
7
8
|
// eslint options that take their value as the next argument. Anything else starting
|
|
8
9
|
// with `-` is a switch, so the argument after it is a file.
|
|
@@ -36,6 +37,7 @@ export function namesFiles(args: readonly string[]): boolean {
|
|
|
36
37
|
* those. Every argument is forwarded verbatim. Returns eslint's exit code.
|
|
37
38
|
*/
|
|
38
39
|
export async function lintCommand(cwd: string, args: readonly string[]): Promise<number> {
|
|
40
|
+
if (args.includes('--list-files')) return listFiles(cwd, args)
|
|
39
41
|
const eslintBin = resolveEslintBin(cwd)
|
|
40
42
|
const configPath = findConfig(cwd)
|
|
41
43
|
const root = configPath === null ? cwd : dirname(configPath)
|
|
@@ -60,3 +62,34 @@ export async function lintCommand(cwd: string, args: readonly string[]): Promise
|
|
|
60
62
|
}
|
|
61
63
|
return result.status ?? 1
|
|
62
64
|
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* `pipework lint --list-files [--shard i/n]`: the repo-relative files `check`'s lint
|
|
68
|
+
* pass covers, one per line, read from pipework.config.ts. Nothing is linted.
|
|
69
|
+
*/
|
|
70
|
+
async function listFiles(cwd: string, args: readonly string[]): Promise<number> {
|
|
71
|
+
const rest = args.filter(a => a !== '--list-files')
|
|
72
|
+
let shard
|
|
73
|
+
if (rest.length === 2 && rest[0] === '--shard') {
|
|
74
|
+
try {
|
|
75
|
+
shard = parseShard(rest[1]!)
|
|
76
|
+
} catch (err) {
|
|
77
|
+
error(err instanceof Error ? err.message : String(err))
|
|
78
|
+
return 1
|
|
79
|
+
}
|
|
80
|
+
} else if (rest.length > 0) {
|
|
81
|
+
error(`--list-files takes only --shard <i>/<n>; got ${rest.join(' ')}. It lists files and does not run eslint.`)
|
|
82
|
+
return 1
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const configPath = findConfig(cwd)
|
|
86
|
+
const root = configPath === null ? cwd : dirname(configPath)
|
|
87
|
+
const scope = await resolveModulePaths(root)
|
|
88
|
+
if (scope.configError !== undefined) {
|
|
89
|
+
error(`Cannot list lint files: pipework.config.ts failed to load: ${scope.configError.message}`)
|
|
90
|
+
return 1
|
|
91
|
+
}
|
|
92
|
+
const population = lintPopulation(root, scope.paths, await loadLintIncludes(root), shard)
|
|
93
|
+
if (population.files.length > 0) process.stdout.write(population.files.join('\n') + '\n')
|
|
94
|
+
return 0
|
|
95
|
+
}
|
package/src/cli/index.ts
CHANGED
|
@@ -18,8 +18,13 @@ ${bold('Commands:')}
|
|
|
18
18
|
openapi Write an http surface's OpenAPI document (no database needed; --surface <name>, --out <file>)
|
|
19
19
|
run <script> Execute a script as a traced, audited flow (with --tenant: one transaction, rolled back if it throws)
|
|
20
20
|
test Run tests with automatic database setup
|
|
21
|
-
check Type-check, lint, and boundary enforcement
|
|
21
|
+
check Type-check, lint, and boundary enforcement (--format json, --only/--skip steps)
|
|
22
|
+
doctrine [files] Doctrine rules alone; JSON per violation and per scanned file with --format json
|
|
23
|
+
flow [files] Flow annotation rules alone (same options)
|
|
24
|
+
cardinality [files] @downstream cardinality rules alone (same options)
|
|
25
|
+
startup Startup validation alone: pipework.config.ts loaded, no database
|
|
22
26
|
lint [eslint args] Lint the current module (the workspace at its root) with check's config; every argument goes to eslint
|
|
27
|
+
lint --list-files [--shard i/n]: print the files check's lint covers, lint nothing
|
|
23
28
|
codemod Fill missing @downstream cardinality across the corpus
|
|
24
29
|
db Database lifecycle: setup, teardown, reset
|
|
25
30
|
alerts unit-fault Record an abnormal systemd stop (ExecStopPost=pipework alerts unit-fault --unit %n)
|
|
@@ -49,6 +54,15 @@ ${bold('Scoping (check, test):')}
|
|
|
49
54
|
--reap Drop orphan test databases and exit (test only)
|
|
50
55
|
--shard <i>/<n> Run part i of n disjoint parts of the selected run (test, check); the n parts together equal the unsharded run
|
|
51
56
|
|
|
57
|
+
${bold('Steps (check, doctrine, flow, cardinality, startup):')}
|
|
58
|
+
--format json Structured result: {step, status, pipeworkVersion, reads, files, violations}; check emits an array, one record per step
|
|
59
|
+
--output-file <path> Write the JSON there instead of stdout (human lines stay on stderr)
|
|
60
|
+
--only <a,b> check: run only these steps
|
|
61
|
+
--skip <a,b> check: run all but these steps (not with --only)
|
|
62
|
+
Steps: checksums, vitest-config, eslint-config, config-load, doctrine, flow, cardinality,
|
|
63
|
+
coverage, lint, typecheck, startup, boundaries, dependencies, install
|
|
64
|
+
doctrine, flow and cardinality take file paths: only those files are judged
|
|
65
|
+
|
|
52
66
|
${bold('Examples:')}
|
|
53
67
|
pipework init
|
|
54
68
|
pipework upgrade
|
|
@@ -74,14 +88,27 @@ ${bold('Examples:')}
|
|
|
74
88
|
pipework test --reap # drop orphan test databases
|
|
75
89
|
pipework check --staged
|
|
76
90
|
pipework check --all
|
|
91
|
+
pipework check --format json --skip lint,typecheck --output-file r.json
|
|
92
|
+
pipework doctrine --format json --output-file d.json src/a.ts src/b.ts
|
|
77
93
|
pipework lint # the module you are in, exactly as check lints it
|
|
78
94
|
pipework lint --format json --output-file r.json src/a.ts # only src/a.ts
|
|
95
|
+
pipework lint --list-files --shard 1/4 # the files check's lint pass covers for part 1 of 4, from pipework.config.ts
|
|
79
96
|
pipework codemod
|
|
80
97
|
pipework codemod --dry-run
|
|
81
98
|
pipework alerts unit-fault --unit subseam-core.service # reads $SERVICE_RESULT, $EXIT_CODE, $EXIT_STATUS
|
|
82
99
|
pipework alerts stale --max-age jobs.claim=5m # exit 1 with a line per stale loop; 0 and silent when none
|
|
83
100
|
`
|
|
84
101
|
|
|
102
|
+
function parseFormat(format: string | undefined, outputFile: string | undefined): 'human' | 'json' {
|
|
103
|
+
if (format !== undefined && format !== 'human' && format !== 'json') {
|
|
104
|
+
throw new Error(`--format takes "json" or "human"; got "${format}".`)
|
|
105
|
+
}
|
|
106
|
+
if (outputFile !== undefined && format !== 'json') {
|
|
107
|
+
throw new Error('--output-file needs --format json.')
|
|
108
|
+
}
|
|
109
|
+
return format === 'json' ? 'json' : 'human'
|
|
110
|
+
}
|
|
111
|
+
|
|
85
112
|
export async function run(argv: string[]): Promise<void> {
|
|
86
113
|
// `lint` hands everything after it to eslint, flags included, so it is dispatched
|
|
87
114
|
// before parseArgs, which would refuse eslint's options.
|
|
@@ -129,6 +156,10 @@ export async function run(argv: string[]): Promise<void> {
|
|
|
129
156
|
test: { type: 'boolean' },
|
|
130
157
|
'admin-url': { type: 'string' },
|
|
131
158
|
'dry-run': { type: 'boolean' },
|
|
159
|
+
format: { type: 'string' },
|
|
160
|
+
'output-file': { type: 'string' },
|
|
161
|
+
only: { type: 'string' },
|
|
162
|
+
skip: { type: 'string' },
|
|
132
163
|
},
|
|
133
164
|
})
|
|
134
165
|
|
|
@@ -256,7 +287,30 @@ export async function run(argv: string[]): Promise<void> {
|
|
|
256
287
|
const { parseShard: parseShardCheck } = await import('./commands/shard.js')
|
|
257
288
|
const shardCheck = values.shard === undefined ? undefined : parseShardCheck(values.shard)
|
|
258
289
|
const { check } = await import('./commands/check.js')
|
|
259
|
-
await check
|
|
290
|
+
const { parseStepSelection } = await import('./commands/check-steps.js')
|
|
291
|
+
await check(cwd, parsedCheck.scope, shardCheck, {
|
|
292
|
+
format: parseFormat(values.format, values['output-file']),
|
|
293
|
+
outputFile: values['output-file'],
|
|
294
|
+
select: parseStepSelection(values.only, values.skip),
|
|
295
|
+
})
|
|
296
|
+
break
|
|
297
|
+
}
|
|
298
|
+
case 'doctrine':
|
|
299
|
+
case 'flow':
|
|
300
|
+
case 'cardinality':
|
|
301
|
+
case 'startup': {
|
|
302
|
+
if (values.only !== undefined || values.skip !== undefined) {
|
|
303
|
+
throw new Error(`--only and --skip select steps of check; ${command} is one step.`)
|
|
304
|
+
}
|
|
305
|
+
const { parseScope: parseScopeStep } = await import('./commands/scope.js')
|
|
306
|
+
const parsedStep = parseScopeStep({ all: values.all, module: values.module, staged: values.staged, fast: values.fast, full: values.full })
|
|
307
|
+
const { stepCommand } = await import('./commands/check.js')
|
|
308
|
+
process.exitCode = await stepCommand(cwd, command, {
|
|
309
|
+
format: parseFormat(values.format, values['output-file']),
|
|
310
|
+
outputFile: values['output-file'],
|
|
311
|
+
scope: parsedStep.scope,
|
|
312
|
+
files: positionals.slice(1),
|
|
313
|
+
})
|
|
260
314
|
break
|
|
261
315
|
}
|
|
262
316
|
case 'codemod': {
|
package/src/lint/doctrine.ts
CHANGED
|
@@ -11,11 +11,14 @@ export interface DoctrineViolation {
|
|
|
11
11
|
export interface DoctrineResult {
|
|
12
12
|
readonly violations: readonly DoctrineViolation[]
|
|
13
13
|
readonly scanned: number
|
|
14
|
+
/** Every file read, in input order; a clean one has no violation. */
|
|
15
|
+
readonly scannedFiles: readonly string[]
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export function validateDoctrine(cwd: string, sourceFiles: readonly string[]): DoctrineResult {
|
|
17
19
|
const violations: DoctrineViolation[] = []
|
|
18
20
|
let scanned = 0
|
|
21
|
+
const scannedFiles: string[] = []
|
|
19
22
|
|
|
20
23
|
for (const file of sourceFiles) {
|
|
21
24
|
const abs = join(cwd, file)
|
|
@@ -24,6 +27,7 @@ export function validateDoctrine(cwd: string, sourceFiles: readonly string[]): D
|
|
|
24
27
|
const content = readFileSync(abs, 'utf-8')
|
|
25
28
|
const lines = content.split('\n')
|
|
26
29
|
scanned++
|
|
30
|
+
scannedFiles.push(file)
|
|
27
31
|
|
|
28
32
|
checkSchemaTableUsage(file, lines, violations)
|
|
29
33
|
checkRawPgTableImport(file, lines, violations)
|
|
@@ -31,7 +35,7 @@ export function validateDoctrine(cwd: string, sourceFiles: readonly string[]): D
|
|
|
31
35
|
checkHandWrittenTraitFields(file, lines, violations)
|
|
32
36
|
}
|
|
33
37
|
|
|
34
|
-
return { violations, scanned }
|
|
38
|
+
return { violations, scanned, scannedFiles }
|
|
35
39
|
}
|
|
36
40
|
|
|
37
41
|
const SCHEMA_TABLE_PATTERN = /\bschema\.table\s*\(/
|
|
@@ -155,7 +155,7 @@ export class Builder<
|
|
|
155
155
|
})
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
route(method: HttpMethod, path: string, options?: { bodyLimit?: number; screen?: RouteScreen }): Builder<TDeps, TDb> {
|
|
158
|
+
route(method: HttpMethod, path: string, options?: { bodyLimit?: number; screen?: RouteScreen; cancelOnDisconnect?: boolean }): Builder<TDeps, TDb> {
|
|
159
159
|
return new Builder<TDeps, TDb>({
|
|
160
160
|
...this.state,
|
|
161
161
|
route: {
|
|
@@ -163,6 +163,7 @@ export class Builder<
|
|
|
163
163
|
path,
|
|
164
164
|
...(options?.bodyLimit !== undefined ? { bodyLimit: options.bodyLimit } : {}),
|
|
165
165
|
...(options?.screen !== undefined ? { screen: options.screen } : {}),
|
|
166
|
+
...(options?.cancelOnDisconnect !== undefined ? { cancelOnDisconnect: options.cancelOnDisconnect } : {}),
|
|
166
167
|
},
|
|
167
168
|
})
|
|
168
169
|
}
|
package/src/request/di/types.ts
CHANGED
|
@@ -242,6 +242,8 @@ export interface RouteMeta {
|
|
|
242
242
|
readonly bodyLimit?: number
|
|
243
243
|
/** Pre-parse screen — inspects headers before the body is read, and may refuse. */
|
|
244
244
|
readonly screen?: RouteScreen
|
|
245
|
+
/** Abort the request when the client disconnects (default true). False: the handler runs to completion and commits. */
|
|
246
|
+
readonly cancelOnDisconnect?: boolean
|
|
245
247
|
}
|
|
246
248
|
|
|
247
249
|
/** HTTP method literal type — 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'. */
|
|
@@ -3,5 +3,5 @@ import { detectMediaType, resolveMediaType, SNIFF_BYTES } from './sniff.js'
|
|
|
3
3
|
|
|
4
4
|
/** HTTP namespace: `http.file()` builds a file response; `http.detectMediaType()` names bytes the way uploads do; the server itself is managed by manifold.start().
|
|
5
5
|
*
|
|
6
|
-
* A client that disconnects before its response is finished aborts the request: the in-flight statement is cancelled, the transaction rolled back and the connection released. The context's `signal` (`flow.require().signal`) aborts with a `RequestAbortedError` (499, `REQUEST_ABORTED`); pass it to the handler's own outbound calls (`fetch(url, { signal })`, a `quota.call` send). The aborted request is not an internal error: `onInternalError` is not called and nothing is sent. */
|
|
6
|
+
* A client that disconnects before its response is finished aborts the request: the in-flight statement is cancelled, the transaction rolled back and the connection released. The context's `signal` (`flow.require().signal`) aborts with a `RequestAbortedError` (499, `REQUEST_ABORTED`); pass it to the handler's own outbound calls (`fetch(url, { signal })`, a `quota.call` send). The aborted request is not an internal error: `onInternalError` is not called and nothing is sent. A route opts out with `.route(method, path, { cancelOnDisconnect: false })`, e.g. a webhook receiver whose sender abandons slow deliveries and does not redeliver: its signal never aborts and the transaction commits after the socket closes. */
|
|
7
7
|
export const http = { file, detectMediaType, resolveMediaType, SNIFF_BYTES } as const
|
|
@@ -444,8 +444,10 @@ export function createServer(instance: Manifold, config: ServerConfig = {}): Pip
|
|
|
444
444
|
const onClose = (): void => {
|
|
445
445
|
if (!reply.raw.writableEnded && !abort.signal.aborted) abort.abort(new RequestAbortedError(method, path))
|
|
446
446
|
}
|
|
447
|
-
|
|
448
|
-
|
|
447
|
+
if (handler.meta.route?.cancelOnDisconnect !== false) {
|
|
448
|
+
reply.raw.once('close', onClose)
|
|
449
|
+
reply.raw.once('finish', () => { reply.raw.removeListener('close', onClose) })
|
|
450
|
+
}
|
|
449
451
|
// Ends the upload stream and closes the connection if the body was left unread.
|
|
450
452
|
const finishUpload = (failed: boolean): void => {
|
|
451
453
|
if (settleUpload(uploaded?.stream) || (failed && upload !== null)) void reply.header('connection', 'close')
|
|
@@ -8,9 +8,15 @@ import { join } from 'node:path'
|
|
|
8
8
|
import { collectFlowStepSites } from '../check/flow-step-site.js'
|
|
9
9
|
import { analyzeCardinality, type AnalyzerEntry, type CardinalityResult } from './analyze.js'
|
|
10
10
|
|
|
11
|
+
export interface CardinalityCheckResult extends CardinalityResult {
|
|
12
|
+
/** Every file parsed, in input order; a clean one has no violation. */
|
|
13
|
+
readonly scannedFiles: readonly string[]
|
|
14
|
+
}
|
|
15
|
+
|
|
11
16
|
/** Analyzes `@downstream` cardinality across `sourceFiles` (paths relative to `cwd`). */
|
|
12
|
-
export function checkCardinality(cwd: string, sourceFiles: readonly string[]):
|
|
17
|
+
export function checkCardinality(cwd: string, sourceFiles: readonly string[]): CardinalityCheckResult {
|
|
13
18
|
const entries: AnalyzerEntry[] = []
|
|
19
|
+
const scannedFiles: string[] = []
|
|
14
20
|
|
|
15
21
|
for (const file of sourceFiles) {
|
|
16
22
|
const abs = join(cwd, file)
|
|
@@ -23,11 +29,12 @@ export function checkCardinality(cwd: string, sourceFiles: readonly string[]): C
|
|
|
23
29
|
// the type checker's diagnostic, not this check's.
|
|
24
30
|
continue
|
|
25
31
|
}
|
|
32
|
+
scannedFiles.push(file)
|
|
26
33
|
|
|
27
34
|
for (const site of sites) {
|
|
28
35
|
entries.push({ file, site })
|
|
29
36
|
}
|
|
30
37
|
}
|
|
31
38
|
|
|
32
|
-
return analyzeCardinality(entries)
|
|
39
|
+
return { ...analyzeCardinality(entries), scannedFiles }
|
|
33
40
|
}
|
|
@@ -37,6 +37,8 @@ export interface CoverageViolation {
|
|
|
37
37
|
export interface CoverageCheckResult {
|
|
38
38
|
readonly violations: readonly CoverageViolation[]
|
|
39
39
|
readonly scanned: number
|
|
40
|
+
/** Every file parsed, in input order; a clean one has no violation. */
|
|
41
|
+
readonly scannedFiles: readonly string[]
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
interface IoSite {
|
|
@@ -61,6 +63,7 @@ export function checkCoverage(
|
|
|
61
63
|
): CoverageCheckResult {
|
|
62
64
|
const violations: CoverageViolation[] = []
|
|
63
65
|
let scanned = 0
|
|
66
|
+
const scannedFiles: string[] = []
|
|
64
67
|
|
|
65
68
|
for (const file of sourceFiles) {
|
|
66
69
|
const abs = join(cwd, file)
|
|
@@ -78,6 +81,7 @@ export function checkCoverage(
|
|
|
78
81
|
continue
|
|
79
82
|
}
|
|
80
83
|
scanned++
|
|
84
|
+
scannedFiles.push(file)
|
|
81
85
|
|
|
82
86
|
const ioSites = collectIoSites(program, extras)
|
|
83
87
|
const flowStepRanges = collectFlowStepRanges(sites)
|
|
@@ -124,7 +128,7 @@ export function checkCoverage(
|
|
|
124
128
|
}
|
|
125
129
|
}
|
|
126
130
|
|
|
127
|
-
return { violations, scanned }
|
|
131
|
+
return { violations, scanned, scannedFiles }
|
|
128
132
|
}
|
|
129
133
|
|
|
130
134
|
interface FlowStepRange {
|
|
@@ -23,12 +23,15 @@ export interface FlowViolation {
|
|
|
23
23
|
export interface FlowCheckResult {
|
|
24
24
|
readonly violations: readonly FlowViolation[]
|
|
25
25
|
readonly scanned: number
|
|
26
|
+
/** Every file parsed, in input order; a clean one has no violation. */
|
|
27
|
+
readonly scannedFiles: readonly string[]
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
/** Validates every `@flow_step` annotation in `sourceFiles` (paths relative to `cwd`). */
|
|
29
31
|
export function checkFlowAnnotations(cwd: string, sourceFiles: readonly string[]): FlowCheckResult {
|
|
30
32
|
const violations: FlowViolation[] = []
|
|
31
33
|
let scanned = 0
|
|
34
|
+
const scannedFiles: string[] = []
|
|
32
35
|
|
|
33
36
|
for (const file of sourceFiles) {
|
|
34
37
|
const abs = join(cwd, file)
|
|
@@ -42,13 +45,14 @@ export function checkFlowAnnotations(cwd: string, sourceFiles: readonly string[]
|
|
|
42
45
|
continue
|
|
43
46
|
}
|
|
44
47
|
scanned++
|
|
48
|
+
scannedFiles.push(file)
|
|
45
49
|
|
|
46
50
|
for (const site of sites) {
|
|
47
51
|
collectSiteViolations(file, site, violations)
|
|
48
52
|
}
|
|
49
53
|
}
|
|
50
54
|
|
|
51
|
-
return { violations, scanned }
|
|
55
|
+
return { violations, scanned, scannedFiles }
|
|
52
56
|
}
|
|
53
57
|
|
|
54
58
|
function collectSiteViolations(file: string, site: FlowStepSite, out: FlowViolation[]): void {
|