flaglint 0.1.1 → 0.1.5
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 +6 -0
- package/README.md +25 -1
- package/dist/bin/flaglint.js +38 -22
- package/package.json +8 -4
- package/dist/bin/flaglint.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,12 @@ 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.1.5] - 2026-05-21
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Corrected the README CI badge URL.
|
|
13
|
+
|
|
8
14
|
## [0.1.0] - 2026-05-18
|
|
9
15
|
|
|
10
16
|
### Added
|
package/README.md
CHANGED
|
@@ -1,8 +1,32 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="docs/assets/logo.png" alt="FlagLint" width="400" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<strong>Find stale feature flags. Detect flag debt. Plan your OpenFeature migration.</strong>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://github.com/flaglint/flaglint/actions/workflows/ci.yml">
|
|
11
|
+
<img src="https://github.com/flaglint/flaglint/actions/workflows/ci.yml/badge.svg" alt="CI" />
|
|
12
|
+
</a>
|
|
13
|
+
<a href="https://www.npmjs.com/package/flaglint">
|
|
14
|
+
<img src="https://img.shields.io/npm/v/flaglint.svg" alt="npm version" />
|
|
15
|
+
</a>
|
|
16
|
+
<a href="https://www.npmjs.com/package/flaglint">
|
|
17
|
+
<img src="https://img.shields.io/npm/dm/flaglint.svg" alt="downloads" />
|
|
18
|
+
</a>
|
|
19
|
+
<a href="https://opensource.org/licenses/MIT">
|
|
20
|
+
<img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="MIT License" />
|
|
21
|
+
</a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
|
|
1
25
|
# FlagLint
|
|
2
26
|
|
|
3
27
|
**Find stale feature flags. Detect flag debt. Plan your OpenFeature migration.**
|
|
4
28
|
|
|
5
|
-
[](https://github.com/flaglint/flaglint/actions/workflows/ci.yml)
|
|
6
30
|
[](https://www.npmjs.com/package/flaglint)
|
|
7
31
|
[](https://opensource.org/licenses/MIT)
|
|
8
32
|
|
package/dist/bin/flaglint.js
CHANGED
|
@@ -141,7 +141,7 @@ function detectUsages(ast, filePath) {
|
|
|
141
141
|
line: loc.line,
|
|
142
142
|
column: loc.column,
|
|
143
143
|
callType: "provider",
|
|
144
|
-
isStale:
|
|
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.
|
|
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(`- \`
|
|
314
|
+
lines.push(`- \`${u.flagKey}\` at ${u.file}:${u.line} \u2014 key determined at runtime`);
|
|
315
315
|
}
|
|
316
316
|
lines.push("");
|
|
317
317
|
}
|
|
@@ -322,7 +322,7 @@ function formatJSON(result) {
|
|
|
322
322
|
}
|
|
323
323
|
function formatHTML(result, options) {
|
|
324
324
|
const { scannedFiles, totalUsages, uniqueFlags, usages, scanDurationMs } = result;
|
|
325
|
-
const staleCount = usages.filter((u) => u.isStale).
|
|
325
|
+
const staleCount = new Set(usages.filter((u) => u.isStale).map((u) => u.flagKey)).size;
|
|
326
326
|
const dynamicCount = usages.filter((u) => u.isDynamic).length;
|
|
327
327
|
const date = (/* @__PURE__ */ new Date()).toLocaleString();
|
|
328
328
|
const flagMap = buildFlagMap(usages);
|
|
@@ -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.
|
|
337
|
+
const version = true ? "0.1.5" : "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
|
-
|
|
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.
|
|
747
|
+
const version = true ? "0.1.5" : "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.
|
|
945
|
+
program2.name("flaglint").description("Find stale feature flags. Detect flag debt. Plan your OpenFeature migration.").version("0.1.5", "-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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flaglint",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Find stale feature flags. Detect flag debt. Plan your OpenFeature migration.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -27,23 +27,27 @@
|
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"repository": {
|
|
29
29
|
"type": "git",
|
|
30
|
-
"url": "
|
|
30
|
+
"url": "https://github.com/flaglint/flaglint.git"
|
|
31
31
|
},
|
|
32
32
|
"homepage": "https://flaglint.dev",
|
|
33
33
|
"bugs": {
|
|
34
|
-
"url": "https://
|
|
34
|
+
"url": "https://github.com/flaglint/flaglint/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",
|
package/dist/bin/flaglint.js.map
DELETED
|
@@ -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, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\");\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"]}
|