flagshark 1.1.2 → 1.3.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 (2) hide show
  1. package/dist/cli.js +91 -5
  2. package/package.json +4 -3
package/dist/cli.js CHANGED
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { scanRepo } from "@flagshark/core";
4
+ import { readFileSync, existsSync } from "node:fs";
5
+ import { parse as parseYaml } from "yaml";
6
+ import { scanRepo, FlagsharkConfigSchema } from "@flagshark/core";
5
7
 
6
8
  // src/formatter.ts
7
- var VERSION = "1.1.2";
9
+ var VERSION = "1.2.0";
8
10
  function pad(str, width) {
9
11
  if (str.length > width) {
10
12
  return str.slice(0, width - 1) + "\u2026";
@@ -49,6 +51,9 @@ function formatText(result, options) {
49
51
  lines.push("");
50
52
  const langCount = Object.keys(result.languageBreakdown).length;
51
53
  lines.push(`Scanned ${result.filesScanned} files across ${langCount} language${langCount === 1 ? "" : "s"}`);
54
+ if (result.excludedCount && result.excludedCount > 0) {
55
+ lines.push(`(${result.excludedCount} excluded via .flagsharkignore + excludes)`);
56
+ }
52
57
  if (result.totalFlags === 0) {
53
58
  lines.push("No feature flags detected.");
54
59
  lines.push("");
@@ -85,6 +90,13 @@ function formatText(result, options) {
85
90
  lines.push("Automate cleanup \u2192 https://flagshark.com");
86
91
  lines.push("Open source CLI \u2192 https://github.com/FlagShark/flagshark");
87
92
  }
93
+ if (result.excludedPaths && result.excludedPaths.length > 0) {
94
+ lines.push("");
95
+ lines.push(`Excluded files (${result.excludedPaths.length}):`);
96
+ for (const p of result.excludedPaths) {
97
+ lines.push(` ${p}`);
98
+ }
99
+ }
88
100
  return lines.join("\n");
89
101
  }
90
102
  function formatJson(result) {
@@ -110,6 +122,7 @@ function formatJson(result) {
110
122
  detectedProviders: result.detectedProviders,
111
123
  languages,
112
124
  flags,
125
+ excludedPaths: result.excludedPaths,
113
126
  scanDuration: result.scanDuration,
114
127
  links: {
115
128
  dashboard: "https://flagshark.com",
@@ -121,7 +134,7 @@ function formatJson(result) {
121
134
  }
122
135
 
123
136
  // src/cli.ts
124
- var VERSION2 = "1.1.2";
137
+ var VERSION2 = "1.3.0";
125
138
  var HELP_TEXT = `
126
139
  flagshark scan [options]
127
140
 
@@ -132,19 +145,30 @@ Options:
132
145
  --verbose Show all stale flags (not just top 10)
133
146
  --help Show help
134
147
  --version Show version
148
+
149
+ Configuration:
150
+ --config <path> Use this config file (overrides .flagshark.yml discovery)
151
+ --no-config Skip config file discovery
152
+ --no-ignore-file Skip .flagsharkignore discovery
153
+ --show-excluded Show excluded files in text output
135
154
  `.trim();
136
155
  function parseArgs(argv) {
137
156
  const args = {
138
157
  json: false,
139
158
  diff: null,
140
- threshold: 6,
159
+ threshold: void 0,
141
160
  verbose: false,
142
161
  help: false,
143
162
  version: false
144
163
  };
145
164
  let i = 2;
146
165
  while (i < argv.length) {
147
- const arg = argv[i];
166
+ let arg = argv[i];
167
+ if (arg.startsWith("--") && arg.includes("=")) {
168
+ const eqIdx = arg.indexOf("=");
169
+ argv.splice(i, 1, arg.slice(0, eqIdx), arg.slice(eqIdx + 1));
170
+ arg = argv[i];
171
+ }
148
172
  switch (arg) {
149
173
  case "--json":
150
174
  args.json = true;
@@ -174,6 +198,32 @@ function parseArgs(argv) {
174
198
  case "-v":
175
199
  args.version = true;
176
200
  break;
201
+ case "--engine": {
202
+ const value = argv[++i];
203
+ if (value !== "regex" && value !== "tree-sitter") {
204
+ process.stderr.write(`Error: --engine must be 'regex' or 'tree-sitter', got '${value}'
205
+ `);
206
+ process.exit(2);
207
+ }
208
+ args.engine = value;
209
+ break;
210
+ }
211
+ case "--config":
212
+ i++;
213
+ args.configPath = argv[i];
214
+ if (!args.configPath) {
215
+ throw new Error("--config requires a file path argument");
216
+ }
217
+ break;
218
+ case "--no-config":
219
+ args.noConfig = true;
220
+ break;
221
+ case "--no-ignore-file":
222
+ args.noIgnoreFile = true;
223
+ break;
224
+ case "--show-excluded":
225
+ args.showExcluded = true;
226
+ break;
177
227
  case "scan":
178
228
  break;
179
229
  default:
@@ -212,12 +262,48 @@ async function main() {
212
262
  } else {
213
263
  logger.info("Scanning current directory...");
214
264
  }
265
+ let configOverride;
266
+ if (args.configPath) {
267
+ if (!existsSync(args.configPath)) {
268
+ process.stderr.write(`Error: config file not found: ${args.configPath}
269
+ `);
270
+ process.exit(2);
271
+ }
272
+ const raw = readFileSync(args.configPath, "utf-8");
273
+ const parsed = parseYaml(raw);
274
+ const configResult = FlagsharkConfigSchema.safeParse(parsed);
275
+ if (!configResult.success) {
276
+ process.stderr.write(`Error: invalid config at ${args.configPath}: ${configResult.error.message}
277
+ `);
278
+ process.exit(2);
279
+ }
280
+ configOverride = configResult.data;
281
+ }
215
282
  const result = await scanRepo({
216
283
  cwd: process.cwd(),
217
284
  threshold: args.threshold,
218
285
  diff: args.diff ?? void 0,
286
+ engine: args.engine,
287
+ config: configOverride,
288
+ noConfig: args.noConfig,
289
+ noIgnoreFile: args.noIgnoreFile,
290
+ collectExcludedPaths: args.showExcluded,
219
291
  logger
220
292
  });
293
+ if (args.verbose && result.effectiveExcludes) {
294
+ const r = result.effectiveExcludes;
295
+ const allRules = [
296
+ ...r.paths.map((p) => `excludes.paths: ${p}`),
297
+ ...r.files.map((p) => `excludes.files: ${p}`),
298
+ ...r.presets.flatMap((name, i) => [`excludes.presets[${i}]: ${name}`]),
299
+ ...r.ignoreFile.map((p) => `.flagsharkignore: ${p}`)
300
+ ];
301
+ if (allRules.length > 0) {
302
+ process.stderr.write("Effective excludes:\n");
303
+ for (const rule of allRules) process.stderr.write(` ${rule}
304
+ `);
305
+ }
306
+ }
221
307
  const output = args.json ? formatJson(result) + "\n" : formatText(result, { json: false, verbose: args.verbose, maxDisplay: 10 }) + "\n";
222
308
  const exitCode = result.staleFlags.length > 0 ? 1 : 0;
223
309
  if (process.stdout.write(output)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flagshark",
3
- "version": "1.1.2",
3
+ "version": "1.3.0",
4
4
  "type": "module",
5
5
  "description": "Find stale feature flags in your codebase",
6
6
  "license": "MIT",
@@ -17,12 +17,13 @@
17
17
  "main": "./dist/cli.js",
18
18
  "files": ["dist/", "bin/"],
19
19
  "scripts": {
20
- "build": "esbuild src/cli.ts --bundle --platform=node --target=node18 --format=esm --outfile=dist/cli.js --external:zod --external:@flagshark/core",
20
+ "build": "esbuild src/cli.ts --bundle --platform=node --target=node18 --format=esm --outfile=dist/cli.js --external:zod --external:@flagshark/core --external:yaml",
21
21
  "test": "vitest run",
22
22
  "typecheck": "tsc --noEmit"
23
23
  },
24
24
  "dependencies": {
25
- "@flagshark/core": "1.0.0",
25
+ "@flagshark/core": "1.2.0",
26
+ "yaml": "^2.4.0",
26
27
  "zod": "^3.23.0"
27
28
  },
28
29
  "devDependencies": {