next-a11y 0.1.4 → 0.1.5
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 +3 -0
- package/dist/cli/index.js +197 -51
- package/dist/cli/index.mjs +192 -46
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/cli/index.js
CHANGED
|
@@ -23,10 +23,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
));
|
|
24
24
|
|
|
25
25
|
// src/cli/index.ts
|
|
26
|
-
var
|
|
26
|
+
var import_dotenv2 = require("dotenv");
|
|
27
27
|
var import_commander = require("commander");
|
|
28
28
|
|
|
29
29
|
// src/cli/scan-command.ts
|
|
30
|
+
var fs7 = __toESM(require("fs"));
|
|
31
|
+
var path7 = __toESM(require("path"));
|
|
32
|
+
var import_dotenv = require("dotenv");
|
|
30
33
|
var import_picocolors4 = __toESM(require("picocolors"));
|
|
31
34
|
|
|
32
35
|
// src/config/resolve.ts
|
|
@@ -100,7 +103,7 @@ async function loadConfigFile(cwd) {
|
|
|
100
103
|
}
|
|
101
104
|
function resolveConfig(fileConfig, cliFlags = {}) {
|
|
102
105
|
const merged = deepMerge(DEFAULT_CONFIG, fileConfig);
|
|
103
|
-
const provider = cliFlags.provider ?? merged.provider;
|
|
106
|
+
const provider = cliFlags.provider ?? merged.provider ?? detectProviderFromEnv();
|
|
104
107
|
const model = cliFlags.model ?? merged.model ?? (provider ? PROVIDER_DEFAULTS[provider] : "gpt-4.1-nano");
|
|
105
108
|
return {
|
|
106
109
|
provider,
|
|
@@ -118,6 +121,12 @@ function resolveConfig(fileConfig, cliFlags = {}) {
|
|
|
118
121
|
minScore: cliFlags.minScore
|
|
119
122
|
};
|
|
120
123
|
}
|
|
124
|
+
function detectProviderFromEnv() {
|
|
125
|
+
for (const [name, envVar] of Object.entries(PROVIDER_ENV)) {
|
|
126
|
+
if (envVar && process.env[envVar]) return name;
|
|
127
|
+
}
|
|
128
|
+
return void 0;
|
|
129
|
+
}
|
|
121
130
|
function deepMerge(target, source) {
|
|
122
131
|
const result = { ...target };
|
|
123
132
|
for (const key of Object.keys(source)) {
|
|
@@ -146,13 +155,13 @@ async function discoverFiles(basePath, include, exclude) {
|
|
|
146
155
|
if (typeof fs2.glob === "function") {
|
|
147
156
|
for (const pattern of include) {
|
|
148
157
|
try {
|
|
149
|
-
const matches = await new Promise((
|
|
158
|
+
const matches = await new Promise((resolve5, reject) => {
|
|
150
159
|
fs2.glob(
|
|
151
160
|
pattern,
|
|
152
161
|
{ cwd: absBase },
|
|
153
162
|
(err, files) => {
|
|
154
163
|
if (err) reject(err);
|
|
155
|
-
else
|
|
164
|
+
else resolve5(files);
|
|
156
165
|
}
|
|
157
166
|
);
|
|
158
167
|
});
|
|
@@ -1924,7 +1933,18 @@ var fs5 = __toESM(require("fs"));
|
|
|
1924
1933
|
var path5 = __toESM(require("path"));
|
|
1925
1934
|
var https = __toESM(require("https"));
|
|
1926
1935
|
var http = __toESM(require("http"));
|
|
1936
|
+
var IMAGE_EXTENSIONS = [".png", ".jpg", ".jpeg", ".webp", ".gif", ".svg", ".avif"];
|
|
1937
|
+
function isImagePath(p) {
|
|
1938
|
+
return IMAGE_EXTENSIONS.some((ext) => p.endsWith(ext));
|
|
1939
|
+
}
|
|
1927
1940
|
async function resolveImageSource(src, file, projectRoot) {
|
|
1941
|
+
if (path5.isAbsolute(src) && isImagePath(src)) {
|
|
1942
|
+
try {
|
|
1943
|
+
const buffer = fs5.readFileSync(src);
|
|
1944
|
+
return { type: "file", buffer, path: src };
|
|
1945
|
+
} catch {
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1928
1948
|
if (src.startsWith("/")) {
|
|
1929
1949
|
const publicPath = path5.join(projectRoot, "public", src);
|
|
1930
1950
|
try {
|
|
@@ -1957,26 +1977,135 @@ async function resolveImageSource(src, file, projectRoot) {
|
|
|
1957
1977
|
}
|
|
1958
1978
|
return { type: "unresolvable", reason: "Dynamic image source" };
|
|
1959
1979
|
}
|
|
1960
|
-
function resolveStaticImportPath(importName, file) {
|
|
1980
|
+
function resolveStaticImportPath(importName, file, projectRoot) {
|
|
1981
|
+
let name = importName;
|
|
1982
|
+
if (name.endsWith(".src")) {
|
|
1983
|
+
name = name.slice(0, -4);
|
|
1984
|
+
}
|
|
1985
|
+
if (name.includes("[") || name.includes("(")) {
|
|
1986
|
+
return void 0;
|
|
1987
|
+
}
|
|
1961
1988
|
const imports = file.getImportDeclarations();
|
|
1989
|
+
const filePath = file.getFilePath();
|
|
1990
|
+
const root = projectRoot ?? findProjectRootFromFile(filePath);
|
|
1991
|
+
const project = file.getProject();
|
|
1962
1992
|
for (const imp of imports) {
|
|
1993
|
+
const moduleSpecifier = imp.getModuleSpecifierValue();
|
|
1963
1994
|
const defaultImport = imp.getDefaultImport();
|
|
1964
|
-
if (defaultImport?.getText() ===
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1995
|
+
if (defaultImport?.getText() === name) {
|
|
1996
|
+
return resolveModuleToImage(moduleSpecifier, filePath, root, void 0, project);
|
|
1997
|
+
}
|
|
1998
|
+
const namedImports = imp.getNamedImports();
|
|
1999
|
+
for (const named of namedImports) {
|
|
2000
|
+
if (named.getName() === name || named.getAliasNode()?.getText() === name) {
|
|
2001
|
+
const originalName = named.getName();
|
|
2002
|
+
return resolveModuleToImage(moduleSpecifier, filePath, root, originalName, project);
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
return void 0;
|
|
2007
|
+
}
|
|
2008
|
+
function resolveModuleToImage(moduleSpecifier, fromFile, projectRoot, namedExport, project) {
|
|
2009
|
+
if (isImagePath(moduleSpecifier)) {
|
|
2010
|
+
return resolveModulePath(moduleSpecifier, fromFile, projectRoot, project);
|
|
2011
|
+
}
|
|
2012
|
+
if (namedExport) {
|
|
2013
|
+
const barrelPath = resolveModulePath(moduleSpecifier, fromFile, projectRoot, project);
|
|
2014
|
+
if (barrelPath) {
|
|
2015
|
+
return followReExport(barrelPath, namedExport);
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
return void 0;
|
|
2019
|
+
}
|
|
2020
|
+
function resolveModulePath(moduleSpecifier, fromFile, projectRoot, project) {
|
|
2021
|
+
let resolved;
|
|
2022
|
+
if (moduleSpecifier.startsWith(".")) {
|
|
2023
|
+
resolved = path5.resolve(path5.dirname(fromFile), moduleSpecifier);
|
|
2024
|
+
} else {
|
|
2025
|
+
const aliasResolved = resolvePathAlias(moduleSpecifier, projectRoot, project);
|
|
2026
|
+
if (aliasResolved) {
|
|
2027
|
+
resolved = aliasResolved;
|
|
2028
|
+
} else {
|
|
2029
|
+
return void 0;
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
if (fs5.existsSync(resolved) && fs5.statSync(resolved).isFile()) {
|
|
2033
|
+
return resolved;
|
|
2034
|
+
}
|
|
2035
|
+
const extensions = [".ts", ".tsx", ".js", ".jsx", ...IMAGE_EXTENSIONS];
|
|
2036
|
+
for (const ext of extensions) {
|
|
2037
|
+
const withExt = resolved + ext;
|
|
2038
|
+
if (fs5.existsSync(withExt)) return withExt;
|
|
2039
|
+
}
|
|
2040
|
+
const indexFiles = ["index.ts", "index.tsx", "index.js", "index.jsx"];
|
|
2041
|
+
for (const idx of indexFiles) {
|
|
2042
|
+
const indexPath = path5.join(resolved, idx);
|
|
2043
|
+
if (fs5.existsSync(indexPath)) return indexPath;
|
|
2044
|
+
}
|
|
2045
|
+
return void 0;
|
|
2046
|
+
}
|
|
2047
|
+
function followReExport(barrelPath, exportName) {
|
|
2048
|
+
try {
|
|
2049
|
+
const content = fs5.readFileSync(barrelPath, "utf-8");
|
|
2050
|
+
const reExportPattern = new RegExp(
|
|
2051
|
+
`export\\s*\\{[^}]*\\b(?:default\\s+as\\s+)?${escapeRegex(exportName)}\\b[^}]*\\}\\s*from\\s*["']([^"']+)["']`
|
|
2052
|
+
);
|
|
2053
|
+
const match = content.match(reExportPattern);
|
|
2054
|
+
if (match) {
|
|
2055
|
+
const reExportPath = match[1];
|
|
2056
|
+
if (isImagePath(reExportPath)) {
|
|
2057
|
+
return path5.resolve(path5.dirname(barrelPath), reExportPath);
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
} catch {
|
|
2061
|
+
}
|
|
2062
|
+
return void 0;
|
|
2063
|
+
}
|
|
2064
|
+
function resolvePathAlias(moduleSpecifier, projectRoot, project) {
|
|
2065
|
+
if (!project) return void 0;
|
|
2066
|
+
const opts = project.getCompilerOptions();
|
|
2067
|
+
const paths = opts.paths;
|
|
2068
|
+
if (!paths) return void 0;
|
|
2069
|
+
const baseDir = opts.baseUrl ?? projectRoot;
|
|
2070
|
+
for (const [pattern, mappings] of Object.entries(paths)) {
|
|
2071
|
+
if (pattern.endsWith("/*")) {
|
|
2072
|
+
const prefix = pattern.slice(0, -1);
|
|
2073
|
+
if (moduleSpecifier.startsWith(prefix)) {
|
|
2074
|
+
const rest = moduleSpecifier.slice(prefix.length);
|
|
2075
|
+
for (const mapping of mappings) {
|
|
2076
|
+
const mappingBase = mapping.endsWith("/*") ? mapping.slice(0, -1) : mapping;
|
|
2077
|
+
const resolved = path5.resolve(baseDir, mappingBase + rest);
|
|
2078
|
+
return resolved;
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2081
|
+
} else if (pattern === moduleSpecifier) {
|
|
2082
|
+
if (mappings.length > 0) {
|
|
2083
|
+
return path5.resolve(baseDir, mappings[0]);
|
|
1969
2084
|
}
|
|
1970
2085
|
}
|
|
1971
2086
|
}
|
|
1972
2087
|
return void 0;
|
|
1973
2088
|
}
|
|
2089
|
+
function escapeRegex(str) {
|
|
2090
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2091
|
+
}
|
|
2092
|
+
function findProjectRootFromFile(filePath) {
|
|
2093
|
+
let dir = path5.dirname(filePath);
|
|
2094
|
+
while (dir !== path5.dirname(dir)) {
|
|
2095
|
+
if (fs5.existsSync(path5.join(dir, "package.json"))) return dir;
|
|
2096
|
+
if (fs5.existsSync(path5.join(dir, "next.config.js"))) return dir;
|
|
2097
|
+
if (fs5.existsSync(path5.join(dir, "next.config.mjs"))) return dir;
|
|
2098
|
+
if (fs5.existsSync(path5.join(dir, "next.config.ts"))) return dir;
|
|
2099
|
+
dir = path5.dirname(dir);
|
|
2100
|
+
}
|
|
2101
|
+
return path5.dirname(filePath);
|
|
2102
|
+
}
|
|
1974
2103
|
function fetchImage(url) {
|
|
1975
|
-
return new Promise((
|
|
2104
|
+
return new Promise((resolve5, reject) => {
|
|
1976
2105
|
const client = url.startsWith("https") ? https : http;
|
|
1977
2106
|
const req = client.get(url, { timeout: 1e4 }, (res) => {
|
|
1978
2107
|
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
1979
|
-
fetchImage(res.headers.location).then(
|
|
2108
|
+
fetchImage(res.headers.location).then(resolve5).catch(reject);
|
|
1980
2109
|
return;
|
|
1981
2110
|
}
|
|
1982
2111
|
const chunks = [];
|
|
@@ -1991,7 +2120,7 @@ function fetchImage(url) {
|
|
|
1991
2120
|
}
|
|
1992
2121
|
chunks.push(chunk);
|
|
1993
2122
|
});
|
|
1994
|
-
res.on("end", () =>
|
|
2123
|
+
res.on("end", () => resolve5(Buffer.concat(chunks)));
|
|
1995
2124
|
res.on("error", reject);
|
|
1996
2125
|
});
|
|
1997
2126
|
req.on("error", reject);
|
|
@@ -2064,6 +2193,8 @@ async function resolveAiFixes(opts) {
|
|
|
2064
2193
|
async function resolveImgAlt(file, violation, model, config2, cache) {
|
|
2065
2194
|
const el = findElement(file, violation.line);
|
|
2066
2195
|
if (!el) return "";
|
|
2196
|
+
const filePath = file.getFilePath();
|
|
2197
|
+
const projectRoot = findProjectRoot(filePath);
|
|
2067
2198
|
const srcAttr = el.getAttribute("src");
|
|
2068
2199
|
let srcValue = "";
|
|
2069
2200
|
if (srcAttr?.getKind() === import_ts_morph18.SyntaxKind.JsxAttribute) {
|
|
@@ -2074,13 +2205,11 @@ async function resolveImgAlt(file, violation, model, config2, cache) {
|
|
|
2074
2205
|
const expr = init.asKind(import_ts_morph18.SyntaxKind.JsxExpression)?.getExpression();
|
|
2075
2206
|
if (expr) {
|
|
2076
2207
|
const importName = expr.getText();
|
|
2077
|
-
const importPath = resolveStaticImportPath(importName, file);
|
|
2208
|
+
const importPath = resolveStaticImportPath(importName, file, projectRoot);
|
|
2078
2209
|
srcValue = importPath ?? importName;
|
|
2079
2210
|
}
|
|
2080
2211
|
}
|
|
2081
2212
|
}
|
|
2082
|
-
const filePath = file.getFilePath();
|
|
2083
|
-
const projectRoot = findProjectRoot(filePath);
|
|
2084
2213
|
const imageSource = await resolveImageSource(srcValue, file, projectRoot);
|
|
2085
2214
|
const context = extractContext(file);
|
|
2086
2215
|
const prompt = buildImgAltPrompt({
|
|
@@ -2167,17 +2296,17 @@ function findElement(file, line) {
|
|
|
2167
2296
|
return elements.find((el) => el.getStartLineNumber() === line);
|
|
2168
2297
|
}
|
|
2169
2298
|
function findProjectRoot(filePath) {
|
|
2170
|
-
const
|
|
2171
|
-
const
|
|
2172
|
-
let dir =
|
|
2173
|
-
while (dir !==
|
|
2174
|
-
if (
|
|
2175
|
-
if (
|
|
2176
|
-
if (
|
|
2177
|
-
if (
|
|
2178
|
-
dir =
|
|
2179
|
-
}
|
|
2180
|
-
return
|
|
2299
|
+
const path9 = require("path");
|
|
2300
|
+
const fs9 = require("fs");
|
|
2301
|
+
let dir = path9.dirname(filePath);
|
|
2302
|
+
while (dir !== path9.dirname(dir)) {
|
|
2303
|
+
if (fs9.existsSync(path9.join(dir, "package.json"))) return dir;
|
|
2304
|
+
if (fs9.existsSync(path9.join(dir, "next.config.js"))) return dir;
|
|
2305
|
+
if (fs9.existsSync(path9.join(dir, "next.config.mjs"))) return dir;
|
|
2306
|
+
if (fs9.existsSync(path9.join(dir, "next.config.ts"))) return dir;
|
|
2307
|
+
dir = path9.dirname(dir);
|
|
2308
|
+
}
|
|
2309
|
+
return path9.dirname(filePath);
|
|
2181
2310
|
}
|
|
2182
2311
|
|
|
2183
2312
|
// src/scan/scan.ts
|
|
@@ -2199,7 +2328,9 @@ async function detect(targetPath, config2) {
|
|
|
2199
2328
|
config2.scanner.include,
|
|
2200
2329
|
config2.scanner.exclude
|
|
2201
2330
|
);
|
|
2331
|
+
const tsconfigPath = path6.join(absPath, "tsconfig.json");
|
|
2202
2332
|
const project = new import_ts_morph19.Project({
|
|
2333
|
+
tsConfigFilePath: fs6.existsSync(tsconfigPath) ? tsconfigPath : void 0,
|
|
2203
2334
|
skipAddingFilesFromTsConfig: true,
|
|
2204
2335
|
compilerOptions: {
|
|
2205
2336
|
jsx: 4,
|
|
@@ -2505,7 +2636,7 @@ async function interactiveReview(violations, onAccept) {
|
|
|
2505
2636
|
return { applied, skipped };
|
|
2506
2637
|
}
|
|
2507
2638
|
function promptAction() {
|
|
2508
|
-
return new Promise((
|
|
2639
|
+
return new Promise((resolve5) => {
|
|
2509
2640
|
const rl = readline.createInterface({
|
|
2510
2641
|
input: process.stdin,
|
|
2511
2642
|
output: process.stdout
|
|
@@ -2516,10 +2647,10 @@ function promptAction() {
|
|
|
2516
2647
|
(answer) => {
|
|
2517
2648
|
rl.close();
|
|
2518
2649
|
const normalized = answer.trim().toLowerCase();
|
|
2519
|
-
if (normalized === "n" || normalized === "no")
|
|
2520
|
-
else if (normalized === "s" || normalized === "skip")
|
|
2521
|
-
else if (normalized === "q" || normalized === "quit")
|
|
2522
|
-
else
|
|
2650
|
+
if (normalized === "n" || normalized === "no") resolve5("no");
|
|
2651
|
+
else if (normalized === "s" || normalized === "skip") resolve5("skip");
|
|
2652
|
+
else if (normalized === "q" || normalized === "quit") resolve5("quit");
|
|
2653
|
+
else resolve5("yes");
|
|
2523
2654
|
}
|
|
2524
2655
|
);
|
|
2525
2656
|
});
|
|
@@ -2528,6 +2659,21 @@ function promptAction() {
|
|
|
2528
2659
|
// src/cli/scan-command.ts
|
|
2529
2660
|
function registerScanCommand(program2) {
|
|
2530
2661
|
program2.command("scan").description("Scan files for accessibility issues").argument("<path>", "Path to scan").option("--fix", "Auto-fix issues").option("-i, --interactive", "Review each fix interactively").option("--no-ai", "Skip AI-powered fixes").option("--provider <provider>", "Override AI provider").option("--model <model>", "Override AI model").option("--min-score <score>", "Minimum score threshold (exit code 1 if below)", parseInt).action(async (targetPath, options) => {
|
|
2662
|
+
let envDir = path7.resolve(targetPath);
|
|
2663
|
+
if (fs7.existsSync(envDir) && fs7.statSync(envDir).isFile()) {
|
|
2664
|
+
envDir = path7.dirname(envDir);
|
|
2665
|
+
}
|
|
2666
|
+
let searchDir = envDir;
|
|
2667
|
+
while (searchDir !== path7.dirname(searchDir)) {
|
|
2668
|
+
for (const envFile of [".env", ".env.local"]) {
|
|
2669
|
+
const envPath = path7.join(searchDir, envFile);
|
|
2670
|
+
if (fs7.existsSync(envPath)) {
|
|
2671
|
+
(0, import_dotenv.config)({ path: envPath, override: false, quiet: true });
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
if (fs7.existsSync(path7.join(searchDir, "package.json"))) break;
|
|
2675
|
+
searchDir = path7.dirname(searchDir);
|
|
2676
|
+
}
|
|
2531
2677
|
const fileConfig = await loadConfigFile(process.cwd());
|
|
2532
2678
|
const config2 = resolveConfig(fileConfig, {
|
|
2533
2679
|
fix: options.fix,
|
|
@@ -2600,8 +2746,8 @@ function registerScanCommand(program2) {
|
|
|
2600
2746
|
}
|
|
2601
2747
|
|
|
2602
2748
|
// src/cli/init-command.ts
|
|
2603
|
-
var
|
|
2604
|
-
var
|
|
2749
|
+
var fs8 = __toESM(require("fs"));
|
|
2750
|
+
var path8 = __toESM(require("path"));
|
|
2605
2751
|
var readline2 = __toESM(require("readline"));
|
|
2606
2752
|
var import_node_child_process = require("child_process");
|
|
2607
2753
|
var import_picocolors5 = __toESM(require("picocolors"));
|
|
@@ -2611,15 +2757,15 @@ function registerInitCommand(program2) {
|
|
|
2611
2757
|
console.log(import_picocolors5.default.bold("\n next-a11y v0.1.4 \u2014 Setup\n"));
|
|
2612
2758
|
const options = await promptInitOptions();
|
|
2613
2759
|
const cwd = process.cwd();
|
|
2614
|
-
const hasAppDir =
|
|
2615
|
-
const hasSrcDir =
|
|
2760
|
+
const hasAppDir = fs8.existsSync(path8.join(cwd, "app"));
|
|
2761
|
+
const hasSrcDir = fs8.existsSync(path8.join(cwd, "src"));
|
|
2616
2762
|
const include = [];
|
|
2617
2763
|
if (hasSrcDir) include.push("src/**/*.{tsx,jsx}");
|
|
2618
2764
|
if (hasAppDir) include.push("app/**/*.{tsx,jsx}");
|
|
2619
2765
|
if (include.length === 0) include.push("**/*.{tsx,jsx}");
|
|
2620
2766
|
const configContent = generateConfig(options.provider, include);
|
|
2621
|
-
const configPath =
|
|
2622
|
-
|
|
2767
|
+
const configPath = path8.join(cwd, "a11y.config.ts");
|
|
2768
|
+
fs8.writeFileSync(configPath, configContent);
|
|
2623
2769
|
console.log(import_picocolors5.default.green(" Created a11y.config.ts"));
|
|
2624
2770
|
if (options.provider !== "none" && options.installDep) {
|
|
2625
2771
|
const pkgMap = {
|
|
@@ -2643,14 +2789,14 @@ function registerInitCommand(program2) {
|
|
|
2643
2789
|
}
|
|
2644
2790
|
}
|
|
2645
2791
|
if (options.addGitignore) {
|
|
2646
|
-
const gitignorePath =
|
|
2792
|
+
const gitignorePath = path8.join(cwd, ".gitignore");
|
|
2647
2793
|
let content = "";
|
|
2648
|
-
if (
|
|
2649
|
-
content =
|
|
2794
|
+
if (fs8.existsSync(gitignorePath)) {
|
|
2795
|
+
content = fs8.readFileSync(gitignorePath, "utf-8");
|
|
2650
2796
|
}
|
|
2651
2797
|
if (!content.includes(".a11y-cache")) {
|
|
2652
2798
|
const newline = content.endsWith("\n") ? "" : "\n";
|
|
2653
|
-
|
|
2799
|
+
fs8.appendFileSync(gitignorePath, `${newline}.a11y-cache
|
|
2654
2800
|
`);
|
|
2655
2801
|
console.log(import_picocolors5.default.green(" Updated .gitignore"));
|
|
2656
2802
|
}
|
|
@@ -2691,7 +2837,7 @@ async function promptInitOptions() {
|
|
|
2691
2837
|
return { provider, installDep, addGitignore };
|
|
2692
2838
|
}
|
|
2693
2839
|
function promptSelect(question, options) {
|
|
2694
|
-
return new Promise((
|
|
2840
|
+
return new Promise((resolve5) => {
|
|
2695
2841
|
const rl = readline2.createInterface({
|
|
2696
2842
|
input: process.stdin,
|
|
2697
2843
|
output: process.stdout
|
|
@@ -2704,15 +2850,15 @@ function promptSelect(question, options) {
|
|
|
2704
2850
|
rl.close();
|
|
2705
2851
|
const idx = parseInt(answer.trim()) - 1;
|
|
2706
2852
|
if (idx >= 0 && idx < options.length) {
|
|
2707
|
-
|
|
2853
|
+
resolve5(options[idx].value);
|
|
2708
2854
|
} else {
|
|
2709
|
-
|
|
2855
|
+
resolve5(options[0].value);
|
|
2710
2856
|
}
|
|
2711
2857
|
});
|
|
2712
2858
|
});
|
|
2713
2859
|
}
|
|
2714
2860
|
function promptYesNo(question) {
|
|
2715
|
-
return new Promise((
|
|
2861
|
+
return new Promise((resolve5) => {
|
|
2716
2862
|
const rl = readline2.createInterface({
|
|
2717
2863
|
input: process.stdin,
|
|
2718
2864
|
output: process.stdout
|
|
@@ -2720,7 +2866,7 @@ function promptYesNo(question) {
|
|
|
2720
2866
|
rl.question(` ${question} ${import_picocolors5.default.dim("[Y/n]")} `, (answer) => {
|
|
2721
2867
|
rl.close();
|
|
2722
2868
|
const normalized = answer.trim().toLowerCase();
|
|
2723
|
-
|
|
2869
|
+
resolve5(normalized !== "n" && normalized !== "no");
|
|
2724
2870
|
});
|
|
2725
2871
|
});
|
|
2726
2872
|
}
|
|
@@ -2798,10 +2944,10 @@ function formatBytes(bytes) {
|
|
|
2798
2944
|
}
|
|
2799
2945
|
|
|
2800
2946
|
// src/cli/index.ts
|
|
2801
|
-
(0,
|
|
2802
|
-
(0,
|
|
2803
|
-
(0,
|
|
2804
|
-
(0,
|
|
2947
|
+
(0, import_dotenv2.config)({ path: ".env", override: false, quiet: true });
|
|
2948
|
+
(0, import_dotenv2.config)({ path: ".env.local", override: true, quiet: true });
|
|
2949
|
+
(0, import_dotenv2.config)({ path: ".env.development", override: true, quiet: true });
|
|
2950
|
+
(0, import_dotenv2.config)({ path: ".env.development.local", override: true, quiet: true });
|
|
2805
2951
|
var program = new import_commander.Command();
|
|
2806
2952
|
program.name("next-a11y").description("AI-powered accessibility codemod for Next.js").version("0.1.4");
|
|
2807
2953
|
registerScanCommand(program);
|
package/dist/cli/index.mjs
CHANGED
|
@@ -11,6 +11,9 @@ import { config } from "dotenv";
|
|
|
11
11
|
import { Command } from "commander";
|
|
12
12
|
|
|
13
13
|
// src/cli/scan-command.ts
|
|
14
|
+
import * as fs7 from "fs";
|
|
15
|
+
import * as path7 from "path";
|
|
16
|
+
import { config as dotenvConfig } from "dotenv";
|
|
14
17
|
import pc4 from "picocolors";
|
|
15
18
|
|
|
16
19
|
// src/config/resolve.ts
|
|
@@ -42,7 +45,7 @@ async function loadConfigFile(cwd) {
|
|
|
42
45
|
}
|
|
43
46
|
function resolveConfig(fileConfig, cliFlags = {}) {
|
|
44
47
|
const merged = deepMerge(DEFAULT_CONFIG, fileConfig);
|
|
45
|
-
const provider = cliFlags.provider ?? merged.provider;
|
|
48
|
+
const provider = cliFlags.provider ?? merged.provider ?? detectProviderFromEnv();
|
|
46
49
|
const model = cliFlags.model ?? merged.model ?? (provider ? PROVIDER_DEFAULTS[provider] : "gpt-4.1-nano");
|
|
47
50
|
return {
|
|
48
51
|
provider,
|
|
@@ -60,6 +63,12 @@ function resolveConfig(fileConfig, cliFlags = {}) {
|
|
|
60
63
|
minScore: cliFlags.minScore
|
|
61
64
|
};
|
|
62
65
|
}
|
|
66
|
+
function detectProviderFromEnv() {
|
|
67
|
+
for (const [name, envVar] of Object.entries(PROVIDER_ENV)) {
|
|
68
|
+
if (envVar && process.env[envVar]) return name;
|
|
69
|
+
}
|
|
70
|
+
return void 0;
|
|
71
|
+
}
|
|
63
72
|
function deepMerge(target, source) {
|
|
64
73
|
const result = { ...target };
|
|
65
74
|
for (const key of Object.keys(source)) {
|
|
@@ -88,13 +97,13 @@ async function discoverFiles(basePath, include, exclude) {
|
|
|
88
97
|
if (typeof fs2.glob === "function") {
|
|
89
98
|
for (const pattern of include) {
|
|
90
99
|
try {
|
|
91
|
-
const matches = await new Promise((
|
|
100
|
+
const matches = await new Promise((resolve5, reject) => {
|
|
92
101
|
fs2.glob(
|
|
93
102
|
pattern,
|
|
94
103
|
{ cwd: absBase },
|
|
95
104
|
(err, files) => {
|
|
96
105
|
if (err) reject(err);
|
|
97
|
-
else
|
|
106
|
+
else resolve5(files);
|
|
98
107
|
}
|
|
99
108
|
);
|
|
100
109
|
});
|
|
@@ -1866,7 +1875,18 @@ import * as fs5 from "fs";
|
|
|
1866
1875
|
import * as path5 from "path";
|
|
1867
1876
|
import * as https from "https";
|
|
1868
1877
|
import * as http from "http";
|
|
1878
|
+
var IMAGE_EXTENSIONS = [".png", ".jpg", ".jpeg", ".webp", ".gif", ".svg", ".avif"];
|
|
1879
|
+
function isImagePath(p) {
|
|
1880
|
+
return IMAGE_EXTENSIONS.some((ext) => p.endsWith(ext));
|
|
1881
|
+
}
|
|
1869
1882
|
async function resolveImageSource(src, file, projectRoot) {
|
|
1883
|
+
if (path5.isAbsolute(src) && isImagePath(src)) {
|
|
1884
|
+
try {
|
|
1885
|
+
const buffer = fs5.readFileSync(src);
|
|
1886
|
+
return { type: "file", buffer, path: src };
|
|
1887
|
+
} catch {
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1870
1890
|
if (src.startsWith("/")) {
|
|
1871
1891
|
const publicPath = path5.join(projectRoot, "public", src);
|
|
1872
1892
|
try {
|
|
@@ -1899,26 +1919,135 @@ async function resolveImageSource(src, file, projectRoot) {
|
|
|
1899
1919
|
}
|
|
1900
1920
|
return { type: "unresolvable", reason: "Dynamic image source" };
|
|
1901
1921
|
}
|
|
1902
|
-
function resolveStaticImportPath(importName, file) {
|
|
1922
|
+
function resolveStaticImportPath(importName, file, projectRoot) {
|
|
1923
|
+
let name = importName;
|
|
1924
|
+
if (name.endsWith(".src")) {
|
|
1925
|
+
name = name.slice(0, -4);
|
|
1926
|
+
}
|
|
1927
|
+
if (name.includes("[") || name.includes("(")) {
|
|
1928
|
+
return void 0;
|
|
1929
|
+
}
|
|
1903
1930
|
const imports = file.getImportDeclarations();
|
|
1931
|
+
const filePath = file.getFilePath();
|
|
1932
|
+
const root = projectRoot ?? findProjectRootFromFile(filePath);
|
|
1933
|
+
const project = file.getProject();
|
|
1904
1934
|
for (const imp of imports) {
|
|
1935
|
+
const moduleSpecifier = imp.getModuleSpecifierValue();
|
|
1905
1936
|
const defaultImport = imp.getDefaultImport();
|
|
1906
|
-
if (defaultImport?.getText() ===
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1937
|
+
if (defaultImport?.getText() === name) {
|
|
1938
|
+
return resolveModuleToImage(moduleSpecifier, filePath, root, void 0, project);
|
|
1939
|
+
}
|
|
1940
|
+
const namedImports = imp.getNamedImports();
|
|
1941
|
+
for (const named of namedImports) {
|
|
1942
|
+
if (named.getName() === name || named.getAliasNode()?.getText() === name) {
|
|
1943
|
+
const originalName = named.getName();
|
|
1944
|
+
return resolveModuleToImage(moduleSpecifier, filePath, root, originalName, project);
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
return void 0;
|
|
1949
|
+
}
|
|
1950
|
+
function resolveModuleToImage(moduleSpecifier, fromFile, projectRoot, namedExport, project) {
|
|
1951
|
+
if (isImagePath(moduleSpecifier)) {
|
|
1952
|
+
return resolveModulePath(moduleSpecifier, fromFile, projectRoot, project);
|
|
1953
|
+
}
|
|
1954
|
+
if (namedExport) {
|
|
1955
|
+
const barrelPath = resolveModulePath(moduleSpecifier, fromFile, projectRoot, project);
|
|
1956
|
+
if (barrelPath) {
|
|
1957
|
+
return followReExport(barrelPath, namedExport);
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
return void 0;
|
|
1961
|
+
}
|
|
1962
|
+
function resolveModulePath(moduleSpecifier, fromFile, projectRoot, project) {
|
|
1963
|
+
let resolved;
|
|
1964
|
+
if (moduleSpecifier.startsWith(".")) {
|
|
1965
|
+
resolved = path5.resolve(path5.dirname(fromFile), moduleSpecifier);
|
|
1966
|
+
} else {
|
|
1967
|
+
const aliasResolved = resolvePathAlias(moduleSpecifier, projectRoot, project);
|
|
1968
|
+
if (aliasResolved) {
|
|
1969
|
+
resolved = aliasResolved;
|
|
1970
|
+
} else {
|
|
1971
|
+
return void 0;
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
if (fs5.existsSync(resolved) && fs5.statSync(resolved).isFile()) {
|
|
1975
|
+
return resolved;
|
|
1976
|
+
}
|
|
1977
|
+
const extensions = [".ts", ".tsx", ".js", ".jsx", ...IMAGE_EXTENSIONS];
|
|
1978
|
+
for (const ext of extensions) {
|
|
1979
|
+
const withExt = resolved + ext;
|
|
1980
|
+
if (fs5.existsSync(withExt)) return withExt;
|
|
1981
|
+
}
|
|
1982
|
+
const indexFiles = ["index.ts", "index.tsx", "index.js", "index.jsx"];
|
|
1983
|
+
for (const idx of indexFiles) {
|
|
1984
|
+
const indexPath = path5.join(resolved, idx);
|
|
1985
|
+
if (fs5.existsSync(indexPath)) return indexPath;
|
|
1986
|
+
}
|
|
1987
|
+
return void 0;
|
|
1988
|
+
}
|
|
1989
|
+
function followReExport(barrelPath, exportName) {
|
|
1990
|
+
try {
|
|
1991
|
+
const content = fs5.readFileSync(barrelPath, "utf-8");
|
|
1992
|
+
const reExportPattern = new RegExp(
|
|
1993
|
+
`export\\s*\\{[^}]*\\b(?:default\\s+as\\s+)?${escapeRegex(exportName)}\\b[^}]*\\}\\s*from\\s*["']([^"']+)["']`
|
|
1994
|
+
);
|
|
1995
|
+
const match = content.match(reExportPattern);
|
|
1996
|
+
if (match) {
|
|
1997
|
+
const reExportPath = match[1];
|
|
1998
|
+
if (isImagePath(reExportPath)) {
|
|
1999
|
+
return path5.resolve(path5.dirname(barrelPath), reExportPath);
|
|
1911
2000
|
}
|
|
1912
2001
|
}
|
|
2002
|
+
} catch {
|
|
1913
2003
|
}
|
|
1914
2004
|
return void 0;
|
|
1915
2005
|
}
|
|
2006
|
+
function resolvePathAlias(moduleSpecifier, projectRoot, project) {
|
|
2007
|
+
if (!project) return void 0;
|
|
2008
|
+
const opts = project.getCompilerOptions();
|
|
2009
|
+
const paths = opts.paths;
|
|
2010
|
+
if (!paths) return void 0;
|
|
2011
|
+
const baseDir = opts.baseUrl ?? projectRoot;
|
|
2012
|
+
for (const [pattern, mappings] of Object.entries(paths)) {
|
|
2013
|
+
if (pattern.endsWith("/*")) {
|
|
2014
|
+
const prefix = pattern.slice(0, -1);
|
|
2015
|
+
if (moduleSpecifier.startsWith(prefix)) {
|
|
2016
|
+
const rest = moduleSpecifier.slice(prefix.length);
|
|
2017
|
+
for (const mapping of mappings) {
|
|
2018
|
+
const mappingBase = mapping.endsWith("/*") ? mapping.slice(0, -1) : mapping;
|
|
2019
|
+
const resolved = path5.resolve(baseDir, mappingBase + rest);
|
|
2020
|
+
return resolved;
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
} else if (pattern === moduleSpecifier) {
|
|
2024
|
+
if (mappings.length > 0) {
|
|
2025
|
+
return path5.resolve(baseDir, mappings[0]);
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
return void 0;
|
|
2030
|
+
}
|
|
2031
|
+
function escapeRegex(str) {
|
|
2032
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2033
|
+
}
|
|
2034
|
+
function findProjectRootFromFile(filePath) {
|
|
2035
|
+
let dir = path5.dirname(filePath);
|
|
2036
|
+
while (dir !== path5.dirname(dir)) {
|
|
2037
|
+
if (fs5.existsSync(path5.join(dir, "package.json"))) return dir;
|
|
2038
|
+
if (fs5.existsSync(path5.join(dir, "next.config.js"))) return dir;
|
|
2039
|
+
if (fs5.existsSync(path5.join(dir, "next.config.mjs"))) return dir;
|
|
2040
|
+
if (fs5.existsSync(path5.join(dir, "next.config.ts"))) return dir;
|
|
2041
|
+
dir = path5.dirname(dir);
|
|
2042
|
+
}
|
|
2043
|
+
return path5.dirname(filePath);
|
|
2044
|
+
}
|
|
1916
2045
|
function fetchImage(url) {
|
|
1917
|
-
return new Promise((
|
|
2046
|
+
return new Promise((resolve5, reject) => {
|
|
1918
2047
|
const client = url.startsWith("https") ? https : http;
|
|
1919
2048
|
const req = client.get(url, { timeout: 1e4 }, (res) => {
|
|
1920
2049
|
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
1921
|
-
fetchImage(res.headers.location).then(
|
|
2050
|
+
fetchImage(res.headers.location).then(resolve5).catch(reject);
|
|
1922
2051
|
return;
|
|
1923
2052
|
}
|
|
1924
2053
|
const chunks = [];
|
|
@@ -1933,7 +2062,7 @@ function fetchImage(url) {
|
|
|
1933
2062
|
}
|
|
1934
2063
|
chunks.push(chunk);
|
|
1935
2064
|
});
|
|
1936
|
-
res.on("end", () =>
|
|
2065
|
+
res.on("end", () => resolve5(Buffer.concat(chunks)));
|
|
1937
2066
|
res.on("error", reject);
|
|
1938
2067
|
});
|
|
1939
2068
|
req.on("error", reject);
|
|
@@ -2006,6 +2135,8 @@ async function resolveAiFixes(opts) {
|
|
|
2006
2135
|
async function resolveImgAlt(file, violation, model, config2, cache) {
|
|
2007
2136
|
const el = findElement(file, violation.line);
|
|
2008
2137
|
if (!el) return "";
|
|
2138
|
+
const filePath = file.getFilePath();
|
|
2139
|
+
const projectRoot = findProjectRoot(filePath);
|
|
2009
2140
|
const srcAttr = el.getAttribute("src");
|
|
2010
2141
|
let srcValue = "";
|
|
2011
2142
|
if (srcAttr?.getKind() === SyntaxKind18.JsxAttribute) {
|
|
@@ -2016,13 +2147,11 @@ async function resolveImgAlt(file, violation, model, config2, cache) {
|
|
|
2016
2147
|
const expr = init.asKind(SyntaxKind18.JsxExpression)?.getExpression();
|
|
2017
2148
|
if (expr) {
|
|
2018
2149
|
const importName = expr.getText();
|
|
2019
|
-
const importPath = resolveStaticImportPath(importName, file);
|
|
2150
|
+
const importPath = resolveStaticImportPath(importName, file, projectRoot);
|
|
2020
2151
|
srcValue = importPath ?? importName;
|
|
2021
2152
|
}
|
|
2022
2153
|
}
|
|
2023
2154
|
}
|
|
2024
|
-
const filePath = file.getFilePath();
|
|
2025
|
-
const projectRoot = findProjectRoot(filePath);
|
|
2026
2155
|
const imageSource = await resolveImageSource(srcValue, file, projectRoot);
|
|
2027
2156
|
const context = extractContext(file);
|
|
2028
2157
|
const prompt = buildImgAltPrompt({
|
|
@@ -2109,17 +2238,17 @@ function findElement(file, line) {
|
|
|
2109
2238
|
return elements.find((el) => el.getStartLineNumber() === line);
|
|
2110
2239
|
}
|
|
2111
2240
|
function findProjectRoot(filePath) {
|
|
2112
|
-
const
|
|
2113
|
-
const
|
|
2114
|
-
let dir =
|
|
2115
|
-
while (dir !==
|
|
2116
|
-
if (
|
|
2117
|
-
if (
|
|
2118
|
-
if (
|
|
2119
|
-
if (
|
|
2120
|
-
dir =
|
|
2121
|
-
}
|
|
2122
|
-
return
|
|
2241
|
+
const path9 = __require("path");
|
|
2242
|
+
const fs9 = __require("fs");
|
|
2243
|
+
let dir = path9.dirname(filePath);
|
|
2244
|
+
while (dir !== path9.dirname(dir)) {
|
|
2245
|
+
if (fs9.existsSync(path9.join(dir, "package.json"))) return dir;
|
|
2246
|
+
if (fs9.existsSync(path9.join(dir, "next.config.js"))) return dir;
|
|
2247
|
+
if (fs9.existsSync(path9.join(dir, "next.config.mjs"))) return dir;
|
|
2248
|
+
if (fs9.existsSync(path9.join(dir, "next.config.ts"))) return dir;
|
|
2249
|
+
dir = path9.dirname(dir);
|
|
2250
|
+
}
|
|
2251
|
+
return path9.dirname(filePath);
|
|
2123
2252
|
}
|
|
2124
2253
|
|
|
2125
2254
|
// src/scan/scan.ts
|
|
@@ -2141,7 +2270,9 @@ async function detect(targetPath, config2) {
|
|
|
2141
2270
|
config2.scanner.include,
|
|
2142
2271
|
config2.scanner.exclude
|
|
2143
2272
|
);
|
|
2273
|
+
const tsconfigPath = path6.join(absPath, "tsconfig.json");
|
|
2144
2274
|
const project = new Project2({
|
|
2275
|
+
tsConfigFilePath: fs6.existsSync(tsconfigPath) ? tsconfigPath : void 0,
|
|
2145
2276
|
skipAddingFilesFromTsConfig: true,
|
|
2146
2277
|
compilerOptions: {
|
|
2147
2278
|
jsx: 4,
|
|
@@ -2447,7 +2578,7 @@ async function interactiveReview(violations, onAccept) {
|
|
|
2447
2578
|
return { applied, skipped };
|
|
2448
2579
|
}
|
|
2449
2580
|
function promptAction() {
|
|
2450
|
-
return new Promise((
|
|
2581
|
+
return new Promise((resolve5) => {
|
|
2451
2582
|
const rl = readline.createInterface({
|
|
2452
2583
|
input: process.stdin,
|
|
2453
2584
|
output: process.stdout
|
|
@@ -2458,10 +2589,10 @@ function promptAction() {
|
|
|
2458
2589
|
(answer) => {
|
|
2459
2590
|
rl.close();
|
|
2460
2591
|
const normalized = answer.trim().toLowerCase();
|
|
2461
|
-
if (normalized === "n" || normalized === "no")
|
|
2462
|
-
else if (normalized === "s" || normalized === "skip")
|
|
2463
|
-
else if (normalized === "q" || normalized === "quit")
|
|
2464
|
-
else
|
|
2592
|
+
if (normalized === "n" || normalized === "no") resolve5("no");
|
|
2593
|
+
else if (normalized === "s" || normalized === "skip") resolve5("skip");
|
|
2594
|
+
else if (normalized === "q" || normalized === "quit") resolve5("quit");
|
|
2595
|
+
else resolve5("yes");
|
|
2465
2596
|
}
|
|
2466
2597
|
);
|
|
2467
2598
|
});
|
|
@@ -2470,6 +2601,21 @@ function promptAction() {
|
|
|
2470
2601
|
// src/cli/scan-command.ts
|
|
2471
2602
|
function registerScanCommand(program2) {
|
|
2472
2603
|
program2.command("scan").description("Scan files for accessibility issues").argument("<path>", "Path to scan").option("--fix", "Auto-fix issues").option("-i, --interactive", "Review each fix interactively").option("--no-ai", "Skip AI-powered fixes").option("--provider <provider>", "Override AI provider").option("--model <model>", "Override AI model").option("--min-score <score>", "Minimum score threshold (exit code 1 if below)", parseInt).action(async (targetPath, options) => {
|
|
2604
|
+
let envDir = path7.resolve(targetPath);
|
|
2605
|
+
if (fs7.existsSync(envDir) && fs7.statSync(envDir).isFile()) {
|
|
2606
|
+
envDir = path7.dirname(envDir);
|
|
2607
|
+
}
|
|
2608
|
+
let searchDir = envDir;
|
|
2609
|
+
while (searchDir !== path7.dirname(searchDir)) {
|
|
2610
|
+
for (const envFile of [".env", ".env.local"]) {
|
|
2611
|
+
const envPath = path7.join(searchDir, envFile);
|
|
2612
|
+
if (fs7.existsSync(envPath)) {
|
|
2613
|
+
dotenvConfig({ path: envPath, override: false, quiet: true });
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
if (fs7.existsSync(path7.join(searchDir, "package.json"))) break;
|
|
2617
|
+
searchDir = path7.dirname(searchDir);
|
|
2618
|
+
}
|
|
2473
2619
|
const fileConfig = await loadConfigFile(process.cwd());
|
|
2474
2620
|
const config2 = resolveConfig(fileConfig, {
|
|
2475
2621
|
fix: options.fix,
|
|
@@ -2542,8 +2688,8 @@ function registerScanCommand(program2) {
|
|
|
2542
2688
|
}
|
|
2543
2689
|
|
|
2544
2690
|
// src/cli/init-command.ts
|
|
2545
|
-
import * as
|
|
2546
|
-
import * as
|
|
2691
|
+
import * as fs8 from "fs";
|
|
2692
|
+
import * as path8 from "path";
|
|
2547
2693
|
import * as readline2 from "readline";
|
|
2548
2694
|
import { execSync } from "child_process";
|
|
2549
2695
|
import pc5 from "picocolors";
|
|
@@ -2553,15 +2699,15 @@ function registerInitCommand(program2) {
|
|
|
2553
2699
|
console.log(pc5.bold("\n next-a11y v0.1.4 \u2014 Setup\n"));
|
|
2554
2700
|
const options = await promptInitOptions();
|
|
2555
2701
|
const cwd = process.cwd();
|
|
2556
|
-
const hasAppDir =
|
|
2557
|
-
const hasSrcDir =
|
|
2702
|
+
const hasAppDir = fs8.existsSync(path8.join(cwd, "app"));
|
|
2703
|
+
const hasSrcDir = fs8.existsSync(path8.join(cwd, "src"));
|
|
2558
2704
|
const include = [];
|
|
2559
2705
|
if (hasSrcDir) include.push("src/**/*.{tsx,jsx}");
|
|
2560
2706
|
if (hasAppDir) include.push("app/**/*.{tsx,jsx}");
|
|
2561
2707
|
if (include.length === 0) include.push("**/*.{tsx,jsx}");
|
|
2562
2708
|
const configContent = generateConfig(options.provider, include);
|
|
2563
|
-
const configPath =
|
|
2564
|
-
|
|
2709
|
+
const configPath = path8.join(cwd, "a11y.config.ts");
|
|
2710
|
+
fs8.writeFileSync(configPath, configContent);
|
|
2565
2711
|
console.log(pc5.green(" Created a11y.config.ts"));
|
|
2566
2712
|
if (options.provider !== "none" && options.installDep) {
|
|
2567
2713
|
const pkgMap = {
|
|
@@ -2585,14 +2731,14 @@ function registerInitCommand(program2) {
|
|
|
2585
2731
|
}
|
|
2586
2732
|
}
|
|
2587
2733
|
if (options.addGitignore) {
|
|
2588
|
-
const gitignorePath =
|
|
2734
|
+
const gitignorePath = path8.join(cwd, ".gitignore");
|
|
2589
2735
|
let content = "";
|
|
2590
|
-
if (
|
|
2591
|
-
content =
|
|
2736
|
+
if (fs8.existsSync(gitignorePath)) {
|
|
2737
|
+
content = fs8.readFileSync(gitignorePath, "utf-8");
|
|
2592
2738
|
}
|
|
2593
2739
|
if (!content.includes(".a11y-cache")) {
|
|
2594
2740
|
const newline = content.endsWith("\n") ? "" : "\n";
|
|
2595
|
-
|
|
2741
|
+
fs8.appendFileSync(gitignorePath, `${newline}.a11y-cache
|
|
2596
2742
|
`);
|
|
2597
2743
|
console.log(pc5.green(" Updated .gitignore"));
|
|
2598
2744
|
}
|
|
@@ -2633,7 +2779,7 @@ async function promptInitOptions() {
|
|
|
2633
2779
|
return { provider, installDep, addGitignore };
|
|
2634
2780
|
}
|
|
2635
2781
|
function promptSelect(question, options) {
|
|
2636
|
-
return new Promise((
|
|
2782
|
+
return new Promise((resolve5) => {
|
|
2637
2783
|
const rl = readline2.createInterface({
|
|
2638
2784
|
input: process.stdin,
|
|
2639
2785
|
output: process.stdout
|
|
@@ -2646,15 +2792,15 @@ function promptSelect(question, options) {
|
|
|
2646
2792
|
rl.close();
|
|
2647
2793
|
const idx = parseInt(answer.trim()) - 1;
|
|
2648
2794
|
if (idx >= 0 && idx < options.length) {
|
|
2649
|
-
|
|
2795
|
+
resolve5(options[idx].value);
|
|
2650
2796
|
} else {
|
|
2651
|
-
|
|
2797
|
+
resolve5(options[0].value);
|
|
2652
2798
|
}
|
|
2653
2799
|
});
|
|
2654
2800
|
});
|
|
2655
2801
|
}
|
|
2656
2802
|
function promptYesNo(question) {
|
|
2657
|
-
return new Promise((
|
|
2803
|
+
return new Promise((resolve5) => {
|
|
2658
2804
|
const rl = readline2.createInterface({
|
|
2659
2805
|
input: process.stdin,
|
|
2660
2806
|
output: process.stdout
|
|
@@ -2662,7 +2808,7 @@ function promptYesNo(question) {
|
|
|
2662
2808
|
rl.question(` ${question} ${pc5.dim("[Y/n]")} `, (answer) => {
|
|
2663
2809
|
rl.close();
|
|
2664
2810
|
const normalized = answer.trim().toLowerCase();
|
|
2665
|
-
|
|
2811
|
+
resolve5(normalized !== "n" && normalized !== "no");
|
|
2666
2812
|
});
|
|
2667
2813
|
});
|
|
2668
2814
|
}
|