Enable lazy-loading of dependencies while using normal `import` syntax, without a bundler. Supports TypeScript's downlevel compilation.
npm install lazy-importsEnable lazy-loading of dependencies while using normal import syntax, without a bundler. Supports TypeScript's downlevel compilation.
I created this to boost the performance of a CLI tool. If the user runs cli-tool --help they should see usage info
very quickly without loading, for example, the aws-sdk. If they run cli-tool upload-report-to-s3 then it should load only the
dependencies used for that subcommand. I wanted to accomplish this lazy-loading without sprinkling our codebase with
manual, delayed require() or import() calls.
For TS support, you must have importHelpers enabled, since we monkey-patch 'tslib'.
``typescriptimport
import 'lazy-imports/enable';
import {parseCliArgs} from './cli-parser';
import {foo, bar} from 'some-expensive-module';
require('lazy-imports/disable'); // <-- as a function call so that autogenerated import statements will appear above it.
// Write as an if you do not want this behavior.
const args = parseCliArgs(); // <-- this should be really fast without loading the expensive module
if(args.doExpensiveOperation) {
foo(); // <-- first usage will trigger require()ing of the expensive module
bar();
}
`
I don't know what will happen if you leave lazy-imports enabled after your
import statements. I always disable it.
The return value from a lazy require() will be a Proxy object pretending to bemodule.exports = 'a string';
a function. Typically this covers all the bases: if the module is a function or
constructor, you can invoke it, and if it's an object, you can access properties
of it. This might break if the module does something atypical like
We replace require('module')._load with a version that returns lazy Proxy objects.require('tslib').__importStar
We also replace and .__importDefault since TypeScriptconst some_import_1 = __importStar(require('some-expensive-module'));
generates code that looks like which would otherwise
immediately trigger eager loading.
lazy-imports/{enable|disable} immediately remove themselves from require cache,enableLazyLoading()
so each time you import them, they execute and enable / disable lazy loading.
This is necessary because import statements are hoisted above all other statements,
so our enable / disable logic must be in the form of import statements.
We can't, for example, do .
Allow automatically enabling laziness across an entire codebase, without enabling and disabling in each file.
For example, pass a root directory, and any imports performed by files under that directory
will be automatically lazy-imported. This allows us to apply lazy-loading to our own codebase and skip it
for external codebases.
* could use globs, but that's more runtime weight; potentially slows things down
* trick is enabling at the top of our entry-point file. We'll still need to use the import trick there
`${ __dirname }/
#!/usr/bin/env node
// entry point file
import 'lazy-imports/enable';
//... imports ...
require('lazy-imports/disable');
require('lazy-imports').enableForPrefix();`
---
Needing to manually enable and disable in every file is annoying and problematic. If you forget to do this in even one file, it might trigger expensive loading of dependencies that kinda defeats the whole purpose of doing this.
There should be an alternative API so you can configure a glob pattern or subdirectory of code that will have laziness applied automatically. So you can call automaticallyLazy(__dirname) once in your entrypoint and then all files in your module's src directory will have lazy imports.
The existing lazy-imports/{enable,disable} API should be modified for compatibility:
import 'lazy-imports/enable'; forces lazyness, ignoring whatever you may have configured via automaticallyLazyimport 'lazy-imports/disable'; force lazyness to be disabled, ignoring whatever you may have configured via automaticallyLazyrequire('lazy-imports/auto'); goes back to automatic behavior, so that automaticallyLazy` behavior is followed.