wcag-scanner 1.2.62 → 1.2.64

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.
@@ -238,8 +238,15 @@ const WcagDevOverlay = ({ level = 'AA', rules, position = 'bottom-right', deboun
238
238
  clearAllHighlights();
239
239
  }, [open]);
240
240
  // ── Scan ────────────────────────────────────────────────────────────────
241
+ const scanningRef = (0, react_1.useRef)(false);
241
242
  const scan = (0, react_1.useCallback)(async () => {
243
+ var _a;
244
+ if (scanningRef.current)
245
+ return;
246
+ scanningRef.current = true;
242
247
  setScanning(true);
248
+ // Pause observer while scanning to prevent scan-triggered mutations causing rescans
249
+ (_a = observerRef.current) === null || _a === void 0 ? void 0 : _a.disconnect();
243
250
  try {
244
251
  const opts = { level, rules };
245
252
  const res = await (0, browserScanner_1.scanBrowserPage)(opts);
@@ -247,7 +254,17 @@ const WcagDevOverlay = ({ level = 'AA', rules, position = 'bottom-right', deboun
247
254
  setLastScan(new Date());
248
255
  }
249
256
  finally {
257
+ scanningRef.current = false;
250
258
  setScanning(false);
259
+ // Reconnect observer after scan settles
260
+ if (observerRef.current) {
261
+ observerRef.current.observe(document.body, {
262
+ childList: true,
263
+ subtree: true,
264
+ attributes: true,
265
+ attributeFilter: ['class', 'hidden', 'aria-hidden', 'role', 'alt', 'src', 'href'],
266
+ });
267
+ }
251
268
  }
252
269
  }, [level, rules]);
253
270
  (0, react_1.useEffect)(() => { scan(); }, [scan]);
@@ -265,7 +282,9 @@ const WcagDevOverlay = ({ level = 'AA', rules, position = 'bottom-right', deboun
265
282
  childList: true,
266
283
  subtree: true,
267
284
  attributes: true,
268
- attributeFilter: ['class', 'style', 'hidden', 'aria-hidden', 'role', 'alt', 'src', 'href'],
285
+ // 'style' intentionally excluded our highlight helper modifies inline styles
286
+ // on page elements which would cause an infinite rescan loop
287
+ attributeFilter: ['class', 'hidden', 'aria-hidden', 'role', 'alt', 'src', 'href'],
269
288
  });
270
289
  return () => {
271
290
  var _a;
@@ -413,13 +432,11 @@ const WcagDevOverlay = ({ level = 'AA', rules, position = 'bottom-right', deboun
413
432
  fontSize: 13,
414
433
  fontWeight: active ? 600 : 400,
415
434
  color: active ? '#7c3aed' : '#64748b',
416
- borderBottom: active ? '2px solid #7c3aed' : '2px solid transparent',
417
435
  cursor: 'pointer',
418
436
  background: 'none',
419
437
  border: 'none',
420
- borderBottomColor: active ? '#7c3aed' : 'transparent',
421
- borderBottomStyle: 'solid',
422
- borderBottomWidth: 2,
438
+ // Use inset box-shadow instead of borderBottom to avoid shorthand/longhand conflict
439
+ boxShadow: active ? 'inset 0 -2px 0 #7c3aed' : 'none',
423
440
  whiteSpace: 'nowrap',
424
441
  });
425
442
  const filterSelectStyle = {
@@ -2,10 +2,9 @@
2
2
  * initWcagOverlay — auto-injects the WCAG Dev Inspector overlay.
3
3
  *
4
4
  * Call this from any file (js / ts / jsx / tsx) in your app entry point.
5
- * It is a no-op outside of development (process.env.NODE_ENV !== 'development').
5
+ * It is a no-op in production builds.
6
6
  *
7
7
  * @example
8
- * // main.ts / index.js / App.tsx — one line is all you need:
9
8
  * import { initWcagOverlay } from 'wcag-scanner/react';
10
9
  * initWcagOverlay();
11
10
  *
@@ -3,88 +3,54 @@
3
3
  * initWcagOverlay — auto-injects the WCAG Dev Inspector overlay.
4
4
  *
5
5
  * Call this from any file (js / ts / jsx / tsx) in your app entry point.
6
- * It is a no-op outside of development (process.env.NODE_ENV !== 'development').
6
+ * It is a no-op in production builds.
7
7
  *
8
8
  * @example
9
- * // main.ts / index.js / App.tsx — one line is all you need:
10
9
  * import { initWcagOverlay } from 'wcag-scanner/react';
11
10
  * initWcagOverlay();
12
11
  *
13
12
  * // With options:
14
13
  * initWcagOverlay({ level: 'AAA', position: 'bottom-left', debounce: 500 });
15
14
  */
16
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
17
- if (k2 === undefined) k2 = k;
18
- var desc = Object.getOwnPropertyDescriptor(m, k);
19
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
20
- desc = { enumerable: true, get: function() { return m[k]; } };
21
- }
22
- Object.defineProperty(o, k2, desc);
23
- }) : (function(o, m, k, k2) {
24
- if (k2 === undefined) k2 = k;
25
- o[k2] = m[k];
26
- }));
27
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
28
- Object.defineProperty(o, "default", { enumerable: true, value: v });
29
- }) : function(o, v) {
30
- o["default"] = v;
31
- });
32
- var __importStar = (this && this.__importStar) || (function () {
33
- var ownKeys = function(o) {
34
- ownKeys = Object.getOwnPropertyNames || function (o) {
35
- var ar = [];
36
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
37
- return ar;
38
- };
39
- return ownKeys(o);
40
- };
41
- return function (mod) {
42
- if (mod && mod.__esModule) return mod;
43
- var result = {};
44
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
45
- __setModuleDefault(result, mod);
46
- return result;
47
- };
48
- })();
15
+ var __importDefault = (this && this.__importDefault) || function (mod) {
16
+ return (mod && mod.__esModule) ? mod : { "default": mod };
17
+ };
49
18
  Object.defineProperty(exports, "__esModule", { value: true });
50
19
  exports.initWcagOverlay = initWcagOverlay;
20
+ const react_1 = __importDefault(require("react"));
21
+ const WcagDevOverlay_1 = require("./WcagDevOverlay");
51
22
  function initWcagOverlay(options = {}) {
52
- // Only run in browser + development
53
- if (typeof window === 'undefined')
23
+ if (typeof window === 'undefined' || typeof document === 'undefined')
54
24
  return;
55
25
  if (typeof process !== 'undefined' && process.env.NODE_ENV === 'production')
56
26
  return;
57
27
  const mount = () => {
58
- // Don't mount twice
59
28
  if (document.querySelector('[data-wcag-overlay-root]'))
60
29
  return;
61
30
  const container = document.createElement('div');
62
31
  container.setAttribute('data-wcag-overlay-root', 'true');
63
32
  document.body.appendChild(container);
64
- // Dynamically import React + ReactDOM to avoid bundling them twice
65
- Promise.all([
66
- Promise.resolve().then(() => __importStar(require('react'))),
67
- Promise.resolve().then(() => __importStar(require('react-dom'))),
68
- Promise.resolve().then(() => __importStar(require('./WcagDevOverlay'))),
69
- ]).then(([React, ReactDOM, { WcagDevOverlay }]) => {
70
- var _a, _b;
71
- const ReactAny = React;
72
- const r = (_a = ReactAny.default) !== null && _a !== void 0 ? _a : ReactAny;
73
- const el = r.createElement(WcagDevOverlay, options);
74
- // Vite/ESM may wrap the module under .default
75
- const rdAny = ReactDOM;
76
- const rd = (_b = rdAny.default) !== null && _b !== void 0 ? _b : rdAny;
77
- // React 18: createRoot
78
- if (rd.createRoot) {
79
- rd.createRoot(container).render(el);
80
- }
81
- else {
82
- // React 17 fallback
83
- rd.render(el, container);
84
- }
85
- }).catch((err) => {
33
+ const el = react_1.default.createElement(WcagDevOverlay_1.WcagDevOverlay, options);
34
+ // React 18+: use createRoot from react-dom/client
35
+ try {
36
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
37
+ const { createRoot } = require('react-dom/client');
38
+ createRoot(container).render(el);
39
+ return;
40
+ }
41
+ catch (_a) {
42
+ // react-dom/client not available — React 17
43
+ }
44
+ // React 17 fallback
45
+ try {
46
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
47
+ const ReactDOM = require('react-dom');
48
+ ReactDOM.render(el, container);
49
+ }
50
+ catch (err) {
86
51
  console.warn('[wcag-scanner] Failed to mount overlay:', err);
87
- });
52
+ document.body.removeChild(container);
53
+ }
88
54
  };
89
55
  if (document.readyState === 'loading') {
90
56
  document.addEventListener('DOMContentLoaded', mount, { once: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wcag-scanner",
3
- "version": "1.2.62",
3
+ "version": "1.2.64",
4
4
  "description": "Scan HTML for WCAG accessibility violations with AI-powered fix suggestions",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",