opencodekit 0.23.0 → 0.23.2
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/dist/index.js +354 -825
- package/dist/template/.opencode/AGENTS.md +15 -0
- package/dist/template/.opencode/command/init.md +198 -34
- package/dist/template/.opencode/context/fallow.md +137 -0
- package/dist/template/.opencode/dcp-prompts/overrides/compress-range.md +89 -0
- package/dist/template/.opencode/opencode.json +110 -315
- package/dist/template/.opencode/plugin/README.md +10 -0
- package/dist/template/.opencode/plugin/memory/compile.ts +171 -186
- package/dist/template/.opencode/plugin/memory/index-generator.ts +118 -133
- package/dist/template/.opencode/plugin/memory/lint.ts +253 -275
- package/dist/template/.opencode/plugin/memory/tools.ts +224 -268
- package/dist/template/.opencode/plugin/memory/validate.ts +154 -164
- package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/web-search-preview.ts +13 -30
- package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/web-search-shared.ts +25 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/web-search.ts +17 -34
- package/dist/template/.opencode/plugin/session-summary.ts +542 -0
- package/dist/template/.opencode/plugin/srcwalk.ts +775 -661
- package/dist/template/.opencode/skill/condition-based-waiting/example.ts +15 -2
- package/dist/template/.opencode/skill/fallow/SKILL.md +409 -0
- package/dist/template/.opencode/skill/fallow/references/cli-reference.md +1905 -0
- package/dist/template/.opencode/skill/fallow/references/gotchas.md +644 -0
- package/dist/template/.opencode/skill/fallow/references/patterns.md +791 -0
- package/package.json +2 -2
|
@@ -0,0 +1,644 @@
|
|
|
1
|
+
# Fallow: Critical Gotchas
|
|
2
|
+
|
|
3
|
+
Common pitfalls and their correct solutions when working with fallow.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## `fix` Requires `--yes` in Non-TTY Environments
|
|
8
|
+
|
|
9
|
+
The `fix` command prompts for confirmation in interactive terminals. In agent subprocesses, CI pipelines, or piped input (non-TTY), the `--yes` flag is mandatory. Without it, `fix` exits with code 2 and an error.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# WRONG: fix exits with code 2 in non-TTY
|
|
13
|
+
fallow fix --format json --quiet
|
|
14
|
+
|
|
15
|
+
# CORRECT: always use --dry-run first, then --yes
|
|
16
|
+
fallow fix --dry-run --format json --quiet # preview
|
|
17
|
+
fallow fix --yes --format json --quiet # apply
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Always preview with `--dry-run` before applying. This is a destructive operation that modifies source files.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Don't Create Config Unless Needed
|
|
25
|
+
|
|
26
|
+
Fallow works with zero configuration for most projects thanks to 118 auto-detecting framework plugins. Creating an unnecessary config file can mask issues or override detection behavior.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# WRONG: creating config for a standard Next.js project
|
|
30
|
+
fallow init
|
|
31
|
+
# This may override auto-detected settings
|
|
32
|
+
|
|
33
|
+
# CORRECT: run analysis first with zero config
|
|
34
|
+
fallow dead-code --format json --quiet
|
|
35
|
+
# Only create config if you need to customize rules, ignore patterns, or entry points
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Only create a config when you need to:
|
|
39
|
+
- Change rule severity levels for incremental adoption
|
|
40
|
+
- Add custom ignore patterns or ignore dependencies
|
|
41
|
+
- Specify additional entry points not auto-detected
|
|
42
|
+
- Configure duplication detection settings
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Use `--format json` for Agent Consumption
|
|
47
|
+
|
|
48
|
+
Human-formatted output contains ANSI colors, progress bars, and timing info. Never parse it programmatically.
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# WRONG: parsing human output
|
|
52
|
+
fallow dead-code | grep "unused"
|
|
53
|
+
|
|
54
|
+
# CORRECT: use structured JSON
|
|
55
|
+
fallow dead-code --format json --quiet
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The `--quiet` flag suppresses progress bars on stderr. Without it, stderr output may interfere with stdout parsing.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## `--changed-since` Shows Only New Issues
|
|
63
|
+
|
|
64
|
+
The `--changed-since` flag limits analysis to files modified since a git ref. It only reports issues in those files, not all issues in the project. Works with both `dead-code` and `dupes`.
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# This only shows issues in files changed since main
|
|
68
|
+
fallow dead-code --format json --quiet --changed-since main
|
|
69
|
+
|
|
70
|
+
# Same for duplication — only clone groups involving changed files
|
|
71
|
+
fallow dupes --format json --quiet --changed-since main
|
|
72
|
+
|
|
73
|
+
# This shows ALL issues in the project
|
|
74
|
+
fallow dead-code --format json --quiet
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Don't use `--changed-since` when auditing the full project. Use it for PR checks and incremental CI.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Filter Flags Are Additive
|
|
82
|
+
|
|
83
|
+
Issue type filter flags (`--unused-exports`, `--unused-files`, etc.) are inclusive. They select which issue types to show. Using multiple flags shows the union.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# Shows only unused exports
|
|
87
|
+
fallow dead-code --format json --quiet --unused-exports
|
|
88
|
+
|
|
89
|
+
# Shows unused exports AND unused files
|
|
90
|
+
fallow dead-code --format json --quiet --unused-exports --unused-files
|
|
91
|
+
|
|
92
|
+
# Shows ALL issue types (default when no filter is specified)
|
|
93
|
+
fallow dead-code --format json --quiet
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Syntactic Analysis: No TypeScript Compiler
|
|
99
|
+
|
|
100
|
+
Fallow uses Oxc for pure syntactic analysis. It does not run the TypeScript compiler. This means:
|
|
101
|
+
|
|
102
|
+
- **Fully dynamic imports** (`import(variable)`) are not resolved. Only static strings, template literals with static prefixes, `import.meta.glob`, and `require.context` patterns
|
|
103
|
+
- **Value-level type narrowing** is not performed. Fallow can't know that `if (x instanceof Foo)` means `Foo` is "used"
|
|
104
|
+
- **Conditional exports** based on runtime values are not analyzed
|
|
105
|
+
- **Function overload signatures are deduplicated**: TypeScript function overloads (multiple signatures for the same function name) are merged into a single export. They are not reported as separate unused exports
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
// RESOLVED: static pattern with prefix
|
|
109
|
+
import(`./locales/${lang}.json`);
|
|
110
|
+
|
|
111
|
+
// RESOLVED: import.meta.glob
|
|
112
|
+
const modules = import.meta.glob('./modules/*.ts');
|
|
113
|
+
|
|
114
|
+
// NOT RESOLVED: fully dynamic
|
|
115
|
+
const mod = import(someVariable);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
If fallow falsely flags something due to dynamic patterns, use inline suppression:
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
// fallow-ignore-next-line unused-export
|
|
122
|
+
export const dynamicallyUsed = createHandler();
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Re-Export Chains Are Resolved
|
|
128
|
+
|
|
129
|
+
Fallow fully resolves `export *` and named re-export chains through barrel files. An export consumed through a chain of barrel files is NOT falsely flagged.
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
// src/utils.ts
|
|
133
|
+
export const helper = () => {}; // NOT flagged, used via barrel chain
|
|
134
|
+
|
|
135
|
+
// src/index.ts (barrel)
|
|
136
|
+
export * from './utils';
|
|
137
|
+
|
|
138
|
+
// src/app.ts
|
|
139
|
+
import { helper } from './index'; // Resolves through the chain
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
If an export IS flagged as unused despite being in a barrel file, it means no downstream consumer actually imports it. The barrel file re-exports it, but nobody uses it from there.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Exit Code 1 vs 2
|
|
147
|
+
|
|
148
|
+
| Code | Meaning | Action |
|
|
149
|
+
|------|---------|--------|
|
|
150
|
+
| 0 | No error-severity issues | Success |
|
|
151
|
+
| 1 | Error-severity issues found | Review findings |
|
|
152
|
+
| 2 | Runtime error (`fix` without `--yes` in non-TTY, invalid config) | Fix config or add `--yes` |
|
|
153
|
+
|
|
154
|
+
Exit code 1 is triggered by issues with `"error"` severity in the rules config. Without a rules section, all issue types default to `"error"`. Use the rules system to control which issues fail CI:
|
|
155
|
+
|
|
156
|
+
```jsonc
|
|
157
|
+
// Only fail on unused files and deps, warn on everything else
|
|
158
|
+
{
|
|
159
|
+
"rules": {
|
|
160
|
+
"unused-files": "error",
|
|
161
|
+
"unused-dependencies": "error",
|
|
162
|
+
"unused-exports": "warn",
|
|
163
|
+
"unused-types": "warn"
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## `--fail-on-issues` Promotes Warn to Error
|
|
171
|
+
|
|
172
|
+
The `--fail-on-issues` flag promotes all `warn`-severity rules to `error` for that run. This means exit code 1 for ANY reported issue.
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# With rules: { "unused-exports": "warn" }
|
|
176
|
+
|
|
177
|
+
# This exits 0 even with warn-level findings
|
|
178
|
+
fallow dead-code --format json --quiet
|
|
179
|
+
|
|
180
|
+
# This exits 1 if ANY issue is found (warn promoted to error)
|
|
181
|
+
fallow dead-code --format json --quiet --fail-on-issues
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Use `--fail-on-issues` for strict CI gates. Use the rules system for gradual adoption.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Baseline Comparison Tracks Issue Identity
|
|
189
|
+
|
|
190
|
+
Baselines track issues by identity (file + issue type + name), not by count. Adding a new unused export while fixing an old one doesn't cancel out.
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
# Save current state as baseline
|
|
194
|
+
fallow dead-code --format json --quiet --save-baseline fallow-baselines/dead-code.json
|
|
195
|
+
|
|
196
|
+
# Later: only fail on NEW issues not in the baseline
|
|
197
|
+
fallow dead-code --format json --quiet --baseline fallow-baselines/dead-code.json --fail-on-issues
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Commit the baseline file to your repo. Update it periodically as you fix existing issues.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Duplication Modes Affect What's Detected
|
|
205
|
+
|
|
206
|
+
The detection mode significantly affects results. Choose based on your needs:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
# strict: exact token match only
|
|
210
|
+
fallow dupes --format json --quiet --mode strict
|
|
211
|
+
# Catches: copy-pasted code with zero changes
|
|
212
|
+
|
|
213
|
+
# mild (default): syntax normalized
|
|
214
|
+
fallow dupes --format json --quiet --mode mild
|
|
215
|
+
# Catches: whitespace and semicolon differences
|
|
216
|
+
|
|
217
|
+
# weak: literal values normalized
|
|
218
|
+
fallow dupes --format json --quiet --mode weak
|
|
219
|
+
# Catches: same structure with different strings/numbers
|
|
220
|
+
|
|
221
|
+
# semantic: identifier names normalized
|
|
222
|
+
fallow dupes --format json --quiet --mode semantic
|
|
223
|
+
# Catches: same logic with renamed variables
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
`semantic` mode produces the most findings but may include false positives where similar structure is coincidental.
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Workspace Flag Scopes Output, Not Analysis
|
|
231
|
+
|
|
232
|
+
The `--workspace` flag scopes **output** to a single package, but the full cross-workspace module graph is still built. This means:
|
|
233
|
+
|
|
234
|
+
- Imports from other workspace packages are still resolved
|
|
235
|
+
- Re-export chains crossing package boundaries are still tracked
|
|
236
|
+
- Only issues IN the specified package are reported
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
# Analyze everything, show only issues in "my-package"
|
|
240
|
+
fallow dead-code --format json --quiet --workspace my-package
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Production Mode Excludes Test Files
|
|
246
|
+
|
|
247
|
+
`--production` excludes test/dev files and only analyzes production scripts. This changes what's reported:
|
|
248
|
+
|
|
249
|
+
- Test files (`*.test.*`, `*.spec.*`, `*.stories.*`, `__tests__/**`) are excluded
|
|
250
|
+
- Only `start`, `build`, `serve`, `preview`, `prepare` scripts are analyzed
|
|
251
|
+
- Unused devDependencies are NOT reported (forced to `off`)
|
|
252
|
+
- Type-only production dependencies ARE reported (should be devDependencies)
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
# WRONG: using --production for a full audit
|
|
256
|
+
fallow dead-code --format json --quiet --production
|
|
257
|
+
# Misses test-file dead code and devDependency issues
|
|
258
|
+
|
|
259
|
+
# CORRECT: use --production only for production-focused CI
|
|
260
|
+
fallow dead-code --format json --quiet --production --fail-on-issues
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Watch Mode Is Not for Agents
|
|
266
|
+
|
|
267
|
+
The `watch` command starts an interactive file watcher that never exits. Never use it in agent workflows.
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
# WRONG: this will hang forever
|
|
271
|
+
fallow watch
|
|
272
|
+
|
|
273
|
+
# CORRECT: run one-shot analysis
|
|
274
|
+
fallow dead-code --format json --quiet
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Suppressing Duplication False Positives
|
|
280
|
+
|
|
281
|
+
Code duplication has its own suppression token: `code-duplication`. Use it for intentionally similar code (e.g., test helpers, generated patterns).
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
// WRONG: using the wrong token
|
|
285
|
+
// fallow-ignore-file unused-export
|
|
286
|
+
// This suppresses dead code, not duplication
|
|
287
|
+
|
|
288
|
+
// CORRECT: suppress duplication for a specific line
|
|
289
|
+
// fallow-ignore-next-line code-duplication
|
|
290
|
+
const handler = createStandardHandler(config);
|
|
291
|
+
|
|
292
|
+
// CORRECT: suppress all duplication in a file
|
|
293
|
+
// fallow-ignore-file code-duplication
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
This is separate from the dead code suppression tokens. See the full list of valid tokens in the [CLI Reference](cli-reference.md#inline-suppression-comments).
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Decorated Members Are Skipped By Default
|
|
301
|
+
|
|
302
|
+
Class members with decorators (NestJS `@Get()`, Angular `@Input()`, TypeORM `@Column()`, etc.) are excluded from unused member detection by default. Decorator-driven frameworks consume these via reflection at runtime, so reporting them as unused would be a false positive.
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
class UserController {
|
|
306
|
+
@Get('/users')
|
|
307
|
+
getUsers() { ... } // NOT flagged, has decorator
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Opt specific decorators out via `ignoreDecorators`
|
|
312
|
+
|
|
313
|
+
If you use utility decorators that DO NOT imply reflective use (Playwright's `@step("label")`, internal labeling decorators like `@measure`, `@log`, `@retry`), list their names in the `ignoreDecorators` config option so the methods carrying them are checked for usage like undecorated methods.
|
|
314
|
+
|
|
315
|
+
```jsonc
|
|
316
|
+
// .fallowrc.json
|
|
317
|
+
{
|
|
318
|
+
"ignoreDecorators": ["@step"]
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Conservative semantics: a method carrying any decorator NOT in the list still gets skipped. So `@step` + `@Inject` on the same method stays treated as framework-managed. Matching rule: entries containing `.` (`"decorators.log"`) match the full dotted path; bare entries (`"step"` or `"decorators"`) match the leftmost segment, so a single bare `"decorators"` entry collapses an entire `@decorators.*` namespace. Both `"@step"` and `"step"` round-trip equivalently. Unmatched entries (a decorator name in the config that never appears in your codebase) surface as a one-time warning at end of run.
|
|
323
|
+
|
|
324
|
+
The default empty list preserves today's skip-all behavior, so existing NestJS / Angular / TypeORM projects see no change.
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## JSDoc Visibility Tags Keep Exports Alive
|
|
329
|
+
|
|
330
|
+
Exports annotated with `/** @public */`, `/** @internal */`, `/** @beta */`, `/** @alpha */`, or `/** @api public */` are never reported as unused. This is designed for library authors whose exports are consumed by external projects not visible to fallow.
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
// NOT flagged: @public annotation
|
|
334
|
+
/** @public */
|
|
335
|
+
export const createWidget = () => {};
|
|
336
|
+
|
|
337
|
+
// NOT flagged: @internal annotation
|
|
338
|
+
/** @internal */
|
|
339
|
+
export const resetState = () => {};
|
|
340
|
+
|
|
341
|
+
// NOT flagged: @beta annotation
|
|
342
|
+
/** @beta */
|
|
343
|
+
export const experimentalFeature = () => {};
|
|
344
|
+
|
|
345
|
+
// NOT flagged: @alpha annotation
|
|
346
|
+
/** @alpha */
|
|
347
|
+
export const unstableApi = () => {};
|
|
348
|
+
|
|
349
|
+
// NOT flagged: @api public variant
|
|
350
|
+
/** @api public */
|
|
351
|
+
export interface WidgetConfig {}
|
|
352
|
+
|
|
353
|
+
// STILL flagged: line comments don't count
|
|
354
|
+
// @public
|
|
355
|
+
export const notProtected = () => {};
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Only `/** */` JSDoc block comments are recognized. Line comments (`// @public`) are ignored.
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
## `@expected-unused` JSDoc Tag for Intentional Dead Code
|
|
363
|
+
|
|
364
|
+
Exports annotated with `/** @expected-unused */` are treated as intentionally unused. They are excluded from unused export detection AND tracked for staleness. If the export later becomes used (imported by another module), fallow reports the `@expected-unused` tag as stale via the `stale-suppressions` rule.
|
|
365
|
+
|
|
366
|
+
```typescript
|
|
367
|
+
// NOT flagged as unused: @expected-unused annotation
|
|
368
|
+
/** @expected-unused */
|
|
369
|
+
export const deprecatedHelper = () => {};
|
|
370
|
+
|
|
371
|
+
// If something starts importing deprecatedHelper,
|
|
372
|
+
// fallow reports the @expected-unused tag as stale
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Use `@expected-unused` instead of `// fallow-ignore-next-line` when you want fallow to notify you if the export becomes referenced again. The `stale-suppressions` rule (default: `warn`) controls severity.
|
|
376
|
+
|
|
377
|
+
Only `/** */` JSDoc block comments are recognized. The tag works on all export types.
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## Stale Suppression Detection
|
|
382
|
+
|
|
383
|
+
Fallow detects `// fallow-ignore` comments and `@expected-unused` JSDoc tags that no longer match any issue. This prevents suppression comments from silently hiding issues that have been resolved or moved.
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
// STALE: the export below is actually used now
|
|
387
|
+
// fallow-ignore-next-line unused-export
|
|
388
|
+
export const helper = () => {}; // imported in app.ts
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
Use `--stale-suppressions` to filter for only stale suppression findings. The `stale-suppressions` rule defaults to `warn`. Set to `error` in CI to enforce suppression hygiene:
|
|
392
|
+
|
|
393
|
+
```jsonc
|
|
394
|
+
{
|
|
395
|
+
"rules": {
|
|
396
|
+
"stale-suppressions": "error"
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## JSDoc `import()` Types Count as References
|
|
404
|
+
|
|
405
|
+
Types referenced only from JSDoc `import()` annotations are tracked as type-only imports, so the referenced exports are not flagged as unused. This works for plain JavaScript files that want TypeScript types without converting to `.ts`.
|
|
406
|
+
|
|
407
|
+
```js
|
|
408
|
+
// src/app.js
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* @param cfg {import('./types.ts').Config}
|
|
412
|
+
* @returns {import('./types.ts').Result}
|
|
413
|
+
*/
|
|
414
|
+
function boot(cfg) {
|
|
415
|
+
return { ok: true };
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
Fallow treats `Config` and `Result` in `./types.ts` as used. Works with `@param`, `@returns`, `@type`, `@typedef`, `@callback`, union annotations (`{import('./a').A | import('./b').B}`), nested member access, bare package specifiers, and parent-relative paths. Only `/** */` blocks are scanned.
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
## JSX `<script src>` and `<link href>` Are Asset References
|
|
424
|
+
|
|
425
|
+
Inside JSX/TSX files, lowercase intrinsic `<script src="...">` and `<link rel="stylesheet|modulepreload" href="...">` are tracked as asset references, same as in plain HTML files. This is needed for SSR frameworks like Hono where layout components emit HTML via JSX.
|
|
426
|
+
|
|
427
|
+
```tsx
|
|
428
|
+
// src/layout.tsx
|
|
429
|
+
|
|
430
|
+
export const Layout = () => (
|
|
431
|
+
<html>
|
|
432
|
+
<head>
|
|
433
|
+
<link rel="stylesheet" href="/static/style.css" />
|
|
434
|
+
<script src="/static/app.js"></script>
|
|
435
|
+
</head>
|
|
436
|
+
<body><h1>Hello</h1></body>
|
|
437
|
+
</html>
|
|
438
|
+
);
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
Fallow marks `static/style.css` and `static/app.js` as reachable. Root-relative paths (starting with `/`) resolve from the source file's parent directory first, then the project root, matching how Vite/Parcel/Hono serve static assets. Only `StringLiteral` attribute values are captured: expression containers (`href={someVar}`) and capitalized React-style components (`<Script>`, `<Link>`) are intentionally ignored because they have component-specific semantics.
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
## GraphQL `#import` Documents Are Tracked
|
|
446
|
+
|
|
447
|
+
GraphQL `.graphql` and `.gql` files can keep nearby fragment documents reachable with relative `#import` comments. Fallow tracks `./` and `../` specifiers, including extensionless imports that resolve through `.graphql` and `.gql`; package-style specifiers are ignored.
|
|
448
|
+
|
|
449
|
+
```graphql
|
|
450
|
+
# src/query.graphql
|
|
451
|
+
|
|
452
|
+
#import "./fragments/user-fields"
|
|
453
|
+
|
|
454
|
+
query CurrentUser {
|
|
455
|
+
currentUser {
|
|
456
|
+
...UserFields
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
Fallow marks `src/fragments/user-fields.graphql` or `src/fragments/user-fields.gql` as reachable when either file exists. A typo in the relative path is reported as an unresolved import instead of silently dropping the edge.
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## Library Packages: Use `publicPackages` Instead of Visibility Tags
|
|
466
|
+
|
|
467
|
+
In monorepos, shared library packages have exported APIs consumed by external consumers not visible to fallow. Instead of annotating every export with `/** @public */` (or `@internal`, `@beta`, `@alpha`), use the `publicPackages` config to mark entire workspace packages as public libraries. Exports and exported enum/class members from these packages are excluded from unused API detection.
|
|
468
|
+
|
|
469
|
+
```jsonc
|
|
470
|
+
{
|
|
471
|
+
"publicPackages": ["@myorg/shared-lib", "@myorg/ui-kit"]
|
|
472
|
+
}
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
This is the correct solution for library false positives in monorepos. Only use JSDoc visibility tags (`/** @public */`, `/** @internal */`, etc.) for individual exports in application packages.
|
|
476
|
+
|
|
477
|
+
---
|
|
478
|
+
|
|
479
|
+
## Dynamically Loaded Files: Use `dynamicallyLoaded`
|
|
480
|
+
|
|
481
|
+
Files loaded at runtime via plugin systems, locale directories, or lazy module patterns are not statically reachable from entry points. Use `dynamicallyLoaded` to mark these files as always-used.
|
|
482
|
+
|
|
483
|
+
```jsonc
|
|
484
|
+
{
|
|
485
|
+
"dynamicallyLoaded": ["plugins/**/*.ts", "locales/**/*.json"]
|
|
486
|
+
}
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
```bash
|
|
490
|
+
# WRONG: suppressing individual files
|
|
491
|
+
# fallow-ignore-file unused-file (in each plugin file)
|
|
492
|
+
|
|
493
|
+
# CORRECT: declare the pattern in config
|
|
494
|
+
# { "dynamicallyLoaded": ["plugins/**/*.ts"] }
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
This is preferable to adding inline suppression comments to every dynamically loaded file.
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
## Class Instance Members Are Tracked
|
|
502
|
+
|
|
503
|
+
Fallow tracks class member usage through instance variables. If you instantiate a class and call methods on the instance, those members are correctly marked as used.
|
|
504
|
+
|
|
505
|
+
```typescript
|
|
506
|
+
class MyService {
|
|
507
|
+
greet() { return 'hello'; } // NOT flagged: used via instance
|
|
508
|
+
unused() { return 'bye'; } // Flagged: never called
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const svc = new MyService();
|
|
512
|
+
svc.greet();
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
This also handles whole-object instance patterns (`Object.values(svc)`, `{ ...svc }`, `for..in`) conservatively (all members marked as used). The tracking is scope-unaware, so same-named variables in different scopes may produce false negatives (not false positives).
|
|
516
|
+
|
|
517
|
+
---
|
|
518
|
+
|
|
519
|
+
## Type-Only Dependencies Should Be devDependencies
|
|
520
|
+
|
|
521
|
+
In `--production` mode, fallow detects production dependencies that are only imported via `import type`. Since TypeScript types are erased at runtime, these packages should be in `devDependencies` instead.
|
|
522
|
+
|
|
523
|
+
```typescript
|
|
524
|
+
// If "zod" is in dependencies (not devDependencies):
|
|
525
|
+
import type { ZodSchema } from 'zod'; // Flagged as type-only dependency
|
|
526
|
+
|
|
527
|
+
// This is a real import, not type-only:
|
|
528
|
+
import { z } from 'zod'; // NOT flagged
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
```bash
|
|
532
|
+
# Detect type-only dependencies (reported automatically with --production)
|
|
533
|
+
fallow dead-code --format json --quiet --production
|
|
534
|
+
|
|
535
|
+
# Suppress for a specific dependency
|
|
536
|
+
# fallow-ignore-next-line type-only-dependency
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
The `type-only-dependencies` rule defaults to `warn`. Suppress with `"type-only-dependencies": "off"` in your rules config if you intentionally keep type-only packages in production dependencies.
|
|
540
|
+
|
|
541
|
+
---
|
|
542
|
+
|
|
543
|
+
## Test-Only Dependencies Should Be devDependencies
|
|
544
|
+
|
|
545
|
+
Fallow detects production dependencies that are only imported from test files (`*.test.*`, `*.spec.*`, `__tests__/**`). Since these packages are never used in production code, they should be in `devDependencies` instead.
|
|
546
|
+
|
|
547
|
+
```typescript
|
|
548
|
+
// If "msw" is in dependencies (not devDependencies):
|
|
549
|
+
// src/handlers.test.ts
|
|
550
|
+
import { setupServer } from 'msw/node'; // Flagged as test-only dependency
|
|
551
|
+
|
|
552
|
+
// src/app.ts — no imports of "msw" here
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
```bash
|
|
556
|
+
# Detect test-only dependencies (reported automatically)
|
|
557
|
+
fallow dead-code --format json --quiet
|
|
558
|
+
|
|
559
|
+
# Suppress for a specific dependency
|
|
560
|
+
# fallow-ignore-next-line test-only-dependency
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
The `test-only-dependencies` rule defaults to `warn`. Suppress with `"test-only-dependencies": "off"` in your rules config if you intentionally keep test-only packages in production dependencies.
|
|
564
|
+
|
|
565
|
+
---
|
|
566
|
+
|
|
567
|
+
## GitLab CI: `FALLOW_COMMENT` vs `FALLOW_REVIEW`
|
|
568
|
+
|
|
569
|
+
These are separate features and can be used independently or together:
|
|
570
|
+
|
|
571
|
+
- **`FALLOW_COMMENT: "true"`** — posts a single summary comment on the MR with issue counts and a findings table
|
|
572
|
+
- **`FALLOW_REVIEW: "true"`** — posts inline code review comments on the exact MR diff lines where issues were found
|
|
573
|
+
|
|
574
|
+
```yaml
|
|
575
|
+
# WRONG: expecting inline review comments from FALLOW_COMMENT
|
|
576
|
+
variables:
|
|
577
|
+
FALLOW_COMMENT: "true"
|
|
578
|
+
# This only posts a summary comment, not inline annotations
|
|
579
|
+
|
|
580
|
+
# CORRECT: use FALLOW_REVIEW for inline diff comments
|
|
581
|
+
variables:
|
|
582
|
+
FALLOW_REVIEW: "true"
|
|
583
|
+
|
|
584
|
+
# CORRECT: use both for summary + inline
|
|
585
|
+
variables:
|
|
586
|
+
FALLOW_COMMENT: "true"
|
|
587
|
+
FALLOW_REVIEW: "true"
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
Both require a `GITLAB_TOKEN` CI/CD variable (project access token with `api` scope). `CI_JOB_TOKEN` is read-only for MR notes in the official GitLab API, so it is not enough for summary comments or inline discussions.
|
|
591
|
+
|
|
592
|
+
---
|
|
593
|
+
|
|
594
|
+
## License Errors Include a Machine-Readable Code Suffix
|
|
595
|
+
|
|
596
|
+
`fallow license refresh` and `fallow license activate --trial` can fail with a backend error. The CLI always appends the raw HTTP status and the backend error code after the human hint, so scripts can grep for the code without parsing prose:
|
|
597
|
+
|
|
598
|
+
```
|
|
599
|
+
fallow license refresh: your stored license is too stale to refresh. Reactivate with: fallow license activate --trial --email <addr> (HTTP 401, code token_stale)
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
Stable codes the CLI surfaces today:
|
|
603
|
+
|
|
604
|
+
| Code | Operation | Meaning |
|
|
605
|
+
|------|-----------|---------|
|
|
606
|
+
| `token_stale` | `refresh` | Stored JWT is more than 45 days past its `exp`. Reactivate. |
|
|
607
|
+
| `invalid_token` | `refresh` | Stored JWT is missing required claims (e.g. `sub`). Reactivate. |
|
|
608
|
+
| `unauthorized` | `refresh` or `trial` | Auth failed. Reactivate. |
|
|
609
|
+
| `rate_limit_exceeded` | `trial` | Trial endpoint is capped at 5 per hour per IP. Wait or use a different network. |
|
|
610
|
+
|
|
611
|
+
To detect a rate-limited trial signup in CI:
|
|
612
|
+
|
|
613
|
+
```bash
|
|
614
|
+
if fallow license activate --trial --email "$EMAIL" 2>&1 | grep -q "code rate_limit_exceeded"; then
|
|
615
|
+
echo "Trial rate-limited; fallback to cached FALLOW_LICENSE" >&2
|
|
616
|
+
fi
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
Unknown codes fall back to the backend `message` field when present, otherwise the raw body, so existing scripts that match on HTTP status alone still work.
|
|
620
|
+
|
|
621
|
+
---
|
|
622
|
+
|
|
623
|
+
## GitLab CI: Auto `--changed-since` in MR Pipelines
|
|
624
|
+
|
|
625
|
+
The official GitLab CI template automatically sets `--changed-since origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME` in merge request pipelines. You do not need to set `FALLOW_CHANGED_SINCE` manually unless you want a different ref.
|
|
626
|
+
|
|
627
|
+
```yaml
|
|
628
|
+
# UNNECESSARY: changed-since is auto-detected in MR pipelines
|
|
629
|
+
variables:
|
|
630
|
+
FALLOW_CHANGED_SINCE: "origin/main"
|
|
631
|
+
|
|
632
|
+
# CORRECT: let the template auto-detect
|
|
633
|
+
# (no FALLOW_CHANGED_SINCE needed — it reads the MR target branch)
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
Override `FALLOW_CHANGED_SINCE` only when you need a specific ref (e.g., a release branch) or want to disable auto-detection by setting it to an empty string.
|
|
637
|
+
|
|
638
|
+
---
|
|
639
|
+
|
|
640
|
+
## GitLab CI: Package Manager Detection
|
|
641
|
+
|
|
642
|
+
The GitLab CI template auto-detects the project's package manager from lockfiles (`package-lock.json` for npm, `pnpm-lock.yaml` for pnpm, `yarn.lock` for yarn). MR comments and review comments use the correct commands for the detected manager.
|
|
643
|
+
|
|
644
|
+
This means review comments will show `pnpm remove lodash` instead of `npm uninstall lodash` in a pnpm project. No configuration is needed — detection is automatic.
|