pumuki-ast-hooks 5.5.46 → 5.5.48
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/docs/CODE_STANDARDS.md +5 -0
- package/docs/VIOLATIONS_RESOLUTION_PLAN.md +22 -34
- package/package.json +2 -2
- package/scripts/hooks-system/.audit_tmp/hook-metrics.jsonl +36 -0
- package/scripts/hooks-system/application/services/installation/FileSystemInstallerService.js +1 -1
- package/scripts/hooks-system/application/services/installation/VSCodeTaskConfigurator.js +8 -2
- package/scripts/hooks-system/bin/gitflow-cycle.js +0 -0
- package/scripts/hooks-system/config/project.config.json +1 -1
- package/scripts/hooks-system/infrastructure/ast/android/analyzers/AndroidSOLIDAnalyzer.js +11 -255
- package/scripts/hooks-system/infrastructure/ast/android/detectors/android-solid-detectors.js +227 -0
- package/scripts/hooks-system/infrastructure/ast/ast-core.js +12 -3
- package/scripts/hooks-system/infrastructure/ast/ast-intelligence.js +36 -13
- package/scripts/hooks-system/infrastructure/ast/backend/ast-backend.js +10 -83
- package/scripts/hooks-system/infrastructure/ast/backend/detectors/god-class-detector.js +83 -0
- package/scripts/hooks-system/infrastructure/ast/common/ast-common.js +17 -2
- package/scripts/hooks-system/infrastructure/ast/frontend/analyzers/FrontendArchitectureDetector.js +12 -142
- package/scripts/hooks-system/infrastructure/ast/frontend/detectors/frontend-architecture-strategies.js +126 -0
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSASTIntelligentAnalyzer.js +30 -783
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSArchitectureDetector.js +21 -224
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSArchitectureRules.js +18 -605
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSModernPracticesRules.js +4 -1
- package/scripts/hooks-system/infrastructure/ast/ios/ast-ios.js +4 -1
- package/scripts/hooks-system/infrastructure/ast/ios/detectors/ios-architecture-rules-strategies.js +595 -0
- package/scripts/hooks-system/infrastructure/ast/ios/detectors/ios-architecture-strategies.js +192 -0
- package/scripts/hooks-system/infrastructure/ast/ios/detectors/ios-ast-intelligent-strategies.js +789 -0
- package/scripts/hooks-system/infrastructure/ast/ios/detectors/ios-god-class-detector.js +79 -0
- package/scripts/hooks-system/infrastructure/ast/ios/native-bridge.js +4 -1
- package/skills/android-guidelines/SKILL.md +1 -0
- package/skills/backend-guidelines/SKILL.md +1 -0
- package/skills/frontend-guidelines/SKILL.md +1 -0
- package/skills/ios-guidelines/SKILL.md +1 -0
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const glob = require('glob');
|
|
4
|
+
const {
|
|
5
|
+
detectFeatureFirstClean,
|
|
6
|
+
detectTCA,
|
|
7
|
+
detectVIPER,
|
|
8
|
+
detectCleanSwift,
|
|
9
|
+
detectMVP,
|
|
10
|
+
detectMVVMC,
|
|
11
|
+
detectMVVM,
|
|
12
|
+
detectMVC,
|
|
13
|
+
} = require('../detectors/ios-architecture-strategies');
|
|
4
14
|
|
|
5
15
|
class iOSArchitectureDetector {
|
|
6
16
|
constructor(projectRoot) {
|
|
7
17
|
this.projectRoot = projectRoot;
|
|
8
18
|
this.patterns = {
|
|
9
|
-
featureFirstClean: 0,
|
|
19
|
+
featureFirstClean: 0,
|
|
10
20
|
mvvm: 0,
|
|
11
21
|
mvvmc: 0,
|
|
12
22
|
mvp: 0,
|
|
@@ -62,249 +72,36 @@ class iOSArchitectureDetector {
|
|
|
62
72
|
}
|
|
63
73
|
|
|
64
74
|
detectFeatureFirstClean(files) {
|
|
65
|
-
|
|
66
|
-
/\/Features?\/\w+\/(domain|application|infrastructure|presentation)\//.test(f)
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
const cleanArchFolders = ['domain', 'application', 'infrastructure', 'presentation'];
|
|
70
|
-
const foundCleanFolders = cleanArchFolders.filter(folder => {
|
|
71
|
-
return files.some(f => f.includes(`/${folder}/`));
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
const dddConcepts = files.filter(f =>
|
|
75
|
-
f.includes('/entities/') ||
|
|
76
|
-
f.includes('/value-objects/') ||
|
|
77
|
-
f.includes('/use-cases/') ||
|
|
78
|
-
f.includes('Entity.swift') ||
|
|
79
|
-
f.includes('VO.swift') ||
|
|
80
|
-
f.includes('UseCase.swift')
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
if (hasFeaturesFolders) {
|
|
84
|
-
this.patterns.featureFirstClean += 10;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (foundCleanFolders.length >= 3) {
|
|
88
|
-
this.patterns.featureFirstClean += foundCleanFolders.length * 3;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (dddConcepts.length > 0) {
|
|
92
|
-
this.patterns.featureFirstClean += dddConcepts.length * 2;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const featureNames = new Set();
|
|
96
|
-
files.forEach(f => {
|
|
97
|
-
const match = f.match(/\/Features?\/(\w+)\//);
|
|
98
|
-
if (match) {
|
|
99
|
-
featureNames.add(match[1]);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
if (featureNames.size >= 2) {
|
|
104
|
-
this.patterns.featureFirstClean += featureNames.size * 4;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
files.forEach(file => {
|
|
108
|
-
const content = this.readFile(file);
|
|
109
|
-
|
|
110
|
-
if (content.includes('struct ') && content.includes('VO')) {
|
|
111
|
-
this.patterns.featureFirstClean += 2;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (file.includes('Entity.swift') && content.includes('func ')) {
|
|
115
|
-
this.patterns.featureFirstClean += 2;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (file.includes('/domain/') && content.includes('protocol ') && content.includes('Repository')) {
|
|
119
|
-
this.patterns.featureFirstClean += 3;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (file.includes('/application/') && content.includes('UseCase')) {
|
|
123
|
-
this.patterns.featureFirstClean += 2;
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
|
|
75
|
+
detectFeatureFirstClean(files, this.patterns, this.projectRoot);
|
|
127
76
|
console.log(`[Architecture Detection] Feature-First + Clean + DDD score: ${this.patterns.featureFirstClean}`);
|
|
128
77
|
}
|
|
129
78
|
|
|
130
79
|
detectTCA(files) {
|
|
131
|
-
|
|
132
|
-
'import ComposableArchitecture',
|
|
133
|
-
'Store<',
|
|
134
|
-
'struct.*State',
|
|
135
|
-
'enum.*Action',
|
|
136
|
-
': Reducer',
|
|
137
|
-
'Effect<'
|
|
138
|
-
];
|
|
139
|
-
|
|
140
|
-
files.forEach(file => {
|
|
141
|
-
const content = this.readFile(file);
|
|
142
|
-
const matches = tcaIndicators.filter(indicator =>
|
|
143
|
-
new RegExp(indicator).test(content)
|
|
144
|
-
).length;
|
|
145
|
-
|
|
146
|
-
if (matches >= 3) {
|
|
147
|
-
this.patterns.tca += matches;
|
|
148
|
-
}
|
|
149
|
-
});
|
|
80
|
+
detectTCA(files, this.patterns);
|
|
150
81
|
}
|
|
151
82
|
|
|
152
83
|
detectVIPER(files) {
|
|
153
|
-
|
|
154
|
-
/Presenter\.swift$|Interactor\.swift$|Router\.swift$|Entity\.swift$/.test(f)
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
if (viperFiles.length >= 4) {
|
|
158
|
-
this.patterns.viper += viperFiles.length;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
files.forEach(file => {
|
|
162
|
-
const content = this.readFile(file);
|
|
163
|
-
const viperProtocols = [
|
|
164
|
-
'ViewProtocol',
|
|
165
|
-
'PresenterProtocol',
|
|
166
|
-
'InteractorProtocol',
|
|
167
|
-
'RouterProtocol'
|
|
168
|
-
];
|
|
169
|
-
|
|
170
|
-
const matches = viperProtocols.filter(proto =>
|
|
171
|
-
content.includes(proto)
|
|
172
|
-
).length;
|
|
173
|
-
|
|
174
|
-
if (matches >= 2) {
|
|
175
|
-
this.patterns.viper += matches * 2;
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
const viperFolders = ['View', 'Interactor', 'Presenter', 'Entity', 'Router'];
|
|
180
|
-
const hasViperStructure = viperFolders.filter(folder => {
|
|
181
|
-
const folderPath = path.join(this.projectRoot, folder);
|
|
182
|
-
return fs.existsSync(folderPath);
|
|
183
|
-
}).length;
|
|
184
|
-
|
|
185
|
-
if (hasViperStructure >= 3) {
|
|
186
|
-
this.patterns.viper += hasViperStructure * 3;
|
|
187
|
-
}
|
|
84
|
+
detectVIPER(files, this.patterns, this.projectRoot);
|
|
188
85
|
}
|
|
189
86
|
|
|
190
87
|
detectCleanSwift(files) {
|
|
191
|
-
|
|
192
|
-
/Models\.swift$/.test(f)
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
files.forEach(file => {
|
|
196
|
-
const content = this.readFile(file);
|
|
197
|
-
const cleanSwiftIndicators = [
|
|
198
|
-
'DisplayLogic',
|
|
199
|
-
'BusinessLogic',
|
|
200
|
-
'PresentationLogic',
|
|
201
|
-
'Request\\s*{',
|
|
202
|
-
'Response\\s*{',
|
|
203
|
-
'ViewModel\\s*{'
|
|
204
|
-
];
|
|
205
|
-
|
|
206
|
-
const matches = cleanSwiftIndicators.filter(indicator =>
|
|
207
|
-
new RegExp(indicator).test(content)
|
|
208
|
-
).length;
|
|
209
|
-
|
|
210
|
-
if (matches >= 3) {
|
|
211
|
-
this.patterns.cleanSwift += matches * 2;
|
|
212
|
-
}
|
|
213
|
-
});
|
|
88
|
+
detectCleanSwift(files, this.patterns);
|
|
214
89
|
}
|
|
215
90
|
|
|
216
91
|
detectMVP(files) {
|
|
217
|
-
|
|
218
|
-
const interactorFiles = files.filter(f => /Interactor\.swift$/.test(f));
|
|
219
|
-
const routerFiles = files.filter(f => /Router\.swift$/.test(f));
|
|
220
|
-
|
|
221
|
-
if (presenterFiles.length >= 2 && interactorFiles.length === 0 && routerFiles.length === 0) {
|
|
222
|
-
this.patterns.mvp += presenterFiles.length * 3;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
files.forEach(file => {
|
|
226
|
-
const content = this.readFile(file);
|
|
227
|
-
|
|
228
|
-
const hasMVPProtocols =
|
|
229
|
-
content.includes('ViewProtocol') &&
|
|
230
|
-
content.includes('PresenterProtocol') &&
|
|
231
|
-
!content.includes('InteractorProtocol');
|
|
232
|
-
|
|
233
|
-
if (hasMVPProtocols) {
|
|
234
|
-
this.patterns.mvp += 3;
|
|
235
|
-
}
|
|
236
|
-
});
|
|
92
|
+
detectMVP(files, this.patterns);
|
|
237
93
|
}
|
|
238
94
|
|
|
239
95
|
detectMVVMC(files) {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
if (coordinatorFiles.length >= 1) {
|
|
243
|
-
this.patterns.mvvmc += coordinatorFiles.length * 3;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
files.forEach(file => {
|
|
247
|
-
const content = this.readFile(file);
|
|
248
|
-
|
|
249
|
-
if (content.includes('protocol Coordinator') ||
|
|
250
|
-
content.includes(': Coordinator') ||
|
|
251
|
-
/func\s+start\(\)/.test(content) && /func\s+navigate/.test(content)) {
|
|
252
|
-
this.patterns.mvvmc += 2;
|
|
253
|
-
}
|
|
254
|
-
});
|
|
96
|
+
detectMVVMC(files, this.patterns);
|
|
255
97
|
}
|
|
256
98
|
|
|
257
99
|
detectMVVM(files) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
if (viewModelFiles.length >= 2) {
|
|
261
|
-
this.patterns.mvvm += viewModelFiles.length * 2;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
files.forEach(file => {
|
|
265
|
-
const content = this.readFile(file);
|
|
266
|
-
|
|
267
|
-
const mvvmIndicators = [
|
|
268
|
-
'@Published',
|
|
269
|
-
': ObservableObject',
|
|
270
|
-
'import Combine',
|
|
271
|
-
'class.*ViewModel'
|
|
272
|
-
];
|
|
273
|
-
|
|
274
|
-
const matches = mvvmIndicators.filter(indicator =>
|
|
275
|
-
new RegExp(indicator).test(content)
|
|
276
|
-
).length;
|
|
277
|
-
|
|
278
|
-
if (matches >= 2) {
|
|
279
|
-
this.patterns.mvvm += matches;
|
|
280
|
-
}
|
|
281
|
-
});
|
|
100
|
+
detectMVVM(files, this.patterns);
|
|
282
101
|
}
|
|
283
102
|
|
|
284
103
|
detectMVC(files) {
|
|
285
|
-
|
|
286
|
-
const viewModelFiles = files.filter(f => /ViewModel\.swift$/.test(f));
|
|
287
|
-
const presenterFiles = files.filter(f => /Presenter\.swift$/.test(f));
|
|
288
|
-
const interactorFiles = files.filter(f => /Interactor\.swift$/.test(f));
|
|
289
|
-
|
|
290
|
-
if (viewControllerFiles.length >= 2 &&
|
|
291
|
-
viewModelFiles.length === 0 &&
|
|
292
|
-
presenterFiles.length === 0 &&
|
|
293
|
-
interactorFiles.length === 0) {
|
|
294
|
-
|
|
295
|
-
viewControllerFiles.forEach(file => {
|
|
296
|
-
const content = this.readFile(file);
|
|
297
|
-
const lines = content.split('\n').length;
|
|
298
|
-
|
|
299
|
-
if (lines > 300) {
|
|
300
|
-
this.patterns.mvc += 3;
|
|
301
|
-
} else if (lines > 150) {
|
|
302
|
-
this.patterns.mvc += 2;
|
|
303
|
-
} else {
|
|
304
|
-
this.patterns.mvc += 1;
|
|
305
|
-
}
|
|
306
|
-
});
|
|
307
|
-
}
|
|
104
|
+
detectMVC(files, this.patterns);
|
|
308
105
|
}
|
|
309
106
|
|
|
310
107
|
getDominantPattern() {
|
|
@@ -323,7 +120,7 @@ class iOSArchitectureDetector {
|
|
|
323
120
|
}
|
|
324
121
|
|
|
325
122
|
if (sorted.length > 1 && sorted[1][1] >= dominantScore * 0.7) {
|
|
326
|
-
return 'MIXED';
|
|
123
|
+
return 'MIXED';
|
|
327
124
|
}
|
|
328
125
|
|
|
329
126
|
return this.normalizePatternName(dominant);
|
|
@@ -331,7 +128,7 @@ class iOSArchitectureDetector {
|
|
|
331
128
|
|
|
332
129
|
normalizePatternName(pattern) {
|
|
333
130
|
const mapping = {
|
|
334
|
-
'featureFirstClean': 'FEATURE_FIRST_CLEAN_DDD',
|
|
131
|
+
'featureFirstClean': 'FEATURE_FIRST_CLEAN_DDD',
|
|
335
132
|
'mvvm': 'MVVM',
|
|
336
133
|
'mvvmc': 'MVVM-C',
|
|
337
134
|
'mvp': 'MVP',
|