egoist / tsup

The simplest and fastest way to bundle your TypeScript libraries.
https://tsup.egoist.dev
MIT License
9.24k stars 218 forks source link

how to keep original directory structure? #728

Open daolou opened 2 years ago

daolou commented 2 years ago
src
  - tool1
    - tool1.ts
  - tool2
    - tool2.ts
  - tool3
    - tool3.ts
  - index.ts

Such as above, src/index.ts is exports all, then use tsup src/index.ts --format esm,cjs,iife build,it will output:

dist
  - index.mjs
  - index.global.js
  - index.js

it not I want.

Upvote & Fund

Fund with Polar

oliveiraantoniocc commented 2 years ago

Maybe you meant about the legacy output modifier. If not, what would be the expected output? image

daolou commented 2 years ago

I want is code split,like this:

dist
  - tool1
    - tool1.js
    - tool1.mjs
  - tool2
    - tool2.js
    - tool2.mjs
  - tool3
    - tool3.js
    - tool3.mjs
  - index.js
  - index.mjs
jimmy-guzman commented 2 years ago

An approach I take to achieve something similar is to set up entry with a glob that would output an array of files, for example

import glob from 'tiny-glob'
import { defineConfig } from 'tsup'

export default defineConfig(async () => {
  return [
    {
      entry: await glob('./src/**/!(*.d|*.spec).ts'),
eric-burel commented 2 years ago

Maybe to elaborate, this seems appropriate when building libraries of React component or libraries of independant utilities (lodash style), see: https://stackoverflow.com/questions/72149666/a-fool-proof-tsup-config-for-a-react-component-library In this case you want to generate one .js per component.

We seem to need:

I still struggle to keep the directory structure. If entry is ["index.ts", "./smth/nested.ts"], the smth folder will be created. But this is probably not what you want since you need no bundling for index, and bundling for "nested". If entry is ["./smth/nested.ts"] it won't respect the directory structure.

Edit: here is the final result. Components are bundled with code splitting (in case 2 components use the same internal sub component for instance), index files are not bundled, only transpiled, and uses an explicit ".js" which is necessary for ESM modules to work. I am surprised that the ".js" extension is accepted by TypeScript, but it works. Named import works as expected, and tree shaking is now possible because each component leaves in an isolated ES module. This is not perfect but a good first step.

daolou commented 2 years ago

An approach I take to achieve something similar is to set up entry with a glob that would output an array of files, for example

import glob from 'tiny-glob'
import { defineConfig } from 'tsup'

export default defineConfig(async () => {
  return [
    {
      entry: await glob('./src/**/!(*.d|*.spec).ts'),

It's entry seem support glob:

export default defineConfig([
  {
    entry: ['src/**/*.ts'],
    splitting: true,
    target: 'es5',
    format: 'cjs',
    dts: true,
    treeshake: true,
    bundle: false,
  }
]);
eric-burel commented 2 years ago

Just to answer this precise question:

outDir: "./dist",
 esbuildOptions(options, context) {
    // the directory structure will be the same as the source
    options.outbase = "./";
    },
  },

The "outbase" option of esbuild will force the bundled files to have the same directory structure as the source.

I've updated my Stack Overflow answer here: https://stackoverflow.com/a/73883783/5513532

At this point I am stuck with one last thing, I need to add ".js" to the imports of "index.ts" and can't find a way to do that during bundling. So I add the ".js" manually in the "index.ts" source (sounds weird, but it works), except that now it breaks storybook.

daolou commented 2 years ago

Just to answer this precise question:

outDir: "./dist",
 esbuildOptions(options, context) {
    // the directory structure will be the same as the source
    options.outbase = "./";
    },
  },

The "outbase" option of esbuild will force the bundled files to have the same directory structure as the source.

I've updated my Stack Overflow answer here: https://stackoverflow.com/a/73883783/5513532

At this point I am stuck with one last thing, I need to add ".js" to the imports of "index.ts" and can't find a way to do that during bundling. So I add the ".js" manually in the "index.ts" source (sounds weird, but it works), except that now it breaks storybook.

use bundle: false and entry: ['./src/**/*.ts'] also work, but I have another problem: compilerOptions.paths not resolved image

daolou commented 2 years ago

BTW

if use options.outbase = "./";, it maybe affect esbuild-plugins that use build.onResolve;

aleclarson commented 2 years ago

One drawback to using globs in entry is that new files aren't picked up, so you have to restart tsup whenever adding a new file that matches one of the globs in entry.

https://github.com/egoist/tsup/issues/748

cenk1cenk2 commented 1 year ago

Just to answer this precise question:

outDir: "./dist",
 esbuildOptions(options, context) {
    // the directory structure will be the same as the source
    options.outbase = "./";
    },
  },

The "outbase" option of esbuild will force the bundled files to have the same directory structure as the source. I've updated my Stack Overflow answer here: https://stackoverflow.com/a/73883783/5513532 At this point I am stuck with one last thing, I need to add ".js" to the imports of "index.ts" and can't find a way to do that during bundling. So I add the ".js" manually in the "index.ts" source (sounds weird, but it works), except that now it breaks storybook.

use bundle: false and entry: ['./src/**/*.ts'] also work, but I have another problem: compilerOptions.paths not resolved image

Have you got a change to find a solution to compilerOptions.paths not being resolved, I have tried a couple of esbuild plugins to temporary fix but to no avail. For DTS part of the application it seems to resolve quite fine, but JavaScript part as you indicate is not resolved.

I guess this behavior is caused by esbuild not following any paths in the tsconfig when the bundling options is off.

MrKou47 commented 1 year ago

I scoured the documentation and couldn't find the bundle: false option. In my case, I just want to replace tsc with tsup cause tsc is too slow. Maybe we should add a section about bundle option to documentation?

cenk1cenk2 commented 1 year ago

For anybody stumbling accross the issue I ended up combining what I usually use for tsc.

Setting bundle to false as expected stops following paths on esbuild side of things. I suppose this would not be a concern of tsup. From the esbuild side I could not make it work using alias plugins and injecting tsconfig in.

So just used tsconfig-replace-paths cli to swap paths on onsuccess step. It is still fast and stable enough for me so that I prefer not to mark it as being a trade off.

Something like as below:

https://github.com/tailoredmedia/backend-nx-skeleton/blob/master/packages/nx-nest/tsup.config.ts

LennardWesterveld commented 1 year ago

Are you guys also having the issue that it gives the error on the first line No input files, try "tsup <your-file>" instead? Getting stuck on this piece of code: https://github.com/egoist/tsup/blob/main/src/index.ts#L77-L79 while I just have the config

  { 
       bundle: false,
       entry: ['src/**/*.{ts,tsx}', '!src/**/*.stories.{ts,tsx}', '!src/**/*.d.{ts,tsx}'],
   }
mxvsh commented 1 year ago

tried all, none of them worked 😒

eddienubes commented 7 months ago

I was trying to build a node library to use in both CJS and ESM environments and figured it out with

tsup ./src --format cjs,esm --no-splitting --dts --tsconfig tsconfig.build.json

to:

Hope it helps someone :)

retrouser955 commented 4 months ago

I was trying to build a Discord bot and changing entry from entry: ['src/index.ts'] to entry: ['src/**/*'] worked. I hope this helps.

Lordfirespeed commented 1 month ago

Interestingly, using the glob pattern src/**/* works fine with a structure like this:

./src
β”œβ”€β”€ index.ts
└── types
    β”œβ”€β”€ application.ts
    └── new-file.ts

Yields

./dist
β”œβ”€β”€ index.js
└── types
    β”œβ”€β”€ application.js
    └── new-file.js

But when the src directory has no top-level files, a nesting level is omitted:

./src
└── types
    β”œβ”€β”€ application.ts
    └── new-file.ts

Yields

./dist
β”œβ”€β”€ application.js
└── new-file.js

Perhaps I should create a new issue for this, though. It may even be an esbuild problem.