a11ylens 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.
Files changed (59) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/LICENSE +21 -0
  3. package/README.md +135 -0
  4. package/dist/cli/index.d.ts +2 -0
  5. package/dist/cli/index.js +162 -0
  6. package/dist/config/defaults.d.ts +2 -0
  7. package/dist/config/defaults.js +9 -0
  8. package/dist/config/loader.d.ts +7 -0
  9. package/dist/config/loader.js +104 -0
  10. package/dist/engine/runA11yLens.d.ts +9 -0
  11. package/dist/engine/runA11yLens.js +158 -0
  12. package/dist/engine/styleResolver.d.ts +2 -0
  13. package/dist/engine/styleResolver.js +63 -0
  14. package/dist/index.d.ts +5 -0
  15. package/dist/index.js +8 -0
  16. package/dist/parser/ast.d.ts +25 -0
  17. package/dist/parser/ast.js +169 -0
  18. package/dist/parser/css.d.ts +7 -0
  19. package/dist/parser/css.js +48 -0
  20. package/dist/reporters/consoleReporter.d.ts +2 -0
  21. package/dist/reporters/consoleReporter.js +67 -0
  22. package/dist/reporters/jsonReporter.d.ts +2 -0
  23. package/dist/reporters/jsonReporter.js +7 -0
  24. package/dist/reporters/sarifReporter.d.ts +2 -0
  25. package/dist/reporters/sarifReporter.js +95 -0
  26. package/dist/rules/clickableNonsemantic.d.ts +2 -0
  27. package/dist/rules/clickableNonsemantic.js +43 -0
  28. package/dist/rules/contrastRatio.d.ts +2 -0
  29. package/dist/rules/contrastRatio.js +149 -0
  30. package/dist/rules/engine.d.ts +4 -0
  31. package/dist/rules/engine.js +22 -0
  32. package/dist/rules/iconOnlyControl.d.ts +2 -0
  33. package/dist/rules/iconOnlyControl.js +50 -0
  34. package/dist/rules/imgAlt.d.ts +2 -0
  35. package/dist/rules/imgAlt.js +48 -0
  36. package/dist/rules/registry.d.ts +3 -0
  37. package/dist/rules/registry.js +19 -0
  38. package/dist/rules/routerLinkText.d.ts +2 -0
  39. package/dist/rules/routerLinkText.js +32 -0
  40. package/dist/rules/utils.d.ts +6 -0
  41. package/dist/rules/utils.js +51 -0
  42. package/dist/scanner/fileAnalyzer.d.ts +1 -0
  43. package/dist/scanner/fileAnalyzer.js +46 -0
  44. package/dist/scanner/fileSystemWalker.js +122 -0
  45. package/dist/scanner/fileWalker.d.ts +8 -0
  46. package/dist/scanner/fileWalker.js +126 -0
  47. package/dist/scanner/styleExtractor.d.ts +13 -0
  48. package/dist/scanner/styleExtractor.js +95 -0
  49. package/dist/scanner/templateExtractor.d.ts +13 -0
  50. package/dist/scanner/templateExtractor.js +101 -0
  51. package/dist/types/config.d.ts +9 -0
  52. package/dist/types/config.js +2 -0
  53. package/dist/types/results.d.ts +22 -0
  54. package/dist/types/results.js +2 -0
  55. package/dist/types/rules.d.ts +14 -0
  56. package/dist/types/rules.js +2 -0
  57. package/dist/types/style.d.ts +9 -0
  58. package/dist/types/style.js +2 -0
  59. package/package.json +65 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,6 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+ - Initial project structure
5
+ - CLI setup with file discovery pipeline
6
+ - Placeholder scanning + scoring engine
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 MADHAN MONISH JAYAKUMAR
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.
package/README.md ADDED
@@ -0,0 +1,135 @@
1
+ # A11yLens
2
+
3
+ A11yLens is a TypeScript Node.js CLI + library that scans Angular component templates (external `.component.html` and inline templates in `.component.ts`) for common accessibility issues. It reports findings in console, JSON, or SARIF formats and computes a project score.
4
+
5
+ No runtime dependencies are required.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g a11ylens
11
+ ```
12
+
13
+ Or run via npx:
14
+
15
+ ```bash
16
+ npx a11ylens ./src
17
+ ```
18
+
19
+ ## CLI
20
+
21
+ ```bash
22
+ a11ylens [roots...] [options]
23
+ ```
24
+
25
+ Options:
26
+
27
+ - `--config <path>`: Use a specific config file
28
+ - `--format <console|json|sarif>`: Output format (default: console)
29
+ - `--out <path>`: Write JSON/SARIF output to a file
30
+ - `--min-score <n>`: Exit non-zero when score is below `n`
31
+ - `--verbose`: Print extra diagnostics
32
+
33
+ Examples:
34
+
35
+ ```bash
36
+ a11ylens ./src
37
+ npx a11ylens ./apps/admin --format json
38
+ npx a11ylens ./src --format sarif --out a11ylens.sarif --min-score 85
39
+ ```
40
+
41
+ ## Library API
42
+
43
+ ```ts
44
+ import { runA11yLens } from "a11ylens";
45
+
46
+ const result = runA11yLens(["./src"], {
47
+ cwd: process.cwd(),
48
+ overrides: { minScore: 90 }
49
+ });
50
+
51
+ console.log(result.score);
52
+ ```
53
+
54
+ ## Configuration
55
+
56
+ A11yLens discovers config files by searching upward from the current working directory.
57
+
58
+ Supported files:
59
+
60
+ - `.a11ylensrc.json`
61
+ - `a11ylens.config.json`
62
+
63
+ Example:
64
+
65
+ ```json
66
+ {
67
+ "includePatterns": ["*.component.html", "*.component.ts"],
68
+ "ignoreDirs": ["node_modules", ".git", "dist"],
69
+ "ignorePatterns": ["**/legacy/**"],
70
+ "styleFiles": ["./styles.css"],
71
+ "minScore": 85
72
+ }
73
+ ```
74
+
75
+ Notes:
76
+
77
+ - `includePatterns` and `ignorePatterns` use a minimal glob-like matcher (`*` prefix/suffix).
78
+ - `ignoreDirs` matches directory names anywhere in the path.
79
+ - `styleFiles` supports simple selectors (`.class`, `tag`, `tag.class`); complex selectors and CSS variables are ignored.
80
+
81
+ ## Output Formats
82
+
83
+ - Console: human-friendly output with summary and score.
84
+ - JSON: machine-readable full results.
85
+ - SARIF: static analysis format for GitHub code scanning.
86
+
87
+ ## Rules (Built-In)
88
+
89
+ - Note: rules are minimal and focus on common Angular template issues.
90
+ - `img-alt`: images require `alt`.
91
+ - `clickable-nonsemantic`: clickable div/span needs semantics.
92
+ - `routerlink-text`: router links need an accessible name.
93
+ - `icon-only-control`: icon-only controls need `aria-label`.
94
+ - `color-contrast`: text color must meet WCAG AA ratios when `color` and `background-color` are available (inline or simple class selectors).
95
+
96
+ ## Exit Codes
97
+
98
+ - `0`: Success
99
+ - `1`: Runtime error
100
+ - `2`: `--min-score` provided and score is below the threshold
101
+
102
+ ## CI Example (SARIF)
103
+
104
+ ```yaml
105
+ name: A11yLens SARIF
106
+
107
+ on:
108
+ workflow_dispatch:
109
+
110
+ jobs:
111
+ scan:
112
+ runs-on: ubuntu-latest
113
+ steps:
114
+ - uses: actions/checkout@v4
115
+ - uses: actions/setup-node@v4
116
+ with:
117
+ node-version: 20
118
+ cache: npm
119
+ - run: npm ci
120
+ - run: npm run build
121
+ - run: node dist/cli/index.js ./src --format sarif --out a11ylens.sarif
122
+ - uses: github/codeql-action/upload-sarif@v3
123
+ with:
124
+ sarif_file: a11ylens.sarif
125
+ ```
126
+
127
+ ## Development
128
+
129
+ ```bash
130
+ npm run build
131
+ ```
132
+
133
+ ## License
134
+
135
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,162 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ const fs = __importStar(require("fs"));
38
+ const runA11yLens_1 = require("../engine/runA11yLens");
39
+ const consoleReporter_1 = require("../reporters/consoleReporter");
40
+ const jsonReporter_1 = require("../reporters/jsonReporter");
41
+ const sarifReporter_1 = require("../reporters/sarifReporter");
42
+ const printUsage = () => {
43
+ console.log("Usage: a11ylens [roots...] [options]");
44
+ console.log("");
45
+ console.log("Options:");
46
+ console.log(" --config <path> Path to config file");
47
+ console.log(" --format <format> console | json | sarif");
48
+ console.log(" --out <path> Output file for json/sarif");
49
+ console.log(" --min-score <n> Minimum passing score");
50
+ console.log(" --verbose Extra logging");
51
+ console.log(" --help Show usage");
52
+ };
53
+ const parseArgs = (argv) => {
54
+ const roots = [];
55
+ let configPath;
56
+ let format = "console";
57
+ let outPath;
58
+ let minScore;
59
+ let verbose = false;
60
+ for (let i = 0; i < argv.length; i += 1) {
61
+ const arg = argv[i];
62
+ if (arg === "--help" || arg === "-h") {
63
+ printUsage();
64
+ process.exit(0);
65
+ }
66
+ if (arg.startsWith("--config")) {
67
+ const value = readValue(arg, argv[i + 1]);
68
+ if (value) {
69
+ configPath = value;
70
+ if (!arg.includes("="))
71
+ i += 1;
72
+ }
73
+ continue;
74
+ }
75
+ if (arg.startsWith("--format")) {
76
+ const value = readValue(arg, argv[i + 1]);
77
+ if (value === "console" || value === "json" || value === "sarif") {
78
+ format = value;
79
+ }
80
+ if (!arg.includes("="))
81
+ i += 1;
82
+ continue;
83
+ }
84
+ if (arg.startsWith("--out")) {
85
+ const value = readValue(arg, argv[i + 1]);
86
+ if (value) {
87
+ outPath = value;
88
+ if (!arg.includes("="))
89
+ i += 1;
90
+ }
91
+ continue;
92
+ }
93
+ if (arg.startsWith("--min-score")) {
94
+ const value = readValue(arg, argv[i + 1]);
95
+ if (value) {
96
+ const parsed = Number(value);
97
+ if (!Number.isNaN(parsed)) {
98
+ minScore = parsed;
99
+ }
100
+ if (!arg.includes("="))
101
+ i += 1;
102
+ }
103
+ continue;
104
+ }
105
+ if (arg === "--verbose") {
106
+ verbose = true;
107
+ continue;
108
+ }
109
+ if (arg.startsWith("-")) {
110
+ continue;
111
+ }
112
+ roots.push(arg);
113
+ }
114
+ if (roots.length === 0) {
115
+ roots.push("src");
116
+ }
117
+ return { roots, configPath, format, outPath, minScore, verbose };
118
+ };
119
+ const readValue = (arg, nextArg) => {
120
+ if (arg.includes("=")) {
121
+ return arg.split("=").slice(1).join("=");
122
+ }
123
+ return nextArg;
124
+ };
125
+ const main = () => {
126
+ const { roots, configPath, format, outPath, minScore, verbose } = parseArgs(process.argv.slice(2));
127
+ const cwd = process.cwd();
128
+ const overrides = minScore !== undefined ? { minScore } : undefined;
129
+ const config = (0, runA11yLens_1.resolveConfig)(cwd, configPath, overrides);
130
+ if (verbose) {
131
+ console.log("Config:", config);
132
+ }
133
+ try {
134
+ const result = (0, runA11yLens_1.runA11yLens)(roots, { cwd, configFilePath: configPath, overrides });
135
+ if (format === "console") {
136
+ (0, consoleReporter_1.reportConsole)(result, cwd);
137
+ }
138
+ else if (format === "json") {
139
+ const output = (0, jsonReporter_1.reportJson)(result);
140
+ writeOutput(output, outPath);
141
+ }
142
+ else if (format === "sarif") {
143
+ const output = (0, sarifReporter_1.reportSarif)(result);
144
+ writeOutput(output, outPath);
145
+ }
146
+ if (config.minScore !== undefined && result.score < config.minScore) {
147
+ process.exit(2);
148
+ }
149
+ }
150
+ catch (error) {
151
+ console.error("Error running A11yLens:", error instanceof Error ? error.message : error);
152
+ process.exit(1);
153
+ }
154
+ };
155
+ const writeOutput = (output, outPath) => {
156
+ if (outPath) {
157
+ fs.writeFileSync(outPath, output, "utf8");
158
+ return;
159
+ }
160
+ console.log(output);
161
+ };
162
+ main();
@@ -0,0 +1,2 @@
1
+ import type { A11yLensConfig } from "../types/config";
2
+ export declare const defaultConfig: A11yLensConfig;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defaultConfig = void 0;
4
+ exports.defaultConfig = {
5
+ includePatterns: ["*.component.html", "*.component.ts"],
6
+ ignoreDirs: ["node_modules", ".git", "dist"],
7
+ ignorePatterns: [],
8
+ styleFiles: []
9
+ };
@@ -0,0 +1,7 @@
1
+ import type { A11yLensConfig, A11yLensConfigOverrides } from "../types/config";
2
+ export interface LoadedConfig {
3
+ config: A11yLensConfig;
4
+ configPath?: string;
5
+ }
6
+ export declare const loadConfig: (cwd: string, configPath?: string, overrides?: A11yLensConfigOverrides) => LoadedConfig;
7
+ export declare const mergeConfig: (base: A11yLensConfig, fileConfig: A11yLensConfigOverrides, overrides: A11yLensConfigOverrides) => A11yLensConfig;
@@ -0,0 +1,104 @@
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.mergeConfig = exports.loadConfig = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const defaults_1 = require("./defaults");
40
+ const configFileNames = [".a11ylensrc.json", "a11ylens.config.json"];
41
+ const loadConfig = (cwd, configPath, overrides) => {
42
+ const resolvedCwd = path.resolve(cwd);
43
+ const discoveredPath = configPath ? path.resolve(configPath) : findConfigUpwards(resolvedCwd);
44
+ let fileConfig = {};
45
+ if (discoveredPath) {
46
+ const content = readConfigFile(discoveredPath);
47
+ if (content)
48
+ fileConfig = content;
49
+ }
50
+ const merged = (0, exports.mergeConfig)(defaults_1.defaultConfig, fileConfig, overrides !== null && overrides !== void 0 ? overrides : {});
51
+ return { config: merged, configPath: discoveredPath };
52
+ };
53
+ exports.loadConfig = loadConfig;
54
+ const mergeConfig = (base, fileConfig, overrides) => {
55
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
56
+ return {
57
+ includePatterns: normalizeStringArray((_b = (_a = overrides.includePatterns) !== null && _a !== void 0 ? _a : fileConfig.includePatterns) !== null && _b !== void 0 ? _b : base.includePatterns),
58
+ ignoreDirs: normalizeStringArray((_d = (_c = overrides.ignoreDirs) !== null && _c !== void 0 ? _c : fileConfig.ignoreDirs) !== null && _d !== void 0 ? _d : base.ignoreDirs),
59
+ ignorePatterns: normalizeStringArray((_f = (_e = overrides.ignorePatterns) !== null && _e !== void 0 ? _e : fileConfig.ignorePatterns) !== null && _f !== void 0 ? _f : base.ignorePatterns),
60
+ styleFiles: normalizeStringArray((_h = (_g = overrides.styleFiles) !== null && _g !== void 0 ? _g : fileConfig.styleFiles) !== null && _h !== void 0 ? _h : base.styleFiles),
61
+ minScore: (_k = (_j = overrides.minScore) !== null && _j !== void 0 ? _j : fileConfig.minScore) !== null && _k !== void 0 ? _k : base.minScore
62
+ };
63
+ };
64
+ exports.mergeConfig = mergeConfig;
65
+ const normalizeStringArray = (value) => {
66
+ if (!Array.isArray(value))
67
+ return [];
68
+ return value.filter((entry) => typeof entry === "string");
69
+ };
70
+ const findConfigUpwards = (startDir) => {
71
+ let current = startDir;
72
+ while (true) {
73
+ for (const name of configFileNames) {
74
+ const candidate = path.join(current, name);
75
+ if (fs.existsSync(candidate))
76
+ return candidate;
77
+ }
78
+ const parent = path.dirname(current);
79
+ if (parent === current)
80
+ break;
81
+ current = parent;
82
+ }
83
+ return undefined;
84
+ };
85
+ const readConfigFile = (configPath) => {
86
+ try {
87
+ const raw = fs.readFileSync(configPath, "utf8");
88
+ const parsed = JSON.parse(raw);
89
+ if (!parsed || typeof parsed !== "object")
90
+ return undefined;
91
+ const record = parsed;
92
+ const minScore = record.minScore;
93
+ return {
94
+ includePatterns: normalizeStringArray(record.includePatterns),
95
+ ignoreDirs: normalizeStringArray(record.ignoreDirs),
96
+ ignorePatterns: normalizeStringArray(record.ignorePatterns),
97
+ styleFiles: normalizeStringArray(record.styleFiles),
98
+ minScore: typeof minScore === "number" ? minScore : undefined
99
+ };
100
+ }
101
+ catch {
102
+ return undefined;
103
+ }
104
+ };
@@ -0,0 +1,9 @@
1
+ import type { A11yLensConfig, A11yLensConfigOverrides } from "../types/config";
2
+ import type { ProjectScanResult } from "../types/results";
3
+ export interface RunOptions {
4
+ cwd?: string;
5
+ configFilePath?: string;
6
+ overrides?: A11yLensConfigOverrides;
7
+ }
8
+ export declare const runA11yLens: (roots: string[], options?: RunOptions) => ProjectScanResult;
9
+ export declare const resolveConfig: (cwd: string, configFilePath?: string, overrides?: A11yLensConfigOverrides) => A11yLensConfig;
@@ -0,0 +1,158 @@
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.resolveConfig = exports.runA11yLens = void 0;
37
+ const path = __importStar(require("path"));
38
+ const loader_1 = require("../config/loader");
39
+ const fileWalker_1 = require("../scanner/fileWalker");
40
+ const fileAnalyzer_1 = require("../scanner/fileAnalyzer");
41
+ const templateExtractor_1 = require("../scanner/templateExtractor");
42
+ const styleExtractor_1 = require("../scanner/styleExtractor");
43
+ const ast_1 = require("../parser/ast");
44
+ const engine_1 = require("../rules/engine");
45
+ const registry_1 = require("../rules/registry");
46
+ const styleResolver_1 = require("./styleResolver");
47
+ const severityWeights = {
48
+ error: 5,
49
+ warn: 2,
50
+ info: 1
51
+ };
52
+ const runA11yLens = (roots, options = {}) => {
53
+ var _a, _b, _c, _d, _e;
54
+ const cwd = (_a = options.cwd) !== null && _a !== void 0 ? _a : process.cwd();
55
+ const { config, configPath } = (0, loader_1.loadConfig)(cwd, options.configFilePath, options.overrides);
56
+ const filePaths = (0, fileWalker_1.fileSystemWalker)(roots, (0, fileWalker_1.defaultWalkOptions)(config));
57
+ const filePathSet = new Set(filePaths.map((filePath) => path.resolve(filePath)));
58
+ const issueMap = new Map();
59
+ const styleMap = new Map();
60
+ const baseDir = configPath ? path.dirname(configPath) : cwd;
61
+ const globalStyles = [];
62
+ const addIssues = (filePath, issues) => {
63
+ var _a;
64
+ if (issues.length === 0)
65
+ return;
66
+ const key = path.resolve(filePath);
67
+ const existing = (_a = issueMap.get(key)) !== null && _a !== void 0 ? _a : [];
68
+ existing.push(...issues);
69
+ issueMap.set(key, existing);
70
+ };
71
+ for (const filePath of filePaths) {
72
+ issueMap.set(path.resolve(filePath), []);
73
+ }
74
+ for (const styleFile of (_b = config.styleFiles) !== null && _b !== void 0 ? _b : []) {
75
+ const resolved = path.resolve(baseDir, styleFile);
76
+ const content = (0, fileAnalyzer_1.readFileContent)(resolved);
77
+ if (!content) {
78
+ addIssues(resolved, [
79
+ {
80
+ ruleId: "style-extraction",
81
+ message: `Unable to read style file: ${resolved}`,
82
+ filePath: resolved,
83
+ severity: "warn"
84
+ }
85
+ ]);
86
+ continue;
87
+ }
88
+ globalStyles.push(content);
89
+ }
90
+ for (const filePath of filePaths) {
91
+ if (!filePath.endsWith(".component.ts"))
92
+ continue;
93
+ const content = (0, fileAnalyzer_1.readFileContent)(filePath);
94
+ const extraction = (0, styleExtractor_1.extractStyles)(filePath, content);
95
+ addIssues(filePath, extraction.issues);
96
+ const collected = (_c = styleMap.get(filePath)) !== null && _c !== void 0 ? _c : [];
97
+ for (const style of extraction.styles) {
98
+ collected.push(style.content);
99
+ }
100
+ styleMap.set(filePath, collected);
101
+ }
102
+ for (const filePath of filePaths) {
103
+ const content = (0, fileAnalyzer_1.readFileContent)(filePath);
104
+ const extraction = (0, templateExtractor_1.extractTemplates)(filePath, content);
105
+ addIssues(filePath, extraction.issues);
106
+ for (const template of extraction.templates) {
107
+ if (template.kind === "external" &&
108
+ template.filePath !== filePath &&
109
+ filePathSet.has(path.resolve(template.filePath))) {
110
+ continue;
111
+ }
112
+ const templateKey = path.resolve(template.filePath);
113
+ if (!issueMap.has(templateKey)) {
114
+ issueMap.set(templateKey, []);
115
+ }
116
+ const styleSheets = [
117
+ ...globalStyles,
118
+ ...((_d = styleMap.get(template.originFilePath)) !== null && _d !== void 0 ? _d : [])
119
+ ];
120
+ const styleLookup = (0, styleResolver_1.buildStyleLookup)(styleSheets);
121
+ let ast;
122
+ try {
123
+ ast = (0, ast_1.parseTemplate)(template.content);
124
+ }
125
+ catch {
126
+ addIssues(template.filePath, [
127
+ {
128
+ ruleId: "template-parse",
129
+ message: "Failed to parse template content.",
130
+ filePath: template.filePath,
131
+ severity: "warn"
132
+ }
133
+ ]);
134
+ continue;
135
+ }
136
+ const issues = (0, engine_1.runRules)(registry_1.builtInRules, ast, template.filePath, template.content, styleLookup);
137
+ addIssues(template.filePath, issues);
138
+ }
139
+ }
140
+ const files = Array.from(issueMap.entries())
141
+ .sort(([a], [b]) => a.localeCompare(b))
142
+ .map(([filePath, issues]) => ({ filePath, issues }));
143
+ const totals = { error: 0, warn: 0, info: 0 };
144
+ let score = 100;
145
+ for (const { issues } of files) {
146
+ for (const issue of issues) {
147
+ totals[issue.severity] += 1;
148
+ score -= (_e = severityWeights[issue.severity]) !== null && _e !== void 0 ? _e : 0;
149
+ }
150
+ }
151
+ score = Math.max(0, Math.min(100, score));
152
+ return { files, score, totals };
153
+ };
154
+ exports.runA11yLens = runA11yLens;
155
+ const resolveConfig = (cwd, configFilePath, overrides) => {
156
+ return (0, loader_1.loadConfig)(cwd, configFilePath, overrides).config;
157
+ };
158
+ exports.resolveConfig = resolveConfig;
@@ -0,0 +1,2 @@
1
+ import type { StyleLookup } from "../types/style";
2
+ export declare const buildStyleLookup: (styleSheets: string[]) => StyleLookup;