recycle recycle

This commit is contained in:
lovebird 2026-04-09 17:54:54 +02:00
parent 7aa76ec55c
commit 1407e6620e
4 changed files with 509 additions and 472 deletions

View File

@ -229,19 +229,53 @@ Before applying this technique to any library, verify:
condition in the exports map — confirmed in the
[official exports map](https://github.com/TanStack/router/blob/main/packages/router-core/package.json)).
4. **Exclude from `optimizeDeps`**
4. **Exclude from `optimizeDeps` — but skip packages with CJS transitive deps**
Without this, Vite's dev pre-bundler re-bundles the source into a single
opaque chunk and defeats the purpose in dev mode.
Without `exclude`, Vite's dev pre-bundler re-bundles the source into a
single opaque chunk and defeats the purpose in dev mode.
([Vite docs — `optimizeDeps.exclude`](https://vitejs.dev/config/dep-optimization-options.html#optimizedeps-exclude):
_"Dependencies to exclude from pre-bundling."_)
The Vite docs warn:
> _"CommonJS dependencies should not be excluded from optimization. If an
> ESM dependency is excluded from optimization, but has a nested CommonJS
> dependency, the CommonJS dependency should be added to
> `optimizeDeps.include`."_
**`@tanstack/react-store` is the exception here.** Its source file
`src/useStore.ts` imports `use-sync-external-store/shim/with-selector`
which is a CJS-only file (`module.exports = ...`). If that package is
excluded from optimizeDeps, Vite serves the raw CJS as ESM and the named
import breaks at runtime in dev:
```
Uncaught SyntaxError: The requested module
'.../use-sync-external-store/shim/with-selector.js'
does not provide an export named 'useSyncExternalStoreWithSelector'
```
The fix requires including the **exact import subpath**, not the package
root. `@tanstack/react-store/dist/esm/useStore.js` imports
`use-sync-external-store/shim/with-selector` — that is the string Vite
must match to serve the pre-bundled ESM wrapper:
```ts
optimizeDeps: {
exclude: ["@tanstack/router-core", "@tanstack/react-router", /* … */],
exclude: ["@tanstack/router-core", "@tanstack/react-router",
"@tanstack/history", "@tanstack/store"],
include: ["use-sync-external-store/shim/with-selector"],
}
```
Including `"use-sync-external-store"` (the root) does **not** fix it —
Vite pre-bundles at subpath granularity and the pre-bundled root is never
served for the subpath import.
**General rule:** before source-aliasing a package, scan its `src/` for
non-relative imports and check whether any of those are CJS-only (no
`"type": "module"`, no ESM `exports` map, uses `module.exports`). If so,
add the exact import path to `optimizeDeps.include`.
5. **Do not set `treeshake.moduleSideEffects: false` globally**
Setting it globally tells Rollup every module is pure and eliminates any
@ -272,22 +306,83 @@ Before applying this technique to any library, verify:
---
## rolldown-vite
[Rolldown](https://rolldown.rs) is a Rust-powered bundler designed as a drop-in
replacement for Rollup. Vite is migrating to it officially; in the meantime it
is available as the separate package `rolldown-vite`.
### Installing
Use the `npm:` package alias so `vite` resolves to `rolldown-vite` everywhere —
plugins and scripts continue to `import from 'vite'` unchanged:
```json
"devDependencies": {
"vite": "npm:rolldown-vite@7.3.1"
}
```
> **Pin the version.** `rolldown-vite` is experimental and may introduce
> breaking changes in patch releases.
> Do not use `@latest` in committed config.
Do **not** use `"overrides"` alongside a direct `vite` devDependency — npm
rejects the conflict.
### What changes
| Area | Before (Rollup/esbuild) | After (Rolldown) |
|---|---|---|
| Production bundler | Rollup | Rolldown (Rust) |
| Dep pre-bundler | esbuild | Rolldown |
| CJS interop | `@rollup/plugin-commonjs` | Rolldown native |
| JS minifier | esbuild | Oxc |
| CSS minifier | esbuild | Lightning CSS |
### Config notes
`rollupOptions.output.format` is a Rollup-only option not recognised by
Rolldown — remove it. Rolldown outputs ES modules by default, so no
replacement is needed.
### Benchmark
| | Build time | Main JS gzip |
|---|---|---|
| Vite 5 + Rollup | ~1100 ms | 46.4 kB |
| rolldown-vite 7 | **331 ms** | **45.8 kB** |
**3× faster build**, marginally smaller output. Dev server cold-start also
drops from ~730 ms to ~557 ms.
### Known limitations
- Some Rollup output options produce validation warnings (unknown keys).
Remove options that Rolldown handles by default (`format: "es"`, etc.).
- `manualChunks` is deprecated in favour of Rolldown's `advancedChunks`.
- The `transformWithEsbuild` Vite helper now requires `esbuild` installed
separately (Rolldown uses Oxc internally).
See the [official rolldown-vite docs](https://vite.dev/guide/rolldown) for the
full compatibility matrix.
---
## Build targets and module format
```ts
build: {
target: "esnext", // no transpilation downgrade, modern browsers only
modulePreload: { polyfill: false }, // drop the ~1.6 kB modulepreload polyfill
rollupOptions: {
output: { format: "es" }, // native ESM output
},
// format defaults to "es" in rolldown-vite — no rollupOptions needed
}
```
`target: "esnext"` + `format: "es"` means no CommonJS wrapper, no legacy
syntax transforms, and native dynamic `import()` for code splitting.
`modulePreload: { polyfill: false }` removes the small polyfill that is
irrelevant for modern browsers.
`target: "esnext"` means no legacy syntax transforms and native dynamic
`import()` for code splitting.
`modulePreload: { polyfill: false }` removes the small polyfill irrelevant for
modern browsers.
---

File diff suppressed because it is too large Load Diff

View File

@ -27,6 +27,6 @@
"@types/react-dom": "^18.3.1",
"rollup-plugin-visualizer": "^7.0.1",
"typescript": "^5.6.3",
"vite": "^5.4.11"
"vite": "npm:rolldown-vite@7.3.1"
}
}

View File

@ -36,20 +36,29 @@ export default defineConfig({
{ find: "@tanstack/react-router", replacement: nm("@tanstack/react-router/src/index.tsx") },
{ find: "@tanstack/history", replacement: nm("@tanstack/history/src/index.ts") },
{ find: "@tanstack/store", replacement: nm("@tanstack/store/src/index.ts") },
{ find: "@tanstack/react-store", replacement: nm("@tanstack/react-store/src/index.ts") },
// @tanstack/react-store is NOT source-aliased: its src/useStore.ts imports
// use-sync-external-store/shim/with-selector (CJS-only). The pre-built
// dist/esm already has that CJS dep inlined as ESM — no interop needed.
{ find: "@", replacement: path.resolve(__dirname, "src") },
],
},
// Don't pre-bundle the tanstack packages — let Rollup see their raw source
// Don't pre-bundle the four source-aliased tanstack packages — let Rollup
// see their raw TypeScript. @tanstack/react-store is intentionally omitted:
// its source pulls in a CJS dep (use-sync-external-store/shim/with-selector)
// that Vite can't serve as ESM without pre-bundling, so we leave it on the
// pre-built dist/esm path where the CJS is already inlined.
optimizeDeps: {
exclude: [
"@tanstack/router-core",
"@tanstack/react-router",
"@tanstack/history",
"@tanstack/store",
"@tanstack/react-store",
],
// use-sync-external-store ships CJS only. Vite cannot serve it as ESM
// without pre-bundling — include the exact subpath that @tanstack/react-store
// imports so the pre-bundler wraps it and named imports work in dev.
include: ["use-sync-external-store/shim/with-selector"],
},
plugins: [
tailwindcss(),
@ -65,8 +74,7 @@ export default defineConfig({
build: {
target: "esnext",
modulePreload: { polyfill: false },
rollupOptions: {
output: { format: "es" },
},
// rollupOptions.output.format is a Rollup-only option not supported by Rolldown;
// Rolldown outputs ES modules by default so it's not needed.
},
});