Skip to content

chore: migrate from eslint/biome to oxlint/oxfmt - #3569

Merged
B4nan merged 4 commits into
v4from
chore/migrate-to-oxlint-oxfmt
Apr 17, 2026
Merged

B4nan merged 4 commits into
v4from
chore/migrate-to-oxlint-oxfmt

Conversation

@B4nan

@B4nan B4nan commented Apr 14, 2026 •

Copy link
Copy Markdown
Member

Summary

Replaces ESLint + Biome with Oxlint + Oxfmt, modeled after mikro-orm's setup. Type-aware linting is enabled via oxlint-tsgolint and the --type-aware flag. Lint + format coverage spans packages, test, and docs.

Notable config choices

  • @apify/tsconfig bumped to ^0.1.2 — that release (chore: drop deprecated importsNotUsedAsValues option apify-tsconfig#3, chore(ci): set up automated NPM publishing via workflow_dispatch apify-tsconfig#4) drops the deprecated importsNotUsedAsValues option that tsgolint refuses to parse. No local patch needed anymore.
  • Root tsconfigs drop baseUrl — tsgolint removed support for it. Non-relative imports under test/* and the one stray packages/* import in memory-storage were converted to relative paths so the mappings could be removed entirely.
  • Rules that were triggering across existing code are fully enabled as errors now. Violations fall into two buckets:
    • Real issues fixed in-place (dead code after throw, let→const, unnecessary template expressions, .match → RegExp#exec, stream → node:stream, deprecated z.ZodSchema → z.ZodType).
    • Patterns the rule flags pessimistically but that are correct (Promise.all(arr.map(fn)) where fn returns Awaitable<void>) — fixed by marking the inner arrow async.
  • typescript/no-deprecated stays at warn — we intentionally use our own deprecated APIs in implementation code (pseudoUrls, experiments.requestLocking opt-out, puppeteer page-level cookies/postData/target). They're visible but don't block CI.
  • no-misused-promises is configured with { checksVoidReturn: { arguments: false } }. Event-listener callbacks (page.on(..., async)) and timer callbacks (setInterval(async () => ...)) are standard Node patterns and the rule's default behavior is too strict.
  • lint-staged runs oxlint --fix then oxfmt --write. A handful of unsafe auto-fixes (no-useless-default-assignment strips destructuring defaults, return-await removes await keywords, no-inferrable-types occasionally strips default values) are turned off so the fixer can run automatically without risk.

Migration mechanics

  • eslint.config.mjs, biome.json, and tsconfig.eslint.json are removed. .oxlintrc.json + .oxfmtrc.json replace them.
  • CI's lint job runs yarn lint + yarn format:check.
  • oxfmt is set to 120 columns / 4-space / single quotes / lf, matching the previous Biome setup. Markdown/MDX and release-managed files (CHANGELOGs, package.json) are in its ignore list to avoid cosmetic churn.

@B4nan
B4nan force-pushed the chore/migrate-to-oxlint-oxfmt branch 4 times, most recently from 2e013df to a24965c Compare April 15, 2026 11:01
B4nan added a commit that referenced this pull request Apr 15, 2026
#3572)

## Summary

Switches \`PuppeteerController._getCookies\` / \`_setCookies\` from the
deprecated page-level API (\`page.cookies\` / \`page.setCookie\`) to the
browser-context API (\`page.browserContext().cookies\` / \`setCookie\`).
This aligns the Puppeteer controller with the Playwright controller,
which has always operated at the context level.

## Behavior change

The page-level API returned cookies scoped to the page's current URL;
the context API returns every cookie in the browser context. For the
default Crawlee setup (one context per session — the
\`useIncognitoPages\` / \`newContextPerSession\` pattern used by
\`PuppeteerCrawler\`) there is no visible difference. Users that share a
browser context across sessions will see cookies bleed between tabs,
which matches playwright-side semantics.

Documented in \`docs/upgrading/upgrading_v4.md\`.

## Why split out

Surfaced in review of #3569 — that PR is a tooling migration
(eslint/biome → oxlint/oxfmt) and originally carried this change as a
drive-by "fix a deprecation warning". Splitting it out keeps the
migration PR behavior-neutral and gives this behavior change a proper
changelog entry.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@B4nan
B4nan force-pushed the chore/migrate-to-oxlint-oxfmt branch from a24965c to f182f23 Compare April 15, 2026 12:06
@B4nan

B4nan commented Apr 15, 2026

Copy link
Copy Markdown
Member Author

Not sure how much we want to review this, I could maybe split the formatting changes into a separate commit to make it clearer. God knows why we had tabs in JSON files here... 🙃

B4nan and others added 2 commits April 15, 2026 15:40
Replace Biome with Oxfmt. Oxfmt is configured for 120 column width,
4-space indent, single quotes, trailing commas, LF line endings —
matching the previous Biome setup. Markdown/MDX and release-managed
files (CHANGELOGs, READMEs, package.json) stay in the ignore list so
oxfmt doesn't touch docs or generated content.

Applies the resulting formatter pass across packages, test, and docs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace ESLint with Oxlint. Type-aware linting is enabled via
oxlint-tsgolint and the --type-aware flag. Config is modeled after
mikro-orm's oxlint setup. Lint scope covers packages, test, and docs.

Key rule choices:
- no-misused-promises is configured with checksVoidReturn.arguments
  disabled because event-listener and timer callbacks routinely take
  async functions on purpose (setInterval(async), page.on(async)).
- no-deprecated stays at warn — internal back-compat support for
  APIs we ourselves deprecated (pseudoUrls, experiments.requestLocking)
  uses inline oxlint-disable comments so it doesn't hide real issues.
- Rules known to produce unsafe auto-fixes (no-useless-default-assignment,
  return-await, no-inferrable-types) are disabled so lint-staged can
  run oxlint --fix without risk of stripping destructuring defaults
  or semantically-significant await keywords.

tsconfig adjustments required by tsgolint:
- Drop baseUrl (tsgolint removed support); the one non-relative import
  in memory-storage was switched to a relative path.
- Prefix tsconfig paths with ./ so tsgolint accepts them.
- Add noEmit to the root tsconfig.json so type-aware lint doesn't try
  to emit over JS files in packages/templates.
- Drop tsconfig.eslint.json — oxlint auto-discovers per-file tsconfigs.

Source fixes for newly-promoted rules:
- Removed dead code after a throw in dataset.ts.
- let→const where oxlint could see the variable was assigned once.
- Stripped unnecessary template expressions in tests.
- Rewrote docs examples to use node: protocol, RegExp#exec, etc.
- Bumped z.ZodSchema → z.ZodType in stagehand (the former is deprecated).
- Made arrow callbacks in puppeteer/playwright click-elements async so
  Promise.all() sees explicit Promise return types.

CI's lint job runs yarn lint followed by yarn format:check.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@B4nan
B4nan force-pushed the chore/migrate-to-oxlint-oxfmt branch from f182f23 to 100eceb Compare April 15, 2026 13:43
@B4nan

B4nan commented Apr 15, 2026 •

Copy link
Copy Markdown
Member Author

So I've split this into two commits, one formatting, the other lint-related changes. The formatting commit should be fine, I don't think we need to review that really.

(also don't forget to skip whitespace diffs to make the review simpler)

@barjin barjin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you for taking care of this @B4nan , I don't really have any objections here 👍

Comment thread docs/guides/session_management_basic.ts Outdated

@vladfrangu vladfrangu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Only the lint-staged and .eslintrc.json in templates are requested changes, the rest are discussions / nits / remarks

Comment thread docs/tsconfig.json
Comment thread packages/basic-crawler/src/internals/basic-crawler.ts Outdated
Comment thread packages/core/src/serialization.ts Outdated
Comment thread packages/templates/.eslintrc.json Outdated
Comment thread .oxfmtrc.json
Comment thread package.json
B4nan and others added 2 commits April 16, 2026 14:15
- expand lint-staged glob to cover all JS/TS extensions
- use `obj?.value ?? null` instead of disable comment in serialization.ts
- await setCookie in session management example
- delete dead packages/templates/.eslintrc.json

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@B4nan
B4nan requested a review from vladfrangu April 16, 2026 13:00

@janbuchar janbuchar left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM, thanks

@B4nan
B4nan merged commit aba7203 into v4 Apr 17, 2026
6 checks passed
@B4nan
B4nan deleted the chore/migrate-to-oxlint-oxfmt branch April 17, 2026 09:08
barjin pushed a commit that referenced this pull request Jul 20, 2026
#3572)

Switches \`PuppeteerController._getCookies\` / \`_setCookies\` from the
deprecated page-level API (\`page.cookies\` / \`page.setCookie\`) to the
browser-context API (\`page.browserContext().cookies\` / \`setCookie\`).
This aligns the Puppeteer controller with the Playwright controller,
which has always operated at the context level.

The page-level API returned cookies scoped to the page's current URL;
the context API returns every cookie in the browser context. For the
default Crawlee setup (one context per session — the
\`useIncognitoPages\` / \`newContextPerSession\` pattern used by
\`PuppeteerCrawler\`) there is no visible difference. Users that share a
browser context across sessions will see cookies bleed between tabs,
which matches playwright-side semantics.

Documented in \`docs/upgrading/upgrading_v4.md\`.

Surfaced in review of #3569 — that PR is a tooling migration
(eslint/biome → oxlint/oxfmt) and originally carried this change as a
drive-by "fix a deprecation warning". Splitting it out keeps the
migration PR behavior-neutral and gives this behavior change a proper
changelog entry.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
barjin pushed a commit that referenced this pull request Jul 20, 2026
Replaces ESLint + Biome with Oxlint + Oxfmt, modeled after [mikro-orm's
setup](https://github.com/mikro-orm/mikro-orm). Type-aware linting is
enabled via `oxlint-tsgolint` and the `--type-aware` flag. Lint + format
coverage spans `packages`, `test`, and `docs`.

- **`@apify/tsconfig` bumped to `^0.1.2`** — that release
(apify/apify-tsconfig#3, apify/apify-tsconfig#4) drops the deprecated
`importsNotUsedAsValues` option that `tsgolint` refuses to parse. No
local patch needed anymore.
- **Root tsconfigs drop `baseUrl`** — `tsgolint` removed support for it.
Non-relative imports under `test/*` and the one stray `packages/*`
import in `memory-storage` were converted to relative paths so the
mappings could be removed entirely.
- **Rules that were triggering across existing code** are fully enabled
as errors now. Violations fall into two buckets:
- Real issues fixed in-place (dead code after `throw`, `let`→`const`,
unnecessary template expressions, `.match` → `RegExp#exec`, `stream` →
`node:stream`, deprecated `z.ZodSchema` → `z.ZodType`).
- Patterns the rule flags pessimistically but that are correct
(`Promise.all(arr.map(fn))` where `fn` returns `Awaitable<void>`) —
fixed by marking the inner arrow `async`.
- **`typescript/no-deprecated` stays at `warn`** — we intentionally use
our own deprecated APIs in implementation code (`pseudoUrls`,
`experiments.requestLocking` opt-out, puppeteer page-level
cookies/postData/target). They're visible but don't block CI.
- **`no-misused-promises`** is configured with `{ checksVoidReturn: {
arguments: false } }`. Event-listener callbacks (`page.on(..., async)`)
and timer callbacks (`setInterval(async () => ...)`) are standard Node
patterns and the rule's default behavior is too strict.
- **`lint-staged`** runs `oxlint --fix` then `oxfmt --write`. A handful
of unsafe auto-fixes (`no-useless-default-assignment` strips
destructuring defaults, `return-await` removes `await` keywords,
`no-inferrable-types` occasionally strips default values) are turned off
so the fixer can run automatically without risk.

- `eslint.config.mjs`, `biome.json`, and `tsconfig.eslint.json` are
removed. `.oxlintrc.json` + `.oxfmtrc.json` replace them.
- CI's lint job runs `yarn lint` + `yarn format:check`.
- `oxfmt` is set to 120 columns / 4-space / single quotes / `lf`,
matching the previous Biome setup. Markdown/MDX and release-managed
files (CHANGELOGs, package.json) are in its ignore list to avoid
cosmetic churn.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
B4nan added a commit that referenced this pull request Aug 12, 2026
#3572)

Switches \`PuppeteerController._getCookies\` / \`_setCookies\` from the
deprecated page-level API (\`page.cookies\` / \`page.setCookie\`) to the
browser-context API (\`page.browserContext().cookies\` / \`setCookie\`).
This aligns the Puppeteer controller with the Playwright controller,
which has always operated at the context level.

The page-level API returned cookies scoped to the page's current URL;
the context API returns every cookie in the browser context. For the
default Crawlee setup (one context per session — the
\`useIncognitoPages\` / \`newContextPerSession\` pattern used by
\`PuppeteerCrawler\`) there is no visible difference. Users that share a
browser context across sessions will see cookies bleed between tabs,
which matches playwright-side semantics.

Documented in \`docs/upgrading/upgrading_v4.md\`.

Surfaced in review of #3569 — that PR is a tooling migration
(eslint/biome → oxlint/oxfmt) and originally carried this change as a
drive-by "fix a deprecation warning". Splitting it out keeps the
migration PR behavior-neutral and gives this behavior change a proper
changelog entry.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
B4nan added a commit that referenced this pull request Aug 12, 2026
Replaces ESLint + Biome with Oxlint + Oxfmt, modeled after [mikro-orm's
setup](https://github.com/mikro-orm/mikro-orm). Type-aware linting is
enabled via `oxlint-tsgolint` and the `--type-aware` flag. Lint + format
coverage spans `packages`, `test`, and `docs`.

- **`@apify/tsconfig` bumped to `^0.1.2`** — that release
(apify/apify-tsconfig#3, apify/apify-tsconfig#4) drops the deprecated
`importsNotUsedAsValues` option that `tsgolint` refuses to parse. No
local patch needed anymore.
- **Root tsconfigs drop `baseUrl`** — `tsgolint` removed support for it.
Non-relative imports under `test/*` and the one stray `packages/*`
import in `memory-storage` were converted to relative paths so the
mappings could be removed entirely.
- **Rules that were triggering across existing code** are fully enabled
as errors now. Violations fall into two buckets:
- Real issues fixed in-place (dead code after `throw`, `let`→`const`,
unnecessary template expressions, `.match` → `RegExp#exec`, `stream` →
`node:stream`, deprecated `z.ZodSchema` → `z.ZodType`).
- Patterns the rule flags pessimistically but that are correct
(`Promise.all(arr.map(fn))` where `fn` returns `Awaitable<void>`) —
fixed by marking the inner arrow `async`.
- **`typescript/no-deprecated` stays at `warn`** — we intentionally use
our own deprecated APIs in implementation code (`pseudoUrls`,
`experiments.requestLocking` opt-out, puppeteer page-level
cookies/postData/target). They're visible but don't block CI.
- **`no-misused-promises`** is configured with `{ checksVoidReturn: {
arguments: false } }`. Event-listener callbacks (`page.on(..., async)`)
and timer callbacks (`setInterval(async () => ...)`) are standard Node
patterns and the rule's default behavior is too strict.
- **`lint-staged`** runs `oxlint --fix` then `oxfmt --write`. A handful
of unsafe auto-fixes (`no-useless-default-assignment` strips
destructuring defaults, `return-await` removes `await` keywords,
`no-inferrable-types` occasionally strips default values) are turned off
so the fixer can run automatically without risk.

- `eslint.config.mjs`, `biome.json`, and `tsconfig.eslint.json` are
removed. `.oxlintrc.json` + `.oxfmtrc.json` replace them.
- CI's lint job runs `yarn lint` + `yarn format:check`.
- `oxfmt` is set to 120 columns / 4-space / single quotes / `lf`,
matching the previous Biome setup. Markdown/MDX and release-managed
files (CHANGELOGs, package.json) are in its ignore list to avoid
cosmetic churn.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
B4nan added a commit that referenced this pull request Aug 18, 2026
#3572)

Switches \`PuppeteerController._getCookies\` / \`_setCookies\` from the
deprecated page-level API (\`page.cookies\` / \`page.setCookie\`) to the
browser-context API (\`page.browserContext().cookies\` / \`setCookie\`).
This aligns the Puppeteer controller with the Playwright controller,
which has always operated at the context level.

The page-level API returned cookies scoped to the page's current URL;
the context API returns every cookie in the browser context. For the
default Crawlee setup (one context per session — the
\`useIncognitoPages\` / \`newContextPerSession\` pattern used by
\`PuppeteerCrawler\`) there is no visible difference. Users that share a
browser context across sessions will see cookies bleed between tabs,
which matches playwright-side semantics.

Documented in \`docs/upgrading/upgrading_v4.md\`.

Surfaced in review of #3569 — that PR is a tooling migration
(eslint/biome → oxlint/oxfmt) and originally carried this change as a
drive-by "fix a deprecation warning". Splitting it out keeps the
migration PR behavior-neutral and gives this behavior change a proper
changelog entry.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
B4nan added a commit that referenced this pull request Aug 18, 2026
Replaces ESLint + Biome with Oxlint + Oxfmt, modeled after [mikro-orm's
setup](https://github.com/mikro-orm/mikro-orm). Type-aware linting is
enabled via `oxlint-tsgolint` and the `--type-aware` flag. Lint + format
coverage spans `packages`, `test`, and `docs`.

- **`@apify/tsconfig` bumped to `^0.1.2`** — that release
(apify/apify-tsconfig#3, apify/apify-tsconfig#4) drops the deprecated
`importsNotUsedAsValues` option that `tsgolint` refuses to parse. No
local patch needed anymore.
- **Root tsconfigs drop `baseUrl`** — `tsgolint` removed support for it.
Non-relative imports under `test/*` and the one stray `packages/*`
import in `memory-storage` were converted to relative paths so the
mappings could be removed entirely.
- **Rules that were triggering across existing code** are fully enabled
as errors now. Violations fall into two buckets:
- Real issues fixed in-place (dead code after `throw`, `let`→`const`,
unnecessary template expressions, `.match` → `RegExp#exec`, `stream` →
`node:stream`, deprecated `z.ZodSchema` → `z.ZodType`).
- Patterns the rule flags pessimistically but that are correct
(`Promise.all(arr.map(fn))` where `fn` returns `Awaitable<void>`) —
fixed by marking the inner arrow `async`.
- **`typescript/no-deprecated` stays at `warn`** — we intentionally use
our own deprecated APIs in implementation code (`pseudoUrls`,
`experiments.requestLocking` opt-out, puppeteer page-level
cookies/postData/target). They're visible but don't block CI.
- **`no-misused-promises`** is configured with `{ checksVoidReturn: {
arguments: false } }`. Event-listener callbacks (`page.on(..., async)`)
and timer callbacks (`setInterval(async () => ...)`) are standard Node
patterns and the rule's default behavior is too strict.
- **`lint-staged`** runs `oxlint --fix` then `oxfmt --write`. A handful
of unsafe auto-fixes (`no-useless-default-assignment` strips
destructuring defaults, `return-await` removes `await` keywords,
`no-inferrable-types` occasionally strips default values) are turned off
so the fixer can run automatically without risk.

- `eslint.config.mjs`, `biome.json`, and `tsconfig.eslint.json` are
removed. `.oxlintrc.json` + `.oxfmtrc.json` replace them.
- CI's lint job runs `yarn lint` + `yarn format:check`.
- `oxfmt` is set to 120 columns / 4-space / single quotes / `lf`,
matching the previous Biome setup. Markdown/MDX and release-managed
files (CHANGELOGs, package.json) are in its ignore list to avoid
cosmetic churn.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants