fileflows 1.0.3 → 1.0.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/README.md +19 -11
- package/cli.mjs +7 -0
- package/config/jest-require-polyfill.cjs +20 -0
- package/config/jest-setup.mjs +28 -0
- package/config/jest.config.mjs +72 -0
- package/config/localVars.js +2 -109
- package/dist/cli.js +76 -0
- package/dist/config/localVars.js +76 -0
- package/dist/index.js +7 -0
- package/dist/lib/dataFlowGrouper.js +137 -0
- package/dist/lib/dependencyExtractor.js +46 -0
- package/dist/lib/fileClassifier.js +78 -0
- package/dist/lib/fileFlowsGenerator.js +301 -0
- package/dist/lib/fileIO.js +35 -0
- package/dist/lib/graphUtils.js +89 -0
- package/dist/lib/index.js +50 -0
- package/dist/lib/jsParser.js +131 -0
- package/dist/lib/otherFileParser.js +131 -0
- package/index.mjs +2 -0
- package/package.json +31 -14
- package/scripts/broadcast.sh +26 -0
- package/scripts/clean-bun-cache.mjs +14 -0
- package/scripts/clean-dist.mjs +7 -0
- package/scripts/ensure-runner.mjs +9 -0
- package/scripts/kill-agent.sh +24 -0
- package/scripts/kill-all-agents.sh +31 -0
- package/scripts/list-agents.sh +16 -0
- package/scripts/send-to-agent.sh +28 -0
- package/scripts/spawn-agent.sh +62 -0
- package/cli.js +0 -81
- package/config/localVars.test.js +0 -37
- package/index.js +0 -13
- package/lib/SUMMARY.md +0 -53
- package/lib/dataFlowGrouper.js +0 -150
- package/lib/dataFlowGrouper.test.js +0 -17
- package/lib/dependencyExtractor.js +0 -70
- package/lib/dependencyExtractor.test.js +0 -9
- package/lib/fileClassifier.js +0 -38
- package/lib/fileClassifier.test.js +0 -9
- package/lib/fileFlowsGenerator.js +0 -156
- package/lib/fileFlowsGenerator.test.js +0 -17
- package/lib/fileIO.js +0 -60
- package/lib/fileIO.test.js +0 -13
- package/lib/graphUtils.js +0 -139
- package/lib/graphUtils.test.js +0 -25
- package/lib/index.js +0 -29
- package/lib/index.test.js +0 -53
- package/lib/jsParser.js +0 -132
- package/lib/jsParser.test.js +0 -13
- package/lib/otherFileParser.js +0 -103
- package/lib/otherFileParser.test.js +0 -9
package/README.md
CHANGED
|
@@ -56,16 +56,16 @@ npx fileflows
|
|
|
56
56
|
npm run generate
|
|
57
57
|
|
|
58
58
|
# Direct node execution
|
|
59
|
-
node node_modules/fileflows/cli.
|
|
59
|
+
node node_modules/fileflows/cli.mjs
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
### Development/Direct Use
|
|
63
63
|
```bash
|
|
64
64
|
# Clone or download the repository, then:
|
|
65
|
-
node cli.
|
|
66
|
-
node cli.
|
|
67
|
-
node cli.
|
|
68
|
-
node cli.
|
|
65
|
+
node cli.mjs # Analyze current directory
|
|
66
|
+
node cli.mjs --dir ./src # Analyze src directory
|
|
67
|
+
node cli.mjs --output flows.md # Custom output file
|
|
68
|
+
node cli.mjs --help # Show help
|
|
69
69
|
```
|
|
70
70
|
|
|
71
71
|
## How It Works
|
|
@@ -92,6 +92,14 @@ The generated `FILE_FLOWS.md` includes:
|
|
|
92
92
|
- File type classification
|
|
93
93
|
- Extracted metadata (imports, exports, functions, etc.)
|
|
94
94
|
- Clear structure for understanding project architecture
|
|
95
|
+
- Companion `FILE_FLOWS.json` containing the same flow groups, each file's metadata, hints about dominant directories/role scores, and cross-flow dependencies for machine consumption
|
|
96
|
+
|
|
97
|
+
## Machine-readable Output
|
|
98
|
+
|
|
99
|
+
- Every run now emits `FILE_FLOWS.json` beside `FILE_FLOWS.md`. It mirrors the Markdown groups and provides structured hints that allow downstream agents to identify which files belong together, which directories dominate a flow, how tightly coupled the group is, and what other flows it depends on.
|
|
100
|
+
- When using `--output my-flows.md`, FileFlows also writes `my-flows.json` beside it.
|
|
101
|
+
- Flow numbering matches the Markdown (`Flow Group [n]`) so orchestrators and humans can refer to the same labels.
|
|
102
|
+
- **LLM agents should read `FILE_FLOWS.json` first** before consulting the Markdown. The structured JSON is the authoritative source for machine workflows; treat `FILE_FLOWS.md` as supplemental human-friendly documentation once the JSON is understood.
|
|
95
103
|
|
|
96
104
|
## Example Output
|
|
97
105
|
|
|
@@ -100,7 +108,7 @@ The generated `FILE_FLOWS.md` includes:
|
|
|
100
108
|
> Auto-generated. Do not edit directly.
|
|
101
109
|
> Files grouped by PRIMARY: actual data flow relationships, SECONDARY: filename similarity.
|
|
102
110
|
|
|
103
|
-
### 🧩 Flow Group: `auth-flow`
|
|
111
|
+
### 🧩 Flow Group: [1] `auth-flow`
|
|
104
112
|
|
|
105
113
|
## [1] `src/auth/login.js`
|
|
106
114
|
**Type:** Feature Logic/UI
|
|
@@ -114,7 +122,7 @@ The generated `FILE_FLOWS.md` includes:
|
|
|
114
122
|
## Command Line Options
|
|
115
123
|
|
|
116
124
|
```
|
|
117
|
-
Usage:
|
|
125
|
+
Usage: fileflows [options]
|
|
118
126
|
|
|
119
127
|
Options:
|
|
120
128
|
--dir <path> Directory to analyze (default: current directory)
|
|
@@ -122,9 +130,9 @@ Options:
|
|
|
122
130
|
--help, -h Show this help message
|
|
123
131
|
|
|
124
132
|
Examples:
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
133
|
+
fileflows # Analyze current directory
|
|
134
|
+
fileflows --dir ./src # Analyze src directory
|
|
135
|
+
fileflows --output flows.md # Custom output file
|
|
128
136
|
```
|
|
129
137
|
|
|
130
|
-
This tool preserves all the sophisticated analysis capabilities of the original system while providing a clean, focused CLI interface.
|
|
138
|
+
This tool preserves all the sophisticated analysis capabilities of the original system while providing a clean, focused CLI interface.
|
package/cli.mjs
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Ensure a global require exists for ESM tests that use CommonJS require().
|
|
2
|
+
// Executed by Jest via setupFiles BEFORE test files are evaluated.
|
|
3
|
+
try {
|
|
4
|
+
if (typeof global.require === 'undefined') {
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
6
|
+
const { createRequire } = require('module');
|
|
7
|
+
let req;
|
|
8
|
+
try {
|
|
9
|
+
req = createRequire(process.cwd() + '/package.json');
|
|
10
|
+
} catch {
|
|
11
|
+
req = createRequire(__filename);
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(global, 'require', {
|
|
14
|
+
value: req,
|
|
15
|
+
writable: false,
|
|
16
|
+
configurable: true,
|
|
17
|
+
enumerable: false,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
} catch {}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import 'qtests/setup';
|
|
2
|
+
import { afterEach, beforeAll, jest as jestFromGlobals } from '@jest/globals';
|
|
3
|
+
import { createRequire } from 'module';
|
|
4
|
+
|
|
5
|
+
process.env.NODE_ENV = 'test';
|
|
6
|
+
|
|
7
|
+
const J = jestFromGlobals ?? globalThis.jest;
|
|
8
|
+
if (!globalThis.jest && J) {
|
|
9
|
+
globalThis.jest = J;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (typeof globalThis.require !== 'function') {
|
|
13
|
+
globalThis.require = createRequire(import.meta.url);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
beforeAll(() => {
|
|
17
|
+
const j = globalThis.jest ?? J;
|
|
18
|
+
if (j && typeof j.setTimeout === 'function') {
|
|
19
|
+
j.setTimeout(10000);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
const j = globalThis.jest ?? J;
|
|
25
|
+
if (j && typeof j.clearAllMocks === 'function') {
|
|
26
|
+
j.clearAllMocks();
|
|
27
|
+
}
|
|
28
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// jest.config.mjs - TypeScript ES Module configuration (React-enabled)
|
|
2
|
+
// Use ESM export to avoid CommonJS issues under "type": "module"
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = path.dirname(__filename);
|
|
7
|
+
const PROJECT_ROOT = path.resolve(__dirname, '..');
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
preset: 'ts-jest/presets/default-esm',
|
|
11
|
+
rootDir: PROJECT_ROOT,
|
|
12
|
+
testEnvironment: 'node',
|
|
13
|
+
globals: {
|
|
14
|
+
'ts-jest': {
|
|
15
|
+
useESM: true,
|
|
16
|
+
tsconfig: 'tsconfig.jest.json'
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
// Ensure CommonJS require() exists in ESM tests
|
|
20
|
+
setupFiles: [path.join(PROJECT_ROOT, 'config', 'jest-require-polyfill.cjs')],
|
|
21
|
+
setupFilesAfterEnv: [path.join(PROJECT_ROOT, 'config', 'jest-setup.mjs')],
|
|
22
|
+
roots: [PROJECT_ROOT],
|
|
23
|
+
testMatch: [
|
|
24
|
+
"**/*.test.ts",
|
|
25
|
+
"**/*.test.tsx",
|
|
26
|
+
"**/*.spec.ts",
|
|
27
|
+
"**/*.spec.tsx",
|
|
28
|
+
"**/*.GenerateTest.test.ts",
|
|
29
|
+
"**/*.GenerateTest.test.tsx",
|
|
30
|
+
"**/*.GeneratedTest.test.ts",
|
|
31
|
+
"**/*.GeneratedTest.test.tsx",
|
|
32
|
+
"**/manual-tests/**/*.test.ts",
|
|
33
|
+
"**/generated-tests/**/*GeneratedTest*.test.ts",
|
|
34
|
+
"**/generated-tests/**/*GeneratedTest*.test.tsx"
|
|
35
|
+
],
|
|
36
|
+
testPathIgnorePatterns: [
|
|
37
|
+
"/node_modules/",
|
|
38
|
+
"/dist/",
|
|
39
|
+
"/build/",
|
|
40
|
+
"/__mocks__/"
|
|
41
|
+
],
|
|
42
|
+
// Harden ignores to avoid duplicate manual mocks and compiled artifacts
|
|
43
|
+
modulePathIgnorePatterns: ['<rootDir>/dist/', '<rootDir>/build/'],
|
|
44
|
+
watchPathIgnorePatterns: ['<rootDir>/dist/', '<rootDir>/build/'],
|
|
45
|
+
moduleFileExtensions: ["ts","tsx","js","jsx","json"],
|
|
46
|
+
transform: {
|
|
47
|
+
"^.+\\.(js|jsx)$": [
|
|
48
|
+
"babel-jest",
|
|
49
|
+
{}
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
extensionsToTreatAsEsm: [".ts",".tsx"],
|
|
53
|
+
transformIgnorePatterns: ['node_modules/(?!(?:qtests|@tanstack|@radix-ui|lucide-react|react-resizable-panels|cmdk|vaul)/)'],
|
|
54
|
+
moduleNameMapper: {
|
|
55
|
+
"^\\.\\./index\\.js$": "<rootDir>/index.ts",
|
|
56
|
+
"^\\.\\./setup\\.js$": "<rootDir>/setup.ts",
|
|
57
|
+
"^\\.\\./lib/(.*)\\.js$": "<rootDir>/lib/$1.ts",
|
|
58
|
+
"^\\.\\./lib/(.*)$": "<rootDir>/lib/$1.ts",
|
|
59
|
+
"^\\.\\./utils/httpTest\\.shim\\.js$": "<rootDir>/utils/httpTest.shim.js",
|
|
60
|
+
"^\\.\\./utils/(.*)\\.js$": "<rootDir>/utils/$1.ts",
|
|
61
|
+
"^(.*/httpTest\\.shim)\\.js$": "$1.js",
|
|
62
|
+
"^external-service-client$": "<rootDir>/utils/jest-proxies/external-service-client.cjs",
|
|
63
|
+
"^feature-x$": "<rootDir>/utils/jest-proxies/feature-x.cjs",
|
|
64
|
+
"^(\\.{1,2}/.*)\\.js$": "$1",
|
|
65
|
+
"^qtests/(.*)$": "<rootDir>/node_modules/qtests/dist/$1.js",
|
|
66
|
+
"^mongoose$": "<rootDir>/__mocks__/mongoose.js",
|
|
67
|
+
"^.+\\\\.(css|less|scss|sass)$": "<rootDir>/__mocks__/fileMock.js",
|
|
68
|
+
"^.+\\\\.(png|jpg|jpeg|gif|svg|webp|avif|ico|bmp)$": "<rootDir>/__mocks__/fileMock.js",
|
|
69
|
+
"^qtests$": "<rootDir>/types/qtests",
|
|
70
|
+
"^qerrors$": "<rootDir>/types/qerrors"
|
|
71
|
+
}
|
|
72
|
+
};
|
package/config/localVars.js
CHANGED
|
@@ -1,109 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* Single source of truth for all hardcoded values in the application
|
|
4
|
-
* Following NPM architecture guidelines - DO NOT modify existing values
|
|
5
|
-
*/
|
|
6
|
-
// 🚩AI: MUST_UPDATE_IF_FILE_EXTENSIONS_CHANGE
|
|
7
|
-
|
|
8
|
-
// === FILE ANALYSIS CONSTANTS ===
|
|
9
|
-
const CODE_EXTENSIONS = [`js`, `ts`, `jsx`, `tsx`];
|
|
10
|
-
const ALL_EXTENSIONS = [...CODE_EXTENSIONS, `json`, `md`, `yml`, `yaml`, `env`, `html`, `css`, `scss`, `sh`, `graphql`];
|
|
11
|
-
|
|
12
|
-
// === DEFAULT PATHS ===
|
|
13
|
-
const DEFAULT_ROOT_DIR = `.`;
|
|
14
|
-
const DEFAULT_OUTPUT_FILE = `FILE_FLOWS.md`;
|
|
15
|
-
|
|
16
|
-
// === GITIGNORE PATTERNS ===
|
|
17
|
-
const IGNORE_PATTERNS = [
|
|
18
|
-
`**/node_modules/**`,
|
|
19
|
-
`**/.config/**`,
|
|
20
|
-
`**/logs/**`,
|
|
21
|
-
`**/.git/**`,
|
|
22
|
-
`**/tmp/**`,
|
|
23
|
-
`**/temp/**`,
|
|
24
|
-
`**/.npm/**`,
|
|
25
|
-
`**/.cache/**`,
|
|
26
|
-
`**/dist/**`,
|
|
27
|
-
`**/build/**`,
|
|
28
|
-
`**/.next/**`,
|
|
29
|
-
`**/.nuxt/**`
|
|
30
|
-
];
|
|
31
|
-
|
|
32
|
-
// === PARSING LIMITS ===
|
|
33
|
-
const MAX_JSON_KEYS = 10;
|
|
34
|
-
const MAX_YAML_KEYS = 5;
|
|
35
|
-
const MAX_SHELL_COMMANDS = 5;
|
|
36
|
-
const MAX_HTML_TAGS = 5;
|
|
37
|
-
|
|
38
|
-
// === CENTRALIZED DEPENDENCIES ===
|
|
39
|
-
// All require statements moved from global scope to centralized configuration
|
|
40
|
-
function getDependencies() {
|
|
41
|
-
return {
|
|
42
|
-
// Core library modules (from lib/index.js)
|
|
43
|
-
generateFileFlows: require(`../lib/fileFlowsGenerator`),
|
|
44
|
-
groupByDataFlow: require(`../lib/dataFlowGrouper`),
|
|
45
|
-
classifyFile: require(`../lib/fileClassifier`),
|
|
46
|
-
parseJSFile: require(`../lib/jsParser`),
|
|
47
|
-
parseOtherFile: require(`../lib/otherFileParser`),
|
|
48
|
-
extractDependencies: require(`../lib/dependencyExtractor`),
|
|
49
|
-
fileIO: require(`../lib/fileIO`),
|
|
50
|
-
graphUtils: require(`../lib/graphUtils`),
|
|
51
|
-
|
|
52
|
-
// System dependencies (from qtests-runner.js)
|
|
53
|
-
fs: require(`fs`),
|
|
54
|
-
path: require(`path`),
|
|
55
|
-
childProcess: require(`child_process`),
|
|
56
|
-
os: require(`os`)
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// === DEVELOPMENT DEPENDENCIES ===
|
|
61
|
-
// Async loading for ES module dependencies (qtests is an ES module)
|
|
62
|
-
async function getDevDependencies() {
|
|
63
|
-
// Check if this is a development environment and qtests is available
|
|
64
|
-
if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
|
|
65
|
-
try {
|
|
66
|
-
// Use dynamic import for ES modules
|
|
67
|
-
const qtests = await import('qtests');
|
|
68
|
-
return {
|
|
69
|
-
qtests: qtests
|
|
70
|
-
};
|
|
71
|
-
} catch (error) {
|
|
72
|
-
// qtests not available
|
|
73
|
-
return {
|
|
74
|
-
qtests: null
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Production environment - don't load qtests at all
|
|
80
|
-
return {
|
|
81
|
-
qtests: null
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// === ENVIRONMENT VARIABLES ===
|
|
86
|
-
// Centralized environment variable access using qerrors utilities
|
|
87
|
-
const { getEnv } = require(`qerrors`);
|
|
88
|
-
|
|
89
|
-
function getEnvironmentConfig() {
|
|
90
|
-
return {
|
|
91
|
-
NODE_ENV: getEnv(`NODE_ENV`, `development`),
|
|
92
|
-
DEBUG_TESTS: getEnv(`DEBUG_TESTS`, `false`) === `true`
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
module.exports = {
|
|
97
|
-
CODE_EXTENSIONS,
|
|
98
|
-
ALL_EXTENSIONS,
|
|
99
|
-
DEFAULT_ROOT_DIR,
|
|
100
|
-
DEFAULT_OUTPUT_FILE,
|
|
101
|
-
IGNORE_PATTERNS,
|
|
102
|
-
MAX_JSON_KEYS,
|
|
103
|
-
MAX_YAML_KEYS,
|
|
104
|
-
MAX_SHELL_COMMANDS,
|
|
105
|
-
MAX_HTML_TAGS,
|
|
106
|
-
getDependencies,
|
|
107
|
-
getDevDependencies,
|
|
108
|
-
getEnvironmentConfig
|
|
109
|
-
};
|
|
1
|
+
// This file proxies to the TypeScript-compiled configuration to maintain existing import paths.
|
|
2
|
+
module.exports = require('../dist/config/localVars.js');
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.showUsage = showUsage;
|
|
5
|
+
exports.main = main;
|
|
6
|
+
const localVars_1 = require("./config/localVars");
|
|
7
|
+
const index_1 = require("./lib/index");
|
|
8
|
+
function showUsage() {
|
|
9
|
+
console.log('Usage: fileflows [options]');
|
|
10
|
+
console.log('');
|
|
11
|
+
console.log('Options:');
|
|
12
|
+
console.log(' --dir <path> Directory to analyze (default: current directory)');
|
|
13
|
+
console.log(' --output <file> Output file path (default: FILE_FLOWS.md)');
|
|
14
|
+
console.log(' --help, -h Show this help message');
|
|
15
|
+
console.log('');
|
|
16
|
+
console.log('Examples:');
|
|
17
|
+
console.log(' fileflows # Analyze current directory');
|
|
18
|
+
console.log(' fileflows --dir ./src # Analyze src directory');
|
|
19
|
+
console.log(' fileflows --output flows.md # Custom output file');
|
|
20
|
+
}
|
|
21
|
+
async function main(argv = process.argv.slice(2)) {
|
|
22
|
+
let rootDir = localVars_1.DEFAULT_ROOT_DIR;
|
|
23
|
+
let outputFile = null;
|
|
24
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
25
|
+
const arg = argv[i];
|
|
26
|
+
if (arg === '--help' || arg === '-h') {
|
|
27
|
+
showUsage();
|
|
28
|
+
process.exitCode = 0;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (arg === '--dir') {
|
|
32
|
+
if (i + 1 < argv.length) {
|
|
33
|
+
rootDir = argv[++i];
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
console.error('Error: --dir requires a directory path');
|
|
37
|
+
process.exitCode = 1;
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (arg === '--output') {
|
|
43
|
+
if (i + 1 < argv.length) {
|
|
44
|
+
outputFile = argv[++i];
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
console.error('Error: --output requires a file path');
|
|
48
|
+
process.exitCode = 1;
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
console.error(`Error: Unknown argument '${arg}'`);
|
|
54
|
+
showUsage();
|
|
55
|
+
process.exitCode = 1;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
console.log('🚀 Generating FILE_FLOWS.md...');
|
|
60
|
+
const result = await (0, index_1.generateFileFlows)(rootDir, outputFile);
|
|
61
|
+
if (result.outputFile && result.jsonOutputFile) {
|
|
62
|
+
console.log(`🧾 Wrote ${result.outputFile} and ${result.jsonOutputFile}`);
|
|
63
|
+
}
|
|
64
|
+
console.log('🎉 Generation complete!');
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
console.error('❌ Error:', error.message);
|
|
68
|
+
process.exitCode = 1;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (typeof require !== 'undefined' && require.main === module) {
|
|
72
|
+
main().catch(error => {
|
|
73
|
+
console.error(error);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MAX_HTML_TAGS = exports.MAX_SHELL_COMMANDS = exports.MAX_YAML_KEYS = exports.MAX_JSON_KEYS = exports.IGNORE_PATTERNS = exports.DEFAULT_OUTPUT_FILE = exports.DEFAULT_ROOT_DIR = exports.ALL_EXTENSIONS = exports.CODE_EXTENSIONS = void 0;
|
|
4
|
+
exports.getDevDependencies = getDevDependencies;
|
|
5
|
+
exports.getEnvironmentConfig = getEnvironmentConfig;
|
|
6
|
+
exports.getDependencies = getDependencies;
|
|
7
|
+
/**
|
|
8
|
+
* Centralized configuration values and helpers for the FileFlows CLI
|
|
9
|
+
* Exports environment helpers, ignore lists, and other shared constants
|
|
10
|
+
*/
|
|
11
|
+
const qerrors_1 = require("qerrors");
|
|
12
|
+
exports.CODE_EXTENSIONS = ['js', 'ts', 'jsx', 'tsx'];
|
|
13
|
+
exports.ALL_EXTENSIONS = [
|
|
14
|
+
...exports.CODE_EXTENSIONS,
|
|
15
|
+
'json',
|
|
16
|
+
'md',
|
|
17
|
+
'yml',
|
|
18
|
+
'yaml',
|
|
19
|
+
'env',
|
|
20
|
+
'html',
|
|
21
|
+
'css',
|
|
22
|
+
'scss',
|
|
23
|
+
'sh',
|
|
24
|
+
'graphql'
|
|
25
|
+
];
|
|
26
|
+
exports.DEFAULT_ROOT_DIR = '.';
|
|
27
|
+
exports.DEFAULT_OUTPUT_FILE = 'FILE_FLOWS.md';
|
|
28
|
+
exports.IGNORE_PATTERNS = [
|
|
29
|
+
'**/node_modules/**',
|
|
30
|
+
'**/.config/**',
|
|
31
|
+
'**/logs/**',
|
|
32
|
+
'**/.git/**',
|
|
33
|
+
'**/tmp/**',
|
|
34
|
+
'**/temp/**',
|
|
35
|
+
'**/.npm/**',
|
|
36
|
+
'**/.cache/**',
|
|
37
|
+
'**/dist/**',
|
|
38
|
+
'**/build/**',
|
|
39
|
+
'**/.next/**',
|
|
40
|
+
'**/.nuxt/**'
|
|
41
|
+
];
|
|
42
|
+
exports.MAX_JSON_KEYS = 10;
|
|
43
|
+
exports.MAX_YAML_KEYS = 5;
|
|
44
|
+
exports.MAX_SHELL_COMMANDS = 5;
|
|
45
|
+
exports.MAX_HTML_TAGS = 5;
|
|
46
|
+
async function getDevDependencies() {
|
|
47
|
+
if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
|
|
48
|
+
try {
|
|
49
|
+
const qtests = await import('qtests/dist/index.js');
|
|
50
|
+
return { qtests };
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return { qtests: null };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return { qtests: null };
|
|
57
|
+
}
|
|
58
|
+
function getEnvironmentConfig() {
|
|
59
|
+
return {
|
|
60
|
+
NODE_ENV: (0, qerrors_1.getEnv)('NODE_ENV', 'development'),
|
|
61
|
+
DEBUG_TESTS: (0, qerrors_1.getEnv)('DEBUG_TESTS', 'false') === 'true'
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
async function getDependencies() {
|
|
65
|
+
const { generateFileFlows, groupByDataFlow, classifyFile, parseJSFile, parseOtherFile, extractDependencies, fileIO, graphUtils } = await import('../lib/index.js');
|
|
66
|
+
return {
|
|
67
|
+
generateFileFlows,
|
|
68
|
+
groupByDataFlow,
|
|
69
|
+
classifyFile,
|
|
70
|
+
parseJSFile,
|
|
71
|
+
parseOtherFile,
|
|
72
|
+
extractDependencies,
|
|
73
|
+
fileIO,
|
|
74
|
+
graphUtils
|
|
75
|
+
};
|
|
76
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateFileFlows = void 0;
|
|
4
|
+
var index_1 = require("./lib/index");
|
|
5
|
+
Object.defineProperty(exports, "generateFileFlows", { enumerable: true, get: function () { return index_1.generateFileFlows; } });
|
|
6
|
+
const index_2 = require("./lib/index");
|
|
7
|
+
exports.default = index_2.generateFileFlows;
|
|
@@ -0,0 +1,137 @@
|
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.groupByDataFlow = groupByDataFlow;
|
|
40
|
+
const fs_1 = require("fs");
|
|
41
|
+
const path_1 = __importDefault(require("path"));
|
|
42
|
+
const localVars = __importStar(require("../config/localVars"));
|
|
43
|
+
const jsParser_1 = require("./jsParser");
|
|
44
|
+
const otherFileParser_1 = require("./otherFileParser");
|
|
45
|
+
const fileClassifier_1 = require("./fileClassifier");
|
|
46
|
+
const dependencyExtractor_1 = require("./dependencyExtractor");
|
|
47
|
+
const graphUtils = __importStar(require("./graphUtils"));
|
|
48
|
+
async function groupByDataFlow(fileList, rootDir) {
|
|
49
|
+
const fileMetadata = new Map();
|
|
50
|
+
for (const filePath of fileList) {
|
|
51
|
+
const fullPath = path_1.default.join(rootDir, filePath);
|
|
52
|
+
const ext = path_1.default.extname(filePath).slice(1);
|
|
53
|
+
try {
|
|
54
|
+
const content = (0, fs_1.readFileSync)(fullPath, 'utf8');
|
|
55
|
+
const metadata = (localVars.CODE_EXTENSIONS.includes(ext)
|
|
56
|
+
? (0, jsParser_1.parseJSFile)(content, filePath)
|
|
57
|
+
: (0, otherFileParser_1.parseOtherFile)(content, filePath, ext));
|
|
58
|
+
const roleScore = graphUtils.calculateRoleScore(filePath);
|
|
59
|
+
const metadataImports = Array.isArray(metadata.Imports) ? metadata.Imports : [];
|
|
60
|
+
fileMetadata.set(filePath, {
|
|
61
|
+
...metadata,
|
|
62
|
+
type: (0, fileClassifier_1.classifyFile)(filePath, ext),
|
|
63
|
+
dependencies: (0, dependencyExtractor_1.extractDependencies)(metadataImports, filePath, fileList),
|
|
64
|
+
roleScore
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
fileMetadata.set(filePath, {
|
|
69
|
+
Imports: [],
|
|
70
|
+
Exports: [],
|
|
71
|
+
Functions: [],
|
|
72
|
+
Components: [],
|
|
73
|
+
ApiCalls: [],
|
|
74
|
+
ReduxActions: [],
|
|
75
|
+
Routes: [],
|
|
76
|
+
type: (0, fileClassifier_1.classifyFile)(filePath, ext),
|
|
77
|
+
dependencies: [],
|
|
78
|
+
roleScore: graphUtils.calculateRoleScore(filePath)
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const dependencyGraph = graphUtils.buildDependencyGraph(fileList, fileMetadata);
|
|
83
|
+
const connectedComponents = graphUtils.findConnectedComponents(dependencyGraph);
|
|
84
|
+
const primaryGroups = [];
|
|
85
|
+
const similarityCandidates = [];
|
|
86
|
+
for (const component of connectedComponents) {
|
|
87
|
+
if (component.length > 1) {
|
|
88
|
+
primaryGroups.push(component);
|
|
89
|
+
}
|
|
90
|
+
else if (component.length === 1) {
|
|
91
|
+
similarityCandidates.push(component[0]);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const groupedResults = [];
|
|
95
|
+
let groupCounter = 1;
|
|
96
|
+
for (const component of primaryGroups) {
|
|
97
|
+
const previousName = buildFlowGroupName(component, groupCounter++, graphUtils);
|
|
98
|
+
groupedResults.push({
|
|
99
|
+
name: previousName,
|
|
100
|
+
files: component,
|
|
101
|
+
metadata: component
|
|
102
|
+
.map(f => fileMetadata.get(f))
|
|
103
|
+
.filter((item) => Boolean(item))
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
if (similarityCandidates.length > 0) {
|
|
107
|
+
const similarityGroups = graphUtils.groupBySimilarity(similarityCandidates);
|
|
108
|
+
for (const [name, files] of similarityGroups) {
|
|
109
|
+
groupedResults.push({
|
|
110
|
+
name: `Similarity-${name}`,
|
|
111
|
+
files,
|
|
112
|
+
metadata: files
|
|
113
|
+
.map(f => fileMetadata.get(f))
|
|
114
|
+
.filter((item) => Boolean(item))
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const result = groupedResults;
|
|
119
|
+
result.fileMetadata = fileMetadata;
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
function buildFlowGroupName(component, counter, graph) {
|
|
123
|
+
const sorted = [...component].sort((a, b) => {
|
|
124
|
+
const scoreB = graph.calculateRoleScore(b);
|
|
125
|
+
const scoreA = graph.calculateRoleScore(a);
|
|
126
|
+
if (scoreB !== scoreA)
|
|
127
|
+
return scoreB - scoreA;
|
|
128
|
+
return a.localeCompare(b);
|
|
129
|
+
});
|
|
130
|
+
const primary = sorted[0];
|
|
131
|
+
const baseName = path_1.default
|
|
132
|
+
.basename(primary, path_1.default.extname(primary))
|
|
133
|
+
.replace(/[^a-zA-Z0-9]+/g, '-')
|
|
134
|
+
.replace(/^-|-$/g, '');
|
|
135
|
+
const normalized = baseName || 'flow';
|
|
136
|
+
return `${normalized}-${counter}`;
|
|
137
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.extractDependencies = extractDependencies;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
function extractDependencies(imports, currentFile, fileList) {
|
|
9
|
+
const dependencies = [];
|
|
10
|
+
for (const imp of imports) {
|
|
11
|
+
if (!imp.startsWith('.')) {
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
const basePath = path_1.default.dirname(currentFile);
|
|
15
|
+
let resolvedPath = path_1.default.normalize(path_1.default.join(basePath, imp));
|
|
16
|
+
resolvedPath = resolvedPath.replace(/\\/g, '/');
|
|
17
|
+
const pathParts = resolvedPath.split('/');
|
|
18
|
+
const possibleBasePaths = [
|
|
19
|
+
resolvedPath,
|
|
20
|
+
pathParts[0] === 'src' ? pathParts.slice(1).join('/') : null,
|
|
21
|
+
resolvedPath.replace(/^src\//, '')
|
|
22
|
+
].filter((value) => Boolean(value));
|
|
23
|
+
const allPossiblePaths = [resolvedPath, ...possibleBasePaths];
|
|
24
|
+
for (const basePathCandidate of allPossiblePaths) {
|
|
25
|
+
const possiblePaths = [
|
|
26
|
+
basePathCandidate,
|
|
27
|
+
`${basePathCandidate}.js`,
|
|
28
|
+
`${basePathCandidate}.ts`,
|
|
29
|
+
`${basePathCandidate}.jsx`,
|
|
30
|
+
`${basePathCandidate}.tsx`,
|
|
31
|
+
`${basePathCandidate}/index.js`,
|
|
32
|
+
`${basePathCandidate}/index.ts`
|
|
33
|
+
];
|
|
34
|
+
for (const possiblePath of possiblePaths) {
|
|
35
|
+
if (fileList.includes(possiblePath)) {
|
|
36
|
+
dependencies.push(possiblePath);
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (dependencies.some(dep => allPossiblePaths.some(base => dep.startsWith(base)))) {
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return dependencies;
|
|
46
|
+
}
|