next-a11y 0.1.3 → 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 CHANGED
@@ -1,3 +1,6 @@
1
+ > [!IMPORTANT]
2
+ > THIS PACKAGE IS IN ACTIVE DEVELOPMENT
3
+
1
4
  # next-a11y
2
5
 
3
6
  **Finds accessibility violations in your Next.js source code. Writes the fix.**
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 import_dotenv = require("dotenv");
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((resolve4, reject) => {
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 resolve4(files);
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() === importName) {
1965
- const moduleSpecifier = imp.getModuleSpecifierValue();
1966
- if (moduleSpecifier.endsWith(".png") || moduleSpecifier.endsWith(".jpg") || moduleSpecifier.endsWith(".jpeg") || moduleSpecifier.endsWith(".webp") || moduleSpecifier.endsWith(".gif") || moduleSpecifier.endsWith(".svg") || moduleSpecifier.endsWith(".avif")) {
1967
- const filePath = file.getFilePath();
1968
- return path5.resolve(path5.dirname(filePath), moduleSpecifier);
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);
1969
2003
  }
1970
2004
  }
1971
2005
  }
1972
2006
  return void 0;
1973
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]);
2084
+ }
2085
+ }
2086
+ }
2087
+ return void 0;
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((resolve4, reject) => {
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(resolve4).catch(reject);
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", () => resolve4(Buffer.concat(chunks)));
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 path8 = require("path");
2171
- const fs8 = require("fs");
2172
- let dir = path8.dirname(filePath);
2173
- while (dir !== path8.dirname(dir)) {
2174
- if (fs8.existsSync(path8.join(dir, "package.json"))) return dir;
2175
- if (fs8.existsSync(path8.join(dir, "next.config.js"))) return dir;
2176
- if (fs8.existsSync(path8.join(dir, "next.config.mjs"))) return dir;
2177
- if (fs8.existsSync(path8.join(dir, "next.config.ts"))) return dir;
2178
- dir = path8.dirname(dir);
2179
- }
2180
- return path8.dirname(filePath);
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,
@@ -2315,7 +2446,7 @@ var RULE_ICONS = {
2315
2446
  function formatReport(result, fix) {
2316
2447
  const lines = [];
2317
2448
  lines.push("");
2318
- lines.push(import_picocolors2.default.bold(` next-a11y v0.1.0`));
2449
+ lines.push(import_picocolors2.default.bold(` next-a11y v0.1.4`));
2319
2450
  lines.push(
2320
2451
  ` Scanned ${result.filesScanned} files`
2321
2452
  );
@@ -2505,7 +2636,7 @@ async function interactiveReview(violations, onAccept) {
2505
2636
  return { applied, skipped };
2506
2637
  }
2507
2638
  function promptAction() {
2508
- return new Promise((resolve4) => {
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") resolve4("no");
2520
- else if (normalized === "s" || normalized === "skip") resolve4("skip");
2521
- else if (normalized === "q" || normalized === "quit") resolve4("quit");
2522
- else resolve4("yes");
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,25 +2746,26 @@ function registerScanCommand(program2) {
2600
2746
  }
2601
2747
 
2602
2748
  // src/cli/init-command.ts
2603
- var fs7 = __toESM(require("fs"));
2604
- var path7 = __toESM(require("path"));
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"));
2754
+ var import_package_manager_detector = require("package-manager-detector");
2608
2755
  function registerInitCommand(program2) {
2609
2756
  program2.command("init").description("Initialize next-a11y configuration").action(async () => {
2610
- console.log(import_picocolors5.default.bold("\n next-a11y v0.1.0 \u2014 Setup\n"));
2757
+ console.log(import_picocolors5.default.bold("\n next-a11y v0.1.4 \u2014 Setup\n"));
2611
2758
  const options = await promptInitOptions();
2612
2759
  const cwd = process.cwd();
2613
- const hasAppDir = fs7.existsSync(path7.join(cwd, "app"));
2614
- const hasSrcDir = fs7.existsSync(path7.join(cwd, "src"));
2760
+ const hasAppDir = fs8.existsSync(path8.join(cwd, "app"));
2761
+ const hasSrcDir = fs8.existsSync(path8.join(cwd, "src"));
2615
2762
  const include = [];
2616
2763
  if (hasSrcDir) include.push("src/**/*.{tsx,jsx}");
2617
2764
  if (hasAppDir) include.push("app/**/*.{tsx,jsx}");
2618
2765
  if (include.length === 0) include.push("**/*.{tsx,jsx}");
2619
2766
  const configContent = generateConfig(options.provider, include);
2620
- const configPath = path7.join(cwd, "a11y.config.ts");
2621
- fs7.writeFileSync(configPath, configContent);
2767
+ const configPath = path8.join(cwd, "a11y.config.ts");
2768
+ fs8.writeFileSync(configPath, configContent);
2622
2769
  console.log(import_picocolors5.default.green(" Created a11y.config.ts"));
2623
2770
  if (options.provider !== "none" && options.installDep) {
2624
2771
  const pkgMap = {
@@ -2629,8 +2776,9 @@ function registerInitCommand(program2) {
2629
2776
  };
2630
2777
  const pkg = pkgMap[options.provider];
2631
2778
  if (pkg) {
2632
- const pm = detectPackageManager(cwd);
2633
- const installCmd = pm === "yarn" ? `yarn add ${pkg}` : pm === "pnpm" ? `pnpm add ${pkg}` : pm === "bun" ? `bun add ${pkg}` : `npm install ${pkg}`;
2779
+ const pm = await (0, import_package_manager_detector.detect)({ cwd });
2780
+ const resolved = (0, import_package_manager_detector.resolveCommand)(pm?.agent ?? "npm", "add", [pkg]);
2781
+ const installCmd = resolved ? `${resolved.command} ${resolved.args.join(" ")}` : `npm install ${pkg}`;
2634
2782
  try {
2635
2783
  console.log(import_picocolors5.default.dim(` Running: ${installCmd}`));
2636
2784
  (0, import_node_child_process.execSync)(installCmd, { cwd, stdio: "pipe" });
@@ -2641,14 +2789,14 @@ function registerInitCommand(program2) {
2641
2789
  }
2642
2790
  }
2643
2791
  if (options.addGitignore) {
2644
- const gitignorePath = path7.join(cwd, ".gitignore");
2792
+ const gitignorePath = path8.join(cwd, ".gitignore");
2645
2793
  let content = "";
2646
- if (fs7.existsSync(gitignorePath)) {
2647
- content = fs7.readFileSync(gitignorePath, "utf-8");
2794
+ if (fs8.existsSync(gitignorePath)) {
2795
+ content = fs8.readFileSync(gitignorePath, "utf-8");
2648
2796
  }
2649
2797
  if (!content.includes(".a11y-cache")) {
2650
2798
  const newline = content.endsWith("\n") ? "" : "\n";
2651
- fs7.appendFileSync(gitignorePath, `${newline}.a11y-cache
2799
+ fs8.appendFileSync(gitignorePath, `${newline}.a11y-cache
2652
2800
  `);
2653
2801
  console.log(import_picocolors5.default.green(" Updated .gitignore"));
2654
2802
  }
@@ -2689,7 +2837,7 @@ async function promptInitOptions() {
2689
2837
  return { provider, installDep, addGitignore };
2690
2838
  }
2691
2839
  function promptSelect(question, options) {
2692
- return new Promise((resolve4) => {
2840
+ return new Promise((resolve5) => {
2693
2841
  const rl = readline2.createInterface({
2694
2842
  input: process.stdin,
2695
2843
  output: process.stdout
@@ -2702,15 +2850,15 @@ function promptSelect(question, options) {
2702
2850
  rl.close();
2703
2851
  const idx = parseInt(answer.trim()) - 1;
2704
2852
  if (idx >= 0 && idx < options.length) {
2705
- resolve4(options[idx].value);
2853
+ resolve5(options[idx].value);
2706
2854
  } else {
2707
- resolve4(options[0].value);
2855
+ resolve5(options[0].value);
2708
2856
  }
2709
2857
  });
2710
2858
  });
2711
2859
  }
2712
2860
  function promptYesNo(question) {
2713
- return new Promise((resolve4) => {
2861
+ return new Promise((resolve5) => {
2714
2862
  const rl = readline2.createInterface({
2715
2863
  input: process.stdin,
2716
2864
  output: process.stdout
@@ -2718,16 +2866,10 @@ function promptYesNo(question) {
2718
2866
  rl.question(` ${question} ${import_picocolors5.default.dim("[Y/n]")} `, (answer) => {
2719
2867
  rl.close();
2720
2868
  const normalized = answer.trim().toLowerCase();
2721
- resolve4(normalized !== "n" && normalized !== "no");
2869
+ resolve5(normalized !== "n" && normalized !== "no");
2722
2870
  });
2723
2871
  });
2724
2872
  }
2725
- function detectPackageManager(cwd) {
2726
- if (fs7.existsSync(path7.join(cwd, "bun.lock"))) return "bun";
2727
- if (fs7.existsSync(path7.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
2728
- if (fs7.existsSync(path7.join(cwd, "yarn.lock"))) return "yarn";
2729
- return "npm";
2730
- }
2731
2873
  function generateConfig(provider, include) {
2732
2874
  const providerLine = provider === "none" ? " // provider: 'openai', // Uncomment and set when ready" : ` provider: "${provider}",`;
2733
2875
  const modelMap = {
@@ -2802,12 +2944,12 @@ function formatBytes(bytes) {
2802
2944
  }
2803
2945
 
2804
2946
  // src/cli/index.ts
2805
- (0, import_dotenv.config)({ path: ".env", override: false, quiet: true });
2806
- (0, import_dotenv.config)({ path: ".env.local", override: true, quiet: true });
2807
- (0, import_dotenv.config)({ path: ".env.development", override: true, quiet: true });
2808
- (0, import_dotenv.config)({ path: ".env.development.local", override: true, quiet: true });
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 });
2809
2951
  var program = new import_commander.Command();
2810
- program.name("next-a11y").description("AI-powered accessibility codemod for Next.js").version("0.1.3");
2952
+ program.name("next-a11y").description("AI-powered accessibility codemod for Next.js").version("0.1.4");
2811
2953
  registerScanCommand(program);
2812
2954
  registerInitCommand(program);
2813
2955
  registerCacheCommand(program);
@@ -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((resolve4, reject) => {
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 resolve4(files);
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() === importName) {
1907
- const moduleSpecifier = imp.getModuleSpecifierValue();
1908
- if (moduleSpecifier.endsWith(".png") || moduleSpecifier.endsWith(".jpg") || moduleSpecifier.endsWith(".jpeg") || moduleSpecifier.endsWith(".webp") || moduleSpecifier.endsWith(".gif") || moduleSpecifier.endsWith(".svg") || moduleSpecifier.endsWith(".avif")) {
1909
- const filePath = file.getFilePath();
1910
- return path5.resolve(path5.dirname(filePath), moduleSpecifier);
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);
1911
1945
  }
1912
1946
  }
1913
1947
  }
1914
1948
  return void 0;
1915
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);
2000
+ }
2001
+ }
2002
+ } catch {
2003
+ }
2004
+ return void 0;
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((resolve4, reject) => {
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(resolve4).catch(reject);
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", () => resolve4(Buffer.concat(chunks)));
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 path8 = __require("path");
2113
- const fs8 = __require("fs");
2114
- let dir = path8.dirname(filePath);
2115
- while (dir !== path8.dirname(dir)) {
2116
- if (fs8.existsSync(path8.join(dir, "package.json"))) return dir;
2117
- if (fs8.existsSync(path8.join(dir, "next.config.js"))) return dir;
2118
- if (fs8.existsSync(path8.join(dir, "next.config.mjs"))) return dir;
2119
- if (fs8.existsSync(path8.join(dir, "next.config.ts"))) return dir;
2120
- dir = path8.dirname(dir);
2121
- }
2122
- return path8.dirname(filePath);
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,
@@ -2257,7 +2388,7 @@ var RULE_ICONS = {
2257
2388
  function formatReport(result, fix) {
2258
2389
  const lines = [];
2259
2390
  lines.push("");
2260
- lines.push(pc2.bold(` next-a11y v0.1.0`));
2391
+ lines.push(pc2.bold(` next-a11y v0.1.4`));
2261
2392
  lines.push(
2262
2393
  ` Scanned ${result.filesScanned} files`
2263
2394
  );
@@ -2447,7 +2578,7 @@ async function interactiveReview(violations, onAccept) {
2447
2578
  return { applied, skipped };
2448
2579
  }
2449
2580
  function promptAction() {
2450
- return new Promise((resolve4) => {
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") resolve4("no");
2462
- else if (normalized === "s" || normalized === "skip") resolve4("skip");
2463
- else if (normalized === "q" || normalized === "quit") resolve4("quit");
2464
- else resolve4("yes");
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,25 +2688,26 @@ function registerScanCommand(program2) {
2542
2688
  }
2543
2689
 
2544
2690
  // src/cli/init-command.ts
2545
- import * as fs7 from "fs";
2546
- import * as path7 from "path";
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";
2696
+ import { detect as detectPM, resolveCommand } from "package-manager-detector";
2550
2697
  function registerInitCommand(program2) {
2551
2698
  program2.command("init").description("Initialize next-a11y configuration").action(async () => {
2552
- console.log(pc5.bold("\n next-a11y v0.1.0 \u2014 Setup\n"));
2699
+ console.log(pc5.bold("\n next-a11y v0.1.4 \u2014 Setup\n"));
2553
2700
  const options = await promptInitOptions();
2554
2701
  const cwd = process.cwd();
2555
- const hasAppDir = fs7.existsSync(path7.join(cwd, "app"));
2556
- const hasSrcDir = fs7.existsSync(path7.join(cwd, "src"));
2702
+ const hasAppDir = fs8.existsSync(path8.join(cwd, "app"));
2703
+ const hasSrcDir = fs8.existsSync(path8.join(cwd, "src"));
2557
2704
  const include = [];
2558
2705
  if (hasSrcDir) include.push("src/**/*.{tsx,jsx}");
2559
2706
  if (hasAppDir) include.push("app/**/*.{tsx,jsx}");
2560
2707
  if (include.length === 0) include.push("**/*.{tsx,jsx}");
2561
2708
  const configContent = generateConfig(options.provider, include);
2562
- const configPath = path7.join(cwd, "a11y.config.ts");
2563
- fs7.writeFileSync(configPath, configContent);
2709
+ const configPath = path8.join(cwd, "a11y.config.ts");
2710
+ fs8.writeFileSync(configPath, configContent);
2564
2711
  console.log(pc5.green(" Created a11y.config.ts"));
2565
2712
  if (options.provider !== "none" && options.installDep) {
2566
2713
  const pkgMap = {
@@ -2571,8 +2718,9 @@ function registerInitCommand(program2) {
2571
2718
  };
2572
2719
  const pkg = pkgMap[options.provider];
2573
2720
  if (pkg) {
2574
- const pm = detectPackageManager(cwd);
2575
- const installCmd = pm === "yarn" ? `yarn add ${pkg}` : pm === "pnpm" ? `pnpm add ${pkg}` : pm === "bun" ? `bun add ${pkg}` : `npm install ${pkg}`;
2721
+ const pm = await detectPM({ cwd });
2722
+ const resolved = resolveCommand(pm?.agent ?? "npm", "add", [pkg]);
2723
+ const installCmd = resolved ? `${resolved.command} ${resolved.args.join(" ")}` : `npm install ${pkg}`;
2576
2724
  try {
2577
2725
  console.log(pc5.dim(` Running: ${installCmd}`));
2578
2726
  execSync(installCmd, { cwd, stdio: "pipe" });
@@ -2583,14 +2731,14 @@ function registerInitCommand(program2) {
2583
2731
  }
2584
2732
  }
2585
2733
  if (options.addGitignore) {
2586
- const gitignorePath = path7.join(cwd, ".gitignore");
2734
+ const gitignorePath = path8.join(cwd, ".gitignore");
2587
2735
  let content = "";
2588
- if (fs7.existsSync(gitignorePath)) {
2589
- content = fs7.readFileSync(gitignorePath, "utf-8");
2736
+ if (fs8.existsSync(gitignorePath)) {
2737
+ content = fs8.readFileSync(gitignorePath, "utf-8");
2590
2738
  }
2591
2739
  if (!content.includes(".a11y-cache")) {
2592
2740
  const newline = content.endsWith("\n") ? "" : "\n";
2593
- fs7.appendFileSync(gitignorePath, `${newline}.a11y-cache
2741
+ fs8.appendFileSync(gitignorePath, `${newline}.a11y-cache
2594
2742
  `);
2595
2743
  console.log(pc5.green(" Updated .gitignore"));
2596
2744
  }
@@ -2631,7 +2779,7 @@ async function promptInitOptions() {
2631
2779
  return { provider, installDep, addGitignore };
2632
2780
  }
2633
2781
  function promptSelect(question, options) {
2634
- return new Promise((resolve4) => {
2782
+ return new Promise((resolve5) => {
2635
2783
  const rl = readline2.createInterface({
2636
2784
  input: process.stdin,
2637
2785
  output: process.stdout
@@ -2644,15 +2792,15 @@ function promptSelect(question, options) {
2644
2792
  rl.close();
2645
2793
  const idx = parseInt(answer.trim()) - 1;
2646
2794
  if (idx >= 0 && idx < options.length) {
2647
- resolve4(options[idx].value);
2795
+ resolve5(options[idx].value);
2648
2796
  } else {
2649
- resolve4(options[0].value);
2797
+ resolve5(options[0].value);
2650
2798
  }
2651
2799
  });
2652
2800
  });
2653
2801
  }
2654
2802
  function promptYesNo(question) {
2655
- return new Promise((resolve4) => {
2803
+ return new Promise((resolve5) => {
2656
2804
  const rl = readline2.createInterface({
2657
2805
  input: process.stdin,
2658
2806
  output: process.stdout
@@ -2660,16 +2808,10 @@ function promptYesNo(question) {
2660
2808
  rl.question(` ${question} ${pc5.dim("[Y/n]")} `, (answer) => {
2661
2809
  rl.close();
2662
2810
  const normalized = answer.trim().toLowerCase();
2663
- resolve4(normalized !== "n" && normalized !== "no");
2811
+ resolve5(normalized !== "n" && normalized !== "no");
2664
2812
  });
2665
2813
  });
2666
2814
  }
2667
- function detectPackageManager(cwd) {
2668
- if (fs7.existsSync(path7.join(cwd, "bun.lock"))) return "bun";
2669
- if (fs7.existsSync(path7.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
2670
- if (fs7.existsSync(path7.join(cwd, "yarn.lock"))) return "yarn";
2671
- return "npm";
2672
- }
2673
2815
  function generateConfig(provider, include) {
2674
2816
  const providerLine = provider === "none" ? " // provider: 'openai', // Uncomment and set when ready" : ` provider: "${provider}",`;
2675
2817
  const modelMap = {
@@ -2749,7 +2891,7 @@ config({ path: ".env.local", override: true, quiet: true });
2749
2891
  config({ path: ".env.development", override: true, quiet: true });
2750
2892
  config({ path: ".env.development.local", override: true, quiet: true });
2751
2893
  var program = new Command();
2752
- program.name("next-a11y").description("AI-powered accessibility codemod for Next.js").version("0.1.3");
2894
+ program.name("next-a11y").description("AI-powered accessibility codemod for Next.js").version("0.1.4");
2753
2895
  registerScanCommand(program);
2754
2896
  registerInitCommand(program);
2755
2897
  registerCacheCommand(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-a11y",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "AI-powered accessibility codemod for Next.js",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -38,6 +38,7 @@
38
38
  "ai": "^6.0.97",
39
39
  "commander": "^12.0.0",
40
40
  "dotenv": "^17.3.1",
41
+ "package-manager-detector": "^1.6.0",
41
42
  "picocolors": "^1.1.0",
42
43
  "ts-morph": "^24.0.0"
43
44
  },