project-graph-mcp 1.5.0 → 2.1.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.
- package/README.md +171 -31
- package/docs/img/explorer-compact.jpg +0 -0
- package/docs/img/explorer-expanded.jpg +0 -0
- package/package.json +12 -8
- package/src/.project-graph-cache.json +1 -1
- package/src/analysis/analysis-cache.js +7 -0
- package/src/analysis/complexity.js +14 -0
- package/src/analysis/custom-rules.js +36 -0
- package/src/analysis/db-analysis.js +9 -0
- package/src/analysis/dead-code.js +19 -0
- package/src/analysis/full-analysis.js +18 -0
- package/src/analysis/jsdoc-checker.js +24 -0
- package/src/analysis/jsdoc-generator.js +10 -0
- package/src/analysis/large-files.js +11 -0
- package/src/analysis/outdated-patterns.js +12 -0
- package/src/analysis/similar-functions.js +16 -0
- package/src/analysis/test-annotations.js +21 -0
- package/src/analysis/type-checker.js +8 -0
- package/src/analysis/undocumented.js +14 -0
- package/src/cli/cli-handlers.js +4 -0
- package/src/cli/cli.js +5 -0
- package/src/compact/.project-graph-cache.json +1 -0
- package/src/compact/ai-context.js +7 -0
- package/src/compact/compact-migrate.js +17 -0
- package/src/compact/compact.js +18 -0
- package/src/compact/compress.js +14 -0
- package/src/compact/ctx-to-jsdoc.js +29 -0
- package/src/compact/doc-dialect.js +30 -0
- package/src/compact/expand.js +37 -0
- package/src/compact/framework-references.js +5 -0
- package/src/compact/instructions.js +3 -0
- package/src/compact/mode-config.js +8 -0
- package/src/compact/validate-pipeline.js +9 -0
- package/src/core/event-bus.js +9 -0
- package/src/core/filters.js +14 -0
- package/src/core/graph-builder.js +12 -0
- package/src/core/parser.js +31 -0
- package/src/core/workspace.js +8 -0
- package/src/lang/lang-go.js +17 -0
- package/src/lang/lang-python.js +12 -0
- package/src/lang/lang-sql.js +23 -0
- package/src/lang/lang-typescript.js +9 -0
- package/src/lang/lang-utils.js +4 -0
- package/src/mcp/mcp-server.js +17 -0
- package/src/mcp/tool-defs.js +3 -0
- package/src/mcp/tools.js +25 -0
- package/src/network/backend-lifecycle.js +19 -0
- package/src/network/backend.js +5 -0
- package/src/network/local-gateway.js +23 -0
- package/src/network/mdns.js +13 -0
- package/src/network/server.js +10 -0
- package/src/network/web-server.js +34 -0
- package/web/.project-graph-cache.json +1 -0
- package/web/app.js +17 -0
- package/web/components/code-block.js +3 -0
- package/web/components/quick-open.js +5 -0
- package/web/dashboard-state.js +3 -0
- package/web/dashboard.html +27 -0
- package/web/dashboard.js +8 -0
- package/web/highlight.js +13 -0
- package/web/index.html +35 -0
- package/web/panels/ActionBoard/ActionBoard.css.js +1 -0
- package/web/panels/ActionBoard/ActionBoard.js +4 -0
- package/web/panels/ActionBoard/ActionBoard.tpl.js +1 -0
- package/web/panels/EventItem/EventItem.css.js +1 -0
- package/web/panels/EventItem/EventItem.js +4 -0
- package/web/panels/EventItem/EventItem.tpl.js +1 -0
- package/web/panels/ProjectItem/ProjectItem.css.js +1 -0
- package/web/panels/ProjectItem/ProjectItem.js +5 -0
- package/web/panels/ProjectItem/ProjectItem.tpl.js +1 -0
- package/web/panels/ProjectList/ProjectList.css.js +1 -0
- package/web/panels/ProjectList/ProjectList.js +4 -0
- package/web/panels/ProjectList/ProjectList.tpl.js +1 -0
- package/web/panels/SettingsPanel/.project-graph-cache.json +1 -0
- package/web/panels/SettingsPanel/SettingsPanel.css.js +1 -0
- package/web/panels/SettingsPanel/SettingsPanel.js +7 -0
- package/web/panels/SettingsPanel/SettingsPanel.tpl.js +1 -0
- package/web/panels/code-viewer.js +5 -0
- package/web/panels/ctx-panel.js +4 -0
- package/web/panels/dep-graph.js +6 -0
- package/web/panels/file-tree.js +188 -0
- package/web/panels/health-panel.js +3 -0
- package/web/panels/live-monitor.js +3 -0
- package/web/state.js +17 -0
- package/web/style.css +157 -0
- package/references/symbiote-3x.md +0 -834
- package/src/ai-context.js +0 -113
- package/src/analysis-cache.js +0 -155
- package/src/cli-handlers.js +0 -271
- package/src/cli.js +0 -95
- package/src/compact.js +0 -207
- package/src/complexity.js +0 -237
- package/src/compress.js +0 -319
- package/src/ctx-to-jsdoc.js +0 -514
- package/src/custom-rules.js +0 -584
- package/src/db-analysis.js +0 -194
- package/src/dead-code.js +0 -468
- package/src/doc-dialect.js +0 -716
- package/src/filters.js +0 -227
- package/src/framework-references.js +0 -177
- package/src/full-analysis.js +0 -470
- package/src/graph-builder.js +0 -299
- package/src/instructions.js +0 -73
- package/src/jsdoc-checker.js +0 -351
- package/src/jsdoc-generator.js +0 -203
- package/src/lang-go.js +0 -285
- package/src/lang-python.js +0 -197
- package/src/lang-sql.js +0 -309
- package/src/lang-typescript.js +0 -190
- package/src/lang-utils.js +0 -124
- package/src/large-files.js +0 -163
- package/src/mcp-server.js +0 -675
- package/src/mode-config.js +0 -127
- package/src/outdated-patterns.js +0 -296
- package/src/parser.js +0 -662
- package/src/server.js +0 -28
- package/src/similar-functions.js +0 -279
- package/src/test-annotations.js +0 -323
- package/src/tool-defs.js +0 -793
- package/src/tools.js +0 -470
- package/src/type-checker.js +0 -188
- package/src/undocumented.js +0 -259
- package/src/workspace.js +0 -70
- /package/{AGENT_ROLE.md → docs/examples/AGENT_ROLE.md} +0 -0
- /package/{AGENT_ROLE_MINIMAL.md → docs/examples/AGENT_ROLE_MINIMAL.md} +0 -0
package/src/mode-config.js
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Compact Code Mode Configuration
|
|
3
|
-
*
|
|
4
|
-
* Manages project-level mode selection for the compact code architecture.
|
|
5
|
-
* Reads/writes `.context/config.json` to configure how agents interact with code.
|
|
6
|
-
*
|
|
7
|
-
* Modes:
|
|
8
|
-
* 1 = Native Compact: code stored minified, agent edits directly
|
|
9
|
-
* 2 = Full Storage: code stored formatted, agent reads compressed view, edits via edit_compressed
|
|
10
|
-
* 3 = Future (IDE): compact storage with IDE virtual display (reserved)
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
14
|
-
import { join, dirname } from 'path';
|
|
15
|
-
|
|
16
|
-
const CONFIG_FILE = '.context/config.json';
|
|
17
|
-
|
|
18
|
-
/** Default configuration */
|
|
19
|
-
const DEFAULTS = {
|
|
20
|
-
mode: 2,
|
|
21
|
-
beautify: true,
|
|
22
|
-
autoValidate: false,
|
|
23
|
-
stripJSDoc: false,
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Read project mode configuration from .context/config.json
|
|
28
|
-
* Returns defaults if file doesn't exist.
|
|
29
|
-
*
|
|
30
|
-
* @param {string} projectDir - Project root directory
|
|
31
|
-
* @returns {{ mode: number, beautify: boolean, autoValidate: boolean, stripJSDoc: boolean }}
|
|
32
|
-
*/
|
|
33
|
-
export function getConfig(projectDir) {
|
|
34
|
-
const configPath = join(projectDir, CONFIG_FILE);
|
|
35
|
-
|
|
36
|
-
if (!existsSync(configPath)) {
|
|
37
|
-
return { ...DEFAULTS };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
try {
|
|
41
|
-
const raw = readFileSync(configPath, 'utf-8');
|
|
42
|
-
const parsed = JSON.parse(raw);
|
|
43
|
-
return { ...DEFAULTS, ...parsed };
|
|
44
|
-
} catch {
|
|
45
|
-
return { ...DEFAULTS };
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Write project mode configuration to .context/config.json
|
|
51
|
-
*
|
|
52
|
-
* @param {string} projectDir - Project root directory
|
|
53
|
-
* @param {Object} config - Configuration to save (merged with existing)
|
|
54
|
-
* @returns {{ saved: boolean, path: string, config: Object }}
|
|
55
|
-
*/
|
|
56
|
-
export function setConfig(projectDir, config) {
|
|
57
|
-
const configPath = join(projectDir, CONFIG_FILE);
|
|
58
|
-
const dir = dirname(configPath);
|
|
59
|
-
|
|
60
|
-
if (!existsSync(dir)) {
|
|
61
|
-
mkdirSync(dir, { recursive: true });
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Merge with existing
|
|
65
|
-
const existing = getConfig(projectDir);
|
|
66
|
-
const merged = { ...existing, ...config };
|
|
67
|
-
|
|
68
|
-
// Validate mode
|
|
69
|
-
if (![1, 2, 3].includes(merged.mode)) {
|
|
70
|
-
throw new Error(`Invalid mode: ${merged.mode}. Valid: 1 (compact), 2 (full), 3 (IDE)`);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
writeFileSync(configPath, JSON.stringify(merged, null, 2) + '\n', 'utf-8');
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
saved: true,
|
|
77
|
-
path: configPath,
|
|
78
|
-
config: merged,
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Get human-readable description of current mode
|
|
84
|
-
* @param {number} mode
|
|
85
|
-
* @returns {string}
|
|
86
|
-
*/
|
|
87
|
-
export function getModeDescription(mode) {
|
|
88
|
-
switch (mode) {
|
|
89
|
-
case 1: return 'Native Compact — code stored minified, agent edits directly';
|
|
90
|
-
case 2: return 'Full Storage — code stored formatted, agent uses get_compressed_file + edit_compressed';
|
|
91
|
-
case 3: return 'IDE Virtual — compact storage with IDE virtual display (future)';
|
|
92
|
-
default: return `Unknown mode: ${mode}`;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Get recommended workflow for current mode
|
|
98
|
-
* @param {number} mode
|
|
99
|
-
* @returns {{ read: string, edit: string, docs: string, validate: string }}
|
|
100
|
-
*/
|
|
101
|
-
export function getModeWorkflow(mode) {
|
|
102
|
-
switch (mode) {
|
|
103
|
-
case 1:
|
|
104
|
-
return {
|
|
105
|
-
read: 'Read .js files directly (already compact)',
|
|
106
|
-
edit: 'Edit .js files directly',
|
|
107
|
-
docs: 'Read .ctx files for types and descriptions',
|
|
108
|
-
validate: 'Run validate-ctx to check .ctx ↔ AST consistency',
|
|
109
|
-
};
|
|
110
|
-
case 2:
|
|
111
|
-
return {
|
|
112
|
-
read: 'Use get_compressed_file for token-efficient reading',
|
|
113
|
-
edit: 'Use edit_compressed(path, symbol, code) for AST-safe editing',
|
|
114
|
-
docs: 'Read .ctx files for types and descriptions',
|
|
115
|
-
validate: 'Run validate-ctx to check .ctx ↔ AST consistency',
|
|
116
|
-
};
|
|
117
|
-
case 3:
|
|
118
|
-
return {
|
|
119
|
-
read: 'IDE renders full view from compact storage',
|
|
120
|
-
edit: 'IDE handles bidirectional mapping',
|
|
121
|
-
docs: 'Managed by IDE plugin',
|
|
122
|
-
validate: 'Automatic via IDE integration',
|
|
123
|
-
};
|
|
124
|
-
default:
|
|
125
|
-
return { read: 'N/A', edit: 'N/A', docs: 'N/A', validate: 'N/A' };
|
|
126
|
-
}
|
|
127
|
-
}
|
package/src/outdated-patterns.js
DELETED
|
@@ -1,296 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Outdated Patterns Detector
|
|
3
|
-
* Finds legacy code patterns and redundant npm dependencies
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { readFileSync, readdirSync, statSync, existsSync } from 'fs';
|
|
7
|
-
import { join, relative, resolve } from 'path';
|
|
8
|
-
import { parse } from '../vendor/acorn.mjs';
|
|
9
|
-
import * as walk from '../vendor/walk.mjs';
|
|
10
|
-
import { shouldExcludeDir, shouldExcludeFile, parseGitignore } from './filters.js';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Redundant npm packages that are now built into Node.js 18+
|
|
14
|
-
*/
|
|
15
|
-
const REDUNDANT_DEPS = {
|
|
16
|
-
'node-fetch': { replacement: 'fetch()', since: 'Node 18' },
|
|
17
|
-
'cross-fetch': { replacement: 'fetch()', since: 'Node 18' },
|
|
18
|
-
'isomorphic-fetch': { replacement: 'fetch()', since: 'Node 18' },
|
|
19
|
-
'uuid': { replacement: 'crypto.randomUUID()', since: 'Node 19' },
|
|
20
|
-
'deep-clone': { replacement: 'structuredClone()', since: 'Node 17' },
|
|
21
|
-
'lodash.clonedeep': { replacement: 'structuredClone()', since: 'Node 17' },
|
|
22
|
-
'abort-controller': { replacement: 'AbortController (global)', since: 'Node 15' },
|
|
23
|
-
'form-data': { replacement: 'FormData (global)', since: 'Node 18' },
|
|
24
|
-
'web-streams-polyfill': { replacement: 'ReadableStream (global)', since: 'Node 18' },
|
|
25
|
-
'url-parse': { replacement: 'URL (global)', since: 'Node 10' },
|
|
26
|
-
'querystring': { replacement: 'URLSearchParams', since: 'Node 10' },
|
|
27
|
-
'rimraf': { replacement: 'fs.rm({ recursive: true })', since: 'Node 14' },
|
|
28
|
-
'mkdirp': { replacement: 'fs.mkdir({ recursive: true })', since: 'Node 10' },
|
|
29
|
-
'recursive-readdir': { replacement: 'fs.readdir({ recursive: true })', since: 'Node 20' },
|
|
30
|
-
'glob': { replacement: 'fs.glob()', since: 'Node 22' },
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Legacy code patterns to detect
|
|
35
|
-
*/
|
|
36
|
-
const CODE_PATTERNS = [
|
|
37
|
-
{
|
|
38
|
-
name: 'var-usage',
|
|
39
|
-
description: 'Use const/let instead of var',
|
|
40
|
-
check: (node) => node.type === 'VariableDeclaration' && node.kind === 'var',
|
|
41
|
-
severity: 'warning',
|
|
42
|
-
replacement: 'const/let',
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
name: 'require-usage',
|
|
46
|
-
description: 'Use ESM import instead of require()',
|
|
47
|
-
check: (node) => node.type === 'CallExpression' &&
|
|
48
|
-
node.callee.type === 'Identifier' && node.callee.name === 'require',
|
|
49
|
-
severity: 'info',
|
|
50
|
-
replacement: 'import ... from',
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
name: 'module-exports',
|
|
54
|
-
description: 'Use ESM export instead of module.exports',
|
|
55
|
-
check: (node) => node.type === 'AssignmentExpression' &&
|
|
56
|
-
node.left.type === 'MemberExpression' &&
|
|
57
|
-
node.left.object.type === 'Identifier' && node.left.object.name === 'module' &&
|
|
58
|
-
node.left.property.type === 'Identifier' && node.left.property.name === 'exports',
|
|
59
|
-
severity: 'info',
|
|
60
|
-
replacement: 'export default/export',
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
name: 'buffer-constructor',
|
|
64
|
-
description: 'new Buffer() is deprecated',
|
|
65
|
-
check: (node) => node.type === 'NewExpression' &&
|
|
66
|
-
node.callee.type === 'Identifier' && node.callee.name === 'Buffer',
|
|
67
|
-
severity: 'error',
|
|
68
|
-
replacement: 'Buffer.from() / Buffer.alloc()',
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
name: 'arguments-usage',
|
|
72
|
-
description: 'Use rest parameters instead of arguments',
|
|
73
|
-
check: (node) => node.type === 'Identifier' && node.name === 'arguments',
|
|
74
|
-
severity: 'warning',
|
|
75
|
-
replacement: '...args',
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
name: 'promisify-usage',
|
|
79
|
-
description: 'Use fs/promises instead of util.promisify',
|
|
80
|
-
check: (node) => node.type === 'CallExpression' &&
|
|
81
|
-
node.callee.type === 'MemberExpression' &&
|
|
82
|
-
node.callee.object.type === 'Identifier' && node.callee.object.name === 'util' &&
|
|
83
|
-
node.callee.property.type === 'Identifier' && node.callee.property.name === 'promisify',
|
|
84
|
-
severity: 'info',
|
|
85
|
-
replacement: 'fs/promises module',
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
name: 'sync-in-async',
|
|
89
|
-
description: 'Avoid sync methods in async context (readFileSync, etc.)',
|
|
90
|
-
check: (node, context) => {
|
|
91
|
-
if (node.type !== 'CallExpression') return false;
|
|
92
|
-
const callee = node.callee;
|
|
93
|
-
if (callee.type === 'MemberExpression' && callee.property.type === 'Identifier') {
|
|
94
|
-
const name = callee.property.name;
|
|
95
|
-
return name.endsWith('Sync') && context.inAsync;
|
|
96
|
-
}
|
|
97
|
-
return false;
|
|
98
|
-
},
|
|
99
|
-
severity: 'warning',
|
|
100
|
-
replacement: 'async fs/promises methods',
|
|
101
|
-
},
|
|
102
|
-
];
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* @typedef {Object} PatternMatch
|
|
106
|
-
* @property {string} pattern
|
|
107
|
-
* @property {string} description
|
|
108
|
-
* @property {string} file
|
|
109
|
-
* @property {number} line
|
|
110
|
-
* @property {string} severity
|
|
111
|
-
* @property {string} replacement
|
|
112
|
-
*/
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* @typedef {Object} RedundantDep
|
|
116
|
-
* @property {string} name
|
|
117
|
-
* @property {string} replacement
|
|
118
|
-
* @property {string} since
|
|
119
|
-
*/
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Find all JS files
|
|
123
|
-
* @param {string} dir
|
|
124
|
-
* @param {string} rootDir
|
|
125
|
-
* @returns {string[]}
|
|
126
|
-
*/
|
|
127
|
-
function findJSFiles(dir, rootDir = dir) {
|
|
128
|
-
if (dir === rootDir) parseGitignore(rootDir);
|
|
129
|
-
const files = [];
|
|
130
|
-
|
|
131
|
-
try {
|
|
132
|
-
for (const entry of readdirSync(dir)) {
|
|
133
|
-
const fullPath = join(dir, entry);
|
|
134
|
-
const relativePath = relative(rootDir, fullPath);
|
|
135
|
-
const stat = statSync(fullPath);
|
|
136
|
-
|
|
137
|
-
if (stat.isDirectory()) {
|
|
138
|
-
if (!shouldExcludeDir(entry, relativePath)) {
|
|
139
|
-
files.push(...findJSFiles(fullPath, rootDir));
|
|
140
|
-
}
|
|
141
|
-
} else if (entry.endsWith('.js') && !entry.endsWith('.css.js') && !entry.endsWith('.tpl.js')) {
|
|
142
|
-
if (!shouldExcludeFile(entry, relativePath)) {
|
|
143
|
-
files.push(fullPath);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
} catch (e) { }
|
|
148
|
-
|
|
149
|
-
return files;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Analyze file for outdated patterns
|
|
154
|
-
* @param {string} filePath
|
|
155
|
-
* @param {string} rootDir - Root directory for relative path calculation
|
|
156
|
-
* @returns {PatternMatch[]}
|
|
157
|
-
*/
|
|
158
|
-
function analyzeFilePatterns(filePath, rootDir) {
|
|
159
|
-
const code = readFileSync(filePath, 'utf-8');
|
|
160
|
-
const relPath = relative(rootDir, filePath);
|
|
161
|
-
const matches = [];
|
|
162
|
-
|
|
163
|
-
let ast;
|
|
164
|
-
try {
|
|
165
|
-
ast = parse(code, { ecmaVersion: 'latest', sourceType: 'module', locations: true });
|
|
166
|
-
} catch (e) {
|
|
167
|
-
return matches;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Track async context
|
|
171
|
-
const context = { inAsync: false };
|
|
172
|
-
|
|
173
|
-
walk.simple(ast, {
|
|
174
|
-
FunctionDeclaration(node) {
|
|
175
|
-
context.inAsync = node.async;
|
|
176
|
-
},
|
|
177
|
-
ArrowFunctionExpression(node) {
|
|
178
|
-
context.inAsync = node.async;
|
|
179
|
-
},
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
// Reset and check patterns
|
|
183
|
-
context.inAsync = false;
|
|
184
|
-
|
|
185
|
-
walk.ancestor(ast, {
|
|
186
|
-
'*'(node, ancestors) {
|
|
187
|
-
// Update async context
|
|
188
|
-
for (const anc of ancestors) {
|
|
189
|
-
if ((anc.type === 'FunctionDeclaration' || anc.type === 'ArrowFunctionExpression' ||
|
|
190
|
-
anc.type === 'FunctionExpression') && anc.async) {
|
|
191
|
-
context.inAsync = true;
|
|
192
|
-
break;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
for (const pattern of CODE_PATTERNS) {
|
|
197
|
-
if (pattern.check(node, context)) {
|
|
198
|
-
matches.push({
|
|
199
|
-
pattern: pattern.name,
|
|
200
|
-
description: pattern.description,
|
|
201
|
-
file: relPath,
|
|
202
|
-
line: node.loc?.start?.line || 0,
|
|
203
|
-
severity: pattern.severity,
|
|
204
|
-
replacement: pattern.replacement,
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
},
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
return matches;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Analyze package.json for redundant dependencies
|
|
216
|
-
* @param {string} dir
|
|
217
|
-
* @returns {RedundantDep[]}
|
|
218
|
-
*/
|
|
219
|
-
function analyzePackageJson(dir) {
|
|
220
|
-
const pkgPath = join(dir, 'package.json');
|
|
221
|
-
const redundant = [];
|
|
222
|
-
|
|
223
|
-
if (!existsSync(pkgPath)) return redundant;
|
|
224
|
-
|
|
225
|
-
try {
|
|
226
|
-
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
227
|
-
const allDeps = {
|
|
228
|
-
...pkg.dependencies,
|
|
229
|
-
...pkg.devDependencies,
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
for (const depName of Object.keys(allDeps)) {
|
|
233
|
-
if (REDUNDANT_DEPS[depName]) {
|
|
234
|
-
redundant.push({
|
|
235
|
-
name: depName,
|
|
236
|
-
...REDUNDANT_DEPS[depName],
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
} catch (e) { }
|
|
241
|
-
|
|
242
|
-
return redundant;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Get outdated patterns analysis
|
|
247
|
-
* @param {string} dir
|
|
248
|
-
* @param {Object} [options]
|
|
249
|
-
* @param {boolean} [options.codeOnly=false] - Only check code patterns
|
|
250
|
-
* @param {boolean} [options.depsOnly=false] - Only check dependencies
|
|
251
|
-
* @returns {Promise<{codePatterns: PatternMatch[], redundantDeps: RedundantDep[], stats: Object}>}
|
|
252
|
-
*/
|
|
253
|
-
export async function getOutdatedPatterns(dir, options = {}) {
|
|
254
|
-
const codeOnly = options.codeOnly || false;
|
|
255
|
-
const depsOnly = options.depsOnly || false;
|
|
256
|
-
const resolvedDir = resolve(dir);
|
|
257
|
-
|
|
258
|
-
let codePatterns = [];
|
|
259
|
-
let redundantDeps = [];
|
|
260
|
-
|
|
261
|
-
if (!depsOnly) {
|
|
262
|
-
const files = findJSFiles(dir);
|
|
263
|
-
for (const file of files) {
|
|
264
|
-
codePatterns.push(...analyzeFilePatterns(file, resolvedDir));
|
|
265
|
-
}
|
|
266
|
-
// Sort by severity
|
|
267
|
-
const severityOrder = { error: 0, warning: 1, info: 2 };
|
|
268
|
-
codePatterns.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
if (!codeOnly) {
|
|
272
|
-
redundantDeps = analyzePackageJson(dir);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const stats = {
|
|
276
|
-
totalPatterns: codePatterns.length,
|
|
277
|
-
byPattern: {},
|
|
278
|
-
bySeverity: {
|
|
279
|
-
error: codePatterns.filter(p => p.severity === 'error').length,
|
|
280
|
-
warning: codePatterns.filter(p => p.severity === 'warning').length,
|
|
281
|
-
info: codePatterns.filter(p => p.severity === 'info').length,
|
|
282
|
-
},
|
|
283
|
-
redundantDeps: redundantDeps.length,
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
// Group by pattern name
|
|
287
|
-
for (const p of codePatterns) {
|
|
288
|
-
stats.byPattern[p.pattern] = (stats.byPattern[p.pattern] || 0) + 1;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
return {
|
|
292
|
-
codePatterns: codePatterns.slice(0, 50),
|
|
293
|
-
redundantDeps,
|
|
294
|
-
stats,
|
|
295
|
-
};
|
|
296
|
-
}
|