-
-
Notifications
You must be signed in to change notification settings - Fork 241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
tsup build failing with ERR_WORKER_OUT_OF_MEMORY #920
Comments
Same with Tsup version 7.0.0 (Using pnpm, turbo) I have to rollback to Tsup 6.5.0 to make it work Context: React library import { defineConfig } from 'tsup'
export default defineConfig((options) => {
return {
entry: ['src'],
sourcemap: true,
minify: !options.watch,
dts: false,
clean: true,
format: ['esm'],
external: ['react', 'react-hook-form'],
// https://github.com/shuding/react-wrap-balancer/blob/main/tsup.config.ts#L10-L13
esbuildOptions(options) {
options.banner = {
js: '"use client"',
}
},
}
}) Terminal output on build script:
|
Luckily they have a build function import glob from "glob"
import { build } from 'tsup'
import _ from 'lodash';
async function buildStage({ clean, entry }) {
console.log("🚀 ~ building entry ", entry)
try {
await build({
dts: true,
minify: true,
sourcemap: true,
treeshake: true,
splitting: true,
outDir: 'dist',
clean,
entry,
external: ['react', 'react-dom'],
format: ['esm', 'cjs'],
// outExtension({ format }) {
// return {
// js: `.${format}.js`,
// };
// },
});
} catch (error) {
console.log("🚀 ~ error while building entries :", entry);
console.log(error);
throw error;
}
}
export async function buildAllStages() {
const root_file = glob.sync('src/index.ts');
const files = glob.sync('src/components/**/index.ts');
const chunkSize = 3;
const chunks = _.chunk(files, chunkSize);
// await buildStage({ clean:true, entry: chunks[0] });
for await (const [index, chunk] of chunks.entries()) {
console.log('🚀 ~ chnk === ', chunk);
await buildStage({ clean:index===0, entry: chunk });
}
await buildStage({ clean:false, entry: root_file });
// await buildStage({ clean:true, entry: root_file });
}
export function invokeBuild(){
buildAllStages().then(()=>{
console.log("🚀 ~ buildAllStages success");
}).catch((error)=>{
console.log("🚀 ~ buildAllStages error === ", error);
})
}
invokeBuild() fixing it might not be that hard , might try it later |
Rolling back to version |
Unable to migrate back because I need Same error
|
My config if helpful import { defineConfig, Options } from 'tsup';
export default defineConfig((options: Options) => ({
entry: {
'Button/index': 'src/components/atoms/Button/Button.tsx',
// lots more components
},
splitting: false,
format: ['esm', 'cjs'],
dts: true,
minify: true,
external: ['react'],
...options,
})); On |
Is there any work around for this? |
Thanks @rajat1saxena. I can confirm that something in On my MBP ( I have not measured memory usage yet but I am also seeing OOM in Github Actions. |
Same here, with this conf when watching for changes (--watch):
It's not systematic, but sometimes after a few minutes we're affected. version 6.6.0 doesn't have this problem. (as mentioned above) |
I'm using
{
"scripts": {
"build": "NODE_OPTIONS='--max-old-space-size=16384' tsup",
"dev": "NODE_OPTIONS='--max-old-space-size=16384' tsup --watch",
}
} |
Duplicate of #875 |
Thanks, worked magically in my case. |
It works for you now but wait a bit while your project grows and it will start to fail. Also while it doesn't fail, it takes significant amount of time that will also cost via your CI. |
I tried running the build commands sequentially like this: const { build } = require("tsup");
const {
buildAllConfig,
buildBlocksConfig,
buildCoreConfig,
buildElementsConfig
} = require("./tsup.config");
async function sequentialBuild() {
await build(buildAllConfig);
await build(buildCoreConfig);
await build(buildBlocksConfig);
await build(buildElementsConfig);
}
sequentialBuild().catch((err) => {
console.error(err);
process.exit(1);
}); where my tsup configurations are split like this: export const buildAllConfig = defineConfig({
name: "Build All",
clean: true,
dts: true,
target: "es2019",
entry: { index: "components/index.ts" },
format: ["cjs", "esm"]
});
export const buildCoreConfig = defineConfig({
name: "Build Core",
clean: true,
dts: true,
target: "es2019",
format: ["cjs", "esm"],
entry: {
// CORE
"types/index": "components/types/index.ts",
"hooks/index": "components/hooks/index.ts",
"blocks/index": "components/blocks/index.ts",
"layout/index": "components/layout/index.ts",
"elements/index": "components/elements/index.ts"
}
});
export const buildBlocksConfig = defineConfig({
name: "Build Blocks",
clean: true,
dts: true,
target: "es2019",
format: ["cjs", "esm"],
entry: {
// BLOCKS
"blocks/misc/index": "components/blocks/misc/index.ts",
"blocks/auth/index": "components/blocks/auth/index.ts",
"blocks/pricing/index": "components/blocks/pricing/index.ts",
"blocks/feedback/index": "components/blocks/feedback/index.ts"
}
});
export const buildElementsConfig = defineConfig({
name: "Build Elements",
clean: true,
dts: true,
target: "es2019",
format: ["cjs", "esm"],
entry: {
// ELEMENTS
"card/index": "components/elements/card/index.ts",
"chip/index": "components/elements/chip/index.ts",
"tabs/index": "components/elements/tabs/index.ts",
"sheet/index": "components/elements/sheet/index.ts",
"logos/index": "components/elements/logos/index.ts",
"radio/index": "components/elements/radio/index.ts",
"table/index": "components/elements/table/index.ts",
"alert/index": "components/elements/alert/index.ts",
"label/index": "components/elements/label/index.ts",
"input/index": "components/elements/input/index.ts",
"badge/index": "components/elements/badge/index.ts",
"dialog/index": "components/elements/dialog/index.ts",
"button/index": "components/elements/button/index.ts",
"select/index": "components/elements/select/index.ts",
"avatar/index": "components/elements/avatar/index.ts",
"switch/index": "components/elements/switch/index.ts",
"command/index": "components/elements/command/index.ts",
"popover/index": "components/elements/popover/index.ts",
"loading/index": "components/elements/loading/index.ts",
"tooltip/index": "components/elements/tooltip/index.ts",
"skeleton/index": "components/elements/skeleton/index.ts",
"combobox/index": "components/elements/combobox/index.ts",
"textarea/index": "components/elements/textarea/index.ts",
"pinInput/index": "components/elements/pinInput/index.ts",
"checkbox/index": "components/elements/checkbox/index.ts",
"progress/index": "components/elements/progress/index.ts",
"accordion/index": "components/elements/accordion/index.ts",
"backToTop/index": "components/elements/backToTop/index.ts",
"dataTable/index": "components/elements/dataTable/index.ts",
"appStores/index": "components/elements/appStores/index.ts",
"sortButton/index": "components/elements/sortButton/index.ts",
"scrollArea/index": "components/elements/scrollArea/index.ts",
"breadcrumb/index": "components/elements/breadcrumb/index.ts",
"phoneInput/index": "components/elements/phoneInput/index.ts",
"splitButton/index": "components/elements/splitButton/index.ts",
"dropdownMenu/index": "components/elements/dropdownMenu/index.ts",
"fileDropzone/index": "components/elements/fileDropzone/index.ts",
"navigationMenu/index": "components/elements/navigationMenu/index.ts",
"stopPropagationWrapper/index":
"components/elements/stopPropagationWrapper/index.ts"
}
});
But I'm still getting the same error:
|
Try only building 3 files at a time |
That actually worked! thanks so much @tigawanna |
Thanks @tigawanna your workaround worked. I was able to build up to 6 files at a time without issues.
Such grouping reduced the number of files built in a set and also made the code look a bit organised. It does rids us of this ERR_WORKER_OUT_OF_MEMORY error until a permanent fix is issued from @egoist |
Glad it helped the fix might be spawning a new worker for every ~4 files , but it feels like such a niche use case that the workaround will be fine for most |
Any news @egoist on this point ? it's preventing us from upgrading to the latest tsup version 😞 (we are stuck on 6.x) |
build: rolled back to tsup 6.6.0 to avoid this issue egoist/tsup#920
Just faced the same issue. Would love if someone from the tsup team can look into patching this |
Getting the same issue on a library with ~100 entrypoints. Running locally I have no problems but in CI seeing the OOM error. Splitting the config into chunks of 2,4,6 basically grinds the whole process to a halt locally. Interestingly |
I have the same issue with |
I played around with a chunking approach where the async function runRollup(options: RollupConfig, chunkSize: number) {
const { rollup } = await import('rollup')
try {
const start = Date.now()
const getDuration = () => {
return `${Math.floor(Date.now() - start)}ms`
}
logger.info('dts', 'Build start')
if (!options.inputConfig.input) {
logger.error('dts', 'No input')
return
}
for (const input of chunkInput(options.inputConfig.input, chunkSize)) {
const inputConfig = {
...options.inputConfig,
input,
}
const bundle = await rollup(inputConfig)
const results = await Promise.all(options.outputConfig.map(bundle.write))
const outputs = results.flatMap((result) => result.output)
logger.success('dts', `⚡️ Chunk build success in ${getDuration()}`)
reportSize(
logger,
'dts',
outputs.reduce((res, info) => {
const name = path.relative(
process.cwd(),
path.join(options.outputConfig[0].dir || '.', info.fileName)
)
return {
...res,
[name]:
info.type === 'chunk' ? info.code.length : info.source.length,
}
}, {})
)
}
} catch (error) {
handleError(error)
logger.error('dts', 'Build error')
}
} What do y'all think? |
Using |
I'm having the same issue with the following defineConfig({
entry: ['src/index.ts'],
target: 'es2022',
format: ['cjs', 'esm'],
clean: true,
sourcemap: true,
dts: false,
}); and the following TypeScript config: {
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Default",
"compilerOptions": {
"outDir": "dist",
"lib": ["ES2022", "dom"],
"module": "ESNext",
"target": "ES2022",
"composite": false,
"emitDeclarationOnly": true,
"declaration": true,
"declarationMap": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"inlineSources": false,
"isolatedModules": true,
"moduleResolution": "node",
"noUnusedLocals": false,
"noUnusedParameters": false,
"preserveWatchOutput": true,
"skipLibCheck": true,
"strict": true,
"resolveJsonModule": true
},
"exclude": ["node_modules", "dist"]
} I noticed that if I set I guess it has something to do with the fact that I also wonder how Turborepo contributes to memory utilization. Is it OK to spawn so many
|
I have been using This approach was better than // tsup.config.ts
import { copyFile } from 'node:fs/promises'
import { exec } from 'node:child_process'
import { promisify } from 'node:util'
import glob from 'tiny-glob'
import { defineConfig } from 'tsup'
const pexec = promisify(exec)
export default defineConfig({
cjsInterop: true,
clean: true,
entry: ['src/**/*.ts', '!src/**/*.test.ts'],
format: ['cjs', 'esm'],
shims: true,
sourcemap: false,
splitting: true,
target: 'node20',
//
async onSuccess () {
try {
await pexec('tsc --emitDeclarationOnly --declaration')
const files = await glob('dist/**/*.d.ts')
await Promise.all(files.map(file => copyFile(file, file.replace('.d.ts', '.d.mts')))) // or to `.d.cjs` for `"type": "module"` projects
} catch (err) {
console.error()
console.error('Typescript compilation error:')
console.error()
console.error(err.stdout)
throw err
}
}
}) Don't forget to add |
I doubt that |
Can someone please confirm if this issue still exists when using Node.js v21 or above? This may have been fixed upstream in Node.js as per nodejs/node#25382 in nodejs/node@ce4102e (part of Node from v21.0.0 onwards), which changes the way that heap memory is allocated to forked child threads. |
Hi, we have observed that the latest v21 is slightly better as it was able to compute more folders but it eventually encounter the same issue.
|
Still the case with Node 22.7.0
|
@egoist any update on this? I'm also heavily blocked |
bumping this thread, we're running into this problem on certain builds in Firebase Genkit |
I decided to run tsup multiple times with a small batch size to avoid the out-of-memory (OOM) error. Below is the content of import { exec } from "child_process";
import fs from "fs";
import path from "path";
const componentsDir = "src/components";
const batchSize = 3;
const tsupFlags =
"--format esm --tsconfig tsconfig.app.json --external react --external react-dom --external class-variance-authority --external clsx --external tailwind-merge --dts";
const components = fs
.readdirSync(componentsDir)
.filter((file) => fs.statSync(path.join(componentsDir, file)).isDirectory());
const buildBatches = (components) => {
const batches = [];
for (let i = 0; i < components.length; i += batchSize) {
batches.push(components.slice(i, i + batchSize));
}
return batches;
};
const transpileComponents = (batch) => {
const entryPoints = batch
.map((comp) => `--entry ${path.join(componentsDir, comp, "index.tsx")}`)
.join(" ");
const command = `tsup ${entryPoints} --outDir dist/components ${tsupFlags}`;
exec(command, (error, stdout, stderr) => {
if (error) {
console.error(`Error compiling batch: ${error.message}`);
return;
}
if (stderr) {
console.error(`stderr: ${stderr}`);
return;
}
console.log(`stdout: ${stdout}`);
});
};
const batches = buildBatches(components);
exec(`tsup src/index.ts ${tsupFlags}`);
batches.forEach(transpileComponents); |
I was same problem and fixed doing this: tsup make build and tsc emit d.ts // tsup.config.ts
export default defineConfig((options) => ({
entry: ['src/**/*'],
format: ['cjs', 'esm'],
dts: false, // disabled d.ts IMPORTANT!
sourcemap: true,
clean: true,
splitting: false,
external: ['react', 'react-dom'],
onSuccess: async () => {
execSync('node generate-exports.js', { stdio: 'inherit' });
},
...options,
})); // tsconfig.json
{
"compilerOptions": {
"target": "es2018",
"module": "ESNext",
"jsx": "react-jsx",
"moduleResolution": "Node",
"outDir": "./dist",
"declaration": true,
"declarationMap": false,
"emitDeclarationOnly": true, // Just emit declaration IMPORTANT
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"baseUrl": "src",
"paths": {
"@components/*": ["components/*"],
"@styles/*": ["styles/*"],
"@typings/*": ["typings/*"],
"@utils/*": ["utils/*"]
}
},
"include": ["src"]
}
// package.json
{
...
"scripts": {
"build": "tsup && tsc"
}
} |
@ale-vncs Sadly this solution doesn't work for everyone. It just reduces the memory footprint (leaks?), so it works in your case because you're not hitting the limit, but my project still fails even when I disable DTS generation. See: #920 (comment) |
I have a bunch of entrypoints, and running it with dts: 'src/index.ts',
entry: [
'src/components/**/index.ts',
'src/helpers/**/*.{ts,tsx}',
'src/index.ts',
], This |
Bumping this as its still an active issue |
Well, I found a workaround, that works good for multiple entry files (in my project I have more than 80 entry files). The concept is to use chunks, because
|
The `npm run build` command was failing due to errors with the tsup build command. The increase in heap space is a temporary fix that we may run into again as the repo grows - the out of memory error that it prevents is an ongoing issue with the library itself (egoist/tsup#920).
tsup build is failing on me when dts is enabled
cli output
Upvote & Fund
The text was updated successfully, but these errors were encountered: