Browse files

Initial commit. Sadly incomplete code

  • Loading branch information...
0 parents commit 61437eaeb32c5c1ad9b6c343d3b82cbc9abe5a84 @CharlotteGore committed May 8, 2012
2 .gitignore
@@ -0,0 +1,2 @@
+node_modules
+.DS_Store
22 LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2012 Charlotte Gore
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
5 Makefile
@@ -0,0 +1,5 @@
+test:
+ ./node_modules/.bin/mocha \
+ --reporter list
+
+.PHONY: test
3 README.md
@@ -0,0 +1,3 @@
+#Cassette Express
+
+WIP. Sadly incomplete and non-functional.
422 app.js
@@ -0,0 +1,422 @@
+var fs = require('fs'),
+ base = require('base-framework'),
+ _ = require('underscore'),
+ gatherer = require('gatherer');
+
+var assetsPath = process.env.PWD + '/public/javascripts/';
+
+var filesIndex = {};
+var bundles = {};
+
+var traverseBundle = function(bundle, currentPath){
+
+ var files = fs.readdirSync(assetsPath + currentPath);
+
+ _.each(files, function(file){
+
+ var currentFile = assetsPath + currentPath + '/' + file,
+ stat = fs.statSync(currentFile),
+ src = '',
+ lines = [],
+ fails = 0,
+ index = 0,
+ matches = false,
+ dependency;
+
+ if(stat.isFile()){
+
+ if(/\.js$/.test(file)){
+
+ var asset = {
+ src : fs.readFileSync(currentFile, 'utf8'),
+ dependencies : {
+ bundles : [],
+ files : []
+ },
+ stat : stat,
+ name : file,
+ path : currentPath + '/'
+
+ }
+
+ lines = asset.src.split(/\n/g)
+
+ asset.src = '';
+
+ // we allow a couple of failures before giving up...
+ while(fails < 2){
+
+ matches = lines[index].match(/([\s]+|)\/\/\/([\s]+|)<reference path="([\.\/a-zA-Z0-9_\-]+)" \/>([\s]+|)/);
+
+ if(matches){
+
+ // we convert relative paths or bundle paths to a real paths
+ dependency = fs.realpathSync(assetsPath + asset.path + matches[3]).replace(assetsPath, '');
+
+ if(/\.js$/.test(dependency)){
+
+ asset.dependencies.files.push( dependency );
+
+ }else{
+
+ asset.dependencies.bundles.push( dependency );
+
+ }
+
+ }else{
+
+ fails++;
+
+ }
+
+ index++;
+
+ }
+
+ filesIndex[asset.path + asset.name] = asset;
+
+ bundle.files.push( filesIndex[asset.path + asset.name] );
+
+ }
+
+
+ }else if(stat.isDirectory()){
+
+ traverseBundle(bundle, currentPath + '/' + file);
+
+ }
+
+ })
+
+ return;
+
+};
+
+var processBundle = function(bundleName){
+
+ var required = {
+ files : {},
+ bundles : {},
+ assembly : []
+ };
+
+ var bundle = bundles[bundleName];
+
+ var noFileDependencies = [];
+
+ var followDependencies = function(asset){
+ // bundles
+ _.each(asset.dependencies.bundles, function(depBundle, index){
+
+ if(bundles[depBundle]){
+
+ required.bundles[depBundle] = bundles[depBundle]; // not yet satisfied
+
+ }else{
+
+ throw(bundleName + ' depends on ' + depBundle + ' which doesnt exist');
+
+ }
+
+ });
+
+ _.each(asset.dependencies.files, function(file, index){
+
+ required.files[file] = filesIndex[file];
+
+ followDependencies(filesIndex[file]);
+
+ });
+
+ };
+
+ _.each(bundle.files, function(asset){
+ required.files[asset.path + asset.name] = asset;
+ followDependencies(asset);
+
+
+ });
+
+ bundles[bundleName].files = required.files;
+ bundles[bundleName].bundles = required.bundles;
+ bundles[bundleName].processed = true;
+
+
+ return;
+
+};
+
+
+var cassette = base.createChild().addInstanceMethods({
+
+ setAssetsPath : function( path ){
+
+ // an absolute path for
+ assetsPath = path;
+
+ return this;
+
+ },
+
+ createBundle : function( path ){
+
+ /* lib/sds/ */
+ var bundleName = path;
+
+ bundles[bundleName] = {
+ files : []
+ };
+
+ traverseBundle(bundles[bundleName], path);
+
+ return this;
+
+ },
+
+ createBundlePerSubDirectory : function( path ){
+
+ var listing = fs.readdirSync(assetsPath + path),
+ self = this;
+
+ _.each(listing, function(item){
+
+ var stat = fs.statSync(assetsPath + path + "/" + item);
+
+ if(stat.isDirectory()){
+
+ self.createBundle(path + "/" + item);
+
+ }
+
+ });
+
+ return this;
+
+ },
+
+ getBundles : function(){
+
+ return bundles;
+
+ },
+
+ getFilesIndex : function(){
+
+ return filesIndex;
+
+ },
+
+ getBundle : function(bundleName, callback){
+
+ var unsorted = {}, assembly = [], noDependencies = [], d = 0;
+
+ var time = (new Date()).getTime();
+
+ var getExternalBundles = function( name ){
+
+ if(!bundles[ name ].processed){
+
+ processBundle( name );
+
+ }
+
+ _.each(bundles[ name ].files, function(file){
+
+ unsorted[file.path + file.name] = file;
+
+ })
+
+ if(!_.isEmpty(bundles[ name ].bundles)){
+
+ _.each(bundles[name].bundles, function(bundle, index){
+
+ getExternalBundles(index);
+
+
+ });
+
+ }
+
+ };
+
+ var collapseTheBundles = function(){
+
+ _.each(unsorted, function(asset, name){
+
+ if(!_.isEmpty(asset.dependencies.bundles)){
+
+ _.each(asset.dependencies.bundles, function(bundle){
+
+ _.each(bundles[bundle].files, function(file){
+
+ asset.dependencies.files.push( file.path + file.name );
+
+ })
+
+ })
+
+ }
+
+ _.each(asset.dependencies.files, function(file, index){
+
+ asset.dependencies.files[index] = filesIndex[file];
+
+ })
+
+ });
+
+ };
+
+ if(bundles[bundleName]){
+
+ if(!bundles[bundleName].assembly){
+
+ getExternalBundles(bundleName);
+ collapseTheBundles();
+ bundles[bundleName].assembly = sortBundle( unsorted );
+ }
+
+ callback({assemble : bundles[bundleName].assembly, duration: (new Date()).getTime() - time});
+
+
+ }else{
+
+ throw('no such bundle');
+
+ }
+
+ }
+
+});
+
+var sortBundle = function( data ){
+
+ var noDependencies = [], assembly =[], unsorted = [], n;
+
+ // convert to an array...
+ _.each(data, function( asset ){
+
+ unsorted.push( asset );
+
+ });
+
+ var findAssetsWithoutDependencies = function(){
+
+ //noDependencies = [];
+
+ _.each(unsorted, function(asset, index){
+
+ if(_.isEmpty(asset.dependencies.files)){
+
+ noDependencies.push(asset);
+ unsorted.splice(index, 1);
+
+ }
+
+ });
+
+ };
+
+ findAssetsWithoutDependencies();
+
+ while(noDependencies.length > 0){
+
+ n = noDependencies.splice(noDependencies.length -1, 1)[0];
+
+ assembly.push(n);
+
+ _.each(unsorted, function(asset, i){
+
+ _.each(asset.dependencies.files, function(file, index){
+
+ //console.log(file);
+
+ if( (n.path + n.name) === (file.path + file.name) ){
+ asset.dependencies.files.splice(index, 1);
+
+ if(asset.dependencies.files.length < 2){
+
+ }
+ }
+
+ });
+
+ })
+
+ findAssetsWithoutDependencies();
+ //console.log('nodep length ' + noDependencies.length);
+
+
+
+ }
+
+ if(unsorted.length > 0){
+
+ throw('unable to resolve dependencies!');
+
+ }else{
+
+ return assembly;
+
+ }
+
+}
+
+
+exports = module.exports = cassette;
+
+/*
+
+
+
+
+
+then... inside Jade..
+
+var bundles = require('cassette-express');
+
+bundles
+ .setAssetsPath( process.env.PWD + '/scripts') // by default it's the express /public/javascripts
+ .createBundle('lib/sds') // sucks in everything, including subfolders
+ .createBundlePerSubDirectory('lib/nice') // turns the sub directorys into bundles..
+ -> is shortcut for
+ .createBundle('nice/nice/nice')
+ .createBundle('nice/nice/ui')
+ .createBundle('nice/nice/app')
+
+add.set('view options', {
+
+ bundles : bundles.middleware('debug')
+
+})
+
+OR
+
+add.set('view options', {
+
+ bundles : bundles.middleware('production')
+
+})
+
+then, inside the template...
+
+!= bundles.useBundle('lib/sds')
+
+.. which will output
+
+<scripts type='text/javascript' src='one for every source file, in order'></scripts>
+
+
+horribly this means converting
+
+../lib/nice/ui
+
+to
+
+~/lib/nice/ui
+
+or ../../sds.js
+
+assetsPath + 'sds/Features/ToolDrawers/Tools'
+
+*/
0 lib/assembly.js
No changes.
111 lib/asset.js
@@ -0,0 +1,111 @@
+var base = require('base-framework'),
+ _ = require('underscore'),
+ parser = require('./referenceParser.js')(),
+ fs = require('fs');
+
+var assetsPath;
+
+var Asset = base.createChild().addInstanceMethods({
+
+ init : function(name, path, assets){
+
+ this.name = path + '/' + name;
+
+ this.path = path + '/';
+
+ assetsPath = assets;
+
+ this.status = {};
+
+ this.dependencies = {
+ files : [],
+ bundles : []
+ };
+
+ this.hasChanged();
+ this.getData();
+ this.parseReferences();
+
+ return this;
+
+ },
+
+ addFileDependency : function( dependency ){
+
+ this.dependencies.files.push( dependency );
+
+ return this;
+
+ },
+
+ addBundleDependency : function( dependency ){
+
+ this.dependencies.bundles.push( dependency );
+
+ return this;
+
+ },
+
+ fileDependencies : function(){
+
+ return this.dependencies.files.slice(0);
+
+ },
+
+ bundleDependencies : function(){
+
+ return this.dependencies.bundles.slice(0);
+
+ },
+
+ parseReferences : function(){
+
+ var refs = parser.getReferences(this.data),
+ dependency;
+
+ _.each(refs, function( ref ){
+
+ dependency = fs.realpathSync(assetsPath + this.path + ref).replace(assetsPath, '');
+
+ if(/\.js$/.test(dependency)){
+
+ this.addFileDependency( dependency );
+
+ } else {
+
+ this.addBundleDependency( dependency );
+
+ }
+
+ }, this);
+
+ return this;
+
+ },
+
+ hasChanged : function(){
+
+ var status = fs.statSync(assetsPath + this.name);
+
+ if(_.isEqual(status, this.status)){
+
+ return false;
+
+ }else{
+
+ this.status = status;
+ return true;
+
+ }
+
+ },
+
+ getData : function( callback ){
+
+ this.data = fs.readFileSync(assetsPath + this.name, 'utf8', callback);
+
+ }
+
+});
+
+exports = module.exports = Asset;
171 lib/cassette.js
@@ -0,0 +1,171 @@
+var base = require('base-framework'),
+ _ = require('underscore'),
+ Manifest = require('./manifest.js');
+
+// some defaults for an Express environment.
+var assetsPath = process.env.PWD + '/public/javascripts/';
+var outputPath = '/javascripts/';
+
+
+var cassette = base.createChild().addInstanceMethods({
+
+ init : function( config ){
+
+ if(config.assetsPath){
+
+ assetsPath = config.assetsPath;
+
+ }
+
+ if(config.outputPath){
+
+ outputPath = config.outputPath;
+
+ }
+
+ if(config.mode){
+
+ this.mode = config.mode;
+
+ }else{
+
+ this.mode = 'debug';
+
+ }
+
+ this.manifest = Manifest(assetsPath);
+
+ return this;
+
+ },
+
+ getAsset : function( asset ){
+
+ if(!this.assemblies[asset]){
+
+ this.assemblies[asset] = Assembly( asset, this.manifest );
+
+ }else{
+
+ if(this.manifest.assetsChanged()){
+
+ this.assemblies[asset].reassemble();
+
+ }
+
+ return this.assemblies[asset].bundle();
+
+ }
+ /*
+ var unsorted = {}, assembly = [], noDependencies = [], d = 0;
+
+ var time = (new Date()).getTime();
+
+ var getExternalBundles = function( name ){
+
+ if(!bundles[ name ].processed){
+
+ processBundle( name );
+
+ }
+
+ _.each(bundles[ name ].files, function(file){
+
+ unsorted[file.path + file.name] = file;
+
+ })
+
+ if(!_.isEmpty(bundles[ name ].bundles)){
+
+ _.each(bundles[name].bundles, function(bundle, index){
+
+ getExternalBundles(index);
+
+
+ });
+
+ }
+
+ };
+
+ var collapseTheBundles = function(){
+
+ _.each(unsorted, function(asset, name){
+
+ if(!_.isEmpty(asset.dependencies.bundles)){
+
+ _.each(asset.dependencies.bundles, function(bundle){
+
+ _.each(bundles[bundle].files, function(file){
+
+ asset.dependencies.files.push( file.path + file.name );
+
+ })
+
+ })
+
+ }
+
+ _.each(asset.dependencies.files, function(file, index){
+
+ asset.dependencies.files[index] = file;
+
+ })
+
+ });
+
+ };
+
+
+
+ if(bundles[bundleName]){
+
+ if(!bundles[bundleName].assembly){
+
+ getExternalBundles(bundleName);
+ collapseTheBundles();
+ bundles[bundleName].assembly = sortBundle( unsorted );
+ }
+
+ return bundles[bundleName].assembly;
+
+
+ }else{
+
+ throw('no such bundle');
+
+ }
+
+ */
+
+ },
+
+ middleware : function(){
+
+ var self = this;
+
+ return {
+
+ useAsset : function( asset ){
+
+ var manifest = self.getAsset( asset );
+ var str = '';
+
+ _.each(manifest, function(item){
+
+ str+="<script type='text/javascript' src='" + outputPath + item.name + "'></script>";
+
+ });
+
+ return str;
+
+ }
+
+ };
+
+ }
+
+});
+
+
+exports = module.exports = cassette;
104 lib/manifest.js
@@ -0,0 +1,104 @@
+var base = require('base-framework'),
+ fs = require('fs'),
+ _ = require('underscore'),
+ Asset = require('./asset.js'),
+ Manifest;
+
+// we keep a reference to all the bundles and all the files.
+// this is so that bundles can satisfy
+var bundleIndex = {};
+var assetIndex = {};
+
+// Here is the Bundle object.
+var Manifest = base.createChild().addInstanceMethods({
+
+ init : function( assetsPath ){
+
+ this.assetsPath = assetsPath;
+
+ // these repre
+ this.assets = [];
+
+ this.bundleDependencies = [];
+ this.externalFileDependencies = [];
+
+ this.scanDirectory('');
+
+ return this;
+
+ },
+
+ // the the files
+ scanDirectory : function( currentPath ){
+
+ var files = fs.readdirSync(this.assetsPath + currentPath);
+
+ if(!bundleIndex[currentPath]){
+
+ bundleIndex[currentPath] = {
+ files : [],
+ bundles : [],
+ };
+
+ };
+
+ _.each(files, function(file){
+
+ var currentFile = this.assetsPath + currentPath + '/' + file,
+ stat = fs.statSync(currentFile),
+ src = '',
+ dependency;
+
+ if(stat.isFile()){
+
+ if(/\.js$/.test(file)){
+
+ var asset = Asset(file, currentPath, this.assetsPath );
+
+ assetIndex[ asset.name ] = asset;
+ bundleIndex[ currentPath ].files.push( asset.name );
+
+ }
+
+
+ }else if(stat.isDirectory()){
+
+ bundleIndex[currentPath].bundles.push(currentPath + file);
+ this.scanDirectory(currentPath + '/' + file);
+
+ }
+
+
+ }, this);
+
+ return this;
+
+ },
+
+ assetsChanged : function(){
+
+ // STUB: go through all the assets/directories, detect changes, refresh if necessary.
+
+ return false;
+
+ },
+
+ getAsset : function( key ){
+
+ // STUB :
+
+ // if it's a directory we return all the files in the directory and subfolders
+
+ // if it's a single file, we return [ single file ]
+
+
+ },
+
+ query : function(){
+
+ return bundleIndex;
+
+ }
+});
+
+exports = module.exports = Manifest;
73 lib/referenceParser.js
@@ -0,0 +1,73 @@
+var parser = require('base-framework').createChild(),
+ _ = require('underscore'),
+
+ xmlRegex = /([\s]+|)\/\/\/([\s]+|)<reference path="([\.\/a-zA-Z0-9_\-]+)" \/>([\s]+|)/,
+ jsRegex = /^([\s]+|)\/\/([\s]+|)@reference\s([\'\"\.\/a-zA-Z0-9_\-\s]+)$/;
+
+
+parser.addInstanceMethods({
+
+ init : function( fails ){ // undocumented feature! You can specify how many non-reference lines the parser will tolerate before giving up.
+
+ if(typeof fails !== 'undefined'){
+
+ this.maxFails = fails;
+
+ } else {
+
+ this.maxFails = 2;
+
+ }
+
+ return this;
+
+ },
+
+ getReferences : function( src ){
+
+ var lines = src.split(/\n/g),
+ references = [],
+ matches,
+ fails = 0,
+ index = 0;
+
+ while(fails < this.maxFails && typeof lines[index] === 'string'){
+
+ matches = (lines[index].match(jsRegex));
+
+ if(!matches){
+
+ matches = (lines[index].match(xmlRegex));
+
+
+ }
+
+ if(matches){
+
+ _.each(matches[3].split(/\s/g), function(ref){
+
+ if(ref!==''){
+ references.push( (ref.replace(/\'/g, '')).replace(/\"/g, '') );
+ }
+
+ });
+
+
+
+ } else {
+
+ fails++;
+
+ }
+
+ index++;
+
+ }
+
+ return references;
+
+ }
+
+})
+
+exports = module.exports = parser;
113 lib/sorter.js
@@ -0,0 +1,113 @@
+var base = require('base-framework'),
+ _ = require('underscore'),
+
+
+ /*
+
+ usage: sorter = require('./lib/sorter.js');
+
+ sorter( CollectionToBeSorted, iterator )
+
+ the iterator will have each item in CollectionToBeSorted passed to it.
+ It must return an object containing a unique key for that item, and an array of dependencies..
+ these should be the keys of other objects.
+
+ */
+
+ sorter = base.createChild().addInstanceMethods({
+
+ sort : function( collection, iterator ){
+
+ this.unsorted = [];
+ this.result = [];
+ this.dependenciesMet = [];
+
+ var sortedCollection = [],
+ node;
+
+ // initial pass. Using the iterator, create an array of sortable objects.
+ _.each(collection, function( item ){
+
+ var node = iterator(item);
+
+ if(!node.key && !_.isArray(node.dependsOn)){
+
+ throw new Error('Iterator for sorter does not have key or array of dependencies');
+
+ }else {
+
+ node.origin = item;
+ this.unsorted.push( node );
+
+ }
+
+ }, this);
+
+ // do an initial search for items without dependencies.
+
+ this.getItemsWithoutDependencies();
+
+ while(this.dependenciesMet.length > 0){
+
+ node = this.dependenciesMet.splice(-1)[0];
+ this.result.push(node);
+
+ this.removeDependencyFromItems( node.key );
+ this.getItemsWithoutDependencies();
+
+ }
+
+ if(this.unsorted.length > 0){
+
+ throw new Error('Cyclical dependencies. Sort failed.')
+
+
+ }else{
+
+ _.each(this.result, function( node ){
+
+ sortedCollection.push( node.origin );
+
+ });
+
+ return sortedCollection;
+
+ }
+
+ },
+
+ getItemsWithoutDependencies : function(){
+
+ _.each(this.unsorted, function( item, index ){
+ // if there's no dependencies left..
+ if(_.isEmpty(item.dependsOn)){
+ // move item into the depencies met list
+ this.dependenciesMet.push( this.unsorted.splice(index, 1)[0] );
+ }
+
+ }, this);
+
+ },
+
+ removeDependencyFromItems : function( key ){
+
+ _.each(this.unsorted, function( item ){
+
+ _.each(item.dependsOn, function( dependency, index ){
+
+ if(dependency === key){
+
+ item.dependsOn.splice(index, 1);
+
+ }
+
+ }, this);
+
+
+ }, this);
+
+ },
+
+ });
+
+ exports = module.exports = sorter;
47 package.json
@@ -0,0 +1,47 @@
+{
+ "name": "cassette-express",
+ "description": "A partial implementation of Andrew Davey's C# Asset Manager for Node and Express",
+ "keywords": [
+ "javascript",
+ "asset management",
+ ""
+ ],
+ "version": "0.0.1",
+ "homepage": "https://CharlotteGore@github.com/CharlotteGore/Cassette.git",
+ "author": {
+ "name": "Charlotte Gore",
+ "email": "conspiracygore@gmail.com",
+ "url": "http://charlottegore.com/"
+ },
+ "contributors": [
+ {
+ "name": "Charlotte Gore",
+ "email": "conspiracygore@gmail.com",
+ "url": "http://charlottegore.com/"
+ }
+ ],
+ "dependencies": {
+ "gatherer" : "x",
+ "base-framework": "1.0.x",
+ "underscore" : "1.3.x"
+ },
+ "devDependencies": {
+ "mocha": "*",
+ "should": "*"
+ },
+ "main": "lib/cassette",
+ "directories": {
+ "lib": "lib"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/CharlotteGore/Cassette.git"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "_npmUser": {
+ "name": "CharlotteGore",
+ "email": "conspiracygore@gmail.com"
+ }
+}
26 test/cassette.js
@@ -0,0 +1,26 @@
+ var should = require('should'),
+ cassette = require('../lib/cassette.js')
+
+
+ describe('Using cassette to manage javascript assets',function(){
+
+ describe('when creating bundles', function(){
+
+ it('can create a manifest', function(){
+
+ var bundles = cassette({ assetsPath : process.env.PWD + '/test/examples' });
+
+ bundles.should.have.property('manifest');
+ var manifest = bundles.manifest.query()
+
+ manifest.should.have.property('');
+ manifest.should.have.property('/CircularFileRefs');
+ manifest.should.have.property('/NonCircularBundleRefs');
+ //bundles.getBundles().should.have.property('NonCircularFileRefs');
+
+ });
+
+ });
+
+ });
+
3 test/examples/CircularBundleRefs/A/a.js
@@ -0,0 +1,3 @@
+// @reference ../B
+
+var a = b;
1 test/examples/CircularBundleRefs/B/b.js
@@ -0,0 +1 @@
+// @reference ../A
3 test/examples/CircularFileRefs/a.js
@@ -0,0 +1,3 @@
+// @reference b.js
+
+var a = b + 10;
3 test/examples/CircularFileRefs/b.js
@@ -0,0 +1,3 @@
+// @reference a.js
+
+var b = a + 10;
3 test/examples/NonCircularBundleRefs/A/a.js
@@ -0,0 +1,3 @@
+// @reference ../B
+
+var a = b;
3 test/examples/NonCircularBundleRefs/B/b.js
@@ -0,0 +1,3 @@
+// @reference ../C
+
+var b = c;
1 test/examples/NonCircularBundleRefs/C/c.js
@@ -0,0 +1 @@
+var c = 10;
3 test/examples/NonCircularFileRefs/a.js
@@ -0,0 +1,3 @@
+// @reference b.js
+
+var a = b + 10;
3 test/examples/NonCircularFileRefs/b.js
@@ -0,0 +1,3 @@
+// @reference c.js
+
+var b = c + 10;
2 test/examples/NonCircularFileRefs/c.js
@@ -0,0 +1,2 @@
+
+var c = 10;
77 test/parser.js
@@ -0,0 +1,77 @@
+ var should = require('should');
+
+
+ describe('using the reference parser', function(){
+
+ var parser = require('../lib/referenceParser.js')();
+
+
+ describe('with javascript style references', function(){
+
+ it('can get a single reference from a single line', function(){
+
+ var references = parser.getReferences('// @reference other.js');
+
+ references.should.be.an.instanceOf(Array);
+ references.should.eql(['other.js']);
+
+ });
+
+ it('can get multiple references on a single line', function(){
+
+ var references = parser.getReferences('// @reference other.js lib/jquery lib/underscore');
+
+ references.should.eql(['other.js','lib/jquery','lib/underscore']);
+
+
+ });
+
+ it('can get multiple references on multiple lines', function(){
+
+ var references = parser.getReferences('// @reference other.js\n // @reference lib/jquery lib/base \n// @reference lib/underscore\n');
+
+ references.should.eql(['other.js','lib/jquery','lib/base','lib/underscore']);
+
+ });
+
+ it('can get references wrapped in single and double quotes', function(){
+
+ var references = parser.getReferences('// @reference other.js\n // @reference \'lib/jquery\' lib/base \n// @reference "lib/underscore"\n');
+
+ references.should.eql(['other.js','lib/jquery','lib/base','lib/underscore']);
+
+ });
+
+ it('returns no results when there are no references', function(){
+
+ var references = parser.getReferences(' // Hello im just a normal comment\n//I have no references\n//Gee, I hope this doesnt break\n');
+
+ references.should.eql([]);
+
+ });
+
+ });
+
+ describe('with xml style references', function(){
+
+ it('can get a single reference from a single line', function(){
+
+ var references = parser.getReferences('/// <reference path="other.js" />');
+
+ references.should.be.an.instanceOf(Array);
+ references.should.eql(['other.js']);
+
+ });
+
+ it('can get references on multiple lines', function(){
+
+ var references = parser.getReferences('/// <reference path="other.js" /> \n/// <reference path="lib/base" />\n/// <reference path="lib/jquery" />\n\n /// <reference path="lib/underscore" /> \n');
+
+ references.should.eql(['other.js','lib/base','lib/jquery','lib/underscore']);
+
+ });
+
+
+ });
+
+ })
72 test/sorter.js
@@ -0,0 +1,72 @@
+ var should = require('should');
+
+ var sorter = require('../lib/sorter.js');
+
+ var nonCyclic = {
+ 'a' : {
+ name : 'a',
+ dependencies : ['b', 'c', 'd']
+
+ },
+ 'b' : {
+ name : 'b',
+ dependencies : ['c', 'd']
+ },
+ 'c' : {
+ name : 'c',
+ dependencies : ['d']
+ },
+ 'd' : {
+ name : 'd',
+ dependencies : []
+ }
+
+ };
+
+ var cyclic = {
+ 'a' : {
+ name : 'a',
+ dependencies : ['b', 'c', 'd']
+
+ },
+ 'b' : {
+ name : 'b',
+ dependencies : ['c', 'd']
+ },
+ 'c' : {
+ name : 'c',
+ dependencies : ['d']
+ },
+ 'd' : {
+ name : 'd',
+ dependencies : ['a']
+ }
+
+ };
+
+ describe('the sorting algorithm', function(){
+
+ describe('using an object collection', function(){
+
+ it('can resolve dependencies', function(){
+
+ var sorted = sorter().sort(nonCyclic, function( item ){
+ return {
+ key : item.name,
+ dependsOn : item.dependencies.slice(0)
+ };
+
+ });
+
+ sorted.should.have.length(4);
+ sorted[0].should.equal(nonCyclic['d']);
+ sorted[1].should.equal(nonCyclic['c']);
+ sorted[2].should.equal(nonCyclic['b']);
+ sorted[3].should.equal(nonCyclic['a']);
+
+ });
+
+
+ })
+
+ })

0 comments on commit 61437ea

Please sign in to comment.