Upload
agelos-pikoulas
View
8.729
Download
1
Tags:
Embed Size (px)
DESCRIPTION
uRequire presented at GreeceJs (Greek JavaScript User Group) @ hackerspace.gr on 04-June-2014
Citation preview
uRequire.org
The JavaScript Universal Module & Resource Converter
“Write modular JavaScript code once, run everywhere” is a reality.
uRequire.org
• Why Modularity• JavaScript module systems• uRequire primer• A modules & dependencies builder• Build config usage
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
uRequire.org
• Why Modularity• JavaScript module systems & woes• uRequire primer• A modules & dependencies builder• Build config usage
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 {}
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'}
});
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 ?
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!
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!
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"], ...);
uRequire.org
• Why Modularity• JavaScript module systems & woes• uRequire primer• A modules & dependencies builder• Build config usage
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
uRequire.org
• Why Modularity• JavaScript module systems & woes• uRequire primer• A modules & dependencies builder• Build config usage
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': '_' }}}
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'); }
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’;' }
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 + "');"; }
uRequire.org
• Why Modularity• JavaScript module systems & woes• uRequire primer• A modules & dependencies builder• Build config usage
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
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:
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)
uRequire.org
• Why Modularity• JavaScript module systems & woes• uRequire primer• A modules & dependencies builder• Build config usage
• Thank you