- bundling should be a separate process (completely optional).
- bundling should produce a ES6 module with import and export statements that defines the vortex of the module graph that forms the bundle.
- resolvers should be configurable so we can bundle things that can be resolved, e.g.:
import "./foo.js"
orimport "intl-messageformat-parser"
, if we have a resolver that understands how to resolve the ES6 implementation of moduleintl-messageformat-parser
. - formatters become a 1-1 mapping, without variable renaming or any other artifact associated with bundling.
- formatters transform export and import statements for the target format (commonjs, browser/global, system-register, amd, yui, etc.).
- formatters don't need to validate input and exports anymore, they just do a blind transformation of them.
Two basic examples, the first one that demonstrates the different steps to bundle 3 files, and then apply CJS and Browser formatters to output production code. The second one, in the other hand, bundles two files, but imports from an external module, while the process remains the same.
main.js ──> bundle [a.js] ──> bundle [b.js] ──> output [bundle.js] ──┬──> transpile [cjs.js]
└──> transpile [browser.js]
main.js ──> bundle [a.js] ──> output [bundle.js] ──┬──> transpile [cjs.js]
└──> transpile [browser.js]
Bundling introduces a performance penalty due to its nature of N-1 file transformation. Even if the bundling process is quick, post processing the bundle with esnext and/or transpilation will have a huge cost for complex applications. The proposal to avoid this performance penalty is to pay a toll by using a loader during development, and using the bundling process only to produce production code.
[entry point file] -> [bundle-up resolved ImportDeclarations and ExportFromDeclarations] -> [output file]
ImportDeclarations
andExportFromDeclarations
that cannot be resolved should be ignored. Potentially renaming their identifiers.ExportFromDeclarations
from non-entry points should be transformed intoImportDeclarations
(see open questions for more details about this one).
[file] -> [validate resolved ImportDeclarations and ExportFromDeclarations] -> [file]
- The validation process should rely on resolvers to locate other ES6 implementations (or equivalent) to statically verify everything that the file is trying to import from other modules.
[file] -> [transform ImportDeclarations] -> [transform ExportDeclarations] -> [output file]
This can be done by doing a simple transpilation process in a 1-1 fashion, producing CJS or System.register() output. Transpiling each file individually will means:
- only export and import statements has to be transformed (not need to do recursive analysis on the AST).
- import/export validation can be skipped, and developers can rely on the runtime execution to catch errors (for the sake of a fast build).
- Do we need a single entry point (main.js)? What if we have more than one entry point? What will be strategy if more than one exports? What if there are conflicts?
- How to specify the namespace
CustomNameSpace
for browser formatter? Maybe a mapping in package.json? - Where should we validate imports and exports? Should the import/export validations happen after the bundling process?
- What if a non-entry point uses
ExportFrom
clause for an external dependency? How can the bundle manipulate this is such a way that the bundle itself does not re-export that? Does this means we really have to have a distinction between entry points and non-entry points? e.g.: second > b.js doingexport {xyz} from "recast";