4. Module systems express code dependencies
› 2000s state of the art:
– One big file with carefully ordered functions, or
– Many small files, and carefully ordered <script> tags
› Other languages solve this problem easily; in JS, until
ES6, we have to solve it ourselves.
› We have two hacky solutions:
– CommonJS modules
– AMD modules
› CommonJS is better for code reuse.
@DOMENIC
5. CommonJS modules
› AMD:
define(['a', './b'], function (a, b) {
return { c: 'd' };
});
› CommonJS:
var a = require('a');
var b = require('./b');
module.exports = { c: 'd' };
@DOMENIC
6. The AMD configuration problem
› In require('a'), what does 'a' refer to?
– AMD module called 'a' (per baseUrl config)
– Some other AMD module (per map config)
– Non-AMD code (per shim config)
– AMD plugin (per plugin config)
– A package’s main module (per packages config)
› In a CommonJS + npm system, the answer is always the
main module for package 'a'.
@DOMENIC
7. The AMD dependency problem
› If Ember depends on RSVP.js, how does it express this in
AMD?
– require('rsvp') doesn’t cut it.
– I know! We’ll use more config!
› Now your Ember-using project needs to know about
Ember’s internal implementation details in order to make
Ember’s require('rsvp') work, via configuration.
› This severely limits the extent to which library authors can
bring in third-party code.
› Where did this 'rsvp' string come from anyway?
These are all problems to be solved by packages, but AMD
tries—and fails—to solve them at the module level.
@DOMENIC
8. AMD is not an advantage
› AMD tries to solve too many problems that are way
beyond the level of a module system.
› CommonJS can solve all AMD use cases except cross-
domain single-module loading.
› Using AMD cuts you off from consuming most module
code out there.
› Your code is more universal with CommonJS. More tools
consume it, and it’s easier to test.
› AMD users can use r.js to convert from CommonJS.
@DOMENIC
10. Packages
› Modules let us reuse our own code.
› Packages let us reuse other people’s code.
› Examples of packages:
– Backbone
– Underscore
– Sinon.JS
– jQuery
– jsdom
– Express
› Packages have dependencies
– E.g. Backbone depends on Underscore and jQuery
@DOMENIC
11. Packages as a unit of code reuse
› Packages are small, often one module.
› They can shuffle work off to their dependencies, allowing
greater code reuse.
› If jQuery UI were composed of packages:
– jqueryui-position depends on nothing
– jqueryui-mouse depends on jqueryui-core, jqueryui-widget
– jqueryui-autocomplete depends on core, widget, menu, position
– etc.
› ―Download builders‖ are hacky and un-encapsulated ways
of solving package management.
› Multi-thousand line modules belong in the 2000s.
@DOMENIC
12. Packages can be versioned independently
› When you separate functionality into small packages, you
aren’t tied to monolithic, coordinated release cycles.
› By depending on small packages, you can get bug fixes
and new features as soon as their authors publish them.
› Then users of your package get bug fixes, without you
doing anything!
› Conventionally, we try to adhere to semantic versioning:
– 0.x.y releases: unstable; upgrade at your peril.
– 1.x.y and beyond:
› Increasing ―patch version‖ y means bug fixes.
› Increasing ―minor version‖ x means new features.
› Increasing ―major version‖ means new API, breaking backward-compat.
– So we can express dependencies as 0.1.2 or 2.x or 2.3.x to get
the benefits of independent versioning.
@DOMENIC
13. Packages can be app-specific
› Each ―widget‖ can be a package.
– Sidebar, header, currency table, news ticker, …
› Or you could have packages for business logic.
– Financial calculations, market predictions, discount rules, …
› Or you can isolate hard problems in packages.
– Talking to SMTP servers, traversing a RESTful API, rendering
PDFs with JavaScript, sandboxing user input, …
› They can separate out cross-cutting concerns.
– Number formatting, CSS theming, injecting logging and error
handling, …
› They can even help with domain-driven design.
– Encapsulating bounded contexts, exposing services, isolating a
shared kernel, building an anticorruption layer, …
@DOMENIC
15. What problems do we need to solve?
› Get code onto our computers
› Automatically install expressed dependencies
› Have a way for one package’s code to ―require‖ another
package’s code.
› Make other peoples’ code available to you
@DOMENIC
16. npm stands for JavaScript package manager
› 26,000 packages and growing
› The emphasis on small packages means many work in the
browser already.
› The npm registry is anarchic.
– This makes package discovery tricky, but in balance is a big win.
– Server code, browser code, even AMD code: it’s all welcome.
– A single namespace means no enforced prefixing.
› npm packages follow a set of conventions that make your
life easy.
@DOMENIC
20. Packages are encapsulation boundaries
› Single point of entry API via the main module:
– require("backbone")
– require("underscore")
– require("sinon-chai")
– require("express")
› You don’t need to know about a package’s dependencies
– Unlike AMD!
› You don’t need to know about a package’s strategy
– Client-side MVC framework choice, templating engine, CSS
precompiler, …
@DOMENIC
21. Packages are concern boundaries
› Their own license
› Their own readme
› Their own coding style and linting rules
› Their own compile-to-JS authoring language
› Their own tests and test style
› Their own author/owner/maintainer
@DOMENIC
23. npm dependency basics
› npm uses a global registry by default: https://npmjs.org
– A CouchDB server: http://isaacs.ic.ht/_utils/
› It uses package.json to install dependencies
@DOMENIC
24. Dependencies are hierarchical
› Unlike some other package managers, there is no global shared
―DLL hell‖ of packages on your system.
› They get installed in a hierarchy of node_modules folders:
request@2.12.0
├─┬ form-data@0.0.3
│ ├── async@0.1.9
│ └─┬ combined-stream@0.0.3
│ └── delayed-stream@0.0.5
└── mime@1.2.7
request/node_modules/form-data
request/node_modules/form-data/node_modules/async
request/node_modules/form-
data/node_modules/async/node_modules/combined-stream
⋮
@DOMENIC
25. Git dependencies
› Dependencies can be Git URLs
› Useful for pointing to forked-and-patched versions of a
library while you wait for your pull request to land.
› Useful for private Git servers.
› For GitHub, use "package": "user/repo#version" syntax.
› This allows an escape hatch from the central registry and
its single namespace, if you’re into that.
@DOMENIC
26. Other important npm features
› npm dedupe: creates the minimal dependency tree that
satisfies everyone’s versions
› npm link: symlinks, for local development
› The "scripts" field in package.json can give you arbitrary
scripts which have access to your devDependencies.
– npm run <scriptName>
– npm test (shortcut for npm run test)
› Packages can have arbitrary metadata: it’s just JSON!
@DOMENIC
28. How can we use this without publishing?
› We can’t put proprietary code in the npm registry.
› But we’d still like to use the package concept, and npm’s
features.
› There are hacks:
– npm link
– Using Git URLs entirely (this actually works pretty well).
› But, really we need our own registry!
@DOMENIC
29. Your own registry
› CouchDB is really good at replicating. So just do that!
› Then:
npm install lab49-ip –reg http://npm.lab49.org
› You can use the publishConfig settings in package.json to
make a given package automatically publish to your
internal registry, instead of the public one.
› We’re working on ―fallback registries,‖ to obviate the
replication step.
@DOMENIC
31. A note on CSS
› Just include it in your package!
› Don’t reference it from your JS, e.g. automatically inject it
into the page.
› It shouldn’t matter whether the CSS is in
node_modules/mywidget or in app/widgets.
› The application’s build tool will consume it, if the
application author needs it; otherwise it sits inert.
@DOMENIC
32. Browserify!
› The best solution for bringing CommonJS modules and
npm packages to the browser.
› Shims common (and not-so-common) Node APIs to give
you access to many npm packages.
– events, path, url, querystring, vm, http, crypto, zlib (!) and more…
› Understands npm’s node_modules structure.
› Has a flexible pipeline and vibrant ecosystem, for e.g.
source map support or just-in-time bundle compilation
when embedded in simple HTTP servers.
› Allows packages to declare source transforms and browser
overrides in their package.json metadata.
@DOMENIC
33. Discovering client-side packages
› We trust packages most if they are validated by continuous
integration, e.g. by Travis CI:
› We can trust client-side packages if they are validated by
testling-ci:
@DOMENIC
37. › Make sure all your code is in
WHAT’S NEXT CommonJS module format.
› Then organize that code into
packages.
› Consume other peoples packages.
› Start publishing to npm—or to a
private registry.
› Use browserify in your build process
(or in your server).
› Use testling-ci to encourage trust
and discovery.
@DOMENIC
Notes de l'éditeur
Intro: units of code reuse.FunctionsModulesPackages
I work at Lab49 in NYC with smart people.
“Code dependency” is a very low-level thing. Functions express code dependencies too; modules are only slightly higher level than that (and sometimes at the same level)AMD is definitely better than nothing, so I won’t fault you for using it. But as you’ll see, it’s the worse option for apps that need true code reuse.
AMD has leaned on excessive configuration to solve problems that are best solved by a package management system/In a CommonJS system alone, require('a') is meaningless---because module systems are just about files!
Large projects will always want to break themselves up into reusable pieces at a larger level than just modules. Third party dependencies are a great example. But how do you express that kind of dependency in AMD?
Problems beyond a module system: that’s why the config exists. Shims, baseUrls, packages, maps… Especially plugins. Modules should just be files!So. These problems should not be solved at the module level. Where should they be solved?
By using CommonJS, we’ve solved one level of code reuse, without overstretching
I mean “our own code” very literally---I think without packages, you can’t scale beyond a single person.
Can you imagine not having to go back to their site, re-input what you had before, click the new box, download the file, overwrite the last one, and check it into source control (even though it’s not code you authored)?
The first two are solved by the package manager, a command-line program.The third is solved by mapping package names to their main modules.The fourth is solved by a central registry.
26K is important, because package ecosystems are heavily empowered by network effects. Small packages can still be powerful in an ecosystem like that.
You can have a header widget coded in Backbone and Stylus, while your folder list widget uses jQuery UI sortable and SASS.The single entry-point API makes it easy to create simple interfaces and contracts, e.g. “exposes a render method that returns a document fragment” or similar.
npm test = a universal format for testing packages, no matter how they were authored
Let’s get this out of the way.
Npm gets packages onto your computer; browserify gets them from their into your browser.
This is the .travis.yml for client-side packages.You can use “harness” OR anything that outputs tap