23
The JavaScript Universal Module & Resource Converter “Write modular JavaScript code once, run everywhere” is a reality. uRequire.org

uRequire@greecejs: An introduction to

Embed Size (px)

DESCRIPTION

uRequire presented at GreeceJs (Greek JavaScript User Group) @ hackerspace.gr on 04-June-2014

Citation preview

Page 1: uRequire@greecejs: An introduction to

uRequire.org

The JavaScript Universal Module & Resource Converter

“Write modular JavaScript code once, run everywhere” is a reality.

Page 2: uRequire@greecejs: An introduction to

uRequire.org

• Why Modularity• JavaScript module systems• uRequire primer• A modules & dependencies builder• Build config usage

Page 3: uRequire@greecejs: An introduction to

uRequire.org

Why modularity• Maintainable & reusable code– Organize code in logical parts– Clearly stated dependencies– Reusability, Replace-ability, Testability etc

• Employ standards and trusted tools– Unit Testing, Versioning, Regression Testing

• Have a dynamic code loading mechanism.• End the damnation of – Authoring “OneHuge.js” file – .js file concatenation

Page 4: uRequire@greecejs: An introduction to

uRequire.org

• Why Modularity• JavaScript module systems & woes• uRequire primer• A modules & dependencies builder• Build config usage

Page 5: uRequire@greecejs: An introduction to

uRequire.org

CommonJS (nodejs)var dep1 = require("../some/path/dep1"),

dep2 = require("../other/path/dep2"), localDep = require("localDep");// ‘localDep’ in

node_modules

// do stuff with dep1, dep2 & localDep

// `return` module valuemodule.exports = {my: "module"}

// or set properties on `exports` exports.my = "module" // `exports` is a pre-given {}

Page 6: uRequire@greecejs: An introduction to

uRequire.org

AMD (browser)Asynchronous Module Definition

define([“../some/path/dep1”, “other/path/dep2”,“localDep”], function(dep1, dep2, localDep) {

// do stuff with dep1, dep2, localDepreturn {my:'module'}

});

Page 7: uRequire@greecejs: An introduction to

uRequire.org

• Many woes on Module formats & incompatibilities

• Verbose syntax, boilerplate ceremony & intricacies (especially AMD)

• execution environment (AMD only for Web, CommonJs only for nodejs)

• capabilities, dependency/path resolutions, plugins, semantics etc are a mess

• UMD is a semi-standard boilerplate, far from usable.

• U need a bridge to enjoy the richness of modules.

Why do JavaScript developers hate modules ?

Page 8: uRequire@greecejs: An introduction to

uRequire.org

Mixing AMD & CommonJs ? define(['main/dep1', 'main/helpers/dep2'],

function(dep1, dep2) {

var dep3 = require('moredeps/dep3');

 

if (dep3(dep1) === 'wow'){

require(['./dep4'], function(dep4) {

// asynchronously do things with dep4

});

// do stuff with dep1, dep2, dep3 

return {my:'module'}

}

);

Too many woes

AMD: “require” needs to be in [] dependencies 1st (& fn

params)

AMD: “moredeps/dep3” not listed as [] dependency,

halts

nodejs: ‘define’ is unknown (use amdefine ?)

nodejs: async ‘require’ not

working

nodejs: bundleRelative paths

not workingUnleash m

odule hell!

Page 9: uRequire@greecejs: An introduction to

uRequire.org

UMD: Universal Module Definition

// https://github.com/umdjs/umd/blob/master/returnExports.js

(function (root, factory) {

if (typeof define === 'function' && define.amd) {

// AMD. Register as an anonymous module.

define(['b'], factory);

} else if (typeof exports === 'object') {

// Node. Does not work with strict CommonJS, but

// only CommonJS-like environments that support module.exports, like Node.

module.exports = factory(require('b'));

} else {

// Browser globals (root is window)

root.returnExports = factory(root.b);

}

}(this, function (b) {

// use b in some fashion. 

// Just return a value to define the module export.

// This example returns {}, but can return a function as the exported value.

return {};

}));

Not really a “Universal” one but … … 10s of proposed module templates, for different scenarios.

‘Unive

rsal’

mes

s!

Page 10: uRequire@greecejs: An introduction to

uRequire.org

Dependency path resolution: • nodejs: relative to requiring file (fileRelative) only:

• AMD: relative to bundle (bundleRelative) also:

• Both are useful (at times). Can we use both ?

AMD + CommonJS = neighbors from hell

var replace = require(“../../../string/replace");

• define(["utils/string/replace"], ...);

Page 11: uRequire@greecejs: An introduction to

uRequire.org

• Why Modularity• JavaScript module systems & woes• uRequire primer• A modules & dependencies builder• Build config usage

Page 12: uRequire@greecejs: An introduction to

uRequire.org

uRequire primer• Convert from any format to any other:– from AMD and CommonJS (.js, .coffee, .iced, .coco, .ls)– to AMD, CommonJS, UMD, Combined for nodejs-Web/AMD-

Web/Script– Control conversion features (runtimeInfo, globalWindow etc)

• Forget the woes or Module formats incompatibilities– Resolve paths, fill missing deps from [], inject ‘require’ etc

• Eliminate boilerplate & write modular Javascript code once, run everywhere : Web/Script, Web/AMD, nodejs

• A Universal Module Format with the power, goodies & standards from all.

• Convert to a single combined.js, that runs everywhere & is super optimized

Page 13: uRequire@greecejs: An introduction to

uRequire.org

• Why Modularity• JavaScript module systems & woes• uRequire primer• A modules & dependencies builder• Build config usage

Page 14: uRequire@greecejs: An introduction to

uRequire.org

A Modules & Dependencies aware builder.

• Exporting modules to global (like window.$) with no boilerplate.

• Want noConflict(), baked in? Its a simple declaration away.

• The same in a config:

• Export to bundle:

// file `uberscore.js` - export it to root (`window`) as `_B`

({ urequire: { rootExports: '_B', noConflict: true }});

module.exports = {...}

dependencies: { exports: { root: { 'uberscore': '_B' }}}

dependencies: { exports: { bundle: { ‘lodash': '_' }}}

Page 15: uRequire@greecejs: An introduction to

uRequire.org

Manipulate module dependencies

• Replace deps with mocks or alternative versions:

• With a ResourceConverter callback:

// underscore is dead, long live _

dependencies: { replace: { lodash: 'underscore'}}

function(m){ m.replaceDeps('models/PersonModel','mock/models/PersonModelMock'); }

Page 16: uRequire@greecejs: An introduction to

uRequire.org

Manipulate Module CodeInject, replace or delete code fragments or AST nodes:• Delete matching code of code skeleton

• Traverse matching nodes, replace or delete them

• Inject code before (or after) each module's body:

function(m){ m.replaceCode('if (debug){}') }

function(m){ m.replaceCode('console.log()', function(nodeAST){}) }

function(m) { m.beforeBody = 'var VERSION = ‘1.0’;' }

Page 17: uRequire@greecejs: An introduction to

uRequire.org

Manipulate Module CodeInject common or merged statements:• beforeBody can be calculated per module

• Before main body at each module:

• Like above, but merge code on 'combined' template:bundle: commonCode: 'var expect = chai.expect;'

function(m) { m.mergedCode = '"var p1 = myModule.p1, p2 = myModule.p2;"'}

function(m) { m.beforeBody = "var l = new _B.Logger('" + m.dstFilename + "');"; }

Page 18: uRequire@greecejs: An introduction to

uRequire.org

• Why Modularity• JavaScript module systems & woes• uRequire primer• A modules & dependencies builder• Build config usage

Page 19: uRequire@greecejs: An introduction to

uRequire config

uRequire.org

uberscore:

path: 'source'

dstPath: 'build'

filez: ['**/*', (f)-> f isnt 'badfile']

copy: [/./]

runtimeInfo: ['!**/*', 'Logger']

dependencies: exports:

bundle: 'lodash': '_'

root: 'uberscore': '_B'

resources: [

['+inject:VERSION', ['uberscore.js'],

(m)-> m.beforeBody = "var VERSION ='0.0.15';"]

]

template: banner: "// uBerscore v0.0.15"

read files from ‘source’

save to ‘build’

filter some filez

copy all other files

affect template selectively

inject ‘lodash’ in each module

export ‘uberscore’ as `window._B` with noConflict()

inject ‘VERSION = ..’ before body

banner after template/optimize

Can be gruntjs, .coffee / .js nodejs module, .json, .yml & more

Page 20: uRequire@greecejs: An introduction to

Deriving a config

uRequire.org

min:

derive: ['uberscore']

filez: ['!', /RegExpSpecs/]

template: 'combined'

dstPath: 'build/uberscore-min.js'

optimize: true

inherit deeply & modify

filter more filez

change template

optimize through Uglify2. Can also pass an options {}

• Deeply inherit / extend all properties

change destination

filez: ['**/*', (f)-> f isnt 'badfile', '!', /RegExpSpecs/]

• Either overwriting or appending props:

Page 21: uRequire@greecejs: An introduction to

Declarative template options

uRequire.org

globalWindow: false

runtimeInfo: ['Logger']

useStrict: true

injectExportsModule:

['circular/Dependency']

bare: true

allNodeRequires: ['/data/preCached']

noRootExports: true

scanAllow: false

window === global always

__isWeb, __isNode & __isAMD

inject 'use strict;‘

inject ‘exports’ & ‘module’

no enclosing function

& more…

• per module (minimatch of module path)• whole bundle (true)

Page 22: uRequire@greecejs: An introduction to

uRequire.org

• Why Modularity• JavaScript module systems & woes• uRequire primer• A modules & dependencies builder• Build config usage

• Thank you

Page 23: uRequire@greecejs: An introduction to

uRequire.org

uRequire.org

@uRequire

github.com/anodynos/uRequire

[email protected]