diff --git a/lib/app/autoload/store.server.js b/lib/app/autoload/store.server.js index 6c3c9336a..02793d04f 100644 --- a/lib/app/autoload/store.server.js +++ b/lib/app/autoload/store.server.js @@ -174,6 +174,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { this._config.dir; this._config.mojitoRoot = this._config.mojitoRoot || this._libs.path.join(__dirname, '../..'); + this._jsonCache = {}; // fullPath: contents as JSON object this._ycbCache = {}; // fullPath: context: YCB config object this._routesCache = {}; // serialized context: route @@ -182,6 +183,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { this._getMojitTypeDetailsCache = {}; // env+posl+lang+mojitType: value this._expandSpecCache = {}; // env+ctx+spec: value + this._packagesVisited = {}; // package@version: path this._appRVs = []; // array of resource versions this._mojitRVs = {}; // mojitType: array of resource versions this._appResources = {}; // env: posl: array of resources @@ -877,8 +879,8 @@ YUI.add('mojito-resource-store', function(Y, NAME) { if (res.url && res.source.fs.isFile) { if (urls[res.url]) { Y.log('Url collision for ' + res.url + - '. Choosing: ' + urls[res.url].source.fs.fullPath + - ' over ' + res.source.fs.fullPath, 'warn', NAME); + '. Choosing:\n' + urls[res.url].source.fs.fullPath + + ' over\n' + res.source.fs.fullPath, 'warn', NAME); } else { urls[res.url] = res; } @@ -1093,6 +1095,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { this.selectors = {}; this._appRVs = []; this._mojitRVs = {}; + this._packagesVisited = {}; walker = new this._libs.walker.BreadthFirst(this._config.root); walker.walk(function(err, info) { @@ -1495,12 +1498,14 @@ YUI.add('mojito-resource-store', function(Y, NAME) { //console.log(selectors); //console.log(affinities); + // app-level if (!this._appResources[env]) { this._appResources[env] = {}; } this._appResources[env][poslKey] = this._resolveVersions(affinities, selectors, sourceBase, [ this._appRVs ]); + // mojit-level if (!this._mojitResources[env]) { this._mojitResources[env] = {}; } @@ -1643,7 +1648,8 @@ YUI.add('mojito-resource-store', function(Y, NAME) { */ _preloadPackage: function(info) { var dir, - pkg; + pkg, + visitKey; // FUTURE: use info.inherit to scope mojit dependencies /* console.log('--PACKAGE-- ' + info.depth + ' ' + info.pkg.name + '@' + info.pkg.version @@ -1666,6 +1672,13 @@ YUI.add('mojito-resource-store', function(Y, NAME) { if (!info.pkg.yahoo || !info.pkg.yahoo.mojito) { return; } + visitKey = [info.pkg.name, info.pkg.version].join('@'); + if (this._packagesVisited[visitKey]) { + Y.log('skipping duplicate package ' + visitKey + '\nskipping ' + + info.dir + '\nprev used ' + this._packagesVisited[visitKey], 'info', NAME); + return; + } + switch (info.pkg.yahoo.mojito.type) { case 'bundle': dir = this._libs.path.join(info.dir, info.pkg.yahoo.mojito.location); @@ -1679,6 +1692,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { Y.log('Unknown package type "' + info.pkg.yahoo.mojito.type + '"', 'warn', NAME); break; } + this._packagesVisited[visitKey] = info.dir; }, diff --git a/tests/fixtures/packages/node_modules/b/node_modules/a/package.json b/tests/fixtures/packages/node_modules/b/node_modules/a/package.json new file mode 100644 index 000000000..a7be100b9 --- /dev/null +++ b/tests/fixtures/packages/node_modules/b/node_modules/a/package.json @@ -0,0 +1,20 @@ +{ + "name": "a", + "version": "666.1.0", + "description": "root package", + "author": "Team Mojito (http://developer.yahoo.com/cocktails/mojito)", + "dependencies": {}, + "main": ".", + "engines": { + "node": ">= 0.4.0 < 0.7.0", + "npm": ">= 1.0.0" + }, + "devDependencies": {}, + "yahoo": { + "mojito": { + "version": "*", + "type": "mojit", + "location": "foo" + } + } +} diff --git a/tests/unit/lib/app/autoload/test-package-walker.server.js b/tests/unit/lib/app/autoload/test-package-walker.server.js index c6f82b0f8..645914351 100644 --- a/tests/unit/lib/app/autoload/test-package-walker.server.js +++ b/tests/unit/lib/app/autoload/test-package-walker.server.js @@ -21,6 +21,7 @@ YUI().use('test', function(Y) { 'breadth first basics': function() { var order = []; var walker = new libwalker.BreadthFirst(fixtures); + var whichPkgA = 0; walker.walk(function(err, info) { order.push(info.pkg.name + '@' + info.pkg.version); switch(info.pkg.name) { @@ -32,13 +33,20 @@ YUI().use('test', function(Y) { break; case 'a': - A.areSame(1, info.depth); + whichPkgA += 1; A.areSame('666.1.0', info.pkg.version); - AA.itemsAreSame(['root'], info.parents); - A.areSame(libpath.join(fixtures,'node_modules/a'), info.dir); + if (1 === whichPkgA) { + A.areSame(1, info.depth, 'a depth'); + AA.itemsAreSame(['root'], info.parents); + A.areSame(libpath.join(fixtures,'node_modules/a'), info.dir); + } else { + A.areSame(2, info.depth, 'a depth'); + AA.itemsAreSame(['b', 'root'], info.parents); + A.areSame(libpath.join(fixtures,'node_modules/b/node_modules/a'), info.dir); + } break; case 'aa': - A.areSame(2, info.depth); + A.areSame(2, info.depth, 'a depth'); A.areSame('666.1.1', info.pkg.version); AA.itemsAreSame(['a','root'], info.parents); A.areSame(libpath.join(fixtures,'node_modules/a/node_modules/aa'), info.dir); @@ -102,6 +110,7 @@ YUI().use('test', function(Y) { 'c@999.999.999', 'aa@666.1.1', 'ab@666.1.2', + 'a@666.1.0', 'ba@666.2.1', 'bb@666.2.2', //'ca@999.999.999', diff --git a/tests/unit/lib/app/autoload/test-store.server.js b/tests/unit/lib/app/autoload/test-store.server.js index e0d9a68cb..b33583cb9 100644 --- a/tests/unit/lib/app/autoload/test-store.server.js +++ b/tests/unit/lib/app/autoload/test-store.server.js @@ -520,6 +520,26 @@ YUI().use( A.areSame('a', details.controller); }, + + 'skip loaded packages': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/packages'), + store = new Y.mojito.ResourceStore({ root: fixtures }); + store.preload(); + var oldlog = Y.log; + var logged = false; + Y.log = function(msg, lvl, src) { + if ('info' === lvl && 'mojito-resource-store' === src && msg.match(/^skipping duplicate package a@666\.1\.0/)) { + logged = true; + } + }; + try { + store.preload(); + } finally { + Y.log = oldlog; + } + A.isTrue(logged, 'info logged'); + }, + 'find and parse resources by convention': function() { var fixtures = libpath.join(__dirname, '../../../../fixtures/conventions'), store = new Y.mojito.ResourceStore({ root: fixtures });