diff --git a/lib/core/Project.js b/lib/core/Project.js index 071768cb..1ed55eb7 100644 --- a/lib/core/Project.js +++ b/lib/core/Project.js @@ -6,13 +6,14 @@ var semver = require('semver'); var mout = require('mout'); var rimraf = require('rimraf'); var promptly = require('promptly'); -var bowerJson = require('bower-json'); var endpointParser = require('bower-endpoint-parser'); var Logger = require('bower-logger'); var Manager = require('./Manager'); var defaultConfig = require('../config'); var md5 = require('../util/md5'); var createError = require('../util/createError'); +var readJson = require('../util/readJson'); +var validLink = require('../util/validLink'); function Project(config, logger) { // This is the only architecture component that ensures defaults @@ -309,43 +310,6 @@ Project.prototype.uninstall = function (names, options) { }); }; -Project.prototype.hasJson = function () { - return this._readJson() - .then(function (json) { - return json ? this._jsonFile : false; - }.bind(this)); -}; - -Project.prototype.getJson = function () { - return this._readJson(); -}; - -Project.prototype.saveJson = function (forceCreate) { - var file; - var jsonStr = JSON.stringify(this._json, null, ' ') + '\n'; - var jsonHash = md5(jsonStr); - - // Save only if there's something different - if (jsonHash === this._jsonHash) { - return Q.resolve(); - } - - // Error out if the json file does not exist, unless force create - // is true - if (!this._jsonFile && !forceCreate) { - this._logger.warn('no-json', 'No bower.json file to save to'); - return Q.resolve(); - } - - file = this._jsonFile || path.join(this._config.cwd, 'bower.json'); - return Q.nfcall(fs.writeFile, file, jsonStr) - .then(function () { - this._jsonHash = jsonHash; - this._jsonFile = file; - return this._json; - }.bind(this)); -}; - Project.prototype.getTree = function () { return this._analyse() .spread(function (json, tree, flattened) { @@ -425,6 +389,43 @@ Project.prototype.walkTree = function (node, fn, onlyOnce) { } }; +Project.prototype.saveJson = function (forceCreate) { + var file; + var jsonStr = JSON.stringify(this._json, null, ' ') + '\n'; + var jsonHash = md5(jsonStr); + + // Save only if there's something different + if (jsonHash === this._jsonHash) { + return Q.resolve(); + } + + // Error out if the json file does not exist, unless force create + // is true + if (!this._jsonFile && !forceCreate) { + this._logger.warn('no-json', 'No bower.json file to save to'); + return Q.resolve(); + } + + file = this._jsonFile || path.join(this._config.cwd, 'bower.json'); + return Q.nfcall(fs.writeFile, file, jsonStr) + .then(function () { + this._jsonHash = jsonHash; + this._jsonFile = file; + return this._json; + }.bind(this)); +}; + +Project.prototype.hasJson = function () { + return this._readJson() + .then(function (json) { + return json ? this._jsonFile : false; + }.bind(this)); +}; + +Project.prototype.getJson = function () { + return this._readJson(); +}; + Project.prototype.getManager = function () { return this._manager; }; @@ -515,37 +516,17 @@ Project.prototype._readJson = function () { } // Read local json - return this._json = Q.nfcall(bowerJson.find, this._config.cwd) - .then(function (filename) { - // If it is a component.json, warn about the deprecation - if (path.basename(filename) === 'component.json') { - process.nextTick(function () { - that._logger.warn('deprecated', 'You are using the deprecated component.json file', { - json: filename - }); - }); + return this._json = readJson(this._config.cwd, { + name: path.basename(this._config.cwd) || 'root' + }) + .spread(function (json, deprecated) { + var jsonStr; + + if (deprecated) { + that._logger.warn('deprecated', 'You are using the deprecated ' + deprecated + ' file'); } - that._jsonFile = filename; - - // Read it - return Q.nfcall(bowerJson.read, filename) - .fail(function (err) { - throw createError('Something went wrong while reading ' + filename, err.code, { - details: err.message, - data: { - filename: filename - } - }); - }); - }, function () { - // No json file was found, assume one - return Q.nfcall(bowerJson.parse, { - name: path.basename(that._config.cwd) || 'root' - }); - }) - .then(function (json) { - var jsonStr = JSON.stringify(json, null, ' ') + '\n'; + jsonStr = JSON.stringify(json, null, ' ') + '\n'; that._jsonHash = md5(jsonStr); return that._json = json; }); @@ -576,8 +557,8 @@ Project.prototype._readInstalled = function () { var metaFile = path.join(componentsDir, filename); // Read package metadata - return Q.nfcall(bowerJson.read, metaFile) - .then(function (pkgMeta) { + return readJson(metaFile) + .spread(function (pkgMeta) { decEndpoints[name] = { name: name, source: pkgMeta._source, @@ -585,8 +566,7 @@ Project.prototype._readInstalled = function () { canonicalDir: path.dirname(metaFile), pkgMeta: pkgMeta }; - // Ignore if failed to read file - }, function () {}); + }); }); // Wait until all files have been read @@ -600,6 +580,7 @@ Project.prototype._readInstalled = function () { Project.prototype._readLinks = function () { var componentsDir; + var that = this; // Read directory, looking for links componentsDir = path.join(this._config.cwd, this._config.directory); @@ -608,44 +589,41 @@ Project.prototype._readLinks = function () { var promises; var decEndpoints = {}; - // Filter only those that are links promises = filenames.map(function (filename) { var dir = path.join(componentsDir, filename); - return Q.nfcall(fs.lstat, dir) - .then(function (stat) { - if (stat.isSymbolicLink()) { - return Q.nfcall(bowerJson.find, dir) - .then(function (jsonFile) { - return Q.nfcall(bowerJson.read, jsonFile) - .then(function (pkgMeta) { - var name = path.basename(filename); + // Filter only those that are valid links + return validLink(dir) + .spread(function (valid, err) { + var name; - decEndpoints[name] = { - name: name, - source: dir, - target: '*', - canonicalDir: dir, - pkgMeta: pkgMeta, - linked: true - }; - }) - .fail(function (err) { - throw createError('Failed to read ' + filename, err.code, { - details: err.message, - data: { - filename: filename - } - }); + if (!valid) { + if (err) { + that._logger.debug('read-link', 'Link ' + dir + ' is invalid', { + filename: dir, + error: err }); - }, function () { - // No json file was found, assume one - return Q.nfcall(bowerJson.parse, { - name: path.basename(dir) - }); - }); + } + return; } - }, function () {}); + + name = path.basename(dir); + return readJson(dir, { name: name }) + .spread(function (json, deprecated) { + if (deprecated) { + that._logger.warn('deprecated', 'Package ' + name + ' is using the deprecated ' + deprecated); + } + + decEndpoints[name] = { + name: name, + source: dir, + target: '*', + canonicalDir: dir, + pkgMeta: json, + linked: true + }; + }); + }); }); // Wait until all links have been read diff --git a/lib/core/ResolveCache.js b/lib/core/ResolveCache.js index 3be26498..2de2e47a 100644 --- a/lib/core/ResolveCache.js +++ b/lib/core/ResolveCache.js @@ -3,11 +3,10 @@ var path = require('path'); var semver = require('semver'); var mout = require('mout'); var Q = require('q'); -var bowerJson = require('bower-json'); var mkdirp = require('mkdirp'); var rimraf = require('rimraf'); var LRU = require('lru-cache'); -var createError = require('../util/createError'); +var readJson = require('../util/readJson'); var copy = require('../util/copy'); var md5 = require('../util/md5'); @@ -325,14 +324,9 @@ ResolveCache.prototype._getPkgRelease = function (pkgMeta) { ResolveCache.prototype._readPkgMeta = function (dir) { var filename = path.join(dir, '.bower.json'); - return Q.nfcall(bowerJson.read, filename) - .fail(function (err) { - throw createError('Something went wrong while reading ' + filename, err.code || 'EMALFORMED', { - details: err.message, - data: { - json: filename - } - }); + return readJson(filename) + .spread(function (json) { + return json; }); }; diff --git a/lib/core/resolvers/FsResolver.js b/lib/core/resolvers/FsResolver.js index c49855e6..b6233e18 100644 --- a/lib/core/resolvers/FsResolver.js +++ b/lib/core/resolvers/FsResolver.js @@ -37,15 +37,14 @@ FsResolver.isTargetable = function () { // is an archive file, by piping read stream to the zip extractor // This will likely increase the complexity of code but might worth it FsResolver.prototype._resolve = function () { - return this._readJson(this._source) - .then(this._copy.bind(this)) + return this._copy() .then(this._extract.bind(this)) .then(this._rename.bind(this)); }; // ----------------- -FsResolver.prototype._copy = function (meta) { +FsResolver.prototype._copy = function () { var that = this; return Q.nfcall(fs.stat, this._source) @@ -55,18 +54,19 @@ FsResolver.prototype._copy = function (meta) { var promise; that._sourceStat = stat; - - // Pass in the ignore to the copy options to avoid copying ignored files - // Also, pass in the mode to avoid additional stat calls when copying - copyOpts = { - mode: stat.mode, - ignore: meta.ignore - }; + copyOpts = { mode: stat.mode }; // If it's a folder if (stat.isDirectory()) { dst = that._tempDir; - promise = copy.copyDir(that._source, that._tempDir, copyOpts) + + // Read the bower.json inside the folder, so that we + // copy only the necessary files if it has ignore specified + promise = that._readJson(that._source) + .then(function (json) { + copyOpts.ignore = json.ignore; + return copy.copyDir(that._source, dst, copyOpts); + }) .then(function () { // Resolve to null because it's a dir return; diff --git a/lib/core/resolvers/Resolver.js b/lib/core/resolvers/Resolver.js index a05aeacb..c2cb865b 100644 --- a/lib/core/resolvers/Resolver.js +++ b/lib/core/resolvers/Resolver.js @@ -3,7 +3,7 @@ var path = require('path'); var Q = require('q'); var tmp = require('tmp'); var mkdirp = require('mkdirp'); -var bowerJson = require('bower-json'); +var readJson = require('../../util/readJson'); var createError = require('../../util/createError'); var removeIgnores = require('../../util/removeIgnores'); @@ -61,10 +61,15 @@ Resolver.prototype.hasNew = function (canonicalDir, pkgMeta) { } else { metaFile = path.join(canonicalDir, '.bower.json'); - promise = Q.nfcall(bowerJson.read, metaFile) - .then(function (pkgMeta) { + promise = readJson(metaFile) + .spread(function (pkgMeta) { return that._hasNew(canonicalDir, pkgMeta); - }, function () { + }, function (err) { + that._logger.debug('read-json', 'Failed to read ' + metaFile, { + filename: metaFile, + error: err + }); + return true; // Simply resolve to true if there was an error reading the file }); } @@ -151,33 +156,17 @@ Resolver.prototype._createTempDir = function () { }; Resolver.prototype._readJson = function (dir) { - dir = dir || this._tempDir; + var that = this; - return Q.nfcall(bowerJson.find, dir) - .then(function (filename) { - // If it is a component.json, warn about the deprecation - if (path.basename(filename) === 'component.json') { - this._logger.warn('deprecated', 'Package ' + this._name + ' is using the deprecated component.json file', { - json: filename - }); + dir = dir || this._tempDir; + return readJson(dir, { name: this._name }) + .spread(function (json, deprecated) { + if (deprecated) { + that._logger.warn('deprecated', 'Package ' + that._name + ' is using the deprecated ' + deprecated); } - // Read it - return Q.nfcall(bowerJson.read, filename) - .fail(function (err) { - throw createError('Something went wrong while reading ' + filename, err.code, { - details: err.message, - data: { - json: filename - } - }); - }); - }.bind(this), function () { - // No json file was found, assume one - return Q.nfcall(bowerJson.parse, { - name: this._name - }); - }.bind(this)); + return json; + }); }; Resolver.prototype._applyPkgMeta = function (meta) { diff --git a/lib/util/readJson.js b/lib/util/readJson.js new file mode 100644 index 00000000..48a60f6d --- /dev/null +++ b/lib/util/readJson.js @@ -0,0 +1,36 @@ +var path = require('path'); +var bowerJson = require('bower-json'); +var Q = require('q'); + +function readJson(file, options) { + options = options || {}; + + // Read + return Q.nfcall(bowerJson.read, file, options) + .spread(function (json, jsonFile) { + var deprecated; + + jsonFile = path.basename(jsonFile); + deprecated = jsonFile === 'component.json' ? jsonFile : false; + + return [json, deprecated]; + }, function (err) { + // No json file was found, assume one + if (err.code === 'ENOENT' && options.name) { + return [bowerJson.parse({ name: options.name }), false]; + } + + err.details = err.message; + + if (err.file) { + err.message = 'Failed to read ' + err.file; + err.data = { filename: err.file }; + } else { + err.message = 'Failed to read json from ' + file; + } + + throw err; + }); +} + +module.exports = readJson; diff --git a/lib/util/validLink.js b/lib/util/validLink.js new file mode 100644 index 00000000..b510cb3c --- /dev/null +++ b/lib/util/validLink.js @@ -0,0 +1,23 @@ +var Q = require('q'); +var fs = require('graceful-fs'); + +function validLink(file) { + // Filter only those that are valid links + return Q.nfcall(fs.lstat, file) + .then(function (stat) { + if (!stat.isSymbolicLink()) { + return [false, null]; + } + + return Q.nfcall(fs.stat, file) + .then(function () { + return [true, null]; + }); + }) + .fail(function (err) { + return [false, err]; + }); +} + +module.exports = validLink; + diff --git a/package.json b/package.json index 9cd64b43..c63b4cae 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "abbrev": "~1.0.4", "bower-config": "~0.2.0", "bower-endpoint-parser": "~0.1.0", - "bower-json": "~0.1.0", + "bower-json": "~0.2.0", "bower-logger": "~0.1.0", "bower-registry-client": "~0.1.1", "chmodr": "~0.1.0",