Skip to content

feat!: drop unmaintained eslint-config-airbnb-base#35

Merged
B4nan merged 6 commits intomasterfrom
feat/drop-airbnb-base
Apr 9, 2026
Merged

feat!: drop unmaintained eslint-config-airbnb-base#35
B4nan merged 6 commits intomasterfrom
feat/drop-airbnb-base

Conversation

@B4nan
Copy link
Copy Markdown
Member

@B4nan B4nan commented Apr 8, 2026

Why

eslint-config-airbnb-base@15.0.0 was published in August 2022 and has been the latest version ever since. It still declares its eslint peer dependency as ^7.32.0 || ^8.2.0.

We use eslint 9. So every consumer of @apify/eslint-config has been getting ERESOLVE peer-dep warnings on every install for as long as we've been on the v9 line:

npm warn ERESOLVE overriding peer dependency
npm warn While resolving: eslint-config-airbnb-base@15.0.0
npm warn Found: eslint@9.x
npm warn   peer eslint@"^7.32.0 || ^8.2.0" from eslint-config-airbnb-base@15.0.0

This package already shipped an overrides block in its own package.json to paper over the conflict — but npm only honors overrides from the root package, not from transitive deps. So that override has been a no-op for years and never actually did anything.

The warnings were tolerable. The breaking change came when apify/actor-templates started enabling npm's min-release-age (1-day) setting in .npmrc to defend against malicious freshly-published packages. With min-release-age set, npm switches to strict peer-dep resolution and the long-standing warning escalates to a hard error:

npm error code ENOVERSIONS
npm error No versions available for eslint

The upstream package is unmaintained — there's no realistic path to a fix there. So the only way forward is to drop the dep.

Approach

Five commits:

  1. feat!: drop unmaintained eslint-config-airbnb-base — replaces the legacy compat.extends('airbnb-base') with the modern js.configs.recommended + pluginImport.flatConfigs.recommended. Drops the dep, drops the dead @eslint/compat dep, drops the no-op overrides block, drops FlatCompat boilerplate.

  2. feat: re-add curated rules from airbnb-base directly — brings back 70 of the meaningful rules airbnb-base used to give us, declared inline with airbnb's exact rule values. Stylistic rules are intentionally not in the base config — they're available via the new opt-in style export below.

  3. fix: silence noisy import/no-named-as-default(-member) rules — see the validation section.

  4. feat: add opt-in @apify/eslint-config/style export — exposes airbnb's stylistic ruleset as a separate opt-in entry point for consumers who don't want a formatter.

  5. fix: also silence import/namespace — same root cause as the import/no-named-as-default fix; surfaced during validation against more downstream consumers.

What's in the curated base rule set

Grouped here the same way they're grouped (with comments) in the diff:

Modern JS / hygiene (16)

eqeqeq, no-var, prefer-const, prefer-template, prefer-arrow-callback, prefer-rest-params, prefer-spread, prefer-object-spread, prefer-numeric-literals, prefer-exponentiation-operator, object-shorthand, no-useless-rename, no-useless-computed-key, no-useless-concat, no-useless-return, no-object-constructor (modern replacement for the deprecated no-new-object), no-array-constructor

Defensive / bug-catching (24)

no-param-reassign (props allowed for acc, req, res, ctx etc.), consistent-return, default-case, default-case-last, default-param-last, radix, no-throw-literal, no-return-assign, no-unused-expressions, no-shadow (TS files use the typescript-eslint variant via ts.js), array-callback-return, no-promise-executor-return, no-template-curly-in-string, no-loop-func, no-constructor-return, no-new, no-new-wrappers, no-self-compare, no-sequences, no-unneeded-ternary, no-nested-ternary, no-lonely-if, no-multi-assign, no-else-return, no-empty-function, prefer-promise-reject-errors, block-scoped-var, vars-on-top

Security (8)

no-eval, no-implied-eval, no-new-func, no-script-url, no-proto, no-extend-native, no-caller, no-iterator

Quality / readability (13)

dot-notation, guard-for-in, no-alert (warn), no-bitwise, no-labels, no-restricted-globals (only isFinite/isNaN), no-multi-str, no-octal-escape, no-undef-init, symbol-description, prefer-regex-literals, new-cap, grouped-accessor-pairs

Imports (8)

import/first, import/no-cycle, import/no-self-import, import/no-mutable-exports, import/no-useless-path-segments, import/no-absolute-path, import/no-dynamic-require, import/newline-after-import

All rule values (severity + options) match airbnb-base's exact configuration.

Cross-checked against @eslint/js.configs.recommended and pluginImport.flatConfigs.recommended: none of the curated rules overlap with what those configs already provide. @eslint/js recommended is deliberately conservative — it covers things that are almost always bugs (no-cond-assign, no-dupe-keys, no-debugger, no-unreachable, etc.) but explicitly does not include style/defensive opinions like eqeqeq, no-var, prefer-const, or consistent-return. So all 70 curated rules are genuinely additive.

Opt-in stylistic rules: @apify/eslint-config/style

The base config does not enforce stylistic / formatting rules — Prettier (or any other formatter) is the right tool for those. For projects that don't use a formatter and want to keep the airbnb stylistic ruleset, this PR adds a new opt-in export:

import apifyTypescriptConfig from '@apify/eslint-config/ts';
import apifyStyleConfig from '@apify/eslint-config/style';

export default [
    ...apifyTypescriptConfig,
    ...apifyStyleConfig,
];

This adds 58 stylistic rules (indent, quotes, semi, comma-dangle, space-before-blocks, keyword-spacing, max-len, arrow-parens, arrow-spacing, object-curly-spacing, padded-blocks, eol-last, linebreak-style, no-multiple-empty-lines, no-trailing-spaces, etc.) with the same options eslint-config-airbnb-base used to enforce, rewritten as @stylistic/* rules so they work with eslint v9 + flat config.

@stylistic/eslint-plugin is added as an optional peer dependency — consumers who don't import this config don't need to install it. The README is updated with full setup instructions.

Note: Don't include this config if you're using Prettier — the rules will conflict with your formatter.

Validation

Tested against five real consumers using their actual lint entry points (npm run lint, not a manual npx eslint invocation):

Repo Baseline (1.1.0) New config Δ errors Δ warnings
apify/actor-templates (root + 4 templates) 0 / 0 0 / 0 0 0
apify/apify-core (lerna, 59 workspaces) 0 / 7 5 / 71 +5 +64
apify/apify-shared-js 0 / 0 0 / 5 0 +5
apify/crawlee 4 / 0 4 / 7 0 +7
apify/apify-docs 0 / 0 0 / 2 0 +2

The +5 errors in apify-core

All 5 are real new findings from @eslint/js recommended catching genuine bugs:

  • 3 × no-constant-binary-expression — patterns like if (cond || true) and x === y === z in console-frontend
  • 2 × no-unused-private-class-members#signalEnd / #endedPromise defined but unused in tests/lib/api_helpers.test.js

The 4 errors in crawlee

Pre-existing @typescript-eslint/await-thenable errors in playwright-crawler and puppeteer-crawler's click-elements.ts. They reproduce against the unmodified @apify/eslint-config@1.1.0 from npm too — unrelated to this PR.

The +78 warnings (across all consumers)

All of them are "Unused eslint-disable directive" reports fired by reportUnusedDisableDirectives: true (which this config has had on for a while). They surface because consumer codebases have inline // eslint-disable-next-line max-classes-per-file (and similar) comments for rules the new config no longer enables — so the disables are dead. Per-consumer breakdown:

  • apify-core (+64): 34 × max-classes-per-file, 5 × camelcase, 4 × newline-per-chained-call, 2 × valid-typeof, 2 × quote-props, 1 × no-restricted-globals, 1 × no-cond-assign, plus stragglers
  • apify-shared-js (+5): 3 × max-classes-per-file, 1 × global-require, 1 generic
  • crawlee (+7): 1 × no-cond-assign, 3 × no-unreachable-loop, 3 generic
  • apify-docs (+2): 1 × global-require, 1 × no-cond-assign

These are not regressions — they're dead inline disables that the consumer can clean up with eslint --fix (which auto-removes unused disable directives).

A note on the import-plugin rules I had to silence

The first iterations of this PR caused +1107 warnings in apify-core, +4 errors in apify-shared-js, and +1 error in crawlee, all from import/no-named-as-default, import/no-named-as-default-member, and import/namespace.

These three rules are part of eslint-plugin-import's flatConfigs.recommended. They were declared as error in airbnb-base too — but they were silently no-op in the legacy baseline because FlatCompat doesn't fully register eslint-plugin-import's plugin context. The new config registers the plugin properly (via pluginImport.flatConfigs.recommended), so the rules suddenly start firing — including a lot of false positives:

  • import/no-named-as-default / -member: false positives on namespace re-exports and on libraries like zod and async whose default and named exports overlap.
  • import/namespace: cannot validate computed property access against imported namespaces (e.g. REGEXS[someKey]).

Two commits in this PR explicitly silence all three to match the effective baseline behavior:

  • fix: silence noisy import/no-named-as-default(-member) rules
  • fix: also silence import/namespace

After both fixes, all five validated consumers have zero new errors from this PR beyond the 5 genuine bug catches in apify-core.

Rule diff vs. current @apify/eslint-config@1.1.0

Count
Before (with airbnb) 328
After (this PR, base config) 173
With opt-in /style 231

The 4 added rules are modern @eslint/js recommended additions airbnb-base didn't have:

  • no-constant-binary-expression
  • no-empty-static-block
  • no-new-native-nonconstructor (replaces the removed no-new-symbol)
  • no-object-constructor (replaces the deprecated no-new-object)

The remaining ~100 still-removed rules (assuming the /style opt-in is used) break down into:

  1. ~76 deprecated/removed in eslint 9 — these can't be brought back even if we wanted, e.g. no-buffer-constructor, no-new-symbol, no-native-reassign, valid-jsdoc, require-jsdoc, prefer-reflect, lines-around-directive, func-call-spacing, no-process-env, no-process-exit, no-sync, handle-callback-err, callback-return, etc.
  2. ~20 stylistic that have been removed/renamed in @stylistic/eslint-plugin — they no longer exist in the modern stylistic plugin either.
  3. ~5 deliberately opinionatedno-restricted-imports/no-restricted-properties/no-restricted-modules/no-restricted-exports (airbnb had them on with no config, which is meaningless), no-confusing-arrow (overlaps with prettier and no-mixed-operators).

Downstream rollout plan

  1. Merge this PR.
  2. Publish via publish_to_npm.yaml workflow_dispatch as a major bump (v2.0.0). The rule diff is large enough to warrant it, but the validation above shows the impact is small in practice.
  3. apify/actor-templates: bump @apify/eslint-config to ^2.0.0 in the root and in all 24 templates, and remove the temporary overrides workaround that was added to root package.json in apify/actor-templates#750 as a stopgap.
  4. apify/apify-core: bump and run eslint --fix to clean up the ~64 dead inline disables, then fix the 5 real new errors (3 × no-constant-binary-expression, 2 × no-unused-private-class-members).
  5. apify/apify-shared-js: bump and run eslint --fix to clean up 5 dead inline disables. Zero real new findings.
  6. apify/crawlee: bump and run eslint --fix to clean up 7 dead inline disables. Zero real new findings (the 4 pre-existing await-thenable errors are unrelated to this PR).
  7. apify/apify-docs: bump and run eslint --fix to clean up 2 dead inline disables. Zero real new findings.
  8. Other consumers: same — bump and observe. If you want to keep the airbnb stylistic rules without adopting a formatter, add the new @apify/eslint-config/style opt-in (see README and the "Opt-in stylistic rules" section above).

🤖 Generated with Claude Code

Replace airbnb-base with @eslint/js recommended + eslint-plugin-import
recommended.

eslint-config-airbnb-base@15.0.0 has been the latest published version
since 2022 and still pins its eslint peer dep to ^7.32.0 || ^8.2.0.
Consumers using eslint 9 see ERESOLVE warnings on every install. The
internal `overrides` block this package shipped to paper over the
peer dep was a no-op because npm only honors `overrides` from the root
package, not from transitive deps. The conflict surfaces hard when
consumers enable npm's `min-release-age` setting: npm strict-resolves
peer deps and the warning escalates to
`ENOVERSIONS - No versions available for eslint`.
@github-actions github-actions bot added this to the 138th sprint - Tooling team milestone Apr 8, 2026
@github-actions github-actions bot added the t-tooling Issues with this label are in the ownership of the tooling team. label Apr 8, 2026
@B4nan B4nan added the adhoc Ad-hoc unplanned task added during the sprint. label Apr 8, 2026
@B4nan B4nan requested a review from jirimoravcik April 8, 2026 11:09
@jirimoravcik jirimoravcik requested a review from Copilot April 8, 2026 11:10
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR removes the unmaintained eslint-config-airbnb-base (which is incompatible with ESLint 9 peer deps under strict resolution) and switches @apify/eslint-config to rely on native flat-config building blocks (@eslint/js + eslint-plugin-import) while keeping the package installable without peer-dep resolution failures.

Changes:

  • Drop eslint-config-airbnb-base (and the now-useless internal overrides) to eliminate ESLint 9 peer-dep conflicts.
  • Remove unused/dead dependencies tied to the legacy config loading path (e.g., @eslint/compat, @eslint/eslintrc usage).
  • Explicitly include @eslint/js recommended + eslint-plugin-import recommended flat config, and set languageOptions.sourceType = 'module' to preserve prior module parsing semantics.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
package.json Removes eslint-config-airbnb-base, @eslint/compat, and the no-op overrides block to fix install-time peer resolution issues.
index.js Replaces legacy FlatCompat-based AirBnB extension with native flat config entries (@eslint/js + eslint-plugin-import) and restores sourceType: 'module'.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Member

@jirimoravcik jirimoravcik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the platform repositories do not use any formatter at the moment. Removing the stylistic rules would be a massive breaking change.

@B4nan
Copy link
Copy Markdown
Member Author

B4nan commented Apr 8, 2026

Well, we can't keep things the way they are now, this was always wrong, you can't use overrides on a production dependency in a library, that only forwards the problem downstream. This literally blocks updates in many of the open source repos, some of them having vulnerability alerts we need to resolve in the next few days. (edit: this is actually only about min release age, not the vulnerabilities, but that doesn't make it any smaller)

This is a major bump, so you could keep using the old version in private repos. Or just adopt a formatter. We keep postponing this for no reason, all tooling repos already have it, and it was super simple to adopt. Alternatively, we could add formatting rules here via https://eslint.style/, but I would rather go with the formatters.

Either way, we need to get rid of that unmaintained dependency. This was causing warnings on install for years now. And if we want to enforce min release age, those warnings become errors, effectively blocking the adoption.

Brings back the meaningful (non-stylistic) rules that airbnb-base used
to provide, declared explicitly in this config so we don't depend on
the unmaintained airbnb-base package. Stylistic rules are intentionally
left out — Prettier (or any formatter) is the right tool for those.

Categorized inline as:
- Modern JS / hygiene (eqeqeq, no-var, prefer-const, prefer-template,
  prefer-arrow-callback, prefer-rest-params, prefer-spread,
  prefer-object-spread, object-shorthand, no-useless-rename, etc.)
- Defensive / bug-catching (no-param-reassign, consistent-return,
  default-case, default-case-last, default-param-last, radix,
  no-throw-literal, no-shadow, array-callback-return,
  no-promise-executor-return, no-template-curly-in-string,
  no-loop-func, no-constructor-return, no-unused-expressions,
  prefer-promise-reject-errors, etc.)
- Security (no-eval, no-implied-eval, no-new-func, no-script-url,
  no-proto, no-extend-native, no-caller, no-iterator)
- Quality (dot-notation, guard-for-in, no-alert, no-bitwise, no-labels,
  no-restricted-globals for legacy isFinite/isNaN, new-cap,
  grouped-accessor-pairs, prefer-regex-literals, etc.)
- Imports (import/first, import/no-cycle, import/no-self-import,
  import/no-mutable-exports, import/no-useless-path-segments,
  import/no-absolute-path, import/no-dynamic-require,
  import/newline-after-import)

All rule values match airbnb-base's exact configuration where
applicable. Rules that airbnb itself had set to 'off', or that have
been removed/deprecated in eslint 9, are not included.
@B4nan
Copy link
Copy Markdown
Member Author

B4nan commented Apr 8, 2026

Discussed this with @fnesveda on slack, I've added some of the missing rules back, keeping the stylistic ones off. PR description is updated in this regard, most of the removed things are stylistic, only a handful of actual lint rules are missing now.

B4nan added 2 commits April 8, 2026 15:17
`eslint-plugin-import`'s `flatConfigs.recommended` enables both
`import/no-named-as-default` and `import/no-named-as-default-member`
at warning level. These rules trigger ~1100 false positives on
common TypeScript patterns (namespace re-exports, libraries like
`zod` / `async` whose default and named exports overlap).

The previous airbnb-base setup declared both as `error` but they
were silently no-op because FlatCompat doesn't fully register
eslint-plugin-import's plugin context. The new config registers
the plugin properly, so the rules suddenly start firing — which
is a regression for downstream consumers despite being more
faithful to airbnb's stated intent.

Match the effective baseline behavior by explicitly turning both
off. Validated against apify-core: drops 1107 false-positive
warnings.
Exposes the airbnb-style stylistic ruleset as a separate opt-in entry
point for consumers who don't want to use a formatter (Prettier, dprint,
Biome). All 58 stylistic rules airbnb-base used to enforce — `indent`,
`quotes`, `semi`, `comma-dangle`, `space-before-blocks`, `keyword-spacing`,
`max-len`, `arrow-parens`, etc. — rewritten as `@stylistic/*` rules so
they work with eslint v9 + flat config, with airbnb's exact options
preserved.

`@stylistic/eslint-plugin` is added as an optional peer dependency so
consumers who don't import the style config don't need to install it.

Usage:

    import apifyTypescriptConfig from '@apify/eslint-config/ts';
    import apifyStyleConfig from '@apify/eslint-config/style';

    export default [
        ...apifyTypescriptConfig,
        ...apifyStyleConfig,
    ];

README updated with the new export and a usage example. Note: not
recommended for projects already using Prettier — the rules will
conflict.
@B4nan
Copy link
Copy Markdown
Member Author

B4nan commented Apr 8, 2026

In the end, I also added opt-in stylistic rules support to ease the migration:

import apifyTypescriptConfig from '@apify/eslint-config/ts';
import apifyStyleConfig from '@apify/eslint-config/style';

export default [
    ...apifyTypescriptConfig,
    ...apifyStyleConfig,
];

Same root cause as the previous fix for `import/no-named-as-default(-member)`:
this rule is part of `import/recommended` and was declared as `error` in
airbnb-base, but was silently no-op in the legacy FlatCompat-based config
because the plugin context was never registered. The new flat-config setup
registers the plugin properly and the rule starts firing.

`import/namespace` cannot validate computed property access against
imported namespaces (e.g. `REGEXS[someKey]`), producing false positives
on legitimate code. Validated against three more downstream consumers:

- apify-shared-js: drops 4 false positives in test/regexs.test.ts
- crawlee: drops 1 false positive in browser-pool/puppeteer-controller.ts
- apify-docs: no change

After this fix, all three consumers (plus actor-templates and apify-core)
have zero new errors from this PR's changes.
@B4nan B4nan merged commit faadb9b into master Apr 9, 2026
1 check passed
@B4nan B4nan deleted the feat/drop-airbnb-base branch April 9, 2026 10:40
B4nan added a commit to apify/actor-templates that referenced this pull request Apr 9, 2026
apify/apify-eslint-config#35 has landed and v2.0.0 is published. v2:
- drops the unmaintained `eslint-config-airbnb-base` dep
- preserves the meaningful airbnb rules inline
- moves stylistic rules to an opt-in `@apify/eslint-config/style` export

Bumped in the root package.json and in all 24 templates. The temporary
`overrides` workaround for `eslint-config-airbnb-base`'s eslint peer dep
(added in 3be0201) is no longer needed and has been removed — the
unmaintained dep is gone entirely.

Note: v2.0.0 was published less than 24 hours ago, so the first CI run
on this commit will hit the `min-release-age=86400` constraint. Once
v2.0.0 crosses the 1-day threshold, CI will pass without intervention.
B4nan added a commit that referenced this pull request Apr 9, 2026
The opt-in `@apify/eslint-config/style` config (added in #35) blindly
copied airbnb-base's stylistic options. Several of those options
conflict with apify's own preferences, which were already enforced via
legacy rules in `index.js`:

| rule                       | airbnb default            | apify (legacy `index.js`)              |
|----------------------------|---------------------------|----------------------------------------|
| `indent`                   | 2 spaces                  | 4 spaces, `SwitchCase: 1`              |
| `quotes`                   | single, no template lits  | single, `allowTemplateLiterals: true`  |
| `max-len`                  | 100 chars                 | 160 chars                              |
| `object-curly-newline`     | `minProperties: 4` rules  | `{ consistent: true }`                 |
| `lines-between-class-members` | `exceptAfterSingleLine: false` | `exceptAfterSingleLine: true` |
| `function-paren-newline`   | `multiline-arguments`     | `'off'`                                |

When a consumer spreads both `apifyTypeScriptConfig` and
`apifyStyleConfig` together, the legacy `indent: 4` from `index.js`
runs alongside the new `@stylistic/indent: 2` from `style.js`. They
have *different rule names*, so neither overrides the other — both
fire on every indented line. `eslint --fix` then bounces between the
two and gives up with `ESLintCircularFixesWarning`.

In `apify-core` this produced ~360k `@stylistic/indent` violations
across 59 workspaces — almost all on code that's been written in
4-space indent for years.

This commit:

1. Aligns the 6 conflicting `@stylistic/*` rules with apify's
   pre-existing preferences from `index.js`, so existing apify code
   passes lint without reformatting the world.
2. Explicitly turns off the legacy eslint stylistic rules (`indent`,
   `quotes`, `max-len`, `object-curly-newline`,
   `lines-between-class-members`, `function-paren-newline`) inside
   `style.js` itself — so when both configs are loaded together,
   only the `@stylistic/*` versions run and there are no circular
   fixes.

Validated against `apify-core` (~59 workspaces): no spurious
`@stylistic/indent` errors.
B4nan added a commit that referenced this pull request Apr 9, 2026
## Why

The opt-in `@apify/eslint-config/style` config (added in #35) blindly
copied airbnb-base's stylistic options. Several of those options
conflict with apify's *own* preferences, which are already enforced via
legacy rules in `index.js` and have been the de-facto Apify code style
for years:

| rule | airbnb default | apify (legacy in `index.js`) |

|-------------------------------|--------------------------------|------------------------------------------|
| `indent` | 2 spaces | **4 spaces**, `SwitchCase: 1` |
| `quotes` | single, no template literals | single,
**`allowTemplateLiterals: true`**|
| `max-len` | 100 chars | **160 chars** |
| `object-curly-newline` | `minProperties: 4` rules | **`{ consistent:
true }`** |
| `lines-between-class-members` | `exceptAfterSingleLine: false` |
**`exceptAfterSingleLine: true`** |
| `function-paren-newline` | `multiline-arguments` | **`'off'`** |

When a consumer spreads both configs together:

```js
import apifyTypeScriptConfig from '@apify/eslint-config/ts.js';
import apifyStyleConfig from '@apify/eslint-config/style.js';

export default [
    ...apifyTypeScriptConfig,  // legacy `indent: 4` from index.js
    ...apifyStyleConfig,       // new `@stylistic/indent: 2` from airbnb defaults
];
```

…the legacy `indent: 4` from `index.js` and the new `@stylistic/indent:
2` from `style.js` **both fire** on every indented line, because they're
different rule names — neither overrides the other. `eslint --fix` then
bounces between the two opinions and gives up with
`ESLintCircularFixesWarning`.

I tried this against [`apify-core`](https://github.com/apify/apify-core)
(the largest internal consumer) by adding `apifyStyleConfig` to its
`eslint.config.mjs`. The result:

```
~360,000 @stylistic/indent errors across 59 lerna workspaces
~17,000 @stylistic/max-len errors
~5,000 @stylistic/object-curly-newline errors
~1,300 @stylistic/quotes errors
~700 @stylistic/lines-between-class-members errors
~550 @stylistic/function-paren-newline errors
```

Almost all of those are on code that's been written in 4-space indent /
160-col / etc. for years. The `style.js` config was effectively saying
"rewrite your entire codebase to airbnb's defaults", which was never the
intent. The intent was "preserve the legacy airbnb stylistic ruleset for
projects that don't use a formatter, with the same options apify was
already using."

## What this PR does

1. **Aligns the 6 conflicting `@stylistic/*` rules with apify's
pre-existing preferences** from `index.js`. Existing apify codebases
pass lint without reformatting the world.

2. **Explicitly turns off the legacy eslint stylistic rules inside
`style.js` itself**:

   ```js
   indent: 'off',
   quotes: 'off',
   'max-len': 'off',
   'object-curly-newline': 'off',
   'lines-between-class-members': 'off',
   'function-paren-newline': 'off',
   ```

This way, when both `index.js` and `style.js` are loaded together, only
the `@stylistic/*` versions run and there are no circular fixes.
Consumers that use *only* `index.js` (no opt-in style) continue to get
the legacy rules unchanged.

## Diff

Just `style.js`, +18 / −6 lines:

```diff
         rules: {
+            // Disable the legacy eslint stylistic rules that this config replaces
+            // with their `@stylistic/*` equivalents — otherwise both fire on the same
+            // code with different opinions and produce circular fixes.
+            indent: 'off',
+            quotes: 'off',
+            'max-len': 'off',
+            'object-curly-newline': 'off',
+            'lines-between-class-members': 'off',
+            'function-paren-newline': 'off',
+
             '@stylistic/array-bracket-spacing': ['error','never'],
             ...
-            '@stylistic/function-paren-newline': ['error','multiline-arguments'],
+            '@stylistic/function-paren-newline': 'off',
             ...
-            '@stylistic/indent': ['error',2,{...huge airbnb config...}],
+            // Apify projects use 4 spaces, not airbnb's 2 — preserved from
+            // the legacy `indent` rule in `index.js`.
+            '@stylistic/indent': ['error', 4, { SwitchCase: 1 }],
             ...
-            '@stylistic/lines-between-class-members': ['error','always',{exceptAfterSingleLine:false}],
-            '@stylistic/max-len': ['error',100,2,{...}],
+            '@stylistic/lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: true }],
+            '@stylistic/max-len': ['error', { code: 160, ignoreUrls: true, ignoreTemplateLiterals: true }],
             ...
-            '@stylistic/object-curly-newline': ['error',{...minProperties: 4 rules...}],
+            '@stylistic/object-curly-newline': ['error', { consistent: true }],
             ...
-            '@stylistic/quotes': ['error','single',{avoidEscape:true}],
+            '@stylistic/quotes': ['error', 'single', { avoidEscape: true, allowTemplateLiterals: true }],
```

## Validation

I'll re-run lint against `apify-core` once this branch is published —
the expected outcome is that the 360k+ spurious indent errors disappear,
and any *real* stylistic violations (which there will be some of, since
apify-core has historically had no stylistic enforcement at all on a few
directories) are surfaced legitimately.

## Rollout

Patch release: `v2.0.1`. No breaking changes — this only affects
consumers who opt into `@apify/eslint-config/style`, and for them it
makes the rules behave the way they probably already expected.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

adhoc Ad-hoc unplanned task added during the sprint. t-tooling Issues with this label are in the ownership of the tooling team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants