test-a11y-js 0.6.3 → 0.7.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.
- package/README.md +16 -1
- package/dist/index.js +86 -9
- package/dist/index.mjs +94 -9
- package/dist/linter/eslint-plugin/index.js +86 -9
- package/dist/linter/eslint-plugin/index.mjs +94 -9
- package/package.json +8 -1
package/README.md
CHANGED
|
@@ -12,6 +12,9 @@ npm install --save-dev test-a11y-js
|
|
|
12
12
|
|
|
13
13
|
- `eslint` (>=8.0.0) - Required for ESLint plugin
|
|
14
14
|
- `vue-eslint-parser` (>=9.0.0) - Optional, only needed for Vue support
|
|
15
|
+
- `jsdom` (>=23.0.0) - Optional, only needed for HTML string parsing in ESLint rules
|
|
16
|
+
|
|
17
|
+
**Note:** jsdom is only required if you use HTML strings in your code. JSX and Vue templates work without jsdom. See [jsdom Guide](./docs/JSDOM.md) for details.
|
|
15
18
|
|
|
16
19
|
## Usage
|
|
17
20
|
|
|
@@ -75,12 +78,24 @@ module.exports = {
|
|
|
75
78
|
|
|
76
79
|
#### Available Configurations
|
|
77
80
|
|
|
81
|
+
- `plugin:test-a11y-js/minimal` - Only critical rules (3 rules) - **Best for large projects**
|
|
78
82
|
- `plugin:test-a11y-js/recommended` - Balanced approach (default)
|
|
79
83
|
- `plugin:test-a11y-js/strict` - All rules as errors
|
|
80
84
|
- `plugin:test-a11y-js/react` - Optimized for React/JSX
|
|
81
85
|
- `plugin:test-a11y-js/vue` - Optimized for Vue SFC
|
|
82
86
|
|
|
83
|
-
|
|
87
|
+
### Quick Start for Large Projects
|
|
88
|
+
|
|
89
|
+
```javascript
|
|
90
|
+
// .eslintrc.js - Start with minimal rules
|
|
91
|
+
module.exports = {
|
|
92
|
+
plugins: ['test-a11y-js'],
|
|
93
|
+
extends: ['plugin:test-a11y-js/minimal'],
|
|
94
|
+
ignorePatterns: ['**/node_modules/**', '**/dist/**']
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
See [Configuration Guide](./docs/CONFIGURATION.md) for more details and [Large Project Setup Guide](./docs/LARGE_PROJECTS.md) for incremental adoption strategies.
|
|
84
99
|
|
|
85
100
|
## API
|
|
86
101
|
|
package/dist/index.js
CHANGED
|
@@ -424,6 +424,15 @@ ${index + 1}. ${violation.id} (${violation.impact})`);
|
|
|
424
424
|
}
|
|
425
425
|
};
|
|
426
426
|
|
|
427
|
+
// src/linter/eslint-plugin/configs/minimal.ts
|
|
428
|
+
var minimal = {
|
|
429
|
+
// Only critical/serious violations that block basic accessibility
|
|
430
|
+
"test-a11y-js/button-label": "error",
|
|
431
|
+
"test-a11y-js/form-label": "error",
|
|
432
|
+
"test-a11y-js/image-alt": "error"
|
|
433
|
+
};
|
|
434
|
+
var minimal_default = minimal;
|
|
435
|
+
|
|
427
436
|
// src/linter/eslint-plugin/configs/recommended.ts
|
|
428
437
|
var recommended = {
|
|
429
438
|
// Critical/Serious violations - set to error
|
|
@@ -478,7 +487,25 @@ var vue = {
|
|
|
478
487
|
var vue_default = vue;
|
|
479
488
|
|
|
480
489
|
// src/linter/eslint-plugin/utils/jsx-ast-utils.ts
|
|
481
|
-
var
|
|
490
|
+
var JSDOM = null;
|
|
491
|
+
var jsdomWarningShown = false;
|
|
492
|
+
function getJSDOM() {
|
|
493
|
+
if (JSDOM !== null) {
|
|
494
|
+
return JSDOM;
|
|
495
|
+
}
|
|
496
|
+
try {
|
|
497
|
+
JSDOM = require("jsdom").JSDOM;
|
|
498
|
+
return JSDOM;
|
|
499
|
+
} catch (error) {
|
|
500
|
+
if (!jsdomWarningShown) {
|
|
501
|
+
console.warn(
|
|
502
|
+
"[test-a11y-js] jsdom not found. JSX element conversion will be skipped. Install jsdom if you need JSX accessibility checks: npm install --save-dev jsdom"
|
|
503
|
+
);
|
|
504
|
+
jsdomWarningShown = true;
|
|
505
|
+
}
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
482
509
|
function getJSXAttributeValue(attr) {
|
|
483
510
|
if (!attr.value) {
|
|
484
511
|
return attr.name.name || null;
|
|
@@ -531,7 +558,11 @@ function jsxToElement(node, context) {
|
|
|
531
558
|
const openingElement = "openingElement" in jsxNode ? jsxNode.openingElement : jsxNode;
|
|
532
559
|
const tagName = getJSXTagName(openingElement);
|
|
533
560
|
const attributes = getJSXAttributes(openingElement);
|
|
534
|
-
const
|
|
561
|
+
const JSDOMClass = getJSDOM();
|
|
562
|
+
if (!JSDOMClass) {
|
|
563
|
+
throw new Error("jsdom is required for JSX element conversion");
|
|
564
|
+
}
|
|
565
|
+
const dom = new JSDOMClass("<!DOCTYPE html><html><body></body></html>");
|
|
535
566
|
const element = dom.window.document.createElement(tagName);
|
|
536
567
|
for (const [name, value] of attributes.entries()) {
|
|
537
568
|
element.setAttribute(name, value);
|
|
@@ -558,18 +589,38 @@ function hasJSXAttribute(element, name) {
|
|
|
558
589
|
return getJSXAttribute(element, name) !== void 0;
|
|
559
590
|
}
|
|
560
591
|
|
|
561
|
-
// src/linter/eslint-plugin/utils/html-ast-utils.ts
|
|
562
|
-
var import_jsdom2 = require("jsdom");
|
|
563
|
-
|
|
564
592
|
// src/linter/eslint-plugin/utils/ast-utils.ts
|
|
565
593
|
function isHTMLLiteral(node) {
|
|
566
594
|
return node.type === "TemplateLiteral" || node.type === "Literal" || node.type === "TaggedTemplateExpression" && node.tag.name === "html";
|
|
567
595
|
}
|
|
568
596
|
|
|
569
597
|
// src/linter/eslint-plugin/utils/html-ast-utils.ts
|
|
598
|
+
var JSDOM2 = null;
|
|
599
|
+
var jsdomWarningShown2 = false;
|
|
600
|
+
function getJSDOM2() {
|
|
601
|
+
if (JSDOM2 !== null) {
|
|
602
|
+
return JSDOM2;
|
|
603
|
+
}
|
|
604
|
+
try {
|
|
605
|
+
JSDOM2 = require("jsdom").JSDOM;
|
|
606
|
+
return JSDOM2;
|
|
607
|
+
} catch (error) {
|
|
608
|
+
if (!jsdomWarningShown2) {
|
|
609
|
+
console.warn(
|
|
610
|
+
"[test-a11y-js] jsdom not found. HTML string parsing will be skipped. Install jsdom if you need HTML string accessibility checks: npm install --save-dev jsdom"
|
|
611
|
+
);
|
|
612
|
+
jsdomWarningShown2 = true;
|
|
613
|
+
}
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
570
617
|
function parseHTMLString(html) {
|
|
618
|
+
const JSDOMClass = getJSDOM2();
|
|
619
|
+
if (!JSDOMClass) {
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
571
622
|
try {
|
|
572
|
-
const dom = new
|
|
623
|
+
const dom = new JSDOMClass(html, { contentType: "text/html" });
|
|
573
624
|
const body = dom.window.document.body;
|
|
574
625
|
if (body.children.length === 1) {
|
|
575
626
|
return body.children[0];
|
|
@@ -617,7 +668,25 @@ function htmlNodeToElement(node, context) {
|
|
|
617
668
|
}
|
|
618
669
|
|
|
619
670
|
// src/linter/eslint-plugin/utils/vue-ast-utils.ts
|
|
620
|
-
var
|
|
671
|
+
var JSDOM3 = null;
|
|
672
|
+
var jsdomWarningShown3 = false;
|
|
673
|
+
function getJSDOM3() {
|
|
674
|
+
if (JSDOM3 !== null) {
|
|
675
|
+
return JSDOM3;
|
|
676
|
+
}
|
|
677
|
+
try {
|
|
678
|
+
JSDOM3 = require("jsdom").JSDOM;
|
|
679
|
+
return JSDOM3;
|
|
680
|
+
} catch (error) {
|
|
681
|
+
if (!jsdomWarningShown3) {
|
|
682
|
+
console.warn(
|
|
683
|
+
"[test-a11y-js] jsdom not found. Vue element conversion will be skipped. Install jsdom if you need Vue accessibility checks: npm install --save-dev jsdom"
|
|
684
|
+
);
|
|
685
|
+
jsdomWarningShown3 = true;
|
|
686
|
+
}
|
|
687
|
+
return null;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
621
690
|
function getVueAttributeValue(attr) {
|
|
622
691
|
if (!attr.value) {
|
|
623
692
|
return attr.key.name || attr.key.argument || null;
|
|
@@ -636,7 +705,11 @@ function vueElementToDOM(node, _context) {
|
|
|
636
705
|
return null;
|
|
637
706
|
}
|
|
638
707
|
const tagName = vueNode.name.toLowerCase();
|
|
639
|
-
const
|
|
708
|
+
const JSDOMClass = getJSDOM3();
|
|
709
|
+
if (!JSDOMClass) {
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
712
|
+
const dom = new JSDOMClass("<!DOCTYPE html><html><body></body></html>");
|
|
640
713
|
const element = dom.window.document.createElement(tagName);
|
|
641
714
|
if (vueNode.startTag?.attributes) {
|
|
642
715
|
for (const attr of vueNode.startTag.attributes) {
|
|
@@ -2273,7 +2346,7 @@ var dialog_modal_default = rule13;
|
|
|
2273
2346
|
var plugin = {
|
|
2274
2347
|
meta: {
|
|
2275
2348
|
name: "test-a11y-js",
|
|
2276
|
-
version: "0.
|
|
2349
|
+
version: "0.7.0"
|
|
2277
2350
|
},
|
|
2278
2351
|
rules: {
|
|
2279
2352
|
"image-alt": image_alt_default,
|
|
@@ -2291,6 +2364,10 @@ var plugin = {
|
|
|
2291
2364
|
"dialog-modal": dialog_modal_default
|
|
2292
2365
|
},
|
|
2293
2366
|
configs: {
|
|
2367
|
+
minimal: {
|
|
2368
|
+
plugins: ["test-a11y-js"],
|
|
2369
|
+
rules: minimal_default
|
|
2370
|
+
},
|
|
2294
2371
|
recommended: {
|
|
2295
2372
|
plugins: ["test-a11y-js"],
|
|
2296
2373
|
rules: recommended_default
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined")
|
|
5
|
+
return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
8
|
+
|
|
1
9
|
// src/core/a11y-checker.ts
|
|
2
10
|
var A11yChecker = class {
|
|
3
11
|
static checkImageAlt(element) {
|
|
@@ -397,6 +405,15 @@ ${index + 1}. ${violation.id} (${violation.impact})`);
|
|
|
397
405
|
}
|
|
398
406
|
};
|
|
399
407
|
|
|
408
|
+
// src/linter/eslint-plugin/configs/minimal.ts
|
|
409
|
+
var minimal = {
|
|
410
|
+
// Only critical/serious violations that block basic accessibility
|
|
411
|
+
"test-a11y-js/button-label": "error",
|
|
412
|
+
"test-a11y-js/form-label": "error",
|
|
413
|
+
"test-a11y-js/image-alt": "error"
|
|
414
|
+
};
|
|
415
|
+
var minimal_default = minimal;
|
|
416
|
+
|
|
400
417
|
// src/linter/eslint-plugin/configs/recommended.ts
|
|
401
418
|
var recommended = {
|
|
402
419
|
// Critical/Serious violations - set to error
|
|
@@ -451,7 +468,25 @@ var vue = {
|
|
|
451
468
|
var vue_default = vue;
|
|
452
469
|
|
|
453
470
|
// src/linter/eslint-plugin/utils/jsx-ast-utils.ts
|
|
454
|
-
|
|
471
|
+
var JSDOM = null;
|
|
472
|
+
var jsdomWarningShown = false;
|
|
473
|
+
function getJSDOM() {
|
|
474
|
+
if (JSDOM !== null) {
|
|
475
|
+
return JSDOM;
|
|
476
|
+
}
|
|
477
|
+
try {
|
|
478
|
+
JSDOM = __require("jsdom").JSDOM;
|
|
479
|
+
return JSDOM;
|
|
480
|
+
} catch (error) {
|
|
481
|
+
if (!jsdomWarningShown) {
|
|
482
|
+
console.warn(
|
|
483
|
+
"[test-a11y-js] jsdom not found. JSX element conversion will be skipped. Install jsdom if you need JSX accessibility checks: npm install --save-dev jsdom"
|
|
484
|
+
);
|
|
485
|
+
jsdomWarningShown = true;
|
|
486
|
+
}
|
|
487
|
+
return null;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
455
490
|
function getJSXAttributeValue(attr) {
|
|
456
491
|
if (!attr.value) {
|
|
457
492
|
return attr.name.name || null;
|
|
@@ -504,7 +539,11 @@ function jsxToElement(node, context) {
|
|
|
504
539
|
const openingElement = "openingElement" in jsxNode ? jsxNode.openingElement : jsxNode;
|
|
505
540
|
const tagName = getJSXTagName(openingElement);
|
|
506
541
|
const attributes = getJSXAttributes(openingElement);
|
|
507
|
-
const
|
|
542
|
+
const JSDOMClass = getJSDOM();
|
|
543
|
+
if (!JSDOMClass) {
|
|
544
|
+
throw new Error("jsdom is required for JSX element conversion");
|
|
545
|
+
}
|
|
546
|
+
const dom = new JSDOMClass("<!DOCTYPE html><html><body></body></html>");
|
|
508
547
|
const element = dom.window.document.createElement(tagName);
|
|
509
548
|
for (const [name, value] of attributes.entries()) {
|
|
510
549
|
element.setAttribute(name, value);
|
|
@@ -531,18 +570,38 @@ function hasJSXAttribute(element, name) {
|
|
|
531
570
|
return getJSXAttribute(element, name) !== void 0;
|
|
532
571
|
}
|
|
533
572
|
|
|
534
|
-
// src/linter/eslint-plugin/utils/html-ast-utils.ts
|
|
535
|
-
import { JSDOM as JSDOM2 } from "jsdom";
|
|
536
|
-
|
|
537
573
|
// src/linter/eslint-plugin/utils/ast-utils.ts
|
|
538
574
|
function isHTMLLiteral(node) {
|
|
539
575
|
return node.type === "TemplateLiteral" || node.type === "Literal" || node.type === "TaggedTemplateExpression" && node.tag.name === "html";
|
|
540
576
|
}
|
|
541
577
|
|
|
542
578
|
// src/linter/eslint-plugin/utils/html-ast-utils.ts
|
|
579
|
+
var JSDOM2 = null;
|
|
580
|
+
var jsdomWarningShown2 = false;
|
|
581
|
+
function getJSDOM2() {
|
|
582
|
+
if (JSDOM2 !== null) {
|
|
583
|
+
return JSDOM2;
|
|
584
|
+
}
|
|
585
|
+
try {
|
|
586
|
+
JSDOM2 = __require("jsdom").JSDOM;
|
|
587
|
+
return JSDOM2;
|
|
588
|
+
} catch (error) {
|
|
589
|
+
if (!jsdomWarningShown2) {
|
|
590
|
+
console.warn(
|
|
591
|
+
"[test-a11y-js] jsdom not found. HTML string parsing will be skipped. Install jsdom if you need HTML string accessibility checks: npm install --save-dev jsdom"
|
|
592
|
+
);
|
|
593
|
+
jsdomWarningShown2 = true;
|
|
594
|
+
}
|
|
595
|
+
return null;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
543
598
|
function parseHTMLString(html) {
|
|
599
|
+
const JSDOMClass = getJSDOM2();
|
|
600
|
+
if (!JSDOMClass) {
|
|
601
|
+
return null;
|
|
602
|
+
}
|
|
544
603
|
try {
|
|
545
|
-
const dom = new
|
|
604
|
+
const dom = new JSDOMClass(html, { contentType: "text/html" });
|
|
546
605
|
const body = dom.window.document.body;
|
|
547
606
|
if (body.children.length === 1) {
|
|
548
607
|
return body.children[0];
|
|
@@ -590,7 +649,25 @@ function htmlNodeToElement(node, context) {
|
|
|
590
649
|
}
|
|
591
650
|
|
|
592
651
|
// src/linter/eslint-plugin/utils/vue-ast-utils.ts
|
|
593
|
-
|
|
652
|
+
var JSDOM3 = null;
|
|
653
|
+
var jsdomWarningShown3 = false;
|
|
654
|
+
function getJSDOM3() {
|
|
655
|
+
if (JSDOM3 !== null) {
|
|
656
|
+
return JSDOM3;
|
|
657
|
+
}
|
|
658
|
+
try {
|
|
659
|
+
JSDOM3 = __require("jsdom").JSDOM;
|
|
660
|
+
return JSDOM3;
|
|
661
|
+
} catch (error) {
|
|
662
|
+
if (!jsdomWarningShown3) {
|
|
663
|
+
console.warn(
|
|
664
|
+
"[test-a11y-js] jsdom not found. Vue element conversion will be skipped. Install jsdom if you need Vue accessibility checks: npm install --save-dev jsdom"
|
|
665
|
+
);
|
|
666
|
+
jsdomWarningShown3 = true;
|
|
667
|
+
}
|
|
668
|
+
return null;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
594
671
|
function getVueAttributeValue(attr) {
|
|
595
672
|
if (!attr.value) {
|
|
596
673
|
return attr.key.name || attr.key.argument || null;
|
|
@@ -609,7 +686,11 @@ function vueElementToDOM(node, _context) {
|
|
|
609
686
|
return null;
|
|
610
687
|
}
|
|
611
688
|
const tagName = vueNode.name.toLowerCase();
|
|
612
|
-
const
|
|
689
|
+
const JSDOMClass = getJSDOM3();
|
|
690
|
+
if (!JSDOMClass) {
|
|
691
|
+
return null;
|
|
692
|
+
}
|
|
693
|
+
const dom = new JSDOMClass("<!DOCTYPE html><html><body></body></html>");
|
|
613
694
|
const element = dom.window.document.createElement(tagName);
|
|
614
695
|
if (vueNode.startTag?.attributes) {
|
|
615
696
|
for (const attr of vueNode.startTag.attributes) {
|
|
@@ -2246,7 +2327,7 @@ var dialog_modal_default = rule13;
|
|
|
2246
2327
|
var plugin = {
|
|
2247
2328
|
meta: {
|
|
2248
2329
|
name: "test-a11y-js",
|
|
2249
|
-
version: "0.
|
|
2330
|
+
version: "0.7.0"
|
|
2250
2331
|
},
|
|
2251
2332
|
rules: {
|
|
2252
2333
|
"image-alt": image_alt_default,
|
|
@@ -2264,6 +2345,10 @@ var plugin = {
|
|
|
2264
2345
|
"dialog-modal": dialog_modal_default
|
|
2265
2346
|
},
|
|
2266
2347
|
configs: {
|
|
2348
|
+
minimal: {
|
|
2349
|
+
plugins: ["test-a11y-js"],
|
|
2350
|
+
rules: minimal_default
|
|
2351
|
+
},
|
|
2267
2352
|
recommended: {
|
|
2268
2353
|
plugins: ["test-a11y-js"],
|
|
2269
2354
|
rules: recommended_default
|
|
@@ -24,6 +24,15 @@ __export(eslint_plugin_exports, {
|
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(eslint_plugin_exports);
|
|
26
26
|
|
|
27
|
+
// src/linter/eslint-plugin/configs/minimal.ts
|
|
28
|
+
var minimal = {
|
|
29
|
+
// Only critical/serious violations that block basic accessibility
|
|
30
|
+
"test-a11y-js/button-label": "error",
|
|
31
|
+
"test-a11y-js/form-label": "error",
|
|
32
|
+
"test-a11y-js/image-alt": "error"
|
|
33
|
+
};
|
|
34
|
+
var minimal_default = minimal;
|
|
35
|
+
|
|
27
36
|
// src/linter/eslint-plugin/configs/recommended.ts
|
|
28
37
|
var recommended = {
|
|
29
38
|
// Critical/Serious violations - set to error
|
|
@@ -477,7 +486,25 @@ ${index + 1}. ${violation.id} (${violation.impact})`);
|
|
|
477
486
|
};
|
|
478
487
|
|
|
479
488
|
// src/linter/eslint-plugin/utils/jsx-ast-utils.ts
|
|
480
|
-
var
|
|
489
|
+
var JSDOM = null;
|
|
490
|
+
var jsdomWarningShown = false;
|
|
491
|
+
function getJSDOM() {
|
|
492
|
+
if (JSDOM !== null) {
|
|
493
|
+
return JSDOM;
|
|
494
|
+
}
|
|
495
|
+
try {
|
|
496
|
+
JSDOM = require("jsdom").JSDOM;
|
|
497
|
+
return JSDOM;
|
|
498
|
+
} catch (error) {
|
|
499
|
+
if (!jsdomWarningShown) {
|
|
500
|
+
console.warn(
|
|
501
|
+
"[test-a11y-js] jsdom not found. JSX element conversion will be skipped. Install jsdom if you need JSX accessibility checks: npm install --save-dev jsdom"
|
|
502
|
+
);
|
|
503
|
+
jsdomWarningShown = true;
|
|
504
|
+
}
|
|
505
|
+
return null;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
481
508
|
function getJSXAttributeValue(attr) {
|
|
482
509
|
if (!attr.value) {
|
|
483
510
|
return attr.name.name || null;
|
|
@@ -530,7 +557,11 @@ function jsxToElement(node, context) {
|
|
|
530
557
|
const openingElement = "openingElement" in jsxNode ? jsxNode.openingElement : jsxNode;
|
|
531
558
|
const tagName = getJSXTagName(openingElement);
|
|
532
559
|
const attributes = getJSXAttributes(openingElement);
|
|
533
|
-
const
|
|
560
|
+
const JSDOMClass = getJSDOM();
|
|
561
|
+
if (!JSDOMClass) {
|
|
562
|
+
throw new Error("jsdom is required for JSX element conversion");
|
|
563
|
+
}
|
|
564
|
+
const dom = new JSDOMClass("<!DOCTYPE html><html><body></body></html>");
|
|
534
565
|
const element = dom.window.document.createElement(tagName);
|
|
535
566
|
for (const [name, value] of attributes.entries()) {
|
|
536
567
|
element.setAttribute(name, value);
|
|
@@ -557,18 +588,38 @@ function hasJSXAttribute(element, name) {
|
|
|
557
588
|
return getJSXAttribute(element, name) !== void 0;
|
|
558
589
|
}
|
|
559
590
|
|
|
560
|
-
// src/linter/eslint-plugin/utils/html-ast-utils.ts
|
|
561
|
-
var import_jsdom2 = require("jsdom");
|
|
562
|
-
|
|
563
591
|
// src/linter/eslint-plugin/utils/ast-utils.ts
|
|
564
592
|
function isHTMLLiteral(node) {
|
|
565
593
|
return node.type === "TemplateLiteral" || node.type === "Literal" || node.type === "TaggedTemplateExpression" && node.tag.name === "html";
|
|
566
594
|
}
|
|
567
595
|
|
|
568
596
|
// src/linter/eslint-plugin/utils/html-ast-utils.ts
|
|
597
|
+
var JSDOM2 = null;
|
|
598
|
+
var jsdomWarningShown2 = false;
|
|
599
|
+
function getJSDOM2() {
|
|
600
|
+
if (JSDOM2 !== null) {
|
|
601
|
+
return JSDOM2;
|
|
602
|
+
}
|
|
603
|
+
try {
|
|
604
|
+
JSDOM2 = require("jsdom").JSDOM;
|
|
605
|
+
return JSDOM2;
|
|
606
|
+
} catch (error) {
|
|
607
|
+
if (!jsdomWarningShown2) {
|
|
608
|
+
console.warn(
|
|
609
|
+
"[test-a11y-js] jsdom not found. HTML string parsing will be skipped. Install jsdom if you need HTML string accessibility checks: npm install --save-dev jsdom"
|
|
610
|
+
);
|
|
611
|
+
jsdomWarningShown2 = true;
|
|
612
|
+
}
|
|
613
|
+
return null;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
569
616
|
function parseHTMLString(html) {
|
|
617
|
+
const JSDOMClass = getJSDOM2();
|
|
618
|
+
if (!JSDOMClass) {
|
|
619
|
+
return null;
|
|
620
|
+
}
|
|
570
621
|
try {
|
|
571
|
-
const dom = new
|
|
622
|
+
const dom = new JSDOMClass(html, { contentType: "text/html" });
|
|
572
623
|
const body = dom.window.document.body;
|
|
573
624
|
if (body.children.length === 1) {
|
|
574
625
|
return body.children[0];
|
|
@@ -616,7 +667,25 @@ function htmlNodeToElement(node, context) {
|
|
|
616
667
|
}
|
|
617
668
|
|
|
618
669
|
// src/linter/eslint-plugin/utils/vue-ast-utils.ts
|
|
619
|
-
var
|
|
670
|
+
var JSDOM3 = null;
|
|
671
|
+
var jsdomWarningShown3 = false;
|
|
672
|
+
function getJSDOM3() {
|
|
673
|
+
if (JSDOM3 !== null) {
|
|
674
|
+
return JSDOM3;
|
|
675
|
+
}
|
|
676
|
+
try {
|
|
677
|
+
JSDOM3 = require("jsdom").JSDOM;
|
|
678
|
+
return JSDOM3;
|
|
679
|
+
} catch (error) {
|
|
680
|
+
if (!jsdomWarningShown3) {
|
|
681
|
+
console.warn(
|
|
682
|
+
"[test-a11y-js] jsdom not found. Vue element conversion will be skipped. Install jsdom if you need Vue accessibility checks: npm install --save-dev jsdom"
|
|
683
|
+
);
|
|
684
|
+
jsdomWarningShown3 = true;
|
|
685
|
+
}
|
|
686
|
+
return null;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
620
689
|
function getVueAttributeValue(attr) {
|
|
621
690
|
if (!attr.value) {
|
|
622
691
|
return attr.key.name || attr.key.argument || null;
|
|
@@ -635,7 +704,11 @@ function vueElementToDOM(node, _context) {
|
|
|
635
704
|
return null;
|
|
636
705
|
}
|
|
637
706
|
const tagName = vueNode.name.toLowerCase();
|
|
638
|
-
const
|
|
707
|
+
const JSDOMClass = getJSDOM3();
|
|
708
|
+
if (!JSDOMClass) {
|
|
709
|
+
return null;
|
|
710
|
+
}
|
|
711
|
+
const dom = new JSDOMClass("<!DOCTYPE html><html><body></body></html>");
|
|
639
712
|
const element = dom.window.document.createElement(tagName);
|
|
640
713
|
if (vueNode.startTag?.attributes) {
|
|
641
714
|
for (const attr of vueNode.startTag.attributes) {
|
|
@@ -2272,7 +2345,7 @@ var dialog_modal_default = rule13;
|
|
|
2272
2345
|
var plugin = {
|
|
2273
2346
|
meta: {
|
|
2274
2347
|
name: "test-a11y-js",
|
|
2275
|
-
version: "0.
|
|
2348
|
+
version: "0.7.0"
|
|
2276
2349
|
},
|
|
2277
2350
|
rules: {
|
|
2278
2351
|
"image-alt": image_alt_default,
|
|
@@ -2290,6 +2363,10 @@ var plugin = {
|
|
|
2290
2363
|
"dialog-modal": dialog_modal_default
|
|
2291
2364
|
},
|
|
2292
2365
|
configs: {
|
|
2366
|
+
minimal: {
|
|
2367
|
+
plugins: ["test-a11y-js"],
|
|
2368
|
+
rules: minimal_default
|
|
2369
|
+
},
|
|
2293
2370
|
recommended: {
|
|
2294
2371
|
plugins: ["test-a11y-js"],
|
|
2295
2372
|
rules: recommended_default
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined")
|
|
5
|
+
return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
// src/linter/eslint-plugin/configs/minimal.ts
|
|
10
|
+
var minimal = {
|
|
11
|
+
// Only critical/serious violations that block basic accessibility
|
|
12
|
+
"test-a11y-js/button-label": "error",
|
|
13
|
+
"test-a11y-js/form-label": "error",
|
|
14
|
+
"test-a11y-js/image-alt": "error"
|
|
15
|
+
};
|
|
16
|
+
var minimal_default = minimal;
|
|
17
|
+
|
|
1
18
|
// src/linter/eslint-plugin/configs/recommended.ts
|
|
2
19
|
var recommended = {
|
|
3
20
|
// Critical/Serious violations - set to error
|
|
@@ -451,7 +468,25 @@ ${index + 1}. ${violation.id} (${violation.impact})`);
|
|
|
451
468
|
};
|
|
452
469
|
|
|
453
470
|
// src/linter/eslint-plugin/utils/jsx-ast-utils.ts
|
|
454
|
-
|
|
471
|
+
var JSDOM = null;
|
|
472
|
+
var jsdomWarningShown = false;
|
|
473
|
+
function getJSDOM() {
|
|
474
|
+
if (JSDOM !== null) {
|
|
475
|
+
return JSDOM;
|
|
476
|
+
}
|
|
477
|
+
try {
|
|
478
|
+
JSDOM = __require("jsdom").JSDOM;
|
|
479
|
+
return JSDOM;
|
|
480
|
+
} catch (error) {
|
|
481
|
+
if (!jsdomWarningShown) {
|
|
482
|
+
console.warn(
|
|
483
|
+
"[test-a11y-js] jsdom not found. JSX element conversion will be skipped. Install jsdom if you need JSX accessibility checks: npm install --save-dev jsdom"
|
|
484
|
+
);
|
|
485
|
+
jsdomWarningShown = true;
|
|
486
|
+
}
|
|
487
|
+
return null;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
455
490
|
function getJSXAttributeValue(attr) {
|
|
456
491
|
if (!attr.value) {
|
|
457
492
|
return attr.name.name || null;
|
|
@@ -504,7 +539,11 @@ function jsxToElement(node, context) {
|
|
|
504
539
|
const openingElement = "openingElement" in jsxNode ? jsxNode.openingElement : jsxNode;
|
|
505
540
|
const tagName = getJSXTagName(openingElement);
|
|
506
541
|
const attributes = getJSXAttributes(openingElement);
|
|
507
|
-
const
|
|
542
|
+
const JSDOMClass = getJSDOM();
|
|
543
|
+
if (!JSDOMClass) {
|
|
544
|
+
throw new Error("jsdom is required for JSX element conversion");
|
|
545
|
+
}
|
|
546
|
+
const dom = new JSDOMClass("<!DOCTYPE html><html><body></body></html>");
|
|
508
547
|
const element = dom.window.document.createElement(tagName);
|
|
509
548
|
for (const [name, value] of attributes.entries()) {
|
|
510
549
|
element.setAttribute(name, value);
|
|
@@ -531,18 +570,38 @@ function hasJSXAttribute(element, name) {
|
|
|
531
570
|
return getJSXAttribute(element, name) !== void 0;
|
|
532
571
|
}
|
|
533
572
|
|
|
534
|
-
// src/linter/eslint-plugin/utils/html-ast-utils.ts
|
|
535
|
-
import { JSDOM as JSDOM2 } from "jsdom";
|
|
536
|
-
|
|
537
573
|
// src/linter/eslint-plugin/utils/ast-utils.ts
|
|
538
574
|
function isHTMLLiteral(node) {
|
|
539
575
|
return node.type === "TemplateLiteral" || node.type === "Literal" || node.type === "TaggedTemplateExpression" && node.tag.name === "html";
|
|
540
576
|
}
|
|
541
577
|
|
|
542
578
|
// src/linter/eslint-plugin/utils/html-ast-utils.ts
|
|
579
|
+
var JSDOM2 = null;
|
|
580
|
+
var jsdomWarningShown2 = false;
|
|
581
|
+
function getJSDOM2() {
|
|
582
|
+
if (JSDOM2 !== null) {
|
|
583
|
+
return JSDOM2;
|
|
584
|
+
}
|
|
585
|
+
try {
|
|
586
|
+
JSDOM2 = __require("jsdom").JSDOM;
|
|
587
|
+
return JSDOM2;
|
|
588
|
+
} catch (error) {
|
|
589
|
+
if (!jsdomWarningShown2) {
|
|
590
|
+
console.warn(
|
|
591
|
+
"[test-a11y-js] jsdom not found. HTML string parsing will be skipped. Install jsdom if you need HTML string accessibility checks: npm install --save-dev jsdom"
|
|
592
|
+
);
|
|
593
|
+
jsdomWarningShown2 = true;
|
|
594
|
+
}
|
|
595
|
+
return null;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
543
598
|
function parseHTMLString(html) {
|
|
599
|
+
const JSDOMClass = getJSDOM2();
|
|
600
|
+
if (!JSDOMClass) {
|
|
601
|
+
return null;
|
|
602
|
+
}
|
|
544
603
|
try {
|
|
545
|
-
const dom = new
|
|
604
|
+
const dom = new JSDOMClass(html, { contentType: "text/html" });
|
|
546
605
|
const body = dom.window.document.body;
|
|
547
606
|
if (body.children.length === 1) {
|
|
548
607
|
return body.children[0];
|
|
@@ -590,7 +649,25 @@ function htmlNodeToElement(node, context) {
|
|
|
590
649
|
}
|
|
591
650
|
|
|
592
651
|
// src/linter/eslint-plugin/utils/vue-ast-utils.ts
|
|
593
|
-
|
|
652
|
+
var JSDOM3 = null;
|
|
653
|
+
var jsdomWarningShown3 = false;
|
|
654
|
+
function getJSDOM3() {
|
|
655
|
+
if (JSDOM3 !== null) {
|
|
656
|
+
return JSDOM3;
|
|
657
|
+
}
|
|
658
|
+
try {
|
|
659
|
+
JSDOM3 = __require("jsdom").JSDOM;
|
|
660
|
+
return JSDOM3;
|
|
661
|
+
} catch (error) {
|
|
662
|
+
if (!jsdomWarningShown3) {
|
|
663
|
+
console.warn(
|
|
664
|
+
"[test-a11y-js] jsdom not found. Vue element conversion will be skipped. Install jsdom if you need Vue accessibility checks: npm install --save-dev jsdom"
|
|
665
|
+
);
|
|
666
|
+
jsdomWarningShown3 = true;
|
|
667
|
+
}
|
|
668
|
+
return null;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
594
671
|
function getVueAttributeValue(attr) {
|
|
595
672
|
if (!attr.value) {
|
|
596
673
|
return attr.key.name || attr.key.argument || null;
|
|
@@ -609,7 +686,11 @@ function vueElementToDOM(node, _context) {
|
|
|
609
686
|
return null;
|
|
610
687
|
}
|
|
611
688
|
const tagName = vueNode.name.toLowerCase();
|
|
612
|
-
const
|
|
689
|
+
const JSDOMClass = getJSDOM3();
|
|
690
|
+
if (!JSDOMClass) {
|
|
691
|
+
return null;
|
|
692
|
+
}
|
|
693
|
+
const dom = new JSDOMClass("<!DOCTYPE html><html><body></body></html>");
|
|
613
694
|
const element = dom.window.document.createElement(tagName);
|
|
614
695
|
if (vueNode.startTag?.attributes) {
|
|
615
696
|
for (const attr of vueNode.startTag.attributes) {
|
|
@@ -2246,7 +2327,7 @@ var dialog_modal_default = rule13;
|
|
|
2246
2327
|
var plugin = {
|
|
2247
2328
|
meta: {
|
|
2248
2329
|
name: "test-a11y-js",
|
|
2249
|
-
version: "0.
|
|
2330
|
+
version: "0.7.0"
|
|
2250
2331
|
},
|
|
2251
2332
|
rules: {
|
|
2252
2333
|
"image-alt": image_alt_default,
|
|
@@ -2264,6 +2345,10 @@ var plugin = {
|
|
|
2264
2345
|
"dialog-modal": dialog_modal_default
|
|
2265
2346
|
},
|
|
2266
2347
|
configs: {
|
|
2348
|
+
minimal: {
|
|
2349
|
+
plugins: ["test-a11y-js"],
|
|
2350
|
+
rules: minimal_default
|
|
2351
|
+
},
|
|
2267
2352
|
recommended: {
|
|
2268
2353
|
plugins: ["test-a11y-js"],
|
|
2269
2354
|
rules: recommended_default
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "test-a11y-js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "A JavaScript library for testing component accessibility across multiple testing frameworks",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -64,8 +64,15 @@
|
|
|
64
64
|
"peerDependenciesMeta": {
|
|
65
65
|
"vue-eslint-parser": {
|
|
66
66
|
"optional": true
|
|
67
|
+
},
|
|
68
|
+
"jsdom": {
|
|
69
|
+
"optional": true,
|
|
70
|
+
"description": "Required only for HTML string parsing in ESLint rules"
|
|
67
71
|
}
|
|
68
72
|
},
|
|
73
|
+
"optionalDependencies": {
|
|
74
|
+
"jsdom": "^23.0.0"
|
|
75
|
+
},
|
|
69
76
|
"devDependencies": {
|
|
70
77
|
"@testing-library/dom": "^9.0.0",
|
|
71
78
|
"@types/node": "^18.15.0",
|