rlint 0.4.0 → 0.6.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.
Files changed (38) hide show
  1. package/README.md +235 -32
  2. package/dist/checks/clickability.d.ts.map +1 -1
  3. package/dist/checks/clickability.js +65 -30
  4. package/dist/checks/clickability.js.map +1 -1
  5. package/dist/checks/index.d.ts +1 -0
  6. package/dist/checks/index.d.ts.map +1 -1
  7. package/dist/checks/index.js +5 -0
  8. package/dist/checks/index.js.map +1 -1
  9. package/dist/checks/touch-targets.d.ts.map +1 -1
  10. package/dist/checks/touch-targets.js +134 -47
  11. package/dist/checks/touch-targets.js.map +1 -1
  12. package/dist/checks/viewport-meta.d.ts +3 -0
  13. package/dist/checks/viewport-meta.d.ts.map +1 -0
  14. package/dist/checks/viewport-meta.js +113 -0
  15. package/dist/checks/viewport-meta.js.map +1 -0
  16. package/dist/cli.js +179 -49
  17. package/dist/cli.js.map +1 -1
  18. package/dist/mcp-server.js +158 -31
  19. package/dist/mcp-server.js.map +1 -1
  20. package/dist/proxy/client-script.d.ts +9 -0
  21. package/dist/proxy/client-script.d.ts.map +1 -0
  22. package/dist/proxy/client-script.js +449 -0
  23. package/dist/proxy/client-script.js.map +1 -0
  24. package/dist/proxy/index.d.ts +4 -0
  25. package/dist/proxy/index.d.ts.map +1 -0
  26. package/dist/proxy/index.js +4 -0
  27. package/dist/proxy/index.js.map +1 -0
  28. package/dist/proxy/result-cache.d.ts +46 -0
  29. package/dist/proxy/result-cache.d.ts.map +1 -0
  30. package/dist/proxy/result-cache.js +27 -0
  31. package/dist/proxy/result-cache.js.map +1 -0
  32. package/dist/proxy/server.d.ts +17 -0
  33. package/dist/proxy/server.d.ts.map +1 -0
  34. package/dist/proxy/server.js +190 -0
  35. package/dist/proxy/server.js.map +1 -0
  36. package/dist/types.d.ts +4 -0
  37. package/dist/types.d.ts.map +1 -1
  38. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  export const touchTargetsCheck = {
2
2
  name: 'touch-targets',
3
- description: 'Ensures interactive elements meet minimum touch target size (44x44px)',
3
+ description: 'Ensures interactive elements meet minimum touch target size and spacing (44x44px, 8px gap)',
4
4
  async run(ctx) {
5
5
  const { page, config } = ctx;
6
6
  const checkConfig = typeof config.checks?.touchTargets === 'object'
@@ -8,6 +8,7 @@ export const touchTargetsCheck = {
8
8
  : {};
9
9
  const minWidth = checkConfig.minWidth ?? 44;
10
10
  const minHeight = checkConfig.minHeight ?? 44;
11
+ const MIN_GAP = 8;
11
12
  const selectors = checkConfig.selectors || [
12
13
  'button',
13
14
  'a',
@@ -25,12 +26,32 @@ export const touchTargetsCheck = {
25
26
  ...(checkConfig.ignore || []),
26
27
  '[data-rlint-ignore]',
27
28
  ];
28
- const smallElements = await page.evaluate(({ selectors, ignoreSelectors, minWidth, minHeight }) => {
29
- const results = [];
29
+ const { smallElements, tooClosePairs } = await page.evaluate(({ selectors, ignoreSelectors, minWidth, minHeight, minGap }) => {
30
+ const smalls = [];
30
31
  const selectorString = selectors.join(', ');
31
32
  const elements = document.querySelectorAll(selectorString);
33
+ function makeSelector(el) {
34
+ let s = el.tagName.toLowerCase();
35
+ if (el.id)
36
+ s += `#${el.id}`;
37
+ if (el.className && typeof el.className === 'string') {
38
+ s += '.' + el.className.trim().split(/\s+/).join('.');
39
+ }
40
+ return s;
41
+ }
42
+ function makeRect(rect) {
43
+ return {
44
+ width: rect.width,
45
+ height: rect.height,
46
+ left: rect.left,
47
+ top: rect.top,
48
+ right: rect.right,
49
+ bottom: rect.bottom,
50
+ };
51
+ }
52
+ // Collect all visible interactive elements and their rects
53
+ const visibleElements = [];
32
54
  for (const el of elements) {
33
- // Skip ignored elements
34
55
  const shouldIgnore = ignoreSelectors.some(sel => {
35
56
  try {
36
57
  return el.matches(sel);
@@ -41,7 +62,6 @@ export const touchTargetsCheck = {
41
62
  });
42
63
  if (shouldIgnore)
43
64
  continue;
44
- // Skip hidden elements
45
65
  const style = getComputedStyle(el);
46
66
  if (style.display === 'none' ||
47
67
  style.visibility === 'hidden' ||
@@ -49,68 +69,135 @@ export const touchTargetsCheck = {
49
69
  continue;
50
70
  }
51
71
  const rect = el.getBoundingClientRect();
52
- // Skip zero-size elements (likely hidden)
53
72
  if (rect.width === 0 && rect.height === 0)
54
73
  continue;
55
- // Skip off-screen elements
56
74
  if (rect.right < 0 ||
57
75
  rect.bottom < 0 ||
58
76
  rect.left > window.innerWidth ||
59
77
  rect.top > window.innerHeight) {
60
78
  continue;
61
79
  }
62
- // Check if too small
80
+ visibleElements.push({ el, rect });
81
+ // Size check
63
82
  if (rect.width < minWidth || rect.height < minHeight) {
64
- let selector = el.tagName.toLowerCase();
65
- if (el.id)
66
- selector += `#${el.id}`;
67
- if (el.className && typeof el.className === 'string') {
68
- selector += '.' + el.className.trim().split(/\s+/).join('.');
69
- }
70
- results.push({
71
- selector,
83
+ smalls.push({
84
+ selector: makeSelector(el),
72
85
  tagName: el.tagName,
73
86
  className: typeof el.className === 'string' ? el.className : '',
74
87
  id: el.id || null,
75
88
  textContent: el.textContent?.slice(0, 30)?.trim() || null,
76
- rect: {
77
- width: rect.width,
78
- height: rect.height,
79
- left: rect.left,
80
- top: rect.top,
81
- right: rect.right,
82
- bottom: rect.bottom,
83
- },
89
+ rect: makeRect(rect),
84
90
  actualWidth: rect.width,
85
91
  actualHeight: rect.height,
86
92
  });
87
93
  }
88
94
  }
89
- return results;
90
- }, { selectors, ignoreSelectors, minWidth, minHeight });
91
- const issues = smallElements.map(el => ({
92
- check: 'touch-targets',
93
- severity: 'warning',
94
- message: `Touch target too small: ${Math.round(el.actualWidth)}x${Math.round(el.actualHeight)}px (min: ${minWidth}x${minHeight}px)`,
95
- element: {
96
- selector: el.selector,
97
- tagName: el.tagName,
98
- className: el.className,
99
- id: el.id,
100
- textContent: el.textContent,
101
- rect: el.rect,
102
- },
103
- details: {
104
- actualWidth: el.actualWidth,
105
- actualHeight: el.actualHeight,
106
- minWidth,
107
- minHeight,
108
- },
109
- fixHint: `Add min-width: ${minWidth}px and min-height: ${minHeight}px, or increase padding`,
110
- }));
95
+ // Spacing check: find pairs of interactive elements that are too close
96
+ const closePairs = [];
97
+ const MAX_PAIRS = 20;
98
+ for (let i = 0; i < visibleElements.length && closePairs.length < MAX_PAIRS; i++) {
99
+ const a = visibleElements[i];
100
+ for (let j = i + 1; j < visibleElements.length && closePairs.length < MAX_PAIRS; j++) {
101
+ const b = visibleElements[j];
102
+ // Skip if one contains the other (parent-child)
103
+ if (a.el.contains(b.el) || b.el.contains(a.el))
104
+ continue;
105
+ // Calculate gap between bounding boxes
106
+ const horizGap = Math.max(0, Math.max(b.rect.left - a.rect.right, a.rect.left - b.rect.right));
107
+ const vertGap = Math.max(0, Math.max(b.rect.top - a.rect.bottom, a.rect.top - b.rect.bottom));
108
+ // If they don't overlap on one axis, the gap is the distance on that axis
109
+ // If they overlap on both axes, gap is 0 (overlapping)
110
+ const overlapHoriz = a.rect.right > b.rect.left && b.rect.right > a.rect.left;
111
+ const overlapVert = a.rect.bottom > b.rect.top && b.rect.bottom > a.rect.top;
112
+ let gap;
113
+ if (overlapHoriz && overlapVert) {
114
+ gap = 0; // overlapping
115
+ }
116
+ else if (overlapHoriz) {
117
+ gap = vertGap; // horizontally aligned, measure vertical gap
118
+ }
119
+ else if (overlapVert) {
120
+ gap = horizGap; // vertically aligned, measure horizontal gap
121
+ }
122
+ else {
123
+ // Diagonal — use the Euclidean distance between nearest corners
124
+ gap = Math.sqrt(horizGap * horizGap + vertGap * vertGap);
125
+ }
126
+ if (gap < minGap) {
127
+ closePairs.push({
128
+ elementA: {
129
+ selector: makeSelector(a.el),
130
+ tagName: a.el.tagName,
131
+ className: typeof a.el.className === 'string' ? a.el.className : '',
132
+ id: a.el.id || null,
133
+ textContent: a.el.textContent?.slice(0, 30)?.trim() || null,
134
+ rect: makeRect(a.rect),
135
+ },
136
+ elementB: {
137
+ selector: makeSelector(b.el),
138
+ tagName: b.el.tagName,
139
+ className: typeof b.el.className === 'string' ? b.el.className : '',
140
+ id: b.el.id || null,
141
+ textContent: b.el.textContent?.slice(0, 30)?.trim() || null,
142
+ rect: makeRect(b.rect),
143
+ },
144
+ gap: Math.round(gap),
145
+ });
146
+ }
147
+ }
148
+ }
149
+ return { smallElements: smalls, tooClosePairs: closePairs };
150
+ }, { selectors, ignoreSelectors, minWidth, minHeight, minGap: MIN_GAP });
151
+ const issues = [];
152
+ // Size issues
153
+ for (const el of smallElements) {
154
+ issues.push({
155
+ check: 'touch-targets',
156
+ severity: 'warning',
157
+ message: `Touch target too small: ${Math.round(el.actualWidth)}x${Math.round(el.actualHeight)}px (min: ${minWidth}x${minHeight}px)`,
158
+ element: {
159
+ selector: el.selector,
160
+ tagName: el.tagName,
161
+ className: el.className,
162
+ id: el.id,
163
+ textContent: el.textContent,
164
+ rect: el.rect,
165
+ },
166
+ details: {
167
+ actualWidth: el.actualWidth,
168
+ actualHeight: el.actualHeight,
169
+ minWidth,
170
+ minHeight,
171
+ },
172
+ fixHint: `Add min-width: ${minWidth}px and min-height: ${minHeight}px, or increase padding`,
173
+ });
174
+ }
175
+ // Spacing issues
176
+ for (const pair of tooClosePairs) {
177
+ issues.push({
178
+ check: 'touch-targets',
179
+ severity: 'warning',
180
+ message: `Touch targets too close: ${pair.gap}px gap between ${pair.elementA.selector} and ${pair.elementB.selector} (min: ${MIN_GAP}px)`,
181
+ element: {
182
+ selector: pair.elementA.selector,
183
+ tagName: pair.elementA.tagName,
184
+ className: pair.elementA.className,
185
+ id: pair.elementA.id,
186
+ textContent: pair.elementA.textContent,
187
+ rect: pair.elementA.rect,
188
+ },
189
+ details: {
190
+ gap: pair.gap,
191
+ minGap: MIN_GAP,
192
+ nearElement: pair.elementB.selector,
193
+ },
194
+ fixHint: `Add at least ${MIN_GAP}px margin between ${pair.elementA.selector} and ${pair.elementB.selector}`,
195
+ });
196
+ }
197
+ const totalElements = (await page.$$(selectors.join(', '))).length;
111
198
  return {
112
199
  check: 'touch-targets',
113
- passed: Math.max(0, (await page.$$(selectors.join(', '))).length - issues.length),
200
+ passed: Math.max(0, totalElements - issues.length),
114
201
  issues,
115
202
  };
116
203
  },
@@ -1 +1 @@
1
- {"version":3,"file":"touch-targets.js","sourceRoot":"","sources":["../../src/checks/touch-targets.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"touch-targets.js","sourceRoot":"","sources":["../../src/checks/touch-targets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Check } from '../types.js';
2
+ export declare const viewportMetaCheck: Check;
3
+ //# sourceMappingURL=viewport-meta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewport-meta.d.ts","sourceRoot":"","sources":["../../src/checks/viewport-meta.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAuB,MAAM,aAAa,CAAC;AAU9D,eAAO,MAAM,iBAAiB,EAAE,KA+H/B,CAAC"}
@@ -0,0 +1,113 @@
1
+ export const viewportMetaCheck = {
2
+ name: 'viewport-meta',
3
+ description: 'Checks for a proper viewport meta tag for mobile rendering',
4
+ async run(ctx) {
5
+ const { page, config } = ctx;
6
+ const checkConfig = typeof config.checks?.viewportMeta === 'object'
7
+ ? config.checks.viewportMeta
8
+ : {};
9
+ const ignoreSelectors = [
10
+ ...(config.ignore || []),
11
+ ...(checkConfig.ignore || []),
12
+ ];
13
+ const result = await page.evaluate(() => {
14
+ const meta = document.querySelector('meta[name="viewport"]');
15
+ if (!meta) {
16
+ return {
17
+ exists: false,
18
+ content: null,
19
+ hasWidthDeviceWidth: false,
20
+ hasUserScalableNo: false,
21
+ hasMaximumScale1: false,
22
+ };
23
+ }
24
+ const content = meta.getAttribute('content') || '';
25
+ const directives = content.split(',').map(d => d.trim().toLowerCase());
26
+ const hasWidthDeviceWidth = directives.some(d => d.replace(/\s+/g, '').startsWith('width=device-width'));
27
+ const hasUserScalableNo = directives.some(d => {
28
+ const cleaned = d.replace(/\s+/g, '');
29
+ return cleaned === 'user-scalable=no' || cleaned === 'user-scalable=0';
30
+ });
31
+ const hasMaximumScale1 = directives.some(d => {
32
+ const cleaned = d.replace(/\s+/g, '');
33
+ return cleaned.startsWith('maximum-scale=') && parseFloat(cleaned.split('=')[1]) <= 1;
34
+ });
35
+ return {
36
+ exists: true,
37
+ content,
38
+ hasWidthDeviceWidth,
39
+ hasUserScalableNo,
40
+ hasMaximumScale1,
41
+ };
42
+ });
43
+ const issues = [];
44
+ const dummyElement = {
45
+ selector: 'head',
46
+ tagName: 'HEAD',
47
+ className: '',
48
+ id: null,
49
+ textContent: null,
50
+ rect: { width: 0, height: 0, left: 0, top: 0, right: 0, bottom: 0 },
51
+ };
52
+ if (!result.exists) {
53
+ issues.push({
54
+ check: 'viewport-meta',
55
+ severity: 'warning',
56
+ message: 'Missing viewport meta tag — page will not render correctly on mobile devices',
57
+ element: dummyElement,
58
+ details: {
59
+ expected: '<meta name="viewport" content="width=device-width, initial-scale=1">',
60
+ },
61
+ fixHint: 'Add <meta name="viewport" content="width=device-width, initial-scale=1"> to <head>',
62
+ });
63
+ }
64
+ else {
65
+ if (!result.hasWidthDeviceWidth) {
66
+ issues.push({
67
+ check: 'viewport-meta',
68
+ severity: 'warning',
69
+ message: 'Viewport meta tag is missing width=device-width — mobile browsers will use default 980px width',
70
+ element: dummyElement,
71
+ details: {
72
+ current: result.content,
73
+ expected: 'width=device-width',
74
+ },
75
+ fixHint: 'Add width=device-width to your viewport meta tag',
76
+ });
77
+ }
78
+ if (result.hasUserScalableNo) {
79
+ issues.push({
80
+ check: 'viewport-meta',
81
+ severity: 'warning',
82
+ message: 'Viewport meta tag disables user scaling — this is an accessibility problem (WCAG 1.4.4)',
83
+ element: dummyElement,
84
+ details: {
85
+ current: result.content,
86
+ problem: 'user-scalable=no',
87
+ },
88
+ fixHint: 'Remove user-scalable=no from your viewport meta tag to allow pinch-to-zoom',
89
+ });
90
+ }
91
+ if (result.hasMaximumScale1) {
92
+ issues.push({
93
+ check: 'viewport-meta',
94
+ severity: 'warning',
95
+ message: 'Viewport meta tag restricts maximum scale to 1 — this prevents pinch-to-zoom (WCAG 1.4.4)',
96
+ element: dummyElement,
97
+ details: {
98
+ current: result.content,
99
+ problem: 'maximum-scale=1',
100
+ },
101
+ fixHint: 'Remove maximum-scale=1 or set it to at least 5 to allow zooming',
102
+ });
103
+ }
104
+ }
105
+ const passed = issues.length === 0 ? 1 : 0;
106
+ return {
107
+ check: 'viewport-meta',
108
+ passed,
109
+ issues,
110
+ };
111
+ },
112
+ };
113
+ //# sourceMappingURL=viewport-meta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewport-meta.js","sourceRoot":"","sources":["../../src/checks/viewport-meta.ts"],"names":[],"mappings":"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"}