flagwatch 1.0.0

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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +178 -0
  3. package/dist/analyzer/analyze-flags.d.ts +3 -0
  4. package/dist/analyzer/analyze-flags.d.ts.map +1 -0
  5. package/dist/analyzer/analyze-flags.js +165 -0
  6. package/dist/analyzer/analyze-flags.js.map +1 -0
  7. package/dist/cli/parse-args.d.ts +5 -0
  8. package/dist/cli/parse-args.d.ts.map +1 -0
  9. package/dist/cli/parse-args.js +122 -0
  10. package/dist/cli/parse-args.js.map +1 -0
  11. package/dist/cli.d.ts +3 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +65 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/config/load-config.d.ts +3 -0
  16. package/dist/config/load-config.d.ts.map +1 -0
  17. package/dist/config/load-config.js +144 -0
  18. package/dist/config/load-config.js.map +1 -0
  19. package/dist/detector/detect-flags.d.ts +4 -0
  20. package/dist/detector/detect-flags.d.ts.map +1 -0
  21. package/dist/detector/detect-flags.js +128 -0
  22. package/dist/detector/detect-flags.js.map +1 -0
  23. package/dist/errors.d.ts +23 -0
  24. package/dist/errors.d.ts.map +1 -0
  25. package/dist/errors.js +51 -0
  26. package/dist/errors.js.map +1 -0
  27. package/dist/index.d.ts +12 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +56 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/logger.d.ts +15 -0
  32. package/dist/logger.d.ts.map +1 -0
  33. package/dist/logger.js +45 -0
  34. package/dist/logger.js.map +1 -0
  35. package/dist/reporter/report-results.d.ts +3 -0
  36. package/dist/reporter/report-results.d.ts.map +1 -0
  37. package/dist/reporter/report-results.js +80 -0
  38. package/dist/reporter/report-results.js.map +1 -0
  39. package/dist/scanner/glob-matcher.d.ts +3 -0
  40. package/dist/scanner/glob-matcher.d.ts.map +1 -0
  41. package/dist/scanner/glob-matcher.js +44 -0
  42. package/dist/scanner/glob-matcher.js.map +1 -0
  43. package/dist/scanner/scan-files.d.ts +2 -0
  44. package/dist/scanner/scan-files.d.ts.map +1 -0
  45. package/dist/scanner/scan-files.js +90 -0
  46. package/dist/scanner/scan-files.js.map +1 -0
  47. package/dist/types.d.ts +51 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +3 -0
  50. package/dist/types.js.map +1 -0
  51. package/package.json +40 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mariselvam
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,178 @@
1
+ # Flagwatch
2
+
3
+ [![npm version](https://img.shields.io/npm/v/flagwatch)](https://www.npmjs.com/package/flagwatch)
4
+ [![License](https://img.shields.io/npm/l/flagwatch)](LICENSE)
5
+ [![CI Status](https://img.shields.io/github/workflow/status/your-org/flagwatch/CI)](https://github.com/your-org/flagwatch/actions)
6
+
7
+ > **CI-first visibility into feature flags and conditional code paths**
8
+ > Detect unused, missing, and misleading flags *before* they turn into dead code or risky releases.
9
+
10
+ ---
11
+
12
+ ## Quick Start
13
+
14
+ ```bash
15
+ # Run analysis
16
+ npx flagwatch
17
+
18
+ # CI-friendly output
19
+ npx flagwatch --ci
20
+
21
+ # JSON output
22
+ npx flagwatch --json
23
+ ```
24
+
25
+ No installation required. Works with any Node.js project.
26
+
27
+ ---
28
+
29
+ ## What Flagwatch Does
30
+
31
+ Flagwatch statically analyzes your codebase to extract feature-flag usage from conditional logic. It identifies:
32
+
33
+ - **Unused flags** - Never enabled anywhere
34
+ - **Missing flags** - Referenced but undefined
35
+ - **Dead conditionals** - Always true or always false
36
+ - **Flag patterns** - Environment variables, feature flags, and conditional logic
37
+
38
+ **Example output:**
39
+
40
+ ```
41
+ 🚩 Feature Flag Summary
42
+
43
+ • 11 flags detected
44
+ • 3 flags never enabled anywhere
45
+ • 2 flags referenced but undefined
46
+ • 1 flag always true (dead conditional)
47
+
48
+ Review recommended
49
+ ```
50
+
51
+ ---
52
+
53
+ ## Core Features
54
+
55
+ - 🔍 **Static Analysis** - Detects feature flags in conditional logic
56
+ - ❌ **Dead Code Detection** - Identifies flags that are never enabled
57
+ - ⚠️ **Conditional Analysis** - Flags always-true / always-false conditionals
58
+ - 🧠 **Deterministic** - Same input always produces same output
59
+ - 🤖 **CI-First** - Designed for continuous integration workflows
60
+ - 🔒 **Secure** - No shell execution, no network calls, no side effects
61
+
62
+ ---
63
+
64
+ ## Why Flagwatch?
65
+
66
+ Modern codebases rely heavily on feature flags, environment-based conditionals, and rollout toggles. Over time, teams lose clarity on which flags are active, which are never enabled, and which conditionals are always true or false.
67
+
68
+ This leads to:
69
+ - Dead or unreachable code
70
+ - Confusing PR reviews
71
+ - Risky refactors
72
+ - Silent production bugs
73
+
74
+ **Flagwatch exists to make conditional logic visible, reviewable, and trustworthy.**
75
+
76
+ ---
77
+
78
+ ## Usage
79
+
80
+ ### Local Development
81
+
82
+ ```bash
83
+ # Basic usage
84
+ npx flagwatch
85
+
86
+ # With configuration file
87
+ npx flagwatch --config flagwatch.config.json
88
+
89
+ # Ignore specific patterns
90
+ npx flagwatch --ignore "**/test/**" --ignore "**/node_modules/**"
91
+ ```
92
+
93
+ ### CI Integration
94
+
95
+ Flagwatch is designed to run in CI and surface conditional-risk early.
96
+
97
+ ```yaml
98
+ # GitHub Actions example
99
+ - name: Flagwatch
100
+ run: npx flagwatch --ci
101
+ ```
102
+
103
+ See [CI Integration Guide](docs/ci-integration.md) for detailed setup instructions.
104
+
105
+ ---
106
+
107
+ ## Documentation
108
+
109
+ - **[Comprehensive Guide](docs/flagwatch_readme.md)** - Full documentation and feature overview
110
+ - **[Configuration Reference](docs/configuration.md)** - Configuration options and examples
111
+ - **[CI Integration](docs/ci-integration.md)** - Step-by-step CI setup guides
112
+ - **[Examples](docs/examples.md)** - Practical code examples and patterns
113
+
114
+ ---
115
+
116
+ ## Design Principles
117
+
118
+ - **Visibility before enforcement** - Inform, don't block (v1)
119
+ - **Static analysis only** - No runtime evaluation
120
+ - **Deterministic output** - Same input → same output
121
+ - **Zero side effects** - No file modifications, no network calls
122
+ - **Readable over exhaustive** - Clear, actionable insights
123
+
124
+ ---
125
+
126
+ ## Operating Modes
127
+
128
+ | Mode | Behavior | Exit Code |
129
+ |------|----------|-----------|
130
+ | Default | Report only | `0` |
131
+ | `--ci` | CI-friendly output (no emojis) | `0` |
132
+ | `--json` | Machine-readable output | `0` |
133
+
134
+ > **Note:** Flagwatch does not block builds in v1. It informs — it does not enforce.
135
+
136
+ **Exit Codes:** `0` = Success, `1` = Policy violation (future), `2` = Internal failure. See [Exit Codes](docs/flagwatch_readme.md#exit-codes) for details.
137
+
138
+ ---
139
+
140
+ ## Signals Analyzed (v1 Scope)
141
+
142
+ - Feature flags in `if` / ternary conditions
143
+ - Environment-based flags (`process.env.*`)
144
+ - Flags that are always true / false
145
+ - Flags referenced but never defined
146
+
147
+ > No runtime evaluation. No mutation. No enforcement.
148
+
149
+ ---
150
+
151
+ ## Contributing
152
+
153
+ Contributions are welcome! Please see our [Contributing Guidelines](docs/flagwatch_readme.md#contributing) for details.
154
+
155
+ 1. Fork the repo
156
+ 2. Create a feature branch
157
+ 3. Add tests
158
+ 4. Submit a PR
159
+
160
+ ---
161
+
162
+ ## Roadmap (Post-v1)
163
+
164
+ - PR comments (GitHub Actions)
165
+ - Optional enforcement mode
166
+ - Config-file based flags
167
+ - Monorepo support
168
+
169
+ ---
170
+
171
+ ## License
172
+
173
+ See [LICENSE](LICENSE) file for details.
174
+
175
+ ---
176
+
177
+ > **Remember:** Dead code is rarely intentional — it's forgotten.
178
+ > Flagwatch restores **clarity, intent, and confidence** to feature-flag driven codebases.
@@ -0,0 +1,3 @@
1
+ import { FlagReference, FlagDefinition, AnalysisResult } from '../types';
2
+ export declare function analyzeFlags(files: string[], references: FlagReference[], definitions: FlagDefinition[]): AnalysisResult;
3
+ //# sourceMappingURL=analyze-flags.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze-flags.d.ts","sourceRoot":"","sources":["../../src/analyzer/analyze-flags.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,cAAc,EAAmB,cAAc,EAAE,MAAM,UAAU,CAAC;AA+G1F,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,EAAE,aAAa,EAAE,EAC3B,WAAW,EAAE,cAAc,EAAE,GAC5B,cAAc,CA4ChB"}
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.analyzeFlags = analyzeFlags;
37
+ const fs = __importStar(require("fs"));
38
+ function findUnusedFlags(definitions, references) {
39
+ const referencedNames = new Set(references.map((ref) => ref.name));
40
+ return definitions.filter((def) => !referencedNames.has(def.name));
41
+ }
42
+ function findMissingFlags(definitions, references) {
43
+ const definedNames = new Set(definitions.map((def) => def.name));
44
+ return references.filter((ref) => !definedNames.has(ref.name));
45
+ }
46
+ function detectDeadConditionals(filePath) {
47
+ const content = fs.readFileSync(filePath, 'utf-8');
48
+ const deadConditionals = [];
49
+ const alwaysTruePattern = /if\s*\(\s*(true|1|"true"|'true')\s*\)/g;
50
+ let match;
51
+ while ((match = alwaysTruePattern.exec(content)) !== null) {
52
+ if (match.index === undefined) {
53
+ continue;
54
+ }
55
+ const beforeMatch = content.substring(0, match.index);
56
+ const lines = beforeMatch.split('\n');
57
+ const line = lines.length;
58
+ const column = lines[lines.length - 1].length + 1;
59
+ deadConditionals.push({
60
+ file: filePath,
61
+ line,
62
+ column,
63
+ condition: match[0],
64
+ alwaysTrue: true,
65
+ alwaysFalse: false,
66
+ });
67
+ }
68
+ const alwaysFalsePattern = /if\s*\(\s*(false|0|"false"|'false'|null|undefined)\s*\)/g;
69
+ while ((match = alwaysFalsePattern.exec(content)) !== null) {
70
+ if (match.index === undefined) {
71
+ continue;
72
+ }
73
+ const beforeMatch = content.substring(0, match.index);
74
+ const lines = beforeMatch.split('\n');
75
+ const line = lines.length;
76
+ const column = lines[lines.length - 1].length + 1;
77
+ deadConditionals.push({
78
+ file: filePath,
79
+ line,
80
+ column,
81
+ condition: match[0],
82
+ alwaysTrue: false,
83
+ alwaysFalse: true,
84
+ });
85
+ }
86
+ const constTruePattern = /const\s+([A-Z_][A-Z0-9_]*)\s*=\s*(true|1|"true"|'true')\s*;[\s\S]*?if\s*\(\s*\1\s*\)/g;
87
+ while ((match = constTruePattern.exec(content)) !== null) {
88
+ if (match.index === undefined || !match[1]) {
89
+ continue;
90
+ }
91
+ const beforeMatch = content.substring(0, match.index);
92
+ const lines = beforeMatch.split('\n');
93
+ const line = lines.length;
94
+ const column = lines[lines.length - 1].length + 1;
95
+ deadConditionals.push({
96
+ file: filePath,
97
+ line,
98
+ column,
99
+ condition: `const ${match[1]} = ${match[2]}`,
100
+ alwaysTrue: true,
101
+ alwaysFalse: false,
102
+ });
103
+ }
104
+ const constFalsePattern = /const\s+([A-Z_][A-Z0-9_]*)\s*=\s*(false|0|"false"|'false')\s*;[\s\S]*?if\s*\(\s*\1\s*\)/g;
105
+ while ((match = constFalsePattern.exec(content)) !== null) {
106
+ if (match.index === undefined || !match[1]) {
107
+ continue;
108
+ }
109
+ const beforeMatch = content.substring(0, match.index);
110
+ const lines = beforeMatch.split('\n');
111
+ const line = lines.length;
112
+ const column = lines[lines.length - 1].length + 1;
113
+ deadConditionals.push({
114
+ file: filePath,
115
+ line,
116
+ column,
117
+ condition: `const ${match[1]} = ${match[2]}`,
118
+ alwaysTrue: false,
119
+ alwaysFalse: true,
120
+ });
121
+ }
122
+ return deadConditionals;
123
+ }
124
+ function analyzeFlags(files, references, definitions) {
125
+ const sortedReferences = [...references].sort((a, b) => {
126
+ if (a.file !== b.file) {
127
+ return a.file.localeCompare(b.file);
128
+ }
129
+ if (a.line !== b.line) {
130
+ return a.line - b.line;
131
+ }
132
+ return a.column - b.column;
133
+ });
134
+ const sortedDefinitions = [...definitions].sort((a, b) => {
135
+ if (a.file !== b.file) {
136
+ return a.file.localeCompare(b.file);
137
+ }
138
+ if (a.line !== b.line) {
139
+ return a.line - b.line;
140
+ }
141
+ return a.column - b.column;
142
+ });
143
+ const unusedFlags = findUnusedFlags(sortedDefinitions, sortedReferences);
144
+ const missingFlags = findMissingFlags(sortedDefinitions, sortedReferences);
145
+ const allDeadConditionals = [];
146
+ for (const file of files) {
147
+ try {
148
+ const deadConditionals = detectDeadConditionals(file);
149
+ allDeadConditionals.push(...deadConditionals);
150
+ }
151
+ catch {
152
+ // Skip files that can't be read
153
+ }
154
+ }
155
+ const uniqueFlags = new Set(references.map((ref) => ref.name));
156
+ const flagsDetected = uniqueFlags.size;
157
+ return {
158
+ flagsDetected,
159
+ flagsUnused: unusedFlags,
160
+ flagsMissing: missingFlags,
161
+ deadConditionals: allDeadConditionals,
162
+ allFlags: sortedReferences,
163
+ };
164
+ }
165
+ //# sourceMappingURL=analyze-flags.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze-flags.js","sourceRoot":"","sources":["../../src/analyzer/analyze-flags.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { CliOptions } from '../types';
2
+ export declare function parseArgs(args: string[]): CliOptions;
3
+ export declare function printHelp(): void;
4
+ export declare function printVersion(): void;
5
+ //# sourceMappingURL=parse-args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-args.d.ts","sourceRoot":"","sources":["../../src/cli/parse-args.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtC,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAuCpD;AAED,wBAAgB,SAAS,IAAI,IAAI,CA+BhC;AAKD,wBAAgB,YAAY,IAAI,IAAI,CAKnC"}
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.parseArgs = parseArgs;
37
+ exports.printHelp = printHelp;
38
+ exports.printVersion = printVersion;
39
+ function parseArgs(args) {
40
+ const options = {
41
+ ci: false,
42
+ json: false,
43
+ ignore: [],
44
+ strict: false,
45
+ verbose: false,
46
+ help: false,
47
+ version: false,
48
+ };
49
+ let i = 0;
50
+ while (i < args.length) {
51
+ const arg = args[i];
52
+ if (arg === '--ci') {
53
+ options.ci = true;
54
+ }
55
+ else if (arg === '--json') {
56
+ options.json = true;
57
+ }
58
+ else if (arg === '--config' && i + 1 < args.length) {
59
+ options.config = args[i + 1];
60
+ i++;
61
+ }
62
+ else if (arg === '--ignore' && i + 1 < args.length) {
63
+ options.ignore.push(args[i + 1]);
64
+ i++;
65
+ }
66
+ else if (arg === '--strict') {
67
+ options.strict = true;
68
+ }
69
+ else if (arg === '--verbose') {
70
+ options.verbose = true;
71
+ }
72
+ else if (arg === '--help' || arg === '-h') {
73
+ options.help = true;
74
+ }
75
+ else if (arg === '--version' || arg === '-v') {
76
+ options.version = true;
77
+ }
78
+ i++;
79
+ }
80
+ return options;
81
+ }
82
+ function printHelp() {
83
+ console.log(`
84
+ Flagwatch - CI-first visibility into feature flags
85
+
86
+ Usage:
87
+ npx flagwatch [options]
88
+
89
+ Options:
90
+ --ci CI-friendly output (no emojis, deterministic)
91
+ --json Machine-readable JSON output
92
+ --config <path> Path to configuration file
93
+ --ignore <pattern> Ignore file patterns (can be used multiple times)
94
+ --strict Enable strict mode (future: exit 1 on violations)
95
+ --verbose Verbose output for debugging
96
+ --help, -h Show this help message
97
+ --version, -v Show version number
98
+
99
+ Examples:
100
+ npx flagwatch
101
+ npx flagwatch --ci
102
+ npx flagwatch --json > flags.json
103
+ npx flagwatch --config flagwatch.config.json
104
+ npx flagwatch --ignore "**/test/**" --ignore "**/node_modules/**"
105
+
106
+ Exit Codes:
107
+ 0 Success
108
+ 1 Policy violation (future)
109
+ 2 Internal failure
110
+
111
+ For more information, see: https://github.com/your-org/flagwatch
112
+ `);
113
+ }
114
+ const fs = __importStar(require("fs"));
115
+ const path = __importStar(require("path"));
116
+ function printVersion() {
117
+ const packageJsonPath = path.join(__dirname, '../../package.json');
118
+ const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf-8');
119
+ const packageJson = JSON.parse(packageJsonContent);
120
+ console.log(packageJson.version);
121
+ }
122
+ //# sourceMappingURL=parse-args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-args.js","sourceRoot":"","sources":["../../src/cli/parse-args.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,8BAuCC;AAED,8BA+BC;AAKD,oCAKC;AAlFD,SAAgB,SAAS,CAAC,IAAc;IACtC,MAAM,OAAO,GAAe;QAC1B,EAAE,EAAE,KAAK;QACT,IAAI,EAAE,KAAK;QACX,MAAM,EAAE,EAAE;QACV,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,KAAK;QACX,OAAO,EAAE,KAAK;KACf,CAAC;IAEF,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAEpB,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;YACnB,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC;QACpB,CAAC;aAAM,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC5B,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;QACtB,CAAC;aAAM,IAAI,GAAG,KAAK,UAAU,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YACrD,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC7B,CAAC,EAAE,CAAC;QACN,CAAC;aAAM,IAAI,GAAG,KAAK,UAAU,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YACrD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACjC,CAAC,EAAE,CAAC;QACN,CAAC;aAAM,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;YAC9B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;QACxB,CAAC;aAAM,IAAI,GAAG,KAAK,WAAW,EAAE,CAAC;YAC/B,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;QACzB,CAAC;aAAM,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YAC5C,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;QACtB,CAAC;aAAM,IAAI,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YAC/C,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;QACzB,CAAC;QAED,CAAC,EAAE,CAAC;IACN,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAgB,SAAS;IACvB,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6Bb,CAAC,CAAC;AACH,CAAC;AAED,uCAAyB;AACzB,2CAA6B;AAE7B,SAAgB,YAAY;IAC1B,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC;IACnE,MAAM,kBAAkB,GAAG,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;IACrE,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAwB,CAAC;IAC1E,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;AACnC,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const parse_args_1 = require("./cli/parse-args");
5
+ const load_config_1 = require("./config/load-config");
6
+ const index_1 = require("./index");
7
+ const errors_1 = require("./errors");
8
+ const logger_1 = require("./logger");
9
+ const EXIT_CODE_SUCCESS = 0;
10
+ const EXIT_CODE_POLICY_VIOLATION = 1;
11
+ const EXIT_CODE_INTERNAL_FAILURE = 2;
12
+ function main() {
13
+ const args = process.argv.slice(2);
14
+ const options = (0, parse_args_1.parseArgs)(args);
15
+ if (options.help) {
16
+ (0, parse_args_1.printHelp)();
17
+ process.exit(EXIT_CODE_SUCCESS);
18
+ }
19
+ if (options.version) {
20
+ (0, parse_args_1.printVersion)();
21
+ process.exit(EXIT_CODE_SUCCESS);
22
+ }
23
+ const reporterOptions = {
24
+ ci: options.ci,
25
+ json: options.json,
26
+ verbose: options.verbose,
27
+ };
28
+ try {
29
+ const config = (0, load_config_1.loadConfig)(options.config, options.ignore);
30
+ const { result, exitCode } = (0, index_1.run)({
31
+ config,
32
+ reporterOptions,
33
+ });
34
+ console.log(result);
35
+ if (exitCode === EXIT_CODE_POLICY_VIOLATION && !config.strict) {
36
+ process.exit(EXIT_CODE_SUCCESS);
37
+ }
38
+ else {
39
+ process.exit(exitCode);
40
+ }
41
+ }
42
+ catch (error) {
43
+ const logger = (0, logger_1.createLogger)(options.verbose);
44
+ if (error instanceof errors_1.ConfigurationError) {
45
+ logger.error(`Configuration error: ${error.message}`);
46
+ if (error.configPath) {
47
+ logger.error(`Config path: ${error.configPath}`);
48
+ }
49
+ process.exit(EXIT_CODE_INTERNAL_FAILURE);
50
+ }
51
+ else if (error instanceof errors_1.InternalError) {
52
+ logger.error(`Internal error: ${error.message}`);
53
+ if (options.verbose && error.cause) {
54
+ logger.debug(`Cause: ${error.cause.message}`);
55
+ }
56
+ process.exit(EXIT_CODE_INTERNAL_FAILURE);
57
+ }
58
+ else {
59
+ logger.error(`Unexpected error: ${error instanceof Error ? error.message : 'Unknown error'}`);
60
+ process.exit(EXIT_CODE_INTERNAL_FAILURE);
61
+ }
62
+ }
63
+ }
64
+ main();
65
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;AAEA,iDAAsE;AACtE,sDAAkD;AAClD,mCAA8B;AAE9B,qCAA6D;AAC7D,qCAAwC;AAExC,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAC5B,MAAM,0BAA0B,GAAG,CAAC,CAAC;AACrC,MAAM,0BAA0B,GAAG,CAAC,CAAC;AAErC,SAAS,IAAI;IACX,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,IAAA,sBAAS,EAAC,IAAI,CAAC,CAAC;IAEhC,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,IAAA,sBAAS,GAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,IAAA,yBAAY,GAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAClC,CAAC;IAED,MAAM,eAAe,GAAoB;QACvC,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,OAAO,EAAE,OAAO,CAAC,OAAO;KACzB,CAAC;IAEF,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAA,wBAAU,EAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAE1D,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAA,WAAG,EAAC;YAC/B,MAAM;YACN,eAAe;SAChB,CAAC,CAAC;QAEH,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAEpB,IAAI,QAAQ,KAAK,0BAA0B,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YAC9D,OAAO,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAClC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,MAAM,GAAG,IAAA,qBAAY,EAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAE7C,IAAI,KAAK,YAAY,2BAAkB,EAAE,CAAC;YACxC,MAAM,CAAC,KAAK,CAAC,wBAAwB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACtD,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;gBACrB,MAAM,CAAC,KAAK,CAAC,gBAAgB,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;YACnD,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QAC3C,CAAC;aAAM,IAAI,KAAK,YAAY,sBAAa,EAAE,CAAC;YAC1C,MAAM,CAAC,KAAK,CAAC,mBAAmB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACjD,IAAI,OAAO,CAAC,OAAO,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;gBACnC,MAAM,CAAC,KAAK,CAAC,UAAU,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAChD,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QAC3C,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,KAAK,CAAC,qBAAqB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC;YAC9F,OAAO,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { FlagwatchConfig } from '../types';
2
+ export declare function loadConfig(configPath?: string, cliExcludes?: string[]): FlagwatchConfig;
3
+ //# sourceMappingURL=load-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-config.d.ts","sourceRoot":"","sources":["../../src/config/load-config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAyF3C,wBAAgB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,WAAW,GAAE,MAAM,EAAO,GAAG,eAAe,CA8C3F"}