jaredpalmer / tsdx

Zero-config CLI for TypeScript package development
https://tsdx.io
MIT License
11.29k stars 508 forks source link

Multiple entries to output multiple build files #175

Open fnky opened 5 years ago

fnky commented 5 years ago

Current Behavior

When building source with multiple entries, using --entry src/**.ts the build only uses the source of the first file it finds as the main entry point for ESM and CJS output.

Desired Behavior

It would be nice to be able to specify multiple destinations for different source files from a single codebase.

Suggested Solution

A solution could be to change the behavior of --entry flag, so that instead of outputting only definition files from multiple sources, it should output entry points for each specified entry file.

For example, with a folder structure like this:

src/
  code.ts
  ui.ts

Running tsdx build --entry src/**.ts or --entry src/code.ts src/ui.ts will yield the following output files:

dist/
  code.js
  code.cjs.production.js
  code.cjs.development.js
  code.esm.js
  ui.js
  ui.cjs.production.js
  ui.cjs.development.js
  ui.esm.js

Where code.js and ui.js are similar to index.js for single entry projects.

Who does this impact? Who is this for?

This is useful for building applications, plugins or other types of projects, where multiple entries are required, without having to create a monolithic repo architecture with a custom build process to combine build files.

An example is the recently released Figma Plugin API, which specifies a main file for the underlying plugin code, and a ui file for browser UI part.

See an example project using React and Webpack to create a Figma UI plugin

Describe alternatives you've considered

Additional context

I was looking for ways to specify multiple entries with --entry as outlined in https://github.com/palmerhq/tsdx/pull/28 but it seems to only output source files for the first file it finds and the rest just outputs as definition files, not actual source files. This is a bit confusing and may be a bug as it shares the same property name as webpack (entry), but works differently.

jaredpalmer commented 5 years ago

This shouldn’t be too tough. Thanks for the write up and use case

elado commented 5 years ago

another use case is adding command line tool (package.json bin) to a package in a separate src/cli.ts file. but here, there's no need to use esm/umd/cjs builds.

tsdx.config.js rollup method returns a single entry per build format (esm/umd/cjs) but would be great to be able to also hook into the final config, so more entries can be injected.

running yarn build --entry src/index.ts --entry src/cli.ts only runs both builds on the same output file, so only the latter is getting in there.

jaredpalmer commented 5 years ago

Yep. This should be easy to implement just have to fool around with the cjs entry generation so it does it multiple times

darthtrevino commented 5 years ago

To follow up with @elado 's comment, if your code uses web-workers at all it would be nice to be able to use multiple entry points split across output files so that worker scripts are separate from everything else.

yordis commented 5 years ago

Related to this I guess,

How could I avoid having one single outputted file for ESM build?

src/
  folder/
    file.ts
  code.ts
  ui.ts
dist/
  folder/
    file.js
  code.js
  ui.js

I am looking for having something like Material UI folks put together, so it is more ESM friendly by default.

agilgur5 commented 4 years ago

So this seems to be a bug caused by the rollup config's output.file being set to the same thing for every entry file: https://github.com/jaredpalmer/tsdx/blob/d621994f3eb21b6f686cccf7170ba541e72886c3/src/createRollupConfig.ts#L32-L33

I created a workaround-ish with tsdx.config.js to support multiple entries:

module.exports = {
  rollup(config, options) {
    const outputDir = process.cwd() + '/dist/'
    const extension = '.' + config.output.file.split('.').slice(1).join('.')
    let filename = config.input.split('src/')[1] // remove src/
    filename = filename.split('.')[0] // remove extension, if any

    config.output.file = outputDir + filename + extension
    console.log(config.output.file)
    // replace / with __ for UMD names
    config.output.name = filename.replace('/', '__')
    return config
  }
}

and this will work with tsdx build --entry src/index.ts --entry src/cli.ts, outputting dist/index.js and ~dist/cli.js~ and their respective formats (cjs, esm, etc) and type declarations. EDIT: see next comment regarding CJS "entry file" issues


Buuut besides being very hacky, the output has some issues (possibly minor, pending your use case):


Also #365 seems directly related to this. Also related to this issue, @yordis created a separate issue #321 for that specific use case above, which I also gave a partial workaround for in https://github.com/jaredpalmer/tsdx/issues/321#issuecomment-564784332

agilgur5 commented 4 years ago

So I realized there's one other fairly big issue with my workaround: tsdx only ever outputs one CJS "entry file" (the one that splits between dev & prod builds), so while dist/index.js will be created, dist/cli.js will not be created, only dist/cli.esm.js, dist/cli.cjs.development.js, dist/cli.cjs.production.min.js and dist/cli.d.ts will be (and any other formats you might specify). There's no way to workaround this in tsdx.config.js, it has to be fixed internally. I submitted a PR (#367) to resolve these issues and make --entry src/index.ts --entry src/cli.ts work correctly without hacky workarounds.

If you're not using CJS though, you don't have a need for CJS entry files, so the hacky workaround could potentially work for you.

EDIT: fixed the duplicate type declarations issue in the PR as well, it similarly can only be fixed internally.

Another issue is that package.json fields like module won't apply for more than one entry, since module only accepts a single file. This could be resolved by creating more package.jsons though... but that's fairly complex of a solution. Directing your users to directly import a .esm.js file or just use the CJS build is the alternative there 😕

brainkim commented 4 years ago

Does anyone know how to do this just with raw rollup? I’m having trouble deduplicating my own files/making d.ts files show up in the right places.

ambroseus commented 4 years ago

@brainkim guess this would be helpful https://levelup.gitconnected.com/code-splitting-for-libraries-bundling-for-npm-with-rollup-1-0-2522c7437697

kevboh commented 4 years ago

Hello! I just ran into this as well. Would love some way to export a library that also bundles a CLI, and I'm happy to pitch in to make that happen.

felixmosh commented 3 years ago

Is it possible to use rollup-plugin-multi-input for this?

yaquawa commented 3 years ago

Hey guys, I'm happy with using tsup now 👍🏼

mikestopcontinues commented 3 years ago

Hey, what's the status of this? It looks like it's holding up a lot of projects, judging by the refs.

fnky commented 3 years ago

@mikestopcontinues It's currently part of the v0.15.0 milestone. Not sure if it's actively worked on at the moment. I have personally moved away from TSDX for now.

mikestopcontinues commented 3 years ago

Thanks @fnky . I guess I'll stick with microbundle for now, but I'll keep an eye out here. :)

flybayer commented 3 years ago

I've switched to https://github.com/preconstruct/preconstruct

y-nk commented 3 years ago

can't believe it's a year without fixing. @jaredpalmer do you need some help with that?

ImanMh commented 3 years ago

Can somebody from TSDX team mentions if this is totally a bad idea or it's on the roadmap so I can make the best decision about my problem?

mikestopcontinues commented 3 years ago

@ImanMh At this point, I think you need to look elsewhere. Look into esbuild. It handles typescript, .d.ts files, and multiple entries.