Optimal JavaScript module design
Perhaps the most acute yet least recognized problem in the JavaScript ecosystem is the widespread lack of understanding of optimal module design. Famous developers with decades of experience are propagating anti-patterns at the same rate as fresh beginners. Entire classes of problems can be avoided with less tooling and effort by simply following one golden rule…
The golden rule for optimal modules
JavaScript modules should only have single purpose default exports.
This means when an exported thing is needed it can be imported on its own without loading unneeded extras. Projects that strictly adhere to this principle might look unusual to most developers but are in fact far simpler, more secure, and more performant to edit, build, and run.
Why the “single purpose” wording? Basically, export one thing and don't attempt to cheat the rule by stuffing a bunch of things into one object and exporting that.
Why a default export and not a single named export? The main reason is better minification (particularly when minifying ESM for use in browsers without bundling), for example:
- import aLongName from "_/aLongName.mjs";
+ import a from "_/aLongName.mjs";
- import { anotherLongName } from "_/anotherLongName.mjs";
+ import { anotherLongName as b } from "_/anotherLongName.mjs";
- console.log(aLongName, anotherLongName);
+ console.log(a, b);
ℹ️ Note: The golden rule for optimal modules can be enforced using the eslint-plugin-optimal-modules
rule no-named-exports
.
The index module anti-pattern
An “index module”, also known as a “barrel module”, re-exports various things from other modules and typically has index
or mod
in the the filename.
Note that a module that only has a default export isn’t considered an “index module” in the context of the index module anti-pattern, even if it happens to have index
in the filename. Simple packages like isobject@4.0.0
that only export one thing are examples.
Index modules should be avoided because it’s better to skip them and deep import the exact thing you need directly from its source module, for these main reasons:
- Importing from an index module creates a waterfall loading step, as the index module has to be loaded by the JavaScript runtime before its imports can be discovered and loaded. This is most noticeable when using ESM in Deno or browsers as they fetch and cache imported modules over HTTP.
- When importing only a subset of the exports available in an index module, the JavaScript runtime still has to waste time and system hardware resources loading the remaining exports.
- Dev tools like type checkers and editor IntelliSense have to work harder analyzing the dependencies and code for other exports in the index module you might not even use.
- Index module files significantly bloat hard drives whether it be Node.js and
node_modules
, Deno cache, or browser cache. - If there is a syntax error or any other other problem preventing the JavaScript runtime or build tool from loading one of the modules re-exported in an index module, then the entire index module fails to load. This means a problem with just part of a package can prevent use of the entire package, if public exports are only available from a main index module.
- If a module re-exported in an index module contains malicious code or imports from a compromised dependency, then the hack can affect all users of the package that import from the index module instead of only users of the particular export that’s compromised.
Beyond efficiency and security issues, sometimes a package simply can’t export certain things together in an index module if they require different JavaScript runtime environments. For example, an index module couldn’t export something that depends on imports from the Node.js API along with something intended to be used in a browser, as when the browser JavaScript runtime loads the index module it will error on the Node.js stuff.
In the npm ecosystem it’s common to see packages export everything that can run universally in Node.js and browsers from a main index module /index.js
, with deeper index modules /server/index.js
and /browser/index.js
. This approach breaks down when it’s hard to arbitrarily divide code by predicted environments; globals like fetch
and AbortController
that were once considered browser-only are now available for Deno and some (but not all) Node.js versions. Do you break index modules down further by the kinds of server environments, e.g. /server/node/index.js
and server/deno/index.js
? Where do you put things that work in both Node.js and Deno, but not browsers? What about things that work in certain combinations of Node.js and browser versions, but not all? Something originally intended for one environment might be perfectly usable in another by polyfilling globals and populating build-in modules via import maps.
Not using index modules completely avoids all of the above problems.
Tragically, most packages on npm expose a main index module using one or more of these package.json
fields as the primary (or only) way to access public exports:
exports
main
jsnext:main
module
browser
The invention of the package.json
field main
is perhaps one of the greatest mistakes in JavaScript history as it encouraged the spread of the index module anti-pattern within the majority of the millions of published JavaScript packages, leading to a convoluted ecosystem and generations of indoctrinated developers who balk at deep imports. The widespread misconception that a package should have a main index module led to a churn of failed bundler conventions such as the the package.json
fields jsnext:main
, module
, and browser
that inhibit deep imports — delaying for years more deep import friendly approaches like the exports
field Node.js pioneered. We will also come to regret the widespread misuse of the exports
field, but that’s another topic for a future article.
Here is a practical comparison of a main index module import vs a deep import from lodash-es@4.17.21
…
With this demo.mjs
module:
import { isNull } from "https://unpkg.com/lodash-es@4.17.21";
The JavaScript runtime has to load the entire Lodash library, when only isNull
is needed.
When run with Deno (v1.22.2) using the command deno run demo.mjs
, the terminal output shows the consequences:
Terminal output…
Download https://unpkg.com/lodash-es@4.17.21
Download https://unpkg.com/lodash-es@4.17.21/lodash.js
Download https://unpkg.com/lodash-es@4.17.21/add.js
Download https://unpkg.com/lodash-es@4.17.21/after.js
Download https://unpkg.com/lodash-es@4.17.21/ary.js
Download https://unpkg.com/lodash-es@4.17.21/assign.js
Download https://unpkg.com/lodash-es@4.17.21/assignIn.js
Download https://unpkg.com/lodash-es@4.17.21/assignInWith.js
Download https://unpkg.com/lodash-es@4.17.21/assignWith.js
Download https://unpkg.com/lodash-es@4.17.21/at.js
Download https://unpkg.com/lodash-es@4.17.21/attempt.js
Download https://unpkg.com/lodash-es@4.17.21/before.js
Download https://unpkg.com/lodash-es@4.17.21/bind.js
Download https://unpkg.com/lodash-es@4.17.21/bindAll.js
Download https://unpkg.com/lodash-es@4.17.21/bindKey.js
Download https://unpkg.com/lodash-es@4.17.21/camelCase.js
Download https://unpkg.com/lodash-es@4.17.21/capitalize.js
Download https://unpkg.com/lodash-es@4.17.21/castArray.js
Download https://unpkg.com/lodash-es@4.17.21/ceil.js
Download https://unpkg.com/lodash-es@4.17.21/chain.js
Download https://unpkg.com/lodash-es@4.17.21/chunk.js
Download https://unpkg.com/lodash-es@4.17.21/clamp.js
Download https://unpkg.com/lodash-es@4.17.21/clone.js
Download https://unpkg.com/lodash-es@4.17.21/cloneDeep.js
Download https://unpkg.com/lodash-es@4.17.21/cloneDeepWith.js
Download https://unpkg.com/lodash-es@4.17.21/cloneWith.js
Download https://unpkg.com/lodash-es@4.17.21/commit.js
Download https://unpkg.com/lodash-es@4.17.21/compact.js
Download https://unpkg.com/lodash-es@4.17.21/concat.js
Download https://unpkg.com/lodash-es@4.17.21/cond.js
Download https://unpkg.com/lodash-es@4.17.21/conforms.js
Download https://unpkg.com/lodash-es@4.17.21/conformsTo.js
Download https://unpkg.com/lodash-es@4.17.21/constant.js
Download https://unpkg.com/lodash-es@4.17.21/countBy.js
Download https://unpkg.com/lodash-es@4.17.21/create.js
Download https://unpkg.com/lodash-es@4.17.21/curry.js
Download https://unpkg.com/lodash-es@4.17.21/curryRight.js
Download https://unpkg.com/lodash-es@4.17.21/debounce.js
Download https://unpkg.com/lodash-es@4.17.21/deburr.js
Download https://unpkg.com/lodash-es@4.17.21/defaultTo.js
Download https://unpkg.com/lodash-es@4.17.21/defaults.js
Download https://unpkg.com/lodash-es@4.17.21/defaultsDeep.js
Download https://unpkg.com/lodash-es@4.17.21/defer.js
Download https://unpkg.com/lodash-es@4.17.21/delay.js
Download https://unpkg.com/lodash-es@4.17.21/difference.js
Download https://unpkg.com/lodash-es@4.17.21/differenceBy.js
Download https://unpkg.com/lodash-es@4.17.21/differenceWith.js
Download https://unpkg.com/lodash-es@4.17.21/divide.js
Download https://unpkg.com/lodash-es@4.17.21/drop.js
Download https://unpkg.com/lodash-es@4.17.21/dropRight.js
Download https://unpkg.com/lodash-es@4.17.21/dropRightWhile.js
Download https://unpkg.com/lodash-es@4.17.21/dropWhile.js
Download https://unpkg.com/lodash-es@4.17.21/each.js
Download https://unpkg.com/lodash-es@4.17.21/eachRight.js
Download https://unpkg.com/lodash-es@4.17.21/endsWith.js
Download https://unpkg.com/lodash-es@4.17.21/entries.js
Download https://unpkg.com/lodash-es@4.17.21/entriesIn.js
Download https://unpkg.com/lodash-es@4.17.21/eq.js
Download https://unpkg.com/lodash-es@4.17.21/escape.js
Download https://unpkg.com/lodash-es@4.17.21/escapeRegExp.js
Download https://unpkg.com/lodash-es@4.17.21/every.js
Download https://unpkg.com/lodash-es@4.17.21/extend.js
Download https://unpkg.com/lodash-es@4.17.21/extendWith.js
Download https://unpkg.com/lodash-es@4.17.21/fill.js
Download https://unpkg.com/lodash-es@4.17.21/filter.js
Download https://unpkg.com/lodash-es@4.17.21/find.js
Download https://unpkg.com/lodash-es@4.17.21/findIndex.js
Download https://unpkg.com/lodash-es@4.17.21/findKey.js
Download https://unpkg.com/lodash-es@4.17.21/findLast.js
Download https://unpkg.com/lodash-es@4.17.21/findLastIndex.js
Download https://unpkg.com/lodash-es@4.17.21/findLastKey.js
Download https://unpkg.com/lodash-es@4.17.21/first.js
Download https://unpkg.com/lodash-es@4.17.21/flatMap.js
Download https://unpkg.com/lodash-es@4.17.21/flatMapDeep.js
Download https://unpkg.com/lodash-es@4.17.21/flatMapDepth.js
Download https://unpkg.com/lodash-es@4.17.21/flatten.js
Download https://unpkg.com/lodash-es@4.17.21/flattenDeep.js
Download https://unpkg.com/lodash-es@4.17.21/flattenDepth.js
Download https://unpkg.com/lodash-es@4.17.21/flip.js
Download https://unpkg.com/lodash-es@4.17.21/floor.js
Download https://unpkg.com/lodash-es@4.17.21/flow.js
Download https://unpkg.com/lodash-es@4.17.21/flowRight.js
Download https://unpkg.com/lodash-es@4.17.21/forEach.js
Download https://unpkg.com/lodash-es@4.17.21/forEachRight.js
Download https://unpkg.com/lodash-es@4.17.21/forIn.js
Download https://unpkg.com/lodash-es@4.17.21/forInRight.js
Download https://unpkg.com/lodash-es@4.17.21/forOwn.js
Download https://unpkg.com/lodash-es@4.17.21/forOwnRight.js
Download https://unpkg.com/lodash-es@4.17.21/fromPairs.js
Download https://unpkg.com/lodash-es@4.17.21/functions.js
Download https://unpkg.com/lodash-es@4.17.21/functionsIn.js
Download https://unpkg.com/lodash-es@4.17.21/get.js
Download https://unpkg.com/lodash-es@4.17.21/groupBy.js
Download https://unpkg.com/lodash-es@4.17.21/gt.js
Download https://unpkg.com/lodash-es@4.17.21/gte.js
Download https://unpkg.com/lodash-es@4.17.21/has.js
Download https://unpkg.com/lodash-es@4.17.21/hasIn.js
Download https://unpkg.com/lodash-es@4.17.21/head.js
Download https://unpkg.com/lodash-es@4.17.21/identity.js
Download https://unpkg.com/lodash-es@4.17.21/inRange.js
Download https://unpkg.com/lodash-es@4.17.21/includes.js
Download https://unpkg.com/lodash-es@4.17.21/indexOf.js
Download https://unpkg.com/lodash-es@4.17.21/initial.js
Download https://unpkg.com/lodash-es@4.17.21/intersection.js
Download https://unpkg.com/lodash-es@4.17.21/intersectionBy.js
Download https://unpkg.com/lodash-es@4.17.21/intersectionWith.js
Download https://unpkg.com/lodash-es@4.17.21/invert.js
Download https://unpkg.com/lodash-es@4.17.21/invertBy.js
Download https://unpkg.com/lodash-es@4.17.21/invoke.js
Download https://unpkg.com/lodash-es@4.17.21/invokeMap.js
Download https://unpkg.com/lodash-es@4.17.21/isArguments.js
Download https://unpkg.com/lodash-es@4.17.21/isArray.js
Download https://unpkg.com/lodash-es@4.17.21/isArrayBuffer.js
Download https://unpkg.com/lodash-es@4.17.21/isArrayLike.js
Download https://unpkg.com/lodash-es@4.17.21/isArrayLikeObject.js
Download https://unpkg.com/lodash-es@4.17.21/isBoolean.js
Download https://unpkg.com/lodash-es@4.17.21/isBuffer.js
Download https://unpkg.com/lodash-es@4.17.21/isDate.js
Download https://unpkg.com/lodash-es@4.17.21/isElement.js
Download https://unpkg.com/lodash-es@4.17.21/isEmpty.js
Download https://unpkg.com/lodash-es@4.17.21/isEqual.js
Download https://unpkg.com/lodash-es@4.17.21/isEqualWith.js
Download https://unpkg.com/lodash-es@4.17.21/isError.js
Download https://unpkg.com/lodash-es@4.17.21/isFinite.js
Download https://unpkg.com/lodash-es@4.17.21/isFunction.js
Download https://unpkg.com/lodash-es@4.17.21/isInteger.js
Download https://unpkg.com/lodash-es@4.17.21/isLength.js
Download https://unpkg.com/lodash-es@4.17.21/isMap.js
Download https://unpkg.com/lodash-es@4.17.21/isMatch.js
Download https://unpkg.com/lodash-es@4.17.21/isMatchWith.js
Download https://unpkg.com/lodash-es@4.17.21/isNaN.js
Download https://unpkg.com/lodash-es@4.17.21/isNative.js
Download https://unpkg.com/lodash-es@4.17.21/isNil.js
Download https://unpkg.com/lodash-es@4.17.21/isNull.js
Download https://unpkg.com/lodash-es@4.17.21/isNumber.js
Download https://unpkg.com/lodash-es@4.17.21/isObject.js
Download https://unpkg.com/lodash-es@4.17.21/isObjectLike.js
Download https://unpkg.com/lodash-es@4.17.21/isPlainObject.js
Download https://unpkg.com/lodash-es@4.17.21/isRegExp.js
Download https://unpkg.com/lodash-es@4.17.21/isSafeInteger.js
Download https://unpkg.com/lodash-es@4.17.21/isSet.js
Download https://unpkg.com/lodash-es@4.17.21/isString.js
Download https://unpkg.com/lodash-es@4.17.21/isSymbol.js
Download https://unpkg.com/lodash-es@4.17.21/isTypedArray.js
Download https://unpkg.com/lodash-es@4.17.21/isUndefined.js
Download https://unpkg.com/lodash-es@4.17.21/isWeakMap.js
Download https://unpkg.com/lodash-es@4.17.21/isWeakSet.js
Download https://unpkg.com/lodash-es@4.17.21/iteratee.js
Download https://unpkg.com/lodash-es@4.17.21/join.js
Download https://unpkg.com/lodash-es@4.17.21/kebabCase.js
Download https://unpkg.com/lodash-es@4.17.21/keyBy.js
Download https://unpkg.com/lodash-es@4.17.21/keys.js
Download https://unpkg.com/lodash-es@4.17.21/keysIn.js
Download https://unpkg.com/lodash-es@4.17.21/last.js
Download https://unpkg.com/lodash-es@4.17.21/lastIndexOf.js
Download https://unpkg.com/lodash-es@4.17.21/lodash.default.js
Download https://unpkg.com/lodash-es@4.17.21/lowerCase.js
Download https://unpkg.com/lodash-es@4.17.21/lowerFirst.js
Download https://unpkg.com/lodash-es@4.17.21/lt.js
Download https://unpkg.com/lodash-es@4.17.21/lte.js
Download https://unpkg.com/lodash-es@4.17.21/map.js
Download https://unpkg.com/lodash-es@4.17.21/mapKeys.js
Download https://unpkg.com/lodash-es@4.17.21/mapValues.js
Download https://unpkg.com/lodash-es@4.17.21/matches.js
Download https://unpkg.com/lodash-es@4.17.21/matchesProperty.js
Download https://unpkg.com/lodash-es@4.17.21/max.js
Download https://unpkg.com/lodash-es@4.17.21/maxBy.js
Download https://unpkg.com/lodash-es@4.17.21/mean.js
Download https://unpkg.com/lodash-es@4.17.21/meanBy.js
Download https://unpkg.com/lodash-es@4.17.21/memoize.js
Download https://unpkg.com/lodash-es@4.17.21/merge.js
Download https://unpkg.com/lodash-es@4.17.21/mergeWith.js
Download https://unpkg.com/lodash-es@4.17.21/method.js
Download https://unpkg.com/lodash-es@4.17.21/methodOf.js
Download https://unpkg.com/lodash-es@4.17.21/min.js
Download https://unpkg.com/lodash-es@4.17.21/minBy.js
Download https://unpkg.com/lodash-es@4.17.21/mixin.js
Download https://unpkg.com/lodash-es@4.17.21/multiply.js
Download https://unpkg.com/lodash-es@4.17.21/negate.js
Download https://unpkg.com/lodash-es@4.17.21/next.js
Download https://unpkg.com/lodash-es@4.17.21/noop.js
Download https://unpkg.com/lodash-es@4.17.21/now.js
Download https://unpkg.com/lodash-es@4.17.21/nth.js
Download https://unpkg.com/lodash-es@4.17.21/nthArg.js
Download https://unpkg.com/lodash-es@4.17.21/omit.js
Download https://unpkg.com/lodash-es@4.17.21/omitBy.js
Download https://unpkg.com/lodash-es@4.17.21/once.js
Download https://unpkg.com/lodash-es@4.17.21/orderBy.js
Download https://unpkg.com/lodash-es@4.17.21/over.js
Download https://unpkg.com/lodash-es@4.17.21/overArgs.js
Download https://unpkg.com/lodash-es@4.17.21/overEvery.js
Download https://unpkg.com/lodash-es@4.17.21/overSome.js
Download https://unpkg.com/lodash-es@4.17.21/pad.js
Download https://unpkg.com/lodash-es@4.17.21/padEnd.js
Download https://unpkg.com/lodash-es@4.17.21/padStart.js
Download https://unpkg.com/lodash-es@4.17.21/parseInt.js
Download https://unpkg.com/lodash-es@4.17.21/partial.js
Download https://unpkg.com/lodash-es@4.17.21/partialRight.js
Download https://unpkg.com/lodash-es@4.17.21/partition.js
Download https://unpkg.com/lodash-es@4.17.21/pick.js
Download https://unpkg.com/lodash-es@4.17.21/pickBy.js
Download https://unpkg.com/lodash-es@4.17.21/plant.js
Download https://unpkg.com/lodash-es@4.17.21/property.js
Download https://unpkg.com/lodash-es@4.17.21/propertyOf.js
Download https://unpkg.com/lodash-es@4.17.21/pull.js
Download https://unpkg.com/lodash-es@4.17.21/pullAll.js
Download https://unpkg.com/lodash-es@4.17.21/pullAllBy.js
Download https://unpkg.com/lodash-es@4.17.21/pullAllWith.js
Download https://unpkg.com/lodash-es@4.17.21/pullAt.js
Download https://unpkg.com/lodash-es@4.17.21/random.js
Download https://unpkg.com/lodash-es@4.17.21/range.js
Download https://unpkg.com/lodash-es@4.17.21/rangeRight.js
Download https://unpkg.com/lodash-es@4.17.21/rearg.js
Download https://unpkg.com/lodash-es@4.17.21/reduce.js
Download https://unpkg.com/lodash-es@4.17.21/reduceRight.js
Download https://unpkg.com/lodash-es@4.17.21/reject.js
Download https://unpkg.com/lodash-es@4.17.21/remove.js
Download https://unpkg.com/lodash-es@4.17.21/repeat.js
Download https://unpkg.com/lodash-es@4.17.21/replace.js
Download https://unpkg.com/lodash-es@4.17.21/rest.js
Download https://unpkg.com/lodash-es@4.17.21/result.js
Download https://unpkg.com/lodash-es@4.17.21/reverse.js
Download https://unpkg.com/lodash-es@4.17.21/round.js
Download https://unpkg.com/lodash-es@4.17.21/sample.js
Download https://unpkg.com/lodash-es@4.17.21/sampleSize.js
Download https://unpkg.com/lodash-es@4.17.21/set.js
Download https://unpkg.com/lodash-es@4.17.21/setWith.js
Download https://unpkg.com/lodash-es@4.17.21/shuffle.js
Download https://unpkg.com/lodash-es@4.17.21/size.js
Download https://unpkg.com/lodash-es@4.17.21/slice.js
Download https://unpkg.com/lodash-es@4.17.21/snakeCase.js
Download https://unpkg.com/lodash-es@4.17.21/some.js
Download https://unpkg.com/lodash-es@4.17.21/sortBy.js
Download https://unpkg.com/lodash-es@4.17.21/sortedIndex.js
Download https://unpkg.com/lodash-es@4.17.21/sortedIndexBy.js
Download https://unpkg.com/lodash-es@4.17.21/sortedIndexOf.js
Download https://unpkg.com/lodash-es@4.17.21/sortedLastIndex.js
Download https://unpkg.com/lodash-es@4.17.21/sortedLastIndexBy.js
Download https://unpkg.com/lodash-es@4.17.21/sortedLastIndexOf.js
Download https://unpkg.com/lodash-es@4.17.21/sortedUniq.js
Download https://unpkg.com/lodash-es@4.17.21/sortedUniqBy.js
Download https://unpkg.com/lodash-es@4.17.21/split.js
Download https://unpkg.com/lodash-es@4.17.21/spread.js
Download https://unpkg.com/lodash-es@4.17.21/startCase.js
Download https://unpkg.com/lodash-es@4.17.21/startsWith.js
Download https://unpkg.com/lodash-es@4.17.21/stubArray.js
Download https://unpkg.com/lodash-es@4.17.21/stubFalse.js
Download https://unpkg.com/lodash-es@4.17.21/stubObject.js
Download https://unpkg.com/lodash-es@4.17.21/stubString.js
Download https://unpkg.com/lodash-es@4.17.21/stubTrue.js
Download https://unpkg.com/lodash-es@4.17.21/subtract.js
Download https://unpkg.com/lodash-es@4.17.21/sum.js
Download https://unpkg.com/lodash-es@4.17.21/sumBy.js
Download https://unpkg.com/lodash-es@4.17.21/tail.js
Download https://unpkg.com/lodash-es@4.17.21/take.js
Download https://unpkg.com/lodash-es@4.17.21/takeRight.js
Download https://unpkg.com/lodash-es@4.17.21/takeRightWhile.js
Download https://unpkg.com/lodash-es@4.17.21/takeWhile.js
Download https://unpkg.com/lodash-es@4.17.21/tap.js
Download https://unpkg.com/lodash-es@4.17.21/template.js
Download https://unpkg.com/lodash-es@4.17.21/templateSettings.js
Download https://unpkg.com/lodash-es@4.17.21/throttle.js
Download https://unpkg.com/lodash-es@4.17.21/thru.js
Download https://unpkg.com/lodash-es@4.17.21/times.js
Download https://unpkg.com/lodash-es@4.17.21/toArray.js
Download https://unpkg.com/lodash-es@4.17.21/toFinite.js
Download https://unpkg.com/lodash-es@4.17.21/toInteger.js
Download https://unpkg.com/lodash-es@4.17.21/toIterator.js
Download https://unpkg.com/lodash-es@4.17.21/toJSON.js
Download https://unpkg.com/lodash-es@4.17.21/toLength.js
Download https://unpkg.com/lodash-es@4.17.21/toLower.js
Download https://unpkg.com/lodash-es@4.17.21/toNumber.js
Download https://unpkg.com/lodash-es@4.17.21/toPairs.js
Download https://unpkg.com/lodash-es@4.17.21/toPairsIn.js
Download https://unpkg.com/lodash-es@4.17.21/toPath.js
Download https://unpkg.com/lodash-es@4.17.21/toPlainObject.js
Download https://unpkg.com/lodash-es@4.17.21/toSafeInteger.js
Download https://unpkg.com/lodash-es@4.17.21/toString.js
Download https://unpkg.com/lodash-es@4.17.21/toUpper.js
Download https://unpkg.com/lodash-es@4.17.21/transform.js
Download https://unpkg.com/lodash-es@4.17.21/trim.js
Download https://unpkg.com/lodash-es@4.17.21/trimEnd.js
Download https://unpkg.com/lodash-es@4.17.21/trimStart.js
Download https://unpkg.com/lodash-es@4.17.21/truncate.js
Download https://unpkg.com/lodash-es@4.17.21/unary.js
Download https://unpkg.com/lodash-es@4.17.21/unescape.js
Download https://unpkg.com/lodash-es@4.17.21/union.js
Download https://unpkg.com/lodash-es@4.17.21/unionBy.js
Download https://unpkg.com/lodash-es@4.17.21/unionWith.js
Download https://unpkg.com/lodash-es@4.17.21/uniq.js
Download https://unpkg.com/lodash-es@4.17.21/uniqBy.js
Download https://unpkg.com/lodash-es@4.17.21/uniqWith.js
Download https://unpkg.com/lodash-es@4.17.21/uniqueId.js
Download https://unpkg.com/lodash-es@4.17.21/unset.js
Download https://unpkg.com/lodash-es@4.17.21/unzip.js
Download https://unpkg.com/lodash-es@4.17.21/unzipWith.js
Download https://unpkg.com/lodash-es@4.17.21/update.js
Download https://unpkg.com/lodash-es@4.17.21/updateWith.js
Download https://unpkg.com/lodash-es@4.17.21/upperCase.js
Download https://unpkg.com/lodash-es@4.17.21/upperFirst.js
Download https://unpkg.com/lodash-es@4.17.21/value.js
Download https://unpkg.com/lodash-es@4.17.21/valueOf.js
Download https://unpkg.com/lodash-es@4.17.21/values.js
Download https://unpkg.com/lodash-es@4.17.21/valuesIn.js
Download https://unpkg.com/lodash-es@4.17.21/without.js
Download https://unpkg.com/lodash-es@4.17.21/words.js
Download https://unpkg.com/lodash-es@4.17.21/wrap.js
Download https://unpkg.com/lodash-es@4.17.21/wrapperAt.js
Download https://unpkg.com/lodash-es@4.17.21/wrapperChain.js
Download https://unpkg.com/lodash-es@4.17.21/wrapperLodash.js
Download https://unpkg.com/lodash-es@4.17.21/wrapperReverse.js
Download https://unpkg.com/lodash-es@4.17.21/wrapperValue.js
Download https://unpkg.com/lodash-es@4.17.21/xor.js
Download https://unpkg.com/lodash-es@4.17.21/xorBy.js
Download https://unpkg.com/lodash-es@4.17.21/xorWith.js
Download https://unpkg.com/lodash-es@4.17.21/zip.js
Download https://unpkg.com/lodash-es@4.17.21/zipObject.js
Download https://unpkg.com/lodash-es@4.17.21/zipObjectDeep.js
Download https://unpkg.com/lodash-es@4.17.21/zipWith.js
Download https://unpkg.com/lodash-es@4.17.21/_createMathOperation.js
Download https://unpkg.com/lodash-es@4.17.21/_createWrap.js
Download https://unpkg.com/lodash-es@4.17.21/_assignValue.js
Download https://unpkg.com/lodash-es@4.17.21/_copyObject.js
Download https://unpkg.com/lodash-es@4.17.21/_createAssigner.js
Download https://unpkg.com/lodash-es@4.17.21/_isPrototype.js
Download https://unpkg.com/lodash-es@4.17.21/_baseAt.js
Download https://unpkg.com/lodash-es@4.17.21/_flatRest.js
Download https://unpkg.com/lodash-es@4.17.21/_apply.js
Download https://unpkg.com/lodash-es@4.17.21/_baseRest.js
Download https://unpkg.com/lodash-es@4.17.21/_getHolder.js
Download https://unpkg.com/lodash-es@4.17.21/_replaceHolders.js
Download https://unpkg.com/lodash-es@4.17.21/_arrayEach.js
Download https://unpkg.com/lodash-es@4.17.21/_baseAssignValue.js
Download https://unpkg.com/lodash-es@4.17.21/_toKey.js
Download https://unpkg.com/lodash-es@4.17.21/_createCompounder.js
Download https://unpkg.com/lodash-es@4.17.21/_createRound.js
Download https://unpkg.com/lodash-es@4.17.21/_baseClamp.js
Download https://unpkg.com/lodash-es@4.17.21/_baseClone.js
Download https://unpkg.com/lodash-es@4.17.21/_LodashWrapper.js
Download https://unpkg.com/lodash-es@4.17.21/_arrayPush.js
Download https://unpkg.com/lodash-es@4.17.21/_baseFlatten.js
Download https://unpkg.com/lodash-es@4.17.21/_copyArray.js
Download https://unpkg.com/lodash-es@4.17.21/_arrayMap.js
Download https://unpkg.com/lodash-es@4.17.21/_baseIteratee.js
Download https://unpkg.com/lodash-es@4.17.21/_baseConforms.js
Download https://unpkg.com/lodash-es@4.17.21/_baseConformsTo.js
Download https://unpkg.com/lodash-es@4.17.21/_createAggregator.js
Download https://unpkg.com/lodash-es@4.17.21/_baseAssign.js
Download https://unpkg.com/lodash-es@4.17.21/_baseCreate.js
Download https://unpkg.com/lodash-es@4.17.21/_deburrLetter.js
Download https://unpkg.com/lodash-es@4.17.21/_isIterateeCall.js
Download https://unpkg.com/lodash-es@4.17.21/_customDefaultsMerge.js
Download https://unpkg.com/lodash-es@4.17.21/_baseDelay.js
Download https://unpkg.com/lodash-es@4.17.21/_baseDifference.js
Download https://unpkg.com/lodash-es@4.17.21/_baseSlice.js
Download https://unpkg.com/lodash-es@4.17.21/_baseWhile.js
Download https://unpkg.com/lodash-es@4.17.21/_baseToString.js
Download https://unpkg.com/lodash-es@4.17.21/_escapeHtmlChar.js
Download https://unpkg.com/lodash-es@4.17.21/_arrayEvery.js
Download https://unpkg.com/lodash-es@4.17.21/_baseEvery.js
Download https://unpkg.com/lodash-es@4.17.21/_baseFill.js
Download https://unpkg.com/lodash-es@4.17.21/_arrayFilter.js
Download https://unpkg.com/lodash-es@4.17.21/_baseFilter.js
Download https://unpkg.com/lodash-es@4.17.21/_createFind.js
Download https://unpkg.com/lodash-es@4.17.21/_baseFindIndex.js
Download https://unpkg.com/lodash-es@4.17.21/_baseFindKey.js
Download https://unpkg.com/lodash-es@4.17.21/_baseForOwn.js
Download https://unpkg.com/lodash-es@4.17.21/_baseForOwnRight.js
Download https://unpkg.com/lodash-es@4.17.21/_createFlow.js
Download https://unpkg.com/lodash-es@4.17.21/_baseEach.js
Download https://unpkg.com/lodash-es@4.17.21/_castFunction.js
Download https://unpkg.com/lodash-es@4.17.21/_arrayEachRight.js
Download https://unpkg.com/lodash-es@4.17.21/_baseEachRight.js
Download https://unpkg.com/lodash-es@4.17.21/_baseFor.js
Download https://unpkg.com/lodash-es@4.17.21/_baseForRight.js
Download https://unpkg.com/lodash-es@4.17.21/_baseFunctions.js
Download https://unpkg.com/lodash-es@4.17.21/_baseGt.js
Download https://unpkg.com/lodash-es@4.17.21/_createRelationalOperation.js
Download https://unpkg.com/lodash-es@4.17.21/_baseHas.js
Download https://unpkg.com/lodash-es@4.17.21/_hasPath.js
Download https://unpkg.com/lodash-es@4.17.21/_baseHasIn.js
Download https://unpkg.com/lodash-es@4.17.21/_baseInRange.js
Download https://unpkg.com/lodash-es@4.17.21/_baseIntersection.js
Download https://unpkg.com/lodash-es@4.17.21/_castArrayLikeObject.js
Download https://unpkg.com/lodash-es@4.17.21/_createInverter.js
Download https://unpkg.com/lodash-es@4.17.21/_baseIsMatch.js
Download https://unpkg.com/lodash-es@4.17.21/_getMatchData.js
Download https://unpkg.com/lodash-es@4.17.21/_baseInvoke.js
Download https://unpkg.com/lodash-es@4.17.21/_baseIsNative.js
Download https://unpkg.com/lodash-es@4.17.21/_isMaskable.js
Download https://unpkg.com/lodash-es@4.17.21/_baseIsDate.js
Download https://unpkg.com/lodash-es@4.17.21/_baseUnary.js
Download https://unpkg.com/lodash-es@4.17.21/_nodeUtil.js
Download https://unpkg.com/lodash-es@4.17.21/_baseGetTag.js
Download https://unpkg.com/lodash-es@4.17.21/_getPrototype.js
Download https://unpkg.com/lodash-es@4.17.21/_baseIsRegExp.js
Download https://unpkg.com/lodash-es@4.17.21/_baseIndexOf.js
Download https://unpkg.com/lodash-es@4.17.21/_baseIsEqual.js
Download https://unpkg.com/lodash-es@4.17.21/_baseIsSet.js
Download https://unpkg.com/lodash-es@4.17.21/_baseIsArguments.js
Download https://unpkg.com/lodash-es@4.17.21/_baseGet.js
Download https://unpkg.com/lodash-es@4.17.21/_baseIsTypedArray.js
Download https://unpkg.com/lodash-es@4.17.21/_getTag.js
Download https://unpkg.com/lodash-es@4.17.21/_root.js
Download https://unpkg.com/lodash-es@4.17.21/_arrayLikeKeys.js
Download https://unpkg.com/lodash-es@4.17.21/_baseKeys.js
Download https://unpkg.com/lodash-es@4.17.21/_baseKeysIn.js
Download https://unpkg.com/lodash-es@4.17.21/_baseIsNaN.js
Download https://unpkg.com/lodash-es@4.17.21/_strictLastIndexOf.js
Download https://unpkg.com/lodash-es@4.17.21/_LazyWrapper.js
Download https://unpkg.com/lodash-es@4.17.21/_Symbol.js
Download https://unpkg.com/lodash-es@4.17.21/_createHybrid.js
Download https://unpkg.com/lodash-es@4.17.21/_lazyClone.js
Download https://unpkg.com/lodash-es@4.17.21/_lazyReverse.js
Download https://unpkg.com/lodash-es@4.17.21/_lazyValue.js
Download https://unpkg.com/lodash-es@4.17.21/_realNames.js
Download https://unpkg.com/lodash-es@4.17.21/array.js
Download https://unpkg.com/lodash-es@4.17.21/collection.js
Download https://unpkg.com/lodash-es@4.17.21/date.js
Download https://unpkg.com/lodash-es@4.17.21/function.js
Download https://unpkg.com/lodash-es@4.17.21/lang.js
Download https://unpkg.com/lodash-es@4.17.21/math.js
Download https://unpkg.com/lodash-es@4.17.21/number.js
Download https://unpkg.com/lodash-es@4.17.21/object.js
Download https://unpkg.com/lodash-es@4.17.21/seq.js
Download https://unpkg.com/lodash-es@4.17.21/string.js
Download https://unpkg.com/lodash-es@4.17.21/util.js
Download https://unpkg.com/lodash-es@4.17.21/_createCaseFirst.js
Download https://unpkg.com/lodash-es@4.17.21/_baseLt.js
Download https://unpkg.com/lodash-es@4.17.21/_baseMap.js
Download https://unpkg.com/lodash-es@4.17.21/_baseMatches.js
Download https://unpkg.com/lodash-es@4.17.21/_baseMatchesProperty.js
Download https://unpkg.com/lodash-es@4.17.21/_baseIsMap.js
Download https://unpkg.com/lodash-es@4.17.21/_baseIsArrayBuffer.js
Download https://unpkg.com/lodash-es@4.17.21/_baseExtremum.js
Download https://unpkg.com/lodash-es@4.17.21/_baseMean.js
Download https://unpkg.com/lodash-es@4.17.21/_MapCache.js
Download https://unpkg.com/lodash-es@4.17.21/_baseMerge.js
Download https://unpkg.com/lodash-es@4.17.21/_baseNth.js
Download https://unpkg.com/lodash-es@4.17.21/_baseUnset.js
Download https://unpkg.com/lodash-es@4.17.21/_castPath.js
Download https://unpkg.com/lodash-es@4.17.21/_customOmitClone.js
Download https://unpkg.com/lodash-es@4.17.21/_getAllKeysIn.js
Download https://unpkg.com/lodash-es@4.17.21/_baseOrderBy.js
Download https://unpkg.com/lodash-es@4.17.21/_createOver.js
Download https://unpkg.com/lodash-es@4.17.21/_castRest.js
Download https://unpkg.com/lodash-es@4.17.21/_arraySome.js
Download https://unpkg.com/lodash-es@4.17.21/_createPadding.js
Download https://unpkg.com/lodash-es@4.17.21/_stringSize.js
Download https://unpkg.com/lodash-es@4.17.21/_basePick.js
Download https://unpkg.com/lodash-es@4.17.21/_basePickBy.js
Download https://unpkg.com/lodash-es@4.17.21/_baseLodash.js
Download https://unpkg.com/lodash-es@4.17.21/_wrapperClone.js
Download https://unpkg.com/lodash-es@4.17.21/_baseProperty.js
Download https://unpkg.com/lodash-es@4.17.21/_basePropertyDeep.js
Download https://unpkg.com/lodash-es@4.17.21/_isKey.js
Download https://unpkg.com/lodash-es@4.17.21/_basePullAll.js
Download https://unpkg.com/lodash-es@4.17.21/_basePullAt.js
Download https://unpkg.com/lodash-es@4.17.21/_compareAscending.js
Download https://unpkg.com/lodash-es@4.17.21/_isIndex.js
Download https://unpkg.com/lodash-es@4.17.21/_baseRandom.js
Download https://unpkg.com/lodash-es@4.17.21/_createRange.js
Download https://unpkg.com/lodash-es@4.17.21/_arrayReduce.js
Download https://unpkg.com/lodash-es@4.17.21/_baseReduce.js
Download https://unpkg.com/lodash-es@4.17.21/_arrayReduceRight.js
Download https://unpkg.com/lodash-es@4.17.21/_baseRepeat.js
Download https://unpkg.com/lodash-es@4.17.21/_arraySample.js
Download https://unpkg.com/lodash-es@4.17.21/_baseSample.js
Download https://unpkg.com/lodash-es@4.17.21/_arraySampleSize.js
Download https://unpkg.com/lodash-es@4.17.21/_baseSampleSize.js
Download https://unpkg.com/lodash-es@4.17.21/_baseSet.js
Download https://unpkg.com/lodash-es@4.17.21/_arrayShuffle.js
Download https://unpkg.com/lodash-es@4.17.21/_baseShuffle.js
Download https://unpkg.com/lodash-es@4.17.21/_baseSome.js
Download https://unpkg.com/lodash-es@4.17.21/_baseSortedIndexBy.js
Download https://unpkg.com/lodash-es@4.17.21/_baseSortedIndex.js
Download https://unpkg.com/lodash-es@4.17.21/_baseSortedUniq.js
Download https://unpkg.com/lodash-es@4.17.21/_castSlice.js
Download https://unpkg.com/lodash-es@4.17.21/_hasUnicode.js
Download https://unpkg.com/lodash-es@4.17.21/_stringToArray.js
Download https://unpkg.com/lodash-es@4.17.21/_baseSum.js
Download https://unpkg.com/lodash-es@4.17.21/_baseValues.js
Download https://unpkg.com/lodash-es@4.17.21/_customDefaultsAssignIn.js
Download https://unpkg.com/lodash-es@4.17.21/_escapeStringChar.js
Download https://unpkg.com/lodash-es@4.17.21/_reInterpolate.js
Download https://unpkg.com/lodash-es@4.17.21/_reEscape.js
Download https://unpkg.com/lodash-es@4.17.21/_reEvaluate.js
Download https://unpkg.com/lodash-es@4.17.21/_baseTimes.js
Download https://unpkg.com/lodash-es@4.17.21/_iteratorToArray.js
Download https://unpkg.com/lodash-es@4.17.21/_mapToArray.js
Download https://unpkg.com/lodash-es@4.17.21/_setToArray.js
Download https://unpkg.com/lodash-es@4.17.21/_baseTrim.js
Download https://unpkg.com/lodash-es@4.17.21/_createToPairs.js
Download https://unpkg.com/lodash-es@4.17.21/_stringToPath.js
Download https://unpkg.com/lodash-es@4.17.21/_charsEndIndex.js
Download https://unpkg.com/lodash-es@4.17.21/_charsStartIndex.js
Download https://unpkg.com/lodash-es@4.17.21/_trimmedEndIndex.js
Download https://unpkg.com/lodash-es@4.17.21/_unescapeHtmlChar.js
Download https://unpkg.com/lodash-es@4.17.21/_baseUniq.js
Download https://unpkg.com/lodash-es@4.17.21/_baseUpdate.js
Download https://unpkg.com/lodash-es@4.17.21/_asciiWords.js
Download https://unpkg.com/lodash-es@4.17.21/_hasUnicodeWord.js
Download https://unpkg.com/lodash-es@4.17.21/_unicodeWords.js
Download https://unpkg.com/lodash-es@4.17.21/_baseWrapperValue.js
Download https://unpkg.com/lodash-es@4.17.21/_baseXor.js
Download https://unpkg.com/lodash-es@4.17.21/_baseZipObject.js
Download https://unpkg.com/lodash-es@4.17.21/_baseToNumber.js
Download https://unpkg.com/lodash-es@4.17.21/_baseSetData.js
Download https://unpkg.com/lodash-es@4.17.21/_createBind.js
Download https://unpkg.com/lodash-es@4.17.21/_createCurry.js
Download https://unpkg.com/lodash-es@4.17.21/_createPartial.js
Download https://unpkg.com/lodash-es@4.17.21/_getData.js
Download https://unpkg.com/lodash-es@4.17.21/_mergeData.js
Download https://unpkg.com/lodash-es@4.17.21/_setData.js
Download https://unpkg.com/lodash-es@4.17.21/_setWrapToString.js
Download https://unpkg.com/lodash-es@4.17.21/_overRest.js
Download https://unpkg.com/lodash-es@4.17.21/_setToString.js
Download https://unpkg.com/lodash-es@4.17.21/_defineProperty.js
Download https://unpkg.com/lodash-es@4.17.21/_Stack.js
Download https://unpkg.com/lodash-es@4.17.21/_baseAssignIn.js
Download https://unpkg.com/lodash-es@4.17.21/_cloneBuffer.js
Download https://unpkg.com/lodash-es@4.17.21/_copySymbols.js
Download https://unpkg.com/lodash-es@4.17.21/_copySymbolsIn.js
Download https://unpkg.com/lodash-es@4.17.21/_getAllKeys.js
Download https://unpkg.com/lodash-es@4.17.21/_initCloneArray.js
Download https://unpkg.com/lodash-es@4.17.21/_initCloneByTag.js
Download https://unpkg.com/lodash-es@4.17.21/_initCloneObject.js
Download https://unpkg.com/lodash-es@4.17.21/_isFlattenable.js
Download https://unpkg.com/lodash-es@4.17.21/_arrayAggregator.js
Download https://unpkg.com/lodash-es@4.17.21/_baseAggregator.js
Download https://unpkg.com/lodash-es@4.17.21/_basePropertyOf.js
Download https://unpkg.com/lodash-es@4.17.21/_SetCache.js
Download https://unpkg.com/lodash-es@4.17.21/_arrayIncludes.js
Download https://unpkg.com/lodash-es@4.17.21/_arrayIncludesWith.js
Download https://unpkg.com/lodash-es@4.17.21/_cacheHas.js
Download https://unpkg.com/lodash-es@4.17.21/_getFuncName.js
Download https://unpkg.com/lodash-es@4.17.21/_isLaziable.js
Download https://unpkg.com/lodash-es@4.17.21/_createBaseEach.js
Download https://unpkg.com/lodash-es@4.17.21/_createBaseFor.js
Download https://unpkg.com/lodash-es@4.17.21/_baseInverter.js
Download https://unpkg.com/lodash-es@4.17.21/_isStrictComparable.js
Download https://unpkg.com/lodash-es@4.17.21/_parent.js
Download https://unpkg.com/lodash-es@4.17.21/_isMasked.js
Download https://unpkg.com/lodash-es@4.17.21/_toSource.js
Download https://unpkg.com/lodash-es@4.17.21/_coreJsData.js
Download https://unpkg.com/lodash-es@4.17.21/_freeGlobal.js
Download https://unpkg.com/lodash-es@4.17.21/_getRawTag.js
Download https://unpkg.com/lodash-es@4.17.21/_objectToString.js
Download https://unpkg.com/lodash-es@4.17.21/_overArg.js
Download https://unpkg.com/lodash-es@4.17.21/_strictIndexOf.js
Download https://unpkg.com/lodash-es@4.17.21/_baseIsEqualDeep.js
Download https://unpkg.com/lodash-es@4.17.21/_DataView.js
Download https://unpkg.com/lodash-es@4.17.21/_Map.js
Download https://unpkg.com/lodash-es@4.17.21/_Promise.js
Download https://unpkg.com/lodash-es@4.17.21/_Set.js
Download https://unpkg.com/lodash-es@4.17.21/_WeakMap.js
Download https://unpkg.com/lodash-es@4.17.21/_nativeKeys.js
Download https://unpkg.com/lodash-es@4.17.21/_nativeKeysIn.js
Download https://unpkg.com/lodash-es@4.17.21/_composeArgs.js
Download https://unpkg.com/lodash-es@4.17.21/_composeArgsRight.js
Download https://unpkg.com/lodash-es@4.17.21/_countHolders.js
Download https://unpkg.com/lodash-es@4.17.21/_createCtor.js
Download https://unpkg.com/lodash-es@4.17.21/_createRecurry.js
Download https://unpkg.com/lodash-es@4.17.21/_reorder.js
Download https://unpkg.com/lodash-es@4.17.21/_getView.js
Download https://unpkg.com/lodash-es@4.17.21/array.default.js
Download https://unpkg.com/lodash-es@4.17.21/collection.default.js
Download https://unpkg.com/lodash-es@4.17.21/date.default.js
Download https://unpkg.com/lodash-es@4.17.21/lang.default.js
Download https://unpkg.com/lodash-es@4.17.21/math.default.js
Download https://unpkg.com/lodash-es@4.17.21/number.default.js
Download https://unpkg.com/lodash-es@4.17.21/object.default.js
Download https://unpkg.com/lodash-es@4.17.21/seq.default.js
Download https://unpkg.com/lodash-es@4.17.21/string.default.js
Download https://unpkg.com/lodash-es@4.17.21/util.default.js
Download https://unpkg.com/lodash-es@4.17.21/_matchesStrictComparable.js
Download https://unpkg.com/lodash-es@4.17.21/_mapCacheClear.js
Download https://unpkg.com/lodash-es@4.17.21/_mapCacheDelete.js
Download https://unpkg.com/lodash-es@4.17.21/_mapCacheGet.js
Download https://unpkg.com/lodash-es@4.17.21/_mapCacheHas.js
Download https://unpkg.com/lodash-es@4.17.21/_mapCacheSet.js
Download https://unpkg.com/lodash-es@4.17.21/_assignMergeValue.js
Download https://unpkg.com/lodash-es@4.17.21/_baseMergeDeep.js
Download https://unpkg.com/lodash-es@4.17.21/_safeGet.js
Download https://unpkg.com/lodash-es@4.17.21/_baseGetAllKeys.js
Download https://unpkg.com/lodash-es@4.17.21/_getSymbolsIn.js
Download https://unpkg.com/lodash-es@4.17.21/_baseSortBy.js
Download https://unpkg.com/lodash-es@4.17.21/_compareMultiple.js
Download https://unpkg.com/lodash-es@4.17.21/_asciiSize.js
Download https://unpkg.com/lodash-es@4.17.21/_unicodeSize.js
Download https://unpkg.com/lodash-es@4.17.21/_baseIndexOfWith.js
Download https://unpkg.com/lodash-es@4.17.21/_baseRange.js
Download https://unpkg.com/lodash-es@4.17.21/function.default.js
Download https://unpkg.com/lodash-es@4.17.21/_shuffleSelf.js
Download https://unpkg.com/lodash-es@4.17.21/_asciiToArray.js
Download https://unpkg.com/lodash-es@4.17.21/_unicodeToArray.js
Download https://unpkg.com/lodash-es@4.17.21/_baseToPairs.js
Download https://unpkg.com/lodash-es@4.17.21/_setToPairs.js
Download https://unpkg.com/lodash-es@4.17.21/_memoizeCapped.js
Download https://unpkg.com/lodash-es@4.17.21/_getSymbols.js
Download https://unpkg.com/lodash-es@4.17.21/_baseSetToString.js
Download https://unpkg.com/lodash-es@4.17.21/_shortOut.js
Download https://unpkg.com/lodash-es@4.17.21/_ListCache.js
Download https://unpkg.com/lodash-es@4.17.21/_stackClear.js
Download https://unpkg.com/lodash-es@4.17.21/_stackDelete.js
Download https://unpkg.com/lodash-es@4.17.21/_stackGet.js
Download https://unpkg.com/lodash-es@4.17.21/_stackHas.js
Download https://unpkg.com/lodash-es@4.17.21/_stackSet.js
Download https://unpkg.com/lodash-es@4.17.21/_metaMap.js
Download https://unpkg.com/lodash-es@4.17.21/_getNative.js
Download https://unpkg.com/lodash-es@4.17.21/_getWrapDetails.js
Download https://unpkg.com/lodash-es@4.17.21/_insertWrapDetails.js
Download https://unpkg.com/lodash-es@4.17.21/_updateWrapDetails.js
Download https://unpkg.com/lodash-es@4.17.21/_equalArrays.js
Download https://unpkg.com/lodash-es@4.17.21/_equalByTag.js
Download https://unpkg.com/lodash-es@4.17.21/_equalObjects.js
Download https://unpkg.com/lodash-es@4.17.21/_createSet.js
Download https://unpkg.com/lodash-es@4.17.21/_setCacheAdd.js
Download https://unpkg.com/lodash-es@4.17.21/_setCacheHas.js
Download https://unpkg.com/lodash-es@4.17.21/_cloneArrayBuffer.js
Download https://unpkg.com/lodash-es@4.17.21/_cloneDataView.js
Download https://unpkg.com/lodash-es@4.17.21/_cloneRegExp.js
Download https://unpkg.com/lodash-es@4.17.21/_cloneSymbol.js
Download https://unpkg.com/lodash-es@4.17.21/_cloneTypedArray.js
Download https://unpkg.com/lodash-es@4.17.21/_getMapData.js
Download https://unpkg.com/lodash-es@4.17.21/_Hash.js
Download https://unpkg.com/lodash-es@4.17.21/_getValue.js
Download https://unpkg.com/lodash-es@4.17.21/_listCacheClear.js
Download https://unpkg.com/lodash-es@4.17.21/_listCacheDelete.js
Download https://unpkg.com/lodash-es@4.17.21/_listCacheGet.js
Download https://unpkg.com/lodash-es@4.17.21/_listCacheHas.js
Download https://unpkg.com/lodash-es@4.17.21/_listCacheSet.js
Download https://unpkg.com/lodash-es@4.17.21/_Uint8Array.js
Download https://unpkg.com/lodash-es@4.17.21/_hashClear.js
Download https://unpkg.com/lodash-es@4.17.21/_hashDelete.js
Download https://unpkg.com/lodash-es@4.17.21/_hashGet.js
Download https://unpkg.com/lodash-es@4.17.21/_hashHas.js
Download https://unpkg.com/lodash-es@4.17.21/_hashSet.js
Download https://unpkg.com/lodash-es@4.17.21/_isKeyable.js
Download https://unpkg.com/lodash-es@4.17.21/_assocIndexOf.js
Download https://unpkg.com/lodash-es@4.17.21/_nativeCreate.js
In contrast, with this demo.mjs
module:
import isNull from "https://unpkg.com/lodash-es@4.17.21/isNull.js";
When run with Deno (v1.22.2) using the command deno run demo.mjs
, the terminal output shows how much less code loads:
Download https://unpkg.com/lodash-es@4.17.21/isNull.js
With this example.html
file:
<!DOCTYPE html>
<html>
<head>
<script type="module">
import { isNull } from "https://unpkg.com/lodash-es@4.17.21";
</script>
</head>
</html>
When opened in Google Chrome (v102.0.5005.61), you can use the network inspector to see the loading waterfall step caused by the main index module, along with all the excess loading:
Because Lodash is quite a large and popular library, lots of people have noticed that significant bloat can be removed from their apps by refactoring to deep imports and many companies have spent time and money correcting their mistake. Smart people have labored to create ESLint rules that detect inefficient imports from Lodash, and build tool plugins that attempt to rewrite them each build. This colossal waste of time, intellect, and electricity could have been avoided if Lodash had of never provided a main index module in the first place; by only using deep imports every single user would have effortlessly fallen into the “pit of success”.
Unfortunately, most people haven’t realized yet that the problem isn’t limited to Lodash; their project module graphs are bloated by thousands of imports from main index modules from all sorts of dependencies. Barely anyone questions imports like this:
import { useState } from "react";
This needs to change.
Complex build tools like “tree-shaking” bundlers can be used to attempt to eliminate unused exports within index modules at build time, but such work slows builds and it’s hard to detect where and why it frequently fails. Package authors and users need to learn “tree-shaking” nuances such as the non-standard package.json
field sideEffects
. Avoid the hassle by following the golden rule and only deep import what is needed; no build tool can achieve a better result than this perfection. “tree shaking” is pointless when you have an optimal module graph with no dead code to eliminate.
The library module anti-pattern
A “library module” exports multiple things that originate within itself, in contrast to an index module that re-exports things from other modules. These anti-patterns have similar consequences, although a library module doesn’t bloat the disk with an extra file and doesn’t cause an extra JavaScript runtime loading waterfall step.
Here is an example:
import { assert } from "https://deno.land/std@0.148.0/testing/asserts.ts";
Because assert
originates from a library module, there is no way to import it without the JavaScript runtime needlessly loading everything else in the asserts library. You can see this particular issue explained in agonizing detail for Deno std
maintainers here.
It’s much easier to incrementally add tests with enforced 100% code coverage for optimal single export modules. In a test module, import the thing to test and keep adding tests for it until achieving 100% code coverage. In contrast, importing a thing to test from a library module containing other not-yet-tested exports results in a noisy amount of missing code coverage until every export of the library module is tested.
For example, imagine an optimal project containing single export modules…
In add.mjs
:
/**
* Adds two numbers.
* @param {number} number1 First number.
* @param {number} number2 Second number.
* @returns {number} Result of adding the numbers.
*/
export default function add(number1, number2) {
return number1 + number2;
}
In multiply.mjs
:
/**
* Multiplies two numbers.
* @param {number} number1 First number.
* @param {number} number2 Second number.
* @returns {number} Result of multiplying the numbers.
*/
export default function multiply(number1, number2) {
return number1 * number2;
}
Now, in a sibling module add.test.mjs
add tests for only the function add
:
import strictEqual from "node:assert";
import { describe, it } from "node:test";
import add from "./add.mjs";
describe("Function `add`.", () => {
it("Adds two numbers.", () => {
strictEqual(add(1, 1), 2);
});
});
Using Node.js v20.7.0, here is the result of running node --experimental-test-coverage --test
:
▶ Function `add`.
✔ Adds two numbers. (0.195542ms)
▶ Function `add`. (1.409041ms)
ℹ tests 1
ℹ suites 1
ℹ pass 1
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 53.524292
ℹ start of coverage report
ℹ -------------------------------------------------------------
ℹ file | line % | branch % | funcs % | uncovered lines
ℹ -------------------------------------------------------------
ℹ add.mjs | 100.00 | 100.00 | 100.00 |
ℹ add.test.mjs | 100.00 | 100.00 | 100.00 |
ℹ -------------------------------------------------------------
ℹ all files | 100.00 | 100.00 | 100.00 |
ℹ -------------------------------------------------------------
ℹ end of coverage report
Note there are no uncovered lines. Although the Node.js v20.7.0 CLI doesn’t yet support exiting with an error status when code coverage is incomplete (see nodejs/node#48739), tools like coverage-node
do and if they were in use for this example scenario the added tests could be contributed without failing CI.
Now imagine if the project instead sub-optimally had both the functions add
and multiply
in a single library module, utils.mjs
:
/**
* Adds two numbers.
* @param {number} number1 First number.
* @param {number} number2 Second number.
* @returns {number} Result of adding the numbers.
*/
export function add(number1, number2) {
return number1 + number2;
}
/**
* Multiplies two numbers.
* @param {number} number1 First number.
* @param {number} number2 Second number.
* @returns {number} Result of multiplying the numbers.
*/
export function multiply(number1, number2) {
return number1 * number2;
}
If you add tests for only the function add
in a sibling module utils.test.mjs
:
import strictEqual from "node:assert";
import { describe, it } from "node:test";
import { add } from "./utils.mjs";
describe("Function `add`.", () => {
it("Adds two numbers.", () => {
strictEqual(add(1, 1), 2);
});
});
Using Node.js v20.7.0, here is the result of running node --experimental-test-coverage --test
:
▶ Function `add`.
✔ Adds two numbers. (0.190042ms)
▶ Function `add`. (1.307167ms)
ℹ tests 1
ℹ suites 1
ℹ pass 1
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 53.201625
ℹ start of coverage report
ℹ ---------------------------------------------------------------
ℹ file | line % | branch % | funcs % | uncovered lines
ℹ ---------------------------------------------------------------
ℹ utils.mjs | 89.47 | 100.00 | 50.00 | 18-19
ℹ utils.test.mjs | 100.00 | 100.00 | 100.00 |
ℹ ---------------------------------------------------------------
ℹ all files | 93.10 | 100.00 | 75.00 |
ℹ ---------------------------------------------------------------
ℹ end of coverage report
Note the missing code coverage.
- If 100% code coverage was enforced in CI you wouldn’t be able to contribute these tests, even though they perfectly cover the function
add
. - It’s difficult to tell without manually investigating the uncovered line ranges what particular functions in the library are missing coverage.
How to help
Become familiar with the JavaScript module design best practices and anti-patterns explained in this article, spread awareness, and optimize!
Package authors
- For new packages, don’t publish index or library modules. Publish a module for each package export that can be deep imported. It’s perfectly valid to not have a
main
field in thepackage.json
. - For packages with index or library modules, remove them in a SemVer major release. To help users migrate, in the changelog entry and GitHub release markdown use diff code blocks to show exactly how each possible import in a project should be updated.
- Use the
eslint-plugin-optimal-modules
ruleno-named-exports
to enforce the golden rule for optimal modules.
Package users
This includes a package author using a dependency.
- Use deep imports from dependencies to avoid main index modules.
- If a dependency with an index module doesn’t support deep imports, check if there is a relevant GitHub issue or PR and lend your support. If there isn’t, raise one and feel free to reference this article for context. If the issue or PR is rejected or ignored, search for or publish an alternative dependency.