developit / microbundle

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

Can microbundle be used to create subpackages? #726

Open Teajey opened 3 years ago

Teajey commented 3 years ago

Currently I have a package @namespace/package built using microbundle. I want to store certain functionality in a subdirectory of the package e.g. import { ... } from "@namespace/package/subdirectory". An example of this would be the way importing now works for Apollo Client 3.0: https://www.apollographql.com/docs/react/migrating/apollo-client-3-migration/#apolloreact-testing

Is this something that can be configured in microbundle, or does this require something else? I'm not even sure what structuring your distributable like this is referred to, and haven't been able to find anything from my searching on Google.

Sorry for my ignorance and thanks for any help 😊

developit commented 3 years ago

Hi @Teajey! Currently Microbundle assumes a single dist/output directory, which means it's not possible to have a single microbundle ... command bundle both a parent directory and subpackage directories.

However, you can still run microbundle multiple times - it's obviously a little slower, but it does work and it properly respects the package.json files in your subdirectory "packages":

{
  "scripts": {
    "build": "npm run -s build:core && npm run -s build:other",
    "build:core": "microbundle",
    "build:other": "cd packages/other && microbundle"
  }
}

Just make sure your subpackages properly import the @namespace/package identifier for your main package and never do import '../..'. On the plus side, if you're not using yarn workspaces or anything like that, you can link the subpackage to the parent package (in a "postinstall" script if you want) by doing npm link ../.. - any imports for the parent package name will resolve to your root directory.

Getting this properly supported in Microbundle without the workarounds is pretty high on my list of things to do. I have a bunch of repositories that are laid out this way (Preact, HTM, unfetch..), and right now it's a little tedious.

Teajey commented 3 years ago

Thanks for the quick and detailed reply!

I'm still struggling with this unfortunately 😅 My distributed package looks like this:

package
├── dist/
├── subpackage
│   ├── dist/
│   └── package.json
└── package.json

package/subpackage/package.json looks like this:

{
  "name": "@namespace/package/subpackage",
  "source": "src/index.js",
  "main": "dist/subpackage.js",
  "module": "dist/subpackage.module.js",
  "unpkg": "dist/subpackage.umd.js"
}

My build scripts look like this:

    "build": "yarn run build:core && yarn run build:core",
    "build:core": "microbundle --no-compress --jsx React.createElement",
    "build:subpackage": "cd subpackage && microbundle --no-compress --jsx React.createElement",

The subpackage does seem to be successfully imported by other workspaces adjacent to package/ (yes, I am using yarn workspaces), but one of my hooks imported from @namespace/package/subpackage triggers the ol' React invalid hook call.

I'm at a loss, because my hook usage is valid, I've tested the hooks by exporting them from @namespace/package instead. And VS Code, at least, tells me that package and subpackage are both importing the same versions of React and React DOM like they're supposed to... So I think I'm failed by my limited understanding of how npm packages work.

Sorry, I'm not sure whether this problem still falls under the purview of microbundle

Teajey commented 3 years ago

For anyone interested, I finally got this working by copying the peerDependencies of package/ into the package.json of subpackage/.

My package/subpackage/package.json has evolved into this:

{
  "name": "@namespace/package/subpackage",
  "private": true,
  "source": "src/index.js",
  "main": "dist/subpackage.js",
  "module": "dist/subpackage.module.js",
  "unpkg": "dist/subpackage.umd.js",
  "peerDependencies": {
    "formik": "^2.1.5",
    "react": "^16.13.1",
    "react-bootstrap": "^1.3.0",
    "react-dom": "^16.13.1",
    "react-i18next": "^11.7.2",
    "react-router-dom": "^5.2.0"
  }
}

The subpackage only requires react and react-dom. But strangely, if I exclude all the other peerDependencies, the package size is larger.

With all of the peerDependencies:

yarn run v1.22.5
$ cd subpackage && microbundle --no-compress --jsx React.createElement
Build "subpackage" to dist:
      1.74 kB: subpackage.js.gz
      1.51 kB: subpackage.js.br
       1132 B: subpackage.modern.js.gz
        974 B: subpackage.modern.js.br
      1.65 kB: subpackage.module.js.gz
      1.43 kB: subpackage.module.js.br
      1.84 kB: subpackage.umd.js.gz
      1.59 kB: subpackage.umd.js.br
✨  Done in 1.57s.
[thomas@thomas-mbp ~/code/namespace/package] master*

With only the essential peerDependencies:

yarn run v1.22.5
$ cd subpackage && microbundle --no-compress --jsx React.createElement
Build "subpackage" to dist:
      2.55 kB: subpackage.js.gz
      2.23 kB: subpackage.js.br
      2.22 kB: subpackage.modern.js.gz
      1.92 kB: subpackage.modern.js.br
      2.47 kB: subpackage.module.js.gz
      2.14 kB: subpackage.module.js.br
      2.68 kB: subpackage.umd.js.gz
      2.33 kB: subpackage.umd.js.br
✨  Done in 11.30s.
[thomas@thomas-mbp ~/code/namespace/package] master*
developit commented 3 years ago

Hiya - sorry for the slow reply. I'm glad you figured out the issue. Microbundle infers whether packages should be bundled or left as imports/requires based on your dependencies, and dependencies are never inhereted from parent package.json files (just like in Node). You should always include all peerDependencies your module relies on in the package.json for that module, otherwise they'll get bundled.