pumuki 6.3.285 → 6.3.287

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/VERSION CHANGED
@@ -1 +1 @@
1
- v6.3.285
1
+ 6.3.287
@@ -46,12 +46,23 @@ import {
46
46
  hasSwiftHardcodedSensitiveStringUsage,
47
47
  hasSwiftUnlocalizedDateFormatterUsage,
48
48
  hasSwiftIconOnlyControlWithoutAccessibilityLabelUsage,
49
+ collectSwiftIconOnlyControlWithoutAccessibilityLabelLines,
49
50
  collectSwiftInteractiveControlWithoutAccessibilityIdentifierLines,
50
51
  hasSwiftInteractiveControlWithoutAccessibilityIdentifierUsage,
51
52
  collectSwiftBindableMissingForObservableBindingUsageLines,
52
53
  collectSwiftExplicitColorStaticMemberLines,
53
54
  collectSwiftFixedFontSizeLines,
54
55
  collectSwiftHardcodedSensitiveStringLines,
56
+ collectSwiftHardcodedUiStringLines,
57
+ collectSwiftNonLazyScrollForEachLines,
58
+ collectSwiftUiForEachConditionalViewCountLines,
59
+ collectSwiftUiInlineActionLogicLines,
60
+ collectSwiftForEachIndicesLines,
61
+ collectSwiftForEachSelfIdentityLines,
62
+ collectSwiftInlineForEachTransformLines,
63
+ collectSwiftUiConditionalSameViewIdentityLines,
64
+ collectSwiftForegroundColorLines,
65
+ collectSwiftCornerRadiusLines,
55
66
  collectSwiftMagicNumberLayoutLines,
56
67
  hasSwiftBindableMissingForObservableBindingUsage,
57
68
  hasSwiftLooseAssetResourceUsage,
@@ -111,6 +122,7 @@ import {
111
122
  hasSwiftExplicitColorStaticMemberUsage,
112
123
  hasSwiftClosureBasedViewBuilderContentUsage,
113
124
  collectSwiftForceUnwrapLines,
125
+ collectSwiftWarningSuppressionLines,
114
126
  hasSwiftLargeConfigContextViewPropertyUsage,
115
127
  hasSwiftUiConditionalSameViewIdentityUsage,
116
128
  hasSwiftUiParentOwnedSheetActionUsage,
@@ -124,6 +136,7 @@ import {
124
136
  hasSwiftTaskDetachedUsage,
125
137
  hasSwiftUnownedSelfCaptureUsage,
126
138
  hasSwiftWaitForExpectationsUsage,
139
+ hasSwiftWarningSuppressionUsage,
127
140
  hasSwiftUIScreenMainBoundsUsage,
128
141
  hasSwiftXCTestAssertionUsage,
129
142
  hasSwiftXCTUnwrapUsage,
@@ -164,6 +177,28 @@ if waitersByKey[key] != nil {
164
177
  assert.deepEqual(collectSwiftForceUnwrapLines(source), []);
165
178
  });
166
179
 
180
+ test('collectSwiftWarningSuppressionLines localiza supresiones de warnings Swift accionables', () => {
181
+ const source = [
182
+ 'struct CheckoutView {',
183
+ ' // swiftlint:disable force_unwrapping',
184
+ ' let title = "swiftlint:disable is just copy"',
185
+ ' #warning("Remove temporary checkout branch")',
186
+ ' // swiftformat:disable:next redundantSelf',
187
+ '}',
188
+ ].join('\n');
189
+ const safe = [
190
+ 'struct CheckoutView {',
191
+ ' let title = "No warnings are suppressed"',
192
+ ' // regular design note without disabling tooling',
193
+ '}',
194
+ ].join('\n');
195
+
196
+ assert.equal(hasSwiftWarningSuppressionUsage(source), true);
197
+ assert.deepEqual(collectSwiftWarningSuppressionLines(source), [2, 4, 5]);
198
+ assert.equal(hasSwiftWarningSuppressionUsage(safe), false);
199
+ assert.deepEqual(collectSwiftWarningSuppressionLines(safe), []);
200
+ });
201
+
167
202
  test('hasSwiftUnownedSelfCaptureUsage detecta captures unowned y preserva weak', () => {
168
203
  const source = `
169
204
  final class ProfileViewModel {
@@ -507,7 +542,9 @@ struct CheckoutView: View {
507
542
  `;
508
543
 
509
544
  assert.equal(hasSwiftUiInlineActionLogicUsage(source), true);
545
+ assert.deepEqual(collectSwiftUiInlineActionLogicLines(source), [6]);
510
546
  assert.equal(hasSwiftUiInlineActionLogicUsage(safe), false);
547
+ assert.deepEqual(collectSwiftUiInlineActionLogicLines(safe), []);
511
548
  });
512
549
 
513
550
  test('hasSwiftForEachSelfIdentityUsage detecta id self y preserva ids estables', () => {
@@ -1206,7 +1243,9 @@ struct OrdersView: View {
1206
1243
  `;
1207
1244
 
1208
1245
  assert.equal(hasSwiftHardcodedUiStringUsage(source), true);
1246
+ assert.deepEqual(collectSwiftHardcodedUiStringLines(source), [5, 6, 7, 8, 9]);
1209
1247
  assert.equal(hasSwiftHardcodedUiStringUsage(ignored), false);
1248
+ assert.deepEqual(collectSwiftHardcodedUiStringLines(ignored), []);
1210
1249
  });
1211
1250
 
1212
1251
  test('detector iOS de assets detecta recursos sueltos sin confundir asset catalogs', () => {
@@ -1342,7 +1381,9 @@ struct ToolbarView: View {
1342
1381
  `;
1343
1382
 
1344
1383
  assert.equal(hasSwiftIconOnlyControlWithoutAccessibilityLabelUsage(source), true);
1384
+ assert.deepEqual(collectSwiftIconOnlyControlWithoutAccessibilityLabelLines(source), [4]);
1345
1385
  assert.equal(hasSwiftIconOnlyControlWithoutAccessibilityLabelUsage(ignored), false);
1386
+ assert.deepEqual(collectSwiftIconOnlyControlWithoutAccessibilityLabelLines(ignored), []);
1346
1387
  });
1347
1388
 
1348
1389
  test('detector iOS de accesibilidad detecta controles interactivos sin accessibilityIdentifier', () => {
@@ -1652,6 +1693,48 @@ let ignored = "Color.green"
1652
1693
  assert.deepEqual(collectSwiftExplicitColorStaticMemberLines(safe), []);
1653
1694
  });
1654
1695
 
1696
+ test('collectors iOS de INC-152 devuelven lineas accionables para reglas SwiftUI bloqueantes', () => {
1697
+ const source = `
1698
+ struct StoresView: View {
1699
+ var body: some View {
1700
+ ScrollView {
1701
+ VStack {
1702
+ ForEach(items.indices, id: \\.self) { index in
1703
+ if items[index].isVisible {
1704
+ Text(items[index].title).foregroundColor(.blue)
1705
+ }
1706
+ }
1707
+ ForEach(items.filter { $0.isVisible }) { item in
1708
+ Text(item.title)
1709
+ }
1710
+ if isLoading {
1711
+ Text("Loading")
1712
+ } else {
1713
+ Text("Ready")
1714
+ }
1715
+ Button {
1716
+ if isLoading { return }
1717
+ } label: {
1718
+ Text("Retry")
1719
+ }
1720
+ Image("hero").cornerRadius(12)
1721
+ }
1722
+ }
1723
+ }
1724
+ }
1725
+ `;
1726
+
1727
+ assert.deepEqual(collectSwiftNonLazyScrollForEachLines(source), [4, 6, 11]);
1728
+ assert.deepEqual(collectSwiftForEachIndicesLines(source), [6]);
1729
+ assert.deepEqual(collectSwiftForEachSelfIdentityLines(source), [6]);
1730
+ assert.deepEqual(collectSwiftUiForEachConditionalViewCountLines(source), [6, 7, 11, 14, 20]);
1731
+ assert.deepEqual(collectSwiftInlineForEachTransformLines(source), [11]);
1732
+ assert.deepEqual(collectSwiftUiConditionalSameViewIdentityLines(source), [7, 14, 20]);
1733
+ assert.deepEqual(collectSwiftUiInlineActionLogicLines(source), [19]);
1734
+ assert.deepEqual(collectSwiftForegroundColorLines(source), [8]);
1735
+ assert.deepEqual(collectSwiftCornerRadiusLines(source), [24]);
1736
+ });
1737
+
1655
1738
  test('hasSwiftClosureBasedViewBuilderContentUsage detecta content closure y preserva @ViewBuilder let content', () => {
1656
1739
  const source = `
1657
1740
  struct Card<Content: View>: View {
@@ -109,6 +109,26 @@ export const hasSwiftProductionCommentUsage = (source: string): boolean => {
109
109
  });
110
110
  };
111
111
 
112
+ export const collectSwiftWarningSuppressionLines = (source: string): readonly number[] => {
113
+ const lines: number[] = [];
114
+
115
+ source.split(/\r?\n/).forEach((rawLine, index) => {
116
+ const line = stripSwiftStringLiterals(rawLine);
117
+ if (
118
+ /^\s*#warning\s*\(/.test(line) ||
119
+ /\/\/\s*(?:swiftlint|swiftformat|periphery)\s*:\s*disable(?::|\b)/i.test(line)
120
+ ) {
121
+ lines.push(index + 1);
122
+ }
123
+ });
124
+
125
+ return sortedUniqueLines(lines);
126
+ };
127
+
128
+ export const hasSwiftWarningSuppressionUsage = (source: string): boolean => {
129
+ return collectSwiftWarningSuppressionLines(source).length > 0;
130
+ };
131
+
112
132
  export const hasSwiftCombineSinkWithoutStoreUsage = (source: string): boolean => {
113
133
  const lines = source.split(/\r?\n/);
114
134
 
@@ -502,6 +522,16 @@ export const hasSwiftNonLazyScrollForEachUsage = (source: string): boolean => {
502
522
  return nonLazyScrollableCollectionPattern.test(swiftSource);
503
523
  };
504
524
 
525
+ export const collectSwiftNonLazyScrollForEachLines = (source: string): readonly number[] => {
526
+ if (!hasSwiftNonLazyScrollForEachUsage(source)) {
527
+ return [];
528
+ }
529
+ return sortedUniqueLines([
530
+ ...collectSwiftRegexLines(source, /\bScrollView\s*(?:\([^)]*\))?\s*\{/),
531
+ ...collectSwiftRegexLines(source, /\bForEach\s*\(/),
532
+ ]);
533
+ };
534
+
505
535
  export const hasSwiftUiForEachConditionalViewCountUsage = (source: string): boolean => {
506
536
  const swiftSource = sanitizeSwiftSourceForMultilineRegex(source);
507
537
  const conditionalForEachPattern =
@@ -510,6 +540,18 @@ export const hasSwiftUiForEachConditionalViewCountUsage = (source: string): bool
510
540
  return conditionalForEachPattern.test(swiftSource);
511
541
  };
512
542
 
543
+ export const collectSwiftUiForEachConditionalViewCountLines = (
544
+ source: string
545
+ ): readonly number[] => {
546
+ if (!hasSwiftUiForEachConditionalViewCountUsage(source)) {
547
+ return [];
548
+ }
549
+ return sortedUniqueLines([
550
+ ...collectSwiftRegexLines(source, /\bForEach\s*\(/),
551
+ ...collectSwiftRegexLines(source, /\b(?:if|switch)\b/),
552
+ ]);
553
+ };
554
+
513
555
  export const hasSwiftViewBodyObjectCreationUsage = (source: string): boolean => {
514
556
  const swiftSource = sanitizeSwiftSourceForMultilineRegex(source);
515
557
  const viewBodyObjectCreationPattern =
@@ -533,6 +575,13 @@ export const hasSwiftUiInlineActionLogicUsage = (source: string): boolean => {
533
575
  return inlineButtonActionPattern.test(swiftSource) || inlineActionParameterPattern.test(swiftSource);
534
576
  };
535
577
 
578
+ export const collectSwiftUiInlineActionLogicLines = (source: string): readonly number[] => {
579
+ if (!hasSwiftUiInlineActionLogicUsage(source)) {
580
+ return [];
581
+ }
582
+ return collectSwiftRegexLines(source, /\bButton\s*(?:\(|\{)/);
583
+ };
584
+
536
585
  export const hasSwiftDispatchQueueUsage = (source: string): boolean => {
537
586
  return scanCodeLikeSource(source, ({ source: swiftSource, index, current }) => {
538
587
  if (current !== 'D' || !hasIdentifierAt(swiftSource, index, 'DispatchQueue')) {
@@ -989,6 +1038,32 @@ export const hasSwiftHardcodedUiStringUsage = (source: string): boolean => {
989
1038
  });
990
1039
  };
991
1040
 
1041
+ export const collectSwiftHardcodedUiStringLines = (source: string): readonly number[] => {
1042
+ const withoutBlockComments = source.replace(/\/\*[\s\S]*?\*\//g, '\n');
1043
+ const matches: number[] = [];
1044
+ withoutBlockComments.split(/\r?\n/).forEach((line, index) => {
1045
+ if (/^\s*\/\//.test(line)) {
1046
+ return;
1047
+ }
1048
+ const withoutInlineComment = line.replace(/\/\/.*$/, '');
1049
+ const hasHardcodedUiLiteral = swiftUiLiteralTextPatterns.some((pattern) => {
1050
+ const match = withoutInlineComment.match(pattern);
1051
+ if (!match) {
1052
+ return false;
1053
+ }
1054
+ const literal = match[1]?.trim() ?? '';
1055
+ if (literal.length === 0) {
1056
+ return false;
1057
+ }
1058
+ return !looksLikeLocalizationKey(literal);
1059
+ });
1060
+ if (hasHardcodedUiLiteral) {
1061
+ matches.push(index + 1);
1062
+ }
1063
+ });
1064
+ return matches;
1065
+ };
1066
+
992
1067
  export const hasSwiftLooseAssetResourceUsage = (source: string): boolean => {
993
1068
  const withoutBlockComments = source.replace(/\/\*[\s\S]*?\*\//g, '\n');
994
1069
  return withoutBlockComments.split(/\r?\n/).some((line) => {
@@ -1089,6 +1164,15 @@ export const hasSwiftIconOnlyControlWithoutAccessibilityLabelUsage = (source: st
1089
1164
  return false;
1090
1165
  };
1091
1166
 
1167
+ export const collectSwiftIconOnlyControlWithoutAccessibilityLabelLines = (
1168
+ source: string
1169
+ ): readonly number[] => {
1170
+ if (!hasSwiftIconOnlyControlWithoutAccessibilityLabelUsage(source)) {
1171
+ return [];
1172
+ }
1173
+ return collectSwiftRegexLines(source, /\bButton\s*(?:\(|\{)/);
1174
+ };
1175
+
1092
1176
  export const collectSwiftInteractiveControlWithoutAccessibilityIdentifierLines = (
1093
1177
  source: string
1094
1178
  ): readonly number[] => {
@@ -1248,10 +1332,21 @@ export const hasSwiftForEachIndicesUsage = (source: string): boolean => {
1248
1332
  );
1249
1333
  };
1250
1334
 
1335
+ export const collectSwiftForEachIndicesLines = (source: string): readonly number[] => {
1336
+ return collectSwiftRegexLines(
1337
+ source,
1338
+ /\bForEach\s*\(\s*(?:Array\s*\(\s*)?[A-Za-z_][A-Za-z0-9_.]*\.indices\b/
1339
+ );
1340
+ };
1341
+
1251
1342
  export const hasSwiftForEachSelfIdentityUsage = (source: string): boolean => {
1252
1343
  return hasSwiftSanitizedRegexMatch(source, /\bForEach\s*\([^)]*\bid\s*:\s*\\\.self\b/g);
1253
1344
  };
1254
1345
 
1346
+ export const collectSwiftForEachSelfIdentityLines = (source: string): readonly number[] => {
1347
+ return collectSwiftRegexLines(source, /\bForEach\s*\([^)]*\bid\s*:\s*\\\.self\b/);
1348
+ };
1349
+
1255
1350
  export const hasSwiftSelfPrintChangesUsage = (source: string): boolean => {
1256
1351
  return hasSwiftSanitizedRegexMatch(source, /\bSelf\s*\.\s*_printChanges\s*\(/g);
1257
1352
  };
@@ -1263,6 +1358,13 @@ export const hasSwiftInlineForEachTransformUsage = (source: string): boolean =>
1263
1358
  );
1264
1359
  };
1265
1360
 
1361
+ export const collectSwiftInlineForEachTransformLines = (source: string): readonly number[] => {
1362
+ return collectSwiftRegexLines(
1363
+ source,
1364
+ /\bForEach\s*\(\s*(?:Array\s*\(\s*)?[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*\s*\.\s*(?:filter|map|compactMap|sorted)\s*(?:\{|\()/
1365
+ );
1366
+ };
1367
+
1266
1368
  const isUserSearchIdentifier = (value: string): boolean => {
1267
1369
  return /^(?:query|search(?:Text|Term|Query|Value)?|filter(?:Text|Value)?|text|term|input)$/i.test(
1268
1370
  value
@@ -1387,6 +1489,15 @@ export const hasSwiftUiConditionalSameViewIdentityUsage = (source: string): bool
1387
1489
  return false;
1388
1490
  };
1389
1491
 
1492
+ export const collectSwiftUiConditionalSameViewIdentityLines = (
1493
+ source: string
1494
+ ): readonly number[] => {
1495
+ if (!hasSwiftUiConditionalSameViewIdentityUsage(source)) {
1496
+ return [];
1497
+ }
1498
+ return collectSwiftRegexLines(source, /\bif\s+[^{}]+\{/);
1499
+ };
1500
+
1390
1501
  export const hasSwiftUiParentOwnedSheetActionUsage = (source: string): boolean => {
1391
1502
  const sanitized = sanitizeSwiftSourceForMultilineRegex(source);
1392
1503
  const swiftUIViewBodyPattern =
@@ -1543,10 +1654,18 @@ export const hasSwiftForegroundColorUsage = (source: string): boolean => {
1543
1654
  return hasSwiftUiModernizationSnapshotMatch(source, 'foreground-color');
1544
1655
  };
1545
1656
 
1657
+ export const collectSwiftForegroundColorLines = (source: string): readonly number[] => {
1658
+ return collectSwiftRegexLines(source, /\.\s*foregroundColor\s*\(/);
1659
+ };
1660
+
1546
1661
  export const hasSwiftCornerRadiusUsage = (source: string): boolean => {
1547
1662
  return hasSwiftUiModernizationSnapshotMatch(source, 'corner-radius');
1548
1663
  };
1549
1664
 
1665
+ export const collectSwiftCornerRadiusLines = (source: string): readonly number[] => {
1666
+ return collectSwiftRegexLines(source, /\.\s*cornerRadius\s*\(/);
1667
+ };
1668
+
1550
1669
  export const hasSwiftTabItemUsage = (source: string): boolean => {
1551
1670
  return hasSwiftUiModernizationSnapshotMatch(source, 'tab-item');
1552
1671
  };