@zigrivers/surface-grounding 0.1.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/LICENSE +22 -0
- package/README.md +15 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.js +390 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ken Allred
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Surface Grounding
|
|
2
|
+
|
|
3
|
+
Measured grounding tools for Surface audits.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @zigrivers/surface-grounding
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
See the repository README for usage and release guidance: https://github.com/zigrivers/surface#readme
|
|
12
|
+
|
|
13
|
+
## License
|
|
14
|
+
|
|
15
|
+
MIT.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Result, SurfaceError } from '@zigrivers/surface-core';
|
|
2
|
+
import { Capture, GroundingTool, SourceFileRef, ToolResult } from '@zigrivers/surface-core/interfaces';
|
|
3
|
+
|
|
4
|
+
declare const AXE_GROUNDING_ID = "axe";
|
|
5
|
+
declare const JSX_A11Y_GROUNDING_ID = "eslint-jsx-a11y";
|
|
6
|
+
declare const LIGHTHOUSE_GROUNDING_ID = "lighthouse";
|
|
7
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
8
|
+
interface AxeNodeResult {
|
|
9
|
+
readonly target?: readonly string[];
|
|
10
|
+
readonly failureSummary?: string;
|
|
11
|
+
readonly any?: readonly AxeCheckResult[];
|
|
12
|
+
readonly all?: readonly AxeCheckResult[];
|
|
13
|
+
readonly none?: readonly AxeCheckResult[];
|
|
14
|
+
}
|
|
15
|
+
interface AxeCheckResult {
|
|
16
|
+
readonly message?: string;
|
|
17
|
+
}
|
|
18
|
+
interface AxeViolation {
|
|
19
|
+
readonly id: string;
|
|
20
|
+
readonly tags?: readonly string[];
|
|
21
|
+
readonly help?: string;
|
|
22
|
+
readonly description?: string;
|
|
23
|
+
readonly nodes?: readonly AxeNodeResult[];
|
|
24
|
+
}
|
|
25
|
+
interface AxeRunResult {
|
|
26
|
+
readonly violations?: readonly AxeViolation[];
|
|
27
|
+
}
|
|
28
|
+
interface AxeGroundingTool extends GroundingTool {
|
|
29
|
+
readonly id: typeof AXE_GROUNDING_ID;
|
|
30
|
+
}
|
|
31
|
+
interface AxeGroundingOptions {
|
|
32
|
+
readonly runAxe: (capture: Capture) => MaybePromise<AxeRunResult>;
|
|
33
|
+
}
|
|
34
|
+
interface JsxA11yGroundingTool extends GroundingTool {
|
|
35
|
+
readonly id: typeof JSX_A11Y_GROUNDING_ID;
|
|
36
|
+
}
|
|
37
|
+
interface JsxA11yGroundingOptions {
|
|
38
|
+
readonly sources: readonly SourceFileRef[];
|
|
39
|
+
}
|
|
40
|
+
interface LighthouseRunnerResult {
|
|
41
|
+
readonly lhr?: LighthouseResult;
|
|
42
|
+
}
|
|
43
|
+
interface LighthouseResult {
|
|
44
|
+
readonly audits?: Readonly<Record<string, unknown>>;
|
|
45
|
+
readonly categories?: Readonly<Record<string, unknown>>;
|
|
46
|
+
}
|
|
47
|
+
interface LighthouseGroundingTool extends GroundingTool {
|
|
48
|
+
readonly id: typeof LIGHTHOUSE_GROUNDING_ID;
|
|
49
|
+
}
|
|
50
|
+
interface LighthouseGroundingOptions {
|
|
51
|
+
readonly runLighthouse?: (capture: Capture) => MaybePromise<LighthouseRunnerResult | undefined>;
|
|
52
|
+
readonly chromeLaunchOptions?: Readonly<Record<string, unknown>>;
|
|
53
|
+
readonly importLighthouse?: () => Promise<LighthouseModule>;
|
|
54
|
+
readonly importChromeLauncher?: () => Promise<ChromeLauncherModule>;
|
|
55
|
+
readonly lighthouseOptions?: Readonly<Record<string, unknown>>;
|
|
56
|
+
}
|
|
57
|
+
interface ChromeLauncherModule {
|
|
58
|
+
readonly launch?: (options?: Readonly<Record<string, unknown>>) => Promise<ChromeInstance>;
|
|
59
|
+
}
|
|
60
|
+
interface ChromeInstance {
|
|
61
|
+
readonly port: number;
|
|
62
|
+
kill(): MaybePromise<void>;
|
|
63
|
+
}
|
|
64
|
+
interface LighthouseModule {
|
|
65
|
+
readonly default?: LighthouseRunner;
|
|
66
|
+
readonly lighthouse?: LighthouseRunner;
|
|
67
|
+
}
|
|
68
|
+
type LighthouseRunner = (url: string, options?: Readonly<Record<string, unknown>>) => MaybePromise<LighthouseRunnerResult | undefined>;
|
|
69
|
+
declare function createAxeGroundingTool(options: AxeGroundingOptions): AxeGroundingTool;
|
|
70
|
+
declare function createLighthouseGroundingTool(options?: LighthouseGroundingOptions): LighthouseGroundingTool;
|
|
71
|
+
declare function axeResultToToolResults(result: AxeRunResult | null | undefined): ToolResult[];
|
|
72
|
+
declare function lighthouseResultToToolResults(result: LighthouseRunnerResult | null | undefined): ToolResult[];
|
|
73
|
+
declare function createJsxA11yGroundingTool(options: JsxA11yGroundingOptions): JsxA11yGroundingTool;
|
|
74
|
+
declare function runJsxA11yStaticPass(sources: readonly SourceFileRef[]): Promise<Result<ToolResult[], SurfaceError>>;
|
|
75
|
+
|
|
76
|
+
export { AXE_GROUNDING_ID, type AxeCheckResult, type AxeGroundingOptions, type AxeGroundingTool, type AxeNodeResult, type AxeRunResult, type AxeViolation, JSX_A11Y_GROUNDING_ID, type JsxA11yGroundingOptions, type JsxA11yGroundingTool, LIGHTHOUSE_GROUNDING_ID, type LighthouseGroundingOptions, type LighthouseGroundingTool, type LighthouseResult, type LighthouseRunnerResult, axeResultToToolResults, createAxeGroundingTool, createJsxA11yGroundingTool, createLighthouseGroundingTool, lighthouseResultToToolResults, runJsxA11yStaticPass };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { ESLint } from "eslint";
|
|
3
|
+
import jsxA11y from "eslint-plugin-jsx-a11y";
|
|
4
|
+
import tseslint from "typescript-eslint";
|
|
5
|
+
import {
|
|
6
|
+
LIGHTHOUSE_ACCESSIBILITY_AUDIT_IDS,
|
|
7
|
+
createSurfaceError
|
|
8
|
+
} from "@zigrivers/surface-core";
|
|
9
|
+
var AXE_GROUNDING_ID = "axe";
|
|
10
|
+
var JSX_A11Y_GROUNDING_ID = "eslint-jsx-a11y";
|
|
11
|
+
var LIGHTHOUSE_GROUNDING_ID = "lighthouse";
|
|
12
|
+
var AXE_WCAG_AA_THRESHOLD = "WCAG 2.2 AA";
|
|
13
|
+
var JSX_A11Y_THRESHOLD = "eslint-plugin-jsx-a11y recommended";
|
|
14
|
+
var JSX_A11Y_RULE_PREFIX = "jsx-a11y/";
|
|
15
|
+
var LIGHTHOUSE_AUDIT_THRESHOLD = "Lighthouse audit score 1";
|
|
16
|
+
var LIGHTHOUSE_GROUNDING_CATEGORIES = ["accessibility", "performance"];
|
|
17
|
+
var LIGHTHOUSE_ACCESSIBILITY_AUDIT_ID_SET = new Set(LIGHTHOUSE_ACCESSIBILITY_AUDIT_IDS);
|
|
18
|
+
var HTTP_URL_SCHEME_PATTERN = /^https?:\/\//iu;
|
|
19
|
+
var SOURCE_EXTENSIONS = [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".mts", ".cts"];
|
|
20
|
+
var CONTRAST_MEASURED_PATTERN = /color contrast of\s+(\d+(?:\.\d+)?)/iu;
|
|
21
|
+
var CONTRAST_THRESHOLD_PATTERN = /expected contrast ratio of\s+(\d+(?:\.\d+)?)\s*:1/iu;
|
|
22
|
+
var CONTRAST_RATIO_PATTERN = /(\d+(?:\.\d+)?)\s*:1/gu;
|
|
23
|
+
function createAxeGroundingTool(options) {
|
|
24
|
+
return {
|
|
25
|
+
id: AXE_GROUNDING_ID,
|
|
26
|
+
async run(capture) {
|
|
27
|
+
try {
|
|
28
|
+
return { ok: true, value: axeResultToToolResults(await options.runAxe(capture)) };
|
|
29
|
+
} catch (cause) {
|
|
30
|
+
return {
|
|
31
|
+
ok: false,
|
|
32
|
+
error: createSurfaceError("step_failed", "Failed to run axe grounding.", { cause })
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function createLighthouseGroundingTool(options = {}) {
|
|
39
|
+
return {
|
|
40
|
+
id: LIGHTHOUSE_GROUNDING_ID,
|
|
41
|
+
async run(capture) {
|
|
42
|
+
try {
|
|
43
|
+
const result = options.runLighthouse === void 0 ? await runLighthouseCapture(capture, options) : await options.runLighthouse(capture);
|
|
44
|
+
return { ok: true, value: lighthouseResultToToolResults(result) };
|
|
45
|
+
} catch (cause) {
|
|
46
|
+
return {
|
|
47
|
+
ok: false,
|
|
48
|
+
error: createSurfaceError("step_failed", "Failed to run lighthouse grounding.", {
|
|
49
|
+
cause
|
|
50
|
+
})
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function axeResultToToolResults(result) {
|
|
57
|
+
const evidence = axeViolations(result).flatMap((violation) => evidenceForAxeViolation(violation)).map((item, index) => ({ index, item })).sort(
|
|
58
|
+
(left, right) => accessibilityAuditSortPriority(left.item.rule) - accessibilityAuditSortPriority(right.item.rule) || compareStableStrings(
|
|
59
|
+
`${left.item.rule}\0${left.item.measuredValue}`,
|
|
60
|
+
`${right.item.rule}\0${right.item.measuredValue}`
|
|
61
|
+
) || left.index - right.index
|
|
62
|
+
).map(({ item }) => item);
|
|
63
|
+
return evidence.length === 0 ? [] : [{ tool: AXE_GROUNDING_ID, evidence }];
|
|
64
|
+
}
|
|
65
|
+
function lighthouseResultToToolResults(result) {
|
|
66
|
+
const lhr = isRecord(result?.lhr) ? result.lhr : void 0;
|
|
67
|
+
const audits = recordOrUndefined(lhr?.audits);
|
|
68
|
+
if (lhr === void 0 || audits === void 0) {
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
const evidence = lighthouseAuditIds(lhr).flatMap((id) => {
|
|
72
|
+
const audit = recordOrUndefined(audits[id]);
|
|
73
|
+
return audit === void 0 ? [] : evidenceForLighthouseAudit(id, audit);
|
|
74
|
+
}).map((item, index) => ({ index, item })).sort(
|
|
75
|
+
(left, right) => compareStableStrings(
|
|
76
|
+
`${left.item.rule}\0${left.item.measuredValue}`,
|
|
77
|
+
`${right.item.rule}\0${right.item.measuredValue}`
|
|
78
|
+
) || left.index - right.index
|
|
79
|
+
).map(({ item }) => item);
|
|
80
|
+
return evidence.length === 0 ? [] : [{ tool: LIGHTHOUSE_GROUNDING_ID, evidence }];
|
|
81
|
+
}
|
|
82
|
+
function accessibilityAuditSortPriority(rule) {
|
|
83
|
+
return LIGHTHOUSE_ACCESSIBILITY_AUDIT_ID_SET.has(rule) ? 0 : 1;
|
|
84
|
+
}
|
|
85
|
+
function createJsxA11yGroundingTool(options) {
|
|
86
|
+
const sources = [...options.sources];
|
|
87
|
+
return {
|
|
88
|
+
id: JSX_A11Y_GROUNDING_ID,
|
|
89
|
+
run: () => runJsxA11yStaticPass(sources)
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
async function runLighthouseCapture(capture, options) {
|
|
93
|
+
const url = lighthouseUrlForCapture(capture);
|
|
94
|
+
if (url === void 0) {
|
|
95
|
+
throw new Error("Lighthouse grounding requires an HTTP(S) URL or localhost capture target.");
|
|
96
|
+
}
|
|
97
|
+
const module = await (options.importLighthouse ?? defaultImportLighthouse)();
|
|
98
|
+
const lighthouse = module.default ?? module.lighthouse;
|
|
99
|
+
if (typeof lighthouse !== "function") {
|
|
100
|
+
throw new Error("lighthouse module did not expose a callable runner.");
|
|
101
|
+
}
|
|
102
|
+
const chromeLauncher = await (options.importChromeLauncher ?? defaultImportChromeLauncher)();
|
|
103
|
+
if (typeof chromeLauncher.launch !== "function") {
|
|
104
|
+
throw new Error("chrome-launcher module did not expose a launch function.");
|
|
105
|
+
}
|
|
106
|
+
const chrome = await chromeLauncher.launch(options.chromeLaunchOptions);
|
|
107
|
+
try {
|
|
108
|
+
return await lighthouse(url, { ...options.lighthouseOptions, port: chrome.port });
|
|
109
|
+
} finally {
|
|
110
|
+
await killChromeSafely(chrome);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async function defaultImportLighthouse() {
|
|
114
|
+
return await importOptionalModule("lighthouse");
|
|
115
|
+
}
|
|
116
|
+
async function defaultImportChromeLauncher() {
|
|
117
|
+
return await importOptionalModule("chrome-launcher");
|
|
118
|
+
}
|
|
119
|
+
async function importOptionalModule(specifier) {
|
|
120
|
+
return import(specifier);
|
|
121
|
+
}
|
|
122
|
+
async function killChromeSafely(chrome) {
|
|
123
|
+
try {
|
|
124
|
+
await chrome.kill();
|
|
125
|
+
} catch {
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function lighthouseUrlForCapture(capture) {
|
|
129
|
+
if (capture.target.kind === "url") {
|
|
130
|
+
return HTTP_URL_SCHEME_PATTERN.test(capture.target.ref) ? capture.target.ref : void 0;
|
|
131
|
+
}
|
|
132
|
+
if (capture.target.kind === "localhost") {
|
|
133
|
+
return HTTP_URL_SCHEME_PATTERN.test(capture.target.ref) ? capture.target.ref : `http://${capture.target.ref}`;
|
|
134
|
+
}
|
|
135
|
+
return void 0;
|
|
136
|
+
}
|
|
137
|
+
function evidenceForAxeViolation(violation) {
|
|
138
|
+
return axeNodes(violation).map((node) => {
|
|
139
|
+
const selector = selectorForAxeNode(node);
|
|
140
|
+
const summary = summaryForAxeNode(violation, node);
|
|
141
|
+
const contrast = contrastResult(summary);
|
|
142
|
+
const threshold = violation.id === "color-contrast" && contrast.threshold !== void 0 ? `${contrast.threshold}:1 (${AXE_WCAG_AA_THRESHOLD})` : void 0;
|
|
143
|
+
return {
|
|
144
|
+
kind: "tool-result",
|
|
145
|
+
tool: AXE_GROUNDING_ID,
|
|
146
|
+
rule: violation.id,
|
|
147
|
+
measuredValue: violation.id === "color-contrast" && contrast.measured !== void 0 ? `${selector}: ${contrast.measured}:1` : `${selector}: ${summary}`,
|
|
148
|
+
...threshold === void 0 ? {} : { threshold }
|
|
149
|
+
};
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
function evidenceForLighthouseAudit(id, audit) {
|
|
153
|
+
const score = lighthouseAuditScore(audit);
|
|
154
|
+
if (score === void 0 || score >= 1) {
|
|
155
|
+
return [];
|
|
156
|
+
}
|
|
157
|
+
const measured = lighthouseAuditMeasuredValue(id, audit, score);
|
|
158
|
+
return lighthouseAuditAnchors(id, audit).map((anchor) => ({
|
|
159
|
+
kind: "tool-result",
|
|
160
|
+
tool: LIGHTHOUSE_GROUNDING_ID,
|
|
161
|
+
rule: id,
|
|
162
|
+
measuredValue: `${anchor}: ${measured}`,
|
|
163
|
+
threshold: LIGHTHOUSE_AUDIT_THRESHOLD
|
|
164
|
+
}));
|
|
165
|
+
}
|
|
166
|
+
function selectorForAxeNode(node) {
|
|
167
|
+
return Array.isArray(node.target) && node.target.length > 0 && node.target.every(isString) ? node.target.join(" ") : "unknown-target";
|
|
168
|
+
}
|
|
169
|
+
function summaryForAxeNode(violation, node) {
|
|
170
|
+
return normalizeWhitespace(
|
|
171
|
+
stringOrUndefined(node.failureSummary) ?? firstCheckMessage(node.any) ?? firstCheckMessage(node.all) ?? firstCheckMessage(node.none) ?? stringOrUndefined(violation.help) ?? stringOrUndefined(violation.description) ?? violation.id
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
function firstCheckMessage(checks) {
|
|
175
|
+
if (!Array.isArray(checks)) {
|
|
176
|
+
return void 0;
|
|
177
|
+
}
|
|
178
|
+
for (const check of checks) {
|
|
179
|
+
if (isRecord(check)) {
|
|
180
|
+
const message = check["message"];
|
|
181
|
+
if (typeof message === "string") {
|
|
182
|
+
return message;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return void 0;
|
|
187
|
+
}
|
|
188
|
+
function contrastResult(value) {
|
|
189
|
+
const measured = value.match(CONTRAST_MEASURED_PATTERN)?.[1];
|
|
190
|
+
const threshold = value.match(CONTRAST_THRESHOLD_PATTERN)?.[1];
|
|
191
|
+
if (measured !== void 0 || threshold !== void 0) {
|
|
192
|
+
return { measured, threshold };
|
|
193
|
+
}
|
|
194
|
+
const ratios = [...value.matchAll(CONTRAST_RATIO_PATTERN)].map((match) => match[1]).filter(isString);
|
|
195
|
+
return { measured: ratios[0], threshold: ratios[1] };
|
|
196
|
+
}
|
|
197
|
+
function lighthouseAuditIds(lhr) {
|
|
198
|
+
const categories = recordOrUndefined(lhr.categories);
|
|
199
|
+
const fromCategories = categories === void 0 ? [] : LIGHTHOUSE_GROUNDING_CATEGORIES.flatMap((categoryId) => {
|
|
200
|
+
const category = recordOrUndefined(categories[categoryId]);
|
|
201
|
+
const auditRefs = arrayOrEmpty(category?.["auditRefs"]);
|
|
202
|
+
return auditRefs.map((auditRef) => stringOrUndefined(recordOrUndefined(auditRef)?.["id"])).filter(isString);
|
|
203
|
+
});
|
|
204
|
+
if (fromCategories.length > 0) {
|
|
205
|
+
return knownAccessibilityAuditIdsFirst(uniqueStrings(fromCategories));
|
|
206
|
+
}
|
|
207
|
+
const auditIds = Object.keys(recordOrUndefined(lhr.audits) ?? {});
|
|
208
|
+
return knownAccessibilityAuditIdsFirst(auditIds);
|
|
209
|
+
}
|
|
210
|
+
function knownAccessibilityAuditIdsFirst(auditIds) {
|
|
211
|
+
const knownAccessibilityAuditIds = auditIds.filter(
|
|
212
|
+
(id) => LIGHTHOUSE_ACCESSIBILITY_AUDIT_ID_SET.has(id)
|
|
213
|
+
);
|
|
214
|
+
const otherAuditIds = auditIds.filter((id) => !LIGHTHOUSE_ACCESSIBILITY_AUDIT_ID_SET.has(id));
|
|
215
|
+
return [...knownAccessibilityAuditIds, ...otherAuditIds];
|
|
216
|
+
}
|
|
217
|
+
function lighthouseAuditScore(audit) {
|
|
218
|
+
const score = audit["score"];
|
|
219
|
+
return typeof score === "number" && Number.isFinite(score) ? score : void 0;
|
|
220
|
+
}
|
|
221
|
+
function lighthouseAuditAnchors(id, audit) {
|
|
222
|
+
const details = recordOrUndefined(audit["details"]);
|
|
223
|
+
const items = arrayOrEmpty(details?.["items"]);
|
|
224
|
+
const selectors = items.map(anchorForLighthouseDetailItem).filter(isNonEmptyString);
|
|
225
|
+
return selectors.length === 0 ? [id] : uniqueStrings(selectors);
|
|
226
|
+
}
|
|
227
|
+
function anchorForLighthouseDetailItem(item) {
|
|
228
|
+
const itemRecord = recordOrUndefined(item);
|
|
229
|
+
const node = recordOrUndefined(itemRecord?.["node"]) ?? itemRecord;
|
|
230
|
+
return stringOrUndefined(node?.["selector"]) ?? stringOrUndefined(node?.["path"]) ?? stringOrUndefined(node?.["snippet"]) ?? stringOrUndefined(node?.["nodeLabel"]) ?? rectAnchor(recordOrUndefined(node?.["boundingRect"]));
|
|
231
|
+
}
|
|
232
|
+
function rectAnchor(rect) {
|
|
233
|
+
if (rect === void 0) {
|
|
234
|
+
return void 0;
|
|
235
|
+
}
|
|
236
|
+
const x = finiteNumberOrUndefined(rect["left"] ?? rect["x"]);
|
|
237
|
+
const y = finiteNumberOrUndefined(rect["top"] ?? rect["y"]);
|
|
238
|
+
const width = finiteNumberOrUndefined(rect["width"]);
|
|
239
|
+
const height = finiteNumberOrUndefined(rect["height"]);
|
|
240
|
+
return x === void 0 || y === void 0 || width === void 0 || height === void 0 ? void 0 : `rect(${formatLighthouseScore(x)},${formatLighthouseScore(y)} ${formatLighthouseScore(width)}x${formatLighthouseScore(height)})`;
|
|
241
|
+
}
|
|
242
|
+
function lighthouseAuditMeasuredValue(id, audit, score) {
|
|
243
|
+
const display = stringOrUndefined(audit["displayValue"]) ?? stringOrUndefined(audit["title"]) ?? id;
|
|
244
|
+
return `${normalizeWhitespace(display)} (score ${formatLighthouseScore(score)})`;
|
|
245
|
+
}
|
|
246
|
+
function formatLighthouseScore(score) {
|
|
247
|
+
return Number.isInteger(score) ? String(score) : score.toFixed(3).replace(/0+$/u, "").replace(/\.$/u, "");
|
|
248
|
+
}
|
|
249
|
+
function normalizeWhitespace(value) {
|
|
250
|
+
return value.replace(/\s+/gu, " ").trim();
|
|
251
|
+
}
|
|
252
|
+
async function runJsxA11yStaticPass(sources) {
|
|
253
|
+
if (!sources.every(isSourceFileRef)) {
|
|
254
|
+
return {
|
|
255
|
+
ok: false,
|
|
256
|
+
error: createSurfaceError("step_failed", "SourceFileRef requires string path and contents.")
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
try {
|
|
260
|
+
const eslint = createEslint();
|
|
261
|
+
const evidence = (await Promise.all(
|
|
262
|
+
sources.filter((source) => supportsSource(source.path)).map(async (source) => {
|
|
263
|
+
const [result] = await eslint.lintText(source.contents, { filePath: source.path });
|
|
264
|
+
return result === void 0 ? [] : evidenceForResult(result);
|
|
265
|
+
})
|
|
266
|
+
)).flat().sort(
|
|
267
|
+
(left, right) => compareStableStrings(
|
|
268
|
+
`${left.rule}\0${left.measuredValue}`,
|
|
269
|
+
`${right.rule}\0${right.measuredValue}`
|
|
270
|
+
)
|
|
271
|
+
);
|
|
272
|
+
return {
|
|
273
|
+
ok: true,
|
|
274
|
+
value: evidence.length === 0 ? [] : [{ tool: JSX_A11Y_GROUNDING_ID, evidence }]
|
|
275
|
+
};
|
|
276
|
+
} catch (cause) {
|
|
277
|
+
return {
|
|
278
|
+
ok: false,
|
|
279
|
+
error: createSurfaceError("step_failed", "Failed to run eslint-jsx-a11y grounding.", {
|
|
280
|
+
cause
|
|
281
|
+
})
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
function createEslint() {
|
|
286
|
+
const plugin = jsxA11y;
|
|
287
|
+
const rules = jsxA11y.configs.recommended.rules;
|
|
288
|
+
return new ESLint({
|
|
289
|
+
overrideConfigFile: true,
|
|
290
|
+
overrideConfig: [
|
|
291
|
+
{
|
|
292
|
+
files: ["**/*.{js,jsx,mjs,cjs}"],
|
|
293
|
+
languageOptions: {
|
|
294
|
+
ecmaVersion: "latest",
|
|
295
|
+
sourceType: "module",
|
|
296
|
+
parserOptions: { ecmaFeatures: { jsx: true } }
|
|
297
|
+
},
|
|
298
|
+
plugins: { "jsx-a11y": plugin },
|
|
299
|
+
rules
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
files: ["**/*.{ts,tsx,mts,cts}"],
|
|
303
|
+
languageOptions: {
|
|
304
|
+
parser: tseslint.parser,
|
|
305
|
+
parserOptions: { ecmaFeatures: { jsx: true } },
|
|
306
|
+
ecmaVersion: "latest",
|
|
307
|
+
sourceType: "module"
|
|
308
|
+
},
|
|
309
|
+
plugins: { "jsx-a11y": plugin },
|
|
310
|
+
rules
|
|
311
|
+
}
|
|
312
|
+
]
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
function evidenceForResult(result) {
|
|
316
|
+
return result.messages.filter(
|
|
317
|
+
(message) => message.ruleId?.startsWith(JSX_A11Y_RULE_PREFIX) === true
|
|
318
|
+
).map((message) => ({
|
|
319
|
+
kind: "tool-result",
|
|
320
|
+
tool: JSX_A11Y_GROUNDING_ID,
|
|
321
|
+
rule: message.ruleId,
|
|
322
|
+
measuredValue: `${result.filePath}:${message.line}:${message.column} ${message.message}`,
|
|
323
|
+
threshold: JSX_A11Y_THRESHOLD
|
|
324
|
+
}));
|
|
325
|
+
}
|
|
326
|
+
function supportsSource(path) {
|
|
327
|
+
const lowerPath = path.toLowerCase();
|
|
328
|
+
return SOURCE_EXTENSIONS.some((extension) => lowerPath.endsWith(extension));
|
|
329
|
+
}
|
|
330
|
+
function isSourceFileRef(source) {
|
|
331
|
+
if (typeof source !== "object" || source === null) {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
const candidate = source;
|
|
335
|
+
return typeof candidate.path === "string" && typeof candidate.contents === "string";
|
|
336
|
+
}
|
|
337
|
+
function isString(value) {
|
|
338
|
+
return typeof value === "string";
|
|
339
|
+
}
|
|
340
|
+
function isNonEmptyString(value) {
|
|
341
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
342
|
+
}
|
|
343
|
+
function finiteNumberOrUndefined(value) {
|
|
344
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
345
|
+
}
|
|
346
|
+
function axeViolations(result) {
|
|
347
|
+
if (!isRecord(result) || !Array.isArray(result.violations)) {
|
|
348
|
+
return [];
|
|
349
|
+
}
|
|
350
|
+
return result.violations.filter(isAxeViolation);
|
|
351
|
+
}
|
|
352
|
+
function axeNodes(violation) {
|
|
353
|
+
return Array.isArray(violation.nodes) ? violation.nodes.filter(isRecord) : [];
|
|
354
|
+
}
|
|
355
|
+
function isAxeViolation(value) {
|
|
356
|
+
return isRecord(value) && typeof value.id === "string";
|
|
357
|
+
}
|
|
358
|
+
function isRecord(value) {
|
|
359
|
+
return typeof value === "object" && value !== null;
|
|
360
|
+
}
|
|
361
|
+
function recordOrUndefined(value) {
|
|
362
|
+
return isRecord(value) ? value : void 0;
|
|
363
|
+
}
|
|
364
|
+
function arrayOrEmpty(value) {
|
|
365
|
+
return Array.isArray(value) ? value : [];
|
|
366
|
+
}
|
|
367
|
+
function stringOrUndefined(value) {
|
|
368
|
+
return typeof value === "string" ? value : void 0;
|
|
369
|
+
}
|
|
370
|
+
function uniqueStrings(values) {
|
|
371
|
+
return [...new Set(values)];
|
|
372
|
+
}
|
|
373
|
+
function compareStableStrings(left, right) {
|
|
374
|
+
if (left < right) {
|
|
375
|
+
return -1;
|
|
376
|
+
}
|
|
377
|
+
return left > right ? 1 : 0;
|
|
378
|
+
}
|
|
379
|
+
export {
|
|
380
|
+
AXE_GROUNDING_ID,
|
|
381
|
+
JSX_A11Y_GROUNDING_ID,
|
|
382
|
+
LIGHTHOUSE_GROUNDING_ID,
|
|
383
|
+
axeResultToToolResults,
|
|
384
|
+
createAxeGroundingTool,
|
|
385
|
+
createJsxA11yGroundingTool,
|
|
386
|
+
createLighthouseGroundingTool,
|
|
387
|
+
lighthouseResultToToolResults,
|
|
388
|
+
runJsxA11yStaticPass
|
|
389
|
+
};
|
|
390
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { ESLint, type Linter } from \"eslint\";\nimport jsxA11y from \"eslint-plugin-jsx-a11y\";\nimport tseslint from \"typescript-eslint\";\n\nimport {\n LIGHTHOUSE_ACCESSIBILITY_AUDIT_IDS,\n createSurfaceError,\n type Result,\n type SurfaceError,\n} from \"@zigrivers/surface-core\";\nimport type {\n Capture,\n GroundingTool,\n SourceFileRef,\n ToolResult,\n} from \"@zigrivers/surface-core/interfaces\";\n\nexport const AXE_GROUNDING_ID = \"axe\";\nexport const JSX_A11Y_GROUNDING_ID = \"eslint-jsx-a11y\";\nexport const LIGHTHOUSE_GROUNDING_ID = \"lighthouse\";\nconst AXE_WCAG_AA_THRESHOLD = \"WCAG 2.2 AA\";\nconst JSX_A11Y_THRESHOLD = \"eslint-plugin-jsx-a11y recommended\";\nconst JSX_A11Y_RULE_PREFIX = \"jsx-a11y/\";\nconst LIGHTHOUSE_AUDIT_THRESHOLD = \"Lighthouse audit score 1\";\nconst LIGHTHOUSE_GROUNDING_CATEGORIES = [\"accessibility\", \"performance\"] as const;\nconst LIGHTHOUSE_ACCESSIBILITY_AUDIT_ID_SET = new Set<string>(LIGHTHOUSE_ACCESSIBILITY_AUDIT_IDS);\nconst HTTP_URL_SCHEME_PATTERN = /^https?:\\/\\//iu;\nconst SOURCE_EXTENSIONS = [\".js\", \".jsx\", \".mjs\", \".cjs\", \".ts\", \".tsx\", \".mts\", \".cts\"] as const;\nconst CONTRAST_MEASURED_PATTERN = /color contrast of\\s+(\\d+(?:\\.\\d+)?)/iu;\nconst CONTRAST_THRESHOLD_PATTERN = /expected contrast ratio of\\s+(\\d+(?:\\.\\d+)?)\\s*:1/iu;\nconst CONTRAST_RATIO_PATTERN = /(\\d+(?:\\.\\d+)?)\\s*:1/gu;\n\ntype LintResult = Awaited<ReturnType<ESLint[\"lintText\"]>>[number];\ntype LintMessageWithRule = LintResult[\"messages\"][number] & { readonly ruleId: string };\ntype MaybePromise<T> = T | Promise<T>;\n\nexport interface AxeNodeResult {\n readonly target?: readonly string[];\n readonly failureSummary?: string;\n readonly any?: readonly AxeCheckResult[];\n readonly all?: readonly AxeCheckResult[];\n readonly none?: readonly AxeCheckResult[];\n}\n\nexport interface AxeCheckResult {\n readonly message?: string;\n}\n\nexport interface AxeViolation {\n readonly id: string;\n readonly tags?: readonly string[];\n readonly help?: string;\n readonly description?: string;\n readonly nodes?: readonly AxeNodeResult[];\n}\n\nexport interface AxeRunResult {\n readonly violations?: readonly AxeViolation[];\n}\n\nexport interface AxeGroundingTool extends GroundingTool {\n readonly id: typeof AXE_GROUNDING_ID;\n}\n\nexport interface AxeGroundingOptions {\n readonly runAxe: (capture: Capture) => MaybePromise<AxeRunResult>;\n}\n\nexport interface JsxA11yGroundingTool extends GroundingTool {\n readonly id: typeof JSX_A11Y_GROUNDING_ID;\n}\n\nexport interface JsxA11yGroundingOptions {\n readonly sources: readonly SourceFileRef[];\n}\n\nexport interface LighthouseRunnerResult {\n readonly lhr?: LighthouseResult;\n}\n\nexport interface LighthouseResult {\n readonly audits?: Readonly<Record<string, unknown>>;\n readonly categories?: Readonly<Record<string, unknown>>;\n}\n\nexport interface LighthouseGroundingTool extends GroundingTool {\n readonly id: typeof LIGHTHOUSE_GROUNDING_ID;\n}\n\nexport interface LighthouseGroundingOptions {\n readonly runLighthouse?: (capture: Capture) => MaybePromise<LighthouseRunnerResult | undefined>;\n readonly chromeLaunchOptions?: Readonly<Record<string, unknown>>;\n readonly importLighthouse?: () => Promise<LighthouseModule>;\n readonly importChromeLauncher?: () => Promise<ChromeLauncherModule>;\n readonly lighthouseOptions?: Readonly<Record<string, unknown>>;\n}\n\ninterface ChromeLauncherModule {\n readonly launch?: (options?: Readonly<Record<string, unknown>>) => Promise<ChromeInstance>;\n}\n\ninterface ChromeInstance {\n readonly port: number;\n kill(): MaybePromise<void>;\n}\n\ninterface LighthouseModule {\n readonly default?: LighthouseRunner;\n readonly lighthouse?: LighthouseRunner;\n}\n\ntype LighthouseRunner = (\n url: string,\n options?: Readonly<Record<string, unknown>>,\n) => MaybePromise<LighthouseRunnerResult | undefined>;\n\nexport function createAxeGroundingTool(options: AxeGroundingOptions): AxeGroundingTool {\n return {\n id: AXE_GROUNDING_ID,\n async run(capture: Capture): Promise<Result<ToolResult[], SurfaceError>> {\n try {\n return { ok: true, value: axeResultToToolResults(await options.runAxe(capture)) };\n } catch (cause) {\n return {\n ok: false,\n error: createSurfaceError(\"step_failed\", \"Failed to run axe grounding.\", { cause }),\n };\n }\n },\n };\n}\n\nexport function createLighthouseGroundingTool(\n options: LighthouseGroundingOptions = {},\n): LighthouseGroundingTool {\n return {\n id: LIGHTHOUSE_GROUNDING_ID,\n async run(capture: Capture): Promise<Result<ToolResult[], SurfaceError>> {\n try {\n const result =\n options.runLighthouse === undefined\n ? await runLighthouseCapture(capture, options)\n : await options.runLighthouse(capture);\n\n return { ok: true, value: lighthouseResultToToolResults(result) };\n } catch (cause) {\n return {\n ok: false,\n error: createSurfaceError(\"step_failed\", \"Failed to run lighthouse grounding.\", {\n cause,\n }),\n };\n }\n },\n };\n}\n\nexport function axeResultToToolResults(result: AxeRunResult | null | undefined): ToolResult[] {\n const evidence = axeViolations(result)\n .flatMap((violation) => evidenceForAxeViolation(violation))\n .map((item, index) => ({ index, item }))\n .sort(\n (left, right) =>\n accessibilityAuditSortPriority(left.item.rule) -\n accessibilityAuditSortPriority(right.item.rule) ||\n compareStableStrings(\n `${left.item.rule}\\0${left.item.measuredValue}`,\n `${right.item.rule}\\0${right.item.measuredValue}`,\n ) ||\n left.index - right.index,\n )\n .map(({ item }) => item);\n\n return evidence.length === 0 ? [] : [{ tool: AXE_GROUNDING_ID, evidence }];\n}\n\nexport function lighthouseResultToToolResults(\n result: LighthouseRunnerResult | null | undefined,\n): ToolResult[] {\n const lhr = isRecord(result?.lhr) ? result.lhr : undefined;\n const audits = recordOrUndefined(lhr?.audits);\n if (lhr === undefined || audits === undefined) {\n return [];\n }\n\n const evidence = lighthouseAuditIds(lhr)\n .flatMap((id) => {\n const audit = recordOrUndefined(audits[id]);\n return audit === undefined ? [] : evidenceForLighthouseAudit(id, audit);\n })\n .map((item, index) => ({ index, item }))\n .sort(\n (left, right) =>\n compareStableStrings(\n `${left.item.rule}\\0${left.item.measuredValue}`,\n `${right.item.rule}\\0${right.item.measuredValue}`,\n ) || left.index - right.index,\n )\n .map(({ item }) => item);\n\n return evidence.length === 0 ? [] : [{ tool: LIGHTHOUSE_GROUNDING_ID, evidence }];\n}\n\nfunction accessibilityAuditSortPriority(rule: string): number {\n return LIGHTHOUSE_ACCESSIBILITY_AUDIT_ID_SET.has(rule) ? 0 : 1;\n}\n\nexport function createJsxA11yGroundingTool(options: JsxA11yGroundingOptions): JsxA11yGroundingTool {\n const sources = [...options.sources];\n\n return {\n id: JSX_A11Y_GROUNDING_ID,\n run: (): Promise<Result<ToolResult[], SurfaceError>> => runJsxA11yStaticPass(sources),\n };\n}\n\nasync function runLighthouseCapture(\n capture: Capture,\n options: LighthouseGroundingOptions,\n): Promise<LighthouseRunnerResult | undefined> {\n const url = lighthouseUrlForCapture(capture);\n if (url === undefined) {\n throw new Error(\"Lighthouse grounding requires an HTTP(S) URL or localhost capture target.\");\n }\n\n const module = await (options.importLighthouse ?? defaultImportLighthouse)();\n const lighthouse = module.default ?? module.lighthouse;\n if (typeof lighthouse !== \"function\") {\n throw new Error(\"lighthouse module did not expose a callable runner.\");\n }\n\n const chromeLauncher = await (options.importChromeLauncher ?? defaultImportChromeLauncher)();\n if (typeof chromeLauncher.launch !== \"function\") {\n throw new Error(\"chrome-launcher module did not expose a launch function.\");\n }\n\n const chrome = await chromeLauncher.launch(options.chromeLaunchOptions);\n try {\n return await lighthouse(url, { ...options.lighthouseOptions, port: chrome.port });\n } finally {\n await killChromeSafely(chrome);\n }\n}\n\nasync function defaultImportLighthouse(): Promise<LighthouseModule> {\n return (await importOptionalModule(\"lighthouse\")) as LighthouseModule;\n}\n\nasync function defaultImportChromeLauncher(): Promise<ChromeLauncherModule> {\n return (await importOptionalModule(\"chrome-launcher\")) as ChromeLauncherModule;\n}\n\nasync function importOptionalModule(specifier: string): Promise<unknown> {\n return import(specifier);\n}\n\nasync function killChromeSafely(chrome: ChromeInstance): Promise<void> {\n try {\n await chrome.kill();\n } catch {\n // Do not discard a successful Lighthouse result because best-effort cleanup failed.\n }\n}\n\nfunction lighthouseUrlForCapture(capture: Capture): string | undefined {\n if (capture.target.kind === \"url\") {\n return HTTP_URL_SCHEME_PATTERN.test(capture.target.ref) ? capture.target.ref : undefined;\n }\n\n if (capture.target.kind === \"localhost\") {\n return HTTP_URL_SCHEME_PATTERN.test(capture.target.ref)\n ? capture.target.ref\n : `http://${capture.target.ref}`;\n }\n\n return undefined;\n}\n\nfunction evidenceForAxeViolation(violation: AxeViolation): ToolResult[\"evidence\"] {\n return axeNodes(violation).map((node) => {\n const selector = selectorForAxeNode(node);\n const summary = summaryForAxeNode(violation, node);\n const contrast = contrastResult(summary);\n const threshold =\n violation.id === \"color-contrast\" && contrast.threshold !== undefined\n ? `${contrast.threshold}:1 (${AXE_WCAG_AA_THRESHOLD})`\n : undefined;\n\n return {\n kind: \"tool-result\" as const,\n tool: AXE_GROUNDING_ID,\n rule: violation.id,\n measuredValue:\n violation.id === \"color-contrast\" && contrast.measured !== undefined\n ? `${selector}: ${contrast.measured}:1`\n : `${selector}: ${summary}`,\n ...(threshold === undefined ? {} : { threshold }),\n };\n });\n}\n\nfunction evidenceForLighthouseAudit(\n id: string,\n audit: Readonly<Record<string, unknown>>,\n): ToolResult[\"evidence\"] {\n const score = lighthouseAuditScore(audit);\n if (score === undefined || score >= 1) {\n return [];\n }\n\n const measured = lighthouseAuditMeasuredValue(id, audit, score);\n\n return lighthouseAuditAnchors(id, audit).map((anchor) => ({\n kind: \"tool-result\" as const,\n tool: LIGHTHOUSE_GROUNDING_ID,\n rule: id,\n measuredValue: `${anchor}: ${measured}`,\n threshold: LIGHTHOUSE_AUDIT_THRESHOLD,\n }));\n}\n\nfunction selectorForAxeNode(node: AxeNodeResult): string {\n return Array.isArray(node.target) && node.target.length > 0 && node.target.every(isString)\n ? node.target.join(\" \")\n : \"unknown-target\";\n}\n\nfunction summaryForAxeNode(violation: AxeViolation, node: AxeNodeResult): string {\n return normalizeWhitespace(\n stringOrUndefined(node.failureSummary) ??\n firstCheckMessage(node.any) ??\n firstCheckMessage(node.all) ??\n firstCheckMessage(node.none) ??\n stringOrUndefined(violation.help) ??\n stringOrUndefined(violation.description) ??\n violation.id,\n );\n}\n\nfunction firstCheckMessage(checks: unknown): string | undefined {\n if (!Array.isArray(checks)) {\n return undefined;\n }\n\n for (const check of checks as readonly unknown[]) {\n if (isRecord(check)) {\n const message = check[\"message\"];\n if (typeof message === \"string\") {\n return message;\n }\n }\n }\n\n return undefined;\n}\n\nfunction contrastResult(value: string): {\n readonly measured: string | undefined;\n readonly threshold: string | undefined;\n} {\n const measured = value.match(CONTRAST_MEASURED_PATTERN)?.[1];\n const threshold = value.match(CONTRAST_THRESHOLD_PATTERN)?.[1];\n\n if (measured !== undefined || threshold !== undefined) {\n return { measured, threshold };\n }\n\n const ratios = [...value.matchAll(CONTRAST_RATIO_PATTERN)]\n .map((match) => match[1])\n .filter(isString);\n\n return { measured: ratios[0], threshold: ratios[1] };\n}\n\nfunction lighthouseAuditIds(lhr: LighthouseResult): readonly string[] {\n const categories = recordOrUndefined(lhr.categories);\n const fromCategories =\n categories === undefined\n ? []\n : LIGHTHOUSE_GROUNDING_CATEGORIES.flatMap((categoryId) => {\n const category = recordOrUndefined(categories[categoryId]);\n const auditRefs = arrayOrEmpty(category?.[\"auditRefs\"]);\n\n return auditRefs\n .map((auditRef) => stringOrUndefined(recordOrUndefined(auditRef)?.[\"id\"]))\n .filter(isString);\n });\n\n if (fromCategories.length > 0) {\n return knownAccessibilityAuditIdsFirst(uniqueStrings(fromCategories));\n }\n\n const auditIds = Object.keys(recordOrUndefined(lhr.audits) ?? {});\n\n return knownAccessibilityAuditIdsFirst(auditIds);\n}\n\n// Accessibility audits sort ahead of performance/other audits, with each group preserving\n// Lighthouse's order. This keeps a11y evidence prominent without shuffling within categories.\nfunction knownAccessibilityAuditIdsFirst(auditIds: readonly string[]): readonly string[] {\n const knownAccessibilityAuditIds = auditIds.filter((id) =>\n LIGHTHOUSE_ACCESSIBILITY_AUDIT_ID_SET.has(id),\n );\n const otherAuditIds = auditIds.filter((id) => !LIGHTHOUSE_ACCESSIBILITY_AUDIT_ID_SET.has(id));\n\n return [...knownAccessibilityAuditIds, ...otherAuditIds];\n}\n\nfunction lighthouseAuditScore(audit: Readonly<Record<string, unknown>>): number | undefined {\n const score = audit[\"score\"];\n\n return typeof score === \"number\" && Number.isFinite(score) ? score : undefined;\n}\n\nfunction lighthouseAuditAnchors(id: string, audit: Readonly<Record<string, unknown>>): string[] {\n const details = recordOrUndefined(audit[\"details\"]);\n const items = arrayOrEmpty(details?.[\"items\"]);\n const selectors = items.map(anchorForLighthouseDetailItem).filter(isNonEmptyString);\n\n return selectors.length === 0 ? [id] : uniqueStrings(selectors);\n}\n\nfunction anchorForLighthouseDetailItem(item: unknown): string | undefined {\n const itemRecord = recordOrUndefined(item);\n const node = recordOrUndefined(itemRecord?.[\"node\"]) ?? itemRecord;\n\n return (\n stringOrUndefined(node?.[\"selector\"]) ??\n stringOrUndefined(node?.[\"path\"]) ??\n stringOrUndefined(node?.[\"snippet\"]) ??\n stringOrUndefined(node?.[\"nodeLabel\"]) ??\n rectAnchor(recordOrUndefined(node?.[\"boundingRect\"]))\n );\n}\n\nfunction rectAnchor(rect: Readonly<Record<string, unknown>> | undefined): string | undefined {\n if (rect === undefined) {\n return undefined;\n }\n\n const x = finiteNumberOrUndefined(rect[\"left\"] ?? rect[\"x\"]);\n const y = finiteNumberOrUndefined(rect[\"top\"] ?? rect[\"y\"]);\n const width = finiteNumberOrUndefined(rect[\"width\"]);\n const height = finiteNumberOrUndefined(rect[\"height\"]);\n\n return x === undefined || y === undefined || width === undefined || height === undefined\n ? undefined\n : `rect(${formatLighthouseScore(x)},${formatLighthouseScore(y)} ${formatLighthouseScore(width)}x${formatLighthouseScore(height)})`;\n}\n\nfunction lighthouseAuditMeasuredValue(\n id: string,\n audit: Readonly<Record<string, unknown>>,\n score: number,\n): string {\n const display =\n stringOrUndefined(audit[\"displayValue\"]) ?? stringOrUndefined(audit[\"title\"]) ?? id;\n\n return `${normalizeWhitespace(display)} (score ${formatLighthouseScore(score)})`;\n}\n\nfunction formatLighthouseScore(score: number): string {\n return Number.isInteger(score)\n ? String(score)\n : score.toFixed(3).replace(/0+$/u, \"\").replace(/\\.$/u, \"\");\n}\n\nfunction normalizeWhitespace(value: string): string {\n return value.replace(/\\s+/gu, \" \").trim();\n}\n\nexport async function runJsxA11yStaticPass(\n sources: readonly SourceFileRef[],\n): Promise<Result<ToolResult[], SurfaceError>> {\n if (!sources.every(isSourceFileRef)) {\n return {\n ok: false,\n error: createSurfaceError(\"step_failed\", \"SourceFileRef requires string path and contents.\"),\n };\n }\n\n try {\n const eslint = createEslint();\n const evidence = (\n await Promise.all(\n sources\n .filter((source) => supportsSource(source.path))\n .map(async (source) => {\n const [result] = await eslint.lintText(source.contents, { filePath: source.path });\n\n return result === undefined ? [] : evidenceForResult(result);\n }),\n )\n )\n .flat()\n .sort((left, right) =>\n compareStableStrings(\n `${left.rule}\\0${left.measuredValue}`,\n `${right.rule}\\0${right.measuredValue}`,\n ),\n );\n\n return {\n ok: true,\n value: evidence.length === 0 ? [] : [{ tool: JSX_A11Y_GROUNDING_ID, evidence }],\n };\n } catch (cause) {\n return {\n ok: false,\n error: createSurfaceError(\"step_failed\", \"Failed to run eslint-jsx-a11y grounding.\", {\n cause,\n }),\n };\n }\n}\n\nfunction createEslint(): ESLint {\n const plugin: ESLint.Plugin = jsxA11y;\n const rules: Linter.RulesRecord = jsxA11y.configs.recommended.rules;\n\n return new ESLint({\n overrideConfigFile: true,\n overrideConfig: [\n {\n files: [\"**/*.{js,jsx,mjs,cjs}\"],\n languageOptions: {\n ecmaVersion: \"latest\",\n sourceType: \"module\",\n parserOptions: { ecmaFeatures: { jsx: true } },\n },\n plugins: { \"jsx-a11y\": plugin },\n rules,\n },\n {\n files: [\"**/*.{ts,tsx,mts,cts}\"],\n languageOptions: {\n parser: tseslint.parser,\n parserOptions: { ecmaFeatures: { jsx: true } },\n ecmaVersion: \"latest\",\n sourceType: \"module\",\n },\n plugins: { \"jsx-a11y\": plugin },\n rules,\n },\n ],\n });\n}\n\nfunction evidenceForResult(result: LintResult): ToolResult[\"evidence\"] {\n return result.messages\n .filter(\n (message): message is LintMessageWithRule =>\n message.ruleId?.startsWith(JSX_A11Y_RULE_PREFIX) === true,\n )\n .map((message) => ({\n kind: \"tool-result\" as const,\n tool: JSX_A11Y_GROUNDING_ID,\n rule: message.ruleId,\n measuredValue: `${result.filePath}:${message.line}:${message.column} ${message.message}`,\n threshold: JSX_A11Y_THRESHOLD,\n }));\n}\n\nfunction supportsSource(path: string): boolean {\n const lowerPath = path.toLowerCase();\n\n return SOURCE_EXTENSIONS.some((extension) => lowerPath.endsWith(extension));\n}\n\nfunction isSourceFileRef(source: unknown): source is SourceFileRef {\n if (typeof source !== \"object\" || source === null) {\n return false;\n }\n\n const candidate = source as { readonly path?: unknown; readonly contents?: unknown };\n\n return typeof candidate.path === \"string\" && typeof candidate.contents === \"string\";\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === \"string\";\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.trim().length > 0;\n}\n\nfunction finiteNumberOrUndefined(value: unknown): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nfunction axeViolations(result: AxeRunResult | null | undefined): readonly AxeViolation[] {\n if (!isRecord(result) || !Array.isArray(result.violations)) {\n return [];\n }\n\n return result.violations.filter(isAxeViolation);\n}\n\nfunction axeNodes(violation: AxeViolation): readonly AxeNodeResult[] {\n return Array.isArray(violation.nodes) ? violation.nodes.filter(isRecord) : [];\n}\n\nfunction isAxeViolation(value: unknown): value is AxeViolation {\n return isRecord(value) && typeof value.id === \"string\";\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction recordOrUndefined(value: unknown): Readonly<Record<string, unknown>> | undefined {\n return isRecord(value) ? value : undefined;\n}\n\nfunction arrayOrEmpty(value: unknown): readonly unknown[] {\n return Array.isArray(value) ? (value as readonly unknown[]) : [];\n}\n\nfunction stringOrUndefined(value: unknown): string | undefined {\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction uniqueStrings(values: readonly string[]): string[] {\n return [...new Set(values)];\n}\n\nfunction compareStableStrings(left: string, right: string): number {\n if (left < right) {\n return -1;\n }\n\n return left > right ? 1 : 0;\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zigrivers/surface-grounding",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"README.md",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"eslint": "^9.39.4",
|
|
20
|
+
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
21
|
+
"typescript-eslint": "^8.48.0",
|
|
22
|
+
"@zigrivers/surface-core": "0.1.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^22.13.0",
|
|
26
|
+
"tsup": "^8.5.1",
|
|
27
|
+
"typescript": "^5.9.3",
|
|
28
|
+
"vitest": "^4.0.14"
|
|
29
|
+
},
|
|
30
|
+
"description": "Measured grounding tools for Surface audits",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"homepage": "https://github.com/zigrivers/surface#readme",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/zigrivers/surface.git",
|
|
36
|
+
"directory": "packages/grounding"
|
|
37
|
+
},
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/zigrivers/surface/issues"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"surface",
|
|
43
|
+
"grounding",
|
|
44
|
+
"accessibility"
|
|
45
|
+
],
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public",
|
|
48
|
+
"provenance": true
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsup",
|
|
52
|
+
"build:smoke": "node scripts/build-smoke.mjs",
|
|
53
|
+
"clean": "node scripts/clean.mjs",
|
|
54
|
+
"lint": "eslint src",
|
|
55
|
+
"test": "vitest run",
|
|
56
|
+
"test:watch": "vitest",
|
|
57
|
+
"typecheck": "tsc --noEmit"
|
|
58
|
+
}
|
|
59
|
+
}
|