Duo is the next Component.
Duo was built because the existing client-side packaging solutions are not sufficient for lean, consistent, client-side applications built and managed by a team.
Duo makes the manifest optional, bundles only the code that you need, has built-in github versioning and supports source transforms.
My main goal for Duo was to blend the very best ideas from the Component and Browserify package managers. I was also inspired by how Go imports dependencies.
Duo aims to grow with your application, optimizing the workflow along these three pillars:
i. creating proof of concepts
ii. writing components
iii. building web applications
As developers, we often we need to test out an idea or isolate a bug. One of the big issues with existing package managers is that you cannot take your package manager with you without setting up whole lot of boilerplate. Duo removes this boilerplate and lets you include your packages right in your source code.
var events = require('component/events');
var uid = require('matthewmueller/uid');
You can also include versions:
require('component/[email protected]');
Or branches:
require('component/tip@master');
Paths work too:
require('yields/[email protected]:/index.js');
It also works with CSS:
@import "necolas/normalize.css";
@import "twbs/bootstrap";
body {
background: salmon;
}
When you're ready to build your file, run:
$ duo in.js out.js
$ duo in.css out.css
For any package manager to be successful, it needs to have a strong component ecosystem. Duo supports nearly all Component packages out of the box. Also, since Duo can load from paths, it supports nearly all Bower packages too. There are plans in the future to support Browserify packages as well with a plugin.
We're hoping to bridge the gap between all the different package managers and come up with a solution that works for everyone.
To create a Duo component, you'll need a component.json
:
{
"name": "duo-component",
"version": "0.0.1",
"main": "index.js",
"dependencies": {
"component/tip": "1.x",
"jkroso/computed-style": "0.1.0"
}
}
and if you have a component with js
and css
:
{
"name": "duo-component",
"version": "0.0.1",
"main": {
"js": "index.js",
"css": "index.css"
}
"dependencies": {
"component/tip": "1.x",
"jkroso/computed-style": "0.1.0"
}
}
If you're coming from the Component community, you'll notice that we no longer need to add scripts
, styles
or templates
. Duo handles all of this for you, walking the dependency tree and including what you need without all the manual work. This also has the added benefit of only bundling what you actually use so you can keep your build size to a minimum.
If you have an html
template or JSON
file that you'd like to include, simply require it. Duo automatically compiles and bundles the file as a javascript string using the string-to-js plugin:
var template = require('./tip.html');
Duo will take care of the rest, transforming the html
into a javascript string. You can also include a json
file:
var json = require('./component.json');
To build our component, we just need to run duo
:
$ duo
By default, this will install all our dependencies to the components/
directory and write our build files to the build/
directory.
In order for a package manager to be truly useful, it needs to scale it's workflow to accommodate big web applications. Once again, Duo makes this process seamless.
You can pass an array of files to main
in the component.json
file. This will tell Duo all the entry files it needs to traverse.
Here's an example root component.json
that we could use to build our app:
{
"name": "duo-app",
"version": "0.0.1",
"main": [
"app/homepage/index.js",
"app/homepage/index.css",
"app/dashboard/index.js",
"app/dashboard/index.css"
]
}
You can build your app by running duo
:
$ duo
You'll notice this component.json
specifies multiple pages (homepage
and dashboard
). Duo allows us to build multiple pages, granting us the flexibility to move between web applications and web pages without having one massive asset bundle.
If Duo discovers an asset like an image or font along the way, it will automatically symlink it to your build/
directory. Say we have the following image in our CSS file
@import "necolas/normalize";
body {
background: url('./images/duo.png');
}
Duo will transform this file to:
@import "necolas/normalize";
body {
background: url('/images/duo.png');
}
And symlink that file to build/images/duo.png
, then up to you to expose build/
on your web server.
Initialize Duo with a root
. All other path will be relative to the root
including the build directory and the installation path.
var duo = Duo(__dirname);
Specify the entry file that Duo will traverse and transform.
var duo = Duo(__dirname)
.entry('main.js');
Attach the component to window object as name.
var duo = Duo(__dirname)
.entry('tip.js')
.global('Tip');
Include a file with name and its stc without requiring it. This is particularly useful for including runtimes.
duo.include('jade-runtime', ...);
Set duo to development mode. This includes "development" dependencies and adds source maps.
Set the github authentication token so you can load private repos. If you do not set this token, Duo will automatically try to load the token
from your ~/.netrc.
Here's how to create a GitHub token: https://github.com/settings/tokens/new
Set the maximum concurrency Duo uses to traverse. Defaults to: 10.
Set the installation path of the dependencies. Defaults to components/
.
Set the asset path of duo. Defaults to build/
.
Run duo traversing and transforming from entry returning the bundle.
If fn
is specified duo.run(fn)
will use fn as its callback but you can also run duo.run()
as a generator.
var src = yield duo.run();
duo.run(function(err, src) {
// ...
});
Run duo traversing and transforming from entry writing to "build/".
If fn
is specified duo.write(fn)
will use fn as its callback but you can also run duo.write()
as a generator.
yield duo.write();
duo.write(function(err) {
// ...
});
Apply a plugin to duo. You can pass a function or a generator. The signature is the following:
function (file, entry, [done]) {
// ...
}
If you don't supply done
, the plugin will be synchronous.
Duo development began back in April when the state of Component 1.x was uncertain.
While the release of Component 1.x solved a lot of my initial gripes with earlier versions of Component, I wanted a more radical departure from Component that borrowed some of the good ideas from Browserify.
Browserify is a great project and if it's working well for you then you should keep using it.
Duo's scope is much more ambitious. Duo aims to be your go-to asset pipeline for Node.js. Much in the same way that Sprockets is for the Ruby community.
Furthermore, Browserify's dependence on NPM to deliver it's packages leads to some big issues:
- naming is a hassle (how many different kinds of tooltips are there?).
- private modules require a privacy NPM server
- ensuring your team has push access to each module is always a pain. If someone leaves, this becomes even harder.
By using Github as your package manager all of these issues just disappear.
- join us at
#duojs
on freenode - Mailing List
... plus many more wonderful contributors!
make test
MIT
if you write
require('component/[email protected]');
you still need to define this dependency in yourcomponent.json
?