zemdomu 1.3.17 → 1.3.19

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 (85) hide show
  1. package/README.md +66 -54
  2. package/out/linter.js +9 -0
  3. package/out/project-linter.js +57 -3
  4. package/out/rule-codes.d.ts +3 -0
  5. package/out/rule-codes.js +3 -0
  6. package/out/rules/ariaValidAttrValue.d.ts +2 -0
  7. package/out/rules/ariaValidAttrValue.js +230 -0
  8. package/out/rules/requireAltText.js +441 -18
  9. package/out/rules/requireDocumentTitle.d.ts +2 -0
  10. package/out/rules/requireDocumentTitle.js +141 -0
  11. package/out/rules/requireHtmlLang.js +120 -7
  12. package/out/rules/requireLinkText.js +289 -52
  13. package/out/rules/requireSingleMain.d.ts +2 -0
  14. package/out/rules/requireSingleMain.js +68 -0
  15. package/package.json +12 -4
  16. package/out/src/cli.js +0 -111
  17. package/out/src/component-analyzer.js +0 -1435
  18. package/out/src/component-path-resolver.js +0 -262
  19. package/out/src/html-visitor.js +0 -13
  20. package/out/src/index.js +0 -32
  21. package/out/src/linter.js +0 -288
  22. package/out/src/performance-diagnostics.js +0 -47
  23. package/out/src/project-linter.js +0 -168
  24. package/out/src/rule-codes.js +0 -36
  25. package/out/src/rules/enforceHeadingOrder.js +0 -83
  26. package/out/src/rules/enforceListNesting.js +0 -43
  27. package/out/src/rules/noTabindexGreaterThanZero.js +0 -33
  28. package/out/src/rules/preventEmptyInlineTags.js +0 -85
  29. package/out/src/rules/preventZemdomuPlaceholders.js +0 -236
  30. package/out/src/rules/requireAltText.js +0 -82
  31. package/out/src/rules/requireAltTextJSX.js +0 -63
  32. package/out/src/rules/requireButtonText.js +0 -78
  33. package/out/src/rules/requireHrefOnAnchors.js +0 -77
  34. package/out/src/rules/requireHtmlLang.js +0 -72
  35. package/out/src/rules/requireIframeTitle.js +0 -69
  36. package/out/src/rules/requireImageInputAlt.js +0 -69
  37. package/out/src/rules/requireLabelForFormControls.js +0 -84
  38. package/out/src/rules/requireLinkText.js +0 -126
  39. package/out/src/rules/requireNavLinks.js +0 -94
  40. package/out/src/rules/requireSectionHeading.js +0 -48
  41. package/out/src/rules/requireTableCaption.js +0 -48
  42. package/out/src/rules/singleH1.js +0 -112
  43. package/out/src/rules/uniqueIds.js +0 -33
  44. package/out/src/rules/utils.js +0 -231
  45. package/out/src/sarif.js +0 -58
  46. package/out/src/simpleHtmlParser.js +0 -82
  47. package/out/src/utils/collectLocalDeps.js +0 -168
  48. package/out/src/utils/vue-sfc.js +0 -93
  49. package/out/tests/button-accessibility-jsx.test.js +0 -53
  50. package/out/tests/cli-custom-rule.test.js +0 -61
  51. package/out/tests/cli-performance.test.js +0 -35
  52. package/out/tests/cross-duplicate-ids-tsx.test.js +0 -36
  53. package/out/tests/cross-heading-reversed.test.js +0 -24
  54. package/out/tests/cross-list-nesting.test.js +0 -34
  55. package/out/tests/cross-section-heading.test.js +0 -32
  56. package/out/tests/crossComponent/Button.js +0 -7
  57. package/out/tests/crossComponent/Page.js +0 -11
  58. package/out/tests/crossComponent/Section.js +0 -11
  59. package/out/tests/crossComponent/SubSection.js +0 -11
  60. package/out/tests/crossComponent/cross-button-text.test.js +0 -31
  61. package/out/tests/crossComponent/cross-heading-order-alias.test.js +0 -31
  62. package/out/tests/crossComponent/cross-heading-order-entry-only.test.js +0 -31
  63. package/out/tests/crossComponent/cross-heading-order.test.js +0 -60
  64. package/out/tests/custom-rule-tsx.test.js +0 -24
  65. package/out/tests/edge-cases.test.js +0 -17
  66. package/out/tests/exports-helpers.test.js +0 -45
  67. package/out/tests/heading-order.test.js +0 -24
  68. package/out/tests/html-visitor.test.js +0 -29
  69. package/out/tests/img-alt-dynamic.test.js +0 -39
  70. package/out/tests/label-form-control-jsx.test.js +0 -29
  71. package/out/tests/link-href-text.test.js +0 -52
  72. package/out/tests/linter.test.js +0 -41
  73. package/out/tests/list-nesting-dynamic.test.js +0 -27
  74. package/out/tests/nav-links-components.test.js +0 -27
  75. package/out/tests/parse-error-multifile.test.js +0 -29
  76. package/out/tests/parse-error.test.js +0 -11
  77. package/out/tests/performance-diagnostics.test.js +0 -12
  78. package/out/tests/prevent-placeholders-jsx.test.js +0 -34
  79. package/out/tests/sarif-output.test.js +0 -15
  80. package/out/tests/section-heading-jsx.test.js +0 -32
  81. package/out/tests/single-h1-returns.test.js +0 -74
  82. package/out/tests/tsx-parse-error.test.js +0 -11
  83. package/out/tests/unique-ids-html.test.js +0 -19
  84. package/out/tests/unique-ids-tsx.test.js +0 -19
  85. package/out/tests/vue-support.test.js +0 -122
package/README.md CHANGED
@@ -1,15 +1,35 @@
1
1
  # ZemDomu Core
2
2
 
3
- Semantic HTML linting engine for clean, accessible, and SEO-friendly markup.
4
- This package provides the shared core logic used by the ZemDomu VS Code
5
- extension and the GitHub Action.
3
+ > The semantic rules engine behind ZemDomu.
6
4
 
7
- ## What it is
5
+ ZemDomu Core is the shared engine that powers the ZemDomu ecosystem. It parses
6
+ HTML, JSX, TSX, and Vue templates and returns semantic issues that affect
7
+ structure, accessibility, and search visibility.
8
8
 
9
- ZemDomu is a semantic-first linter that helps developers write better HTML and
10
- JSX by catching accessibility and structural issues. It parses `.html`, `.jsx`,
11
- `.tsx`, and `.vue` files and exposes a simple `lint()` function that returns
12
- semantic violations.
9
+ Most linters check syntax. ZemDomu checks meaning.
10
+
11
+ ## What It Is
12
+
13
+ ZemDomu Core is a semantic-first linting engine for modern frontend codebases.
14
+ It helps developers catch issues like missing landmarks, confusing heading
15
+ structure, unlabeled controls, weak semantic relationships, and cross-component
16
+ composition problems before those issues become late-stage audit findings.
17
+
18
+ This package provides the shared logic used by:
19
+
20
+ - the ZemDomu CLI
21
+ - the ZemDomu VS Code Extension
22
+ - the ZemDomu GitHub Action
23
+
24
+ ## Why ZemDomu
25
+
26
+ Compared with generic linters and scanner-only workflows, ZemDomu is designed
27
+ to keep semantic analysis practical in real component codebases.
28
+
29
+ - Cross-component analysis catches issues that only appear when components are composed.
30
+ - One shared rules engine powers editor, CLI, and CI behavior consistently.
31
+ - Diagnostics focus on semantic HTML, accessible naming, and document structure.
32
+ - Custom-rule support lets teams extend checks without rebuilding a lint stack.
13
33
 
14
34
  ## Features
15
35
 
@@ -20,7 +40,6 @@ semantic violations.
20
40
  - Command line interface with `--custom` and `--cross`.
21
41
  - Configurable rule severity (`error`, `warning`, `off`).
22
42
  - Performance diagnostics for profiling lint runs.
23
- - Shared by the extension and GitHub Action.
24
43
  - Simple API: `lint(content, options)`.
25
44
 
26
45
  ## Installation
@@ -44,14 +63,10 @@ console.log(results);
44
63
  // {
45
64
  // line: 0,
46
65
  // column: 0,
47
- // message: '<img> tag missing alt attribute',
48
- // rule: 'requireAltText'
66
+ // message: "<img> tag missing alt attribute",
67
+ // rule: "requireAltText"
49
68
  // }
50
69
  // ]
51
-
52
- // Custom rules can be supplied via the `customRules` option
53
- // const myRule = { name: 'demo', test: node => false, message: 'demo' };
54
- // lint(html, { customRules: [myRule] });
55
70
  ```
56
71
 
57
72
  ## API
@@ -64,6 +79,7 @@ console.log(results);
64
79
  - `options.rules`: severity settings for built-in rules.
65
80
  - `options.customRules`: array of additional rules.
66
81
  - `options.filePath`: optional source file path.
82
+ - `options.forceHtml`: treat input as HTML.
67
83
  - `options.perf`: attach a `PerformanceRecorder` instance.
68
84
 
69
85
  ### Example `LinterOptions`
@@ -78,7 +94,7 @@ interface LinterOptions {
78
94
  }
79
95
  ```
80
96
 
81
- Example enabling one rule as a warning:
97
+ Example enabling rule severities:
82
98
 
83
99
  ```ts
84
100
  const results = lint(html, {
@@ -97,11 +113,11 @@ interface LintResult {
97
113
  }
98
114
  ```
99
115
 
100
- ## CLI usage
116
+ ## CLI Usage
101
117
 
102
- Run the linter from the command line by installing the package globally or
118
+ Run the linter from the command line by installing the package globally or by
103
119
  using `npx`. Provide one or more glob patterns to specify the files to lint.
104
- Patterns may be separated by spaces, commas, or newlines:
120
+ Patterns may be separated by spaces, commas, or newlines.
105
121
 
106
122
  ```bash
107
123
  npx zemdomu "src/**/*.{html,jsx,tsx,vue}" --custom my-rule.js
@@ -110,27 +126,30 @@ npx zemdomu "src/**/*.html,src/**/*.jsx"
110
126
 
111
127
  Use `--custom` (or `-c`) to provide a path to a JavaScript or TypeScript module
112
128
  exporting a custom rule or array of rules. For safety, the CLI only accepts
113
- files inside a `./custom-rules` directory (relative to your current working
114
- directory). You can repeat `--custom` to load multiple rule files. Use `--cross`
115
- to enable cross component analysis.
129
+ files inside a `./custom-rules` directory relative to your current working
130
+ directory. You can repeat `--custom` to load multiple rule files.
131
+
132
+ Use `--cross` to enable cross-component analysis.
116
133
 
117
134
  Use `--perf` to emit a JSON timing report to stdout, and `--perf-slowest` to
118
135
  also print the slowest file and phase.
119
136
 
120
- ### Cross-component analysis
137
+ ## Cross-Component Analysis
121
138
 
122
- When analyzing JSX or Vue projects you can track `<h1>` usage or similar
123
- patterns across component boundaries. Instantiate `ProjectLinter` with the
139
+ When analyzing JSX or Vue projects you can track semantic issues across
140
+ component boundaries. Instantiate `ProjectLinter` with the
124
141
  `crossComponentAnalysis` option or pass `--cross` to the CLI. Use
125
- `crossComponentDepth` (or `--cross-depth`) to limit how deep component trees are
126
- traversed during analysis:
142
+ `crossComponentDepth` or `--cross-depth` to limit how deep component trees are
143
+ traversed during analysis.
127
144
 
128
145
  ```ts
129
146
  import { ProjectLinter } from "zemdomu";
147
+
130
148
  const linter = new ProjectLinter({
131
149
  crossComponentAnalysis: true,
132
- crossComponentDepth: 2
150
+ crossComponentDepth: 2,
133
151
  });
152
+
134
153
  await linter.lintFile("App.jsx");
135
154
  ```
136
155
 
@@ -138,23 +157,24 @@ await linter.lintFile("App.jsx");
138
157
  npx zemdomu "src/**/*.{jsx,tsx,vue}" --cross --cross-depth 2
139
158
  ```
140
159
 
141
- ### Performance diagnostics
160
+ ## Performance Diagnostics
142
161
 
143
- Attach a `PerformanceDiagnostics` recorder to gather timing information for
144
- each file and rule:
162
+ Attach a `PerformanceDiagnostics` recorder to gather timing information for each
163
+ file and rule.
145
164
 
146
165
  ```ts
147
166
  import { lint, PerformanceDiagnostics } from "zemdomu";
167
+
148
168
  const perf = new PerformanceDiagnostics();
149
169
  lint(code, { perf });
150
170
  console.log(perf.getAsJSON());
151
171
  ```
152
172
 
153
- ## Writing custom rules
173
+ ## Writing Custom Rules
154
174
 
155
- Custom rules are simple objects implementing the `Rule` interface. At minimum
156
- provide a `name`, a `test` function that returns `true` when a node violates the
157
- rule, and a `message` describing the problem:
175
+ Custom rules are simple objects implementing the `Rule` interface. At minimum,
176
+ provide a `name`, a `test` function that returns `true` when a node violates
177
+ the rule, and a `message` describing the problem.
158
178
 
159
179
  ```ts
160
180
  interface Rule {
@@ -177,13 +197,14 @@ Use it programmatically:
177
197
 
178
198
  ```ts
179
199
  import { lint } from "zemdomu";
200
+
180
201
  const results = lint("<foo></foo>", { customRules: [require("./my-rule")] });
181
202
  ```
182
203
 
183
- ### Helper utilities
204
+ ### Helper Utilities
184
205
 
185
206
  For more advanced rules you may need direct access to the parsed HTML or JSX
186
- AST. ZemDomu exposes a few helpers to make this easier:
207
+ AST. ZemDomu exposes helpers for traversal and attribute inspection:
187
208
 
188
209
  ```ts
189
210
  import {
@@ -201,13 +222,6 @@ import {
201
222
  } from "zemdomu";
202
223
  ```
203
224
 
204
- `parseHtml` returns the root `ElementNode`. The `visitHtml` function performs a
205
- simple depth-first traversal using an `HtmlVisitor` with optional `enter` and
206
- `exit` callbacks. Utility functions like `getAttr` and `getJsxAttr` help reading
207
- attributes. JSX helpers like `getJsxAttribute`, `getJsxAttributeState`, and
208
- `getJsxExpressionState` help interpret JSX attributes and expressions, while
209
- `getTag` resolves JSX element names.
210
-
211
225
  Or via the CLI:
212
226
 
213
227
  ```bash
@@ -217,9 +231,10 @@ npx zemdomu file.html --custom custom-rules/my-rule.js
217
231
  npx zemdomu "src/**/*.{html,jsx,tsx,vue}" --perf --perf-slowest
218
232
  ```
219
233
 
220
- There is a sample rule in `custom-rules/example-rule.js` you can copy and edit.
234
+ There is a sample rule in `custom-rules/example-rule.js` that you can copy and
235
+ edit.
221
236
 
222
- ## Local development (monorepo)
237
+ ## Local Development
223
238
 
224
239
  From the core package:
225
240
 
@@ -231,15 +246,12 @@ npm run build
231
246
 
232
247
  ## Links
233
248
 
234
- Development happens in a private monorepo; this repository is the public mirror
235
- for issues and updates.
236
-
237
- - NPM package: https://www.npmjs.com/package/zemdomu
238
- - Public mirror: https://github.com/Zemdomu/ZemDomu-core
239
- - Issues and suggestions: https://github.com/Zemdomu/ZemDomu-core/issues
249
+ - npm package: https://www.npmjs.com/package/zemdomu
250
+ - Website: https://zemdomu.dev/
251
+ - Issues and suggestions: https://github.com/ZemDomu/ZemDomu-core/issues
240
252
  - VS Code extension: https://marketplace.visualstudio.com/items?itemName=ZachariasErydBerlin.zemdomu
241
- - GitHub Action: https://github.com/Zemdomu/ZemDomu-action
253
+ - GitHub Action: https://github.com/ZemDomu/ZemDomu-action
242
254
 
243
255
  ## License
244
256
 
245
- MIT (c) 2025 Zacharias Eryd Berlin
257
+ MIT (c) 2025 Zacharias Eryd Berlin
package/out/linter.js CHANGED
@@ -25,6 +25,9 @@ const requireNavLinks_1 = __importDefault(require("./rules/requireNavLinks"));
25
25
  const uniqueIds_1 = __importDefault(require("./rules/uniqueIds"));
26
26
  const noTabindexGreaterThanZero_1 = __importDefault(require("./rules/noTabindexGreaterThanZero"));
27
27
  const preventZemdomuPlaceholders_1 = __importDefault(require("./rules/preventZemdomuPlaceholders"));
28
+ const requireDocumentTitle_1 = __importDefault(require("./rules/requireDocumentTitle"));
29
+ const requireSingleMain_1 = __importDefault(require("./rules/requireSingleMain"));
30
+ const ariaValidAttrValue_1 = __importDefault(require("./rules/ariaValidAttrValue"));
28
31
  const rule_codes_1 = require("./rule-codes");
29
32
  const builtInRules = {
30
33
  requireSectionHeading: requireSectionHeading_1.default,
@@ -45,6 +48,9 @@ const builtInRules = {
45
48
  uniqueIds: uniqueIds_1.default,
46
49
  noTabindexGreaterThanZero: noTabindexGreaterThanZero_1.default,
47
50
  preventZemdomuPlaceholders: preventZemdomuPlaceholders_1.default,
51
+ requireDocumentTitle: requireDocumentTitle_1.default,
52
+ requireSingleMain: requireSingleMain_1.default,
53
+ ariaValidAttrValue: ariaValidAttrValue_1.default,
48
54
  };
49
55
  const defaultOptions = {
50
56
  rules: {
@@ -66,6 +72,9 @@ const defaultOptions = {
66
72
  uniqueIds: "error",
67
73
  noTabindexGreaterThanZero: "warning",
68
74
  preventZemdomuPlaceholders: "warning",
75
+ requireDocumentTitle: "error",
76
+ requireSingleMain: "error",
77
+ ariaValidAttrValue: "error",
69
78
  },
70
79
  customRules: [],
71
80
  };
@@ -46,6 +46,55 @@ const component_path_resolver_1 = require("./component-path-resolver");
46
46
  const collectLocalDeps_1 = require("./utils/collectLocalDeps");
47
47
  const vue_sfc_1 = require("./utils/vue-sfc");
48
48
  const rule_codes_1 = require("./rule-codes");
49
+ const FRAMEWORK_HOST_DOCUMENT_RULES = [
50
+ "requireHtmlLang",
51
+ "requireDocumentTitle",
52
+ "requireSingleMain",
53
+ ];
54
+ function isHtmlFile(filePath) {
55
+ return /\.(html|htm)$/i.test(filePath);
56
+ }
57
+ function getHtmlTagAttribute(tag, name) {
58
+ var _a, _b, _c;
59
+ const match = new RegExp(`\\b${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+))`, "i").exec(tag);
60
+ return match ? (_c = (_b = (_a = match[1]) !== null && _a !== void 0 ? _a : match[2]) !== null && _b !== void 0 ? _b : match[3]) !== null && _c !== void 0 ? _c : "" : null;
61
+ }
62
+ function hasFrameworkMountPoint(content) {
63
+ return /<[a-z][\w:-]*\b(?=[^>]*\bid\s*=\s*["'](?:app|root|app-root|mount|__nuxt|__next|svelte)["'])[^>]*>/i.test(content);
64
+ }
65
+ function isFrameworkEntrySrc(src) {
66
+ const normalized = src.replace(/[?#].*$/, "").replace(/\\/g, "/");
67
+ return /(?:^|\/)(?:src\/)?(?:main|index|app)\.(?:[cm]?[jt]sx?|vue)$/i.test(normalized);
68
+ }
69
+ function hasFrameworkModuleEntry(content) {
70
+ const scriptRe = /<script\b[^>]*>/gi;
71
+ let match;
72
+ while ((match = scriptRe.exec(content))) {
73
+ const tag = match[0];
74
+ const type = getHtmlTagAttribute(tag, "type");
75
+ if (!type || type.toLowerCase() !== "module")
76
+ continue;
77
+ const src = getHtmlTagAttribute(tag, "src");
78
+ if (src && isFrameworkEntrySrc(src))
79
+ return true;
80
+ }
81
+ return /<script\b(?=[^>]*\btype\s*=\s*["']?module["']?)[^>]*>[\s\S]*?\b(?:createApp|createRoot|ReactDOM\.render|new\s+Vue)\s*\(/i.test(content);
82
+ }
83
+ function isFrameworkHostHtml(filePath, content) {
84
+ if (path_1.default.basename(filePath).toLowerCase() !== "index.html")
85
+ return false;
86
+ if (!hasFrameworkMountPoint(content))
87
+ return false;
88
+ return hasFrameworkModuleEntry(content);
89
+ }
90
+ function suppressRules(options, ruleNames) {
91
+ var _a;
92
+ const rules = { ...((_a = options.rules) !== null && _a !== void 0 ? _a : {}) };
93
+ for (const ruleName of ruleNames) {
94
+ rules[ruleName] = "off";
95
+ }
96
+ return { ...options, rules };
97
+ }
49
98
  class ProjectLinter {
50
99
  constructor(options = {}) {
51
100
  var _a;
@@ -61,16 +110,21 @@ class ProjectLinter {
61
110
  if (!content) {
62
111
  content = await fs.readFile(filePath, "utf8");
63
112
  }
64
- const isVue = path_1.default.extname(filePath).toLowerCase() === ".vue";
113
+ const ext = path_1.default.extname(filePath).toLowerCase();
114
+ const isVue = ext === ".vue";
115
+ const isHtml = isHtmlFile(filePath);
65
116
  let lintContent = content;
66
117
  if (isVue) {
67
118
  const template = (0, vue_sfc_1.extractVueTemplate)(content);
68
119
  lintContent = (0, vue_sfc_1.isHtmlVueTemplate)(template) ? template.content : "";
69
120
  }
121
+ const lintOptions = isHtml && isFrameworkHostHtml(filePath, content)
122
+ ? suppressRules(this.opts, FRAMEWORK_HOST_DOCUMENT_RULES)
123
+ : this.opts;
70
124
  const results = (0, linter_1.lint)(lintContent, {
71
- ...this.opts,
125
+ ...lintOptions,
72
126
  filePath,
73
- forceHtml: isVue || this.opts.forceHtml,
127
+ forceHtml: isHtml || isVue || this.opts.forceHtml,
74
128
  });
75
129
  const resolvedResults = results.map((result) => result.filePath ? result : { ...result, filePath });
76
130
  const byFile = new Map();
@@ -17,6 +17,9 @@ declare const RULE_CODES: {
17
17
  readonly uniqueIds: "ZMD016";
18
18
  readonly noTabindexGreaterThanZero: "ZMD017";
19
19
  readonly preventZemdomuPlaceholders: "ZMD018";
20
+ readonly requireDocumentTitle: "ZMD019";
21
+ readonly requireSingleMain: "ZMD020";
22
+ readonly ariaValidAttrValue: "ZMD021";
20
23
  };
21
24
  export declare function getRuleCode(rule: string): string | undefined;
22
25
  export declare function applyRuleCode<T extends {
package/out/rule-codes.js CHANGED
@@ -22,6 +22,9 @@ const RULE_CODES = {
22
22
  uniqueIds: "ZMD016",
23
23
  noTabindexGreaterThanZero: "ZMD017",
24
24
  preventZemdomuPlaceholders: "ZMD018",
25
+ requireDocumentTitle: "ZMD019",
26
+ requireSingleMain: "ZMD020",
27
+ ariaValidAttrValue: "ZMD021",
25
28
  };
26
29
  exports.RULE_CODES = RULE_CODES;
27
30
  function getRuleCode(rule) {
@@ -0,0 +1,2 @@
1
+ import { Rule } from "../linter";
2
+ export default function ariaValidAttrValue(): Rule;
@@ -0,0 +1,230 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.default = ariaValidAttrValue;
37
+ const t = __importStar(require("@babel/types"));
38
+ const BOOLEAN_ATTRS = new Set([
39
+ "aria-hidden",
40
+ "aria-expanded",
41
+ "aria-selected",
42
+ "aria-disabled",
43
+ "aria-required",
44
+ "aria-modal",
45
+ "aria-multiline",
46
+ "aria-multiselectable",
47
+ "aria-readonly",
48
+ "aria-busy",
49
+ "aria-atomic",
50
+ ]);
51
+ const TRISTATE_ATTRS = new Set(["aria-checked", "aria-pressed"]);
52
+ const NUMERIC_ATTRS = new Set([
53
+ "aria-level",
54
+ "aria-valuemin",
55
+ "aria-valuemax",
56
+ "aria-valuenow",
57
+ "aria-colindex",
58
+ "aria-rowindex",
59
+ "aria-colcount",
60
+ "aria-rowcount",
61
+ "aria-setsize",
62
+ "aria-posinset",
63
+ ]);
64
+ const IDREF_LIST_ATTRS = new Set([
65
+ "aria-labelledby",
66
+ "aria-describedby",
67
+ "aria-controls",
68
+ "aria-owns",
69
+ "aria-details",
70
+ "aria-errormessage",
71
+ "aria-flowto",
72
+ ]);
73
+ const TOKEN_ATTRS = {
74
+ "aria-current": new Set([
75
+ "page",
76
+ "step",
77
+ "location",
78
+ "date",
79
+ "time",
80
+ "true",
81
+ "false",
82
+ ]),
83
+ "aria-live": new Set(["off", "polite", "assertive"]),
84
+ "aria-sort": new Set(["none", "ascending", "descending", "other"]),
85
+ "aria-orientation": new Set(["horizontal", "vertical"]),
86
+ "aria-haspopup": new Set([
87
+ "false",
88
+ "true",
89
+ "menu",
90
+ "listbox",
91
+ "tree",
92
+ "grid",
93
+ "dialog",
94
+ ]),
95
+ "aria-autocomplete": new Set(["inline", "list", "both", "none"]),
96
+ "aria-invalid": new Set(["false", "true", "grammar", "spelling"]),
97
+ };
98
+ const MULTI_TOKEN_ATTRS = {
99
+ "aria-relevant": new Set(["additions", "removals", "text", "all"]),
100
+ };
101
+ function normalize(value) {
102
+ return value.trim().toLowerCase();
103
+ }
104
+ function isNumeric(value) {
105
+ if (!value.trim())
106
+ return false;
107
+ return Number.isFinite(Number(value));
108
+ }
109
+ function isNonEmptyIdRefList(value) {
110
+ const tokens = value
111
+ .split(/\s+/)
112
+ .map((token) => token.trim())
113
+ .filter(Boolean);
114
+ return tokens.length > 0;
115
+ }
116
+ function isSupportedAriaAttr(attr) {
117
+ return (BOOLEAN_ATTRS.has(attr) ||
118
+ TRISTATE_ATTRS.has(attr) ||
119
+ NUMERIC_ATTRS.has(attr) ||
120
+ IDREF_LIST_ATTRS.has(attr) ||
121
+ Object.prototype.hasOwnProperty.call(TOKEN_ATTRS, attr) ||
122
+ Object.prototype.hasOwnProperty.call(MULTI_TOKEN_ATTRS, attr));
123
+ }
124
+ function isValidAriaValue(attr, rawValue) {
125
+ const value = normalize(rawValue);
126
+ if (!isSupportedAriaAttr(attr))
127
+ return true;
128
+ if (!value)
129
+ return false;
130
+ if (BOOLEAN_ATTRS.has(attr)) {
131
+ return value === "true" || value === "false";
132
+ }
133
+ if (TRISTATE_ATTRS.has(attr)) {
134
+ return (value === "true" ||
135
+ value === "false" ||
136
+ value === "mixed" ||
137
+ value === "undefined");
138
+ }
139
+ if (NUMERIC_ATTRS.has(attr)) {
140
+ return isNumeric(value);
141
+ }
142
+ if (IDREF_LIST_ATTRS.has(attr)) {
143
+ return isNonEmptyIdRefList(rawValue);
144
+ }
145
+ const tokenSet = TOKEN_ATTRS[attr];
146
+ if (tokenSet) {
147
+ return tokenSet.has(value);
148
+ }
149
+ const multiTokenSet = MULTI_TOKEN_ATTRS[attr];
150
+ if (multiTokenSet) {
151
+ const tokens = value.split(/\s+/).filter(Boolean);
152
+ return tokens.length > 0 && tokens.every((token) => multiTokenSet.has(token));
153
+ }
154
+ return true;
155
+ }
156
+ function jsxStaticAriaValue(attr) {
157
+ if (!attr.value)
158
+ return { value: "", dynamic: false };
159
+ if (t.isStringLiteral(attr.value))
160
+ return { value: attr.value.value, dynamic: false };
161
+ if (!t.isJSXExpressionContainer(attr.value))
162
+ return { value: "", dynamic: true };
163
+ const expr = attr.value.expression;
164
+ if (t.isStringLiteral(expr))
165
+ return { value: expr.value, dynamic: false };
166
+ if (t.isBooleanLiteral(expr))
167
+ return { value: String(expr.value), dynamic: false };
168
+ if (t.isNumericLiteral(expr))
169
+ return { value: String(expr.value), dynamic: false };
170
+ if (t.isTemplateLiteral(expr) && expr.expressions.length === 0) {
171
+ const staticValue = expr.quasis.map((q) => { var _a; return (_a = q.value.cooked) !== null && _a !== void 0 ? _a : q.value.raw; }).join("");
172
+ return { value: staticValue, dynamic: false };
173
+ }
174
+ return { value: "", dynamic: true };
175
+ }
176
+ function invalidValueResult(attr, rawValue, line, column) {
177
+ return {
178
+ line,
179
+ column,
180
+ message: `ARIA attribute "${attr}" has invalid value "${rawValue}"`,
181
+ rule: "ariaValidAttrValue",
182
+ };
183
+ }
184
+ function ariaValidAttrValue() {
185
+ return {
186
+ name: "ariaValidAttrValue",
187
+ enterHtml(node) {
188
+ if (node.type !== "element")
189
+ return [];
190
+ const results = [];
191
+ for (const [rawName, rawValue] of Object.entries(node.attrs)) {
192
+ const name = rawName.toLowerCase();
193
+ if (name.startsWith(":aria-") || name.startsWith("v-bind:aria-"))
194
+ continue;
195
+ if (!name.startsWith("aria-"))
196
+ continue;
197
+ if (!isSupportedAriaAttr(name))
198
+ continue;
199
+ const value = String(rawValue !== null && rawValue !== void 0 ? rawValue : "");
200
+ if (!isValidAriaValue(name, value)) {
201
+ results.push(invalidValueResult(name, value, 0, 0));
202
+ }
203
+ }
204
+ return results;
205
+ },
206
+ enterJsx(path) {
207
+ var _a, _b, _c, _d, _e, _f, _g, _h;
208
+ const results = [];
209
+ const attrs = path.node.openingElement.attributes;
210
+ for (const attr of attrs) {
211
+ if (!t.isJSXAttribute(attr) || !t.isJSXIdentifier(attr.name))
212
+ continue;
213
+ const name = attr.name.name.toLowerCase();
214
+ if (!name.startsWith("aria-"))
215
+ continue;
216
+ if (!isSupportedAriaAttr(name))
217
+ continue;
218
+ const { value, dynamic } = jsxStaticAriaValue(attr);
219
+ if (dynamic)
220
+ continue;
221
+ if (!isValidAriaValue(name, value)) {
222
+ const line = ((_d = (_b = (_a = attr.loc) === null || _a === void 0 ? void 0 : _a.start.line) !== null && _b !== void 0 ? _b : (_c = path.node.loc) === null || _c === void 0 ? void 0 : _c.start.line) !== null && _d !== void 0 ? _d : 1) - 1;
223
+ const column = (_h = (_f = (_e = attr.loc) === null || _e === void 0 ? void 0 : _e.start.column) !== null && _f !== void 0 ? _f : (_g = path.node.loc) === null || _g === void 0 ? void 0 : _g.start.column) !== null && _h !== void 0 ? _h : 0;
224
+ results.push(invalidValueResult(name, value, line, column));
225
+ }
226
+ }
227
+ return results;
228
+ },
229
+ };
230
+ }