modular javascript with commonjs compiler
DESCRIPTION
Introduction into a developing approach where you can keep your JavaScript modular without any performance lossTRANSCRIPT
WITH
Dmitry Sheiko
Modular JavaScript
CommonJS Compiler
separates the functionality of a program into independent modules
MODULAR PROGRAMMING
encapsulates everything required to implement a single aspect of the desired
functionality
MODULE
Therefore, a complex problem can be broken into simpler tasks
The entire system becomes easier to
debugupdatemodify
What about JavaScript?
MODULE PATTERNvar bar = (function(){ // Functionality return exportObj; }()),
foo = (function( bar ){ // Functionality }( bar ));
And what the structure does it give for your codebase?
HOW ABOUT THIS?
AMD
• Designed to accommodate asynchronous loading• Lazy-load scripts • Can load more than just JavaScript files• Config settings to simplify path resolution and
dependency listing
AMD IMPROVES PERFORMANCE OF WEB APPLICATION by bypassing module loading along with the rest of the page content
AMD HARMS PERFORMANCE OF WEB APPLICATION by producing numerous HTTP requests
COMMONJS MODULES/1.1
• Designed for server-side JavaScript and for native desktop applications
• Simple and clean module definition syntax
CJS MODULES + COMMONJS COMPILER
• Designed for server-side JavaScript and for native desktop applications
• Simple and clean module definition syntax• Can load more than just JavaScript files• Config settings to simplify path resolution and
dependency listing
COMMONJS COMPILER is the keyhttp://dsheiko.github.io/cjsc
Let’s get started!
INSTALLING COMMONJS COMPILER$sudo npm i cjsc -g
EXAMPLE 1
`foo.js`:console.log( "foo.js: Hello World" );
`bar.js`:require( "./foo" );console.log( "bar.js: Hello World" );
EXAMPLE 1
Compiling `bar.js`:$cjsc bar.js build.js
Output of `build.js`:foo.js: Hello Worldbar.js: Hello World
WHAT HAVE WE JUST DONE?
We loaded one module in another. Both are executed in the compiled code
EXAMPLE 2
`foo.js`:var privateState = “lorem“;module.exports = { name: "foo.js" };
`bar.js`:console.log( require( "./foo" ) );console.log(“privateState:" + typeof privateState );
EXAMPLE 2
Output of `build.js`:{ name: "foo.js" }privateState: undefined
WHAT HAVE WE JUST DONE?
We accessed an exported object and made certain that private state isn't available outside the module.
EXAMPLE 3
`foo.js`:console.log( "foo.js: constructing" );module.exports = { name: "foo.js" };
`bar.js`:console.log( require( "./foo" ) );console.log( require( "./foo" ) );
EXAMPLE 3
Output of `build.js`:foo.js: constructing{ name: "foo.js" }{ name: "foo.js" }
WHAT HAVE WE JUST DONE?
We checked that loading a module URL multiple times results in a single cached instance.
EXAMPLE 4
`foo.tpl`:Lorem ipsum dolor sit amet, Lorem ipsum dolor sit amet
`bar.js`:var tpl = require( "./foo.tpl" );console.log( "foo:" + tpl );
EXAMPLE 4
Output of `build.js`:foo: Lorem ipsum dolor sit amet, Lorem ipsum dolor sit amet
WHAT HAVE WE JUST DONE?
We found out that while resolving module dependencies CommonJS Compiler exports any content of non-JavaScript or JSON syntax as a string.
EXAMPLE 5`foo.tpl`:{{title}} spends {{calc}}
`bar.js`:var mustache = require( "./mustache" ), tpl = require( "./foo.tpl" ), view = { title: "Joe", calc: function () { return 2 + 4;}};console.log( mustache.render( tpl, view ) );
EXAMPLE 5
Output of `build.js`:
Joe spends 6
WHAT HAVE WE JUST DONE?
We leveraged loading of plain text resource to obtain a template for further use with a template engine (mustache.js).
DEBUGGING COMPILED CODEGenerating source map:$cjsc bar.js build.js --source-map=build.js.map
JavaScript console refers to original sources:
RUN-TIME CONFIGURATIONJSON configuration syntax:{ "<dependency-name>": { "path": "<dependency-path>", "globalProperty": "<global-property>", exports: [ "<variable>", "<variable>" ], require: [ "<dependency-name>", "<dependency-name>" ] }}
RUN-TIME CONFIGURATION EXAMPLE{ "jQuery": { "globalProperty": "jQuery" }, "plugin": { "path": "./config/vendors/jquery.plugin.js", "require": "jQuery", "exports": "jQuery" }}
ENABLING CONFIGURATION
$cjsc foo.js build.js --config=config.json
BUILD AUTOMATION WITH GRUNTGruntfile.js:grunt.loadNpmTasks( "grunt-contrib-cjsc" );grunt.initConfig({
cjsc: { debug: { options: { sourceMap: "./wwwroot/build/js/*.map", config: { "backbone": { "path": "./wwwroot/vendors/backbone/backbone" }}}, files: { "./wwwroot/build/js/app.js": "./wwwroot/js/app.js" }},
BUILD AUTOMATION WITH GRUNTGruntfile.js: build: { options: { minify: true, banner: "/* License */", config: { "backbone": { "path": "path": "./wwwroot/vendors/backbone/backbone" }}}, files: { "./wwwroot/build/js/app.js": "./wwwroot/js/app.js" }}}
BUILD AUTOMATION WITH GRUNT
It gives us two options: cjsc:debug and cjsc:build. The first one we run during development; it provides source maps for debugging and doesn't compress output. The second option we use when preparing production build.
THANK YOU!
COMMONJS COMPILERhttp://dsheiko.github.io/cjsc COMMONJS COMPILER GRUNT TASKhttps://github.com/dsheiko/grunt-contrib-cjsc
DMITRY SHEIKO
@sheikohttps://github.com/dsheiko
dsheiko.com