Comparing version 0.1.0 to 0.1.1
@@ -11,2 +11,3 @@ var _ = require( "underscore" ), | ||
var kBundleFileName = "bundle.json"; | ||
var kBowerFileName = "bower.json"; | ||
@@ -16,2 +17,3 @@ var kBundleDefaults = { | ||
dependencies : [], | ||
directoriesToIgnore : [], | ||
prioritizeFlattenedDirectories : false, | ||
@@ -179,2 +181,4 @@ filePriority : [], | ||
combinedFile.sourceFilePaths = filePaths; | ||
// The file may already exist from a previously processed parcel. | ||
@@ -234,2 +238,23 @@ if( ! fs.existsSync( combinedFile.path ) ) | ||
// Get dependencies from the bower.json file if it exists. | ||
if( _.contains( _.keys( files ), kBowerFileName ) ) { | ||
var bowerFileContents = walker.cat( kBowerFileName ); | ||
var bowerJSON; | ||
try { | ||
bowerJSON = JSON.parse( bowerFileContents.toString() ); | ||
if( bowerJSON.dependencies ) { | ||
_.extend( bundleOptions, { | ||
dependencies : _.map( _.keys( bowerJSON.dependencies ), function( dep ) { | ||
return namespacePrefix + dep; | ||
} ) | ||
} ); | ||
} | ||
} | ||
catch( e ) { | ||
console.log( "Failed to parse contents of bower.json file in " + bundleName ); | ||
} | ||
} | ||
if( _.isUndefined( bundleOptions.directoriesToFlatten ) ) | ||
@@ -246,3 +271,3 @@ bundleOptions.directoriesToFlatten = dirOptions.directoriesToFlatten; | ||
if( fileStats.isDirectory() ) { | ||
if( _s.startsWith( fileName, "__" ) ) { | ||
if( _.contains( bundleOptions.directoriesToIgnore, fileName ) ) { | ||
return; | ||
@@ -342,2 +367,2 @@ } | ||
module.exports = Bundle; | ||
module.exports = Bundle; |
{ | ||
"name": "cartero", | ||
"description": "An asset manager for modern web apps.", | ||
"version": "0.1.0", | ||
"version": "0.1.1", | ||
"author": { | ||
@@ -39,3 +39,8 @@ "name": "Rotunda Software", | ||
"keywords": [ | ||
"gruntplugin" | ||
"gruntplugin", | ||
"asset", | ||
"asset manager", | ||
"css", | ||
"javascript", | ||
"html" | ||
], | ||
@@ -42,0 +47,0 @@ "dependencies": { |
134
README.md
@@ -0,52 +1,45 @@ | ||
<h1>Cartero</h1> | ||
In the year 2013, why do we still organize our web assets like it's 1990, grouping them together in big directories separated by their type? Instead, why don't we leverage directories a bit more effectively to put files together that really belong together? For example, why don't we put JavaScript and stylesheet assets that are just used by one particular page in the same directory as that page's template? And what about related assets like personModel.js, personView.js, person.css, etc.? Why don't we keep those together in a single "person" directory, instead of spreading them out all over the place? It sure would be nice to be able to quickly switch between those files! | ||
One of the obstacles, besides resistance to change, has been that asset management has a lot of moving parts. A complete general solution needs to address preprocessing (i.e. compiling .scss, .coffee, etc.) for arbitrary asset types, minification and concatenation in production mode, and dependency management. | ||
Cartero works on top of [Grunt.js](http://gruntjs.com/) and together with package managers like [Bower](http://bower.io/), addressing these issues so that we can more effectively organize assets, optimize their delivery, and scale up applications. | ||
<p align="center"> | ||
<img src="http://www.rotundasoftware.com/images/cartero.png"/> | ||
<img src="http://www.rotundasoftware.com/images/cartero/combo-directory-structure.png" /> | ||
</p> | ||
Cartero is a client side asset manager, especially suited for organizing, processing, and serving the many JavaScript, stylesheet, and template assets needed in "thick client" web applications built with JavaScript MVC frameworks. | ||
As of the time of this writing Cartero only works with Node.js / Express, but [the very small amount](https://github.com/rotundasoftware/cartero-express-hook/blob/master/middleware.js) of web framework specific logic is easy to port to any environment. | ||
## Benefits | ||
* Instead of using separate directories for each type of asset, group your assets into "bundles" of related JavaScript files, stylesheets, templates, and images (e.g. keep `person.coffee`, `person.scss`, `person.tmpl` together in *one directory*). | ||
* Specify the exact bundles that are required for each page in the page's template. | ||
* Easily manage bundle dependencies. | ||
* All assets that a page requires are automatically injected into the served HTML when the page's template is rendered. No more messing with `<script>` and `<link>` tags! | ||
* Keep assets for a particular page with that page's template to automatically serve them with the page. | ||
* Store your common or third party assets in "bundles" of related JavaScript files, stylesheets, templates, and even images. Then just specify the bundles that each page requires. | ||
* All necessary `<script>` and `<link>` tags are generated for you. | ||
* Bundle dependencies (and inter-bundle dependencies) are resolved. | ||
* In development mode, served assets are preprocessed, but not minified or concatenated. | ||
* In production mode, served assets are preprocessed, minified and concatenated. | ||
* All assets that live in the same directory as a page's template are automatically included when that page is rendered. | ||
* Use your preferred JavaScript module system (e.g. RequireJS, [Marionette](https://github.com/marionettejs/backbone.marionette) Modules, etc.). If you'd like, even enjoy built in support for client side CommonJS style modules via [Browserify](https://github.com/substack/node-browserify)! | ||
* Easily run any and all of your favorite preprocessing and minification tasks (scss, coffee, uglify, etc.). | ||
* Works in harmony with package managers like [Bower](http://bower.io/). | ||
* Large asset bundles can optionally be kept separate for optimal loading and caching. | ||
* Use your preferred JavaScript module system, e.g. RequireJS, [Marionette](https://github.com/marionettejs/backbone.marionette) Modules, or even CommonJS! | ||
* Include [Bower](http://bower.io/) packages as bundles. | ||
Cartero is JavaScript framework, stylesheet and templating language agnostic. It also *almost* works with any web framework – the [very small "hook"](https://github.com/rotundasoftware/cartero-express-hook/blob/master/middleware.js) of runtime logic is easy to port to any web framework, but is currently only available for Node.js / Express. Instructions for writing a Hook for another framework <a href="#hook">are below</a>. | ||
## Overview | ||
### The Asset Library | ||
### Page specific assets | ||
Get ready for a slight paradigm shift from the traditional js / css / template directory structure. With Cartero, you can keep all your assets, regardless of type, in your application's **_Asset Library_** (except for assets that are just used by a particular page, which can be stored with that page's template - see below). Each subdirectory of your Asset Library defines a **_Bundle_** that may contain JavaScript files, stylesheets, templates, and images. Additionally, each bundle may contain a `bundle.json` file, which contains meta-data about that bundle, such as any dependencies on other bundles. Take the following example library: | ||
Often times assets are just used by one particular page. Just keep those assets in the same directory as the page's template, and they will be automatically be included when it is rendered. No more messing with `<script>` and `<link>` tags! For example, say your page templates live in a directory named `views`, as is typical for most web frameworks. | ||
``` | ||
assetLibrary/ | ||
JQuery/ | ||
jquery.js | ||
JQueryUI/ | ||
bundle.json = { dependencies : [ "JQuery" ] } | ||
jquery-ui.js | ||
jquery-ui.css | ||
Backbone/ | ||
bundle.json = { dependencies : [ "JQuery" ] } | ||
backbone.js | ||
Dialogs/ | ||
bundle.json = { dependencies : [ "Backbone", "JQueryUI" ] } | ||
dialogManager.coffee | ||
EditPersonDialog/ | ||
bundle.json = { dependencies : [ "Dialogs" ] } | ||
editPersonDialog.coffee | ||
editPersonDialog.scss | ||
editPersonDialog.tmpl | ||
views/ | ||
login/ | ||
login.jade | ||
login.coffee | ||
login.scss | ||
``` | ||
Because of the `bundle.json` files (contents inlined), the `EditPersonDialog` bundle depends on the `Dialogs` bundle, and indirectly depends on the other three bundles. When a page requires a bundle, dependencies are automatically resolved. | ||
When the `login.jade` template is rendered, the compiled `login.coffee` and `login.scss` assets will automatically be included. A page can also "extend" on the assets required by another page. | ||
It is also possible to implicitly declare dependencies by nesting bundles because, by default, child bundles automatically depend on their parent bundles. For example, we can put the `EditPersonDialog` bundle inside the `Dialogs` bundle, like so: | ||
### The Asset Library | ||
Some assets are needed by many different pages and / or are supplied by third parties. Keep all of these common assets in one or more **_Asset Libraries_**, grouped into subdirectories, called **_Bundles_**, that may contain JavaScript files, stylesheets, templates, and even images. Additionally, each bundle may have meta-data such as any dependencies on other bundles. Take the following example library: | ||
``` | ||
@@ -56,38 +49,12 @@ assetLibrary/ | ||
jquery.js | ||
JQueryUI/ | ||
bundle.json = { dependencies : [ "JQuery" ] } | ||
jquery-ui.js | ||
jquery-ui.css | ||
Backbone/ | ||
bundle.json = { dependencies : [ "JQuery" ] } | ||
backbone.js | ||
Dialogs/ | ||
bundle.json = { dependencies : [ "Backbone", "JQueryUI" ] } | ||
dialogManager.coffee | ||
EditPersonDialog/ | ||
editPersonDialog.coffee | ||
editPersonDialog.scss | ||
editPersonDialog.tmpl | ||
Person/ | ||
person.coffee | ||
person.scss | ||
person.tmpl | ||
``` | ||
Now the bundle named `Dialogs/EditPersonDialog` depends on on the `Dialogs` bundle (and indirectly depends on the other three bundles) by virtue of the directory structure. | ||
Here, the Person bundle might depend on the Backbone bundle, which in turn depends on the JQuery bundle. Dependencies can be specified in `bundle.json` files that live in bundle directories themselves, or in an external bundle meta-data file. When a page requires a bundle, dependencies are automatically resolved. Setup your dependencies, require your bundles, and each page loads with the exact set of assets that it needs. | ||
### Page specific assets | ||
In addition to the assets in bundles that are required by a page, the assets that live in the same directory as a page's server side template will automatically be included when it is rendered. For example, say your page templates live in a directory named `views`, as is typical for most web frameworks. | ||
``` | ||
views/ | ||
login/ | ||
login.jade | ||
login.coffee | ||
login.scss | ||
peopleList/ | ||
peopleList.jade | ||
peopleList.coffee | ||
peopleList.scss | ||
``` | ||
When the `login.jade` template is rendered, the `login.coffee` and `login.scss` assets will automatically be injected into the HTML of the page, as will the `peopleList.*` assets when the `peopleList.jade` template is rendered. | ||
## How it works | ||
@@ -101,3 +68,3 @@ | ||
There is also a very small but important piece of logic for serving up assets and injecting them into rendered HTML, called the **_Hook_**. The Hook needs to reside in your web application framework, since it is used at the time your templates are rendered. Currently there is a Hook available only for Node.js / Express, but there is minimal logic involved and it is easy to implement in any environment. Each time you render a template, the Hook is used to look up the template in the `cartero.json` file generated by the Cartero Grunt Task, and place raw HTML into three variables that are exposed to the template: | ||
There is also a very small but important piece of logic for serving up assets and injecting them into rendered HTML, called the **_Hook_**. The Hook needs to reside in your web application framework, since it is used at the time your templates are rendered. Currently there is a Hook available only for Node.js / Express, but there is minimal logic involved and it is <a href="#hook">easy to implement</a> in any environment. Each time you render a template, the Hook is used to look up the template in the `cartero.json` file generated by the Cartero Grunt Task, and place raw HTML into three variables that are exposed to the template: | ||
@@ -110,3 +77,3 @@ `cartero_js` - the raw HTML of the `<script>` elements that load all the required JavaScript files. | ||
You may then output the contents of those variables in the appropriate places in your template just like any other template variable. For example, if you are using Jade templates, your page structure might look something like this: | ||
You may then output the contents of those variables in the appropriate places in your template just like any other template variable. For example, if you are using Jade templates, your page structure might look like this: | ||
@@ -141,16 +108,16 @@ ```jade | ||
options : { | ||
projectDir : __dirname, // the root directory of your project. All other paths in | ||
// these options are relative to this directory. | ||
projectDir : __dirname, // the root directory of your project. All other paths | ||
// in these options are relative to this directory. | ||
library : { | ||
path : "assetLibrary/" // the relative path to your Asset Library directory. | ||
path : "assetLibrary/" // the relative path to your Asset Library directory. | ||
}, | ||
views : { | ||
path : "views/", // the directoy that contains your server side view templates. | ||
viewFileExt : ".jade" // the file extension of your server side view templates. | ||
path : "views/", // the directoy containing your server side templates. | ||
viewFileExt : ".jade" // the file extension of your server side templates. | ||
} | ||
publicDir : "static/", // your app's "public" or "static" directory (into which | ||
// processed assets will ultimately be dumped). | ||
publicDir : "static/", // your app's "public" or "static" directory (into | ||
// which processed assets will ultimately be dumped). | ||
tmplExt : ".tmpl", // the file extension(s) of your client side template files. | ||
mode : "dev" // "dev" or "prod" | ||
tmplExt : ".tmpl", // the file extension(s) of your client side template. | ||
mode : "dev" // "dev" or "prod" | ||
} | ||
@@ -171,3 +138,3 @@ | ||
grunt.loadNpmTasks( "cartero" ); | ||
grunt.loadNpmTasks( "grunt-contrib-watch" ); // only needed if the `dev` mode `--watch` flag is used | ||
grunt.loadNpmTasks( "grunt-contrib-watch" ); // for `--watch` flag | ||
}; | ||
@@ -178,3 +145,3 @@ ``` | ||
Once you have configured the Cartero Grunt Task, you need to configure the Hook in your web framework. As of this writing there is only a Hook available for Node.js / Express, which is implemented as Express middleware. To install the middleware run: | ||
Once you have configured the Cartero Grunt Task, you need to configure the Hook in your web framework. As of this writing there is only a Hook available for Node.js / Express, which is implemented as Express middleware. To install it, run: | ||
@@ -458,3 +425,3 @@ npm install cartero-express-hook | ||
#### Q: Does Cartero work with Rails, PHP, etc., or just with Node.js / Express? | ||
#### <a name="hook"></a>Q: Does Cartero work with Rails, PHP, etc., or just with Node.js / Express? | ||
@@ -480,3 +447,3 @@ The heart of Cartero is an intelligent Grunt.js task, and can be used with any web framework. However, there is a small piece of logic called the Hook which must be called from your web framework, since it is used when each page is rendered. If you are interested in developing a Cartero Hook for your web framework of choice, keep reading - it's not hard. | ||
// `js`, `css`, and `tmpl` are arrays of the relative paths of the assets in this parcel. | ||
// `js`, `css`, and `tmpl` are the relative paths of the assets in this parcel. | ||
@@ -536,2 +503,5 @@ js : [ | ||
##Change Log | ||
See the [CHANGELOG.md](CHANGELOG.md) file. | ||
## About | ||
@@ -538,0 +508,0 @@ |
@@ -30,2 +30,4 @@ /* | ||
var kBundleJsonFile = "bundle.json"; | ||
var kLibraryAssetsDirPrefix = "library-assets"; | ||
@@ -190,4 +192,4 @@ var kViewAssetsDirPrefix = "view-assets"; | ||
grunt.event.on( "watch", function( action, filePath ) { | ||
//if the file is new, rebuild all the bundle stuff (if its a pageFile or bundle.json file, this is already handled by the watch ) | ||
if( ( action === "added" || action === "deleted" ) && ! isViewFile( filePath ) && ! _s.endsWith( filePath, "bundle.json" ) ) { | ||
// If its a new file, deleted file, viewFile, or a bundle.json file, need to rebuild all bundles. | ||
if( action === "added" || action === "deleted" || isViewFile( filePath ) || _s.endsWith( filePath, kBundleJsonFile ) ) { | ||
rebundle(); | ||
@@ -269,3 +271,3 @@ } | ||
// When in prod mode, need to replace relative URLs in CSS files with absolute ones because CSS file location | ||
// may change due to bundling. | ||
// may change due to bundling. This needs to happen before files are concatentated in buildParcelRegistry in prod mode. | ||
if( mode === "prod" ) { | ||
@@ -280,7 +282,8 @@ grunt.task.run( kCarteroTaskPrefix + "replaceRelativeUrlsInCssFile" ); | ||
// This task should run after everything has been copied/preprocessed into publicDir | ||
grunt.task.run( kCarteroTaskPrefix + "replaceCarteroDirTokens" ); | ||
// Builds the parcelRegistry | ||
grunt.task.run( kCarteroTaskPrefix + "buildParcelRegistry:" + mode ); | ||
grunt.task.run( kCarteroTaskPrefix + "replaceCarteroDirTokens" ); | ||
grunt.task.run( kCarteroTaskPrefix + "separateFilesToServeByType" ); | ||
@@ -321,3 +324,3 @@ | ||
files : viewFilePatterns, | ||
tasks : [ kCarteroTaskPrefix + "processViewFileChange" ], | ||
tasks : [], | ||
options : { | ||
@@ -337,5 +340,5 @@ nospawn : true | ||
files : _.map( options.library, function ( dir ) { | ||
return dir.path + "/**/bundle.json"; | ||
return dir.path + "/**/" + kBundleJsonFile; | ||
} ), | ||
tasks : [ kCarteroTaskPrefix + "processBundleJsonChange" ], | ||
tasks : [], | ||
options : { | ||
@@ -554,10 +557,2 @@ nospawn : true | ||
grunt.registerTask( kCarteroTaskPrefix + "processViewFileChange", "", function() { | ||
rebundle(); | ||
} ); | ||
grunt.registerTask( kCarteroTaskPrefix + "processBundleJsonChange", "", function() { | ||
rebundle(); | ||
} ); | ||
// Creates the assetLibrary and appPages destination directories | ||
@@ -564,0 +559,0 @@ grunt.registerTask( kCarteroTaskPrefix + "prepare", "Prepare directories for build", function() { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
77590
16
1218
505