flaglint 0.2.0 → 0.2.1

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 CHANGED
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.2.1] - 2026-05-23
9
+
10
+ ### Fixed
11
+
12
+ - **Parse failure on generic TypeScript arrow functions** — `flagPredicate = <T>(...)` and similar generic arrows in `.ts` files now parse correctly. Root cause: `@typescript-eslint/typescript-estree` wasn't receiving a `filePath`, so it couldn't apply TypeScript's extension-based JSX rules. Adding `filePath` tells the compiler to treat `.ts` files as non-JSX (generics parse cleanly) and `.tsx` as JSX (LDProvider detection still works). Validated against LaunchDarkly's own docs codebase.
13
+
14
+ ### Added
15
+
16
+ - **`wrappers` config option** — detect custom wrapper functions as flag usages. Add your wrapper names to `.flaglintrc`:
17
+ ```json
18
+ { "wrappers": ["flagPredicate", "useFlag", "getFlag", "isEnabled"] }
19
+ ```
20
+ FlagLint will treat calls to these functions as `variation`-equivalent. Supports static and dynamic flag keys. Default is `[]` — no behaviour change for existing users.
21
+
8
22
  ## [0.2.0] - 2026-05-22
9
23
 
10
24
  ### Breaking Changes
package/README.md CHANGED
@@ -118,6 +118,7 @@ Create `.flaglintrc` in your project root:
118
118
  | `exclude` | `string[]` | `["**/node_modules/**", ...]` | Glob patterns to ignore |
119
119
  | `provider` | `string` | `"launchdarkly"` | Feature flag provider |
120
120
  | `minFileCount` | `number` | `1` | A flag is stale if it appears in ≤ N files (default: 1) |
121
+ | `wrappers` | `string[]` | `[]` | Function names that wrap LD SDK calls. FlagLint will detect calls to these functions as flag usages. Example: `["flagPredicate", "useFlag", "getFlag", "isEnabled"]` |
121
122
  | `reportTitle` | `string` | — | Custom title for generated reports |
122
123
  | `outputDir` | `string` | `"."` | Default output directory |
123
124
 
@@ -76,7 +76,7 @@ function walk(root, visit) {
76
76
  }
77
77
  }
78
78
  }
79
- function detectUsages(ast, filePath) {
79
+ function detectUsages(ast, filePath, wrappers) {
80
80
  const usages = [];
81
81
  walk(ast, (node) => {
82
82
  if (node.type === "CallExpression") {
@@ -141,6 +141,20 @@ function detectUsages(ast, filePath) {
141
141
  return;
142
142
  }
143
143
  }
144
+ if (wrappers.length > 0 && callee.type === "Identifier" && wrappers.includes(callee.name) && call.arguments.length >= 1) {
145
+ const { flagKey, isDynamic } = extractFlagKey(call.arguments[0]);
146
+ const sig = checkStale(flagKey, filePath);
147
+ usages.push({
148
+ flagKey,
149
+ isDynamic,
150
+ file: filePath,
151
+ line: loc.line,
152
+ column: loc.column,
153
+ callType: "variation",
154
+ stalenessSignals: sig ? [sig] : []
155
+ });
156
+ return;
157
+ }
144
158
  if (callee.type === "CallExpression" && callee.callee.type === "Identifier" && callee.callee.name === "withLDConsumer") {
145
159
  const sig = checkStale("*", filePath);
146
160
  usages.push({
@@ -202,12 +216,13 @@ async function scan(source, config, onProgress) {
202
216
  loc: true,
203
217
  range: false,
204
218
  comment: false,
205
- tokens: false
219
+ tokens: false,
220
+ filePath: file
206
221
  });
207
222
  } catch {
208
223
  return { usages: [], warning: `warn: failed to parse ${file}` };
209
224
  }
210
- return { usages: detectUsages(ast, file), warning: null };
225
+ return { usages: detectUsages(ast, file, config.wrappers), warning: null };
211
226
  }
212
227
  const limit = pLimit(50);
213
228
  const results = await Promise.all(
@@ -389,7 +404,7 @@ function formatHTML(result, options) {
389
404
  return `<tr class="${cls}"><td><code>${esc(key)}</code></td><td>${data.usages.length}</td><td>${fileList}</td><td>${[...data.callTypes].map(esc).join(", ")}</td><td>${status}</td></tr>`;
390
405
  }).join("\n ");
391
406
  const title = options.title ? esc(options.title) : "FlagLint Scan Report";
392
- const version = true ? "0.2.0" : "0.1.0";
407
+ const version = true ? "0.2.1" : "0.1.0";
393
408
  return `<!DOCTYPE html>
394
409
  <html lang="en">
395
410
  <head>
@@ -486,6 +501,7 @@ var FlagLintConfigSchema = z.object({
486
501
  provider: z.enum(["launchdarkly", "unleash", "growthbook", "custom"]).default("launchdarkly"),
487
502
  // TODO v0.3: replace minFileCount with real date-based staleness via git log
488
503
  minFileCount: z.number().int().min(0).default(1),
504
+ wrappers: z.array(z.string()).default([]),
489
505
  reportTitle: z.string().optional(),
490
506
  outputDir: z.string().default(".")
491
507
  });
@@ -808,7 +824,7 @@ function analyze(result) {
808
824
  function formatMigrationReport(analysis) {
809
825
  const { readinessScore, requiredPackages, items, manualReviewCount, autoMigrateCount } = analysis;
810
826
  const date = (/* @__PURE__ */ new Date()).toLocaleDateString();
811
- const version = true ? "0.2.0" : "0.1.0";
827
+ const version = true ? "0.2.1" : "0.1.0";
812
828
  let scoreLabel;
813
829
  if (readinessScore >= 80) scoreLabel = "\u2713 Your codebase is ready for migration";
814
830
  else if (readinessScore >= 50) scoreLabel = "\u26A0 Some manual work required before migration";
@@ -1006,7 +1022,7 @@ Examples:
1006
1022
  // src/cli.ts
1007
1023
  function createCLI() {
1008
1024
  const program2 = new Command();
1009
- program2.name("flaglint").description("Find stale feature flags. Detect flag debt. Plan your OpenFeature migration.").version("0.2.0", "-v, --version", "output the current version").addHelpText(
1025
+ program2.name("flaglint").description("Find stale feature flags. Detect flag debt. Plan your OpenFeature migration.").version("0.2.1", "-v, --version", "output the current version").addHelpText(
1010
1026
  "after",
1011
1027
  `
1012
1028
  Examples:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flaglint",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Find stale feature flags. Detect flag debt. Plan your OpenFeature migration.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,10 +41,6 @@
41
41
  "test": "vitest",
42
42
  "test:run": "vitest run",
43
43
  "test:coverage": "vitest run --coverage",
44
- "agent": "tsx scripts/agent/agent.ts",
45
- "agent:launch": "tsx scripts/agent/agent.ts launch",
46
- "agent:parallel": "tsx scripts/agent/agent.ts parallel",
47
- "agent:sync": "tsx scripts/agent/agent.ts sync-docs",
48
44
  "release:patch": "tsx scripts/release.ts patch",
49
45
  "release:minor": "tsx scripts/release.ts minor",
50
46
  "release:major": "tsx scripts/release.ts major"