@aiready/testability 0.1.6 → 0.1.8

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/dist/cli.js CHANGED
@@ -27,63 +27,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
  var import_commander = require("commander");
28
28
 
29
29
  // src/analyzer.ts
30
+ var import_core = require("@aiready/core");
30
31
  var import_fs = require("fs");
31
32
  var import_path = require("path");
32
33
  var import_typescript_estree = require("@typescript-eslint/typescript-estree");
33
- var import_core = require("@aiready/core");
34
- var SRC_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
35
- var DEFAULT_EXCLUDES = [
36
- "node_modules",
37
- "dist",
38
- ".git",
39
- "coverage",
40
- ".turbo",
41
- "build"
42
- ];
43
- var TEST_PATTERNS = [
44
- /\.(test|spec)\.(ts|tsx|js|jsx)$/,
45
- /__tests__\//,
46
- /\/tests?\//,
47
- /\/e2e\//,
48
- /\/fixtures\//
49
- ];
50
- function isTestFile(filePath, extra) {
51
- if (TEST_PATTERNS.some((p) => p.test(filePath))) return true;
52
- if (extra) return extra.some((p) => filePath.includes(p));
53
- return false;
54
- }
55
- function isSourceFile(filePath) {
56
- return SRC_EXTENSIONS.has((0, import_path.extname)(filePath));
57
- }
58
- function collectFiles(dir, options, depth = 0) {
59
- if (depth > (options.maxDepth ?? 20)) return [];
60
- const excludes = [...DEFAULT_EXCLUDES, ...options.exclude ?? []];
61
- const files = [];
62
- let entries;
63
- try {
64
- entries = (0, import_fs.readdirSync)(dir);
65
- } catch {
66
- return files;
67
- }
68
- for (const entry of entries) {
69
- if (excludes.some((ex) => entry === ex || entry.includes(ex))) continue;
70
- const full = (0, import_path.join)(dir, entry);
71
- let stat;
72
- try {
73
- stat = (0, import_fs.statSync)(full);
74
- } catch {
75
- continue;
76
- }
77
- if (stat.isDirectory()) {
78
- files.push(...collectFiles(full, options, depth + 1));
79
- } else if (stat.isFile() && isSourceFile(full)) {
80
- if (!options.include || options.include.some((p) => full.includes(p))) {
81
- files.push(full);
82
- }
83
- }
84
- }
85
- return files;
86
- }
87
34
  function countMethodsInInterface(node) {
88
35
  if (node.type === "TSInterfaceDeclaration") {
89
36
  return node.body.body.filter(
@@ -235,8 +182,24 @@ function detectTestFramework(rootDir) {
235
182
  return false;
236
183
  }
237
184
  }
185
+ var TEST_PATTERNS = [
186
+ /\.(test|spec)\.(ts|tsx|js|jsx)$/,
187
+ /__tests__\//,
188
+ /\/tests?\//,
189
+ /\/e2e\//,
190
+ /\/fixtures\//
191
+ ];
192
+ function isTestFile(filePath, extra) {
193
+ if (TEST_PATTERNS.some((p) => p.test(filePath))) return true;
194
+ if (extra) return extra.some((p) => filePath.includes(p));
195
+ return false;
196
+ }
238
197
  async function analyzeTestability(options) {
239
- const allFiles = collectFiles(options.rootDir, options);
198
+ const allFiles = await (0, import_core.scanFiles)({
199
+ ...options,
200
+ include: options.include || ["**/*.{ts,tsx,js,jsx}"],
201
+ includeTests: true
202
+ });
240
203
  const sourceFiles = allFiles.filter(
241
204
  (f) => !isTestFile(f, options.testPatterns)
242
205
  );
@@ -250,7 +213,14 @@ async function analyzeTestability(options) {
250
213
  totalInterfaces: 0,
251
214
  externalStateMutations: 0
252
215
  };
216
+ let processed = 0;
253
217
  for (const f of sourceFiles) {
218
+ processed++;
219
+ options.onProgress?.(
220
+ processed,
221
+ sourceFiles.length,
222
+ `testability: analyzing files`
223
+ );
254
224
  const a = analyzeFileTestability(f);
255
225
  for (const key of Object.keys(aggregated)) {
256
226
  aggregated[key] += a[key];
package/dist/cli.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  analyzeTestability,
4
4
  calculateTestabilityScore
5
- } from "./chunk-YLYLRZRS.mjs";
5
+ } from "./chunk-DDNB7FI4.mjs";
6
6
 
7
7
  // src/cli.ts
8
8
  import { Command } from "commander";
package/dist/index.d.mts CHANGED
@@ -13,6 +13,8 @@ interface TestabilityOptions {
13
13
  include?: string[];
14
14
  /** File glob patterns to exclude */
15
15
  exclude?: string[];
16
+ /** Progress callback */
17
+ onProgress?: (processed: number, total: number, message: string) => void;
16
18
  }
17
19
  interface TestabilityIssue extends Issue {
18
20
  type: 'low-testability';
package/dist/index.d.ts CHANGED
@@ -13,6 +13,8 @@ interface TestabilityOptions {
13
13
  include?: string[];
14
14
  /** File glob patterns to exclude */
15
15
  exclude?: string[];
16
+ /** Progress callback */
17
+ onProgress?: (processed: number, total: number, message: string) => void;
16
18
  }
17
19
  interface TestabilityIssue extends Issue {
18
20
  type: 'low-testability';
package/dist/index.js CHANGED
@@ -26,63 +26,10 @@ __export(index_exports, {
26
26
  module.exports = __toCommonJS(index_exports);
27
27
 
28
28
  // src/analyzer.ts
29
+ var import_core = require("@aiready/core");
29
30
  var import_fs = require("fs");
30
31
  var import_path = require("path");
31
32
  var import_typescript_estree = require("@typescript-eslint/typescript-estree");
32
- var import_core = require("@aiready/core");
33
- var SRC_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
34
- var DEFAULT_EXCLUDES = [
35
- "node_modules",
36
- "dist",
37
- ".git",
38
- "coverage",
39
- ".turbo",
40
- "build"
41
- ];
42
- var TEST_PATTERNS = [
43
- /\.(test|spec)\.(ts|tsx|js|jsx)$/,
44
- /__tests__\//,
45
- /\/tests?\//,
46
- /\/e2e\//,
47
- /\/fixtures\//
48
- ];
49
- function isTestFile(filePath, extra) {
50
- if (TEST_PATTERNS.some((p) => p.test(filePath))) return true;
51
- if (extra) return extra.some((p) => filePath.includes(p));
52
- return false;
53
- }
54
- function isSourceFile(filePath) {
55
- return SRC_EXTENSIONS.has((0, import_path.extname)(filePath));
56
- }
57
- function collectFiles(dir, options, depth = 0) {
58
- if (depth > (options.maxDepth ?? 20)) return [];
59
- const excludes = [...DEFAULT_EXCLUDES, ...options.exclude ?? []];
60
- const files = [];
61
- let entries;
62
- try {
63
- entries = (0, import_fs.readdirSync)(dir);
64
- } catch {
65
- return files;
66
- }
67
- for (const entry of entries) {
68
- if (excludes.some((ex) => entry === ex || entry.includes(ex))) continue;
69
- const full = (0, import_path.join)(dir, entry);
70
- let stat;
71
- try {
72
- stat = (0, import_fs.statSync)(full);
73
- } catch {
74
- continue;
75
- }
76
- if (stat.isDirectory()) {
77
- files.push(...collectFiles(full, options, depth + 1));
78
- } else if (stat.isFile() && isSourceFile(full)) {
79
- if (!options.include || options.include.some((p) => full.includes(p))) {
80
- files.push(full);
81
- }
82
- }
83
- }
84
- return files;
85
- }
86
33
  function countMethodsInInterface(node) {
87
34
  if (node.type === "TSInterfaceDeclaration") {
88
35
  return node.body.body.filter(
@@ -234,8 +181,24 @@ function detectTestFramework(rootDir) {
234
181
  return false;
235
182
  }
236
183
  }
184
+ var TEST_PATTERNS = [
185
+ /\.(test|spec)\.(ts|tsx|js|jsx)$/,
186
+ /__tests__\//,
187
+ /\/tests?\//,
188
+ /\/e2e\//,
189
+ /\/fixtures\//
190
+ ];
191
+ function isTestFile(filePath, extra) {
192
+ if (TEST_PATTERNS.some((p) => p.test(filePath))) return true;
193
+ if (extra) return extra.some((p) => filePath.includes(p));
194
+ return false;
195
+ }
237
196
  async function analyzeTestability(options) {
238
- const allFiles = collectFiles(options.rootDir, options);
197
+ const allFiles = await (0, import_core.scanFiles)({
198
+ ...options,
199
+ include: options.include || ["**/*.{ts,tsx,js,jsx}"],
200
+ includeTests: true
201
+ });
239
202
  const sourceFiles = allFiles.filter(
240
203
  (f) => !isTestFile(f, options.testPatterns)
241
204
  );
@@ -249,7 +212,14 @@ async function analyzeTestability(options) {
249
212
  totalInterfaces: 0,
250
213
  externalStateMutations: 0
251
214
  };
215
+ let processed = 0;
252
216
  for (const f of sourceFiles) {
217
+ processed++;
218
+ options.onProgress?.(
219
+ processed,
220
+ sourceFiles.length,
221
+ `testability: analyzing files`
222
+ );
253
223
  const a = analyzeFileTestability(f);
254
224
  for (const key of Object.keys(aggregated)) {
255
225
  aggregated[key] += a[key];
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  analyzeTestability,
3
3
  calculateTestabilityScore
4
- } from "./chunk-YLYLRZRS.mjs";
4
+ } from "./chunk-DDNB7FI4.mjs";
5
5
  export {
6
6
  analyzeTestability,
7
7
  calculateTestabilityScore
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/testability",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Measures how safely and verifiably AI-generated changes can be made to your codebase",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -40,7 +40,7 @@
40
40
  "chalk": "^5.3.0",
41
41
  "commander": "^14.0.0",
42
42
  "glob": "^13.0.0",
43
- "@aiready/core": "0.9.33"
43
+ "@aiready/core": "0.9.35"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/node": "^24.0.0",
package/src/analyzer.ts CHANGED
@@ -10,82 +10,17 @@
10
10
  * 5. Observability (return values vs. external state mutations)
11
11
  */
12
12
 
13
- import { readdirSync, statSync, readFileSync, existsSync } from 'fs';
14
- import { join, extname, basename } from 'path';
13
+ import { scanFiles, calculateTestabilityIndex } from '@aiready/core';
14
+ import { readFileSync, existsSync } from 'fs';
15
+ import { join } from 'path';
15
16
  import { parse } from '@typescript-eslint/typescript-estree';
16
17
  import type { TSESTree } from '@typescript-eslint/types';
17
- import { calculateTestabilityIndex } from '@aiready/core';
18
18
  import type {
19
19
  TestabilityOptions,
20
20
  TestabilityIssue,
21
21
  TestabilityReport,
22
22
  } from './types';
23
23
 
24
- // ---------------------------------------------------------------------------
25
- // File classification
26
- // ---------------------------------------------------------------------------
27
-
28
- const SRC_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx']);
29
- const DEFAULT_EXCLUDES = [
30
- 'node_modules',
31
- 'dist',
32
- '.git',
33
- 'coverage',
34
- '.turbo',
35
- 'build',
36
- ];
37
- const TEST_PATTERNS = [
38
- /\.(test|spec)\.(ts|tsx|js|jsx)$/,
39
- /__tests__\//,
40
- /\/tests?\//,
41
- /\/e2e\//,
42
- /\/fixtures\//,
43
- ];
44
-
45
- function isTestFile(filePath: string, extra?: string[]): boolean {
46
- if (TEST_PATTERNS.some((p) => p.test(filePath))) return true;
47
- if (extra) return extra.some((p) => filePath.includes(p));
48
- return false;
49
- }
50
-
51
- function isSourceFile(filePath: string): boolean {
52
- return SRC_EXTENSIONS.has(extname(filePath));
53
- }
54
-
55
- function collectFiles(
56
- dir: string,
57
- options: TestabilityOptions,
58
- depth = 0
59
- ): string[] {
60
- if (depth > (options.maxDepth ?? 20)) return [];
61
- const excludes = [...DEFAULT_EXCLUDES, ...(options.exclude ?? [])];
62
- const files: string[] = [];
63
- let entries: string[];
64
- try {
65
- entries = readdirSync(dir);
66
- } catch {
67
- return files;
68
- }
69
- for (const entry of entries) {
70
- if (excludes.some((ex) => entry === ex || entry.includes(ex))) continue;
71
- const full = join(dir, entry);
72
- let stat;
73
- try {
74
- stat = statSync(full);
75
- } catch {
76
- continue;
77
- }
78
- if (stat.isDirectory()) {
79
- files.push(...collectFiles(full, options, depth + 1));
80
- } else if (stat.isFile() && isSourceFile(full)) {
81
- if (!options.include || options.include.some((p) => full.includes(p))) {
82
- files.push(full);
83
- }
84
- }
85
- }
86
- return files;
87
- }
88
-
89
24
  // ---------------------------------------------------------------------------
90
25
  // Per-file analysis
91
26
  // ---------------------------------------------------------------------------
@@ -324,10 +259,29 @@ function detectTestFramework(rootDir: string): boolean {
324
259
  // Main analyzer
325
260
  // ---------------------------------------------------------------------------
326
261
 
262
+ const TEST_PATTERNS = [
263
+ /\.(test|spec)\.(ts|tsx|js|jsx)$/,
264
+ /__tests__\//,
265
+ /\/tests?\//,
266
+ /\/e2e\//,
267
+ /\/fixtures\//,
268
+ ];
269
+
270
+ function isTestFile(filePath: string, extra?: string[]): boolean {
271
+ if (TEST_PATTERNS.some((p) => p.test(filePath))) return true;
272
+ if (extra) return extra.some((p) => filePath.includes(p));
273
+ return false;
274
+ }
275
+
327
276
  export async function analyzeTestability(
328
277
  options: TestabilityOptions
329
278
  ): Promise<TestabilityReport> {
330
- const allFiles = collectFiles(options.rootDir, options);
279
+ // Use core scanFiles which respects .gitignore recursively
280
+ const allFiles = await scanFiles({
281
+ ...options,
282
+ include: options.include || ['**/*.{ts,tsx,js,jsx}'],
283
+ includeTests: true,
284
+ });
331
285
 
332
286
  const sourceFiles = allFiles.filter(
333
287
  (f) => !isTestFile(f, options.testPatterns)
@@ -344,7 +298,15 @@ export async function analyzeTestability(
344
298
  externalStateMutations: 0,
345
299
  };
346
300
 
301
+ let processed = 0;
347
302
  for (const f of sourceFiles) {
303
+ processed++;
304
+ options.onProgress?.(
305
+ processed,
306
+ sourceFiles.length,
307
+ `testability: analyzing files`
308
+ );
309
+
348
310
  const a = analyzeFileTestability(f);
349
311
  for (const key of Object.keys(aggregated) as Array<keyof FileAnalysis>) {
350
312
  aggregated[key] += a[key];
package/src/scoring.ts CHANGED
@@ -1,4 +1,3 @@
1
- import { calculateTestabilityIndex } from '@aiready/core';
2
1
  import type { ToolScoringOutput } from '@aiready/core';
3
2
  import type { TestabilityReport } from './types';
4
3
 
package/src/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ScanOptions, Issue } from '@aiready/core';
1
+ import type { Issue } from '@aiready/core';
2
2
 
3
3
  export interface TestabilityOptions {
4
4
  /** Root directory to scan */
@@ -13,6 +13,8 @@ export interface TestabilityOptions {
13
13
  include?: string[];
14
14
  /** File glob patterns to exclude */
15
15
  exclude?: string[];
16
+ /** Progress callback */
17
+ onProgress?: (processed: number, total: number, message: string) => void;
16
18
  }
17
19
 
18
20
  export interface TestabilityIssue extends Issue {