Skip to content

Commit 9bcb7b0

Browse files
authored
feat: bundle types (#14020)
This PR updates the build process to generate a single `dist/index.bundled.d.ts` file that bundles all `payload` package types. Having one bundled declaration file makes it easy to load types into the Monaco editor (e.g. for the new Code block), enabling full type completion for the `payload` package. ## Example ```ts BlocksFeature({ blocks: [ CodeBlock({ slug: 'PayloadCode', languages: { ts: 'TypeScript', }, typescript: { fetchTypes: [ { filePath: 'file:///node_modules/payload/index.d.ts', url: 'https://unpkg.com/payload@3.59.0-internal.e247081/dist/index.bundled.d.ts', // <= download bundled .d.ts }, ], paths: { payload: ['file:///node_modules/payload/index.d.ts'], }, typeRoots: ['node_modules/@types', 'node_modules/payload'], }, }), ], }), ``` <img width="1506" height="866" alt="Screenshot 2025-10-01 at 12 38 54@2x" src="https://github.com/user-attachments/assets/135b9b69-058a-42b9-afa0-daa328f64f38" /> --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211524241290884
1 parent 5d86d5c commit 9bcb7b0

File tree

6 files changed

+353
-35
lines changed

6 files changed

+353
-35
lines changed

eslint.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export const defaultESLintIgnores = [
2424
'**/app',
2525
'src/**/*.spec.ts',
2626
'**/jest.setup.js',
27+
'packages/payload/rollup.dts.config.mjs',
2728
]
2829

2930
/** @typedef {import('eslint').Linter.Config} Config */

packages/payload/eslint.config.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import { rootEslintConfig, rootParserOptions } from '../../eslint.config.js'
55

66
/**
77
* We've removed all eslint.config.js from the packages, but we have to leave this one as an exception.
8-
* The payload package is so large that without its own eslint.config, an M3 Pro (18GB) runs out of
8+
* The payload package is so large that without its own eslint.config, an M3 Pro (18GB) runs out of
99
* memory when linting. Interestingly, two days ago when I started this PR, this didn't happen, but it
1010
* started happening when I did a merge from main to update the PR.
11-
* tsc also takes a long time to run. It's likely that at some point we'll need to split the package
11+
* tsc also takes a long time to run. It's likely that at some point we'll need to split the package
1212
* (into payload1, payload2, etc.) and re-export them in payload.
1313
*/
1414
/** @type {Config[]} */
@@ -21,11 +21,11 @@ export const index = [
2121
tsconfigRootDir: import.meta.dirname,
2222
projectService: {
2323
// See comment in packages/eslint-config/index.mjs
24-
allowDefaultProject: ['bin.js', 'bundle.js'],
24+
allowDefaultProject: ['bin.js', 'bundle.js', 'rollup.dts.config.mjs'],
2525
},
2626
},
2727
},
2828
},
2929
]
3030

31-
export default index
31+
export default index

packages/payload/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,11 @@
7171
"bin.js"
7272
],
7373
"scripts": {
74-
"build": "rimraf .dist && rimraf tsconfig.tsbuildinfo && pnpm copyfiles && pnpm build:types && pnpm build:swc && echo skipping esbuild",
74+
"build": "rimraf .dist && rimraf tsconfig.tsbuildinfo && pnpm copyfiles && pnpm build:types && pnpm build:swc && pnpm bundle:types && echo skipping esbuild",
7575
"build:bundle-for-analysis": "node ./bundle.js esbuild",
7676
"build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
7777
"build:types": "tsc --emitDeclarationOnly --outDir dist",
78+
"bundle:types": "rollup -c rollup.dts.config.mjs",
7879
"clean": "rimraf -g {dist,*.tsbuildinfo}",
7980
"clean:cache": "rimraf node_modules/.cache",
8081
"copyfiles": "copyfiles -u 1 \"src/**/*.{html,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
@@ -132,6 +133,8 @@
132133
"graphql-http": "^1.22.0",
133134
"react-datepicker": "7.6.0",
134135
"rimraf": "6.0.1",
136+
"rollup": "4.52.3",
137+
"rollup-plugin-dts": "6.2.3",
135138
"sharp": "0.32.6"
136139
},
137140
"peerDependencies": {
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import dts from 'rollup-plugin-dts'
2+
import path from 'node:path'
3+
4+
import { builtinModules } from 'node:module'
5+
6+
const WHITELIST = ['ts-essentials', 'croner', '@payloadcms/translations'] // <-- only these get bundled
7+
8+
/**
9+
* One-step DTS bundle:
10+
* - Input from your TS source entry
11+
* - Output to a single dist/index.bundled.d.ts
12+
* - respectExternal: true -> aggressively inline third-party .d.ts (helps with ts-essentials)
13+
*/
14+
export default [
15+
{
16+
input: 'src/index.ts',
17+
output: { file: 'dist/index.bundled.d.ts', format: 'es' },
18+
plugins: [
19+
dts({
20+
tsconfig: './tsconfig.bundletypes.json',
21+
respectExternal: true,
22+
compilerOptions: {},
23+
}),
24+
],
25+
26+
/**
27+
* Externalize all non-whitelisted packages and Node builtins.
28+
* If we bundle all packages, this script runs out of memory.
29+
*/
30+
external: (id) => {
31+
// 1) Always keep Node builtins external
32+
if (builtinModules.includes(id) || builtinModules.includes(id.replace(/^node:/, '')))
33+
return true
34+
35+
// 2) Keep virtual/internal rollup ids external just in case
36+
if (id.startsWith('\0')) return true
37+
38+
// 3) Never externalize *local* files (we want our own .d.ts bundled)
39+
if (id.startsWith('.') || path.isAbsolute(id)) return false
40+
41+
// 4) Bundle only whitelisted packages (opt-in)
42+
const isWhitelisted = WHITELIST.some((p) => id === p || id.startsWith(`${p}/`))
43+
return !isWhitelisted // everything else is external
44+
},
45+
},
46+
]
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"extends": "../../tsconfig.base.json",
3+
"references": [{ "path": "../translations" }],
4+
"compilerOptions": {
5+
// Do not include DOM and DOM.Iterable as payload is a server-only package.
6+
// This ensures node types do not conflict with node types (e.g. fetch.dispatcher)
7+
"lib": ["ES2022"],
8+
"paths": {
9+
"@payloadcms/translations": ["../translations/dist/exports/index.d.ts"],
10+
"@payloadcms/translations/utilities": ["../translations/dist/exports/utilities.d.ts"]
11+
}
12+
}
13+
}

0 commit comments

Comments
 (0)