Tree shaking on a meteor package

Hello !

I’m working on a app split into several package, and one of them exports some React components.
Let call it component:provider for simplicity sake

In component:provider/client/main.js, i’ve got something like that :

export { default as Foo } from "../imports/ui/components/Foo.jsx";
export { default as Bar } from "../imports/ui/components/Bar.jsx";

// ....

Let’s say i’m consuming Bar in my app, and only Bar :

import Bar from "meteor/component:provider";

// ...

Ok, so when i npm run visualize, and check bundle, i can see that Foo is onboarded

Are my expectations unrealistics, or should a dead code elimination normally clean these bits ?

I guess tree shaking is hard, and cannot infer all situations, so maybe i could find a workaround.
I’ve found that i can directly import my component from app, if my consumer knows its path :

import Bar from "meteor/component:provider/lib/ui/component/Bar";

It looks a bit dirty to me, but why not ?
The thing is that i would like my package to be built, depending on an ENV specifying a theme:

Package.onUse(function (api) {
  const style = process.env.STYLE;
  const getMainFile = (arch) =>
    `lib/${arch}/main${style ? `.${style}` : ''}.ts`;

  api.use(['typescript@4.4.1']);

  api.mainModule(getMainFile('server'), 'server');
  api.mainModule(getMainFile('client'), 'client', {
    lazy: true,
  });
});

So ideally, I don’t want my consumer to import a specific file, when he doesn’t have to know which theme i’ll be using.

What would you advise in this situation, how do you organise your imports and exports in Meteor to optimize build output ?

Thanks for your help !

Hello !

So there’s a PR that doesn’t seem stale about optimising tree shaking : Tree shaking by renanccastro · Pull Request #11164 · meteor/meteor · GitHub

As a workaround, before seeing these features in a new version, i’ve created a babel plugin that overwrites imports, based on theme defined in env variable. With this, I don’t have any unused file exported from my package.

Have a nice day !

I have a similar scenario with npm-defined packages. Would you mind sharing your solution?

Consider the following package structure:

package/file1.js

export const foo = 1

package/file2.js

export const bar = 2

If you omit api.mainModule and only import these files directly via

import { foo } from ‘meteor/component:provider/file1’ and nowhere reference the other (like you do in main.js) then you will have them separated by Meteors exact code splitting already.

Hi !

my babel plugin slowed rebuild during devs too much, so I abandoned it and created multiple main files in my packages, and the choice is done via process.env variable.

It means the code elimination is done during build, and it’s as if rest of the code didn’t exist.
Of course it’s not relevant for switching at runtime.

Example:

Package.describe({
  domain: 'soolid',
  code: 'illustration',
  name: 'soolid:illustration',
  summary: 'soolid illustration',
  version: '0.0.19',
  git: 'https://github.com/soolidtech/soolid.git',
});

Package.onUse((api) => {
  const illustration = process.env.SOOLID_ILLUSTRATION ?? 'unDraw';

  const getModule = (arch) => `./lib/${arch}/${illustration}/main.ts`;

  api.use(['typescript@4.5.4']);

  api.mainModule(getModule('server'), 'server');
  api.mainModule(getModule('client'), 'client', { lazy: true });
});

if you need to switch during runtime, then go with dynamic imports : dynamic-import | Meteor API Docs, it’s a cool feature