developit / microbundle

📦 Zero-configuration bundler for tiny modules.
https://npm.im/microbundle
MIT License
8k stars 361 forks source link

[Question] Sub-exports depending on main export bundle the entire main-export. #1027

Open usefulthink opened 1 year ago

usefulthink commented 1 year ago

I have a project where I have a main-export and multiple other exports. The other exports are extensions that depend on the main-export, but I don't want them to be in the main-export to keep it as small as possible.

Users should be able to do:

import {Thing} from 'package';

// optional addition
import {OtherThing} from 'package/a';

(or similarly using multiple script-tags with the umd-builds)

As for the source-files, it's roughly like this:

// index.ts
export * from './main';

// main.ts
export class Thing {}

// a.ts
import {Thing} from './main';

export class OtherThing extends Thing {}

And the package.json contains these sub-exports:

"exports": {
    ".": {
      "import": "./dist/index.modern.js",
      "require": "./dist/index.js",
      "umd": "./dist/index.umd.js",
      "browser": "./dist/index.module.js"
    },
    "./a": {
      "import": "./dist/a.modern.js",
      "require": "./dist/a.js",
      "umd": "./dist/a.umd.js",
      "browser": "./dist/a.module.js"
    },
  }

With this, the output-files for './a' (all formats) have the entire definition of './main.ts' inlined.

So my question is, is there a way to configure microbundle to treat those other files as externals?

developit commented 1 year ago

You need to import the package name in your source, not the entry filename:

// index.ts
export * from './main';

// main.ts
export class Thing {}

// a.ts
import {Thing} from 'package';

export class OtherThing extends Thing {}
usefulthink commented 1 year ago

Thanks a lot for the quick response!

I tried that, and first got the following error:

(rpt2 plugin) Error: [...]: semantic error TS2307: Cannot find module '[...]' or its corresponding type declarations.

So I tried to tell typescript about the package:

// in tsconfig.json / compilerOptions

    "paths": { "@me/my-package": ["./src/main.ts"] },

That compiles without error, but unfortunately didn't change anything in the output (still contains everything from the main.ts in a.ts' output).

The next thing I tried was to instead specify the path as .:

// in tsconfig.json / compilerOptions
    "paths": { "@me/my-package": ["."] },

but that leads to the following error:

rpt2: options error TS5055: Cannot write file '[...]/dist/x.d.ts' because it would overwrite input file.

I finally solved it, by explicitly telling microbundle to treat the module as external (and using the first solution from above to make typescript happy):

microbundle src/index.ts src/a.ts --external '@me/my-package'   --globals @me/my-package=my.package

Is that something that microbundle should have been able to pick up on automatically?

bhollis commented 11 months ago

I ran into a similar issue - @usefulthink your solution worked, but it means the UMD build now contains a require call rather than being all bundled together. I think this is probably something where microbundle needs to be smart about setting external differently for different outputs.