Skip to content

fix: use verbatimModuleSyntax consistently in exports#2702

Merged
kettanaito merged 5 commits intomswjs:mainfrom
christoph-fricke:fix/types-imported-as-value
Apr 16, 2026
Merged

fix: use verbatimModuleSyntax consistently in exports#2702
kettanaito merged 5 commits intomswjs:mainfrom
christoph-fricke:fix/types-imported-as-value

Conversation

@christoph-fricke
Copy link
Copy Markdown
Contributor

@christoph-fricke christoph-fricke commented Apr 10, 2026

The new network-source API is not usable as soon as a project tries to import from msw/experimental. It crashes with a runtime error:

SyntaxError: The requested module './define-network.mjs' does not provide an export named 'DefineNetworkOptions'

This PR fixes the problem by using a type export in src/core/experimental/index.ts to ensure DefineNetworkOptions is stripped from the JS output.


I initially tried to prevent such problems in the future by enabling verbatimModule syntax in tsconfig.base.json and fixing multiple TS errors reported by pnpm exec tsc --noEmit -p src/tsconfig.src.json, but it caused strange problems in CI.

Besides this, there appear to be multiple TS errors in the project. Some picked in the editor, some only reported by checking with one of the various TS config files such as pnpm exec tsc --noEmit -p ./tsconfig.test.unit.json. Some source code errors I noticed:

  • /src/browser/sources/fallback-http-source.ts
  • /src/browser/utils/deserializeRequest.ts
  • /src/browser/utils/pruneGetRequestBody.ts

It does not look like the CI finds and reports the errors. Are you aware of these existing TypeScript problems?

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 10, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e61ff1dd-ac8c-49bf-8eb3-be24d8db8711

📥 Commits

Reviewing files that changed from the base of the PR and between fb8cda5 and ea26ceb.

📒 Files selected for processing (92)
  • src/browser/glossary.ts
  • src/browser/setup-worker.ts
  • src/browser/sources/service-worker-source.ts
  • src/browser/utils/deserializeRequest.ts
  • src/core/experimental/define-network.ts
  • src/core/experimental/frames/http-frame.test.ts
  • src/core/experimental/frames/http-frame.ts
  • src/core/experimental/frames/websocket-frame.test.ts
  • src/core/experimental/frames/websocket-frame.ts
  • src/core/experimental/index.ts
  • src/core/experimental/on-unhandled-frame.test.ts
  • src/core/experimental/setup-api.ts
  • src/core/experimental/sources/interceptor-source.ts
  • src/core/graphql.ts
  • src/core/handlers/GraphQLHandler.test.ts
  • src/core/handlers/GraphQLHandler.ts
  • src/core/handlers/HttpHandler.test.ts
  • src/core/handlers/HttpHandler.ts
  • src/core/handlers/RequestHandler.ts
  • src/core/http.ts
  • src/core/utils/cookieStore.ts
  • src/core/utils/executeHandlers.ts
  • src/core/utils/handleRequest.test.ts
  • src/core/utils/handleRequest.ts
  • src/core/utils/internal/parseGraphQLRequest.test.ts
  • src/core/utils/internal/parseMultipartData.ts
  • src/core/utils/internal/pipeEvents.ts
  • src/core/utils/internal/requestHandlerUtils.ts
  • src/core/utils/request/onUnhandledRequest.test.ts
  • src/core/ws.ts
  • src/core/ws/WebSocketClientManager.test.ts
  • src/core/ws/WebSocketClientManager.ts
  • src/core/ws/WebSocketIndexedDBClientStore.ts
  • src/core/ws/WebSocketMemoryClientStore.ts
  • src/core/ws/handleWebSocketEvent.ts
  • src/native/index.ts
  • src/node/async-handlers-controller.ts
  • src/node/setup-server-common.ts
  • src/node/setup-server.ts
  • test/browser/graphql-api/custom-predicate.test.ts
  • test/browser/graphql-api/response-patching.test.ts
  • test/browser/msw-api/context/delay.mocks.ts
  • test/browser/msw-api/req/passthrough.test.ts
  • test/browser/msw-api/setup-worker/fallback-mode/fallback-mode.test.ts
  • test/browser/msw-api/setup-worker/life-cycle-events/on.mocks.ts
  • test/browser/msw-api/setup-worker/life-cycle-events/removeAllListeners.test.ts
  • test/browser/msw-api/setup-worker/life-cycle-events/removeListener.test.ts
  • test/browser/msw-api/setup-worker/listHandlers.test.ts
  • test/browser/msw-api/setup-worker/resetHandlers.test.ts
  • test/browser/msw-api/setup-worker/restoreHandlers.test.ts
  • test/browser/msw-api/setup-worker/scenarios/iframe/iframe.test.ts
  • test/browser/msw-api/setup-worker/start/error.test.ts
  • test/browser/msw-api/setup-worker/start/find-worker.test.ts
  • test/browser/msw-api/setup-worker/start/quiet.test.ts
  • test/browser/msw-api/setup-worker/start/start.test.ts
  • test/browser/msw-api/setup-worker/stop/quiet.test.ts
  • test/browser/msw-api/setup-worker/use.test.ts
  • test/browser/msw-api/unregister.test.ts
  • test/browser/playwright.extend.ts
  • test/browser/rest-api/body.mocks.ts
  • test/browser/rest-api/request/matching/all.test.ts
  • test/browser/rest-api/request/matching/custom-predicate.test.ts
  • test/browser/sse-api/sse.client.send.extraneous.test.ts
  • test/browser/sse-api/sse.client.send.multiline.test.ts
  • test/browser/sse-api/sse.client.send.test.ts
  • test/browser/sse-api/sse.quiet.test.ts
  • test/browser/sse-api/sse.retry.test.ts
  • test/browser/sse-api/sse.server.connect.test.ts
  • test/browser/sse-api/sse.use.test.ts
  • test/browser/sse-api/sse.with-credentials.test.ts
  • test/node/graphql-api/batched-queries.apollo.test.ts
  • test/node/graphql-api/batched-queries.batched-execute.test.ts
  • test/node/msw-api/setup-server/life-cycle-events/on.node.test.ts
  • test/node/msw-api/setup-server/use.node.test.ts
  • test/node/rest-api/cookies-inheritance.node.test.ts
  • test/node/third-party/axios-error-response.test.ts
  • test/support/WebSocketServer.ts
  • test/support/environments/vitest-environment-node-websocket.ts
  • test/support/graphql.ts
  • test/support/utils.ts
  • test/support/ws-test-utils.ts
  • test/typings/custom-handler.test-d.ts
  • test/typings/custom-resolver.test-d.ts
  • test/typings/define-network.test-d.ts
  • test/typings/graphql-typed-document-node.test-d.ts
  • test/typings/graphql-typed-document-string.test-d.ts
  • test/typings/http-custom-predicate.test-d.ts
  • test/typings/regressions/default-resolver-type.test-d.ts
  • test/typings/regressions/request-handler-type.test-d.ts
  • test/typings/regressions/response-body-type.test-d.ts
  • test/typings/ws.test-d.ts
  • tsconfig.base.json
💤 Files with no reviewable changes (1)
  • src/browser/utils/deserializeRequest.ts
✅ Files skipped from review due to trivial changes (88)
  • src/core/utils/handleRequest.ts
  • tsconfig.base.json
  • src/core/experimental/define-network.ts
  • src/native/index.ts
  • test/browser/msw-api/setup-worker/listHandlers.test.ts
  • src/core/utils/cookieStore.ts
  • test/support/environments/vitest-environment-node-websocket.ts
  • test/node/graphql-api/batched-queries.batched-execute.test.ts
  • src/core/utils/internal/parseMultipartData.ts
  • src/browser/glossary.ts
  • src/core/experimental/on-unhandled-frame.test.ts
  • test/browser/msw-api/setup-worker/use.test.ts
  • test/support/graphql.ts
  • src/core/ws/handleWebSocketEvent.ts
  • src/core/utils/internal/requestHandlerUtils.ts
  • src/core/ws/WebSocketMemoryClientStore.ts
  • test/node/third-party/axios-error-response.test.ts
  • test/browser/rest-api/body.mocks.ts
  • src/core/experimental/sources/interceptor-source.ts
  • src/node/async-handlers-controller.ts
  • test/browser/graphql-api/response-patching.test.ts
  • src/core/ws/WebSocketIndexedDBClientStore.ts
  • test/browser/sse-api/sse.use.test.ts
  • test/browser/sse-api/sse.server.connect.test.ts
  • test/browser/msw-api/context/delay.mocks.ts
  • src/core/utils/request/onUnhandledRequest.test.ts
  • test/browser/msw-api/setup-worker/restoreHandlers.test.ts
  • src/core/experimental/frames/websocket-frame.ts
  • test/node/msw-api/setup-server/use.node.test.ts
  • src/core/http.ts
  • test/browser/msw-api/setup-worker/life-cycle-events/on.mocks.ts
  • test/browser/msw-api/setup-worker/start/start.test.ts
  • test/support/WebSocketServer.ts
  • test/typings/graphql-typed-document-node.test-d.ts
  • test/browser/rest-api/request/matching/all.test.ts
  • test/node/rest-api/cookies-inheritance.node.test.ts
  • test/typings/regressions/default-resolver-type.test-d.ts
  • test/browser/msw-api/setup-worker/start/error.test.ts
  • test/browser/graphql-api/custom-predicate.test.ts
  • test/browser/msw-api/setup-worker/life-cycle-events/removeAllListeners.test.ts
  • test/node/msw-api/setup-server/life-cycle-events/on.node.test.ts
  • test/browser/msw-api/setup-worker/resetHandlers.test.ts
  • test/typings/graphql-typed-document-string.test-d.ts
  • test/browser/msw-api/setup-worker/stop/quiet.test.ts
  • test/browser/msw-api/setup-worker/start/quiet.test.ts
  • src/core/utils/internal/parseGraphQLRequest.test.ts
  • src/core/utils/executeHandlers.ts
  • test/support/ws-test-utils.ts
  • src/node/setup-server-common.ts
  • src/core/experimental/frames/http-frame.ts
  • src/core/experimental/setup-api.ts
  • test/typings/regressions/response-body-type.test-d.ts
  • src/core/handlers/HttpHandler.test.ts
  • test/browser/msw-api/setup-worker/life-cycle-events/removeListener.test.ts
  • src/core/utils/internal/pipeEvents.ts
  • test/browser/rest-api/request/matching/custom-predicate.test.ts
  • test/node/graphql-api/batched-queries.apollo.test.ts
  • src/browser/sources/service-worker-source.ts
  • src/core/experimental/frames/http-frame.test.ts
  • test/browser/msw-api/setup-worker/fallback-mode/fallback-mode.test.ts
  • test/browser/sse-api/sse.retry.test.ts
  • test/browser/msw-api/req/passthrough.test.ts
  • test/typings/http-custom-predicate.test-d.ts
  • test/browser/msw-api/setup-worker/start/find-worker.test.ts
  • test/support/utils.ts
  • src/node/setup-server.ts
  • src/core/utils/handleRequest.test.ts
  • src/core/experimental/index.ts
  • src/core/graphql.ts
  • src/core/ws/WebSocketClientManager.test.ts
  • test/browser/sse-api/sse.client.send.multiline.test.ts
  • test/browser/sse-api/sse.client.send.extraneous.test.ts
  • src/core/ws/WebSocketClientManager.ts
  • test/typings/custom-handler.test-d.ts
  • test/typings/ws.test-d.ts
  • src/core/handlers/HttpHandler.ts
  • test/typings/regressions/request-handler-type.test-d.ts
  • src/core/handlers/GraphQLHandler.test.ts
  • test/typings/custom-resolver.test-d.ts
  • test/browser/playwright.extend.ts
  • test/browser/sse-api/sse.client.send.test.ts
  • test/browser/sse-api/sse.with-credentials.test.ts
  • src/core/handlers/RequestHandler.ts
  • test/browser/msw-api/unregister.test.ts
  • test/typings/define-network.test-d.ts
  • test/browser/msw-api/setup-worker/scenarios/iframe/iframe.test.ts
  • test/browser/sse-api/sse.quiet.test.ts
  • src/browser/setup-worker.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/core/experimental/frames/websocket-frame.test.ts
  • src/core/ws.ts
  • src/core/handlers/GraphQLHandler.ts

📝 Walkthrough

Walkthrough

This PR converts numerous imports and exports throughout the codebase from runtime imports to TypeScript type-only imports, and enables the verbatimModuleSyntax compiler option to preserve the declared import syntax. This systematically separates type-only symbols from runtime values across core, browser, and Node modules.

Changes

Cohort / File(s) Summary
Core Experimental Framework
src/core/experimental/index.ts, src/core/experimental/define-network.ts, src/core/experimental/setup-api.ts, src/core/experimental/sources/interceptor-source.ts
Converted imports for DefineNetworkOptions, AnyHandler, HandlersController, LifeCycleEventEmitter, Interceptor, and RequestController to type-only imports where these symbols are only used for typing.
Core Experimental Frames
src/core/experimental/frames/http-frame.ts, src/core/experimental/frames/http-frame.test.ts, src/core/experimental/frames/websocket-frame.ts, src/core/experimental/frames/websocket-frame.test.ts, src/core/experimental/on-unhandled-frame.test.ts
Changed imports for NetworkFrameResolutionContext, HandlersController, AnyHandler, UnhandledFrameHandle, HttpNetworkFrameEventMap, WebSocketNetworkFrameEventMap, and UnhandledFrameCallback to type-only imports in frame implementations and tests.
Core HTTP & GraphQL Handlers
src/core/handlers/HttpHandler.ts, src/core/handlers/HttpHandler.test.ts, src/core/handlers/GraphQLHandler.ts, src/core/handlers/GraphQLHandler.test.ts, src/core/handlers/RequestHandler.ts
Converted type-related imports like ResponseResolver, RequestHandlerOptions, RequestHandlerDefaultInfo, Match, Path, DefaultBodyType, HttpResponseResolver, GraphQLRequestBody, and GraphQLResolverExtras to type-only imports.
Core Public APIs
src/core/graphql.ts, src/core/http.ts, src/core/ws.ts
Changed exports and imports for ResponseResolver, RequestHandlerOptions, GraphQLVariables, GraphQLOperationType, HttpRequestResolverExtras, and WebSocketData to use type-only syntax.
Core Utilities
src/core/utils/cookieStore.ts, src/core/utils/executeHandlers.ts, src/core/utils/handleRequest.ts, src/core/utils/handleRequest.test.ts, src/core/utils/internal/parseGraphQLRequest.test.ts, src/core/utils/internal/parseMultipartData.ts, src/core/utils/internal/pipeEvents.ts, src/core/utils/internal/requestHandlerUtils.ts, src/core/utils/request/onUnhandledRequest.test.ts
Converted imports for SerializedCookie, RequestHandler, RequestHandlerExecutionResult, Emitter, LifeCycleEventsMap, ParsedGraphQLRequest, DefaultRequestMultipartBody, EventMap, and UnhandledRequestCallback to type-only imports.
Browser Module
src/browser/glossary.ts, src/browser/setup-worker.ts, src/browser/sources/service-worker-source.ts, src/browser/utils/deserializeRequest.ts
Changed AnyHandler, Emitter, WorkerChannelEventMap, FindWorker imports to type-only; removed an unused type-only import from deserializeRequest.ts.
Node Module
src/node/async-handlers-controller.ts, src/node/setup-server-common.ts, src/node/setup-server.ts
Converted Interceptor, AnyHandler, HandlersController, HandlersControllerState, NetworkApi imports to type-only where appropriate.
WebSocket Support
src/core/ws/WebSocketClientManager.ts, src/core/ws/WebSocketClientManager.test.ts, src/core/ws/WebSocketIndexedDBClientStore.ts, src/core/ws/WebSocketMemoryClientStore.ts, src/core/ws/handleWebSocketEvent.ts
Changed imports for WebSocketClientStore, WebSocketClientConnectionProtocol, SerializedWebSocketClient, RequestHandler, WebSocketHandler, and UnhandledRequestStrategy to type-only imports.
Browser Integration Tests
test/browser/graphql-api/custom-predicate.test.ts, test/browser/graphql-api/response-patching.test.ts, test/browser/msw-api/context/delay.mocks.ts, test/browser/msw-api/req/passthrough.test.ts, test/browser/msw-api/setup-worker/.../*.test.ts, test/browser/rest-api/.../*.test.ts, test/browser/sse-api/.../*.test.ts, test/browser/playwright.extend.ts
Systematically converted MSW API imports (SetupWorkerApi, http, graphql, HttpResponse, LifeCycleEventsMap, ServerSentEventMessage, Page, Frame, etc.) to type-only imports across 30+ test files.
Node Integration Tests
test/node/graphql-api/.../*.test.ts, test/node/msw-api/setup-server/.../*.test.ts, test/node/rest-api/.../*.test.ts, test/node/third-party/axios-error-response.test.ts
Changed MSW and third-party type imports (SetupServerApi, SetupServer, RequestHandler, GraphQLVariables, AxiosError) to type-only imports across 8+ test files.
Type Definition Tests
test/typings/custom-handler.test-d.ts, test/typings/custom-resolver.test-d.ts, test/typings/define-network.test-d.ts, test/typings/graphql-typed-document-node.test-d.ts, test/typings/graphql-typed-document-string.test-d.ts, test/typings/http-custom-predicate.test-d.ts, test/typings/regressions/.../*.test-d.ts, test/typings/ws.test-d.ts
Converted type-related imports in typing tests (HttpRequestHandler, GraphQLRequestHandler, HttpResponseResolver, TypedDocumentNode, DocumentTypeDecoration, Path, DefaultBodyType, WebSocketData, WebSocketLink, etc.) to type-only imports.
Test Support
test/support/WebSocketServer.ts, test/support/environments/vitest-environment-node-websocket.ts, test/support/graphql.ts, test/support/utils.ts, test/support/ws-test-utils.ts
Changed imports for FastifyInstance, Environment, DocumentTypeDecoration, TypedDocumentNode, ClientRequest, IncomingMessage, WebSocketData, WebSocketTransport, and WebSocketConnectionData to type-only imports.
TypeScript Configuration
tsconfig.base.json
Added compilerOptions.verbatimModuleSyntax: true to enforce preservation of declared ESM/CJS import/export forms during TypeScript compilation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • mswjs/msw#2650: Adds the DefineNetworkOptions symbol to the experimental index, directly preceding this PR's conversion of that export to type-only.

Poem

🐰 A hop and skip through imports so fine,
Types now float, at compile time!
Runtime slims with syntax true,
Bundles bundle, just for you! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main fix: enabling and using verbatimModuleSyntax consistently to ensure type exports are properly handled.
Description check ✅ Passed The description clearly explains the problem (runtime SyntaxError from type-only export), the fix, and context about broader TypeScript configuration changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@christoph-fricke christoph-fricke force-pushed the fix/types-imported-as-value branch 2 times, most recently from aee80f6 to 0c34cc6 Compare April 10, 2026 12:41
Comment thread src/browser/utils/deserializeRequest.ts
@christoph-fricke christoph-fricke force-pushed the fix/types-imported-as-value branch from 0c34cc6 to fb8cda5 Compare April 10, 2026 14:17
@christoph-fricke christoph-fricke changed the title fix: mark all type imports and exports as type-only fix: importing msw/experimental throws runtime error Apr 10, 2026
Comment thread src/core/experimental/index.ts Outdated
@kettanaito kettanaito changed the title fix: importing msw/experimental throws runtime error fix: use verbatimModuleSyntax consistently in exports Apr 16, 2026
@kettanaito
Copy link
Copy Markdown
Member

Thanks for opening this, @christoph-fricke. I've discovered a bigger issue: we weren't using verbatimModuleSyntax at all so there was no restriction to export types properly from the public entrypoints.

I've fixed that and updated everything we export/import publicly and internally.

@christoph-fricke
Copy link
Copy Markdown
Contributor Author

@kettanaito Awesome! When I tried to verbatimModuleSyntax it caused weird, seemingly unrelated runtime errors in CI. I enabled it in tsconfig.base.json as well... Weird.

But I am glad it works now! Installed a local built into mswjs/playwright#48 and the tests run without crashing. Looks good to go from my POV.

@kettanaito kettanaito merged commit 9dedf52 into mswjs:main Apr 16, 2026
19 checks passed
@christoph-fricke christoph-fricke deleted the fix/types-imported-as-value branch April 16, 2026 16:00
@kettanaito
Copy link
Copy Markdown
Member

Released: v2.13.4 🎉

This has been released in v2.13.4.

Get these changes by running the following command:

npm i msw@latest

Predictable release automation by Release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants