@ontrails/regrade 1.0.0-beta.32 → 1.0.0-beta.39
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 +54 -0
- package/package.json +5 -5
- package/src/downstream/ast-rewrite.ts +200 -5
- package/src/downstream/collect.ts +16 -1
- package/src/downstream/export-restructure.ts +1588 -0
- package/src/downstream/report.ts +269 -47
- package/src/downstream/vocabulary-registry.ts +98 -0
- package/src/downstream/vocabulary.ts +1070 -102
- package/src/index.ts +27 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
# @ontrails/regrade
|
|
2
2
|
|
|
3
|
+
## 1.0.0-beta.39
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`65b362f`](https://github.com/outfitter-dev/trails/commit/65b362f65fa2a75e2121d1a8b31882d52fa0376b): Regrade history consolidates per transition, append-only: `regrade apply` appends a run entry stamped `{ planContentHash, lockHashAtRun }` to `.trails/regrade/history/<transition>.json` instead of overwriting lockhash-named files, identical re-runs are recognized as replays, the artifact carries a stable internal transition `id`, and `regrade check <transition>` verifies each recorded run at its own stamped lock. The report `history.status` union widens to `applied | checked | replay`.
|
|
8
|
+
- [`d3f7a25`](https://github.com/outfitter-dev/trails/commit/d3f7a25e66b6f0149ab2f33ac31ae1458860dce3): Regrade review entries carry structured review detail through Warden-backed and AST-backed classes: matched form, candidate replacement, machine-readable signals, preserve cautions, and a `judgment` field that distinguishes unresolved occurrence judgment from completed preserve/rewrite verdicts. Safe rewrite entries keep their existing report shape.
|
|
9
|
+
|
|
10
|
+
### Patch Changes
|
|
11
|
+
|
|
12
|
+
- [`b077fb7`](https://github.com/outfitter-dev/trails/commit/b077fb7ba6d9724cac6f0e59bc3fec9aec28984c): Add the export-restructure Regrade class family (TRL-1210). `export-restructure:cli-aliases` inverts legacy `cliAliases`/`trailsCliAliases` exports into `surfaceOverlay({ cli })` bindings inside the module's `trailsOverlays` export — adding the `@ontrails/core` import, deleting the legacy export, and routing anything it cannot prove safe (computed keys, spreads, in-module `aliases:` references) to `needs-review` with the exact target shape named. `export-restructure:mcp-trailheads` projects call-site MCP trailhead maps into `surfaceOverlay({ mcp })` group bindings: it rewrites in place when the same module exports `trailsOverlays`, and otherwise emits a classified `needs-review` handoff naming the module-overlay target while the call-site map stays as the richer-metadata override-in-context. Warden's fix-class union grows to `'export-restructure' | 'term-rewrite'`, `no-legacy-cli-alias-export` now advertises the `export-restructure` class, and `loadWardenRegradeClasses` supersedes `loadWardenTermRewriteClasses` (still exported) as the full Warden-routed class loader. Class-mode Regrade also gains the full plan lifecycle: `trails regrade plan --type class --class-ids ...` writes a `.trails/regrade/<slug>.json` plan carrying class ids, scope, and intent, `regrade check` re-runs the dry run and gates on outstanding rewrites or review, and `regrade apply` applies and graduates the plan to `.trails/regrade/history/<slug>-<hash>.json` — the same plan → check → apply → history evidence trail vocabulary regrades already had, now available to structural transforms. The class family ships for downstream apps bridging the pre-1.0 gap, so pre-cutover alias exports and trailhead maps migrate mechanically instead of by hand.
|
|
13
|
+
|
|
14
|
+
## 1.0.0-beta.38
|
|
15
|
+
|
|
16
|
+
## 1.0.0-beta.37
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- [`09f15de`](https://github.com/outfitter-dev/trails/commit/09f15def8fcc8c28b0d604f436e6eeed46da8f37): Stage wide-net Regrade expansion candidates as structured plan review inventory with evidence, status, and pending counts.
|
|
21
|
+
|
|
22
|
+
## 1.0.0-beta.36
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- [`6e63e48`](https://github.com/outfitter-dev/trails/commit/6e63e483617b84cb6868d0c4d58d5b5a8d3b9ed2): Complete the v1 grouped surface-entry vocabulary cutover from facet to trailhead, including Regrade dogfood support for governed string literal renames and composed AST rewrite application.
|
|
27
|
+
- [`26786a1`](https://github.com/outfitter-dev/trails/commit/26786a14acbe9ed03f69adbdac22968891e33df1): Persist vocabulary Regrade plan artifacts, expose plan/check/preview/apply flows across CLI and MCP, and write applied plan history for reviewed vocabulary migrations.
|
|
28
|
+
|
|
29
|
+
## 1.0.0-beta.35
|
|
30
|
+
|
|
31
|
+
### Patch Changes
|
|
32
|
+
|
|
33
|
+
- [`3842160`](https://github.com/outfitter-dev/trails/commit/3842160ac4030807cb2dfca8e1da75e03febccf2): Teach vocabulary regrades to discover simple prose morphology as deferred
|
|
34
|
+
review inventory so authored plans surface forms like `blazed` and `blazing`.
|
|
35
|
+
|
|
36
|
+
## 1.0.0-beta.34
|
|
37
|
+
|
|
38
|
+
### Patch Changes
|
|
39
|
+
|
|
40
|
+
- [`d67558b`](https://github.com/outfitter-dev/trails/commit/d67558bea3bfa363ed57e0f4091b6eccbf2a7710): Run governed AST symbol renames from the registry-backed `trails regrade` command path, including MCP parity, while preserving derived live API forms.
|
|
41
|
+
|
|
42
|
+
## 1.0.0-beta.33
|
|
43
|
+
|
|
44
|
+
### Patch Changes
|
|
45
|
+
|
|
46
|
+
- [`0c40138`](https://github.com/outfitter-dev/trails/commit/0c40138efb962e779710daa172bdfd756d9d992f): Harden vocabulary regrades by deferring Markdown code contexts for review and
|
|
47
|
+
exposing structured preserve rules through the Trails `regrade` command.
|
|
48
|
+
- [`7d65189`](https://github.com/outfitter-dev/trails/commit/7d65189cc33408755fe07a0b5679f1ed01123455): Expose derived live-API preserve inventory in vocabulary Regrade runs and have the Trails operator regrade surface derive current facet API preserves from live topo and MCP surface facts.
|
|
49
|
+
- [`fc002d5`](https://github.com/outfitter-dev/trails/commit/fc002d5669f4303427e99f45f9998fd0b0172bdb): Add governed AST identifier rename helpers and Warden residue detection for
|
|
50
|
+
active vocabulary symbol transitions.
|
|
51
|
+
- [`6ca0d8f`](https://github.com/outfitter-dev/trails/commit/6ca0d8f776801eee71ddd86cb88c198eaf5815fd): Add a typed governed-vocabulary transition registry that Warden owns and Regrade
|
|
52
|
+
can consume for migration planning.
|
|
53
|
+
- [`04bb8a4`](https://github.com/outfitter-dev/trails/commit/04bb8a42af4ada51a74b1d8c83697db92035b5e9): Expose vocabulary Regrade occurrence dispositions and disposition summary counts alongside mechanical verdicts.
|
|
54
|
+
|
|
55
|
+
Accept explicit preserve-rule dispositions through the `trails regrade` CLI/MCP contract.
|
|
56
|
+
|
|
3
57
|
## 1.0.0-beta.32
|
|
4
58
|
|
|
5
59
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ontrails/regrade",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.39",
|
|
4
4
|
"description": "Downstream migration reporting and safe rewrite helpers for Trails.",
|
|
5
5
|
"files": [
|
|
6
6
|
"src/**/*.ts",
|
|
@@ -22,12 +22,12 @@
|
|
|
22
22
|
"clean": "rm -rf dist *.tsbuildinfo"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@ontrails/core": "^1.0.0-beta.
|
|
26
|
-
"@ontrails/warden": "^1.0.0-beta.
|
|
25
|
+
"@ontrails/core": "^1.0.0-beta.39",
|
|
26
|
+
"@ontrails/warden": "^1.0.0-beta.39",
|
|
27
27
|
"zod": "^4.3.5"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@ontrails/testing": "^1.0.0-beta.
|
|
31
|
-
"@ontrails/topographer": "^1.0.0-beta.
|
|
30
|
+
"@ontrails/testing": "^1.0.0-beta.39",
|
|
31
|
+
"@ontrails/topographer": "^1.0.0-beta.39"
|
|
32
32
|
}
|
|
33
33
|
}
|
|
@@ -6,12 +6,15 @@ import type {
|
|
|
6
6
|
import {
|
|
7
7
|
applySourceEdits,
|
|
8
8
|
createSourceEdit,
|
|
9
|
+
getStringValue,
|
|
9
10
|
identifierName,
|
|
11
|
+
isStringLiteral,
|
|
10
12
|
offsetToLineColumn,
|
|
11
13
|
parseWithDiagnostics,
|
|
12
14
|
validateSourceEdits,
|
|
13
15
|
walkWithScopeContext,
|
|
14
16
|
} from '@ontrails/warden/ast';
|
|
17
|
+
import type { GovernedVocabularyTransition } from '@ontrails/warden';
|
|
15
18
|
|
|
16
19
|
import type {
|
|
17
20
|
RegradeClass,
|
|
@@ -231,12 +234,45 @@ export interface AstIdentifierRenameClassOptions {
|
|
|
231
234
|
readonly from: string;
|
|
232
235
|
readonly id?: string;
|
|
233
236
|
readonly reviewDeclarationTypes?: ReadonlySet<string>;
|
|
237
|
+
readonly shouldPreserve?: (
|
|
238
|
+
occurrence: AstIdentifierRenameOccurrence
|
|
239
|
+
) => boolean;
|
|
240
|
+
readonly to: string;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export interface AstIdentifierRenameOccurrence {
|
|
244
|
+
readonly end: number;
|
|
245
|
+
readonly from: string;
|
|
246
|
+
readonly path: string;
|
|
247
|
+
readonly source: string;
|
|
248
|
+
readonly start: number;
|
|
249
|
+
readonly to: string;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export interface AstStringLiteralRenameClassOptions {
|
|
253
|
+
readonly describe?: string;
|
|
254
|
+
readonly from: string;
|
|
255
|
+
readonly id?: string;
|
|
256
|
+
readonly shouldPreserve?: (
|
|
257
|
+
occurrence: AstIdentifierRenameOccurrence
|
|
258
|
+
) => boolean;
|
|
234
259
|
readonly to: string;
|
|
235
260
|
}
|
|
236
261
|
|
|
237
262
|
const isIdentifierNamed = (node: AstNode, name: string): boolean =>
|
|
238
263
|
node.type === 'Identifier' && identifierName(node) === name;
|
|
239
264
|
|
|
265
|
+
const identifierTokenSpan = (
|
|
266
|
+
node: AstNode,
|
|
267
|
+
source: string,
|
|
268
|
+
name: string
|
|
269
|
+
): { readonly end: number; readonly start: number } | null => {
|
|
270
|
+
const end = node.start + name.length;
|
|
271
|
+
return source.slice(node.start, end) === name
|
|
272
|
+
? { end, start: node.start }
|
|
273
|
+
: null;
|
|
274
|
+
};
|
|
275
|
+
|
|
240
276
|
export const createAstIdentifierRenameClass = (
|
|
241
277
|
options: AstIdentifierRenameClassOptions
|
|
242
278
|
): RegradeClass => {
|
|
@@ -253,14 +289,20 @@ export const createAstIdentifierRenameClass = (
|
|
|
253
289
|
return null;
|
|
254
290
|
}
|
|
255
291
|
|
|
256
|
-
const
|
|
257
|
-
if (
|
|
292
|
+
const span = identifierTokenSpan(node, context.source, options.from);
|
|
293
|
+
if (span === null) {
|
|
258
294
|
const location = offsetToLineColumn(context.source, node.start);
|
|
295
|
+
const caution = `Identifier "${options.from}" token span could not be verified; routed to review.`;
|
|
259
296
|
return {
|
|
260
297
|
detail: {
|
|
298
|
+
candidateReplacement: options.to,
|
|
261
299
|
expectedTarget: `Rename identifier "${options.from}" to "${options.to}".`,
|
|
300
|
+
judgment: 'unresolved',
|
|
301
|
+
matchedForm: options.from,
|
|
262
302
|
nodeKind: node.type,
|
|
263
|
-
|
|
303
|
+
preserveCautions: [caution],
|
|
304
|
+
reason: 'ast-identifier-token-span-unverified',
|
|
305
|
+
signals: ['ast:identifier-rename'],
|
|
264
306
|
span: {
|
|
265
307
|
column: location.column,
|
|
266
308
|
end: node.end,
|
|
@@ -271,16 +313,169 @@ export const createAstIdentifierRenameClass = (
|
|
|
271
313
|
symbol: options.from,
|
|
272
314
|
},
|
|
273
315
|
kind: 'review',
|
|
274
|
-
note:
|
|
316
|
+
note: caution,
|
|
317
|
+
reason: 'ast-identifier-token-span-unverified',
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (
|
|
322
|
+
options.shouldPreserve?.({
|
|
323
|
+
end: span.end,
|
|
324
|
+
from: options.from,
|
|
325
|
+
path: context.path,
|
|
326
|
+
source: context.source,
|
|
327
|
+
start: span.start,
|
|
328
|
+
to: options.to,
|
|
329
|
+
}) === true
|
|
330
|
+
) {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const declaration = context.getDeclaration(options.from);
|
|
335
|
+
if (declaration && reviewDeclarationTypes.has(declaration.type)) {
|
|
336
|
+
const location = offsetToLineColumn(context.source, span.start);
|
|
337
|
+
const caution = `Identifier "${options.from}" resolves to ${declaration.type}; routed to review.`;
|
|
338
|
+
return {
|
|
339
|
+
detail: {
|
|
340
|
+
candidateReplacement: options.to,
|
|
341
|
+
expectedTarget: `Rename identifier "${options.from}" to "${options.to}".`,
|
|
342
|
+
judgment: 'unresolved',
|
|
343
|
+
matchedForm: options.from,
|
|
344
|
+
nodeKind: node.type,
|
|
345
|
+
preserveCautions: [caution],
|
|
346
|
+
reason: 'ast-identifier-review-declaration',
|
|
347
|
+
signals: ['ast:identifier-rename'],
|
|
348
|
+
span: {
|
|
349
|
+
column: location.column,
|
|
350
|
+
end: span.end,
|
|
351
|
+
line: location.line,
|
|
352
|
+
start: span.start,
|
|
353
|
+
},
|
|
354
|
+
suggestedValidation: 'bun run typecheck',
|
|
355
|
+
symbol: options.from,
|
|
356
|
+
},
|
|
357
|
+
kind: 'review',
|
|
358
|
+
note: caution,
|
|
275
359
|
reason: 'ast-identifier-review-declaration',
|
|
276
360
|
};
|
|
277
361
|
}
|
|
278
362
|
|
|
279
363
|
return {
|
|
280
|
-
edit: createSourceEdit(
|
|
364
|
+
edit: createSourceEdit(span.start, span.end, options.to),
|
|
281
365
|
kind: 'edit',
|
|
282
366
|
note: `Renamed identifier "${options.from}" to "${options.to}".`,
|
|
283
367
|
};
|
|
284
368
|
},
|
|
285
369
|
});
|
|
286
370
|
};
|
|
371
|
+
|
|
372
|
+
const stringLiteralValueSpan = (
|
|
373
|
+
node: AstNode,
|
|
374
|
+
source: string,
|
|
375
|
+
value: string
|
|
376
|
+
): { readonly end: number; readonly start: number } | null => {
|
|
377
|
+
const raw = source.slice(node.start, node.end);
|
|
378
|
+
const relativeStart = raw.indexOf(value);
|
|
379
|
+
if (relativeStart === -1 || raw.includes(value, relativeStart + 1)) {
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
const start = node.start + relativeStart;
|
|
383
|
+
return { end: start + value.length, start };
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
export const createAstStringLiteralRenameClass = (
|
|
387
|
+
options: AstStringLiteralRenameClassOptions
|
|
388
|
+
): RegradeClass =>
|
|
389
|
+
createAstRewriteClass({
|
|
390
|
+
describe:
|
|
391
|
+
options.describe ??
|
|
392
|
+
`Rename string literal "${options.from}" to "${options.to}".`,
|
|
393
|
+
id:
|
|
394
|
+
options.id ?? `ast-string-literal-rename:${options.from}->${options.to}`,
|
|
395
|
+
visit: (node, context) => {
|
|
396
|
+
if (!isStringLiteral(node) || getStringValue(node) !== options.from) {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const span = stringLiteralValueSpan(node, context.source, options.from);
|
|
401
|
+
if (span === null) {
|
|
402
|
+
const location = offsetToLineColumn(context.source, node.start);
|
|
403
|
+
const caution = `String literal "${options.from}" token span could not be verified; routed to review.`;
|
|
404
|
+
return {
|
|
405
|
+
detail: {
|
|
406
|
+
candidateReplacement: options.to,
|
|
407
|
+
expectedTarget: `Rename string literal "${options.from}" to "${options.to}".`,
|
|
408
|
+
judgment: 'unresolved',
|
|
409
|
+
matchedForm: options.from,
|
|
410
|
+
nodeKind: node.type,
|
|
411
|
+
preserveCautions: [caution],
|
|
412
|
+
reason: 'ast-string-literal-token-span-unverified',
|
|
413
|
+
signals: ['ast:string-literal-rename'],
|
|
414
|
+
span: {
|
|
415
|
+
column: location.column,
|
|
416
|
+
end: node.end,
|
|
417
|
+
line: location.line,
|
|
418
|
+
start: node.start,
|
|
419
|
+
},
|
|
420
|
+
suggestedValidation: 'bun run typecheck',
|
|
421
|
+
symbol: options.from,
|
|
422
|
+
},
|
|
423
|
+
kind: 'review',
|
|
424
|
+
note: caution,
|
|
425
|
+
reason: 'ast-string-literal-token-span-unverified',
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (
|
|
430
|
+
options.shouldPreserve?.({
|
|
431
|
+
end: span.end,
|
|
432
|
+
from: options.from,
|
|
433
|
+
path: context.path,
|
|
434
|
+
source: context.source,
|
|
435
|
+
start: span.start,
|
|
436
|
+
to: options.to,
|
|
437
|
+
}) === true
|
|
438
|
+
) {
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return {
|
|
443
|
+
edit: createSourceEdit(span.start, span.end, options.to),
|
|
444
|
+
kind: 'edit',
|
|
445
|
+
note: `Renamed string literal "${options.from}" to "${options.to}".`,
|
|
446
|
+
};
|
|
447
|
+
},
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
export const createGovernedAstIdentifierRenameClasses = (
|
|
451
|
+
transition: GovernedVocabularyTransition,
|
|
452
|
+
options: {
|
|
453
|
+
readonly shouldPreserve?: (
|
|
454
|
+
occurrence: AstIdentifierRenameOccurrence
|
|
455
|
+
) => boolean;
|
|
456
|
+
} = {}
|
|
457
|
+
): readonly RegradeClass[] => [
|
|
458
|
+
...transition.symbolRenames.map((rename) =>
|
|
459
|
+
createAstIdentifierRenameClass({
|
|
460
|
+
describe: `Rename governed symbol "${rename.from}" to "${rename.to}" for ${transition.id}.`,
|
|
461
|
+
from: rename.from,
|
|
462
|
+
id: `ast-symbol-rename:${transition.id}:${rename.from}->${rename.to}`,
|
|
463
|
+
reviewDeclarationTypes: new Set(rename.reviewDeclarationTypes),
|
|
464
|
+
...(options.shouldPreserve === undefined
|
|
465
|
+
? {}
|
|
466
|
+
: { shouldPreserve: options.shouldPreserve }),
|
|
467
|
+
to: rename.to,
|
|
468
|
+
})
|
|
469
|
+
),
|
|
470
|
+
...transition.stringLiteralRenames.map((rename) =>
|
|
471
|
+
createAstStringLiteralRenameClass({
|
|
472
|
+
describe: `Rename governed string literal "${rename.from}" to "${rename.to}" for ${transition.id}.`,
|
|
473
|
+
from: rename.from,
|
|
474
|
+
id: `ast-string-literal-rename:${transition.id}:${rename.from}->${rename.to}`,
|
|
475
|
+
...(options.shouldPreserve === undefined
|
|
476
|
+
? {}
|
|
477
|
+
: { shouldPreserve: options.shouldPreserve }),
|
|
478
|
+
to: rename.to,
|
|
479
|
+
})
|
|
480
|
+
),
|
|
481
|
+
];
|
|
@@ -73,6 +73,8 @@ export interface DownstreamCollectionOptions {
|
|
|
73
73
|
readonly extensions?: readonly string[];
|
|
74
74
|
/** Root-relative path globs to skip before collection. */
|
|
75
75
|
readonly exclude?: readonly string[];
|
|
76
|
+
/** Root-relative path globs to collect. Omit to collect all matching files. */
|
|
77
|
+
readonly include?: readonly string[];
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
/** Outcome of classifying a single directory entry. */
|
|
@@ -204,7 +206,15 @@ export const collectDownstreamSources = (
|
|
|
204
206
|
options
|
|
205
207
|
);
|
|
206
208
|
if (classification.action === 'collect') {
|
|
207
|
-
|
|
209
|
+
if (
|
|
210
|
+
options.include !== undefined &&
|
|
211
|
+
options.include.length > 0 &&
|
|
212
|
+
!matchesAnyPathGlob(path, options.include)
|
|
213
|
+
) {
|
|
214
|
+
skipped.push({ path, reason: 'not-included-glob' });
|
|
215
|
+
} else {
|
|
216
|
+
files.push({ absolutePath, path });
|
|
217
|
+
}
|
|
208
218
|
} else if (classification.action === 'recurse') {
|
|
209
219
|
queue.push(absolutePath);
|
|
210
220
|
} else {
|
|
@@ -231,6 +241,10 @@ export const collectDownstreamSourcesInput = z.object({
|
|
|
231
241
|
.array(z.string())
|
|
232
242
|
.optional()
|
|
233
243
|
.describe('Directory names to skip during collection'),
|
|
244
|
+
include: z
|
|
245
|
+
.array(z.string())
|
|
246
|
+
.optional()
|
|
247
|
+
.describe('Root-relative path globs to collect'),
|
|
234
248
|
root: z
|
|
235
249
|
.string()
|
|
236
250
|
.describe('Absolute path to the downstream repo root to scan'),
|
|
@@ -273,6 +287,7 @@ export const collectDownstreamSourcesTrail = trail(
|
|
|
273
287
|
? {}
|
|
274
288
|
: { extensions: input.extensions }),
|
|
275
289
|
...(input.exclude === undefined ? {} : { exclude: input.exclude }),
|
|
290
|
+
...(input.include === undefined ? {} : { include: input.include }),
|
|
276
291
|
...(input.ignoredDirectories === undefined
|
|
277
292
|
? {}
|
|
278
293
|
: { ignoredDirectories: input.ignoredDirectories }),
|