Beispiel #1
0
describe('resolverFactory', function () {
    var tempSource;
    var logger = new Logger();
    var registryClient = new RegistryClient(mout.object.fillIn({
        cache: defaultConfig._registry
    }, defaultConfig));

    afterEach(function (next) {
        logger.removeAllListeners();

        if (tempSource) {
            rimraf(tempSource, next);
            tempSource = null;
        } else {
            next();
        }
    });

    after(function (next) {
        rimraf('dejavu', next);
    });

    function callFactory (decEndpoint, config) {
        return resolverFactory(decEndpoint, config || defaultConfig, logger, registryClient);
    }

    it('should recognize git remote endpoints correctly', function (next) {
        var promise = Q.resolve();
        var endpoints;

        endpoints = {
            // git:
            'git://hostname.com/user/project': 'git://hostname.com/user/project',
            'git://hostname.com/user/project/': 'git://hostname.com/user/project',
            'git://hostname.com/user/project.git': 'git://hostname.com/user/project.git',
            'git://hostname.com/user/project.git/': 'git://hostname.com/user/project.git',
            // git@:
            'git@hostname.com:user/project': 'git@hostname.com:user/project',
            'git@hostname.com:user/project/': 'git@hostname.com:user/project',
            'git@hostname.com:user/project.git': 'git@hostname.com:user/project.git',
            'git@hostname.com:user/project.git/': 'git@hostname.com:user/project.git',
            // git+ssh:
            'git+ssh://user@hostname.com:project': 'ssh://user@hostname.com:project',
            'git+ssh://user@hostname.com:project/': 'ssh://user@hostname.com:project',
            'git+ssh://user@hostname.com:project.git': 'ssh://user@hostname.com:project.git',
            'git+ssh://user@hostname.com:project.git/': 'ssh://user@hostname.com:project.git',
            'git+ssh://user@hostname.com/project': 'ssh://user@hostname.com/project',
            'git+ssh://user@hostname.com/project/': 'ssh://user@hostname.com/project',
            'git+ssh://user@hostname.com/project.git': 'ssh://user@hostname.com/project.git',
            'git+ssh://user@hostname.com/project.git/': 'ssh://user@hostname.com/project.git',
            // git+http
            'git+http://hostname.com/project/blah': 'http://hostname.com/project/blah',
            'git+http://hostname.com/project/blah/': 'http://hostname.com/project/blah',
            'git+http://hostname.com/project/blah.git': 'http://hostname.com/project/blah.git',
            'git+http://hostname.com/project/blah.git/': 'http://hostname.com/project/blah.git',
            'git+http://user@hostname.com/project/blah': 'http://user@hostname.com/project/blah',
            'git+http://user@hostname.com/project/blah/': 'http://user@hostname.com/project/blah',
            'git+http://user@hostname.com/project/blah.git': 'http://user@hostname.com/project/blah.git',
            'git+http://user@hostname.com/project/blah.git/': 'http://user@hostname.com/project/blah.git',
            // git+https
            'git+https://hostname.com/project/blah': 'https://hostname.com/project/blah',
            'git+https://hostname.com/project/blah/': 'https://hostname.com/project/blah',
            'git+https://hostname.com/project/blah.git': 'https://hostname.com/project/blah.git',
            'git+https://hostname.com/project/blah.git/': 'https://hostname.com/project/blah.git',
            'git+https://user@hostname.com/project/blah': 'https://user@hostname.com/project/blah',
            'git+https://user@hostname.com/project/blah/': 'https://user@hostname.com/project/blah',
            'git+https://user@hostname.com/project/blah.git': 'https://user@hostname.com/project/blah.git',
            'git+https://user@hostname.com/project/blah.git/': 'https://user@hostname.com/project/blah.git',
            // ssh .git$
            'ssh://user@hostname.com:project.git': 'ssh://user@hostname.com:project.git',
            'ssh://user@hostname.com:project.git/': 'ssh://user@hostname.com:project.git',
            'ssh://user@hostname.com/project.git': 'ssh://user@hostname.com/project.git',
            'ssh://user@hostname.com/project.git/': 'ssh://user@hostname.com/project.git',
            // http .git$
            'http://hostname.com/project.git': 'http://hostname.com/project.git',
            'http://hostname.com/project.git/': 'http://hostname.com/project.git',
            'http://user@hostname.com/project.git': 'http://user@hostname.com/project.git',
            'http://user@hostname.com/project.git/': 'http://user@hostname.com/project.git',
            // https .git$
            'https://hostname.com/project.git': 'https://hostname.com/project.git',
            'https://hostname.com/project.git/': 'https://hostname.com/project.git',
            'https://user@hostname.com/project.git': 'https://user@hostname.com/project.git',
            'https://user@hostname.com/project.git/': 'https://user@hostname.com/project.git',
            // shorthand
            'bower/bower': 'git://github.com/bower/bower.git'
        };

        mout.object.forOwn(endpoints, function (value, key) {
            // Test without name and target
            promise = promise.then(function () {
                return callFactory({source: key});
            })
                    .then(function (resolver) {
                        expect(resolver).to.be.a(resolvers.GitRemote);
                        expect(resolver).to.not.be(resolvers.GitHub);
                        expect(resolver.getSource()).to.equal(value);
                        expect(resolver.getTarget()).to.equal('*');
                    });

            // Test with target
            promise = promise.then(function () {
                return callFactory({source: key, target: 'commit-ish'});
            })
                    .then(function (resolver) {
                        expect(resolver).to.be.a(resolvers.GitRemote);
                        expect(resolver).to.not.be(resolvers.GitHub);
                        expect(resolver.getSource()).to.equal(value);
                        expect(resolver.getTarget()).to.equal('commit-ish');
                    });

            // Test with name
            promise = promise.then(function () {
                return callFactory({name: 'foo', source: key});
            })
                    .then(function (resolver) {
                        expect(resolver).to.be.a(resolvers.GitRemote);
                        expect(resolver).to.not.be(resolvers.GitHub);
                        expect(resolver.getSource()).to.equal(value);
                        expect(resolver.getName()).to.equal('foo');
                        expect(resolver.getTarget()).to.equal('*');
                    });
        });

        promise
                .then(next.bind(next, null))
                .done();
    });

    it('should recognize GitHub endpoints correctly', function (next) {
        var promise = Q.resolve();
        var gitHub;
        var nonGitHub;

        gitHub = {
            // git:
            'git://github.com/user/project': 'git://github.com/user/project.git',
            'git://github.com/user/project/': 'git://github.com/user/project.git',
            'git://github.com/user/project.git': 'git://github.com/user/project.git',
            'git://github.com/user/project.git/': 'git://github.com/user/project.git',
            // git@:
            'git@github.com:user/project': 'git@github.com:user/project.git',
            'git@github.com:user/project/': 'git@github.com:user/project.git',
            'git@github.com:user/project.git': 'git@github.com:user/project.git',
            'git@github.com:user/project.git/': 'git@github.com:user/project.git',
            // git+ssh:
            'git+ssh://git@github.com:project/blah': 'ssh://git@github.com:project/blah.git',
            'git+ssh://git@github.com:project/blah/': 'ssh://git@github.com:project/blah.git',
            'git+ssh://git@github.com:project/blah.git': 'ssh://git@github.com:project/blah.git',
            'git+ssh://git@github.com:project/blah.git/': 'ssh://git@github.com:project/blah.git',
            'git+ssh://git@github.com/project/blah': 'ssh://git@github.com/project/blah.git',
            'git+ssh://git@github.com/project/blah/': 'ssh://git@github.com/project/blah.git',
            'git+ssh://git@github.com/project/blah.git': 'ssh://git@github.com/project/blah.git',
            'git+ssh://git@github.com/project/blah.git/': 'ssh://git@github.com/project/blah.git',
            // git+http
            'git+http://github.com/project/blah': 'http://github.com/project/blah.git',
            'git+http://github.com/project/blah/': 'http://github.com/project/blah.git',
            'git+http://github.com/project/blah.git': 'http://github.com/project/blah.git',
            'git+http://github.com/project/blah.git/': 'http://github.com/project/blah.git',
            'git+http://user@github.com/project/blah': 'http://user@github.com/project/blah.git',
            'git+http://user@github.com/project/blah/': 'http://user@github.com/project/blah.git',
            'git+http://user@github.com/project/blah.git': 'http://user@github.com/project/blah.git',
            'git+http://user@github.com/project/blah.git/': 'http://user@github.com/project/blah.git',
            // git+https
            'git+https://github.com/project/blah': 'https://github.com/project/blah.git',
            'git+https://github.com/project/blah/': 'https://github.com/project/blah.git',
            'git+https://github.com/project/blah.git': 'https://github.com/project/blah.git',
            'git+https://github.com/project/blah.git/': 'https://github.com/project/blah.git',
            'git+https://user@github.com/project/blah': 'https://user@github.com/project/blah.git',
            'git+https://user@github.com/project/blah/': 'https://user@github.com/project/blah.git',
            'git+https://user@github.com/project/blah.git': 'https://user@github.com/project/blah.git',
            'git+https://user@github.com/project/blah.git/': 'https://user@github.com/project/blah.git',
            // ssh .git$
            'ssh://git@github.com:project/blah.git': 'ssh://git@github.com:project/blah.git',
            'ssh://git@github.com:project/blah.git/': 'ssh://git@github.com:project/blah.git',
            'ssh://git@github.com/project/blah.git': 'ssh://git@github.com/project/blah.git',
            'ssh://git@github.com/project/blah.git/': 'ssh://git@github.com/project/blah.git',
            // http .git$
            'http://github.com/project/blah.git': 'http://github.com/project/blah.git',
            'http://github.com/project/blah.git/': 'http://github.com/project/blah.git',
            'http://user@github.com/project/blah.git': 'http://user@github.com/project/blah.git',
            'http://user@github.com/project/blah.git/': 'http://user@github.com/project/blah.git',
            // https
            'https://github.com/project/blah.git': 'https://github.com/project/blah.git',
            'https://github.com/project/blah.git/': 'https://github.com/project/blah.git',
            'https://user@github.com/project/blah.git': 'https://user@github.com/project/blah.git',
            'https://user@github.com/project/blah.git/': 'https://user@github.com/project/blah.git',
            // shorthand
            'bower/bower': 'git://github.com/bower/bower.git'
        };

        nonGitHub = [
            'git://github.com/user/project/bleh.git',
            'git://xxxxgithub.com/user/project.git',
            'git@xxxxgithub.com:user:project.git',
            'git@xxxxgithub.com:user/project.git',
            'git+ssh://git@xxxxgithub.com:user/project',
            'git+ssh://git@xxxxgithub.com/user/project',
            'git+http://user@xxxxgithub.com/user/project',
            'git+https://user@xxxxgithub.com/user/project',
            'ssh://git@xxxxgithub.com:user/project.git',
            'ssh://git@xxxxgithub.com/user/project.git',
            'http://xxxxgithub.com/user/project.git',
            'https://xxxxgithub.com/user/project.git',
            'http://user@xxxxgithub.com/user/project.git',
            'https://user@xxxxgithub.com/user/project.git'
        ];

        // Test GitHub ones
        mout.object.forOwn(gitHub, function (value, key) {
            // Test without name and target
            promise = promise.then(function () {
                return callFactory({source: key});
            })
                    .then(function (resolver) {
                        expect(resolver).to.be.a(resolvers.GitHub);
                        expect(resolver.getSource()).to.equal(value);
                        expect(resolver.getTarget()).to.equal('*');
                    });

            // Test with target
            promise = promise.then(function () {
                return callFactory({source: key, target: 'commit-ish'});
            })
                    .then(function (resolver) {
                        if (value) {
                            expect(resolver).to.be.a(resolvers.GitHub);
                            expect(resolver.getSource()).to.equal(value);
                            expect(resolver.getTarget()).to.equal('commit-ish');
                        } else {
                            expect(resolver).to.not.be.a(resolvers.GitHub);
                        }
                    });

            // Test with name
            promise = promise.then(function () {
                return callFactory({name: 'foo', source: key});
            })
                    .then(function (resolver) {
                        if (value) {
                            expect(resolver).to.be.a(resolvers.GitHub);
                            expect(resolver.getSource()).to.equal(value);
                            expect(resolver.getName()).to.equal('foo');
                            expect(resolver.getTarget()).to.equal('*');
                        } else {
                            expect(resolver).to.not.be.a(resolvers.GitHub);
                        }
                    });
        });

        // Test similar to GitHub but not real GitHub
        nonGitHub.forEach(function (value) {
            promise = promise.then(function () {
                return callFactory({source: value});
            })
                    .then(function (resolver) {
                        expect(resolver).to.not.be.a(resolvers.GitHub);
                        expect(resolver).to.be.a(resolvers.GitRemote);
                    });
        });

        promise
                .then(next.bind(next, null))
                .done();
    });

    it('should recognize local fs git endpoints correctly', function (next) {
        var promise = Q.resolve();
        var endpoints;
        var temp;

        endpoints = {};

        // Absolute path
        temp = path.resolve(__dirname, '../assets/package-a');
        endpoints[temp] = temp;

        // Absolute path that ends with a /
        // See: https://github.com/bower/bower/issues/898
        temp = path.resolve(__dirname, '../assets/package-a') + '/';
        endpoints[temp] = temp;

        // Relative path
        endpoints[__dirname + '/../assets/package-a'] = temp;

        // TODO: test with backslashes on windows and ~/ on unix

        mout.object.forOwn(endpoints, function (value, key) {
            // Test without name
            promise = promise.then(function () {
                return callFactory({source: key});
            })
                    .then(function (resolver) {
                        expect(resolver).to.be.a(resolvers.GitFs);
                        expect(resolver.getTarget()).to.equal('*');
                    });

            // Test with name
            promise = promise.then(function () {
                return callFactory({name: 'foo', source: key});
            })
                    .then(function (resolver) {
                        expect(resolver).to.be.a(resolvers.GitFs);
                        expect(resolver.getName()).to.equal('foo');
                        expect(resolver.getTarget()).to.equal('*');
                    });
        });

        promise
                .then(next.bind(next, null))
                .done();
    });

    it('should recognize svn remote endpoints correctly', function (next) {
        var promise = Q.resolve();
        var endpoints;

        endpoints = {
            // svn:
            'svn://hostname.com/user/project': 'http://hostname.com/user/project',
            'svn://hostname.com/user/project/': 'http://hostname.com/user/project',
            // svn@:
            'svn://svn@hostname.com:user/project': 'http://svn@hostname.com:user/project',
            'svn://svn@hostname.com:user/project/': 'http://svn@hostname.com:user/project',
            // svn+http
            'svn+http://hostname.com/project/blah': 'http://hostname.com/project/blah',
            'svn+http://hostname.com/project/blah/': 'http://hostname.com/project/blah',
            'svn+http://user@hostname.com/project/blah': 'http://user@hostname.com/project/blah',
            'svn+http://user@hostname.com/project/blah/': 'http://user@hostname.com/project/blah',
            // svn+https
            'svn+https://hostname.com/project/blah': 'https://hostname.com/project/blah',
            'svn+https://hostname.com/project/blah/': 'https://hostname.com/project/blah',
            'svn+https://user@hostname.com/project/blah': 'https://user@hostname.com/project/blah',
            'svn+https://user@hostname.com/project/blah/': 'https://user@hostname.com/project/blah',
            // svn+ssh
            'svn+ssh://hostname.com/project/blah': 'svn+ssh://hostname.com/project/blah',
            'svn+ssh://hostname.com/project/blah/': 'svn+ssh://hostname.com/project/blah',
            'svn+ssh://user@hostname.com/project/blah': 'svn+ssh://user@hostname.com/project/blah',
            'svn+ssh://user@hostname.com/project/blah/': 'svn+ssh://user@hostname.com/project/blah',
            // svn+file
            'svn+file:///project/blah': 'file:///project/blah',
            'svn+file:///project/blah/': 'file:///project/blah'
        };

        mout.object.forOwn(endpoints, function (value, key) {
            // Test without name and target
            promise = promise.then(function () {
                return callFactory({source: key});
            })
                    .then(function (resolver) {
                        expect(resolver).to.be.a(resolvers.Svn);
                        expect(resolver).to.not.be(resolvers.GitHub);
                        expect(resolvers.Svn.getSource(resolver.getSource())).to.equal(value);
                        expect(resolver.getTarget()).to.equal('*');
                    });

            // Test with target
            promise = promise.then(function () {
                return callFactory({source: key, target: 'commit-ish'});
            })
                    .then(function (resolver) {
                        expect(resolver).to.be.a(resolvers.Svn);
                        expect(resolver).to.not.be(resolvers.GitHub);
                        expect(resolvers.Svn.getSource(resolver.getSource())).to.equal(value);
                        expect(resolver.getTarget()).to.equal('commit-ish');
                    });

            // Test with name
            promise = promise.then(function () {
                return callFactory({name: 'foo', source: key});
            })
                    .then(function (resolver) {
                        expect(resolver).to.be.a(resolvers.Svn);
                        expect(resolver).to.not.be(resolvers.GitHub);
                        expect(resolvers.Svn.getSource(resolver.getSource())).to.equal(value);
                        expect(resolver.getName()).to.equal('foo');
                        expect(resolver.getTarget()).to.equal('*');
                    });
        });

        promise
                .then(next.bind(next, null))
                .done();
    });

    it('should recognize local fs files/folder endpoints correctly', function (next) {
        var promise = Q.resolve();
        var endpoints;
        var temp;

        tempSource = path.resolve(__dirname, '../tmp/tmp');
        mkdirp.sync(tempSource);
        fs.writeFileSync(path.join(tempSource, '.git'), 'foo');
        fs.writeFileSync(path.join(tempSource, 'file.with.multiple.dots'), 'foo');

        endpoints = {};

        // Absolute path to folder with .git file
        endpoints[tempSource] = tempSource;
        // Relative path to folder with .git file
        endpoints[__dirname + '/../tmp/tmp'] = tempSource;

        // Absolute path to folder
        temp = path.resolve(__dirname, '../assets/test-temp-dir');
        endpoints[temp] = temp;
        // Absolute + relative path to folder
        endpoints[__dirname + '/../assets/test-temp-dir'] = temp;

        // Absolute path to file
        temp = path.resolve(__dirname, '../assets/package-zip.zip');
        endpoints[temp] = temp;
        // Absolute + relative path to file
        endpoints[__dirname + '/../assets/package-zip.zip'] = temp;

        // Relative ../
        endpoints['../'] = path.normalize(__dirname + '/../../..');

        // Relative ./
        endpoints['./test/assets'] = path.join(__dirname, '../assets');

        // Relative with just one slash, to test fs resolution
        // priority against shorthands
        endpoints['./test'] = path.join(__dirname, '..');

        // Test files with multiple dots (PR #474)
        temp = path.join(tempSource, 'file.with.multiple.dots');
        endpoints[temp] = temp;

        mout.object.forOwn(endpoints, function (value, key) {
            // Test without name
            promise = promise.then(function () {
                return callFactory({source: key});
            })
                    .then(function (resolver) {
                        expect(resolver.getSource()).to.equal(value);
                        expect(resolver).to.be.a(resolvers.Fs);
                        expect(resolver.getTarget()).to.equal('*');
                    });

            // Test with name
            promise = promise.then(function () {
                return callFactory({name: 'foo', source: key});
            })
                    .then(function (resolver) {
                        expect(resolver).to.be.a(resolvers.Fs);
                        expect(resolver.getName()).to.equal('foo');
                        expect(resolver.getTarget()).to.equal('*');
                        expect(resolver.getSource()).to.equal(value);
                    });
        });


        promise
                .then(next.bind(next, null))
                .done();
    });

    it('should recognize URL endpoints correctly', function (next) {
        var promise = Q.resolve();
        var endpoints;

        endpoints = [
            'http://bower.io/foo.js',
            'https://bower.io/foo.js'
        ];

        endpoints.forEach(function (source) {
            // Test without name
            promise = promise.then(function () {
                return callFactory({source: source});
            })
                    .then(function (resolver) {
                        expect(resolver).to.be.a(resolvers.Url);
                        expect(resolver.getSource()).to.equal(source);
                    });

            // Test with name
            promise = promise.then(function () {
                return callFactory({name: 'foo', source: source});
            })
                    .then(function (resolver) {
                        expect(resolver).to.be.a(resolvers.Url);
                        expect(resolver.getName()).to.equal('foo');
                        expect(resolver.getSource()).to.equal(source);
                    });
        });

        promise
                .then(next.bind(next, null))
                .done();
    });

    it('should recognize registry endpoints correctly', function (next) {
        // Create a 'dejavu' file at the root to prevent regressions of #666
        fs.writeFileSync('dejavu', 'foo');

        callFactory({source: 'dejavu'})
                .then(function (resolver) {
                    expect(resolver).to.be.a(resolvers.GitRemote);
                    expect(resolver.getSource()).to.equal('git://github.com/IndigoUnited/dejavu.git');
                    expect(resolver.getTarget()).to.equal('*');
                })
                .then(function () {
                    // Test with name
                    return callFactory({source: 'dejavu', name: 'foo'})
                            .then(function (resolver) {
                                expect(resolver).to.be.a(resolvers.GitRemote);
                                expect(resolver.getSource()).to.equal('git://github.com/IndigoUnited/dejavu.git');
                                expect(resolver.getName()).to.equal('foo');
                                expect(resolver.getTarget()).to.equal('*');
                            });
                })
                .then(function () {
                    // Test with target
                    return callFactory({source: 'dejavu', target: '~2.0.0'})
                            .then(function (resolver) {
                                expect(resolver).to.be.a(resolvers.GitRemote);
                                expect(resolver.getTarget()).to.equal('~2.0.0');

                                next();
                            });
                })
                .done();
    });

    it('should error out if the package was not found in the registry', function (next) {
        callFactory({source: 'some-package-that-will-never-exist'})
                .then(function () {
                    throw new Error('Should have failed');
                }, function (err) {
                    expect(err).to.be.an(Error);
                    expect(err.code).to.equal('ENOTFOUND');
                    expect(err.message).to.contain('some-package-that-will-never-exist');

                    next();
                })
                .done();
    });

    it('should set registry to true on the decomposed endpoint if fetched from the registry', function (next) {
        var decEndpoint = {source: 'dejavu'};

        callFactory(decEndpoint)
                .then(function () {
                    expect(decEndpoint.registry).to.be(true);
                    next();
                })
                .done();
    });

    it('should use the configured shorthand resolver', function (next) {
        callFactory({source: 'bower/bower'})
                .then(function (resolver) {
                    var config;

                    expect(resolver.getSource()).to.equal('git://github.com/bower/bower.git');

                    config = mout.object.fillIn({
                        shorthandResolver: 'git://bower.io/{{owner}}/{{package}}/{{shorthand}}'
                    }, defaultConfig);

                    return callFactory({source: 'IndigoUnited/promptly'}, config);
                })
                .then(function (resolver) {
                    expect(resolver.getSource()).to.equal('git://bower.io/IndigoUnited/promptly/IndigoUnited/promptly');
                    next();
                })
                .done();
    });

    it('should not expand using the shorthand resolver if it looks like a SSH URL', function (next) {
        callFactory({source: 'bleh@xxx.com:foo/bar'})
                .then(function (resolver) {
                    throw new Error('Should have failed');
                }, function (err) {
                    expect(err).to.be.an(Error);
                    expect(err.code).to.equal('ENOTFOUND');
                    expect(err.message).to.contain('bleh@xxx.com:foo/bar');
                    next();
                })
                .done();
    });


    it('should error out if there\'s no suitable resolver for a given source', function (next) {
        resolverFactory({source: 'some-package-that-will-never-exist'}, defaultConfig, logger)
                .then(function () {
                    throw new Error('Should have failed');
                }, function (err) {
                    expect(err).to.be.an(Error);
                    expect(err.code).to.be('ENORESOLVER');
                    expect(err.message).to.contain('appropriate resolver');
                    next();
                })
                .done();
    });

    it.skip('should use config.cwd when resolving relative paths');

    it('should not swallow constructor errors when instantiating resolvers', function (next) {
        var promise = Q.resolve();
        var endpoints;

        // TODO: test with others
        endpoints = [
            'http://bower.io/foo.js',
            path.resolve(__dirname, '../assets/test-temp-dir')
        ];

        endpoints.forEach(function (source) {
            promise = promise.then(function () {
                return callFactory({source: source, target: 'bleh'});
            })
                    .then(function () {
                        throw new Error('Should have failed');
                    }, function (err) {
                        expect(err).to.be.an(Error);
                        expect(err.message).to.match(/can't resolve targets/i);
                        expect(err.code).to.equal('ENORESTARGET');
                    });
        });

        promise
                .then(next.bind(next, null))
                .done();
    });

    describe('.clearRuntimeCache', function () {
        it('should call every resolver static method that clears the runtime cache', function () {
            var originalMethods = {};
            var called = [];
            var error;

            mout.object.forOwn(resolvers, function (ConcreteResolver, key) {
                originalMethods[key] = ConcreteResolver.clearRuntimeCache;
                ConcreteResolver.clearRuntimeCache = function () {
                    called.push(key);
                    return originalMethods[key].apply(this, arguments);
                };
            });

            try {
                resolverFactory.clearRuntimeCache();
            } catch (e) {
                error = e;
            } finally {
                mout.object.forOwn(resolvers, function (ConcreteResolver, key) {
                    ConcreteResolver.clearRuntimeCache = originalMethods[key];
                });
            }

            if (error) {
                throw error;
            }

            expect(called.sort()).to.eql(Object.keys(resolvers).sort());
        });
    });
});
Beispiel #2
0
Project.prototype._restoreNode = function (node, flattened, jsonKey) {
    var deps;

    // Do not restore if the node is missing
    if (node.missing) {
        return;
    }

    node.dependencies = node.dependencies || {};
    node.dependants = node.dependants || {};

    // Only process deps that are yet processed
    deps = mout.object.filter(node.pkgMeta[jsonKey], function (value, key) {
        return !node.dependencies[key];
    });

    mout.object.forOwn(deps, function (value, key) {
        var local = flattened[key];
        var json = endpointParser.json2decomposed(key, value);
        var restored;
        var compatible;
        var originalSource;

        // Check if the dependency is not installed
        if (!local) {
            flattened[key] = restored = json;
            restored.missing = true;
        // Even if it is installed, check if it's compatible
        // Note that linked packages are interpreted as compatible
        // This might change in the future: #673
        } else {
            compatible = local.linked || (!local.missing && json.target === local.pkgMeta._target);

            if (!compatible) {
                restored = json;

                if (!local.missing) {
                    restored.pkgMeta = local.pkgMeta;
                    restored.canonicalDir = local.canonicalDir;
                    restored.incompatible = true;
                } else {
                    restored.missing = true;
                }
            } else {
                restored = local;
                mout.object.mixIn(local, json);
            }

            // Check if source changed, marking as different if it did
            // We only do this for direct root dependencies that are compatible
            if (node.root && compatible) {
                originalSource = mout.object.get(local, 'pkgMeta._originalSource');
                if (originalSource && originalSource !== json.source) {
                    restored.different = true;
                }
            }
        }

        // Cross reference
        node.dependencies[key] = restored;
        restored.dependants = restored.dependants || {};
        restored.dependants[node.name] = node;

        // Call restore for this dependency
        this._restoreNode(restored, flattened, 'dependencies');

        // Do the same for the incompatible local package
        if (local && restored !== local) {
            this._restoreNode(local, flattened, 'dependencies');
        }
    }, this);
};
Beispiel #3
0
 that.walkTree(tree, function (node, nodeName) {
     if (name === nodeName) {
         dependants.push.apply(dependants, mout.object.values(node.dependants));
     }
 }, true);
Beispiel #4
0
    it('should recognize local fs files/folder endpoints correctly', function (next) {
        var promise = Q.resolve();
        var endpoints;
        var temp;

        tempSource = path.resolve(__dirname, '../tmp/tmp');
        mkdirp.sync(tempSource);
        fs.writeFileSync(path.join(tempSource, '.git'), 'foo');
        fs.writeFileSync(path.join(tempSource, 'file.with.multiple.dots'), 'foo');

        endpoints = {};

        // Absolute path to folder with .git file
        endpoints[tempSource] = tempSource;
        // Relative path to folder with .git file
        endpoints[__dirname + '/../tmp/tmp'] = tempSource;

        // Absolute path to folder
        temp = path.resolve(__dirname, '../assets/test-temp-dir');
        endpoints[temp] = temp;
        // Absolute + relative path to folder
        endpoints[__dirname + '/../assets/test-temp-dir'] = temp;

        // Absolute path to file
        temp = path.resolve(__dirname, '../assets/package-zip.zip');
        endpoints[temp] = temp;
        // Absolute + relative path to file
        endpoints[__dirname + '/../assets/package-zip.zip'] = temp;

        // Relative ../
        endpoints['../'] = path.normalize(__dirname + '/../../..');

        // Relative ./
        endpoints['./test/assets'] = path.join(__dirname, '../assets');

        // Relative with just one slash, to test fs resolution
        // priority against shorthands
        endpoints['./test'] = path.join(__dirname, '..');

        // Test files with multiple dots (PR #474)
        temp = path.join(tempSource, 'file.with.multiple.dots');
        endpoints[temp] = temp;

        mout.object.forOwn(endpoints, function (value, key) {
            // Test without name
            promise = promise.then(function () {
                return callFactory({ source: key });
            })
            .then(function (resolver) {
                expect(resolver.getSource()).to.equal(value);
                expect(resolver).to.be.a(resolvers.Fs);
                expect(resolver.getTarget()).to.equal('*');
            });

            // Test with name
            promise = promise.then(function () {
                return callFactory({ name: 'foo', source: key });
            })
            .then(function (resolver) {
                expect(resolver).to.be.a(resolvers.Fs);
                expect(resolver.getName()).to.equal('foo');
                expect(resolver.getTarget()).to.equal('*');
                expect(resolver.getSource()).to.equal(value);
            });
        });


        promise
        .then(next.bind(next, null))
        .done();
    });
Beispiel #5
0
var path = require('path');
var fs = require('graceful-fs');
var Handlebars = require('handlebars');
var mout = require('mout');
var helpers = require('../../templates/helpers');

var templatesDir = path.resolve(__dirname, '../../templates');
var cache = {};

// Register helpers
mout.object.forOwn(helpers, function (register) {
    register(Handlebars);
});

function render(name, data, escape) {
    var contents;

    // Check if already compiled
    if (cache[name]) {
	return cache[name](data);
    }

    // Otherwise, read the file, compile and cache
    contents = fs.readFileSync(path.join(templatesDir, name)).toString();
    cache[name] = Handlebars.compile(contents, {
	noEscape: !escape
    });

    // Call the function again
    return render(name, data, escape);
}
Manager.prototype._electSuitable = function (name, semvers, nonSemvers) {
    var suitable;
    var resolution;
    var unresolvable;
    var dataPicks;
    var save;
    var choices;
    var picks = [];

    // If there are both semver and non-semver, there's no way
    // to figure out the suitable one
    if (semvers.length && nonSemvers.length) {
        picks.push.apply(picks, semvers);
        picks.push.apply(picks, nonSemvers);
    // If there are only non-semver ones, the suitable is elected
    // only if there's one
    } else if (nonSemvers.length) {
        if (nonSemvers.length === 1) {
            return Q.resolve(nonSemvers[0]);
        }

        picks.push.apply(picks, nonSemvers);
    // If there are only semver ones, figure out the which one
    // is compatible with every requirement
    } else {
        suitable = mout.array.find(semvers, function (subject) {
            return semvers.every(function (decEndpoint) {
                return subject === decEndpoint ||
                       semver.satisfies(subject.pkgMeta.version, decEndpoint.target);
            });
        });

        if (suitable) {
            return Q.resolve(suitable);
        }

        picks.push.apply(picks, semvers);
    }

    // At this point, there's a conflict
    this._conflicted[name] = true;

    // Prepare data to be sent bellow
    // 1 - Sort picks by version/release
    picks.sort(function (pick1, pick2) {
        var version1 = pick1.pkgMeta.version;
        var version2 = pick2.pkgMeta.version;
        var comp;

        // If both have versions, compare their versions using semver
        if (version1 && version2) {
            comp = semver.compare(version1, version2);
            if (comp) {
                return comp;
            }
        } else {
            // If one of them has a version, it's considered higher
            if (version1) {
                return 1;
            }
            if (version2) {
                return -1;
            }
        }

        // Give priority to the one with most dependants
        if (pick1.dependants.length > pick2.dependants.length) {
            return -1;
        }
        if (pick1.dependants.length < pick2.dependants.length) {
            return 1;
        }

        return 0;
    });

    // 2 - Transform data
    dataPicks = picks.map(function (pick) {
        var dataPick = this.toData(pick);
        dataPick.dependants = pick.dependants.map(this.toData, this);
        dataPick.dependants.sort(function (dependant1, dependant2) {
            return dependant1.endpoint.name.localeCompare(dependant2.endpoint.name);
        });
        return dataPick;
    }, this);

    // Check if there's a resolution that resolves the conflict
    // Note that if one of them is marked as unresolvable,
    // the resolution has no effect
    resolution = this._resolutions[name];
    unresolvable = mout.object.find(picks, function (pick) {
        return pick.unresolvable;
    });

    if (resolution && !unresolvable) {
        if (semver.validRange(resolution)) {
            suitable = mout.array.findIndex(picks, function (pick) {
                return pick.pkgMeta.version &&
                       semver.satisfies(pick.pkgMeta.version, resolution);
            });
        } else {
            suitable = mout.array.findIndex(picks, function (pick) {
                return pick.pkgMeta._release === resolution;
            });
        }

        if (suitable === -1) {
            this._logger.warn('resolution', 'Unsuitable resolution declared for ' + name + ': ' + resolution, {
                name: name,
                picks: dataPicks,
                resolution: resolution
            });
        } else {
            this._logger.conflict('solved', 'Unable to find suitable version for ' + name, {
                name: name,
                picks: dataPicks,
                resolution: resolution,
                suitable: dataPicks[suitable]
            });
            return Q.resolve(picks[suitable]);
        }
    }

    // If force latest is enabled, resolve to the highest semver version
    // or whatever non-semver if none available
    if (this._forceLatest) {
        suitable = picks.length - 1;

        this._logger.conflict('solved', 'Unable to find suitable version for ' + name, {
            name: name,
            picks: dataPicks,
            suitable: dataPicks[suitable],
            forced: true
        });
        return Q.resolve(picks[suitable]);
    }

    // If interactive is disabled, error out
    if (!this._config.interactive) {
        throw createError('Unable to find suitable version for ' + name, 'ECONFLICT', {
            name: name,
            picks: dataPicks
        });
    }

    // At this point the user needs to make a decision
    this._logger.conflict('incompatible', 'Unable to find suitable version for ' + name, {
        name: name,
        picks: dataPicks
    });

    choices = picks.map(function (pick, index) { return index + 1; });
    return Q.nfcall(this._logger.prompt.bind(this._logger), {
        type: 'input',
        message: 'Answer:',
        validate: function (choice) {
            choice = Number(mout.string.trim(choice.trim(), '!'));

            if (!choice || choice < 1 || choice > picks.length) {
                return 'Invalid choice';
            }

            return true;
        }
    })
    .then(function (choice) {
        var pick;
        var resolution;

        // Sanitize choice
        choice = choice.trim();
        save = /^!/.test(choice) || /!$/.test(choice);  // Save if prefixed or suffixed with !
        choice = Number(mout.string.trim(choice, '!'));
        pick = picks[choice - 1];

        // Store choice into resolutions
        if (pick.target === '*') {
            resolution = pick.pkgMeta._release || '*';
        } else {
            resolution = pick.target;
        }

        if (save) {
            this._logger.info('resolution', 'Saved ' + name + '#' + resolution + ' as resolution', {
                name: name,
                resolution: resolution,
                action: this._resolutions[name] ? 'edit' : 'add'
            });
            this._resolutions[name] = resolution;
        }

        return pick;
    }.bind(this));
};
Beispiel #7
0
    else it('should recognize svn remote endpoints correctly', function (next) {
        var promise = Q.resolve();
        var endpoints;

        endpoints = {
            // svn:
            'svn://hostname.com/user/project': 'http://hostname.com/user/project',
            'svn://hostname.com/user/project/': 'http://hostname.com/user/project',

            // svn@:
            'svn://svn@hostname.com:user/project': 'http://svn@hostname.com:user/project',
            'svn://svn@hostname.com:user/project/': 'http://svn@hostname.com:user/project',

            // svn+http
            'svn+http://hostname.com/project/blah': 'http://hostname.com/project/blah',
            'svn+http://hostname.com/project/blah/': 'http://hostname.com/project/blah',
            'svn+http://user@hostname.com/project/blah': 'http://user@hostname.com/project/blah',
            'svn+http://user@hostname.com/project/blah/': 'http://user@hostname.com/project/blah',

            // svn+https
            'svn+https://hostname.com/project/blah': 'https://hostname.com/project/blah',
            'svn+https://hostname.com/project/blah/': 'https://hostname.com/project/blah',
            'svn+https://user@hostname.com/project/blah': 'https://user@hostname.com/project/blah',
            'svn+https://user@hostname.com/project/blah/': 'https://user@hostname.com/project/blah',

            // svn+ssh
            'svn+ssh://hostname.com/project/blah': 'svn+ssh://hostname.com/project/blah',
            'svn+ssh://hostname.com/project/blah/': 'svn+ssh://hostname.com/project/blah',
            'svn+ssh://user@hostname.com/project/blah': 'svn+ssh://user@hostname.com/project/blah',
            'svn+ssh://user@hostname.com/project/blah/': 'svn+ssh://user@hostname.com/project/blah',

            // svn+file
            'svn+file:///project/blah': 'file:///project/blah',
            'svn+file:///project/blah/': 'file:///project/blah'
        };

        mout.object.forOwn(endpoints, function (value, key) {
            // Test without name and target
            promise = promise.then(function () {
                return callFactory({ source: key });
            })
            .then(function (resolver) {
                expect(resolver).to.be.a(resolvers.Svn);
                expect(resolver).to.not.be(resolvers.GitHub);
                expect(resolvers.Svn.getSource(resolver.getSource())).to.equal(value);
                expect(resolver.getTarget()).to.equal('*');
            });

            // Test with target
            promise = promise.then(function () {
                return callFactory({ source: key, target: 'commit-ish' });
            })
            .then(function (resolver) {
                expect(resolver).to.be.a(resolvers.Svn);
                expect(resolver).to.not.be(resolvers.GitHub);
                expect(resolvers.Svn.getSource(resolver.getSource())).to.equal(value);
                expect(resolver.getTarget()).to.equal('commit-ish');
            });

            // Test with name
            promise = promise.then(function () {
                return callFactory({ name: 'foo', source: key });
            })
            .then(function (resolver) {
                expect(resolver).to.be.a(resolvers.Svn);
                expect(resolver).to.not.be(resolvers.GitHub);
                expect(resolvers.Svn.getSource(resolver.getSource())).to.equal(value);
                expect(resolver.getName()).to.equal('foo');
                expect(resolver.getTarget()).to.equal('*');
            });
        });

        promise
        .then(next.bind(next, null))
        .done();
    });
Beispiel #8
0
delete config.json;

// If interactive is auto (null), guess its value
if (config.interactive == null) {
    config.interactive = (
            process.bin === 'upt' &&
            tty.isatty(1) &&
            !process.env.CI
            );
}

// If `analytics` hasn't been explicitly set, we disable
// it when ran programatically.
if (config.analytics == null) {
    // Don't enable analytics on CI server unless explicitly configured.
    config.analytics = config.interactive;
}

// Merge common CLI options into the config
mout.object.mixIn(config, cli.readOptions({
    force: {type: Boolean, shorthand: 'f'},
    offline: {type: Boolean, shorthand: 'o'},
    verbose: {type: Boolean, shorthand: 'V'},
    quiet: {type: Boolean, shorthand: 'q'},
    loglevel: {type: String, shorthand: 'l'},
    json: {type: Boolean, shorthand: 'j'},
    silent: {type: Boolean, shorthand: 's'}
}));

module.exports = config;
}

// Get the command to execute
while (options.argv.remain.length) {
    command = options.argv.remain.join(' ');

    // Alias lookup
    if (bower.abbreviations[command]) {
        command = bower.abbreviations[command].replace(/\s/g, '.');
        break;
    }

    command = command.replace(/\s/g, '.');

    // Direct lookup
    if (mout.object.has(bower.commands, command)) {
        break;
    }

    options.argv.remain.pop();
}

// Execute the command
commandFunc = command && mout.object.get(bower.commands, command);
command = command && command.replace(/\./g, ' ');

// If no command was specified, show bower help
// Do the same if the command is unknown
if (!commandFunc) {
    logger = bower.commands.help();
    command = 'help';
Beispiel #10
0
    analytics.setup(upt.config).then(function () {
        // Execute the command
        commandFunc = command && mout.object.get(upt.commands, command);
        command = command && command.replace(/\./g, ' ');

        // If no command was specified, show upt help
        // Do the same if the command is unknown
        if (!commandFunc) {
            logger = upt.commands.help();
            command = 'help';
            // If the user requested help, show the command's help
            // Do the same if the actual command is a group of other commands (e.g.: cache)
        } else if (options.help || !commandFunc.line) {
            logger = upt.commands.help(command);
            command = 'help';
            // Call the line method
        } else {
            logger = commandFunc.line(process.argv);

            // If the method failed to interpret the process arguments
            // show the command help
            if (!logger) {
                logger = upt.commands.help(command);
                command = 'help';
            }
        }

        // Get the renderer and configure it with the executed command
        renderer = cli.getRenderer(command, logger.json, upt.config);

        logger
                .on('end', function (data) {
                    if (!upt.config.silent && !upt.config.quiet) {
                        renderer.end(data);
                    }
                })
                .on('error', function (err) {
                    if (levels.error >= loglevel) {
                        renderer.error(err);
                    }

                    process.exit(1);
                })
                .on('log', function (log) {
                    if (levels[log.level] >= loglevel) {
                        renderer.log(log);
                    }
                })
                .on('prompt', function (prompt, callback) {
                    renderer.prompt(prompt)
                            .then(function (answer) {
                                callback(answer);
                            });
                });

        // Warn if HOME is not SET
        if (!osenv.home()) {
            logger.warn('no-home', 'HOME not set, user configuration will not be loaded');
        }

        if (upt.config.interactive) {
            var updateNotifier = require('update-notifier');

            // Check for newer version of Upt
            var notifier = updateNotifier({
                packageName: pkg.name,
                packageVersion: pkg.version
            });

            if (notifier.update && levels.info >= loglevel) {
                notifier.notify();
            }
        }
    });
Beispiel #11
0
var createError = require('../../util/createError');

function FsResolver(decEndpoint, config, logger) {
    Resolver.call(this, decEndpoint, config, logger);

    // Ensure absolute path
    this._source = path.resolve(this._config.cwd, this._source);

    // If target was specified, simply reject the promise
    if (this._target !== '*') {
        throw createError('File system sources can\'t resolve targets', 'ENORESTARGET');
    }
}

util.inherits(FsResolver, Resolver);
mout.object.mixIn(FsResolver, Resolver);

// -----------------

FsResolver.isTargetable = function () {
    return false;
};

// TODO: Should we store latest mtimes in the resolution and compare?
//       This would be beneficial when copying big files/folders

// TODO: There's room for improvement by using streams if the source
//       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._copy()
Beispiel #12
0
Hw2Core(function () {
    var Q = require('q');
    var mout = require('mout');
    var Logger = require('bower-logger');
    var osenv = require('osenv');
    var pkg = require('../package.json');
    var upt = require('../src/lib');
    var cli = require('../src/lib/util/cli');
    var rootCheck = require('../src/lib/util/rootCheck');
    var analytics = require('../src/lib/util/analytics');

    var options;
    var renderer;
    var loglevel;
    var command;
    var commandFunc;
    var logger;
    var levels = Logger.LEVELS;

    options = cli.readOptions({
        version: {type: Boolean, shorthand: 'v'},
        help: {type: Boolean, shorthand: 'h'},
        'allow-root': {type: Boolean}
    });

// Handle print of version
    if (options.version) {
        process.stdout.write(pkg.version + '\n');
        process.exit();
    }

// Root check
    rootCheck(options, upt.config);

// Set loglevel
    if (upt.config.silent) {
        loglevel = levels.error;
    } else if (upt.config.verbose) {
        loglevel = -Infinity;
        Q.longStackSupport = true;
    } else if (upt.config.quiet) {
        loglevel = levels.warn;
    } else {
        loglevel = levels[upt.config.loglevel] || levels.info;
    }

// Get the command to execute
    while (options.argv.remain.length) {
        command = options.argv.remain.join(' ');

        // Alias lookup
        if (upt.abbreviations[command]) {
            command = upt.abbreviations[command].replace(/\s/g, '.');
            break;
        }

        command = command.replace(/\s/g, '.');

        // Direct lookup
        if (mout.object.has(upt.commands, command)) {
            break;
        }

        options.argv.remain.pop();
    }

// Ask for Insights on first run.
    analytics.setup(upt.config).then(function () {
        // Execute the command
        commandFunc = command && mout.object.get(upt.commands, command);
        command = command && command.replace(/\./g, ' ');

        // If no command was specified, show upt help
        // Do the same if the command is unknown
        if (!commandFunc) {
            logger = upt.commands.help();
            command = 'help';
            // If the user requested help, show the command's help
            // Do the same if the actual command is a group of other commands (e.g.: cache)
        } else if (options.help || !commandFunc.line) {
            logger = upt.commands.help(command);
            command = 'help';
            // Call the line method
        } else {
            logger = commandFunc.line(process.argv);

            // If the method failed to interpret the process arguments
            // show the command help
            if (!logger) {
                logger = upt.commands.help(command);
                command = 'help';
            }
        }

        // Get the renderer and configure it with the executed command
        renderer = cli.getRenderer(command, logger.json, upt.config);

        logger
                .on('end', function (data) {
                    if (!upt.config.silent && !upt.config.quiet) {
                        renderer.end(data);
                    }
                })
                .on('error', function (err) {
                    if (levels.error >= loglevel) {
                        renderer.error(err);
                    }

                    process.exit(1);
                })
                .on('log', function (log) {
                    if (levels[log.level] >= loglevel) {
                        renderer.log(log);
                    }
                })
                .on('prompt', function (prompt, callback) {
                    renderer.prompt(prompt)
                            .then(function (answer) {
                                callback(answer);
                            });
                });

        // Warn if HOME is not SET
        if (!osenv.home()) {
            logger.warn('no-home', 'HOME not set, user configuration will not be loaded');
        }

        if (upt.config.interactive) {
            var updateNotifier = require('update-notifier');

            // Check for newer version of Upt
            var notifier = updateNotifier({
                packageName: pkg.name,
                packageVersion: pkg.version
            });

            if (notifier.update && levels.info >= loglevel) {
                notifier.notify();
            }
        }
    });

});
Beispiel #13
0
SvnResolver.clearRuntimeCache = function () {
    // Reset cache for branches, tags, etc
    mout.object.forOwn(SvnResolver._cache, function (lru) {
        lru.reset();
    });
};
Beispiel #14
0
    which.sync('svn');
    hasSvn = true;
} catch (ex) {
    hasSvn = false;
}

function SvnResolver(decEndpoint, config, logger) {
    Resolver.call(this, decEndpoint, config, logger);

    if (!hasSvn) {
        throw createError('svn is not installed or not in the PATH', 'ENOSVN');
    }
}

util.inherits(SvnResolver, Resolver);
mout.object.mixIn(SvnResolver, Resolver);

// -----------------

SvnResolver.getSource = function (source) {
    var uri = this._source || source;

    return uri
        .replace(/^svn\+(https?|file):\/\//i, '$1://')  // Change svn+http or svn+https or svn+file to http(s), file respectively
        .replace('svn://', 'http://')  // Change svn to http
        .replace(/\/+$/, '');  // Remove trailing slashes
};

SvnResolver.prototype._hasNew = function (canonicalDir, pkgMeta) {
    var oldResolution = pkgMeta._resolution || {};
 mout.object.forOwn(setup.resolved, function (decEndpoint, name) {
     decEndpoint.dependants = mout.object.values(decEndpoint.dependants);
     this._resolved[name] = [decEndpoint];
     this._installed[name] = decEndpoint.pkgMeta;
 }, this);
Beispiel #16
0
 mout.object.forOwn(flattened, function (node) {
     if (names.indexOf(node.endpoint.name) !== -1) {
         children.push.apply(children, mout.object.keys(node.bowerDependencies));
     }
 });
Manager.prototype._dissect = function () {
    var err;
    var componentsDir;
    var promise = Q.resolve();
    var suitables = {};
    var that = this;

    // If something failed, reject the whole resolve promise
    // with the first error
    if (this._hasFailed) {
        clearTimeout(this._failFastTimeout); // Cancel fail fast timeout

        err = mout.object.values(this._failed)[0][0];
        this._deferred.reject(err);
        return;
    }

    // Find a suitable version for each package name
    mout.object.forOwn(this._resolved, function (decEndpoints, name) {
        var semvers;
        var nonSemvers;

        // Filter semver ones
        semvers = decEndpoints.filter(function (decEndpoint) {
            return !!decEndpoint.pkgMeta.version;
        });

        // Sort semver ones DESC
        semvers.sort(function (first, second) {
            var result = semver.rcompare(first.pkgMeta.version, second.pkgMeta.version);

            // If they are equal and one of them is a wildcard target,
            // give lower priority
            if (!result) {
                if (first.target === '*') {
                    return 1;
                }
                if (second.target === '*') {
                    return -1;
                }
            }

            return result;
        });

        // Convert wildcard targets to semver range targets if they are newly
        // Note that this can only be made if they can be targetable
        // If they are not, the resolver is incapable of handling targets
        semvers.forEach(function (decEndpoint) {
            if (decEndpoint.newly && decEndpoint.target === '*' && !decEndpoint.untargetable) {
                decEndpoint.target = '~' + decEndpoint.pkgMeta.version;
                decEndpoint.originalTarget = '*';
            }
        });

        // Filter non-semver ones
        nonSemvers = decEndpoints.filter(function (decEndpoint) {
            return !decEndpoint.pkgMeta.version;
        });

        promise = promise.then(function () {
            return that._electSuitable(name, semvers, nonSemvers)
            .then(function (suitable) {
                suitables[name] = suitable;
            });
        });
    }, this);

    // After a suitable version has been elected for every package
    promise
    .then(function () {
        // Look for extraneous resolutions
        mout.object.forOwn(this._resolutions, function (resolution, name) {
            if (this._conflicted[name]) {
                return;
            }

            this._logger.info('resolution', 'Removed unnecessary ' + name + '#' + resolution + ' resolution', {
                name: name,
                resolution: resolution,
                action: 'delete'
            });

            delete this._resolutions[name];
        }, this);

        // Filter only packages that need to be installed
        componentsDir = path.resolve(that._config.cwd, that._config.directory);
        this._dissected = mout.object.filter(suitables, function (decEndpoint, name) {
            var installedMeta = this._installed[name];
            var dst;

            // Analyse a few props
            if (installedMeta &&
                installedMeta._target === decEndpoint.target &&
                installedMeta._originalSource === decEndpoint.source &&
                installedMeta._release === decEndpoint.pkgMeta._release
            ) {
                return false;
            }

            // Skip if source is the same as dest
            dst = path.join(componentsDir, name);
            if (dst === decEndpoint.canonicalDir) {
                return false;
            }

            return true;
        }, this);

        // Resolve with meaningful data
        return mout.object.map(this._dissected, function (decEndpoint) {
            return this.toData(decEndpoint);
        }, this);
    }.bind(this))
    .then(this._deferred.resolve, this._deferred.reject);
};
Beispiel #18
0
 nodes.forEach(function (node) {
     children.push.apply(children, mout.object.keys(node.bowerDependencies));
 });
Beispiel #19
0
    it('should recognize GitHub endpoints correctly', function (next) {
        var promise = Q.resolve();
        var gitHub;
        var nonGitHub;

        gitHub = {
            // git:
            'git://github.com/user/project': 'git://github.com/user/project.git',
            'git://github.com/user/project/': 'git://github.com/user/project.git',
            'git://github.com/user/project.git': 'git://github.com/user/project.git',
            'git://github.com/user/project.git/': 'git://github.com/user/project.git',

            // git@:
            'git@github.com:user/project': 'git@github.com:user/project.git',
            'git@github.com:user/project/': 'git@github.com:user/project.git',
            'git@github.com:user/project.git': 'git@github.com:user/project.git',
            'git@github.com:user/project.git/': 'git@github.com:user/project.git',

            // git+ssh:
            'git+ssh://git@github.com:project/blah': 'ssh://git@github.com:project/blah.git',
            'git+ssh://git@github.com:project/blah/': 'ssh://git@github.com:project/blah.git',
            'git+ssh://git@github.com:project/blah.git': 'ssh://git@github.com:project/blah.git',
            'git+ssh://git@github.com:project/blah.git/': 'ssh://git@github.com:project/blah.git',
            'git+ssh://git@github.com/project/blah': 'ssh://git@github.com/project/blah.git',
            'git+ssh://git@github.com/project/blah/': 'ssh://git@github.com/project/blah.git',
            'git+ssh://git@github.com/project/blah.git': 'ssh://git@github.com/project/blah.git',
            'git+ssh://git@github.com/project/blah.git/': 'ssh://git@github.com/project/blah.git',

            // git+http
            'git+http://github.com/project/blah': 'http://github.com/project/blah.git',
            'git+http://github.com/project/blah/': 'http://github.com/project/blah.git',
            'git+http://github.com/project/blah.git': 'http://github.com/project/blah.git',
            'git+http://github.com/project/blah.git/': 'http://github.com/project/blah.git',
            'git+http://user@github.com/project/blah': 'http://user@github.com/project/blah.git',
            'git+http://user@github.com/project/blah/': 'http://user@github.com/project/blah.git',
            'git+http://user@github.com/project/blah.git': 'http://user@github.com/project/blah.git',
            'git+http://user@github.com/project/blah.git/': 'http://user@github.com/project/blah.git',

            // git+https
            'git+https://github.com/project/blah': 'https://github.com/project/blah.git',
            'git+https://github.com/project/blah/': 'https://github.com/project/blah.git',
            'git+https://github.com/project/blah.git': 'https://github.com/project/blah.git',
            'git+https://github.com/project/blah.git/': 'https://github.com/project/blah.git',
            'git+https://user@github.com/project/blah': 'https://user@github.com/project/blah.git',
            'git+https://user@github.com/project/blah/': 'https://user@github.com/project/blah.git',
            'git+https://user@github.com/project/blah.git': 'https://user@github.com/project/blah.git',
            'git+https://user@github.com/project/blah.git/': 'https://user@github.com/project/blah.git',

            // ssh .git$
            'ssh://git@github.com:project/blah.git': 'ssh://git@github.com:project/blah.git',
            'ssh://git@github.com:project/blah.git/': 'ssh://git@github.com:project/blah.git',
            'ssh://git@github.com/project/blah.git': 'ssh://git@github.com/project/blah.git',
            'ssh://git@github.com/project/blah.git/': 'ssh://git@github.com/project/blah.git',

            // http .git$
            'http://github.com/project/blah.git': 'http://github.com/project/blah.git',
            'http://github.com/project/blah.git/': 'http://github.com/project/blah.git',
            'http://user@github.com/project/blah.git': 'http://user@github.com/project/blah.git',
            'http://user@github.com/project/blah.git/': 'http://user@github.com/project/blah.git',

            // https
            'https://github.com/project/blah.git': 'https://github.com/project/blah.git',
            'https://github.com/project/blah.git/': 'https://github.com/project/blah.git',
            'https://user@github.com/project/blah.git': 'https://user@github.com/project/blah.git',
            'https://user@github.com/project/blah.git/': 'https://user@github.com/project/blah.git',

            // shorthand
            'bower/bower': 'git://github.com/bower/bower.git'
        };

        nonGitHub = [
            'git://github.com/user/project/bleh.git',
            'git://xxxxgithub.com/user/project.git',
            'git@xxxxgithub.com:user:project.git',
            'git@xxxxgithub.com:user/project.git',
            'git+ssh://git@xxxxgithub.com:user/project',
            'git+ssh://git@xxxxgithub.com/user/project',
            'git+http://user@xxxxgithub.com/user/project',
            'git+https://user@xxxxgithub.com/user/project',
            'ssh://git@xxxxgithub.com:user/project.git',
            'ssh://git@xxxxgithub.com/user/project.git',
            'http://xxxxgithub.com/user/project.git',
            'https://xxxxgithub.com/user/project.git',
            'http://user@xxxxgithub.com/user/project.git',
            'https://user@xxxxgithub.com/user/project.git'
        ];

        // Test GitHub ones
        mout.object.forOwn(gitHub, function (value, key) {
            // Test without name and target
            promise = promise.then(function () {
                return callFactory({ source: key });
            })
            .then(function (resolver) {
                expect(resolver).to.be.a(resolvers.GitHub);
                expect(resolver.getSource()).to.equal(value);
                expect(resolver.getTarget()).to.equal('*');
            });

            // Test with target
            promise = promise.then(function () {
                return callFactory({ source: key, target: 'commit-ish' });
            })
            .then(function (resolver) {
                if (value) {
                    expect(resolver).to.be.a(resolvers.GitHub);
                    expect(resolver.getSource()).to.equal(value);
                    expect(resolver.getTarget()).to.equal('commit-ish');
                } else {
                    expect(resolver).to.not.be.a(resolvers.GitHub);
                }
            });

            // Test with name
            promise = promise.then(function () {
                return callFactory({ name: 'foo', source: key });
            })
            .then(function (resolver) {
                if (value) {
                    expect(resolver).to.be.a(resolvers.GitHub);
                    expect(resolver.getSource()).to.equal(value);
                    expect(resolver.getName()).to.equal('foo');
                    expect(resolver.getTarget()).to.equal('*');
                } else {
                    expect(resolver).to.not.be.a(resolvers.GitHub);
                }
            });
        });

        // Test similar to GitHub but not real GitHub
        nonGitHub.forEach(function (value) {
            promise = promise.then(function () {
                return callFactory({ source: value });
            })
            .then(function (resolver) {
                expect(resolver).to.not.be.a(resolvers.GitHub);
                expect(resolver).to.be.a(resolvers.GitRemote);
            });
        });

        promise
        .then(next.bind(next, null))
        .done();
    });
Beispiel #20
0
 var depsSatisfied = function (packageName) {
     return mout.array.difference(mout.object.keys(packages[packageName].dependencies), installed, ordered).length === 0;
 };
Beispiel #21
0
    it('should recognize git remote endpoints correctly', function (next) {
        var promise = Q.resolve();
        var endpoints;

        endpoints = {
            // git:
            'git://hostname.com/user/project': 'git://hostname.com/user/project',
            'git://hostname.com/user/project/': 'git://hostname.com/user/project',
            'git://hostname.com/user/project.git': 'git://hostname.com/user/project.git',
            'git://hostname.com/user/project.git/': 'git://hostname.com/user/project.git',

            // git@:
            'git@hostname.com:user/project': 'git@hostname.com:user/project',
            'git@hostname.com:user/project/': 'git@hostname.com:user/project',
            'git@hostname.com:user/project.git': 'git@hostname.com:user/project.git',
            'git@hostname.com:user/project.git/': 'git@hostname.com:user/project.git',

            // git+ssh:
            'git+ssh://user@hostname.com:project': 'ssh://user@hostname.com:project',
            'git+ssh://user@hostname.com:project/': 'ssh://user@hostname.com:project',
            'git+ssh://user@hostname.com:project.git': 'ssh://user@hostname.com:project.git',
            'git+ssh://user@hostname.com:project.git/': 'ssh://user@hostname.com:project.git',
            'git+ssh://user@hostname.com/project': 'ssh://user@hostname.com/project',
            'git+ssh://user@hostname.com/project/': 'ssh://user@hostname.com/project',
            'git+ssh://user@hostname.com/project.git': 'ssh://user@hostname.com/project.git',
            'git+ssh://user@hostname.com/project.git/': 'ssh://user@hostname.com/project.git',

            // git+http
            'git+http://hostname.com/project/blah': 'http://hostname.com/project/blah',
            'git+http://hostname.com/project/blah/': 'http://hostname.com/project/blah',
            'git+http://hostname.com/project/blah.git': 'http://hostname.com/project/blah.git',
            'git+http://hostname.com/project/blah.git/': 'http://hostname.com/project/blah.git',
            'git+http://user@hostname.com/project/blah': 'http://user@hostname.com/project/blah',
            'git+http://user@hostname.com/project/blah/': 'http://user@hostname.com/project/blah',
            'git+http://user@hostname.com/project/blah.git': 'http://user@hostname.com/project/blah.git',
            'git+http://user@hostname.com/project/blah.git/': 'http://user@hostname.com/project/blah.git',

            // git+https
            'git+https://hostname.com/project/blah': 'https://hostname.com/project/blah',
            'git+https://hostname.com/project/blah/': 'https://hostname.com/project/blah',
            'git+https://hostname.com/project/blah.git': 'https://hostname.com/project/blah.git',
            'git+https://hostname.com/project/blah.git/': 'https://hostname.com/project/blah.git',
            'git+https://user@hostname.com/project/blah': 'https://user@hostname.com/project/blah',
            'git+https://user@hostname.com/project/blah/': 'https://user@hostname.com/project/blah',
            'git+https://user@hostname.com/project/blah.git': 'https://user@hostname.com/project/blah.git',
            'git+https://user@hostname.com/project/blah.git/': 'https://user@hostname.com/project/blah.git',

            // ssh .git$
            'ssh://user@hostname.com:project.git': 'ssh://user@hostname.com:project.git',
            'ssh://user@hostname.com:project.git/': 'ssh://user@hostname.com:project.git',
            'ssh://user@hostname.com/project.git': 'ssh://user@hostname.com/project.git',
            'ssh://user@hostname.com/project.git/': 'ssh://user@hostname.com/project.git',

            // http .git$
            'http://hostname.com/project.git': 'http://hostname.com/project.git',
            'http://hostname.com/project.git/': 'http://hostname.com/project.git',
            'http://user@hostname.com/project.git': 'http://user@hostname.com/project.git',
            'http://user@hostname.com/project.git/': 'http://user@hostname.com/project.git',

            // https .git$
            'https://hostname.com/project.git': 'https://hostname.com/project.git',
            'https://hostname.com/project.git/': 'https://hostname.com/project.git',
            'https://user@hostname.com/project.git': 'https://user@hostname.com/project.git',
            'https://user@hostname.com/project.git/': 'https://user@hostname.com/project.git',

            // shorthand
            'bower/bower': 'git://github.com/bower/bower.git'
        };

        mout.object.forOwn(endpoints, function (value, key) {
            // Test without name and target
            promise = promise.then(function () {
                return callFactory({ source: key });
            })
            .then(function (resolver) {
                expect(resolver).to.be.a(resolvers.GitRemote);
                expect(resolver).to.not.be(resolvers.GitHub);
                expect(resolver.getSource()).to.equal(value);
                expect(resolver.getTarget()).to.equal('*');
            });

            // Test with target
            promise = promise.then(function () {
                return callFactory({ source: key, target: 'commit-ish' });
            })
            .then(function (resolver) {
                expect(resolver).to.be.a(resolvers.GitRemote);
                expect(resolver).to.not.be(resolvers.GitHub);
                expect(resolver.getSource()).to.equal(value);
                expect(resolver.getTarget()).to.equal('commit-ish');
            });

            // Test with name
            promise = promise.then(function () {
                return callFactory({ name: 'foo', source: key });
            })
            .then(function (resolver) {
                expect(resolver).to.be.a(resolvers.GitRemote);
                expect(resolver).to.not.be(resolvers.GitHub);
                expect(resolver.getSource()).to.equal(value);
                expect(resolver.getName()).to.equal('foo');
                expect(resolver.getTarget()).to.equal('*');
            });
        });

        promise
        .then(next.bind(next, null))
        .done();
    });
Beispiel #22
0
Manager.prototype._dissect = function () {
  var err;
  var componentsDir;
  var promise = Q.resolve();
  var suitables = {};
  var that = this;

  // If something failed, reject the whole resolve promise
  // with the first error
  if (this._hasFailed) {
    clearTimeout(this._failFastTimeout); // Cancel fail fast timeout

    err = mout.object.values(this._failed)[0][0];
    this._deferred.reject(err);
    return;
  }

  // Find a suitable version for each package name
  mout.object.forOwn(this._resolved, function (decEndpoints, name) {
    var semvers;
    var nonSemvers;

    // Filter out non-semver ones
    semvers = decEndpoints.filter(function (decEndpoint) {
      return !!decEndpoint.pkgMeta.version;
    });

    // Sort semver ones DESC
    semvers.sort(function (first, second) {
      var result = semver.rcompare(first.pkgMeta.version, second.pkgMeta.version);

      // If they are equal and one of them is a wildcard target,
      // give lower priority
      if (!result) {
        if (first.target === '*') {
          return 1;
        }
        if (second.target === '*') {
          return -1;
        }
      }

      return result;
    });

    // Convert wildcard targets to semver range targets if they are newly
    // Note that this can only be made if they can be targetable
    // If they are not, the resolver is incapable of handling targets
    semvers.forEach(function (decEndpoint) {
      if (decEndpoint.newly && decEndpoint.target === '*' && !decEndpoint.untargetable) {
        decEndpoint.target = '^' + decEndpoint.pkgMeta.version;
        decEndpoint.originalTarget = '*';
      }
    });

    // Filter non-semver ones
    nonSemvers = decEndpoints.filter(function (decEndpoint) {
      return !decEndpoint.pkgMeta.version;
    });

    promise = promise.then(function () {
      return that._electSuitable(name, semvers, nonSemvers)
        .then(function (suitable) {
          suitables[name] = suitable;
        });
    });
  }, this);

  // After a suitable version has been elected for every package
  promise
    .then(function () {
      // Look for extraneous resolutions
      mout.object.forOwn(this._resolutions, function (resolution, name) {
        if (this._conflicted[name]) {
          return;
        }

        // Packages that didn't have any conflicts in resolution yet have
        // "resolution" entry in library.json are deemed unnecessary
        //
        // Probably in future we want to use them for force given resolution, but for now...
        // See: https://github.com/bower/bower/issues/1362
        this._logger.warn('extra-resolution', 'Unnecessary resolution: ' + name + '#' + resolution, {
          name: name,
          resolution: resolution,
          action: 'delete'
        });
      }, this);

      // Filter only libraries that need to be installed
      componentsDir = relativeToBaseDir(this._config.cwd)(this._config.directory);
      this._dissected = mout.object.filter(suitables, function (decEndpoint, name) {
        var installedMeta = this._installed[name];
        var dst;

        // Skip linked dependencies
        if (decEndpoint.linked) {
          return false;
        }

        // Skip if source is the same as dest
        // FIXME: Shoudn't we force installation if force flag is used here?
        dst = path.join(componentsDir, name);
        if (dst === decEndpoint.canonicalDir) {
          return false;
        }

        // We skip installing decEndpoint, if:
        //   1. We have installed package with the same name
        //   2. Packages have matching meta fields (needs explanation)
        //   3. We didn't force the installation
        if (installedMeta &&
          installedMeta._target === decEndpoint.target &&
          installedMeta._originalSource === decEndpoint.source &&
          installedMeta._release === decEndpoint.pkgMeta._release
        ) {
          return this._config.force;
        }

        return true;
      }, this);
    }.bind(this))
    .then(this._deferred.resolve, this._deferred.reject);
};
Beispiel #23
0
function promptUser(logger, json) {
    var questions = [
        {
            'name': 'name',
            'message': 'name',
            'default': json.name,
            'type': 'input'
        },
        {
            'name': 'version',
            'message': 'version',
            'default': json.version,
            'type': 'input'
        },
        {
            'name': 'description',
            'message': 'description',
            'default': json.description,
            'type': 'input'
        },
        {
            'name': 'main',
            'message': 'main file',
            'default': json.main,
            'type': 'input'
        },
        {
            'name': 'keywords',
            'message': 'keywords',
            'default': json.keywords ? json.keywords.toString() : null,
            'type': 'input'
        },
        {
            'name': 'authors',
            'message': 'authors',
            'default': json.authors ? json.authors.toString() : null,
            'type': 'input'
        },
        {
            'name': 'license',
            'message': 'license',
            'default': json.license || 'MIT',
            'type': 'input'
        },
        {
            'name': 'homepage',
            'message': 'homepage',
            'default': json.homepage,
            'type': 'input'
        },
        {
            'name': 'dependencies',
            'message': 'set currently installed components as dependencies?',
            'default': !mout.object.size(json.dependencies) && !mout.object.size(json.devDependencies),
            'type': 'confirm'
        },
        {
            'name': 'ignore',
            'message': 'add commonly ignored files to ignore list?',
            'default': true,
            'type': 'confirm'
        },
        {
            'name': 'private',
            'message': 'would you like to mark this package as private which prevents it from being accidentally published to the registry?',
            'default': !!json.private,
            'type': 'confirm'
        }
    ];

    return Q.nfcall(logger.prompt.bind(logger), questions)
    .then(function (answers) {
        json.name = answers.name;
        json.version = answers.version;
        json.description = answers.description;
        json.main = answers.main;
        json.keywords = toArray(answers.keywords);
        json.authors = toArray(answers.authors, ',');
        json.license = answers.license;
        json.homepage = answers.homepage;
        json.private = answers.private || null;

        return [json, answers];
    });
}
function download(url, file, options) {
    var operation;
    var response;
    var deferred = Q.defer();
    var progressDelay = 8000;

    options = mout.object.mixIn({
        retries: 5,
        factor: 2,
        minTimeout: 1000,
        maxTimeout: 35000,
        randomize: true
    }, options || {});

    // Retry on network errors
    operation = retry.operation(options);
    operation.attempt(function () {
        var req;
        var writeStream;
        var contentLength;
        var bytesDownloaded = 0;

        req = progress(request(url, options), {
            delay: progressDelay
        })
        .on('response', function (res) {
            var status = res.statusCode;

            if (status < 200 || status >= 300) {
                return deferred.reject(createError('Status code of ' + status, 'EHTTP'));
            }

            response = res;
            contentLength = Number(res.headers['content-length']);
        })
        .on('data', function (data) {
            bytesDownloaded += data.length;
        })
        .on('progress', function (state) {
            deferred.notify(state);
        })
        .on('end', function () {
            // Check if the whole file was downloaded
            // In some unstable connections the ACK/FIN packet might be sent in the
            // middle of the download
            // See: https://github.com/joyent/node/issues/6143
            if (contentLength && bytesDownloaded < contentLength) {
                req.emit('error', createError('Transfer closed with ' + (contentLength - bytesDownloaded) + ' bytes remaining to read', 'EINCOMPLETE'));
            }
        })
        .on('error', function (error) {
            var timeout = operation._timeouts[0];

            // Reject if error is not a network error
            if (errorCodes.indexOf(error.code) === -1) {
                return deferred.reject(error);
            }

            // Next attempt will start reporting download progress immediately
            progressDelay = 0;

            // Check if there are more retries
            if (operation.retry(error)) {
                // Ensure that there are no more events from this request
                req.removeAllListeners();
                req.on('error', function () {});
                // Ensure that there are no more events from the write stream
                writeStream.removeAllListeners();
                writeStream.on('error', function () {});

                return deferred.notify({
                    retry: true,
                    delay: timeout,
                    error: error
                });
            }

            // No more retries, reject!
            deferred.reject(error);
        });

        // Pipe read stream to write stream
        writeStream = req
        .pipe(fs.createWriteStream(file))
        .on('error', deferred.reject)
        .on('close', function () {
            deferred.resolve(response);
        });
    });

    return deferred.promise;
}
Beispiel #25
0
 .then(function () {
     return mout.object.filter(packages, function (dir) {
         return !!dir;
     });
 });
 .then(function () {
     // Resolve with meaningful data
     return mout.object.map(that._dissected, function (decEndpoint) {
         return this.toData(decEndpoint);
     }, that);
 })
Beispiel #27
0
        throw createError('URL sources can\'t resolve targets', 'ENORESTARGET');
    }

    // If the name was guessed, remove the ? part
    if (this._guessedName) {
        pos = this._name.indexOf('?');
        if (pos !== -1) {
            this._name = path.basename(this._name.substr(0, pos));
        }
    }

    this._remote = url.parse(this._source);
}

util.inherits(UrlResolver, Resolver);
mout.object.mixIn(UrlResolver, Resolver);

// -----------------

UrlResolver.isTargetable = function () {
    return false;
};

UrlResolver.prototype._hasNew = function (canonicalDir, pkgMeta) {
    var oldCacheHeaders = pkgMeta._cacheHeaders || {};
    var reqHeaders = {};

    // If the previous cache headers contain an ETag,
    // send the "If-None-Match" header with it
    if (oldCacheHeaders.ETag) {
        reqHeaders['If-None-Match'] = oldCacheHeaders.ETag;
Manager.prototype._parseDependencies = function (decEndpoint, pkgMeta, jsonKey) {
    decEndpoint.dependencies = decEndpoint.dependencies || {};

    // Parse package dependencies
    mout.object.forOwn(pkgMeta[jsonKey], function (value, key) {
        var resolved;
        var fetching;
        var compatible;
        var childDecEndpoint = endpointParser.json2decomposed(key, value);

        // Check if a compatible one is already resolved
        // If there's one, we don't need to resolve it twice
        resolved = this._resolved[key];
        if (resolved) {
            // Find if there's one with the exact same target
            compatible = mout.array.find(resolved, function (resolved) {
                return childDecEndpoint.target === resolved.target;
            }, this);

            // If we found one, merge stuff instead of adding as resolved
            if (compatible) {
                decEndpoint.dependencies[key] = compatible;
                compatible.dependants.push(decEndpoint);
                compatible.dependants = this._uniquify(compatible.dependants);

                return;
            }

            // Find one that is compatible
            compatible = mout.array.find(resolved, function (resolved) {
                return this._areCompatible(childDecEndpoint, resolved);
            }, this);

            // If we found one, add as resolved
            // and copy resolved properties from the compatible one
            if (compatible) {
                decEndpoint.dependencies[key] = compatible;
                childDecEndpoint.canonicalDir = compatible.canonicalDir;
                childDecEndpoint.pkgMeta = compatible.pkgMeta;
                childDecEndpoint.dependencies = compatible.dependencies;
                childDecEndpoint.dependants = [decEndpoint];
                this._resolved[key].push(childDecEndpoint);

                return;
            }
        }

        // Check if a compatible one is being fetched
        // If there's one, we wait and reuse it to avoid resolving it twice
        fetching = this._fetching[key];
        if (fetching) {
            compatible = mout.array.find(fetching, function (fetching) {
                return this._areCompatible(childDecEndpoint, fetching);
            }, this);

            if (compatible) {
                compatible.promise
                .then(function () {
                    this._parseDependencies(decEndpoint, pkgMeta, jsonKey);
                }.bind(this));
                return;
            }
        }

        // Mark endpoint as unresolvable if the parent is also unresolvable
        childDecEndpoint.unresolvable = !!decEndpoint.unresolvable;

        // Otherwise, just fetch it from the repository
        decEndpoint.dependencies[key] = childDecEndpoint;
        childDecEndpoint.dependants = [decEndpoint];
        this._fetch(childDecEndpoint);
    }, this);
};
Beispiel #29
0
 .then(function () {
     // If the resolutions is empty, delete key
     if (!mout.object.size(this._json.resolutions)) {
         delete this._json.resolutions;
     }
 }.bind(this))
Beispiel #30
0
		function resolveName( name, values ) {
			return mout.object.get( values, name ) || "";
		}