Skip to content
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

🚀 Feature: Rethinking building: entry points, bundle: false, and tsup issues #1910

Open
2 tasks done
JoshuaKGoldberg opened this issue Jan 22, 2025 · 2 comments
Open
2 tasks done
Labels
status: in discussion Not yet ready for implementation or a pull request type: feature New enhancement or request

Comments

@JoshuaKGoldberg
Copy link
Owner

JoshuaKGoldberg commented Jan 22, 2025

Bug Report Checklist

Overview

Coming over and recapping from JoshuaKGoldberg/eslint-fix-utils#7 -> JoshuaKGoldberg/eslint-fix-utils#13: CTA right now uses tsup for production builds. tsup is a pretty darn good wrapper around esbuild. It was originally added to CTA in #623 -> #640 to avoid transpiling test files such as *.test.*.

tsup is configured at the moment with a couple opinionated settings:

  • bundle: false: I prefer having output lib/ files match the structure in src/ for a few reasons:
    • Development: faster incremental rebuilds, especially in larger packages
    • Production: IMO it's easier to debug node_modules/ contents when the structure matches
  • entry: ["src/**/*.ts", "!src/**/*.test.*"]: since there isn't bundling, the full list of files to build needs to be told to tsup

We've also started to see some issues with tsup falling out of maintenance a bit (💔 - sustained open source maintenance is hard and taxing!):

I think there are two root issues here:

  • tsup is build on a bundler, esbuild, and is tailored to the bundling use case. The file-by-file transpilation use case is not as emphasized.
  • tsup is not being maintained at a level needed to match its growing userbase.

Therefore, I think it would make sense to think critically about how CTA packages build lib/ output. Is there a tool that can satisfy all the desired needs?

  • File-by-file transpilation
  • Tree-shaking based on entry points, not hardcoded heuristics like *.test.*
  • Quick JS output (read: not waiting on type checking to output JS files)
  • Full .d.ts output (read: not being limited to just what's supported in isolatedDeclarations)

If not, I shudder to think I might have to build one. 😬

Additional Info

The fact that tsup exists & works at all is awesome. I don't mean to file this issue judgementally. https://xkcd.com/2347, https://nolanlawson.com/2017/03/05/what-it-feels-like-to-be-an-open-source-maintainer etc. etc. This is a pragmatic issue: tsup isn't completely able to do what CTA needs right now and so we need to evaluate what's best for the project.

Edit: see also https://bsky.app/profile/joshuakgoldberg.com/post/3lhgppvnyjc2e for links to other projects

🎁

@JoshuaKGoldberg JoshuaKGoldberg added status: in discussion Not yet ready for implementation or a pull request type: feature New enhancement or request labels Jan 22, 2025
@michaelfaith
Copy link
Contributor

michaelfaith commented Jan 22, 2025

My general approach to packaging (which has largely been informed by projects with multiple entry points (think MUI, or a similar multiple component library that exposes 10s of entry points, each with their own set of symbols)), while also dual bundling and supporting maximum module resolution compatibility, has been to use rollup and to bundle each entry point, with one set of mjs, cjs, d.mts, and d.cts files for each entry point. The cjs/cts entry point files in a cjs folder, mjs/mts entry point files in an esm folder, and then an additional set of .d.ts files in the relative file locations that they'd be according to the project structure to support node10 module resolution (export conditions pointing at the other type files).

That kind of complexity can be overkill for simple one or two entry point package with a handful of files. But I think the model of having the end package exactly reflect the project's source structure can have a bit of a scaling problem. With bundling and export conditions you can be very strategic about the structure of your package big or small.

Additionally, as long as we're not minifying / tersering the output, then having something in a single bundle vs exploded out shouldn't really change how debuggable it is, in my opinion. Consumers of the package don't know what the original project structure was to begin with, so maintaining that structure for them doesn't feel like it buys us anything. You know what I mean?

@michaelfaith
Copy link
Contributor

michaelfaith commented Jan 22, 2025

With all that said, tsup was kind of nice because it's very minimal config to do a lot. My rollup configs have been far more involved, especially if you want them to be dynamic based on some entry point configuration file. But, to your point, it starts to break down in a dual-bundle model when you're not bundling. It can work ok in a non-bundle approach if you're not wanting to include both cjs and esm versions of the output. At the point where you do, then relative import path re-writing becomes necessary, which has only just recently been added to tsc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: in discussion Not yet ready for implementation or a pull request type: feature New enhancement or request
Projects
None yet
Development

No branches or pull requests

2 participants