at-builder 1.4.1 → 1.4.3

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.
@@ -64,7 +64,13 @@
64
64
  "Bash(npm view *)",
65
65
  "Bash(git tag *)",
66
66
  "Bash(git push *)",
67
- "Bash(npm publish *)"
67
+ "Bash(npm publish *)",
68
+ "Bash(git commit -m 'feat\\(eslint\\): honor consumer .eslintrc.* via FlatCompat *)",
69
+ "Bash(atb build *)",
70
+ "Bash(ACTIVITY_FOLDER_NAME=\"UPSDDO - 7313 - AB-26.3.1 - Left Hand Navigation With Flyout - Dashboard - EWS - FWS - Tracking - Support Portal - Billing - Pickup - Claims\" atb build --prod)",
71
+ "Bash(grep -oE \"\\\\\\([a-z-]+\\\\\\)$\")",
72
+ "Bash(awk '/^ERROR in/{flag=1; next} /^WARNING in/{flag=0; next} flag')",
73
+ "Bash(git commit -m 'fix\\(eslint\\): honor consumer ESLint config as sole authority + 2 FlatCompat bugs *)"
68
74
  ]
69
75
  }
70
76
  }
@@ -1,13 +1,95 @@
1
1
  /* eslint-disable no-undef */
2
2
  const { ESLint } = require('eslint');
3
3
  const path = require('path');
4
+ const fs = require('fs');
5
+
6
+ // Legacy .eslintrc.* names ESLint v8 supported. ESLint v9 dropped them, but a
7
+ // lot of existing consumer projects still ship them — auto-convert via
8
+ // @eslint/eslintrc's FlatCompat shim so those rules are honored.
9
+ const LEGACY_ESLINTRC_NAMES = [
10
+ '.eslintrc.js',
11
+ '.eslintrc.cjs',
12
+ '.eslintrc.json',
13
+ '.eslintrc'
14
+ ];
15
+
16
+ /**
17
+ * Find the first .eslintrc.* file in `context`. Returns absolute path or null.
18
+ */
19
+ function findLegacyEslintrc(context) {
20
+ for (const name of LEGACY_ESLINTRC_NAMES) {
21
+ const p = path.join(context, name);
22
+ if (fs.existsSync(p)) return p;
23
+ }
24
+ return null;
25
+ }
26
+
27
+ /**
28
+ * Convert a legacy ESLint config (.eslintrc.*) to the flat-config array
29
+ * format ESLint v9 expects. Uses FlatCompat from @eslint/eslintrc.
30
+ *
31
+ * Returns [] (and warns once) if @eslint/eslintrc isn't installed or the
32
+ * config can't be parsed — better to ship a partial config than to fail
33
+ * the build outright.
34
+ */
35
+ function convertLegacyEslintrc(legacyPath, context) {
36
+ let FlatCompat;
37
+ try {
38
+ ({ FlatCompat } = require('@eslint/eslintrc'));
39
+ } catch (_e) {
40
+ console.warn(
41
+ `[at-builder] Found ${path.relative(context, legacyPath)} but @eslint/eslintrc is not installed; ` +
42
+ 'legacy config will be ignored. Migrate to eslint.config.js or report this.'
43
+ );
44
+ return [];
45
+ }
46
+
47
+ let legacyConfig;
48
+ try {
49
+ if (legacyPath.endsWith('.json') || legacyPath.endsWith('.eslintrc')) {
50
+ legacyConfig = JSON.parse(fs.readFileSync(legacyPath, 'utf8'));
51
+ } else {
52
+ delete require.cache[require.resolve(legacyPath)];
53
+ legacyConfig = require(legacyPath);
54
+ }
55
+ } catch (err) {
56
+ console.warn(`[at-builder] Failed to read ${path.relative(context, legacyPath)}: ${err.message}`);
57
+ return [];
58
+ }
59
+
60
+ // recommendedConfig/allConfig are required when the legacy file
61
+ // `extends: ["eslint:recommended"]` (or "eslint:all"). Without them,
62
+ // FlatCompat throws "Missing parameter 'recommendedConfig'" because
63
+ // ESLint v9 no longer ships those built-in strings — they live in the
64
+ // separate @eslint/js package now.
65
+ let jsConfigs;
66
+ try {
67
+ jsConfigs = require('@eslint/js').configs;
68
+ } catch (_e) {
69
+ jsConfigs = {};
70
+ }
71
+
72
+ try {
73
+ const compat = new FlatCompat({
74
+ baseDirectory: context,
75
+ recommendedConfig: jsConfigs.recommended,
76
+ allConfig: jsConfigs.all
77
+ });
78
+ const flatArray = compat.config(legacyConfig);
79
+ console.log(`[at-builder] Loaded legacy ESLint config from ${path.relative(context, legacyPath)}`);
80
+ return flatArray;
81
+ } catch (err) {
82
+ console.warn(`[at-builder] FlatCompat could not convert ${path.relative(context, legacyPath)}: ${err.message}`);
83
+ return [];
84
+ }
85
+ }
4
86
 
5
87
  class ESLintFlatConfigPlugin {
6
88
  constructor(options = {}) {
7
89
  this.options = {
8
90
  context: options.context || process.cwd(),
9
91
  configFile: options.configFile,
10
- overrideConfigFiles: options.overrideConfigFiles || [], // Array of additional config files
92
+ overrideConfigFiles: options.overrideConfigFiles || [], // Array of additional flat-config file paths
11
93
  failOnError: options.failOnError !== false,
12
94
  failOnWarning: options.failOnWarning || false,
13
95
  fix: options.fix || false, // Enable auto-fix
@@ -40,33 +122,52 @@ class ESLintFlatConfigPlugin {
40
122
  if (filesToLint.length === 0) return;
41
123
 
42
124
  try {
43
- // Load base config
125
+ // Load base config (at-builder's bundled flat config)
44
126
  let baseConfig = [];
45
- if (this.options.configFile && require('fs').existsSync(this.options.configFile)) {
127
+ if (this.options.configFile && fs.existsSync(this.options.configFile)) {
46
128
  delete require.cache[require.resolve(this.options.configFile)];
47
129
  baseConfig = require(this.options.configFile);
48
130
  }
49
131
 
50
- // Load and merge override configs
132
+ // Load consumer flat-config overrides (eslint.config.js etc.)
51
133
  const overrideConfigs = [];
52
134
  for (const configPath of this.options.overrideConfigFiles) {
53
135
  try {
54
136
  const resolvedPath = path.resolve(this.options.context, configPath);
55
- if (require('fs').existsSync(resolvedPath)) {
137
+ if (fs.existsSync(resolvedPath)) {
56
138
  delete require.cache[require.resolve(resolvedPath)];
57
139
  const config = require(resolvedPath);
58
140
  overrideConfigs.push(...(Array.isArray(config) ? config : [config]));
59
141
  }
60
142
  } catch (err) {
61
- // Silently skip if config doesn't exist
143
+ // Silently skip if config doesn't exist or can't be required
62
144
  }
63
145
  }
64
146
 
65
- // Merge all configs (base + overrides)
66
- const mergedConfig = [
67
- ...(Array.isArray(baseConfig) ? baseConfig : [baseConfig]),
68
- ...overrideConfigs
69
- ];
147
+ // Auto-discover legacy .eslintrc.* in the consumer project and
148
+ // convert it via FlatCompat. Only kicks in if no flat-config
149
+ // override was found (flat config wins by convention).
150
+ let legacyConfigs = [];
151
+ if (overrideConfigs.length === 0) {
152
+ const legacyPath = findLegacyEslintrc(this.options.context);
153
+ if (legacyPath) {
154
+ legacyConfigs = convertLegacyEslintrc(legacyPath, this.options.context);
155
+ }
156
+ }
157
+
158
+ // When the consumer ships their own ESLint config (flat or
159
+ // legacy), it becomes the sole authority — at-builder's
160
+ // bundled defaults are NOT merged in. Otherwise consumers
161
+ // would have to defensively `'no-console': 'off'` every
162
+ // rule at-builder happens to opinionate on. at-builder's
163
+ // bundle is a fallback for projects with no ESLint config
164
+ // at all, not a baseline that gets merged with theirs.
165
+ const consumerHasConfig =
166
+ overrideConfigs.length > 0 || legacyConfigs.length > 0;
167
+
168
+ const mergedConfig = consumerHasConfig
169
+ ? [...overrideConfigs, ...legacyConfigs]
170
+ : (Array.isArray(baseConfig) ? baseConfig : [baseConfig]);
70
171
 
71
172
  // overrideConfigFile: true tells ESLint v9 to skip the
72
173
  // cwd-based flat-config lookup entirely and treat
@@ -75,12 +176,23 @@ class ESLintFlatConfigPlugin {
75
176
  // `eslint.config.js` (which they don't have, since at-builder
76
177
  // ships its own) and fails the build with "Could not find
77
178
  // config file." even though we provided everything inline.
179
+ // ignore: true (default) respects the consumer's
180
+ // ignorePatterns / ignores entries. We previously had
181
+ // ignore:false thinking it suppressed ESLint's defaults,
182
+ // but it actually disabled ALL ignore handling — including
183
+ // the consumer's own patterns — so e.g. `.plop/**` in
184
+ // their .eslintrc.js was silently ignored.
185
+ //
186
+ // warnIgnored:false suppresses ESLint's "File ignored
187
+ // because of a matching ignore pattern" warnings — webpack
188
+ // happens to walk into ignored files via imports, and we
189
+ // don't want to surface a warning for each one.
78
190
  const eslint = new ESLint({
79
191
  cwd: this.options.context,
80
192
  overrideConfigFile: true,
81
193
  overrideConfig: mergedConfig,
82
- ignore: false, // Don't use default ignore patterns
83
- fix: this.options.fix // Enable auto-fix if option is set
194
+ warnIgnored: false,
195
+ fix: this.options.fix
84
196
  });
85
197
 
86
198
  const results = await eslint.lintFiles(filesToLint);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "at-builder",
3
- "version": "1.4.1",
3
+ "version": "1.4.3",
4
4
  "main": "bin/index.js",
5
5
  "bin": {
6
6
  "atb": "bin/index.js"
@@ -24,6 +24,7 @@
24
24
  "@babel/eslint-parser": "^7.26.8",
25
25
  "@babel/plugin-transform-runtime": "^7.18.9",
26
26
  "@babel/preset-env": "^7.18.9",
27
+ "@eslint/eslintrc": "^3.2.0",
27
28
  "@eslint/js": "^9.20.0",
28
29
  "@types/node": "^22.13.2",
29
30
  "async": "^3.2.3",