Mainly AMD and node/CJS
- YUI
- old Dojo, Closure
- XPCOM Components.utils.import
The breakdown
- declare
- reference
- normalize
- resolve
- fetch
- translate
- link and execute
Declare
// node
var x = require('x');
exports.y = ...
// or
module.exports = function() {}
// AMD
define(function(require, exports, module) {
var x = require('x');
exports.y = ...
// or
return function() {};
});
Difference
- Node: imperative, runs at point of first require
- AMD: declarative, runs before factory function
Consequences
- Node: sync require not so great in browser
- AMD: weaker circular ref support, function wrapping
Reference
// node
// static
var x = require('x');
// dynamic
var z = require(something + '/z');
// AMD
define(function(require, exports, module) {
// static
var x = require('x');
// dynamic
require([something + '/z'], function(z) {
});
});
Difference
- Node: cannot solve generically in browser case
ES: Declare and Reference
// static
// import of default export
import x from 'x';
// non-default import from 'lib': `export let w = 3`;
import { w } from 'lib';
// named export
export let y = ...;
// and/or
// default export
export default function() {};
// dynamic
System.import(something + '/z', function(z) {
});
Properties
- Similar to AMD resolution, but no function wrapper
- import/export top level, no nesting
- import/export are mutable slots
- Solves cycle support, via new syntax
- Dynamic resolution in runtime callback API
Normalize
// Module 'a/b' asks for ./c
import c from './c'
- Node: path-based: relative to a/b's .js file
- AMD: module ID based: relative to 'a/b' module ID
- ES: Pluggable, likely module ID based by default
Resolve: ID-to-path
// convert 'a/b/c' to path on disk
- Node: nested multiple IO node_modules
- AMD: baseURL + moduleID + '.js', declarative config
- ES: Likely baseURL + moduleID + '.js' by default,
controlled by JS env. Has loader hook.
- ES in browser: could have declarative config?
Fetch: get file contents
- Node: synchronous IO
- AMD: async, usually network IO.
- ES: async by default. Loader hook assumes async.
Translate
- Node: require('module')._extensions['.ext']
- AMD: loader plugins: 'text!a.html', loader hooks
- ES: Imperative loader hook
Link / Execute
- Node: usual JS evaluation
- AMD: usual JS evaluation
- ES: linking, check import/export names, then eval
Migration patterns
- Node: use ES Module Loader hooks for backcompat, ID-to-path resolution
- AMD: likely small script for loader plugin syntax, adapter old API. Browser env might have good declartive config?
- ES: module inlining still funky
What can we do now?
- ID resolution
- Make it easy: baseURL + moduleID + '.js'
- Better package managers
Hey, package managers
- Favor ease of runtime use over your use
- Single file deps at baseURL + moduleID + '.js'
- Your metadata in app package.json or .metadata dir
- Front end: commit app-level deps.
Packages still OK
- Adapter module at baseURL + moduleID + '.js'
// node
module.exports = require('x/main');
// AMD
define(['x/main'], function(x) {return x});
// ES
export default from 'x/main'; // maybe?