I'm curious how tree-shaking/dead code elimination of ESM works. I'm using Typescript for various Node.js projects and I started to export my own ESM packages (tsc --module es2015 --target es5 --outDir dist/esm) instead of CJS packages. Moreover, I tried to replace dependencies (like lodash) that are only available as CJS module with libraries that are available as ESM.
When I build a project, my entire TS codebase (./src) is transpiled to JS (./dist); dependencies are still taken from (./node_modules). No tree-shaking performed.
I guess I'd still need a bundler (like Webpack) that needs (at least) an entry point so that it can shake away everything that's not needed, so that I can reduce the package size of (e.g.) an AWS lambda? Is this something you would do?
When you use import instead of require, the transpiler is able to build the dependency tree on compile time (that is why you cannot dynamically import code).
For example, if you write this:
import { myfunc } from 'mylib';
The transpiler understands that you only need the myfunc function from mylib. If mylib includes other functions that are not used by myfunc, the transpiler can remove them from the bundle.
This is the short version. Tree-shaking is actually more complex than that. Webpack has a good article about it if you want to learn more:
https://webpack.js.org/guides/tree-shaking/
Related
I am new to npmjs package publishing and recently published my 1st package successfully. But since it's written in ES5 style. It is accessed only via "require"
const smiley = require('smiley-id');
What should be done to make it accessible via import statement also? like this
import smiley from 'smiley-id';
or/and
import { smileyId } from 'smiley-id';
require is part of the CommonJS module system (also known as cjs modules) whereas import and export are used in es2015 module system (also known as JavaScript modules, es6 modules or esm modules).
You can use import for both cjs and esm modules, but you cannot use require for esm modules. This means that in an esm module (or source file which you will compile with Typescript tsc or Babel or bundle with Webpack) you can already import your package, as it is, with import. However, you cannot use named imports. Instead you have to destructure the imported item afterwards.
If you want to distribute it as an esm package, this is also possible and it comes with some advantages, out of which the most important is related to tree-shaking. To distribute it as an esm module, you tell your build tool to output esm modules. This is different for different tools. Fortsc for example you specify --module es6 (there are multiple targets that output esm modules). For Webpack, Babel and Rollup, the procedure is different.
I was wondering
I am used to work with Webpack and bundle my own libraries to UMD and ESM.
But now I see that some of the libraries I am using, do not even create ESM bundles.
Even though, I am able to import them using es6 syntax!.
If it is the case, does it mean that modern bundlers like webpack and rollup knows how to import the modules without them having to be defined in ESM format?
Is it really necessary that I produce ESM format for my own builds?
I've been playing around with using es6 modules directly, without using Webpack, rollup, etc. After reading this article, I settled on the following:
import { MyObject } from './node_modules/module_name/index.mjs'
This works fairly well (in both browsers, and node using the esm module and running with node -r esm), but falls apart if you have nested dependencies. For example, if you have two modules that both depend on the same version of a third module, npm will only install a single copy of the third module at the top level, so when the first two modules go looking for it in ./node_modules it doesn't exist.
As far as I know there's not currently away around this other than bundling. Is there any plan for a unified syntax?
A dependency package that I'm using only has module and main as entry points. By default, the webpack version I'm using resolves to module entry point first. If I set resolve.mainFields to main in webpack configuration, I might affect other dependencies that were being resolved by 'browser' entry point by default.
So, the question is, how can I transpile a dependency like that and have it bundled in my bundle in ES5?
Some package writers distributes their ES6 sources, some other don't.
If you can figure out what "modern JavaScript syntax" the dependency is using, In general, the standard format for distributing a module is called CJS or Common JS.
If the author of the package you are trying to use did not export CJS from it's bundle (in 99% they do), then you don't need to transpile anything.
What do you mean by "the other ones"?
Be sure to import the right sources.
I'm stuck with dynamic requering es6 modules by invoking them through:
System.import('SOME_PATH').then(function (MODULE_FROM_SOME_PATH) {});
It works well with es6-module-loader and babel runtime compilation in browser, but when i want to precompile it to ES5 syntax (for production uses) it just passes System.import expression in code, leaving in practically untouched, just replacing System.import with equivalent System['import'].
I've tried gulp-babel and babel npm package. So when opened in browser it gives expected module loading error. How can i transpile my code to AMD syntax for ES5. Hoping for your help.
Just pushed babel-plugin-system-import-transformer that replaces System.import to the equivalent UMD import (AMD, CommonnJS & Global module imports).
I have also created a separate localforage branch that uses System.import statements as an example.
Hope this helps.