talk-vanjs-201311-modules



talk-vanjs-201311-modules

0 0


talk-vanjs-201311-modules

Super VanJS Nov 2013 talk

On Github jrburke / talk-vanjs-201311-modules

Modules

Stories of Future Past

James Burke | github | twitter

Firefox OS - Gaia | RequireJS | volo

http://jrburke.com/talks/vanmodules

Stories of future past

IMO

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?

Example app layout