* chore(eslint): migrate .eslintrc + .eslintignore to .eslintrc.cjs
Consolidates the per-package ESLint setup into a single typed
`.eslintrc.cjs` file across the monorepo (177 files touched).
Why
- `.eslintignore` is removed in ESLint v9/v10. Folding ignore patterns
into `ignorePatterns` inside the rc file is the supported migration
path and unblocks future ESLint upgrades (which will likely be needed
alongside the in-flight TypeScript 6 migration).
- `.cjs` extension is explicit about module format, so the configs keep
working if any package later adopts `"type": "module"` (no CJS/ESM
surprises).
- Wrapping each config in `// @ts-check` + a `Linter.Config`-typed
`const config` block gives editor autocomplete and catches typos.
Surfaced two real duplicates in
`eslint-config-custom/back/typescript.js`
(`node/no-unsupported-features/es-syntax`,
`node/no-missing-import`), now deduplicated.
- `extends` references switched from the `custom/*` shorthand to the
explicit `eslint-config-custom/*` package name. ESLint resolves both,
but the explicit form makes the source unambiguous.
- Added `coverage/` to `ignorePatterns` so coverage output produced
inside a workspace is not linted.
Misc
- `examples/kitchensink` had ESLint configured but no `lint` script;
added one so the config actually runs.
- `nx.json` hash inputs updated:
- removed `.eslintignore` (no longer exists)
- replaced `{projectRoot}/.eslintrc.js` with
`{projectRoot}/**/.eslintrc.cjs` so nested package configs are also
part of the cache key (the previous glob missed them).
- `packages/utils/upgrade/tsconfig.eslint.json` include path updated
from `.eslintrc.js` to `.eslintrc.cjs`.
Verification
- `eslint --print-config` resolves cleanly on sampled back/front
packages (admin, users-permissions, upload).
- Full `pnpm -r lint` is clean.
* chore(eslint): set tsconfigRootDir for vscode-eslint typed linting
CLI lint resolved tsconfig paths via cwd, but vscode-eslint runs from
the workspace root and could not locate per-package tsconfigs. Set
parserOptions.tsconfigRootDir to __dirname in each package's
.eslintrc.cjs so the editor resolves typed-lint configs correctly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(eslint): self-ignore configs, scope coverage to jest packages
- Replace `env: { es6: true }` workaround with `.eslintrc.cjs` in
ignorePatterns so eslint never lints the config file itself
(root cause of the "const is not a keyword" parse error in es5)
- Add `.eslintrc.cjs` to ignorePatterns across all configs that
were missing it
- Restrict `coverage/` ignorePatterns to workspace roots that
actually run jest; drop from non-test packages and admin/server
subdir configs (tests run from the workspace root)
* chore(deps): bump msw to 1.3.5 and typedoc to 0.25.13
Widens TypeScript peer ranges in msw (>= 4.4.x) and typedoc (adds 5.4.x),
unblocking a future bump to typescript 5.6 required by react-intl 7.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(deps): bump msw to 2.13.4 across admin test suites
Migrates 8 workspaces (admin, content-manager, content-releases, upload,
review-workflows, plugin-i18n, plugin-documentation, plugin-users-permissions)
from msw 1.3.5 to 2.13.4; bumps email's unused dep for consistency.
Handler changes:
- rest.X((req, res, ctx) => res(ctx.json(X))) → http.X(() => HttpResponse.json(X))
- res(ctx.status(N)) → new HttpResponse(null, { status: N })
- res.once(...) → http.X(path, handler, { once: true })
- res.networkError(...) → HttpResponse.error()
- await req.json<T>() / casts → http.METHOD<Params, Body>(...) generics
- RequestHandler → HttpHandler
- Empty 200 responses use HttpResponse.json({}) to avoid cross-realm
SyntaxError instanceof check in getFetchClient.ts
Jest setup (per msw frequent-issues guide):
- jest-preset.front.js: customExportConditions: [''] so msw/node resolves
under jsdom; .mjs swc transform; ESM allow-list for msw deps
(rettime, @open-draft, outvariant, until-async, etc.)
- packages/admin-test-utils/src/environment.ts: polyfill Node fetch +
streams + BroadcastChannel + URL into jsdom global. Blob/File/FormData
intentionally left as jsdom's — Strapi app code threads them through
jsdom-only APIs (FileReader, Image, HTMLFormElement) where realm matters.
Comment in env.ts documents the trade-off.
Verified vs develop+msw@1.3.5 baseline: 0 net regressions across all 7
workspaces with msw test files; TypeScript clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Revert "chore(deps): bump typedoc to 0.25.13"
The typedoc bump was bundled with the msw 1.3.5 bump in 39d169cfbb but
isn't required for the msw v2 migration. Reverting the docs/ and
core/types typedoc dep back to 0.25.10 to keep this branch focused on
the msw upgrade.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(tests): type msw handler params via generics
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(tests): address review feedback on msw v2 migration
- env.ts: snapshot jsdom globals in setup(), restore in teardown() so
per-test mutations cannot leak across files in a Jest worker.
- env.ts: collapse polyfill list and value map into a single NODE_GLOBALS
source of truth; document the fetch trade-off (Node undici vs jsdom).
- handlers: replace `<never, Body>` generics with `<Record<string, never>,
Body>` (msw v2 convention; `{}` blocked by ban-types).
- email: drop unused msw devDep entirely (zero source usage in workspace).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(tests): use realm-agnostic SyntaxError check; drop URL polyfill
Two follow-ups to the msw v2 migration:
- `getFetchClient.responseInterceptor` now matches `error?.name === 'SyntaxError'`
rather than `instanceof SyntaxError`. Constructor identity differs across JS realms
(under jsdom + Node fetch, an empty 200 throws a Node-realm `SyntaxError` that
fails the jsdom-realm `instanceof` check; the same is true in browsers when the
Response originates from a service worker / iframe). Name comparison is realm-
agnostic. With this in place, empty 200 handlers can return `new HttpResponse()`
again (closer to v1's `res(ctx.status(200))`) instead of the `HttpResponse.json({})`
workaround.
- Drop `URL` / `URLSearchParams` from the env.ts polyfill list. jsdom already
provides browser-spec versions, and Node's `URL.createObjectURL` only accepts
Node-realm `Blob` — replacing it broke the "from computer" upload path in
`LogoInput.tsx`. MSW v2's interceptor doesn't require these to come from Node.
env.ts comments restructured: all polyfill rationale lives in one top-of-file block
so the body is short and the trade-offs are documented in one place.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(tests): switch content-type-builder jest env to shared polyfilled env
`aiClient.test.ts` constructs `new Response(...)` which jsdom does not provide
spec-compliantly. Align CTB with the other admin test suites by using
`@strapi/admin-test-utils/environment`, which polyfills Node's
`fetch`/`Request`/`Response`/streams into the jsdom global.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(tests): make msw v2 + undici body + jsdom realms work together
Three regressions surfaced by running the full front test suite under
jsdom + Node's undici fetch polyfill:
1. `TypeError: Body is unusable: Body has already been read` in every
handler doing `await request.json()`. MSW v2's `RequestHandler.run()`
calls `request.clone()` before invoking the resolver (to keep a copy
for post-hoc logging). Under Node's undici, `Request.clone()` tees the
underlying `ReadableStream` and locks the original body, so the
resolver's `.json()` throws. Fix in `environment.ts`: wrap the global
`Request` constructor to stash the stringified body on every instance
and patch `Request.prototype.json` / `.text` to return the stash when
present. Handlers now get the body regardless of how many times msw
clones internally.
2. `expect.any(Array)` and `toStrictEqual` failing with "serializes to
the same string" against msw response bodies. `Response.prototype.json`
runs Node's `JSON.parse`, so the resulting objects carry Node-realm
`Object`/`Array` prototypes; jsdom's `instanceof`/strict-equality
matchers reject them. Patch `Response.prototype.json` to parse with
the jsdom-realm `JSON`.
3. `useWidgets > should handle network errors gracefully` expected the
toast text "Network error", which msw v1's `res.networkError('Network
error')` surfaced verbatim. v2's `HttpResponse.error()` drops the
custom message and fetch throws `TypeError: Failed to fetch`; update
the assertion accordingly.
Also: `UIDInput` uses `jest.useFakeTimers()`. Adding `doNotFake:
['queueMicrotask', 'setImmediate']` keeps per-test runtime at ~1s
instead of ~10s (plain fake timers stall undici's internal microtasks
between request emission and handler resolution), and an `afterEach`
restores real timers so a failing test cannot leak fake timers into
the next one.
Verified with `nx run-many --target=test:front --parallel=1` across all
12 projects — all suites green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(admin): assert toast container in Widgets error tests
Replace text-match (`findByText('Server error' | 'Failed to fetch')`) with
`[data-sonner-toast]` container assertion. Decouples the test from toast
copy and from msw v1/v2 network-error message differences.
Addresses @jhoward1994's review comment on PR #26065.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(admin): assert toast text, scope msw handlers, use jsdom env
- Widgets tests: match actual toast text (Server error / fetch failure
regex) instead of data-sonner-toast container.
- ConfirmBulkActionDialog: mark msw runtime handlers { once: true } so
they do not leak across cases.
- content-type-builder: jest testEnvironment back to jsdom.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(tests): split jest env patches into modules
Refactor packages/admin-test-utils/src/environment.ts by extracting
the msw v2 + undici body + jsdom realm shims into dedicated modules
under src/patches/ (node-globals, request-body-stash,
response-json-realm, types).
Co-Authored-By: Jamie Howard <jhoward1994@gmail.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>