aria-ease 6.14.0 → 7.0.1
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 +1 -1
- package/dist/AccordionComponentStrategy-2SWMNUR6.js +1 -0
- package/dist/ComboboxComponentStrategy-YSYLR2U5.js +5 -0
- package/dist/MenuComponentStrategy-C22BZEBH.js +5 -0
- package/dist/RelativeTargetResolver-T4P25J2M.js +1 -0
- package/dist/TabsComponentStrategy-ADEEFJXM.js +1 -0
- package/dist/audit-WBKVW7H6.js +9 -0
- package/dist/badgeHelper-IB5RTMAG.js +11 -0
- package/dist/badgeHelper-JSROP5ML.js +1 -0
- package/dist/buildContracts-T4XQZBDU.js +13 -0
- package/dist/chunk-52I3INNG.js +11 -0
- package/dist/chunk-APUMBDOT.js +1 -0
- package/dist/chunk-BHNO4ZI3.js +1 -0
- package/dist/chunk-CNU4N4AY.js +1 -0
- package/dist/chunk-SM6ZKEDR.js +1 -0
- package/dist/chunk-ZNQ5BXVJ.js +1 -0
- package/dist/cli.cjs +132 -3560
- package/dist/cli.js +19 -161
- package/dist/configLoader-ZEJVXLX7.js +1 -0
- package/dist/configLoader-ZXTSCIP6.js +1 -0
- package/dist/contractTestRunnerPlaywright-FOCQTM4L.js +46 -0
- package/dist/contractTestRunnerPlaywright-QPU6HZXG.js +46 -0
- package/dist/formatters-H3CPDLG5.js +87 -0
- package/dist/index.cjs +64 -5103
- package/dist/index.d.cts +4 -6
- package/dist/index.d.ts +4 -6
- package/dist/index.js +17 -2703
- package/dist/src/accordion/index.cjs +1 -183
- package/dist/src/accordion/index.js +1 -181
- package/dist/src/block/index.cjs +1 -124
- package/dist/src/block/index.js +1 -122
- package/dist/src/checkbox/index.cjs +1 -109
- package/dist/src/checkbox/index.js +1 -107
- package/dist/src/combobox/index.cjs +1 -265
- package/dist/src/combobox/index.js +1 -263
- package/dist/src/menu/index.cjs +1 -339
- package/dist/src/menu/index.js +1 -337
- package/dist/src/radio/index.cjs +1 -117
- package/dist/src/radio/index.js +1 -115
- package/dist/src/tabs/index.cjs +1 -265
- package/dist/src/tabs/index.js +1 -263
- package/dist/src/toggle/index.cjs +1 -119
- package/dist/src/toggle/index.js +1 -117
- package/dist/src/utils/test/AccordionComponentStrategy-X2GSQ5KT.js +1 -0
- package/dist/src/utils/test/ComboboxComponentStrategy-SICWLI27.js +5 -0
- package/dist/src/utils/test/MenuComponentStrategy-R4VPAHDE.js +5 -0
- package/dist/src/utils/test/RelativeTargetResolver-UQQMZHI6.js +1 -0
- package/dist/src/utils/test/TabsComponentStrategy-L2PYNEW6.js +1 -0
- package/dist/src/utils/test/badgeHelper-ER5ZOHWF.js +11 -0
- package/dist/src/utils/test/chunk-APUMBDOT.js +1 -0
- package/dist/src/utils/test/chunk-BHNO4ZI3.js +1 -0
- package/dist/src/utils/test/configLoader-NCYRL2O6.js +1 -0
- package/dist/src/utils/test/contractTestRunnerPlaywright-YZCMF64Q.js +46 -0
- package/dist/src/utils/test/dsl/index.cjs +1 -838
- package/dist/src/utils/test/dsl/index.d.cts +2 -4
- package/dist/src/utils/test/dsl/index.d.ts +2 -4
- package/dist/src/utils/test/dsl/index.js +1 -836
- package/dist/src/utils/test/index.cjs +64 -2672
- package/dist/src/utils/test/index.d.cts +2 -2
- package/dist/src/utils/test/index.d.ts +2 -2
- package/dist/src/utils/test/index.js +16 -340
- package/dist/test-VXSCSKV5.js +19 -0
- package/package.json +8 -10
- package/dist/AccordionComponentStrategy-4ZEIQ2V6.js +0 -42
- package/dist/ComboboxComponentStrategy-DU342VMB.js +0 -64
- package/dist/MenuComponentStrategy-VYCC2XOM.js +0 -81
- package/dist/RelativeTargetResolver-DJAITO6D.js +0 -7
- package/dist/TabsComponentStrategy-3SQURPMX.js +0 -29
- package/dist/audit-JYEPKLHR.js +0 -63
- package/dist/badgeHelper-JOWO6RQG.js +0 -15
- package/dist/badgeHelper-RDOMCC6E.js +0 -108
- package/dist/buildContracts-VIV6GM56.js +0 -437
- package/dist/chunk-4DU5Z5BR.js +0 -340
- package/dist/chunk-GJGUY643.js +0 -182
- package/dist/chunk-GLT43UVH.js +0 -43
- package/dist/chunk-I2KLQ2HA.js +0 -22
- package/dist/chunk-JJEPLK7L.js +0 -107
- package/dist/chunk-PK5L2SAF.js +0 -17
- package/dist/configLoader-Q7N5XV4P.js +0 -183
- package/dist/configLoader-REHK3S3Q.js +0 -7
- package/dist/contractTestRunnerPlaywright-B2HLZKKK.js +0 -1394
- package/dist/contractTestRunnerPlaywright-RWK52C7S.js +0 -1394
- package/dist/formatters-32KQIIYS.js +0 -183
- package/dist/src/utils/test/AccordionComponentStrategy-WRHZOEN6.js +0 -38
- package/dist/src/utils/test/ComboboxComponentStrategy-XKQ72RFD.js +0 -60
- package/dist/src/utils/test/MenuComponentStrategy-6XWU5KLW.js +0 -77
- package/dist/src/utils/test/RelativeTargetResolver-G2XDN2VV.js +0 -1
- package/dist/src/utils/test/TabsComponentStrategy-BKG53SEV.js +0 -26
- package/dist/src/utils/test/badgeHelper-HZKGOPB4.js +0 -102
- package/dist/src/utils/test/chunk-4DU5Z5BR.js +0 -332
- package/dist/src/utils/test/chunk-GLT43UVH.js +0 -41
- package/dist/src/utils/test/configLoader-NA7IBCS3.js +0 -181
- package/dist/src/utils/test/contractTestRunnerPlaywright-5FIGA5G4.js +0 -1372
- package/dist/test-WDBS5JWO.js +0 -358
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import "./chunk-I2KLQ2HA.js";
|
|
2
|
-
|
|
3
|
-
// src/utils/test/src/component-strategies/TabsComponentStrategy.ts
|
|
4
|
-
var TabsComponentStrategy = class {
|
|
5
|
-
constructor(mainSelector, selectors) {
|
|
6
|
-
this.mainSelector = mainSelector;
|
|
7
|
-
this.selectors = selectors;
|
|
8
|
-
}
|
|
9
|
-
async resetState() {
|
|
10
|
-
}
|
|
11
|
-
async shouldSkipTest(test, page) {
|
|
12
|
-
if (test.isVertical !== void 0 && this.selectors.tablist) {
|
|
13
|
-
const tablistSelector = this.selectors.tablist;
|
|
14
|
-
const tablist = page.locator(tablistSelector).first();
|
|
15
|
-
const orientation = await tablist.getAttribute("aria-orientation");
|
|
16
|
-
const isVertical = orientation === "vertical";
|
|
17
|
-
if (test.isVertical !== isVertical) {
|
|
18
|
-
return true;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
getMainSelector() {
|
|
24
|
-
return this.mainSelector;
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
export {
|
|
28
|
-
TabsComponentStrategy
|
|
29
|
-
};
|
package/dist/audit-JYEPKLHR.js
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import "./chunk-I2KLQ2HA.js";
|
|
2
|
-
|
|
3
|
-
// src/utils/audit/src/audit/audit.ts
|
|
4
|
-
import AxeBuilder from "@axe-core/playwright";
|
|
5
|
-
import { chromium } from "playwright";
|
|
6
|
-
async function createAuditBrowser() {
|
|
7
|
-
return await chromium.launch({ headless: true });
|
|
8
|
-
}
|
|
9
|
-
async function runAudit(url, options) {
|
|
10
|
-
let browser = options.browser;
|
|
11
|
-
let browserCreated = false;
|
|
12
|
-
const timeout = 6e4;
|
|
13
|
-
const waitUntil = "domcontentloaded";
|
|
14
|
-
try {
|
|
15
|
-
if (!browser) {
|
|
16
|
-
browser = await chromium.launch({ headless: true });
|
|
17
|
-
browserCreated = true;
|
|
18
|
-
}
|
|
19
|
-
const context = await browser.newContext();
|
|
20
|
-
const page = await context.newPage();
|
|
21
|
-
await page.goto(url, { waitUntil, timeout });
|
|
22
|
-
try {
|
|
23
|
-
await page.waitForSelector("main", { state: "visible", timeout });
|
|
24
|
-
} catch (waitError) {
|
|
25
|
-
console.warn(`\u26A0\uFE0F Warning: <main> landmark not found or not visible on ${url} after ${timeout}ms. Audit will continue, but results may be inaccurate. Consider adding a <main> element to improve audit accuracy. ${waitError instanceof Error ? waitError.message : String(waitError)}`);
|
|
26
|
-
}
|
|
27
|
-
const axe = new AxeBuilder({ page });
|
|
28
|
-
const axeResults = await axe.analyze();
|
|
29
|
-
await page.close();
|
|
30
|
-
await context.close();
|
|
31
|
-
return axeResults;
|
|
32
|
-
} catch (error) {
|
|
33
|
-
if (error instanceof Error) {
|
|
34
|
-
if (error.message.includes("Executable doesn't exist")) {
|
|
35
|
-
console.error("\n\u274C Playwright browsers not found!\n");
|
|
36
|
-
console.log("\u{1F4E6} First-time setup required:");
|
|
37
|
-
console.log(" Run: npx playwright install chromium\n");
|
|
38
|
-
console.log("\u{1F4A1} This downloads the browser needed for auditing (~200MB)");
|
|
39
|
-
console.log(" You only need to do this once.\n");
|
|
40
|
-
} else if (error.message.includes("page.goto: net::ERR_CONNECTION_REFUSED")) {
|
|
41
|
-
console.error("\n\u274C Server Not Running!\n");
|
|
42
|
-
console.log(" Make sure your server is running before auditing URL");
|
|
43
|
-
console.log(" Run: npm run dev # or your start command");
|
|
44
|
-
} else if (error.message.includes("page.goto: Protocol error (Page.navigate): Cannot navigate to invalid URL")) {
|
|
45
|
-
console.error("\n\u274C Cannot audit invalid URL\n");
|
|
46
|
-
} else {
|
|
47
|
-
console.error("\u274C Audit error:", error.message);
|
|
48
|
-
console.log(" Make sure you provide a valid URL");
|
|
49
|
-
}
|
|
50
|
-
} else {
|
|
51
|
-
console.error("\u274C Audit error (non-Error):", String(error));
|
|
52
|
-
}
|
|
53
|
-
throw error;
|
|
54
|
-
} finally {
|
|
55
|
-
if (browser && browserCreated) {
|
|
56
|
-
await browser.close();
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
export {
|
|
61
|
-
createAuditBrowser,
|
|
62
|
-
runAudit
|
|
63
|
-
};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
BADGE_CONFIGS,
|
|
3
|
-
displayAllBadges,
|
|
4
|
-
displayBadgeInfo,
|
|
5
|
-
getBadgeMarkdown,
|
|
6
|
-
promptAddBadge
|
|
7
|
-
} from "./chunk-JJEPLK7L.js";
|
|
8
|
-
import "./chunk-I2KLQ2HA.js";
|
|
9
|
-
export {
|
|
10
|
-
BADGE_CONFIGS,
|
|
11
|
-
displayAllBadges,
|
|
12
|
-
displayBadgeInfo,
|
|
13
|
-
getBadgeMarkdown,
|
|
14
|
-
promptAddBadge
|
|
15
|
-
};
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import "./chunk-I2KLQ2HA.js";
|
|
2
|
-
|
|
3
|
-
// src/utils/cli/badgeHelper.ts
|
|
4
|
-
import fs from "fs-extra";
|
|
5
|
-
import path from "path";
|
|
6
|
-
import chalk from "chalk";
|
|
7
|
-
import readline from "readline";
|
|
8
|
-
var BADGE_CONFIGS = {
|
|
9
|
-
audit: {
|
|
10
|
-
type: "audit",
|
|
11
|
-
fileName: "audited-by-aria-ease.svg",
|
|
12
|
-
label: "Audited by aria-ease",
|
|
13
|
-
markdownUrl: "https://cdn.jsdelivr.net/gh/aria-ease/aria-ease@main/badges/audited-by-aria-ease.svg"
|
|
14
|
-
},
|
|
15
|
-
component: {
|
|
16
|
-
type: "component",
|
|
17
|
-
fileName: "components-tested-aria-ease.svg",
|
|
18
|
-
label: "Components tested: aria-ease",
|
|
19
|
-
markdownUrl: "https://cdn.jsdelivr.net/gh/aria-ease/aria-ease@main/badges/components-tested-aria-ease.svg"
|
|
20
|
-
},
|
|
21
|
-
verified: {
|
|
22
|
-
type: "verified",
|
|
23
|
-
fileName: "verified-by-aria-ease.svg",
|
|
24
|
-
label: "Verified by aria-ease",
|
|
25
|
-
markdownUrl: "https://cdn.jsdelivr.net/gh/aria-ease/aria-ease@main/badges/verified-by-aria-ease.svg"
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
function getBadgeMarkdown(badgeType) {
|
|
29
|
-
const config = BADGE_CONFIGS[badgeType];
|
|
30
|
-
return `[](https://github.com/aria-ease/aria-ease)`;
|
|
31
|
-
}
|
|
32
|
-
function displayBadgeInfo(badgeType) {
|
|
33
|
-
const markdown = getBadgeMarkdown(badgeType);
|
|
34
|
-
console.log(chalk.cyan("\n\u{1F3C5} Show your accessibility commitment!"));
|
|
35
|
-
console.log(chalk.white(" Add this badge to your README.md:\n"));
|
|
36
|
-
console.log(chalk.green(" " + markdown));
|
|
37
|
-
console.log(chalk.dim("\n This helps others discover accessibility tools and shows you care!\n"));
|
|
38
|
-
}
|
|
39
|
-
async function promptAddBadge(badgeType, cwd = process.cwd()) {
|
|
40
|
-
const readmePath = path.join(cwd, "README.md");
|
|
41
|
-
const readmeExists = await fs.pathExists(readmePath);
|
|
42
|
-
if (!readmeExists) {
|
|
43
|
-
console.log(chalk.yellow(" \u2139\uFE0F No README.md found in current directory"));
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
const readmeContent = await fs.readFile(readmePath, "utf-8");
|
|
47
|
-
const markdown = getBadgeMarkdown(badgeType);
|
|
48
|
-
if (readmeContent.includes(markdown) || readmeContent.includes(BADGE_CONFIGS[badgeType].fileName)) {
|
|
49
|
-
console.log(chalk.gray(" \u2713 Badge already in README.md"));
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
const rl = readline.createInterface({
|
|
53
|
-
input: process.stdin,
|
|
54
|
-
output: process.stdout
|
|
55
|
-
});
|
|
56
|
-
const answer = await new Promise((resolve) => {
|
|
57
|
-
rl.question(chalk.cyan(" Add badge to README.md now? (y/n): "), (ans) => {
|
|
58
|
-
rl.close();
|
|
59
|
-
resolve(ans.toLowerCase().trim());
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
if (answer === "y" || answer === "yes") {
|
|
63
|
-
await addBadgeToReadme(readmePath, readmeContent, markdown);
|
|
64
|
-
console.log(chalk.green(" \u2713 Badge added to README.md!"));
|
|
65
|
-
} else {
|
|
66
|
-
console.log(chalk.gray(" Skipped. You can add it manually anytime."));
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
async function addBadgeToReadme(readmePath, content, badge) {
|
|
70
|
-
const lines = content.split("\n");
|
|
71
|
-
let insertIndex = 0;
|
|
72
|
-
for (let i = 0; i < lines.length; i++) {
|
|
73
|
-
const line = lines[i].trim();
|
|
74
|
-
if (line.startsWith("[![") || line.startsWith("[!")) {
|
|
75
|
-
insertIndex = i + 1;
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
if (insertIndex > 0 && !line.startsWith("[![") && !line.startsWith("[!") && line.length > 0) {
|
|
79
|
-
break;
|
|
80
|
-
}
|
|
81
|
-
if (insertIndex === 0 && line.startsWith("#")) {
|
|
82
|
-
insertIndex = i + 2;
|
|
83
|
-
break;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
if (insertIndex === 0) {
|
|
87
|
-
insertIndex = 1;
|
|
88
|
-
}
|
|
89
|
-
lines.splice(insertIndex, 0, badge);
|
|
90
|
-
await fs.writeFile(readmePath, lines.join("\n"), "utf-8");
|
|
91
|
-
}
|
|
92
|
-
function displayAllBadges() {
|
|
93
|
-
console.log(chalk.cyan("\n\u{1F4CD} Available badges:"));
|
|
94
|
-
console.log(chalk.white("\n For audits:"));
|
|
95
|
-
console.log(chalk.green(" " + getBadgeMarkdown("audit")));
|
|
96
|
-
console.log(chalk.white("\n For component testing:"));
|
|
97
|
-
console.log(chalk.green(" " + getBadgeMarkdown("component")));
|
|
98
|
-
console.log(chalk.white("\n For both (verified):"));
|
|
99
|
-
console.log(chalk.green(" " + getBadgeMarkdown("verified")));
|
|
100
|
-
console.log("");
|
|
101
|
-
}
|
|
102
|
-
export {
|
|
103
|
-
BADGE_CONFIGS,
|
|
104
|
-
displayAllBadges,
|
|
105
|
-
displayBadgeInfo,
|
|
106
|
-
getBadgeMarkdown,
|
|
107
|
-
promptAddBadge
|
|
108
|
-
};
|
|
@@ -1,437 +0,0 @@
|
|
|
1
|
-
import "./chunk-I2KLQ2HA.js";
|
|
2
|
-
|
|
3
|
-
// src/utils/test/dsl/src/buildContracts.ts
|
|
4
|
-
import path from "path";
|
|
5
|
-
import fs from "fs-extra";
|
|
6
|
-
import { glob } from "fs/promises";
|
|
7
|
-
import chalk from "chalk";
|
|
8
|
-
|
|
9
|
-
// src/utils/test/dsl/src/contractValidator.ts
|
|
10
|
-
function validateContractSchema(contract) {
|
|
11
|
-
const errors = [];
|
|
12
|
-
if (!contract || typeof contract !== "object") {
|
|
13
|
-
return {
|
|
14
|
-
valid: false,
|
|
15
|
-
errors: [{ path: "$", message: "Contract must be an object" }]
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
const c = contract;
|
|
19
|
-
if (!c.selectors) {
|
|
20
|
-
errors.push({ path: "$.selectors", message: "selectors is required" });
|
|
21
|
-
} else if (typeof c.selectors !== "object" || c.selectors === null || Array.isArray(c.selectors)) {
|
|
22
|
-
errors.push({ path: "$.selectors", message: "selectors must be an object" });
|
|
23
|
-
} else {
|
|
24
|
-
const selectors = c.selectors;
|
|
25
|
-
Object.entries(selectors).forEach(([key, value]) => {
|
|
26
|
-
if (typeof value !== "string") {
|
|
27
|
-
errors.push({ path: `$.selectors['${key}']`, message: "All selectors must be strings" });
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
if (!Array.isArray(c.static)) {
|
|
32
|
-
errors.push({ path: "$.static", message: "static must be an array" });
|
|
33
|
-
} else {
|
|
34
|
-
c.static.forEach((item, idx) => {
|
|
35
|
-
if (typeof item !== "object" || item === null) {
|
|
36
|
-
errors.push({ path: `$.static[${idx}]`, message: "static item must be an object" });
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
const staticItem = item;
|
|
40
|
-
if (!Array.isArray(staticItem.assertions)) {
|
|
41
|
-
errors.push({
|
|
42
|
-
path: `$.static[${idx}].assertions`,
|
|
43
|
-
message: "assertions must be an array"
|
|
44
|
-
});
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
staticItem.assertions.forEach((assertion, assertIdx) => {
|
|
48
|
-
if (typeof assertion !== "object" || assertion === null) {
|
|
49
|
-
errors.push({
|
|
50
|
-
path: `$.static[${idx}].assertions[${assertIdx}]`,
|
|
51
|
-
message: "assertion must be an object"
|
|
52
|
-
});
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
const a = assertion;
|
|
56
|
-
if (typeof a.target !== "string") {
|
|
57
|
-
errors.push({
|
|
58
|
-
path: `$.static[${idx}].assertions[${assertIdx}].target`,
|
|
59
|
-
message: "target is required and must be a string"
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
if (typeof a.attribute !== "string") {
|
|
63
|
-
errors.push({
|
|
64
|
-
path: `$.static[${idx}].assertions[${assertIdx}].attribute`,
|
|
65
|
-
message: "attribute is required and must be a string"
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
if (typeof a.failureMessage !== "string") {
|
|
69
|
-
errors.push({
|
|
70
|
-
path: `$.static[${idx}].assertions[${assertIdx}].failureMessage`,
|
|
71
|
-
message: "failureMessage is required and must be a string"
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
if (a.level !== void 0 && !["required", "recommended", "optional"].includes(a.level)) {
|
|
75
|
-
errors.push({
|
|
76
|
-
path: `$.static[${idx}].assertions[${assertIdx}].level`,
|
|
77
|
-
message: "level must be one of: required, recommended, optional"
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
if (!Array.isArray(c.dynamic)) {
|
|
84
|
-
errors.push({ path: "$.dynamic", message: "dynamic must be an array" });
|
|
85
|
-
} else {
|
|
86
|
-
c.dynamic.forEach((item, idx) => {
|
|
87
|
-
if (typeof item !== "object" || item === null) {
|
|
88
|
-
errors.push({ path: `$.dynamic[${idx}]`, message: "dynamic item must be an object" });
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
const dynamicItem = item;
|
|
92
|
-
if (typeof dynamicItem.description !== "string") {
|
|
93
|
-
errors.push({
|
|
94
|
-
path: `$.dynamic[${idx}].description`,
|
|
95
|
-
message: "description is required and must be a string"
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
if (!Array.isArray(dynamicItem.action)) {
|
|
99
|
-
errors.push({
|
|
100
|
-
path: `$.dynamic[${idx}].action`,
|
|
101
|
-
message: "action is required and must be an array"
|
|
102
|
-
});
|
|
103
|
-
} else {
|
|
104
|
-
dynamicItem.action.forEach((action, actIdx) => {
|
|
105
|
-
if (typeof action !== "object" || action === null) {
|
|
106
|
-
errors.push({
|
|
107
|
-
path: `$.dynamic[${idx}].action[${actIdx}]`,
|
|
108
|
-
message: "action item must be an object"
|
|
109
|
-
});
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
const a = action;
|
|
113
|
-
if (typeof a.type !== "string") {
|
|
114
|
-
errors.push({
|
|
115
|
-
path: `$.dynamic[${idx}].action[${actIdx}].type`,
|
|
116
|
-
message: "type is required and must be a string"
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
if (typeof a.target !== "string") {
|
|
120
|
-
errors.push({
|
|
121
|
-
path: `$.dynamic[${idx}].action[${actIdx}].target`,
|
|
122
|
-
message: "target is required and must be a string"
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
if (!Array.isArray(dynamicItem.assertions)) {
|
|
128
|
-
errors.push({
|
|
129
|
-
path: `$.dynamic[${idx}].assertions`,
|
|
130
|
-
message: "assertions is required and must be an array"
|
|
131
|
-
});
|
|
132
|
-
} else {
|
|
133
|
-
dynamicItem.assertions.forEach((assertion, assertIdx) => {
|
|
134
|
-
if (typeof assertion !== "object" || assertion === null) {
|
|
135
|
-
errors.push({
|
|
136
|
-
path: `$.dynamic[${idx}].assertions[${assertIdx}]`,
|
|
137
|
-
message: "assertion must be an object"
|
|
138
|
-
});
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
const a = assertion;
|
|
142
|
-
if (typeof a.target !== "string") {
|
|
143
|
-
errors.push({
|
|
144
|
-
path: `$.dynamic[${idx}].assertions[${assertIdx}].target`,
|
|
145
|
-
message: "target is required and must be a string"
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
if (typeof a.assertion !== "string") {
|
|
149
|
-
errors.push({
|
|
150
|
-
path: `$.dynamic[${idx}].assertions[${assertIdx}].assertion`,
|
|
151
|
-
message: "assertion is required and must be a string"
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
if (a.level !== void 0 && !["required", "recommended", "optional"].includes(a.level)) {
|
|
155
|
-
errors.push({
|
|
156
|
-
path: `$.dynamic[${idx}].assertions[${assertIdx}].level`,
|
|
157
|
-
message: "level must be one of: required, recommended, optional"
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
if (c.relationships !== void 0) {
|
|
165
|
-
if (!Array.isArray(c.relationships)) {
|
|
166
|
-
errors.push({ path: "$.relationships", message: "relationships must be an array" });
|
|
167
|
-
} else {
|
|
168
|
-
c.relationships.forEach((rel, idx) => {
|
|
169
|
-
if (typeof rel !== "object" || rel === null) {
|
|
170
|
-
errors.push({ path: `$.relationships[${idx}]`, message: "relationship must be an object" });
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
const r = rel;
|
|
174
|
-
const type = r.type;
|
|
175
|
-
if (!["aria-reference", "contains"].includes(type)) {
|
|
176
|
-
errors.push({
|
|
177
|
-
path: `$.relationships[${idx}].type`,
|
|
178
|
-
message: "type must be one of: aria-reference, contains"
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
if (type === "aria-reference") {
|
|
182
|
-
if (typeof r.from !== "string") {
|
|
183
|
-
errors.push({
|
|
184
|
-
path: `$.relationships[${idx}].from`,
|
|
185
|
-
message: "from is required for aria-reference and must be a string"
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
if (typeof r.attribute !== "string") {
|
|
189
|
-
errors.push({
|
|
190
|
-
path: `$.relationships[${idx}].attribute`,
|
|
191
|
-
message: "attribute is required for aria-reference and must be a string"
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
if (typeof r.to !== "string") {
|
|
195
|
-
errors.push({
|
|
196
|
-
path: `$.relationships[${idx}].to`,
|
|
197
|
-
message: "to is required for aria-reference and must be a string"
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
} else if (type === "contains") {
|
|
201
|
-
if (typeof r.parent !== "string") {
|
|
202
|
-
errors.push({
|
|
203
|
-
path: `$.relationships[${idx}].parent`,
|
|
204
|
-
message: "parent is required for contains and must be a string"
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
if (typeof r.child !== "string") {
|
|
208
|
-
errors.push({
|
|
209
|
-
path: `$.relationships[${idx}].child`,
|
|
210
|
-
message: "child is required for contains and must be a string"
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
return {
|
|
218
|
-
valid: errors.length === 0,
|
|
219
|
-
errors
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
function validateRelationshipReferences(contract, selectorKeys) {
|
|
223
|
-
const errors = [];
|
|
224
|
-
if (!contract || typeof contract !== "object") {
|
|
225
|
-
return errors;
|
|
226
|
-
}
|
|
227
|
-
const c = contract;
|
|
228
|
-
const relationships = c.relationships;
|
|
229
|
-
if (!Array.isArray(relationships)) {
|
|
230
|
-
return errors;
|
|
231
|
-
}
|
|
232
|
-
relationships.forEach((rel, idx) => {
|
|
233
|
-
const type = rel.type;
|
|
234
|
-
if (type === "aria-reference") {
|
|
235
|
-
const from = rel.from;
|
|
236
|
-
const to = rel.to;
|
|
237
|
-
if (from && !selectorKeys.has(from)) {
|
|
238
|
-
errors.push({
|
|
239
|
-
path: `$.relationships[${idx}].from`,
|
|
240
|
-
message: `Selector '${from}' not found in selectors`
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
if (to && !selectorKeys.has(to)) {
|
|
244
|
-
errors.push({
|
|
245
|
-
path: `$.relationships[${idx}].to`,
|
|
246
|
-
message: `Selector '${to}' not found in selectors`
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
} else if (type === "contains") {
|
|
250
|
-
const parent = rel.parent;
|
|
251
|
-
const child = rel.child;
|
|
252
|
-
if (parent && !selectorKeys.has(parent)) {
|
|
253
|
-
errors.push({
|
|
254
|
-
path: `$.relationships[${idx}].parent`,
|
|
255
|
-
message: `Selector '${parent}' not found in selectors`
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
if (child && !selectorKeys.has(child)) {
|
|
259
|
-
errors.push({
|
|
260
|
-
path: `$.relationships[${idx}].child`,
|
|
261
|
-
message: `Selector '${child}' not found in selectors`
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
return errors;
|
|
267
|
-
}
|
|
268
|
-
function validateTargetReferences(contract, selectorKeys) {
|
|
269
|
-
const errors = [];
|
|
270
|
-
if (!contract || typeof contract !== "object") {
|
|
271
|
-
return errors;
|
|
272
|
-
}
|
|
273
|
-
const c = contract;
|
|
274
|
-
const staticItems = c.static;
|
|
275
|
-
if (Array.isArray(staticItems)) {
|
|
276
|
-
staticItems.forEach((item, itemIdx) => {
|
|
277
|
-
const assertions = item.assertions;
|
|
278
|
-
if (Array.isArray(assertions)) {
|
|
279
|
-
assertions.forEach((assertion, assertIdx) => {
|
|
280
|
-
const target = assertion.target;
|
|
281
|
-
if (target && !selectorKeys.has(target)) {
|
|
282
|
-
errors.push({
|
|
283
|
-
path: `$.static[${itemIdx}].assertions[${assertIdx}].target`,
|
|
284
|
-
message: `Selector '${target}' not found in selectors`
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
const dynamicItems = c.dynamic;
|
|
292
|
-
if (Array.isArray(dynamicItems)) {
|
|
293
|
-
dynamicItems.forEach((item, itemIdx) => {
|
|
294
|
-
const actions = item.action;
|
|
295
|
-
if (Array.isArray(actions)) {
|
|
296
|
-
actions.forEach((action, actIdx) => {
|
|
297
|
-
const target = action.target;
|
|
298
|
-
if (target && target !== "document" && !selectorKeys.has(target)) {
|
|
299
|
-
errors.push({
|
|
300
|
-
path: `$.dynamic[${itemIdx}].action[${actIdx}].target`,
|
|
301
|
-
message: `Selector '${target}' not found in selectors (or use 'document')`
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
const assertions = item.assertions;
|
|
307
|
-
if (Array.isArray(assertions)) {
|
|
308
|
-
assertions.forEach((assertion, assertIdx) => {
|
|
309
|
-
const target = assertion.target;
|
|
310
|
-
if (target && target !== "relative" && !selectorKeys.has(target)) {
|
|
311
|
-
errors.push({
|
|
312
|
-
path: `$.dynamic[${itemIdx}].assertions[${assertIdx}].target`,
|
|
313
|
-
message: `Selector '${target}' not found in selectors (or use 'relative')`
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
return errors;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// src/utils/test/dsl/src/buildContracts.ts
|
|
324
|
-
async function buildContracts(cwd, config) {
|
|
325
|
-
const errors = [];
|
|
326
|
-
const built = [];
|
|
327
|
-
if (!config.contracts || config.contracts.length === 0) {
|
|
328
|
-
console.log(chalk.yellow('\u2139\uFE0F No contracts configured. Add "contracts" array to ariaease.config.js'));
|
|
329
|
-
return { success: true, built, errors };
|
|
330
|
-
}
|
|
331
|
-
try {
|
|
332
|
-
console.log(chalk.cyanBright("\n\u{1F3D7}\uFE0F Building contracts...\n"));
|
|
333
|
-
for (const contractSource of config.contracts) {
|
|
334
|
-
const srcPattern = path.resolve(cwd, contractSource.src);
|
|
335
|
-
const outDir = contractSource.out ? path.resolve(cwd, contractSource.out) : void 0;
|
|
336
|
-
console.log(chalk.gray(` Pattern: ${contractSource.src}`));
|
|
337
|
-
if (outDir) {
|
|
338
|
-
console.log(chalk.gray(` Output: ${contractSource.out}
|
|
339
|
-
`));
|
|
340
|
-
} else {
|
|
341
|
-
console.log(chalk.gray(` Output: Same directory as source
|
|
342
|
-
`));
|
|
343
|
-
}
|
|
344
|
-
const contractFiles = [];
|
|
345
|
-
for await (const file of glob(srcPattern, { cwd })) {
|
|
346
|
-
contractFiles.push(file);
|
|
347
|
-
}
|
|
348
|
-
if (contractFiles.length === 0) {
|
|
349
|
-
console.log(chalk.yellow(`\u26A0\uFE0F No contract files found matching pattern: ${contractSource.src}`));
|
|
350
|
-
continue;
|
|
351
|
-
}
|
|
352
|
-
if (outDir) {
|
|
353
|
-
await fs.ensureDir(outDir);
|
|
354
|
-
}
|
|
355
|
-
for (const contractFile of contractFiles) {
|
|
356
|
-
try {
|
|
357
|
-
const absolutePath = contractFile.startsWith("/") ? contractFile : path.resolve(cwd, contractFile);
|
|
358
|
-
const module = await import(`file://${absolutePath}`);
|
|
359
|
-
let contract = module.default;
|
|
360
|
-
if (!contract) {
|
|
361
|
-
const namedExports = Object.entries(module).find(
|
|
362
|
-
([, value]) => value && typeof value === "object" && "toJSON" in value
|
|
363
|
-
);
|
|
364
|
-
if (namedExports) {
|
|
365
|
-
contract = namedExports[1];
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
if (!contract || typeof contract.toJSON !== "function") {
|
|
369
|
-
errors.push(`${path.basename(contractFile)}: No contract with toJSON() method found`);
|
|
370
|
-
continue;
|
|
371
|
-
}
|
|
372
|
-
const json = contract.toJSON();
|
|
373
|
-
const schemaValidation = validateContractSchema(json);
|
|
374
|
-
if (!schemaValidation.valid) {
|
|
375
|
-
const errorLines = schemaValidation.errors.map((err) => ` ${chalk.red("\u2717")} ${err.path}: ${err.message}`).join("\n");
|
|
376
|
-
const errorMsg = `Schema validation failed:
|
|
377
|
-
${errorLines}`;
|
|
378
|
-
errors.push(`${path.basename(contractFile)}: ${errorMsg}`);
|
|
379
|
-
console.log(chalk.red(`\u274C ${path.basename(contractFile)}`));
|
|
380
|
-
console.log(chalk.red(` ${errorMsg}`));
|
|
381
|
-
continue;
|
|
382
|
-
}
|
|
383
|
-
const selectorKeys = /* @__PURE__ */ new Set();
|
|
384
|
-
if (json && typeof json === "object" && "selectors" in json) {
|
|
385
|
-
const selectors = json.selectors;
|
|
386
|
-
Object.keys(selectors).forEach((key) => selectorKeys.add(key));
|
|
387
|
-
}
|
|
388
|
-
const refErrors = [
|
|
389
|
-
...validateRelationshipReferences(json, selectorKeys),
|
|
390
|
-
...validateTargetReferences(json, selectorKeys)
|
|
391
|
-
];
|
|
392
|
-
if (refErrors.length > 0) {
|
|
393
|
-
const errorLines = refErrors.map((err) => ` ${chalk.red("\u2717")} ${err.path}: ${err.message}`).join("\n");
|
|
394
|
-
const errorMsg = `Reference validation failed:
|
|
395
|
-
${errorLines}`;
|
|
396
|
-
errors.push(`${path.basename(contractFile)}: ${errorMsg}`);
|
|
397
|
-
console.log(chalk.red(`\u274C ${path.basename(contractFile)}`));
|
|
398
|
-
console.log(chalk.red(` ${errorMsg}`));
|
|
399
|
-
continue;
|
|
400
|
-
}
|
|
401
|
-
const contractName = path.basename(contractFile, path.extname(contractFile));
|
|
402
|
-
const outputFile = outDir ? path.join(outDir, `${contractName}.json`) : path.join(path.dirname(absolutePath), `${contractName}.json`);
|
|
403
|
-
await fs.writeFile(outputFile, JSON.stringify(json, null, 2) + "\n", "utf-8");
|
|
404
|
-
const relativePath = path.relative(cwd, outputFile);
|
|
405
|
-
built.push(relativePath);
|
|
406
|
-
console.log(chalk.green(`\u2705 ${path.basename(contractFile)} \u2192 ${path.basename(outputFile)}`));
|
|
407
|
-
} catch (error) {
|
|
408
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
409
|
-
const filename = path.basename(contractFile);
|
|
410
|
-
errors.push(`${filename}: ${errorMsg}`);
|
|
411
|
-
console.log(chalk.red(`\u274C ${filename}`));
|
|
412
|
-
console.log(chalk.red(` Error: ${errorMsg}`));
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
console.log("");
|
|
417
|
-
if (errors.length === 0) {
|
|
418
|
-
console.log(chalk.green(`\u2705 Built ${built.length} contract${built.length !== 1 ? "s" : ""} successfully
|
|
419
|
-
`));
|
|
420
|
-
return { success: true, built, errors };
|
|
421
|
-
} else {
|
|
422
|
-
console.log(chalk.yellow(`\u26A0\uFE0F Built ${built.length} contracts with ${errors.length} error${errors.length !== 1 ? "s" : ""}
|
|
423
|
-
`));
|
|
424
|
-
return { success: false, built, errors };
|
|
425
|
-
}
|
|
426
|
-
} catch (error) {
|
|
427
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
428
|
-
const msg = `Failed to build contracts: ${errorMsg}`;
|
|
429
|
-
errors.push(msg);
|
|
430
|
-
console.log(chalk.red(`\u274C ${msg}
|
|
431
|
-
`));
|
|
432
|
-
return { success: false, built, errors };
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
export {
|
|
436
|
-
buildContracts
|
|
437
|
-
};
|