How can I modularize a complex app for faster build times?

I have a fairly complex app with many modules. I follow the structure guide provided by mdg.
The problem is that the build times are huge for this app.

What is the best way to modularize this app and easily disable multiple modules during development, so that the even the files are not included during build.
The best method that I have come up with (in my head) so far is that I use the following structure:

imports
  -- modules
      -- all
          -- module1
      -- stubs
          -- module1

The stubs directory has all file names with just the required top level variables. I can run a preprocessor to symlink the modules from all or stubs depending on a config json.

This ideally should work and decrease the load on the build times depending on the number of disabled modules.

Is there a better method?

Edit:
I had a belief that all required files are included in the build even if they are put in a conditional statement. It seems I was wrong. So something like this should work:

import {module1} from '/modules.json';
if (module1) require('.....');

This approach would be much simpler. I will test it to check and report back.

TLDR;
Not sure if the effort is worthwhile :slight_smile:
I am guessing ImportScanner#_findImportedModuleIdentifiers might have nothing to do with the app code, but more to do with the packages and npm imports. Someone from the mdg may help me out here.

So this is what I did:

settings.json > public :

"disabledModules": {
	"m1": true,
	"m2": true,
	"m3": true,
}

changed the directory structure from

imports
  api/module/
    server/publish.js
    db.js

imports
  ui/module
    compoents
    index.js

to

imports
  module/
    server/publish.js
    db.js
    ui
      compoents
      index.js

It was not required, but it makes more sense to have a directory structure matching the module structure.

On server
Then in my server/publish.js and server/methods.js, I simply put my modular imports under conditionals,

const {disabledModules as disabled} = Meteor.settings.public;
if (!disabled.m1) require('/imports/m1/...');

On client
I am using react. So I created a component

export const ModuleDisabled = ({name}) => <div className='...'>
  <div className='...'>Module <span className='bold'>{name}</span> is disabled</div></div>;

I segregated my routes too modulewise:

const routes = {
  m1: ({disabled}) => disabled ? <ModuleDisabled name='m1'/> : <Route exact path='/m1' render={.... normal render ...}/>,
}

const {disabledModules = {
	m1: false,
	m2: false,
	m3: false,
}

const Layout = <Switch>
	{Object.entries(disabledModules).map(([module, disabled]) => <RouteWithProps key={module} path={`/${module}`} component={Routes[module]} disabled={disabled}/>)}
</Switch>

Now lots of these modules have components which are extensively used in other modules
So I created a wrapper for these reusable components in imports/components/m1.js

const {disabledModules = {}} = Meteor.settings.public;

let C1, C2;
if (disabledModules.m1) {
	const {ModuleDisabled} = require('/imports/components/generic');
	C1 = C2 = () => <ModuleDisabled name='m1'/>;
}
else {
	({C1} = require('/imports/modules/m1/ui/c1'));
	({C2} = require('/imports/modules/m1/ui/c2'));
}

export {C1, C2};

and wherever I have to use these C1, C2, I require from this file.
Important Note: While import does not care about the order in which files are imported, require does. So if you require file c1 before c2, and c1 also requires C2 from this components/m1.js for any purpose, you will get C2 undefined.

The bad news
After this great deal of hard work, I was hoping to shave off a a huge chunk of the import time from my build.

| ImportScanner#_findImportedModuleIdentifiers.............7,543 ms (5227)
| files.rm_recursive.......................................4,582 ms (5)
| files.stat...............................................4,335 ms (66699)
| other optimistic statOrNull..............................3,459 ms (137067)
| Babel.compile............................................2,204 ms (2184)

But I didn’t.

It remains more or less the same.

The slightly good news
The files that are not required, are not seen anywhere in the profile logs, and whatever 1-5ms per file, that was earlier needed to import them, is now gone. So I do get a few ms off, but not enough to claim anything concrete here.

Conclusion
But IMO, this seems to be a good code structure wherein I can disable specific modules for different groups of people based on a variable, esp. in a complex project where the modules are linked a lot. Do share your feedback.

2 Likes