@stacksjs/stx 0.2.174 → 0.2.176
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/dist/a11y.d.ts +72 -2
- package/dist/a11y.js +25 -10
- package/dist/cli.js +1045 -930
- package/dist/client-script.js +1 -0
- package/dist/component-renderer.js +17 -0
- package/dist/craft.js +561 -453
- package/dist/includes.js +8 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/process.js +2 -1
- package/dist/props.d.ts +51 -8
- package/dist/pwa.js +512 -404
- package/dist/resolve-stx-targets.d.ts +49 -0
- package/dist/resolve-stx-targets.js +38 -0
- package/dist/runtime-globals.d.ts +24 -0
- package/dist/runtime-globals.js +16 -0
- package/dist/runtime-injection.d.ts +36 -0
- package/dist/runtime-injection.js +15 -0
- package/dist/server-bindings.js +3 -0
- package/dist/signal-processing.js +1 -1
- package/dist/signals.js +30 -27
- package/dist/ssg.d.ts +23 -0
- package/dist/ssg.js +509 -397
- package/dist/stx-virtual-ts.d.ts +27 -3
- package/dist/stx-virtual-ts.js +36 -0
- package/dist/typecheck.js +96 -62
- package/dist/types/config-types.d.ts +24 -0
- package/dist/utils.js +5 -4
- package/package.json +5 -5
package/dist/a11y.d.ts
CHANGED
|
@@ -36,6 +36,29 @@ export interface A11yViolation {
|
|
|
36
36
|
/** URL to more information */
|
|
37
37
|
helpUrl?: string;
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Has this element declared itself decorative rather than semantic?
|
|
41
|
+
*
|
|
42
|
+
* `role="presentation"` and its synonym `role="none"` remove an element's
|
|
43
|
+
* implicit semantics from the accessibility tree. On a table that is exactly the
|
|
44
|
+
* declaration that it carries layout, not data — so demanding a `<th>` or a
|
|
45
|
+
* `<caption>` asks the author to reintroduce the semantics the role exists to
|
|
46
|
+
* remove (stacksjs/stx#1916).
|
|
47
|
+
*
|
|
48
|
+
* This is not a corner case. Every HTML email is nested layout tables: mail
|
|
49
|
+
* clients strip `<style>` and have no reliable box model, so `role="presentation"`
|
|
50
|
+
* is the standard, correct pattern there. One email template accounted for every
|
|
51
|
+
* remaining finding in an otherwise-clean app, and none of them was fixable —
|
|
52
|
+
* the correct markup was what triggered them. An advisory checker with a
|
|
53
|
+
* permanent finding nobody can close is one people learn to ignore, which costs
|
|
54
|
+
* more than the rule is worth.
|
|
55
|
+
*
|
|
56
|
+
* The rule itself is right to exist: a DATA table with no headers is a real
|
|
57
|
+
* problem. It just has to take the author's word for which kind it is.
|
|
58
|
+
*/
|
|
59
|
+
export declare function isPresentationalRole(role: string | null | undefined): boolean;
|
|
60
|
+
/** Read one attribute out of a raw tag's attribute text. */
|
|
61
|
+
export declare function attributeValue(attrs: string, name: string): string | null;
|
|
39
62
|
/**
|
|
40
63
|
* Process accessibility directives.
|
|
41
64
|
* Handles @a11y hints, @screenReader content, and @ariaDescribe.
|
|
@@ -63,6 +86,23 @@ else {
|
|
|
63
86
|
* ```
|
|
64
87
|
*/
|
|
65
88
|
export declare function hasDOMSupport(): boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Regex-based accessibility checker for non-DOM environments.
|
|
91
|
+
* Provides a subset of checks that can be performed with regex parsing.
|
|
92
|
+
*
|
|
93
|
+
* @param html - HTML content to check
|
|
94
|
+
* @param _filePath - File path for error reporting
|
|
95
|
+
* @returns Array of accessibility violations found
|
|
96
|
+
*/
|
|
97
|
+
/**
|
|
98
|
+
* Regex-based checking, used when no DOM is available — which is the CLI.
|
|
99
|
+
*
|
|
100
|
+
* Exported so tests can exercise it directly. It and the DOM path implement the
|
|
101
|
+
* same rules twice, and the way two implementations of one rule go wrong is by
|
|
102
|
+
* disagreeing; pinning only whichever one the test environment happens to pick
|
|
103
|
+
* leaves the other free to drift (#1916).
|
|
104
|
+
*/
|
|
105
|
+
export declare function checkA11yWithRegex(html: string, _filePath: string): A11yViolation[];
|
|
66
106
|
/**
|
|
67
107
|
* Automatically check template for accessibility issues.
|
|
68
108
|
*
|
|
@@ -81,9 +121,39 @@ export declare function hasDOMSupport(): boolean;
|
|
|
81
121
|
* ```
|
|
82
122
|
*/
|
|
83
123
|
export declare function checkA11y(html: string, filePath: string): Promise<A11yViolation[]>;
|
|
124
|
+
/** What a scan actually read, alongside what it found. */
|
|
125
|
+
export interface A11yScanResult {
|
|
126
|
+
/** Files that had at least one violation, mapped to their violations. */
|
|
127
|
+
results: Record<string, A11yViolation[]>;
|
|
128
|
+
/**
|
|
129
|
+
* Every file the scan READ.
|
|
130
|
+
*
|
|
131
|
+
* Reported separately because "no violations" and "no files" are different
|
|
132
|
+
* answers that used to be the same one: `results` only ever contains files
|
|
133
|
+
* WITH violations, so an empty map means both "everything is clean" and
|
|
134
|
+
* "nothing was scanned", and the success banner was printed for both
|
|
135
|
+
* (stacksjs/stx#1918).
|
|
136
|
+
*/
|
|
137
|
+
scannedFiles: string[];
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Scan a directory, a file, or a glob for accessibility issues.
|
|
141
|
+
*
|
|
142
|
+
* The argument used to be joined into a scan pattern, so only a directory
|
|
143
|
+
* worked: a file path became `<file>/**` + `/*.stx` and a glob became a doubled
|
|
144
|
+
* pattern, both matching nothing and both reported as a clean pass. See
|
|
145
|
+
* `resolve-stx-targets.ts` for what that cost.
|
|
146
|
+
*/
|
|
147
|
+
export declare function scanA11yTargets(target: string | readonly string[], options?: {
|
|
148
|
+
recursive?: boolean;
|
|
149
|
+
ignorePaths?: string[];
|
|
150
|
+
}): Promise<A11yScanResult>;
|
|
84
151
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
152
|
+
* Back-compatible wrapper: violations only.
|
|
153
|
+
*
|
|
154
|
+
* Kept so existing callers keep working, but it cannot say whether the scan read
|
|
155
|
+
* anything — which is the distinction #1918 turned on. Prefer
|
|
156
|
+
* {@link scanA11yTargets}.
|
|
87
157
|
*/
|
|
88
158
|
export declare function scanA11yIssues(directory: string, options?: {
|
|
89
159
|
recursive?: boolean;
|
package/dist/a11y.js
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { resolveStxTargets } from "./resolve-stx-targets.js";
|
|
3
|
+
export function isPresentationalRole(role) {
|
|
4
|
+
if (!role)
|
|
5
|
+
return !1;
|
|
6
|
+
return role.trim().split(/\s+/)[0]?.toLowerCase() === "presentation" || role.trim().split(/\s+/)[0]?.toLowerCase() === "none";
|
|
7
|
+
}
|
|
8
|
+
export function attributeValue(attrs, name) {
|
|
9
|
+
const match = new RegExp(`\\b${name}\\s*=\\s*("([^"]*)"|'([^']*)'|([^\\s>]+))`, "i").exec(attrs);
|
|
10
|
+
if (!match)
|
|
11
|
+
return null;
|
|
12
|
+
return match[2] ?? match[3] ?? match[4] ?? null;
|
|
13
|
+
}
|
|
2
14
|
export function processA11yDirectives(template, _context, _filePath, _options) {
|
|
3
15
|
let output = template;
|
|
4
16
|
output = output.replace(/@a11y\(\s*['"]([^'"]+)['"]\s*(?:,\s*['"]([^'"]+)['"]\s*)?\)/g, (_, hintType, customMessage) => {
|
|
@@ -58,7 +70,7 @@ function protectStxComponentTags(source) {
|
|
|
58
70
|
})
|
|
59
71
|
};
|
|
60
72
|
}
|
|
61
|
-
function checkA11yWithRegex(html, _filePath) {
|
|
73
|
+
export function checkA11yWithRegex(html, _filePath) {
|
|
62
74
|
const violations = [], imgRegex = /<img\s+([^>]*)>/gi;
|
|
63
75
|
let match;
|
|
64
76
|
match = imgRegex.exec(html);
|
|
@@ -156,11 +168,11 @@ function checkA11yWithRegex(html, _filePath) {
|
|
|
156
168
|
help: "Add lang attribute to the html element",
|
|
157
169
|
helpUrl: "https://web.dev/learn/accessibility/more-html/#language"
|
|
158
170
|
});
|
|
159
|
-
const tableRegex = /<table[^>]
|
|
171
|
+
const tableRegex = /<table([^>]*)>([\s\S]*?)<\/table>/gi;
|
|
160
172
|
match = tableRegex.exec(html);
|
|
161
173
|
while (match !== null) {
|
|
162
|
-
const tableContent = match[
|
|
163
|
-
if (!/<th\b/i.test(tableContent))
|
|
174
|
+
const tableContent = match[2];
|
|
175
|
+
if (!isPresentationalRole(attributeValue(match[1], "role")) && !/<th\b/i.test(tableContent))
|
|
164
176
|
violations.push({
|
|
165
177
|
type: "table-missing-headers",
|
|
166
178
|
element: "<table>",
|
|
@@ -321,6 +333,8 @@ export async function checkA11y(html, filePath) {
|
|
|
321
333
|
});
|
|
322
334
|
}
|
|
323
335
|
container.querySelectorAll("table").forEach((table) => {
|
|
336
|
+
if (isPresentationalRole(table.getAttribute("role")))
|
|
337
|
+
return;
|
|
324
338
|
const hasCaption = table.querySelector("caption");
|
|
325
339
|
if (!table.querySelector("th"))
|
|
326
340
|
violations.push({
|
|
@@ -411,23 +425,24 @@ export async function checkA11y(html, filePath) {
|
|
|
411
425
|
}
|
|
412
426
|
return violations;
|
|
413
427
|
}
|
|
414
|
-
export async function
|
|
415
|
-
const results = {}, { recursive = !0, ignorePaths = [] } = options,
|
|
416
|
-
for await (const file of glob.scan())
|
|
417
|
-
if (!ignorePaths.some((ignorePath) => file.includes(path.normalize(ignorePath))))
|
|
418
|
-
files.push(file);
|
|
428
|
+
export async function scanA11yTargets(target, options = {}) {
|
|
429
|
+
const results = {}, { recursive = !0, ignorePaths = [] } = options, targets = Array.isArray(target) ? target : [target], files = await resolveStxTargets(targets, { recursive, ignorePaths }), scannedFiles = [];
|
|
419
430
|
for (const file of files)
|
|
420
431
|
try {
|
|
421
432
|
const content = await Bun.file(file).text();
|
|
422
433
|
if (file.endsWith("good-a11y.stx"))
|
|
423
434
|
continue;
|
|
424
435
|
const violations = await checkA11y(content, file);
|
|
436
|
+
scannedFiles.push(file);
|
|
425
437
|
if (violations.length > 0)
|
|
426
438
|
results[file] = violations;
|
|
427
439
|
} catch (error) {
|
|
428
440
|
console.error(`Error scanning ${file}:`, error);
|
|
429
441
|
}
|
|
430
|
-
return results;
|
|
442
|
+
return { results, scannedFiles };
|
|
443
|
+
}
|
|
444
|
+
export async function scanA11yIssues(directory, options = {}) {
|
|
445
|
+
return (await scanA11yTargets(directory, options)).results;
|
|
431
446
|
}
|
|
432
447
|
const defaultAutoFixConfig = {
|
|
433
448
|
fixMissingAlt: !0,
|