I want to enable verbatimModuleSyntax. What do I need to beware of?
#8332
|
The docs say:
The way I understand it is: // client.ts
import { type User } from "./server";Will be compiled to: // client.js
import {} from "./server";Hence, the server code has been leaked into the client bundle. However, if I write the following: // client.ts
import type { User } from "./server";It will be compiled to: // client.js
// No import from "./server"So, as long as I always use |
Replies: 2 comments
|
Hi @aaditmshah ! Your understanding is essentially correct, with one important nuance. With verbatimModuleSyntax enabled, TypeScript preserves what you write: import type { User } from "./server" → fully erased, no runtime import. That is why the TanStack Start note exists: “Enabling verbatimModuleSyntax can result in server bundles leaking into client bundles. It is recommended to keep this option disabled.” Source: TanStack/router docs/start/framework/react/build-from-scratch.md, tanstack.com/start docs, and start-core SKILL.md (“HIGH: Enabling verbatimModuleSyntax… Keep it disabled”). TanStack Start Import Protection confirms the same rule: “Type-only imports and re-exports are ignored… because they are erased… Mixed imports still count when they include at least one runtime value.” Source: https://tanstack.com/start/latest/docs/framework/react/guide/import-protection So yes: if every type-only import is written as import type / export type, that statement cannot leak. The risk is everything around it: One forgotten import { type X } leaves a live import {} that executes ./server on the client. Recommendation: |
|
I checked this with a real build instead of reasoning about the transforms. I made a small Start app where every "server" module has a top-level side effect containing a unique string. Each module is imported through exactly one import form. I ran Versions: Is the server module in
In the leaking builds the side effect ends up as top-level code in client chunks. The route-file one lands in the chunk that the Start manifest loads as the root Why it depends on the file
TypeScript won't flag itWith What caught it
A note on
|
import typenever leaked in anything I tested. But "useimport typeinstead ofimport { type X }" is not the whole rule. WithverbatimModuleSyntax: true, a value import that you only use in a type position still leaks, for exampleimport { getAuth } from './auth'used only asReturnType<typeof getAuth>.tscdoesn't report it, and the Start/Router compilers don't always remove it. Whetherimport { type X }leaks also depends on whether one of those compilers rewrote the importing file.I checked this with a real build instead of reasoning about the transforms. I made a small Start app where every "server" module has a top-level side effect containing a unique string. Each module is importe…