pumuki 6.3.228 → 6.3.229
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/core/facts/detectors/text/ios.test.ts +54 -0
- package/core/facts/detectors/text/ios.ts +9 -0
- package/core/facts/extractHeuristicFacts.ts +1 -0
- package/core/rules/presets/heuristics/ios.ts +19 -0
- package/integrations/config/skillsDetectorRegistry.ts +4 -0
- package/integrations/config/skillsMarkdownRules.ts +7 -0
- package/package.json +1 -1
- package/skills.lock.json +16 -28
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
hasSwiftNSManagedObjectBoundaryUsage,
|
|
49
49
|
hasSwiftNSManagedObjectStateLeakUsage,
|
|
50
50
|
hasSwiftNavigationViewUsage,
|
|
51
|
+
hasSwiftUntypedNavigationLinkDestinationUsage,
|
|
51
52
|
hasSwiftNonPrivateStateOwnershipUsage,
|
|
52
53
|
hasSwiftNonIBOutletImplicitlyUnwrappedOptionalUsage,
|
|
53
54
|
hasSwiftObservableObjectUsage,
|
|
@@ -291,6 +292,59 @@ struct CheckoutView: View {
|
|
|
291
292
|
assert.equal(hasSwiftUiInlineActionLogicUsage(safe), false);
|
|
292
293
|
});
|
|
293
294
|
|
|
295
|
+
test('hasSwiftUntypedNavigationLinkDestinationUsage detecta NavigationLink no tipado y preserva value navigation', () => {
|
|
296
|
+
const source = `
|
|
297
|
+
struct FeedView: View {
|
|
298
|
+
let items: [Item]
|
|
299
|
+
|
|
300
|
+
var body: some View {
|
|
301
|
+
NavigationStack {
|
|
302
|
+
List(items) { item in
|
|
303
|
+
NavigationLink(destination: DetailView(item: item)) {
|
|
304
|
+
Text(item.title)
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
`;
|
|
311
|
+
const trailing = `
|
|
312
|
+
struct FeedView: View {
|
|
313
|
+
var body: some View {
|
|
314
|
+
NavigationLink {
|
|
315
|
+
DetailView()
|
|
316
|
+
} label: {
|
|
317
|
+
Text("Open")
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
`;
|
|
322
|
+
const safe = `
|
|
323
|
+
struct FeedView: View {
|
|
324
|
+
let items: [Item]
|
|
325
|
+
|
|
326
|
+
var body: some View {
|
|
327
|
+
NavigationStack {
|
|
328
|
+
List(items) { item in
|
|
329
|
+
NavigationLink(value: item) {
|
|
330
|
+
Text(item.title)
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
.navigationDestination(for: Item.self) { item in
|
|
334
|
+
DetailView(item: item)
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
let sample = "NavigationLink(destination: DetailView())"
|
|
338
|
+
// NavigationLink { DetailView() } label: { Text("Open") }
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
`;
|
|
342
|
+
|
|
343
|
+
assert.equal(hasSwiftUntypedNavigationLinkDestinationUsage(source), true);
|
|
344
|
+
assert.equal(hasSwiftUntypedNavigationLinkDestinationUsage(trailing), true);
|
|
345
|
+
assert.equal(hasSwiftUntypedNavigationLinkDestinationUsage(safe), false);
|
|
346
|
+
});
|
|
347
|
+
|
|
294
348
|
test('hasSwiftForceTryUsage detecta try! y descarta try?', () => {
|
|
295
349
|
const positive = `
|
|
296
350
|
func load() {
|
|
@@ -1017,6 +1017,15 @@ export const hasSwiftNavigationViewUsage = (source: string): boolean => {
|
|
|
1017
1017
|
});
|
|
1018
1018
|
};
|
|
1019
1019
|
|
|
1020
|
+
export const hasSwiftUntypedNavigationLinkDestinationUsage = (source: string): boolean => {
|
|
1021
|
+
const swiftSource = sanitizeSwiftSourceForMultilineRegex(source);
|
|
1022
|
+
const destinationParameterPattern = /\bNavigationLink\s*\([^)]*\bdestination\s*:/;
|
|
1023
|
+
const trailingDestinationPattern =
|
|
1024
|
+
/\bNavigationLink\s*\{[\s\S]{0,900}\b[A-Z][A-Za-z0-9_]*View\s*\([^}]*\)[\s\S]{0,900}\}\s*label\s*:/;
|
|
1025
|
+
|
|
1026
|
+
return destinationParameterPattern.test(swiftSource) || trailingDestinationPattern.test(swiftSource);
|
|
1027
|
+
};
|
|
1028
|
+
|
|
1020
1029
|
export const hasSwiftForegroundColorUsage = (source: string): boolean => {
|
|
1021
1030
|
return hasSwiftUiModernizationSnapshotMatch(source, 'foreground-color');
|
|
1022
1031
|
};
|
|
@@ -691,6 +691,7 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
|
|
|
691
691
|
{ platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftUiImageDataDecodingUsage, ruleId: 'heuristics.ios.swiftui.image-data-decoding.ast', code: 'HEURISTICS_IOS_SWIFTUI_IMAGE_DATA_DECODING_AST', message: 'AST heuristic detected UIImage(data:) in SwiftUI presentation; downsample image data before rendering large images.' },
|
|
692
692
|
{ platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftUiInlineActionLogicUsage, ruleId: 'heuristics.ios.swiftui.inline-action-logic.ast', code: 'HEURISTICS_IOS_SWIFTUI_INLINE_ACTION_LOGIC_AST', message: 'AST heuristic detected inline logic inside a SwiftUI action handler; action handlers should reference methods and keep view declarations focused.' },
|
|
693
693
|
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftNavigationViewUsage, ruleId: 'heuristics.ios.navigation-view.ast', code: 'HEURISTICS_IOS_NAVIGATION_VIEW_AST', message: 'AST heuristic detected NavigationView usage.' },
|
|
694
|
+
{ platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftUntypedNavigationLinkDestinationUsage, ruleId: 'heuristics.ios.swiftui.untyped-navigation-link-destination.ast', code: 'HEURISTICS_IOS_SWIFTUI_UNTYPED_NAVIGATION_LINK_DESTINATION_AST', message: 'AST heuristic detected untyped NavigationLink destination usage; prefer NavigationLink(value:) with navigationDestination(for:) for type-safe navigation.' },
|
|
694
695
|
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftForegroundColorUsage, ruleId: 'heuristics.ios.foreground-color.ast', code: 'HEURISTICS_IOS_FOREGROUND_COLOR_AST', message: 'AST heuristic detected foregroundColor usage.' },
|
|
695
696
|
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftCornerRadiusUsage, ruleId: 'heuristics.ios.corner-radius.ast', code: 'HEURISTICS_IOS_CORNER_RADIUS_AST', message: 'AST heuristic detected cornerRadius usage.' },
|
|
696
697
|
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftTabItemUsage, ruleId: 'heuristics.ios.tab-item.ast', code: 'HEURISTICS_IOS_TAB_ITEM_AST', message: 'AST heuristic detected tabItem usage.' },
|
|
@@ -1024,6 +1024,25 @@ export const iosRules: RuleSet = [
|
|
|
1024
1024
|
code: 'HEURISTICS_IOS_NAVIGATION_VIEW_AST',
|
|
1025
1025
|
},
|
|
1026
1026
|
},
|
|
1027
|
+
{
|
|
1028
|
+
id: 'heuristics.ios.swiftui.untyped-navigation-link-destination.ast',
|
|
1029
|
+
description: 'Detects untyped SwiftUI NavigationLink destination usage.',
|
|
1030
|
+
severity: 'WARN',
|
|
1031
|
+
platform: 'ios',
|
|
1032
|
+
locked: true,
|
|
1033
|
+
when: {
|
|
1034
|
+
kind: 'Heuristic',
|
|
1035
|
+
where: {
|
|
1036
|
+
ruleId: 'heuristics.ios.swiftui.untyped-navigation-link-destination.ast',
|
|
1037
|
+
},
|
|
1038
|
+
},
|
|
1039
|
+
then: {
|
|
1040
|
+
kind: 'Finding',
|
|
1041
|
+
message:
|
|
1042
|
+
'AST heuristic detected untyped NavigationLink destination usage; prefer NavigationLink(value:) with navigationDestination(for:) for type-safe navigation.',
|
|
1043
|
+
code: 'HEURISTICS_IOS_SWIFTUI_UNTYPED_NAVIGATION_LINK_DESTINATION_AST',
|
|
1044
|
+
},
|
|
1045
|
+
},
|
|
1027
1046
|
{
|
|
1028
1047
|
id: 'heuristics.ios.foreground-color.ast',
|
|
1029
1048
|
description: 'Detects foregroundColor usage in modern SwiftUI code paths.',
|
|
@@ -211,6 +211,10 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
|
|
|
211
211
|
'skills.ios.no-navigation-view': heuristicDetector('ios.navigation-view', [
|
|
212
212
|
'heuristics.ios.navigation-view.ast',
|
|
213
213
|
]),
|
|
214
|
+
'skills.ios.guideline.ios-swiftui-expert.use-navigationdestination-for-for-type-safe-navigation':
|
|
215
|
+
heuristicDetector('ios.swiftui.untyped-navigation-link-destination', [
|
|
216
|
+
'heuristics.ios.swiftui.untyped-navigation-link-destination.ast',
|
|
217
|
+
]),
|
|
214
218
|
'skills.ios.no-foreground-color': heuristicDetector('ios.foreground-color', [
|
|
215
219
|
'heuristics.ios.foreground-color.ast',
|
|
216
220
|
]),
|
|
@@ -277,6 +277,13 @@ const normalizeKnownRuleTarget = (
|
|
|
277
277
|
if (includes('navigationview') || includes('navigation view')) {
|
|
278
278
|
return 'skills.ios.no-navigation-view';
|
|
279
279
|
}
|
|
280
|
+
if (
|
|
281
|
+
includes('navigationdestination for') ||
|
|
282
|
+
(includes('navigationdestination') && includes('type safe navigation')) ||
|
|
283
|
+
(includes('navigationlink') && includes('value') && includes('navigationdestination'))
|
|
284
|
+
) {
|
|
285
|
+
return 'skills.ios.guideline.ios-swiftui-expert.use-navigationdestination-for-for-type-safe-navigation';
|
|
286
|
+
}
|
|
280
287
|
if (
|
|
281
288
|
includes('foregroundstyle instead of foregroundcolor') ||
|
|
282
289
|
includes('foregroundstyle over foregroundcolor') ||
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.229",
|
|
4
4
|
"description": "Enterprise-grade AST Intelligence System with multi-platform support (iOS, Android, Backend, Frontend) and Feature-First + DDD + Clean Architecture enforcement. Includes dynamic violations API for intelligent querying.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
package/skills.lock.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "1.0",
|
|
3
3
|
"compilerVersion": "1.0.0",
|
|
4
|
-
"generatedAt": "2026-05-13T16:
|
|
4
|
+
"generatedAt": "2026-05-13T16:44:08.385Z",
|
|
5
5
|
"bundles": [
|
|
6
6
|
{
|
|
7
7
|
"name": "android-guidelines",
|
|
@@ -5764,8 +5764,20 @@
|
|
|
5764
5764
|
"name": "ios-guidelines",
|
|
5765
5765
|
"version": "1.0.0",
|
|
5766
5766
|
"source": "file:vendor/skills/ios-enterprise-rules/SKILL.md",
|
|
5767
|
-
"hash": "
|
|
5767
|
+
"hash": "a70065766533e822f2139da42d7933b6f240634c93939ee60969e3fd009409ce",
|
|
5768
5768
|
"rules": [
|
|
5769
|
+
{
|
|
5770
|
+
"id": "skills.ios.guideline.ios-swiftui-expert.use-navigationdestination-for-for-type-safe-navigation",
|
|
5771
|
+
"description": "navigationDestination(for:) - Destinos tipados",
|
|
5772
|
+
"severity": "WARN",
|
|
5773
|
+
"platform": "ios",
|
|
5774
|
+
"sourceSkill": "ios-guidelines",
|
|
5775
|
+
"sourcePath": "vendor/skills/ios-enterprise-rules/SKILL.md",
|
|
5776
|
+
"confidence": "MEDIUM",
|
|
5777
|
+
"locked": true,
|
|
5778
|
+
"evaluationMode": "AUTO",
|
|
5779
|
+
"origin": "core"
|
|
5780
|
+
},
|
|
5769
5781
|
{
|
|
5770
5782
|
"id": "skills.ios.guideline.ios-swiftui-expert.use-task-modifier-for-automatic-cancellation-of-async-work",
|
|
5771
5783
|
"description": ".task/.task(id:) - Trabajos async con cancelación automática",
|
|
@@ -7051,30 +7063,6 @@
|
|
|
7051
7063
|
"evaluationMode": "DECLARATIVE",
|
|
7052
7064
|
"origin": "core"
|
|
7053
7065
|
},
|
|
7054
|
-
{
|
|
7055
|
-
"id": "skills.ios.guideline.ios.navigationdestination-for-destinos-tipados",
|
|
7056
|
-
"description": "navigationDestination(for:) - Destinos tipados",
|
|
7057
|
-
"severity": "WARN",
|
|
7058
|
-
"platform": "ios",
|
|
7059
|
-
"sourceSkill": "ios-guidelines",
|
|
7060
|
-
"sourcePath": "vendor/skills/ios-enterprise-rules/SKILL.md",
|
|
7061
|
-
"confidence": "MEDIUM",
|
|
7062
|
-
"locked": true,
|
|
7063
|
-
"evaluationMode": "DECLARATIVE",
|
|
7064
|
-
"origin": "core"
|
|
7065
|
-
},
|
|
7066
|
-
{
|
|
7067
|
-
"id": "skills.ios.guideline.ios.navigationdestination-for-para-destinos",
|
|
7068
|
-
"description": "NavigationDestination(for:) para destinos",
|
|
7069
|
-
"severity": "WARN",
|
|
7070
|
-
"platform": "ios",
|
|
7071
|
-
"sourceSkill": "ios-guidelines",
|
|
7072
|
-
"sourcePath": "vendor/skills/ios-enterprise-rules/SKILL.md",
|
|
7073
|
-
"confidence": "MEDIUM",
|
|
7074
|
-
"locked": true,
|
|
7075
|
-
"evaluationMode": "DECLARATIVE",
|
|
7076
|
-
"origin": "core"
|
|
7077
|
-
},
|
|
7078
7066
|
{
|
|
7079
7067
|
"id": "skills.ios.guideline.ios.navigationstack-navigationpath-con-rutas-tipadas",
|
|
7080
7068
|
"description": "NavigationStack + NavigationPath con rutas tipadas",
|
|
@@ -8632,7 +8620,7 @@
|
|
|
8632
8620
|
"name": "ios-swiftui-expert-guidelines",
|
|
8633
8621
|
"version": "1.0.0",
|
|
8634
8622
|
"source": "file:vendor/skills/swiftui-expert-skill/SKILL.md",
|
|
8635
|
-
"hash": "
|
|
8623
|
+
"hash": "92aa9bdb43b5641206e53aaba4647a5c88b88a7aca83dc00eea373d5e70b7080",
|
|
8636
8624
|
"rules": [
|
|
8637
8625
|
{
|
|
8638
8626
|
"id": "skills.ios.guideline.ios-swiftui-expert.action-handlers-should-reference-methods-not-contain-inline-logic",
|
|
@@ -8895,7 +8883,7 @@
|
|
|
8895
8883
|
"sourcePath": "vendor/skills/swiftui-expert-skill/SKILL.md",
|
|
8896
8884
|
"confidence": "MEDIUM",
|
|
8897
8885
|
"locked": true,
|
|
8898
|
-
"evaluationMode": "
|
|
8886
|
+
"evaluationMode": "AUTO",
|
|
8899
8887
|
"origin": "core"
|
|
8900
8888
|
},
|
|
8901
8889
|
{
|