flaglint 0.1.0 → 0.1.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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Find stale feature flags. Detect flag debt. Plan your OpenFeature migration.**
4
4
 
5
- [![CI](https://github.com/flaglint/flaglint/actions/workflows/ci.yml/badge.svg)](https://github.com/flaglint/flaglint/actions/workflows/ci.yml)
5
+ [![CI](https://https://github.com/flaglint/flagkit-cli/actions/workflows/ci.yml/badge.svg)](https://https://github.com/flaglint/flagkit-cli/actions/workflows/ci.yml)
6
6
  [![npm version](https://img.shields.io/npm/v/flaglint.svg)](https://www.npmjs.com/package/flaglint)
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
8
 
@@ -141,7 +141,7 @@ function detectUsages(ast, filePath) {
141
141
  line: loc.line,
142
142
  column: loc.column,
143
143
  callType: "provider",
144
- isStale: false
144
+ isStale: checkStale("*", filePath)
145
145
  });
146
146
  }
147
147
  }
@@ -269,7 +269,7 @@ function formatMarkdown(result, options) {
269
269
  lines.push("");
270
270
  lines.push(`**Scanned:** ${scannedFiles} files in ${scanDurationMs}ms `);
271
271
  lines.push(`**Flag usages:** ${totalUsages} across ${uniqueFlags.length} unique flags `);
272
- lines.push(`**Stale candidates:** ${staleUsages.length} flags flagged for review`);
272
+ lines.push(`**Stale candidates:** ${new Set(staleUsages.map((u) => u.flagKey)).size} flags flagged for review`);
273
273
  lines.push("");
274
274
  lines.push("## Flag Inventory");
275
275
  lines.push("| Flag Key | Usages | Files | Call Types | Status |");
@@ -300,7 +300,7 @@ function formatMarkdown(result, options) {
300
300
  lines.push("| Flag Key | Reason | Location |");
301
301
  lines.push("|----------|--------|----------|");
302
302
  for (const [key, data] of staleFlags) {
303
- const first = data.usages[0];
303
+ const first = data.usages.find((u) => u.isStale) ?? data.usages[0];
304
304
  lines.push(`| ${key} | ${staleReason(first)} | ${first.file}:${first.line} |`);
305
305
  }
306
306
  lines.push("");
@@ -311,7 +311,7 @@ function formatMarkdown(result, options) {
311
311
  "Flags with non-static keys that could not be automatically identified:"
312
312
  );
313
313
  for (const u of dynamicUsages) {
314
- lines.push(`- \`dynamic\` at ${u.file}:${u.line} \u2014 key determined at runtime`);
314
+ lines.push(`- \`${u.flagKey}\` at ${u.file}:${u.line} \u2014 key determined at runtime`);
315
315
  }
316
316
  lines.push("");
317
317
  }
@@ -334,7 +334,7 @@ function formatHTML(result, options) {
334
334
  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>`;
335
335
  }).join("\n ");
336
336
  const title = options.title ? esc(options.title) : "FlagLint Scan Report";
337
- const version = true ? "0.1.0" : "0.1.0";
337
+ const version = true ? "0.1.2" : "0.1.0";
338
338
  return `<!DOCTYPE html>
339
339
  <html lang="en">
340
340
  <head>
@@ -461,14 +461,15 @@ function loadConfig(configPath) {
461
461
  // src/commands/scan.ts
462
462
  var VALID_FORMATS = ["json", "markdown", "html"];
463
463
  function registerScanCommand(program2) {
464
- program2.command("scan").description("Scan a directory for feature flag usages and detect stale flags").argument("[dir]", "directory to scan", process.cwd()).option("-f, --format <format>", "output format: json | markdown | html", "markdown").option("-o, --output <file>", "write report to file").option("-c, --config <path>", "path to .flaglintrc config file").addHelpText(
464
+ program2.command("scan").description("Scan a directory for feature flag usages and detect stale flags").argument("[dir]", "directory to scan", process.cwd()).option("-f, --format <format>", "output format: json | markdown | html", "markdown").option("-o, --output <file>", "write report to file").option("-c, --config <path>", "path to .flaglintrc config file").option("--exclude-tests", "exclude test files (*.test.*, *.spec.*, __tests__/, tests/)").addHelpText(
465
465
  "after",
466
466
  `
467
467
  Examples:
468
468
  $ flaglint scan scan current directory
469
469
  $ flaglint scan ./src scan specific directory
470
470
  $ flaglint scan --format json output as JSON
471
- $ flaglint scan --output report.md save to file`
471
+ $ flaglint scan --output report.md save to file
472
+ $ flaglint scan --exclude-tests skip test and spec files`
472
473
  ).action(
473
474
  async (dir, options) => {
474
475
  if (!VALID_FORMATS.includes(options.format)) {
@@ -508,6 +509,16 @@ Examples:
508
509
  process.stderr.write(chalk.red(String(err instanceof Error ? err.message : err)) + "\n");
509
510
  process.exit(1);
510
511
  }
512
+ if (options.excludeTests) {
513
+ config.exclude.push(
514
+ "**/*.test.ts",
515
+ "**/*.test.tsx",
516
+ "**/*.spec.ts",
517
+ "**/*.spec.tsx",
518
+ "**/__tests__/**",
519
+ "**/tests/**"
520
+ );
521
+ }
511
522
  const format = options.format;
512
523
  const spinner = ora(`Scanning ${dir}...`).start();
513
524
  process.once("SIGINT", () => {
@@ -687,14 +698,7 @@ function buildItem(usage) {
687
698
  requiresManualReview: false
688
699
  };
689
700
  default:
690
- return {
691
- usage,
692
- openFeatureEquivalent: null,
693
- codeChangeBefore: `// ${usage.callType} call`,
694
- codeChangeAfter: `// Manual migration required`,
695
- requiresManualReview: true,
696
- reviewReason: `Unrecognized call type: ${usage.callType}`
697
- };
701
+ throw new Error(`Unhandled callType: ${usage.callType}`);
698
702
  }
699
703
  }
700
704
  function calcReadinessScore(usages) {
@@ -740,7 +744,7 @@ function analyze(result) {
740
744
  function formatMigrationReport(analysis) {
741
745
  const { readinessScore, requiredPackages, items, manualReviewCount, autoMigrateCount } = analysis;
742
746
  const date = (/* @__PURE__ */ new Date()).toLocaleDateString();
743
- const version = true ? "0.1.0" : "0.1.0";
747
+ const version = true ? "0.1.2" : "0.1.0";
744
748
  let scoreLabel;
745
749
  if (readinessScore >= 80) scoreLabel = "\u2713 Your codebase is ready for migration";
746
750
  else if (readinessScore >= 50) scoreLabel = "\u26A0 Some manual work required before migration";
@@ -814,14 +818,15 @@ function formatMigrationReport(analysis) {
814
818
 
815
819
  // src/commands/migrate.ts
816
820
  function registerMigrateCommand(program2) {
817
- program2.command("migrate").description("Analyze migration readiness and generate an OpenFeature migration plan").argument("[dir]", "directory to analyze", process.cwd()).option("-o, --output <file>", "write migration plan to file", "MIGRATION.md").option("-c, --config <path>", "path to .flaglintrc config file").option("--dry-run", "print migration plan to stdout without writing file").addHelpText(
821
+ program2.command("migrate").description("Analyze migration readiness and generate an OpenFeature migration plan").argument("[dir]", "directory to analyze", process.cwd()).option("-o, --output <file>", "write migration plan to file", "MIGRATION.md").option("-c, --config <path>", "path to .flaglintrc config file").option("--dry-run", "print migration plan to stdout without writing file").option("--exclude-tests", "exclude test files (*.test.*, *.spec.*, __tests__/, tests/)").addHelpText(
818
822
  "after",
819
823
  `
820
824
  Examples:
821
825
  $ flaglint migrate generate migration plan for current directory
822
826
  $ flaglint migrate ./src analyze specific directory
823
827
  $ flaglint migrate --dry-run preview without writing file
824
- $ flaglint migrate --output plan.md write to custom file`
828
+ $ flaglint migrate --output plan.md write to custom file
829
+ $ flaglint migrate --exclude-tests skip test and spec files`
825
830
  ).action(
826
831
  async (dir, options) => {
827
832
  try {
@@ -852,6 +857,16 @@ Examples:
852
857
  process.stderr.write(chalk2.red(String(err instanceof Error ? err.message : err)) + "\n");
853
858
  process.exit(1);
854
859
  }
860
+ if (options.excludeTests) {
861
+ config.exclude.push(
862
+ "**/*.test.ts",
863
+ "**/*.test.tsx",
864
+ "**/*.spec.ts",
865
+ "**/*.spec.tsx",
866
+ "**/__tests__/**",
867
+ "**/tests/**"
868
+ );
869
+ }
855
870
  const spinner = ora2(`Scanning ${dir}...`).start();
856
871
  process.once("SIGINT", () => {
857
872
  spinner.stop();
@@ -859,7 +874,9 @@ Examples:
859
874
  });
860
875
  let scanResult;
861
876
  try {
862
- scanResult = await scan(dir, config);
877
+ scanResult = await scan(dir, config, (filesScanned) => {
878
+ spinner.text = `Scanning files... ${filesScanned}`;
879
+ });
863
880
  spinner.text = "Analyzing migration readiness...";
864
881
  } catch (err) {
865
882
  spinner.fail("Scan failed");
@@ -925,7 +942,7 @@ Examples:
925
942
  // src/cli.ts
926
943
  function createCLI() {
927
944
  const program2 = new Command();
928
- program2.name("flaglint").description("Find stale feature flags. Detect flag debt. Plan your OpenFeature migration.").version("0.1.0", "-v, --version", "output the current version").addHelpText(
945
+ program2.name("flaglint").description("Find stale feature flags. Detect flag debt. Plan your OpenFeature migration.").version("0.1.2", "-v, --version", "output the current version").addHelpText(
929
946
  "after",
930
947
  `
931
948
  Examples:
@@ -944,4 +961,3 @@ Examples:
944
961
  // bin/flaglint.ts
945
962
  var program = createCLI();
946
963
  program.parse(process.argv);
947
- //# sourceMappingURL=flaglint.js.map
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "flaglint",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Find stale feature flags. Detect flag debt. Plan your OpenFeature migration.",
5
5
  "type": "module",
6
6
  "bin": {
7
- "flaglint": "./dist/bin/flaglint.js"
7
+ "flaglint": "dist/bin/flaglint.js"
8
8
  },
9
9
  "files": [
10
10
  "dist/",
@@ -13,7 +13,7 @@
13
13
  "LICENSE"
14
14
  ],
15
15
  "engines": {
16
- "node": ">=18"
16
+ "node": ">=22"
17
17
  },
18
18
  "keywords": [
19
19
  "feature-flags",
@@ -27,23 +27,27 @@
27
27
  "license": "MIT",
28
28
  "repository": {
29
29
  "type": "git",
30
- "url": "https://github.com/flaglint/flaglint.git"
30
+ "url": "https://github.com/flaglint/flagkit-cli.git"
31
31
  },
32
32
  "homepage": "https://flaglint.dev",
33
33
  "bugs": {
34
- "url": "https://github.com/flaglint/flaglint/issues"
34
+ "url": "https://github.com/flaglint/flagkit-cli/issues"
35
35
  },
36
36
  "scripts": {
37
37
  "build": "tsup",
38
38
  "dev": "tsup --watch",
39
39
  "typecheck": "tsc --noEmit",
40
+ "typecheck:agent": "tsc --project tsconfig.agent.json",
40
41
  "test": "vitest",
41
42
  "test:run": "vitest run",
42
43
  "test:coverage": "vitest run --coverage",
43
44
  "agent": "tsx scripts/agent/agent.ts",
44
45
  "agent:launch": "tsx scripts/agent/agent.ts launch",
45
46
  "agent:parallel": "tsx scripts/agent/agent.ts parallel",
46
- "agent:sync": "tsx scripts/agent/agent.ts sync-docs"
47
+ "agent:sync": "tsx scripts/agent/agent.ts sync-docs",
48
+ "release:patch": "tsx scripts/release.ts patch",
49
+ "release:minor": "tsx scripts/release.ts minor",
50
+ "release:major": "tsx scripts/release.ts major"
47
51
  },
48
52
  "dependencies": {
49
53
  "@typescript-eslint/types": "^8.59.4",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/cli.ts","../../src/commands/scan.ts","../../src/scanner/index.ts","../../src/reporter/index.ts","../../src/config.ts","../../src/commands/migrate.ts","../../src/migrator/index.ts","../../bin/flaglint.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { registerScanCommand } from \"./commands/scan.js\";\nimport { registerMigrateCommand } from \"./commands/migrate.js\";\n\ndeclare const __PKG_VERSION__: string;\ndeclare const __PKG_DESCRIPTION__: string;\n\nexport function createCLI(): Command {\n const program = new Command();\n\n program\n .name(\"flaglint\")\n .description(__PKG_DESCRIPTION__)\n .version(__PKG_VERSION__, \"-v, --version\", \"output the current version\")\n .addHelpText(\n \"after\",\n `\nExamples:\n $ flaglint scan scan current directory\n $ flaglint scan ./src scan specific directory\n $ flaglint scan --format json output as JSON\n $ flaglint scan --output report.md save to file\n $ flaglint migrate generate migration plan\n $ flaglint migrate --dry-run preview without writing`\n );\n\n registerScanCommand(program);\n registerMigrateCommand(program);\n\n return program;\n}\n","import { writeFile } from \"fs/promises\";\nimport { stat } from \"fs/promises\";\nimport { resolve } from \"path\";\nimport type { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { scan } from \"../scanner/index.js\";\nimport { formatReport } from \"../reporter/index.js\";\nimport { loadConfig } from \"../config.js\";\nimport type { ReporterOptions } from \"../types.js\";\n\nconst VALID_FORMATS = [\"json\", \"markdown\", \"html\"];\n\nexport function registerScanCommand(program: Command): void {\n program\n .command(\"scan\")\n .description(\"Scan a directory for feature flag usages and detect stale flags\")\n .argument(\"[dir]\", \"directory to scan\", process.cwd())\n .option(\"-f, --format <format>\", \"output format: json | markdown | html\", \"markdown\")\n .option(\"-o, --output <file>\", \"write report to file\")\n .option(\"-c, --config <path>\", \"path to .flaglintrc config file\")\n .addHelpText(\n \"after\",\n `\nExamples:\n $ flaglint scan scan current directory\n $ flaglint scan ./src scan specific directory\n $ flaglint scan --format json output as JSON\n $ flaglint scan --output report.md save to file`\n )\n .action(\n async (dir: string, options: { format: string; output?: string; config?: string }) => {\n // Validate format before doing any I/O\n if (!VALID_FORMATS.includes(options.format)) {\n process.stderr.write(\n chalk.red(\n `Error: Invalid format '${options.format}'. Must be one of: ${VALID_FORMATS.join(\", \")}\\n`\n )\n );\n process.exit(2);\n }\n\n // Validate directory exists\n try {\n const s = await stat(resolve(dir));\n if (!s.isDirectory()) {\n process.stderr.write(chalk.red(`Error: Not a directory: ${dir}\\n`));\n process.exit(1);\n }\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\") {\n process.stderr.write(chalk.red(`Error: Directory not found: ${dir}\\n`));\n } else if (code === \"EACCES\") {\n process.stderr.write(chalk.red(`Error: Permission denied: ${dir}\\n`));\n } else {\n process.stderr.write(chalk.red(`Error: Cannot access directory: ${dir}\\n`));\n }\n process.exit(1);\n }\n\n // Load config — propagate parse errors with clear message\n let config;\n try {\n config = loadConfig(options.config);\n } catch (err) {\n process.stderr.write(chalk.red(String(err instanceof Error ? err.message : err)) + \"\\n\");\n process.exit(1);\n }\n\n const format = options.format as ReporterOptions[\"format\"];\n const spinner = ora(`Scanning ${dir}...`).start();\n process.once(\"SIGINT\", () => { spinner.stop(); process.exit(130); });\n let lastSpinnerUpdate = 0;\n\n let result;\n try {\n result = await scan(dir, config, (filesScanned) => {\n if (filesScanned - lastSpinnerUpdate >= 50) {\n spinner.text = `Scanning... (${filesScanned} files)`;\n lastSpinnerUpdate = filesScanned;\n }\n });\n spinner.stop();\n } catch (err) {\n spinner.fail(\"Scan failed\");\n process.stderr.write(chalk.red(String(err)) + \"\\n\");\n process.exit(1);\n }\n\n for (const w of result.warnings) {\n process.stderr.write(chalk.yellow(w + \"\\n\"));\n }\n\n // Guard: no matching files\n if (result.scannedFiles === 0) {\n process.stderr.write(\n chalk.yellow(\"No matching files found. Check your .flaglintrc include patterns.\\n\")\n );\n process.exit(0);\n }\n\n // Guard: no LD usage\n if (result.totalUsages === 0) {\n process.stderr.write(\n chalk.dim(\n `No LaunchDarkly SDK usage detected in ${result.scannedFiles} files.\\n`\n )\n );\n process.exit(0);\n }\n\n const staleCount = new Set(result.usages.filter((u) => u.isStale).map((u) => u.flagKey)).size;\n const dynamicCount = new Set(result.usages.filter((u) => u.isDynamic).map((u) => u.flagKey)).size;\n\n process.stderr.write(\n chalk.green(\n `✓ ${result.totalUsages} flag usages found across ${result.uniqueFlags.length} unique flags (${result.scanDurationMs}ms)\\n`\n )\n );\n if (staleCount > 0) {\n process.stderr.write(\n chalk.yellow(`⚠ ${staleCount} potentially stale flag(s) — review recommended\\n`)\n );\n }\n if (dynamicCount > 0) {\n process.stderr.write(\n chalk.blue(`ℹ ${dynamicCount} dynamic flag key(s) require manual review\\n`)\n );\n }\n\n const report = formatReport(result, { format, title: config.reportTitle });\n\n if (options.output) {\n const outPath = resolve(options.output);\n try {\n await writeFile(outPath, report, \"utf8\");\n process.stderr.write(chalk.dim(` Report written to ${options.output}\\n`));\n } catch (err) {\n process.stderr.write(\n chalk.red(\n `Error: Failed to write report to ${options.output}: ${err instanceof Error ? err.message : String(err)}\\n`\n )\n );\n process.exit(1);\n }\n } else {\n process.stdout.write(report + \"\\n\");\n }\n\n process.exit(staleCount > 0 ? 1 : 0);\n }\n );\n}\n","import { readFile } from \"fs/promises\";\nimport { relative } from \"path\";\nimport fg from \"fast-glob\";\nimport { parse } from \"@typescript-eslint/typescript-estree\";\nimport type { TSESTree } from \"@typescript-eslint/types\";\nimport type { FlagUsage, ScanResult, FlagLintConfig } from \"../types.js\";\n\nconst LD_MEMBER_METHODS = new Set([\"variation\", \"variationDetail\", \"allFlags\"]);\nconst LD_CLIENT_PATTERN = /ld|client/i;\nconst LD_HOOKS = new Set([\"useFlags\", \"useLDClient\"]);\nconst STALE_KEY_WORDS = [\"old\", \"deprecated\", \"legacy\", \"temp\", \"tmp\", \"test\", \"demo\"];\nconst STALE_FILE_RE = /\\.(test|spec|mock)\\.[jt]sx?$/;\nconst STALE_PATH_RE = /\\/deprecated\\/|\\/old\\/|\\/legacy\\//;\nconst DEFAULT_EXCLUDE = [\"**/node_modules/**\", \"**/dist/**\", \"**/build/**\", \"**/.next/**\"];\n\nfunction extractFlagKey(arg: TSESTree.Node | undefined): { flagKey: string; isDynamic: boolean } {\n if (!arg) return { flagKey: \"dynamic\", isDynamic: true };\n\n if (arg.type === \"Literal\" && typeof (arg as TSESTree.StringLiteral).value === \"string\") {\n return { flagKey: (arg as TSESTree.StringLiteral).value, isDynamic: false };\n }\n\n if (\n arg.type === \"TemplateLiteral\" &&\n (arg as TSESTree.TemplateLiteral).expressions.length === 0\n ) {\n const cooked = (arg as TSESTree.TemplateLiteral).quasis[0]?.value.cooked;\n if (cooked != null) return { flagKey: cooked, isDynamic: false };\n }\n\n return { flagKey: \"dynamic\", isDynamic: true };\n}\n\nfunction checkStale(flagKey: string, filePath: string): boolean {\n if (STALE_FILE_RE.test(filePath)) return true;\n if (STALE_PATH_RE.test(filePath)) return true;\n const lk = flagKey.toLowerCase();\n return STALE_KEY_WORDS.some((kw) => lk.includes(kw));\n}\n\nfunction walk(node: TSESTree.Node | null | undefined, visit: (n: TSESTree.Node) => void): void {\n if (!node || typeof node !== \"object\") return;\n visit(node);\n for (const key of Object.keys(node)) {\n if (key === \"parent\") continue;\n const val = (node as unknown as Record<string, unknown>)[key];\n if (Array.isArray(val)) {\n for (const item of val) {\n if (item && typeof item === \"object\" && \"type\" in item) {\n walk(item as TSESTree.Node, visit);\n }\n }\n } else if (val && typeof val === \"object\" && \"type\" in val) {\n walk(val as TSESTree.Node, visit);\n }\n }\n}\n\nfunction detectUsages(ast: TSESTree.Program, filePath: string): FlagUsage[] {\n const usages: FlagUsage[] = [];\n\n walk(ast, (node) => {\n if (node.type === \"CallExpression\") {\n const call = node as TSESTree.CallExpression;\n const { callee } = call;\n const loc = call.loc?.start ?? { line: 0, column: 0 };\n\n // ldClient.variation / ldClient.variationDetail / ldClient.allFlags\n if (\n callee.type === \"MemberExpression\" &&\n !callee.computed &&\n callee.object.type === \"Identifier\" &&\n callee.property.type === \"Identifier\" &&\n LD_CLIENT_PATTERN.test((callee.object as TSESTree.Identifier).name) &&\n LD_MEMBER_METHODS.has((callee.property as TSESTree.Identifier).name)\n ) {\n const method = (callee.property as TSESTree.Identifier).name;\n if (method === \"allFlags\") {\n usages.push({\n flagKey: \"*\",\n isDynamic: false,\n file: filePath,\n line: loc.line,\n column: loc.column,\n callType: \"allFlags\",\n isStale: checkStale(\"*\", filePath),\n });\n } else {\n const { flagKey, isDynamic } = extractFlagKey(call.arguments[0]);\n usages.push({\n flagKey,\n isDynamic,\n file: filePath,\n line: loc.line,\n column: loc.column,\n callType: method,\n isStale: checkStale(flagKey, filePath),\n });\n }\n return;\n }\n\n if (callee.type === \"Identifier\") {\n const name = (callee as TSESTree.Identifier).name;\n\n // isFeatureEnabled(flagKey, ...)\n if (name === \"isFeatureEnabled\") {\n const { flagKey, isDynamic } = extractFlagKey(call.arguments[0]);\n usages.push({\n flagKey,\n isDynamic,\n file: filePath,\n line: loc.line,\n column: loc.column,\n callType: \"isFeatureEnabled\",\n isStale: checkStale(flagKey, filePath),\n });\n return;\n }\n\n // useFlags() / useLDClient()\n if (LD_HOOKS.has(name)) {\n usages.push({\n flagKey: \"*\",\n isDynamic: false,\n file: filePath,\n line: loc.line,\n column: loc.column,\n callType: name === \"useFlags\" ? \"hook-useFlags\" : \"hook-useLDClient\",\n isStale: checkStale(\"*\", filePath),\n });\n return;\n }\n }\n\n // withLDConsumer()(...) — callee is itself a CallExpression\n if (\n callee.type === \"CallExpression\" &&\n (callee as TSESTree.CallExpression).callee.type === \"Identifier\" &&\n ((callee as TSESTree.CallExpression).callee as TSESTree.Identifier).name === \"withLDConsumer\"\n ) {\n usages.push({\n flagKey: \"*\",\n isDynamic: false,\n file: filePath,\n line: loc.line,\n column: loc.column,\n callType: \"hoc\",\n isStale: checkStale(\"*\", filePath),\n });\n return;\n }\n }\n\n // JSX: <LDProvider ...>\n if (node.type === \"JSXOpeningElement\") {\n const jsx = node as TSESTree.JSXOpeningElement;\n if (jsx.name.type === \"JSXIdentifier\" && (jsx.name as TSESTree.JSXIdentifier).name === \"LDProvider\") {\n const loc = jsx.loc?.start ?? { line: 0, column: 0 };\n usages.push({\n flagKey: \"*\",\n isDynamic: false,\n file: filePath,\n line: loc.line,\n column: loc.column,\n callType: \"provider\",\n isStale: false,\n });\n }\n }\n });\n\n return usages;\n}\n\nexport async function scan(\n dir: string,\n config: FlagLintConfig,\n onProgress?: (filesScanned: number) => void\n): Promise<ScanResult> {\n const start = Date.now();\n\n for (const pattern of config.include) {\n if (pattern.startsWith(\"/\") || pattern.startsWith(\"..\")) {\n throw new Error(\n `Invalid include pattern: \"${pattern}\" — patterns must be relative and must not start with \"..\"`\n );\n }\n }\n\n const files = await fg(config.include, {\n cwd: dir,\n absolute: true,\n ignore: [...DEFAULT_EXCLUDE, ...config.exclude],\n onlyFiles: true,\n });\n\n const allUsages: FlagUsage[] = [];\n const warnings: string[] = [];\n let scannedFiles = 0;\n\n for (const file of files) {\n scannedFiles++;\n onProgress?.(scannedFiles);\n\n let code: string;\n try {\n code = await readFile(file, \"utf8\");\n } catch {\n continue;\n }\n\n let ast: TSESTree.Program;\n try {\n ast = parse(code, {\n jsx: true,\n loc: true,\n range: false,\n comment: false,\n tokens: false,\n });\n } catch {\n warnings.push(`warn: failed to parse ${relative(dir, file)}`);\n continue;\n }\n\n allUsages.push(...detectUsages(ast, file));\n }\n\n if (config.staleThreshold > 0) {\n const flagFileCount = new Map<string, Set<string>>();\n for (const usage of allUsages) {\n if (!usage.isDynamic && usage.flagKey !== \"*\") {\n if (!flagFileCount.has(usage.flagKey)) {\n flagFileCount.set(usage.flagKey, new Set());\n }\n flagFileCount.get(usage.flagKey)!.add(usage.file);\n }\n }\n for (const usage of allUsages) {\n if (!usage.isDynamic && usage.flagKey !== \"*\") {\n const fileCount = flagFileCount.get(usage.flagKey)?.size ?? 0;\n if (fileCount <= config.staleThreshold) {\n usage.isStale = true;\n }\n }\n }\n }\n\n const uniqueFlags = [\n ...new Set(\n allUsages\n .filter((u) => !u.isDynamic && u.flagKey !== \"*\")\n .map((u) => u.flagKey)\n ),\n ];\n\n return {\n scannedFiles,\n totalUsages: allUsages.length,\n uniqueFlags,\n usages: allUsages,\n scanDurationMs: Date.now() - start,\n warnings,\n };\n}\n","import type { FlagUsage, ReporterOptions, ScanResult } from \"../types.js\";\n\ndeclare const __PKG_VERSION__: string;\n\n// ── helpers ──────────────────────────────────────────────────────────────────\n\nfunction esc(s: string): string {\n return s\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\");\n}\n\nfunction staleReason(u: FlagUsage): string {\n if (/\\.(test|spec|mock)\\.[jt]sx?$/.test(u.file)) return \"Located in test file\";\n if (/\\/deprecated\\/|\\/old\\/|\\/legacy\\//.test(u.file)) return \"Located in deprecated path\";\n const kw = [\"old\", \"deprecated\", \"legacy\", \"temp\", \"tmp\", \"test\", \"demo\"].find((k) =>\n u.flagKey.toLowerCase().includes(k)\n );\n return kw ? `Contains \"${kw}\" in key` : \"Flagged as stale\";\n}\n\ntype FlagEntry = {\n usages: FlagUsage[];\n files: Set<string>;\n callTypes: Set<string>;\n isStale: boolean;\n};\n\nfunction buildFlagMap(usages: FlagUsage[]): Map<string, FlagEntry> {\n const map = new Map<string, FlagEntry>();\n for (const u of usages) {\n if (!map.has(u.flagKey)) {\n map.set(u.flagKey, { usages: [], files: new Set(), callTypes: new Set(), isStale: false });\n }\n const entry = map.get(u.flagKey)!;\n entry.usages.push(u);\n entry.files.add(u.file);\n entry.callTypes.add(u.callType);\n if (u.isStale) entry.isStale = true;\n }\n return map;\n}\n\nfunction sortedFlagEntries(map: Map<string, FlagEntry>): [string, FlagEntry][] {\n return [...map.entries()].sort(([, a], [, b]) => {\n if (a.isStale !== b.isStale) return a.isStale ? -1 : 1;\n return b.usages.length - a.usages.length;\n });\n}\n\n// ── markdown ─────────────────────────────────────────────────────────────────\n\nfunction formatMarkdown(result: ScanResult, options: ReporterOptions): string {\n const { scannedFiles, totalUsages, uniqueFlags, usages, scanDurationMs } = result;\n const staleUsages = usages.filter((u) => u.isStale);\n const dynamicUsages = usages.filter((u) => u.isDynamic);\n\n const flagMap = buildFlagMap(usages);\n const sorted = sortedFlagEntries(flagMap);\n const staleFlags = sorted.filter(([, d]) => d.isStale);\n\n const lines: string[] = [];\n\n lines.push(\"# FlagLint Scan Report\");\n if (options.title) lines.push(\"\", options.title);\n lines.push(\"\");\n lines.push(`**Scanned:** ${scannedFiles} files in ${scanDurationMs}ms `);\n lines.push(`**Flag usages:** ${totalUsages} across ${uniqueFlags.length} unique flags `);\n lines.push(`**Stale candidates:** ${staleUsages.length} flags flagged for review`);\n lines.push(\"\");\n\n // Flag Inventory\n lines.push(\"## Flag Inventory\");\n lines.push(\"| Flag Key | Usages | Files | Call Types | Status |\");\n lines.push(\"|----------|--------|-------|------------|--------|\");\n for (const [key, data] of sorted) {\n const status = data.isStale ? \"⚠ Stale\" : \"✓ Active\";\n lines.push(\n `| ${key} | ${data.usages.length} | ${data.files.size} | ${[...data.callTypes].join(\", \")} | ${status} |`\n );\n }\n lines.push(\"\");\n\n // Usages by File\n lines.push(\"## Usages by File\");\n const byFile = new Map<string, FlagUsage[]>();\n for (const u of usages) {\n if (!byFile.has(u.file)) byFile.set(u.file, []);\n byFile.get(u.file)!.push(u);\n }\n for (const [file, fileUsages] of byFile) {\n lines.push(`### ${file}`);\n for (const u of [...fileUsages].sort((a, b) => a.line - b.line)) {\n lines.push(`- Line ${u.line}: \\`${u.flagKey}\\` (${u.callType})`);\n }\n lines.push(\"\");\n }\n\n // Stale candidates\n if (staleFlags.length > 0) {\n lines.push(\"## ⚠ Stale Flag Candidates\");\n lines.push(\"Flags that may be safe to remove:\");\n lines.push(\"| Flag Key | Reason | Location |\");\n lines.push(\"|----------|--------|----------|\");\n for (const [key, data] of staleFlags) {\n const first = data.usages[0]!;\n lines.push(`| ${key} | ${staleReason(first)} | ${first.file}:${first.line} |`);\n }\n lines.push(\"\");\n }\n\n // Dynamic keys\n if (dynamicUsages.length > 0) {\n lines.push(\"## Dynamic Flag Keys (Manual Review Required)\");\n lines.push(\n \"Flags with non-static keys that could not be automatically identified:\"\n );\n for (const u of dynamicUsages) {\n lines.push(`- \\`dynamic\\` at ${u.file}:${u.line} — key determined at runtime`);\n }\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\");\n}\n\n// ── json ─────────────────────────────────────────────────────────────────────\n\nfunction formatJSON(result: ScanResult): string {\n return JSON.stringify({ generatedAt: new Date().toISOString(), ...result }, null, 2);\n}\n\n// ── html ─────────────────────────────────────────────────────────────────────\n\nfunction formatHTML(result: ScanResult, options: ReporterOptions): string {\n const { scannedFiles, totalUsages, uniqueFlags, usages, scanDurationMs } = result;\n const staleCount = usages.filter((u) => u.isStale).length;\n const dynamicCount = usages.filter((u) => u.isDynamic).length;\n const date = new Date().toLocaleString();\n\n const flagMap = buildFlagMap(usages);\n const sorted = sortedFlagEntries(flagMap);\n\n const rows = sorted\n .map(([key, data]) => {\n const cls = data.isStale ? \"stale\" : data.usages.some((u) => u.isDynamic) ? \"dynamic\" : \"\";\n const status = data.isStale ? \"⚠ Stale\" : \"✓ Active\";\n const fileList = [...data.files].map((f) => esc(f)).join(\"<br>\");\n 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>`;\n })\n .join(\"\\n \");\n\n const title = options.title ? esc(options.title) : \"FlagLint Scan Report\";\n const version = typeof __PKG_VERSION__ !== \"undefined\" ? __PKG_VERSION__ : \"0.1.0\";\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${title}</title>\n <style>\n :root{--bg:#fff;--surface:#f8f9fa;--border:#dee2e6;--text:#212529;--muted:#6c757d;--stale-bg:#fef3c7;--dyn-bg:#dbeafe;--card-shadow:0 1px 3px rgba(0,0,0,.1)}\n @media(prefers-color-scheme:dark){:root{--bg:#0f172a;--surface:#1e293b;--border:#334155;--text:#e2e8f0;--muted:#94a3b8;--stale-bg:#78350f;--dyn-bg:#1e3a5f;--card-shadow:0 1px 3px rgba(0,0,0,.4)}}\n *{box-sizing:border-box;margin:0;padding:0}\n body{background:var(--bg);color:var(--text);font-family:system-ui,-apple-system,sans-serif;padding:2rem;max-width:1200px;margin:0 auto;line-height:1.5}\n h1{font-size:1.75rem;margin-bottom:.25rem}\n h2{font-size:1.125rem;margin:2rem 0 .75rem;padding-bottom:.5rem;border-bottom:1px solid var(--border)}\n .subtitle{color:var(--muted);margin-bottom:1.5rem;font-size:.875rem}\n .cards{display:flex;gap:1rem;flex-wrap:wrap;margin-bottom:2rem}\n .card{flex:1;min-width:140px;background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:1rem;box-shadow:var(--card-shadow)}\n .card-num{font-size:1.875rem;font-weight:700;line-height:1}\n .card-num.yellow{color:#d97706}\n .card-num.blue{color:#3b82f6}\n .card-label{color:var(--muted);font-size:.75rem;margin-top:.375rem;text-transform:uppercase;letter-spacing:.05em}\n .filter-wrap{margin-bottom:.75rem}\n #filter{width:100%;padding:.5rem .75rem;border:1px solid var(--border);border-radius:6px;background:var(--surface);color:var(--text);font-size:.875rem;outline:none}\n #filter:focus{border-color:#6366f1}\n table{width:100%;border-collapse:collapse;font-size:.8125rem}\n th{text-align:left;padding:.625rem .75rem;background:var(--surface);border-bottom:2px solid var(--border);font-weight:600;white-space:nowrap}\n td{padding:.625rem .75rem;border-bottom:1px solid var(--border);vertical-align:top}\n tr.stale td{background:var(--stale-bg)}\n tr.dynamic td{background:var(--dyn-bg)}\n code{font-family:ui-monospace,monospace;font-size:.8em;background:var(--surface);padding:.1em .3em;border-radius:3px}\n footer{margin-top:3rem;padding-top:1rem;border-top:1px solid var(--border);color:var(--muted);font-size:.75rem;text-align:center}\n </style>\n</head>\n<body>\n <h1>${title}</h1>\n <p class=\"subtitle\">Scanned ${scannedFiles} files in ${scanDurationMs}ms</p>\n\n <div class=\"cards\">\n <div class=\"card\"><div class=\"card-num\">${scannedFiles}</div><div class=\"card-label\">Files Scanned</div></div>\n <div class=\"card\"><div class=\"card-num\">${uniqueFlags.length}</div><div class=\"card-label\">Unique Flags</div></div>\n <div class=\"card\"><div class=\"card-num\">${totalUsages}</div><div class=\"card-label\">Total Usages</div></div>\n <div class=\"card\"><div class=\"card-num yellow\">${staleCount}</div><div class=\"card-label\">Stale Candidates</div></div>\n <div class=\"card\"><div class=\"card-num blue\">${dynamicCount}</div><div class=\"card-label\">Dynamic Keys</div></div>\n </div>\n\n <h2>Flag Inventory</h2>\n <div class=\"filter-wrap\">\n <input type=\"text\" id=\"filter\" placeholder=\"Filter by flag key, file, or call type…\">\n </div>\n <table id=\"flags-table\">\n <thead><tr><th>Flag Key</th><th>Usages</th><th>Files</th><th>Call Types</th><th>Status</th></tr></thead>\n <tbody>\n ${rows}\n </tbody>\n </table>\n\n <footer>Generated by FlagLint ${esc(version)} on ${esc(date)}</footer>\n\n <script>\n const input = document.getElementById('filter');\n const rows = document.querySelectorAll('#flags-table tbody tr');\n input.addEventListener('input', () => {\n const q = input.value.toLowerCase();\n rows.forEach(r => { r.style.display = r.textContent.toLowerCase().includes(q) ? '' : 'none'; });\n });\n </script>\n</body>\n</html>`;\n}\n\n// ── public export ─────────────────────────────────────────────────────────────\n\nexport function formatReport(result: ScanResult, options: ReporterOptions): string {\n switch (options.format) {\n case \"json\":\n return formatJSON(result);\n case \"html\":\n return formatHTML(result, options);\n default:\n return formatMarkdown(result, options);\n }\n}\n","import { existsSync, readFileSync } from \"fs\";\nimport { resolve } from \"path\";\nimport { z, ZodError } from \"zod\";\n\nexport const FlagLintConfigSchema = z.object({\n include: z.array(z.string()).default([\"**/*.{ts,tsx,js,jsx}\"]),\n exclude: z\n .array(z.string())\n .default([\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/build/**\",\n \"**/.next/**\",\n \"**/coverage/**\",\n \"**/*.d.ts\",\n ]),\n provider: z\n .enum([\"launchdarkly\", \"unleash\", \"growthbook\", \"custom\"])\n .default(\"launchdarkly\"),\n staleThreshold: z.number().int().min(0).default(1),\n reportTitle: z.string().optional(),\n outputDir: z.string().default(\".\"),\n});\n\nexport type FlagLintConfig = z.infer<typeof FlagLintConfigSchema>;\n\nconst SEARCH_PATHS = [\".flaglintrc\", \".flaglintrc.json\", \"flaglint.config.json\"];\n\nexport function loadConfig(configPath?: string): FlagLintConfig {\n const candidates = configPath ? [configPath] : SEARCH_PATHS;\n\n for (const candidate of candidates) {\n const full = resolve(candidate);\n if (!existsSync(full)) continue;\n\n let raw: unknown;\n try {\n raw = JSON.parse(readFileSync(full, \"utf8\"));\n } catch (err) {\n throw new Error(`Error reading ${candidate}: ${String(err)}`);\n }\n\n try {\n return FlagLintConfigSchema.parse(raw);\n } catch (err) {\n if (err instanceof ZodError) {\n const detail = err.errors.map((e) => `${e.path.join(\".\")}: ${e.message}`).join(\"; \");\n throw new Error(`Error in ${candidate}: ${detail}`);\n }\n throw err;\n }\n }\n\n return FlagLintConfigSchema.parse({});\n}\n","import { writeFile } from \"fs/promises\";\nimport { stat } from \"fs/promises\";\nimport { resolve } from \"path\";\nimport type { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { scan } from \"../scanner/index.js\";\nimport { analyze, formatMigrationReport } from \"../migrator/index.js\";\nimport { loadConfig } from \"../config.js\";\n\nexport function registerMigrateCommand(program: Command): void {\n program\n .command(\"migrate\")\n .description(\"Analyze migration readiness and generate an OpenFeature migration plan\")\n .argument(\"[dir]\", \"directory to analyze\", process.cwd())\n .option(\"-o, --output <file>\", \"write migration plan to file\", \"MIGRATION.md\")\n .option(\"-c, --config <path>\", \"path to .flaglintrc config file\")\n .option(\"--dry-run\", \"print migration plan to stdout without writing file\")\n .addHelpText(\n \"after\",\n `\nExamples:\n $ flaglint migrate generate migration plan for current directory\n $ flaglint migrate ./src analyze specific directory\n $ flaglint migrate --dry-run preview without writing file\n $ flaglint migrate --output plan.md write to custom file`\n )\n .action(\n async (dir: string, options: { output: string; config?: string; dryRun?: boolean }) => {\n // Validate directory exists\n try {\n const s = await stat(resolve(dir));\n if (!s.isDirectory()) {\n process.stderr.write(chalk.red(`Error: Not a directory: ${dir}\\n`));\n process.exit(1);\n }\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\") {\n process.stderr.write(chalk.red(`Error: Directory not found: ${dir}\\n`));\n } else if (code === \"EACCES\") {\n process.stderr.write(chalk.red(`Error: Permission denied: ${dir}\\n`));\n } else {\n process.stderr.write(chalk.red(`Error: Cannot access directory: ${dir}\\n`));\n }\n process.exit(1);\n }\n\n // Load config\n let config;\n try {\n config = loadConfig(options.config);\n } catch (err) {\n process.stderr.write(chalk.red(String(err instanceof Error ? err.message : err)) + \"\\n\");\n process.exit(1);\n }\n\n const spinner = ora(`Scanning ${dir}...`).start();\n process.once(\"SIGINT\", () => { spinner.stop(); process.exit(130); });\n\n let scanResult;\n try {\n scanResult = await scan(dir, config);\n spinner.text = \"Analyzing migration readiness...\";\n } catch (err) {\n spinner.fail(\"Scan failed\");\n process.stderr.write(chalk.red(String(err)) + \"\\n\");\n process.exit(1);\n }\n\n // Guard: no matching files\n if (scanResult.scannedFiles === 0) {\n spinner.stop();\n process.stderr.write(\n chalk.yellow(\"No matching files found. Check your .flaglintrc include patterns.\\n\")\n );\n process.exit(0);\n }\n\n // Guard: no LD usage\n if (scanResult.totalUsages === 0) {\n spinner.stop();\n process.stderr.write(\n chalk.dim(\n `No LaunchDarkly SDK usage detected in ${scanResult.scannedFiles} files.\\n`\n )\n );\n process.exit(0);\n }\n\n const analysis = analyze(scanResult);\n spinner.stop();\n\n for (const w of scanResult.warnings) {\n process.stderr.write(chalk.yellow(w + \"\\n\"));\n }\n\n const { readinessScore } = analysis;\n const scoreColor =\n readinessScore >= 80 ? chalk.green : readinessScore >= 50 ? chalk.yellow : chalk.red;\n process.stderr.write(scoreColor(`Migration Readiness Score: ${readinessScore}/100\\n`));\n process.stderr.write(\n chalk.gray(\n `Auto-migratable: ${analysis.autoMigrateCount} · Manual review: ${analysis.manualReviewCount}\\n`\n )\n );\n\n const report = formatMigrationReport(analysis);\n\n if (options.dryRun) {\n process.stdout.write(report + \"\\n\");\n process.exit(0);\n }\n\n const outPath = resolve(options.output);\n try {\n await writeFile(outPath, report, \"utf8\");\n process.stderr.write(chalk.green(`Migration plan written to ${options.output}\\n`));\n } catch (err) {\n process.stderr.write(\n chalk.red(\n `Error: Failed to write migration plan to ${options.output}: ${err instanceof Error ? err.message : String(err)}\\n`\n )\n );\n process.exit(1);\n }\n\n process.exit(0);\n }\n );\n}\n","import type { FlagUsage, MigrationAnalysis, MigrationItem, ScanResult } from \"../types.js\";\n\ndeclare const __PKG_VERSION__: string;\n\n// ── mapping helpers ───────────────────────────────────────────────────────────\n\nfunction keyLiteral(usage: FlagUsage): string {\n return usage.isDynamic ? \"flagKey\" : `'${usage.flagKey}'`;\n}\n\nfunction buildItem(usage: FlagUsage): MigrationItem {\n const k = keyLiteral(usage);\n\n if (usage.isDynamic) {\n return {\n usage,\n openFeatureEquivalent: \"client.getBooleanValue()\",\n codeChangeBefore: `ldClient.variation(flagKey, context, false)`,\n codeChangeAfter: `await client.getBooleanValue(flagKey, false) // server SDK is async`,\n requiresManualReview: true,\n reviewReason: \"Flag key determined at runtime; OpenFeature server SDK methods are async — add await and make the enclosing function async\",\n };\n }\n\n switch (usage.callType) {\n case \"variation\":\n return {\n usage,\n openFeatureEquivalent: \"client.getBooleanValue()\",\n codeChangeBefore: `ldClient.variation(${k}, context, false)`,\n codeChangeAfter: `await client.getBooleanValue(${k}, false) // server SDK is async`,\n requiresManualReview: true,\n reviewReason: \"OpenFeature server SDK methods are async — add await and make the enclosing function async\",\n };\n\n case \"variationDetail\":\n return {\n usage,\n openFeatureEquivalent: \"client.getBooleanDetails()\",\n codeChangeBefore: `ldClient.variationDetail(${k}, context, false)`,\n codeChangeAfter: `await client.getBooleanDetails(${k}, false) // server SDK is async`,\n requiresManualReview: true,\n reviewReason: \"OpenFeature server SDK methods are async — add await and make the enclosing function async\",\n };\n\n case \"allFlags\":\n return {\n usage,\n openFeatureEquivalent: null,\n codeChangeBefore: `ldClient.allFlags(context)`,\n codeChangeAfter: `// No direct OpenFeature equivalent — requires manual implementation`,\n requiresManualReview: true,\n reviewReason: \"allFlags() has no direct OpenFeature equivalent\",\n };\n\n case \"isFeatureEnabled\":\n return {\n usage,\n openFeatureEquivalent: \"client.getBooleanValue()\",\n codeChangeBefore: `isFeatureEnabled(${k}, context)`,\n codeChangeAfter: `await client.getBooleanValue(${k}, false) // server SDK is async`,\n requiresManualReview: true,\n reviewReason: \"OpenFeature server SDK methods are async — add await and make the enclosing function async\",\n };\n\n case \"hook-useFlags\":\n return {\n usage,\n openFeatureEquivalent: \"useBooleanFlagValue()\",\n codeChangeBefore: `const flags = useFlags()`,\n codeChangeAfter: `const flagValue = useBooleanFlagValue('your-flag-key', false) // TODO: one hook call per flag`,\n requiresManualReview: true,\n reviewReason: \"useFlags() returns all flags; OpenFeature requires one useBooleanFlagValue() call per flag\",\n };\n\n case \"hook-useLDClient\":\n return {\n usage,\n openFeatureEquivalent: \"useOpenFeatureClient()\",\n codeChangeBefore: `const client = useLDClient()`,\n codeChangeAfter: `const client = useOpenFeatureClient()`,\n requiresManualReview: false,\n };\n\n case \"hoc\":\n return {\n usage,\n openFeatureEquivalent: null,\n codeChangeBefore: `withLDConsumer()(Component)`,\n codeChangeAfter: `// withOpenFeature() does not exist in OpenFeature SDK 0.4+\\n// Convert to a functional component and use useBooleanFlagValue() instead`,\n requiresManualReview: true,\n reviewReason: \"withOpenFeature() HOC does not exist in OpenFeature SDK 0.4+; convert to a functional component with hooks\",\n };\n\n case \"provider\":\n return {\n usage,\n openFeatureEquivalent: \"OpenFeatureProvider\",\n codeChangeBefore: `<LDProvider clientSideID=\"...\">`,\n codeChangeAfter: `<OpenFeatureProvider provider={...}>`,\n requiresManualReview: false,\n };\n\n default:\n return {\n usage,\n openFeatureEquivalent: null,\n codeChangeBefore: `// ${usage.callType} call`,\n codeChangeAfter: `// Manual migration required`,\n requiresManualReview: true,\n reviewReason: `Unrecognized call type: ${usage.callType}`,\n };\n }\n}\n\nfunction calcReadinessScore(usages: FlagUsage[]): number {\n let score = 100;\n\n const dynamicCount = usages.filter((u) => u.isDynamic).length;\n score -= Math.min(dynamicCount * 10, 40);\n\n const useFlagsCount = usages.filter((u) => u.callType === \"hook-useFlags\").length;\n score -= useFlagsCount * 5;\n\n const hasAllFlags = usages.some((u) => u.callType === \"allFlags\");\n if (hasAllFlags) score -= 15;\n\n const hocCount = usages.filter((u) => u.callType === \"hoc\").length;\n score -= hocCount * 5;\n\n const hasStaticKeys = usages.some((u) => !u.isDynamic && u.flagKey !== \"*\");\n if (!hasStaticKeys) score -= 20;\n\n return Math.max(0, score);\n}\n\nfunction calcRequiredPackages(usages: FlagUsage[]): string[] {\n const REACT_CALL_TYPES = [\"hook-useFlags\", \"hook-useLDClient\", \"hoc\", \"provider\"];\n const SERVER_CALL_TYPES = [\"variation\", \"variationDetail\", \"allFlags\", \"isFeatureEnabled\"];\n\n const hasReactUsage = usages.some((u) => REACT_CALL_TYPES.includes(u.callType));\n const hasServerUsage = usages.some((u) => SERVER_CALL_TYPES.includes(u.callType));\n\n const pkgs = new Set<string>();\n\n if (hasReactUsage && !hasServerUsage) {\n pkgs.add(\"@openfeature/web-sdk\");\n pkgs.add(\"@openfeature/react-sdk\");\n } else if (hasReactUsage && hasServerUsage) {\n pkgs.add(\"@openfeature/server-sdk\");\n pkgs.add(\"@openfeature/web-sdk\");\n pkgs.add(\"@openfeature/react-sdk\");\n } else {\n pkgs.add(\"@openfeature/server-sdk\");\n }\n\n return [...pkgs].sort();\n}\n\n// ── public exports ────────────────────────────────────────────────────────────\n\nexport function analyze(result: ScanResult): MigrationAnalysis {\n const items = result.usages.map(buildItem);\n const readinessScore = calcReadinessScore(result.usages);\n const requiredPackages = calcRequiredPackages(result.usages);\n const manualReviewCount = items.filter((i) => i.requiresManualReview).length;\n const autoMigrateCount = items.filter((i) => !i.requiresManualReview).length;\n\n return { readinessScore, requiredPackages, items, manualReviewCount, autoMigrateCount };\n}\n\nexport function formatMigrationReport(analysis: MigrationAnalysis): string {\n const { readinessScore, requiredPackages, items, manualReviewCount, autoMigrateCount } = analysis;\n const date = new Date().toLocaleDateString();\n const version = typeof __PKG_VERSION__ !== \"undefined\" ? __PKG_VERSION__ : \"0.1.0\";\n\n let scoreLabel: string;\n if (readinessScore >= 80) scoreLabel = \"✓ Your codebase is ready for migration\";\n else if (readinessScore >= 50) scoreLabel = \"⚠ Some manual work required before migration\";\n else scoreLabel = \"✗ Significant refactoring needed\";\n\n const lines: string[] = [];\n\n lines.push(`# OpenFeature Migration Plan`);\n lines.push(`Generated by FlagLint v${version} on ${date}`);\n lines.push(\"\");\n lines.push(`## Migration Readiness Score: ${readinessScore}/100`);\n lines.push(scoreLabel);\n lines.push(\"\");\n lines.push(`**Auto-migratable:** ${autoMigrateCount} usages `);\n lines.push(`**Requires manual review:** ${manualReviewCount} usages`);\n lines.push(\"\");\n\n lines.push(\"## Required Packages\");\n lines.push(\"```\");\n lines.push(`npm install ${requiredPackages.join(\" \")}`);\n lines.push(\"```\");\n lines.push(\"\");\n\n lines.push(\"## Step-by-Step Checklist\");\n lines.push(\"- [ ] Install OpenFeature packages\");\n lines.push(\"- [ ] Configure your OpenFeature provider (LaunchDarkly, Unleash, etc.)\");\n lines.push(\"- [ ] Replace LDProvider with OpenFeatureProvider\");\n lines.push(\"- [ ] Update each flag evaluation call (see below)\");\n lines.push(\"- [ ] Remove LaunchDarkly SDK dependency\");\n lines.push(\"- [ ] Test all flagged features\");\n lines.push(\"\");\n\n const autoItems = items.filter((i) => !i.requiresManualReview);\n const manualItems = items.filter((i) => i.requiresManualReview);\n\n if (autoItems.length > 0) {\n lines.push(\"## Code Changes Required\");\n for (const item of autoItems) {\n const { usage } = item;\n lines.push(`### ${usage.file}:${usage.line} — \\`${usage.flagKey}\\``);\n lines.push(\"**Before:**\");\n lines.push(\"```typescript\");\n lines.push(item.codeChangeBefore);\n lines.push(\"```\");\n lines.push(\"**After:**\");\n lines.push(\"```typescript\");\n lines.push(item.codeChangeAfter);\n lines.push(\"```\");\n lines.push(\"\");\n }\n }\n\n if (manualItems.length > 0) {\n lines.push(\"## Manual Review Required\");\n for (const item of manualItems) {\n const { usage } = item;\n lines.push(`### ${usage.file}:${usage.line} — \\`${usage.flagKey}\\``);\n if (item.reviewReason) lines.push(`> ${item.reviewReason}`);\n lines.push(\"**Before:**\");\n lines.push(\"```typescript\");\n lines.push(item.codeChangeBefore);\n lines.push(\"```\");\n lines.push(\"**After:**\");\n lines.push(\"```typescript\");\n lines.push(item.codeChangeAfter);\n lines.push(\"```\");\n lines.push(\"\");\n }\n }\n\n lines.push(\"## Resources\");\n lines.push(\"- OpenFeature docs: https://openfeature.dev/docs\");\n lines.push(\n \"- OpenFeature React SDK: https://openfeature.dev/docs/reference/technologies/client/web/react\"\n );\n lines.push(\"\");\n\n return lines.join(\"\\n\");\n}\n","import { createCLI } from \"../src/cli.js\";\n\nconst program = createCLI();\nprogram.parse(process.argv);\n"],"mappings":"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","names":["resolve","program","resolve","writeFile","stat","resolve","chalk","ora","program","stat","resolve","chalk","ora","writeFile","program"]}