<commit_before>// Load modules

var Util = require('util');
var Stream = require('stream');
var Fs = require('fs');
var Zlib = require('zlib');
var Lab = require('lab');
var Shot = require('../lib');

// Declare internals

var internals = {};

// Test shortcuts

var lab = exports.lab = Lab.script();
var describe = lab.describe;
var it = lab.it;
var expect = Lab.expect;

describe('Shot', function () {

    describe('inject()', function () {

        it('returns non-chunked payload', function (done) {

            var output = 'example.com:8080|/hello';

            var dispatch = function (req, res) {

                res.writeHead(200, { 'Content-Type': 'text/plain', 'Content-Length': output.length });
                res.end(req.headers.host + '|' + req.url);
            };

            Shot.inject(dispatch, 'http://example.com:8080/hello', function (res) {

                expect(res.headers.date).to.exist;
                expect(res.headers.connection).to.exist;
                expect(res.headers['transfer-encoding']).to.not.exist;
                expect(res.payload).to.equal(output);
                done();
            });
        });

        it('returns single buffer payload', function (done) {

            var dispatch = function (req, res) {

                res.writeHead(200, { 'Content-Type': 'text/plain' });
                res.end(req.headers.host + '|' + req.url);
            };

            Shot.inject(dispatch, { url: 'http://example.com:8080/hello' }, function (res) {

                expect(res.headers.date).to.exist;
                expect(res.headers.connection).to.exist;
                expect(res.headers['transfer-encoding']).to.equal('chunked');
                expect(res.payload).to.equal('example.com:8080|/hello');
                done();
            });
        });

        it('passes headers', function (done) {

            var dispatch = function (req, res) {

                res.writeHead(200, { 'Content-Type': 'text/plain' });
                res.end(req.headers.super);
            };

            Shot.inject(dispatch, { method: 'get', url: 'http://example.com:8080/hello', headers: { Super: 'duper' } }, function (res) {

                expect(res.payload).to.equal('duper');
                done();
            });
        });

        it('leaves user-agent unmodified', function (done) {

            var dispatch = function (req, res) {

                res.writeHead(200, { 'Content-Type': 'text/plain' });
                res.end(req.headers['user-agent']);
            };

            Shot.inject(dispatch, { method: 'get', url: 'http://example.com:8080/hello', headers: { 'user-agent': 'duper' } }, function (res) {

                expect(res.payload).to.equal('duper');
                done();
            });
        });

        it('returns chunked payload', function (done) {

            var dispatch = function (req, res) {

                res.writeHead(200, 'OK');
                res.write('a');
                res.write('b');
                res.end();
            };

            Shot.inject(dispatch, { method: 'get', url: '/' }, function (res) {

                expect(res.headers.date).to.exist;
                expect(res.headers.connection).to.exist;
                expect(res.headers['transfer-encoding']).to.equal('chunked');
                expect(res.payload).to.equal('ab');
                done();
            });
        });

        it('returns chunked payload with trailer', function (done) {

            var dispatch = function (req, res) {

                res.setHeader('Trailer', 'Server-Authorization');
                res.setHeader('Transfer-Encoding', 'chunked');
                res.writeHead(200, 'OK');
                res.write('a');
                res.write('b');
                res.addTrailers({ 'Test': 123 });
                res.end();
            };

            Shot.inject(dispatch, { method: 'get', url: '/' }, function (res) {

                expect(res.payload).to.equal('ab');
                expect(res.headers.test).to.equal('123');
                done();
            });
        });

        it('parses zipped payload', function (done) {

            var dispatch = function (req, res) {

                res.writeHead(200, 'OK');
                var stream = Fs.createReadStream('./package.json');
                stream.pipe(Zlib.createGzip()).pipe(res);
            };

            Shot.inject(dispatch, { method: 'get', url: '/' }, function (res) {

                Fs.readFile('./package.json', { encoding: 'utf-8' }, function (err, file) {

                    Zlib.unzip(new Buffer(res.payload, 'binary'), function (err, unzipped) {

                        expect(err).to.not.exist;
                        expect(unzipped.toString('utf-8')).to.deep.equal(file);
                        done();
                    });
                });
            });
        });

        it('returns multi buffer payload', function (done) {

            var dispatch = function (req, res) {

                res.writeHead(200);
                res.write('a');
                res.write(new Buffer('b'));
                res.end();
            };

            Shot.inject(dispatch, { method: 'get', url: '/' }, function (res) {

                expect(res.payload).to.equal('ab');
                done();
            });
        });

        it('returns null payload', function (done) {

            var dispatch = function (req, res) {

                res.writeHead(200, { 'Content-Length': 0 });
                res.end();
            };

            Shot.inject(dispatch, { method: 'get', url: '/' }, function (res) {

                expect(res.payload).to.equal('');
                done();
            });
        });

        it('allows ending twice', function (done) {

            var dispatch = function (req, res) {

                res.writeHead(200, { 'Content-Length': 0 });
                res.end();
                res.end();
            };

            Shot.inject(dispatch, { method: 'get', url: '/' }, function (res) {

                expect(res.payload).to.equal('');
                done();
            });
        });

        it('identifies injection object', function (done) {

            var dispatch = function (req, res) {

                expect(Shot.isInjection(req)).to.equal(true);
                expect(Shot.isInjection(res)).to.equal(true);

                res.writeHead(200, { 'Content-Length': 0 });
                res.end();
            };

            Shot.inject(dispatch, { method: 'get', url: '/' }, function (res) {

                done();
            });
        });

        it('pipes response', function (done) {

            var Read = function () {

                Stream.Readable.call(this);
            };

            Util.inherits(Read, Stream.Readable);

            Read.prototype._read = function (size) {

                this.push('hi');
                this.push(null);
            };

            var finished = false;
            var dispatch = function (req, res) {

                res.writeHead(200);
                var stream = new Read();

                res.on('finish', function () {

                    finished = true;
                });

                stream.pipe(res);
            };

            Shot.inject(dispatch, { method: 'get', url: '/' }, function (res) {

                expect(finished).to.equal(true);
                expect(res.payload).to.equal('hi');
                done();
            });
        });

        it('pipes response with old stream', function (done) {

            var Read = function () {

                Stream.Readable.call(this);
            };

            Util.inherits(Read, Stream.Readable);

            Read.prototype._read = function (size) {

                this.push('hi');
                this.push(null);
            };

            var finished = false;
            var dispatch = function (req, res) {

                res.writeHead(200);
                var stream = new Read();
                stream.pause();
                var stream2 = new Stream.Readable().wrap(stream);
                stream.resume();

                res.on('finish', function () {

                    finished = true;
                });

                stream2.pipe(res);
            };

            Shot.inject(dispatch, { method: 'get', url: '/' }, function (res) {

                expect(finished).to.equal(true);
                expect(res.payload).to.equal('hi');
                done();
            });
        });

        it('echos object payload', function (done) {

            var dispatch = function (req, res) {

                res.writeHead(200, { 'content-type': req.headers['content-type'] });
                req.pipe(res);
            };

            Shot.inject(dispatch, { method: 'post', url: '/test', payload: { a: 1 } }, function (res) {

                expect(res.headers['content-type']).to.equal('application/json');
                expect(res.payload).to.equal('{"a":1}');
                done();
            });
        });

        it('echos object payload without payload', function (done) {

            var dispatch = function (req, res) {

                res.writeHead(200);
                req.pipe(res);
            };

            Shot.inject(dispatch, { method: 'post', url: '/test' }, function (res) {

                expect(res.payload).to.equal('');
                done();
            });
        });

        it('retains content-type header', function (done) {

            var dispatch = function (req, res) {

                res.writeHead(200, { 'content-type': req.headers['content-type'] });
                req.pipe(res);
            };

            Shot.inject(dispatch, { method: 'post', url: '/test', payload: { a: 1 }, headers: { 'content-type': 'something' } }, function (res) {

                expect(res.headers['content-type']).to.equal('something');
                expect(res.payload).to.equal('{"a":1}');
                done();
            });
        });
    });

    describe('writeHead()', function () {

        it('returns single buffer payload', function (done) {

            var reply = 'Hello World';
            var dispatch = function (req, res) {

                res.writeHead(200, 'OK', { 'Content-Type': 'text/plain', 'Content-Length': reply.length });
                res.end(reply);
            };

            Shot.inject(dispatch, { method: 'get', url: '/' }, function (res) {

                expect(res.payload).to.equal(reply);
                done();
            });
        });
    });

    describe('_read()', function () {

        it('plays payload', function (done) {

            var dispatch = function (req, res) {

                var buffer = '';
                req.on('readable', function () {

                    buffer += req.read() || '';
                });

                req.on('error', function (err) {
                });

                req.on('close', function () {
                });

                req.on('end', function () {

                    res.writeHead(200, { 'Content-Length': 0 });
                    res.end(buffer);
                    req.destroy();
                });
            };

            var body = 'something special just for you';
            Shot.inject(dispatch, { method: 'get', url: '/', payload: body }, function (res) {

                expect(res.payload).to.equal(body);
                done();
            });
        });

        it('simulates split', function (done) {

            var dispatch = function (req, res) {

                var buffer = '';
                req.on('readable', function () {

                    buffer += req.read() || '';
                });

                req.on('error', function (err) {
                });

                req.on('close', function () {
                });

                req.on('end', function () {

                    res.writeHead(200, { 'Content-Length': 0 });
                    res.end(buffer);
                    req.destroy();
                });
            };

            var body = 'something special just for you';
            Shot.inject(dispatch, { method: 'get', url: '/', payload: body, simulate: { split: true } }, function (res) {

                expect(res.payload).to.equal(body);
                done();
            });
        });

        it('simulates error', function (done) {

            var dispatch = function (req, res) {

                req.on('readable', function () {
                });

                req.on('error', function (err) {

                    res.writeHead(200, { 'Content-Length': 0 });
                    res.end('error');
                });
            };

            var body = 'something special just for you';
            Shot.inject(dispatch, { method: 'get', url: '/', payload: body, simulate: { error: true } }, function (res) {

                expect(res.payload).to.equal('error');
                done();
            });
        });

        it('simulates no end without payload', function (done) {

            var end = false;
            var dispatch = function (req, res) {

                req.resume();
                req.on('end', function () {

                    end = true;
                });
            };

            var replied = false;
            Shot.inject(dispatch, { method: 'get', url: '/', simulate: { end: false } }, function (res) {

                replied = true;
            });

            setTimeout(function () {

                expect(end).to.equal(false);
                expect(replied).to.equal(false);
                done();
            }, 10);
        });

        it('simulates no end with payload', function (done) {

            var end = false;
            var dispatch = function (req, res) {

                req.resume();
                req.on('end', function () {

                    end = true;
                });
            };

            var replied = false;
            Shot.inject(dispatch, { method: 'get', url: '/', payload: '1234567', simulate: { end: false } }, function (res) {

                replied = true;
            });

            setTimeout(function () {

                expect(end).to.equal(false);
                expect(replied).to.equal(false);
                done();
            }, 10);
        });

        it('simulates close', function (done) {

            var dispatch = function (req, res) {

                var buffer = '';
                req.on('readable', function () {

                    buffer += req.read() || '';
                });

                req.on('error', function (err) {
                });

                req.on('close', function () {

                    res.writeHead(200, { 'Content-Length': 0 });
                    res.end('close');
                });

                req.on('end', function () {
                });
            };

            var body = 'something special just for you';
            Shot.inject(dispatch, { method: 'get', url: '/', payload: body, simulate: { close: true } }, function (res) {

                expect(res.payload).to.equal('close');
                done();
            });
        });
    });
});

<commit_msg>renamed test block.<commit_after>// Load modules

var Util = require('util');
var Stream = require('stream');
var Fs = require('fs');
var Zlib = require('zlib');
var Lab = require('lab');
var Shot = require('../lib');

// Declare internals

var internals = {};

// Test shortcuts

var lab = exports.lab = Lab.script();
var describe = lab.describe;
var it = lab.it;
var expect = Lab.expect;

describe('shot', function () {

    describe('inject()', function () {

        it('returns non-chunked payload', function (done) {

            var output = 'example.com:8080|/hello';

            var dispatch = function (req, res) {

                res.writeHead(200, { 'Content-Type': 'text/plain', 'Content-Length': output.length });
                res.end(req.headers.host + '|' + req.url);
            };

            Shot.inject(dispatch, 'http://example.com:8080/hello', function (res) {

                expect(res.headers.date).to.exist;
                expect(res.headers.connection).to.exist;
                expect(res.headers['transfer-encoding']).to.not.exist;
                expect(res.payload).to.equal(output);
                done();
            });
        });

        it('returns single buffer payload', function (done) {

            var dispatch = function (req, res) {

                res.writeHead(200, { 'Content-Type': 'text/plain' });
                res.end(req.headers.host + '|' + req.url);
            };

            Shot.inject(dispatch, { url: 'http://example.com:8080/hello' }, function (res) {

                expect(res.headers.date).to.exist;
                expect(res.headers.connection).to.exist;
                expect(res.headers['transfer-encoding']).to.equal('chunked');
                expect(res.payload).to.equal('example.com:8080|/hello');
                done();
            });
        });

        it('passes headers', function (done) {

            var dispatch = function (req, res) {

                res.writeHead(200, { 'Content-Type': 'text/plain' });
                res.end(req.headers.super);
            };

            Shot.inject(dispatch, { method: 'get', url: 'http://example.com:8080/hello', headers: { Super: 'duper' } }, function (res) {

                expect(res.payload).to.equal('duper');
                done();
            });
        });

        it('leaves user-agent unmodified', function (done) {

            var dispatch = function (req, res) {

                res.writeHead(200, { 'Content-Type': 'text/plain' });
                res.end(req.headers['user-agent']);
            };

            Shot.inject(dispatch, { method: 'get', url: 'http://example.com:8080/hello', headers: { 'user-agent': 'duper' } }, function (res) {

                expect(res.payload).to.equal('duper');
                done();
            });
        });

        it('returns chunked payload', function (done) {

            var dispatch = function (req, res) {

                res.writeHead(200, 'OK');
                res.write('a');
                res.write('b');
                res.end();
            };

            Shot.inject(dispatch, { method: 'get', url: '/' }, function (res) {

                expect(res.headers.date).to.exist;
                expect(res.headers.connection).to.exist;
                expect(res.headers['transfer-encoding']).to.equal('chunked');
                expect(res.payload).to.equal('ab');
                done();
            });
        });

        it('returns chunked payload with trailer', function (done) {

            var dispatch = function (req, res) {

                res.setHeader('Trailer', 'Server-Authorization');
                res.setHeader('Transfer-Encoding', 'chunked');
                res.writeHead(200, 'OK');
                res.write('a');
                res.write('b');
                res.addTrailers({ 'Test': 123 });
                res.end();
            };

            Shot.inject(dispatch, { method: 'get', url: '/' }, function (res) {

                expect(res.payload).to.equal('ab');
                expect(res.headers.test).to.equal('123');
                done();
            });
        });

        it('parses zipped payload', function (done) {

            var dispatch = function (req, res) {

                res.writeHead(200, 'OK');
                var stream = Fs.createReadStream('./package.json');
                stream.pipe(Zlib.createGzip()).pipe(res);
            };

            Shot.inject(dispatch, { method: 'get', url: '/' }, function (res) {

                Fs.readFile('./package.json', { encoding: 'utf-8' }, function (err, file) {

                    Zlib.unzip(new Buffer(res.payload, 'binary'), function (err, unzipped) {

                        expect(err).to.not.exist;
                        expect(unzipped.toString('utf-8')).to.deep.equal(file);
                        done();
                    });
                });
            });
        });

        it('returns multi buffer payload', function (done) {

            var dispatch = function (req, res) {

                res.writeHead(200);
                res.write('a');
                res.write(new Buffer('b'));
                res.end();
            };

            Shot.inject(dispatch, { method: 'get', url: '/' }, function (res) {

                expect(res.payload).to.equal('ab');
                done();
            });
        });

        it('returns null payload', function (done) {

            var dispatch = function (req, res) {

                res.writeHead(200, { 'Content-Length': 0 });
                res.end();
            };

            Shot.inject(dispatch, { method: 'get', url: '/' }, function (res) {

                expect(res.payload).to.equal('');
                done();
            });
        });

        it('allows ending twice', function (done) {

            var dispatch = function (req, res) {

                res.writeHead(200, { 'Content-Length': 0 });
                res.end();
                res.end();
            };

            Shot.inject(dispatch, { method: 'get', url: '/' }, function (res) {

                expect(res.payload).to.equal('');
                done();
            });
        });

        it('identifies injection object', function (done) {

            var dispatch = function (req, res) {

                expect(Shot.isInjection(req)).to.equal(true);
                expect(Shot.isInjection(res)).to.equal(true);

                res.writeHead(200, { 'Content-Length': 0 });
                res.end();
            };

            Shot.inject(dispatch, { method: 'get', url: '/' }, function (res) {

                done();
            });
        });

        it('pipes response', function (done) {

            var Read = function () {

                Stream.Readable.call(this);
            };

            Util.inherits(Read, Stream.Readable);

            Read.prototype._read = function (size) {

                this.push('hi');
                this.push(null);
            };

            var finished = false;
            var dispatch = function (req, res) {

                res.writeHead(200);
                var stream = new Read();

                res.on('finish', function () {

                    finished = true;
                });

                stream.pipe(res);
            };

            Shot.inject(dispatch, { method: 'get', url: '/' }, function (res) {

                expect(finished).to.equal(true);
                expect(res.payload).to.equal('hi');
                done();
            });
        });

        it('pipes response with old stream', function (done) {

            var Read = function () {

                Stream.Readable.call(this);
            };

            Util.inherits(Read, Stream.Readable);

            Read.prototype._read = function (size) {

                this.push('hi');
                this.push(null);
            };

            var finished = false;
            var dispatch = function (req, res) {

                res.writeHead(200);
                var stream = new Read();
                stream.pause();
                var stream2 = new Stream.Readable().wrap(stream);
                stream.resume();

                res.on('finish', function () {

                    finished = true;
                });

                stream2.pipe(res);
            };

            Shot.inject(dispatch, { method: 'get', url: '/' }, function (res) {

                expect(finished).to.equal(true);
                expect(res.payload).to.equal('hi');
                done();
            });
        });

        it('echos object payload', function (done) {

            var dispatch = function (req, res) {

                res.writeHead(200, { 'content-type': req.headers['content-type'] });
                req.pipe(res);
            };

            Shot.inject(dispatch, { method: 'post', url: '/test', payload: { a: 1 } }, function (res) {

                expect(res.headers['content-type']).to.equal('application/json');
                expect(res.payload).to.equal('{"a":1}');
                done();
            });
        });

        it('echos object payload without payload', function (done) {

            var dispatch = function (req, res) {

                res.writeHead(200);
                req.pipe(res);
            };

            Shot.inject(dispatch, { method: 'post', url: '/test' }, function (res) {

                expect(res.payload).to.equal('');
                done();
            });
        });

        it('retains content-type header', function (done) {

            var dispatch = function (req, res) {

                res.writeHead(200, { 'content-type': req.headers['content-type'] });
                req.pipe(res);
            };

            Shot.inject(dispatch, { method: 'post', url: '/test', payload: { a: 1 }, headers: { 'content-type': 'something' } }, function (res) {

                expect(res.headers['content-type']).to.equal('something');
                expect(res.payload).to.equal('{"a":1}');
                done();
            });
        });
    });

    describe('writeHead()', function () {

        it('returns single buffer payload', function (done) {

            var reply = 'Hello World';
            var dispatch = function (req, res) {

                res.writeHead(200, 'OK', { 'Content-Type': 'text/plain', 'Content-Length': reply.length });
                res.end(reply);
            };

            Shot.inject(dispatch, { method: 'get', url: '/' }, function (res) {

                expect(res.payload).to.equal(reply);
                done();
            });
        });
    });

    describe('_read()', function () {

        it('plays payload', function (done) {

            var dispatch = function (req, res) {

                var buffer = '';
                req.on('readable', function () {

                    buffer += req.read() || '';
                });

                req.on('error', function (err) {
                });

                req.on('close', function () {
                });

                req.on('end', function () {

                    res.writeHead(200, { 'Content-Length': 0 });
                    res.end(buffer);
                    req.destroy();
                });
            };

            var body = 'something special just for you';
            Shot.inject(dispatch, { method: 'get', url: '/', payload: body }, function (res) {

                expect(res.payload).to.equal(body);
                done();
            });
        });

        it('simulates split', function (done) {

            var dispatch = function (req, res) {

                var buffer = '';
                req.on('readable', function () {

                    buffer += req.read() || '';
                });

                req.on('error', function (err) {
                });

                req.on('close', function () {
                });

                req.on('end', function () {

                    res.writeHead(200, { 'Content-Length': 0 });
                    res.end(buffer);
                    req.destroy();
                });
            };

            var body = 'something special just for you';
            Shot.inject(dispatch, { method: 'get', url: '/', payload: body, simulate: { split: true } }, function (res) {

                expect(res.payload).to.equal(body);
                done();
            });
        });

        it('simulates error', function (done) {

            var dispatch = function (req, res) {

                req.on('readable', function () {
                });

                req.on('error', function (err) {

                    res.writeHead(200, { 'Content-Length': 0 });
                    res.end('error');
                });
            };

            var body = 'something special just for you';
            Shot.inject(dispatch, { method: 'get', url: '/', payload: body, simulate: { error: true } }, function (res) {

                expect(res.payload).to.equal('error');
                done();
            });
        });

        it('simulates no end without payload', function (done) {

            var end = false;
            var dispatch = function (req, res) {

                req.resume();
                req.on('end', function () {

                    end = true;
                });
            };

            var replied = false;
            Shot.inject(dispatch, { method: 'get', url: '/', simulate: { end: false } }, function (res) {

                replied = true;
            });

            setTimeout(function () {

                expect(end).to.equal(false);
                expect(replied).to.equal(false);
                done();
            }, 10);
        });

        it('simulates no end with payload', function (done) {

            var end = false;
            var dispatch = function (req, res) {

                req.resume();
                req.on('end', function () {

                    end = true;
                });
            };

            var replied = false;
            Shot.inject(dispatch, { method: 'get', url: '/', payload: '1234567', simulate: { end: false } }, function (res) {

                replied = true;
            });

            setTimeout(function () {

                expect(end).to.equal(false);
                expect(replied).to.equal(false);
                done();
            }, 10);
        });

        it('simulates close', function (done) {

            var dispatch = function (req, res) {

                var buffer = '';
                req.on('readable', function () {

                    buffer += req.read() || '';
                });

                req.on('error', function (err) {
                });

                req.on('close', function () {

                    res.writeHead(200, { 'Content-Length': 0 });
                    res.end('close');
                });

                req.on('end', function () {
                });
            };

            var body = 'something special just for you';
            Shot.inject(dispatch, { method: 'get', url: '/', payload: body, simulate: { close: true } }, function (res) {

                expect(res.payload).to.equal('close');
                done();
            });
        });
    });
});

<|endoftext|>


<commit_before>let GitHubApi = require("github");
let isFunction = function(fn) {
    return typeof fn === "function";
};

/**
 * Abstract GitHub repository label importer
 */
class LabelSync {
    /**
     * In order to work as an NPM module this class has to be called with specific parameters
     * @param {Object} options - GitHub
     * @param {string} user - GitHub repository user
     * @param {string} repo - GitHub repository name
     * @param {string} token - GitHub personal access token
     */
    constructor(options = {}, user, repo, token) {
        this._githubOptions = options;
        this._githubUser = user;
        this._githubRepo = repo;
        this._githubToken = token;
        this._github = new GitHubApi(this.githubOptions);

        this.authenticate();
    }

    /**
     * Get GitHubApi module
     * @returns {GitHubApi|module.exports}
     */
    get github() {
        return this._github;
    }

    /**
     * Get GitHubApi module connect options
     * @returns {Object}
     */
    get githubOptions() {
        return this._githubOptions;
    }

    /**
     * Get GitHub repository username
     * @returns {string}
     */
    get githubUser() {
        return this._githubUser;
    }

    /**
     * Get GitHub repository name
     * @returns {string}
     */
    get githubRepo() {
        return this._githubRepo;
    }

    /**
     * Get GitHub repository token
     * @returns {string}
     */
    get githubToken() {
        return this._githubToken;
    }

    /**
     * Get GitHub labels for current repository
     * @returns {*}
     */
    get githubLabels() {
        return this._githubLabels;
    }

    /**
     * Remove "meta" key from GitHubApi response
     * @param labels
     */
    set githubLabels(labels) {
        if (labels) {
            delete labels.meta;
        }

        this._githubLabels = labels;
    }

    authenticate() {
        this.github.authenticate({
            type: "oauth",
            token: this.githubToken
        });
    }

    /**
     * Get GitHubApi labels for current repository response and set labels
     * @param {Function} callback
     */
    getRepositoryLabels(callback) {
        this.github.issues.getLabels({
            user: this.githubUser,
            repo: this.githubRepo
        }, (error, resource) => {
            this.githubLabels = resource;

            if (isFunction(callback)) {
                callback(error, this.githubLabels);
            }
        });
    }
}

module.exports = LabelSync;
<commit_msg>feat(github): added purge current labels feature<commit_after>let GitHubApi = require("github");
let isFunction = function(fn) {
    return typeof fn === "function";
};

/**
 * Abstract GitHub repository label importer
 */
class LabelSync {
    /**
     * In order to work as an NPM module this class has to be called with specific parameters
     * @param {Object} options - GitHub
     * @param {string} user - GitHub repository user
     * @param {string} repo - GitHub repository name
     * @param {string} token - GitHub personal access token
     */
    constructor(options = {}, user, repo, token) {
        this._githubDeletedLabels = [];
        this._githubOptions = options;
        this._githubUser = user;
        this._githubRepo = repo;
        this._githubToken = token;
        this._github = new GitHubApi(this.githubOptions);

        this.authenticate();
    }

    /**
     * Get GitHubApi module
     * @returns {GitHubApi|module.exports}
     */
    get github() {
        return this._github;
    }

    /**
     * Get GitHubApi module connect options
     * @returns {Object}
     */
    get githubOptions() {
        return this._githubOptions;
    }

    /**
     * Get GitHub repository username
     * @returns {string}
     */
    get githubUser() {
        return this._githubUser;
    }

    /**
     * Get GitHub repository name
     * @returns {string}
     */
    get githubRepo() {
        return this._githubRepo;
    }

    /**
     * Get GitHub repository token
     * @returns {string}
     */
    get githubToken() {
        return this._githubToken;
    }

    /**
     * Get GitHub labels for current repository
     * @returns {*}
     */
    get githubLabels() {
        return this._githubLabels;
    }

    /**
     * Get GitHub deleted labels for current repository
     * @returns {Array}
     */
    get githubDeletedLabels() {
        return this._githubDeletedLabels;
    }

    /**
     * Remove "meta" key from GitHubApi response
     * @param {Array} labels
     */
    set githubLabels(labels) {
        if (labels) {
            delete labels.meta;
        }

        this._githubLabels = labels;
    }

    /**
     * Send a deleted label to the log
     * @param {Object} label
     */
    set githubDeletedLabel(label) {
        this._githubDeletedLabels.push(label);
    }

    authenticate() {
        this.github.authenticate({
            type: "oauth",
            token: this.githubToken
        });
    }

    /**
     * Get GitHubApi labels for current repository response and set labels
     * @param {Function} callback
     */
    getRepositoryLabels(callback) {
        this.github.issues.getLabels({
            user: this.githubUser,
            repo: this.githubRepo
        }, (error, resource) => {
            this.githubLabels = resource;

            if (isFunction(callback)) {
                callback(error, this.githubLabels);
            }
        });
    }

    /**
     * Delete a GitHubApi label for current repository
     * @param {Object} label
     * @param {Function} callback
     */
    deleteLabel(label, callback) {
        this.github.issues.deleteLabel({
            user: this.githubUser,
            repo: this.githubRepo,
            name: label.name
        }, (error, resource) => {
            if (! error) {
                this.githubDeletedLabel = label;
            }

            if (isFunction(callback)) {
                callback(error, resource);
            }
        });
    }

    /**
     * Delete all GitHubApi labels for current repository
     * @param {Function} callback
     */
    deleteAllLabels(callback) {
        this.getRepositoryLabels((error, labels) => {
            if (labels) {
                labels.map((label) => {
                    this.deleteLabel(label, (error) => {
                        if (isFunction(callback)) {
                            callback(error);
                        }
                    });
                });
            }
        });
    }
}

module.exports = LabelSync;
<|endoftext|>


<commit_before>import BaseComponent from 'base-component'
import classNames from 'classnames'
import defined from '@xen-orchestra/defined'
import Icon from 'icon'
import map from 'lodash/map'
import PropTypes from 'prop-types'
import randomPassword from 'random-password'
import React from 'react'
import round from 'lodash/round'
import SingleLineRow from 'single-line-row'
import { Container, Col } from 'grid'
import { DropdownButton, MenuItem } from 'react-bootstrap-4/lib'

import Button from '../button'
import Component from '../base-component'
import getEventValue from '../get-event-value'
import { formatSizeRaw, parseSize } from '../utils'

export Number from './number'
export Select from './select'

// ===================================================================

export class Password extends Component {
  static propTypes = {
    enableGenerator: PropTypes.bool,
  }

  get value () {
    return this.refs.field.value
  }

  set value (value) {
    this.refs.field.value = value
  }

  _generate = () => {
    const value = randomPassword(8)
    const isControlled = this.props.value !== undefined
    if (isControlled) {
      this.props.onChange(value)
    } else {
      this.refs.field.value = value
    }

    // FIXME: in controlled mode, visibility should only be updated
    // when the value prop is changed according to the emitted value.
    this.setState({
      visible: true,
    })
  }

  _toggleVisibility = () => {
    this.setState({
      visible: !this.state.visible,
    })
  }

  render () {
    const { className, enableGenerator = false, ...props } = this.props
    const { visible } = this.state

    return (

        {enableGenerator && (

            <Button onClick={this._generate}>
              <Icon icon='password' />
            </Button>

        )}
        <input
          {...props}
          className={classNames(className, 'form-control')}
          ref='field'
          type={visible ? 'text' : 'password'}
        />

          <Button onClick={this._toggleVisibility}>
            <Icon icon={visible ? 'shown' : 'hidden'} />
          </Button>

    )
  }
}

// ===================================================================

export class Range extends Component {
  static propTypes = {
    max: PropTypes.number.isRequired,
    min: PropTypes.number.isRequired,
    onChange: PropTypes.func,
    required: PropTypes.boolean,
    step: PropTypes.number,
    value: PropTypes.number,
  }

  componentDidMount () {
    const { min, onChange, required, value } = this.props

    if (value === undefined && required) {
      onChange !== undefined && onChange(min)
    }
  }

  _onChange = value => this.props.onChange(getEventValue(value))

  render () {
    const { max, min, step, value } = this.props

    return (
      <Container>
        <SingleLineRow>
          <Col size={2}>
            {value}
          </Col>
          <Col size={10}>
            <input
              className='form-control'
              max={max}
              min={min}
              onChange={this._onChange}
              step={step}
              type='range'
              value={value}
            />
          </Col>
        </SingleLineRow>
      </Container>
    )
  }
}

export Toggle from './toggle'

const UNITS = ['kiB', 'MiB', 'GiB']
const DEFAULT_UNIT = 'GiB'

export class SizeInput extends BaseComponent {
  static propTypes = {
    autoFocus: PropTypes.bool,
    className: PropTypes.string,
    defaultUnit: PropTypes.oneOf(UNITS),
    defaultValue: PropTypes.number,
    onChange: PropTypes.func,
    placeholder: PropTypes.string,
    readOnly: PropTypes.bool,
    required: PropTypes.bool,
    style: PropTypes.object,
    value: PropTypes.oneOfType([PropTypes.number, PropTypes.oneOf([null])]),
  }

  constructor (props) {
    super(props)

    this.state = this._createStateFromBytes(
      defined(props.value, props.defaultValue, null)
    )
  }

  componentWillReceiveProps (props) {
    const { value } = props
    if (value !== undefined && value !== this.props.value) {
      this.setState(this._createStateFromBytes(value))
    }
  }

  _createStateFromBytes (bytes) {
    if (bytes === this._bytes) {
      return {
        input: this._input,
        unit: this._unit,
      }
    }

    if (bytes === null) {
      return {
        input: '',
        unit: this.props.defaultUnit || DEFAULT_UNIT,
      }
    }

    const { prefix, value } = formatSizeRaw(bytes)
    return {
      input: String(round(value, 2)),
      unit: `${prefix}B`,
    }
  }

  get value () {
    const { input, unit } = this.state

    if (!input) {
      return null
    }

    return parseSize(`${+input} ${unit}`)
  }

  set value (value) {
    if (
      process.env.NODE_ENV !== 'production' &&
      this.props.value !== undefined
    ) {
      throw new Error('cannot set value of controlled SizeInput')
    }
    this.setState(this._createStateFromBytes(value))
  }

  _onChange (input, unit) {
    const { onChange } = this.props

    // Empty input equals null.
    const bytes = input ? parseSize(`${+input} ${unit}`) : null

    const isControlled = this.props.value !== undefined
    if (isControlled) {
      // Store input and unit for this change to update correctly on new
      // props.
      this._bytes = bytes
      this._input = input
      this._unit = unit
    } else {
      this.setState({ input, unit })

      // onChange is optional in uncontrolled mode.
      if (!onChange) {
        return
      }
    }

    onChange(bytes)
  }

  _updateNumber = event => {
    const input = event.target.value

    if (!input) {
      return this._onChange(input, this.state.unit)
    }

    const number = +input

    if (Number.isNaN(number)) {
      return
    }

    // Same numeric value: simply update the input.
    const prevInput = this.state.input
    if (prevInput && +prevInput === number) {
      return this.setState({ input })
    }

    this._onChange(input, this.state.unit)
  }

  _updateUnit = unit => {
    const { input } = this.state

    // 0 is always 0, no matter the unit.
    if (+input) {
      this._onChange(input, unit)
    } else {
      this.setState({ unit })
    }
  }

  render () {
    const {
      autoFocus,
      className,
      readOnly,
      placeholder,
      required,
      style,
    } = this.props

    return (

        <input
          autoFocus={autoFocus}
          className='form-control'
          disabled={readOnly}
          onChange={this._updateNumber}
          placeholder={placeholder}
          required={required}
          type='text'
          value={this.state.input}
        />

          <DropdownButton
            bsStyle='secondary'
            id='size'
            pullRight
            disabled={readOnly}
            title={this.state.unit}
          >
            {map(UNITS, unit => (
              <MenuItem key={unit} onClick={() => this._updateUnit(unit)}>
                {unit}
              </MenuItem>
            ))}
          </DropdownButton>

    )
  }
}
<commit_msg>fix(xo-web/form/range): fix required prop type<commit_after>import BaseComponent from 'base-component'
import classNames from 'classnames'
import defined from '@xen-orchestra/defined'
import Icon from 'icon'
import map from 'lodash/map'
import PropTypes from 'prop-types'
import randomPassword from 'random-password'
import React from 'react'
import round from 'lodash/round'
import SingleLineRow from 'single-line-row'
import { Container, Col } from 'grid'
import { DropdownButton, MenuItem } from 'react-bootstrap-4/lib'

import Button from '../button'
import Component from '../base-component'
import getEventValue from '../get-event-value'
import { formatSizeRaw, parseSize } from '../utils'

export Number from './number'
export Select from './select'

// ===================================================================

export class Password extends Component {
  static propTypes = {
    enableGenerator: PropTypes.bool,
  }

  get value () {
    return this.refs.field.value
  }

  set value (value) {
    this.refs.field.value = value
  }

  _generate = () => {
    const value = randomPassword(8)
    const isControlled = this.props.value !== undefined
    if (isControlled) {
      this.props.onChange(value)
    } else {
      this.refs.field.value = value
    }

    // FIXME: in controlled mode, visibility should only be updated
    // when the value prop is changed according to the emitted value.
    this.setState({
      visible: true,
    })
  }

  _toggleVisibility = () => {
    this.setState({
      visible: !this.state.visible,
    })
  }

  render () {
    const { className, enableGenerator = false, ...props } = this.props
    const { visible } = this.state

    return (

        {enableGenerator && (

            <Button onClick={this._generate}>
              <Icon icon='password' />
            </Button>

        )}
        <input
          {...props}
          className={classNames(className, 'form-control')}
          ref='field'
          type={visible ? 'text' : 'password'}
        />

          <Button onClick={this._toggleVisibility}>
            <Icon icon={visible ? 'shown' : 'hidden'} />
          </Button>

    )
  }
}

// ===================================================================

export class Range extends Component {
  static propTypes = {
    max: PropTypes.number.isRequired,
    min: PropTypes.number.isRequired,
    onChange: PropTypes.func,
    required: PropTypes.bool,
    step: PropTypes.number,
    value: PropTypes.number,
  }

  componentDidMount () {
    const { min, onChange, required, value } = this.props

    if (value === undefined && required) {
      onChange !== undefined && onChange(min)
    }
  }

  _onChange = value => this.props.onChange(getEventValue(value))

  render () {
    const { max, min, step, value } = this.props

    return (
      <Container>
        <SingleLineRow>
          <Col size={2}>
            {value}
          </Col>
          <Col size={10}>
            <input
              className='form-control'
              max={max}
              min={min}
              onChange={this._onChange}
              step={step}
              type='range'
              value={value}
            />
          </Col>
        </SingleLineRow>
      </Container>
    )
  }
}

export Toggle from './toggle'

const UNITS = ['kiB', 'MiB', 'GiB']
const DEFAULT_UNIT = 'GiB'

export class SizeInput extends BaseComponent {
  static propTypes = {
    autoFocus: PropTypes.bool,
    className: PropTypes.string,
    defaultUnit: PropTypes.oneOf(UNITS),
    defaultValue: PropTypes.number,
    onChange: PropTypes.func,
    placeholder: PropTypes.string,
    readOnly: PropTypes.bool,
    required: PropTypes.bool,
    style: PropTypes.object,
    value: PropTypes.oneOfType([PropTypes.number, PropTypes.oneOf([null])]),
  }

  constructor (props) {
    super(props)

    this.state = this._createStateFromBytes(
      defined(props.value, props.defaultValue, null)
    )
  }

  componentWillReceiveProps (props) {
    const { value } = props
    if (value !== undefined && value !== this.props.value) {
      this.setState(this._createStateFromBytes(value))
    }
  }

  _createStateFromBytes (bytes) {
    if (bytes === this._bytes) {
      return {
        input: this._input,
        unit: this._unit,
      }
    }

    if (bytes === null) {
      return {
        input: '',
        unit: this.props.defaultUnit || DEFAULT_UNIT,
      }
    }

    const { prefix, value } = formatSizeRaw(bytes)
    return {
      input: String(round(value, 2)),
      unit: `${prefix}B`,
    }
  }

  get value () {
    const { input, unit } = this.state

    if (!input) {
      return null
    }

    return parseSize(`${+input} ${unit}`)
  }

  set value (value) {
    if (
      process.env.NODE_ENV !== 'production' &&
      this.props.value !== undefined
    ) {
      throw new Error('cannot set value of controlled SizeInput')
    }
    this.setState(this._createStateFromBytes(value))
  }

  _onChange (input, unit) {
    const { onChange } = this.props

    // Empty input equals null.
    const bytes = input ? parseSize(`${+input} ${unit}`) : null

    const isControlled = this.props.value !== undefined
    if (isControlled) {
      // Store input and unit for this change to update correctly on new
      // props.
      this._bytes = bytes
      this._input = input
      this._unit = unit
    } else {
      this.setState({ input, unit })

      // onChange is optional in uncontrolled mode.
      if (!onChange) {
        return
      }
    }

    onChange(bytes)
  }

  _updateNumber = event => {
    const input = event.target.value

    if (!input) {
      return this._onChange(input, this.state.unit)
    }

    const number = +input

    if (Number.isNaN(number)) {
      return
    }

    // Same numeric value: simply update the input.
    const prevInput = this.state.input
    if (prevInput && +prevInput === number) {
      return this.setState({ input })
    }

    this._onChange(input, this.state.unit)
  }

  _updateUnit = unit => {
    const { input } = this.state

    // 0 is always 0, no matter the unit.
    if (+input) {
      this._onChange(input, unit)
    } else {
      this.setState({ unit })
    }
  }

  render () {
    const {
      autoFocus,
      className,
      readOnly,
      placeholder,
      required,
      style,
    } = this.props

    return (

        <input
          autoFocus={autoFocus}
          className='form-control'
          disabled={readOnly}
          onChange={this._updateNumber}
          placeholder={placeholder}
          required={required}
          type='text'
          value={this.state.input}
        />

          <DropdownButton
            bsStyle='secondary'
            id='size'
            pullRight
            disabled={readOnly}
            title={this.state.unit}
          >
            {map(UNITS, unit => (
              <MenuItem key={unit} onClick={() => this._updateUnit(unit)}>
                {unit}
              </MenuItem>
            ))}
          </DropdownButton>

    )
  }
}
<|endoftext|>


<commit_before>import * as builtInComponents from './runtime/components'

const elementMap = new Map()

const defaultViewMeta = {
  skipAddToDom: false,
  isUnaryTag: false,
  tagNamespace: '',
  canBeLeftOpen: false,
  model: null,
  component: null
}

export function normalizeElementName(elementName) {
  return `native${elementName
    .replace(/Native/gi, '')
    .replace(/-/g, '')
    .toLowerCase()}`
}

export function registerElement(elementName, resolver, meta) {
  const normalizedName = normalizeElementName(elementName)

  meta = Object.assign({}, defaultViewMeta, meta)

  if (elementMap.has(normalizedName)) {
    throw new Error(`Element for ${elementName} already registered.`)
  }

  if (!meta.component) {
    // if no Vue component is passed, wrap the simpler vue component
    // which bind the events and attributes to the NS one
    meta.component = {
      functional: true,
      model: meta.model,
      render: (h, { data, children }) => {
        return h(normalizedName, data, children)
      }
    }
  }
  meta.component.name = elementName

  const entry = {
    resolver: resolver,
    meta: meta
  }
  elementMap.set(normalizedName, entry)
}

export function getElementMap() {
  return elementMap
}

export function getViewClass(elementName) {
  const normalizedName = normalizeElementName(elementName)
  const entry = elementMap.get(normalizedName)

  if (!entry) {
    throw new TypeError(`No known component for element ${elementName}.`)
  }

  try {
    return entry.resolver()
  } catch (e) {
    throw new TypeError(`Could not load view for: ${elementName}. ${e}`)
  }
}

export function getViewMeta(elementName) {
  const normalizedName = normalizeElementName(elementName)

  let meta = defaultViewMeta
  const entry = elementMap.get(normalizedName)

  if (entry && entry.meta) {
    meta = entry.meta
  }

  return meta
}

export function isKnownView(elementName) {
  return elementMap.has(normalizeElementName(elementName))
}

registerElement(
  'ActionBar',
  () => require('tns-core-modules/ui/action-bar').ActionBar,
  {
    removeChild(parent, child) {
      try {
        parent.nativeView._removeView(child.nativeView)
      } catch (e) {
        // ignore exception - child is likely already removed/replaced
        // fixes #76
      }
    },
    component: builtInComponents.ActionBar
  }
)

registerElement(
  'ActionItem',
  () => require('tns-core-modules/ui/action-bar').ActionItem
)

registerElement('android', null, {
  component: builtInComponents.android
})

registerElement('ios', null, {
  component: builtInComponents.ios
})

registerElement(
  'ListView',
  () => require('tns-core-modules/ui/list-view').ListView,
  {
    component: builtInComponents.ListView
  }
)

registerElement(
  'NavigationButton',
  () => require('tns-core-modules/ui/action-bar').NavigationButton
)

registerElement(
  'TabView',
  () => require('tns-core-modules/ui/tab-view').TabView,
  {
    model: {
      prop: 'selectedIndex',
      event: 'selectedIndexChange'
    },
    component: builtInComponents.TabView
  }
)

registerElement(
  'TabViewItem',
  () => require('tns-core-modules/ui/tab-view').TabViewItem,
  {
    skipAddToDom: true,
    component: builtInComponents.TabViewItem
  }
)

registerElement('transition', null, {
  component: builtInComponents.transition
})

registerElement('v-template', null, {
  component: builtInComponents.VTemplate
})

// NS components which uses the automatic registerElement Vue wrapper
// as they do not need any special logic

registerElement('Label', () => require('tns-core-modules/ui/label').Label, {
  model: {
    prop: 'text',
    event: 'textChange'
  }
})

registerElement(
  'DatePicker',
  () => require('tns-core-modules/ui/date-picker').DatePicker,
  {
    model: {
      prop: 'date',
      event: 'dateChange'
    }
  }
)

registerElement(
  'AbsoluteLayout',
  () => require('tns-core-modules/ui/layouts/absolute-layout').AbsoluteLayout
)
registerElement(
  'ActivityIndicator',
  () => require('tns-core-modules/ui/activity-indicator').ActivityIndicator
)
registerElement('Border', () => require('tns-core-modules/ui/border').Border)
registerElement('Button', () => require('tns-core-modules/ui/button').Button)
registerElement(
  'ContentView',
  () => require('tns-core-modules/ui/content-view').ContentView
)
registerElement(
  'DockLayout',
  () => require('tns-core-modules/ui/layouts/dock-layout').DockLayout
)
registerElement(
  'GridLayout',
  () => require('tns-core-modules/ui/layouts/grid-layout').GridLayout
)
registerElement(
  'HtmlView',
  () => require('tns-core-modules/ui/html-view').HtmlView
)
registerElement('Image', () => require('tns-core-modules/ui/image').Image)
registerElement('img', () => require('tns-core-modules/ui/image').Image)
registerElement(
  'ListPicker',
  () => require('tns-core-modules/ui/list-picker').ListPicker,
  {
    model: {
      prop: 'selectedIndex',
      event: 'selectedIndexChange'
    }
  }
)
registerElement('Page', () => require('tns-core-modules/ui/page').Page, {
  skipAddToDom: true
})
registerElement(
  'Placeholder',
  () => require('tns-core-modules/ui/placeholder').Placeholder
)
registerElement(
  'Progress',
  () => require('tns-core-modules/ui/progress').Progress,
  {
    model: {
      prop: 'value',
      event: 'valueChange'
    }
  }
)
registerElement(
  'ProxyViewContainer',
  () => require('tns-core-modules/ui/proxy-view-container').ProxyViewContainer
)
// registerElement(
//   'Repeater',
//   () => require('tns-core-modules/ui/repeater').Repeater
// )
registerElement(
  'ScrollView',
  () => require('tns-core-modules/ui/scroll-view').ScrollView
)
registerElement(
  'SearchBar',
  () => require('tns-core-modules/ui/search-bar').SearchBar,
  {
    model: {
      prop: 'text',
      event: 'textChange'
    }
  }
)
registerElement(
  'SegmentedBar',
  () => require('tns-core-modules/ui/segmented-bar').SegmentedBar,
  {
    model: {
      prop: 'selectedIndex',
      event: 'selectedIndexChange'
    }
  }
)
registerElement(
  'SegmentedBarItem',
  () => require('tns-core-modules/ui/segmented-bar').SegmentedBarItem
)
registerElement('Slider', () => require('tns-core-modules/ui/slider').Slider, {
  model: {
    prop: 'value',
    event: 'valueChange'
  }
})
registerElement(
  'StackLayout',
  () => require('tns-core-modules/ui/layouts/stack-layout').StackLayout
)
registerElement(
  'FlexboxLayout',
  () => require('tns-core-modules/ui/layouts/flexbox-layout').FlexboxLayout
)
registerElement('Switch', () => require('tns-core-modules/ui/switch').Switch, {
  model: {
    prop: 'checked',
    event: 'checkedChange'
  }
})

registerElement(
  'TextField',
  () => require('tns-core-modules/ui/text-field').TextField,
  {
    model: {
      prop: 'text',
      event: 'textChange'
    }
  }
)
registerElement(
  'TextView',
  () => require('tns-core-modules/ui/text-view').TextView,
  {
    model: {
      prop: 'text',
      event: 'textChange'
    }
  }
)
registerElement(
  'TimePicker',
  () => require('tns-core-modules/ui/time-picker').TimePicker,
  {
    model: {
      prop: 'time',
      event: 'timeChange'
    }
  }
)
registerElement(
  'WebView',
  () => require('tns-core-modules/ui/web-view').WebView
)
registerElement(
  'WrapLayout',
  () => require('tns-core-modules/ui/layouts/wrap-layout').WrapLayout
)
registerElement(
  'FormattedString',
  () => require('tns-core-modules/text/formatted-string').FormattedString
)
registerElement('Span', () => require('tns-core-modules/text/span').Span)

registerElement(
  'DetachedContainer',
  () => require('tns-core-modules/ui/proxy-view-container').ProxyViewContainer,
  {
    skipAddToDom: true
  }
)
registerElement(
  'DetachedText',
  () => require('tns-core-modules/ui/placeholder').Placeholder,
  {
    skipAddToDom: true
  }
)
registerElement(
  'Comment',
  () => require('tns-core-modules/ui/placeholder').Placeholder
)

registerElement(
  'Document',
  () => require('tns-core-modules/ui/proxy-view-container').ProxyViewContainer,
  {
    skipAddToDom: true
  }
)

registerElement('Frame', () => require('tns-core-modules/ui/frame').Frame, {
  insertChild(parentNode, childNode, atIndex) {
    if (normalizeElementName(childNode.tagName) === 'nativepage') {
      parentNode.nativeView.navigate({ create: () => childNode.nativeView })
    }
  }
})
<commit_msg>fix: canBeLeftOpenTag in defaultViewMeta instead of canBeLeftOpen (#308)<commit_after>import * as builtInComponents from './runtime/components'

const elementMap = new Map()

const defaultViewMeta = {
  skipAddToDom: false,
  isUnaryTag: false,
  tagNamespace: '',
  canBeLeftOpenTag: false,
  model: null,
  component: null
}

export function normalizeElementName(elementName) {
  return `native${elementName
    .replace(/Native/gi, '')
    .replace(/-/g, '')
    .toLowerCase()}`
}

export function registerElement(elementName, resolver, meta) {
  const normalizedName = normalizeElementName(elementName)

  meta = Object.assign({}, defaultViewMeta, meta)

  if (elementMap.has(normalizedName)) {
    throw new Error(`Element for ${elementName} already registered.`)
  }

  if (!meta.component) {
    // if no Vue component is passed, wrap the simpler vue component
    // which bind the events and attributes to the NS one
    meta.component = {
      functional: true,
      model: meta.model,
      render: (h, { data, children }) => {
        return h(normalizedName, data, children)
      }
    }
  }
  meta.component.name = elementName

  const entry = {
    resolver: resolver,
    meta: meta
  }
  elementMap.set(normalizedName, entry)
}

export function getElementMap() {
  return elementMap
}

export function getViewClass(elementName) {
  const normalizedName = normalizeElementName(elementName)
  const entry = elementMap.get(normalizedName)

  if (!entry) {
    throw new TypeError(`No known component for element ${elementName}.`)
  }

  try {
    return entry.resolver()
  } catch (e) {
    throw new TypeError(`Could not load view for: ${elementName}. ${e}`)
  }
}

export function getViewMeta(elementName) {
  const normalizedName = normalizeElementName(elementName)

  let meta = defaultViewMeta
  const entry = elementMap.get(normalizedName)

  if (entry && entry.meta) {
    meta = entry.meta
  }

  return meta
}

export function isKnownView(elementName) {
  return elementMap.has(normalizeElementName(elementName))
}

registerElement(
  'ActionBar',
  () => require('tns-core-modules/ui/action-bar').ActionBar,
  {
    removeChild(parent, child) {
      try {
        parent.nativeView._removeView(child.nativeView)
      } catch (e) {
        // ignore exception - child is likely already removed/replaced
        // fixes #76
      }
    },
    component: builtInComponents.ActionBar
  }
)

registerElement(
  'ActionItem',
  () => require('tns-core-modules/ui/action-bar').ActionItem
)

registerElement('android', null, {
  component: builtInComponents.android
})

registerElement('ios', null, {
  component: builtInComponents.ios
})

registerElement(
  'ListView',
  () => require('tns-core-modules/ui/list-view').ListView,
  {
    component: builtInComponents.ListView
  }
)

registerElement(
  'NavigationButton',
  () => require('tns-core-modules/ui/action-bar').NavigationButton
)

registerElement(
  'TabView',
  () => require('tns-core-modules/ui/tab-view').TabView,
  {
    model: {
      prop: 'selectedIndex',
      event: 'selectedIndexChange'
    },
    component: builtInComponents.TabView
  }
)

registerElement(
  'TabViewItem',
  () => require('tns-core-modules/ui/tab-view').TabViewItem,
  {
    skipAddToDom: true,
    component: builtInComponents.TabViewItem
  }
)

registerElement('transition', null, {
  component: builtInComponents.transition
})

registerElement('v-template', null, {
  component: builtInComponents.VTemplate
})

// NS components which uses the automatic registerElement Vue wrapper
// as they do not need any special logic

registerElement('Label', () => require('tns-core-modules/ui/label').Label, {
  model: {
    prop: 'text',
    event: 'textChange'
  }
})

registerElement(
  'DatePicker',
  () => require('tns-core-modules/ui/date-picker').DatePicker,
  {
    model: {
      prop: 'date',
      event: 'dateChange'
    }
  }
)

registerElement(
  'AbsoluteLayout',
  () => require('tns-core-modules/ui/layouts/absolute-layout').AbsoluteLayout
)
registerElement(
  'ActivityIndicator',
  () => require('tns-core-modules/ui/activity-indicator').ActivityIndicator
)
registerElement('Border', () => require('tns-core-modules/ui/border').Border)
registerElement('Button', () => require('tns-core-modules/ui/button').Button)
registerElement(
  'ContentView',
  () => require('tns-core-modules/ui/content-view').ContentView
)
registerElement(
  'DockLayout',
  () => require('tns-core-modules/ui/layouts/dock-layout').DockLayout
)
registerElement(
  'GridLayout',
  () => require('tns-core-modules/ui/layouts/grid-layout').GridLayout
)
registerElement(
  'HtmlView',
  () => require('tns-core-modules/ui/html-view').HtmlView
)
registerElement('Image', () => require('tns-core-modules/ui/image').Image)
registerElement('img', () => require('tns-core-modules/ui/image').Image)
registerElement(
  'ListPicker',
  () => require('tns-core-modules/ui/list-picker').ListPicker,
  {
    model: {
      prop: 'selectedIndex',
      event: 'selectedIndexChange'
    }
  }
)
registerElement('Page', () => require('tns-core-modules/ui/page').Page, {
  skipAddToDom: true
})
registerElement(
  'Placeholder',
  () => require('tns-core-modules/ui/placeholder').Placeholder
)
registerElement(
  'Progress',
  () => require('tns-core-modules/ui/progress').Progress,
  {
    model: {
      prop: 'value',
      event: 'valueChange'
    }
  }
)
registerElement(
  'ProxyViewContainer',
  () => require('tns-core-modules/ui/proxy-view-container').ProxyViewContainer
)
// registerElement(
//   'Repeater',
//   () => require('tns-core-modules/ui/repeater').Repeater
// )
registerElement(
  'ScrollView',
  () => require('tns-core-modules/ui/scroll-view').ScrollView
)
registerElement(
  'SearchBar',
  () => require('tns-core-modules/ui/search-bar').SearchBar,
  {
    model: {
      prop: 'text',
      event: 'textChange'
    }
  }
)
registerElement(
  'SegmentedBar',
  () => require('tns-core-modules/ui/segmented-bar').SegmentedBar,
  {
    model: {
      prop: 'selectedIndex',
      event: 'selectedIndexChange'
    }
  }
)
registerElement(
  'SegmentedBarItem',
  () => require('tns-core-modules/ui/segmented-bar').SegmentedBarItem
)
registerElement('Slider', () => require('tns-core-modules/ui/slider').Slider, {
  model: {
    prop: 'value',
    event: 'valueChange'
  }
})
registerElement(
  'StackLayout',
  () => require('tns-core-modules/ui/layouts/stack-layout').StackLayout
)
registerElement(
  'FlexboxLayout',
  () => require('tns-core-modules/ui/layouts/flexbox-layout').FlexboxLayout
)
registerElement('Switch', () => require('tns-core-modules/ui/switch').Switch, {
  model: {
    prop: 'checked',
    event: 'checkedChange'
  }
})

registerElement(
  'TextField',
  () => require('tns-core-modules/ui/text-field').TextField,
  {
    model: {
      prop: 'text',
      event: 'textChange'
    }
  }
)
registerElement(
  'TextView',
  () => require('tns-core-modules/ui/text-view').TextView,
  {
    model: {
      prop: 'text',
      event: 'textChange'
    }
  }
)
registerElement(
  'TimePicker',
  () => require('tns-core-modules/ui/time-picker').TimePicker,
  {
    model: {
      prop: 'time',
      event: 'timeChange'
    }
  }
)
registerElement(
  'WebView',
  () => require('tns-core-modules/ui/web-view').WebView
)
registerElement(
  'WrapLayout',
  () => require('tns-core-modules/ui/layouts/wrap-layout').WrapLayout
)
registerElement(
  'FormattedString',
  () => require('tns-core-modules/text/formatted-string').FormattedString
)
registerElement('Span', () => require('tns-core-modules/text/span').Span)

registerElement(
  'DetachedContainer',
  () => require('tns-core-modules/ui/proxy-view-container').ProxyViewContainer,
  {
    skipAddToDom: true
  }
)
registerElement(
  'DetachedText',
  () => require('tns-core-modules/ui/placeholder').Placeholder,
  {
    skipAddToDom: true
  }
)
registerElement(
  'Comment',
  () => require('tns-core-modules/ui/placeholder').Placeholder
)

registerElement(
  'Document',
  () => require('tns-core-modules/ui/proxy-view-container').ProxyViewContainer,
  {
    skipAddToDom: true
  }
)

registerElement('Frame', () => require('tns-core-modules/ui/frame').Frame, {
  insertChild(parentNode, childNode, atIndex) {
    if (normalizeElementName(childNode.tagName) === 'nativepage') {
      parentNode.nativeView.navigate({ create: () => childNode.nativeView })
    }
  }
})
<|endoftext|>


<commit_before>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

"use strict";

const { RawSource } = require("webpack-sources");
const AsyncDependenciesBlock = require("../AsyncDependenciesBlock");
const Dependency = require("../Dependency");
const Module = require("../Module");
const ModuleFactory = require("../ModuleFactory");
const RuntimeGlobals = require("../RuntimeGlobals");
const Template = require("../Template");
const CommonJsRequireDependency = require("../dependencies/CommonJsRequireDependency");
const { registerNotSerializable } = require("../util/serialization");

/** @typedef {import("../../declarations/WebpackOptions")} WebpackOptions */
/** @typedef {import("../Compilation")} Compilation */
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */
/** @typedef {import("../Module").BuildMeta} BuildMeta */
/** @typedef {import("../Module").CodeGenerationContext} CodeGenerationContext */
/** @typedef {import("../Module").CodeGenerationResult} CodeGenerationResult */
/** @typedef {import("../Module").LibIdentOptions} LibIdentOptions */
/** @typedef {import("../Module").NeedBuildContext} NeedBuildContext */
/** @typedef {import("../ModuleFactory").ModuleFactoryCreateData} ModuleFactoryCreateData */
/** @typedef {import("../ModuleFactory").ModuleFactoryResult} ModuleFactoryResult */
/** @typedef {import("../RequestShortener")} RequestShortener */
/** @typedef {import("../ResolverFactory").ResolverWithOptions} ResolverWithOptions */
/** @typedef {import("../WebpackError")} WebpackError */
/** @typedef {import("../util/Hash")} Hash */
/** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */

/**
 * @typedef {Object} BackendApi
 * @property {function(Error=): void} dispose
 * @property {function(Module): { client: string, data: string, active: boolean }} module
 */

const IGNORED_DEPENDENCY_TYPES = new Set([
	"import.meta.webpackHot.accept",
	"import.meta.webpackHot.decline",
	"module.hot.accept",
	"module.hot.decline"
]);

/**
 * @param {undefined|string|RegExp|Function} test test option
 * @param {Module} module the module
 * @returns {boolean} true, if the module should be selected
 */
const checkTest = (test, module) => {
	if (test === undefined) return true;
	if (typeof test === "function") {
		return test(module);
	}
	if (typeof test === "string") {
		const name = module.nameForCondition();
		return name && name.startsWith(test);
	}
	if (test instanceof RegExp) {
		const name = module.nameForCondition();
		return name && test.test(name);
	}
	return false;
};

const TYPES = new Set(["javascript"]);

class LazyCompilationDependency extends Dependency {
	constructor(proxyModule) {
		super();
		this.proxyModule = proxyModule;
	}

	get category() {
		return "esm";
	}

	get type() {
		return "lazy import()";
	}

	/**
	 * @returns {string | null} an identifier to merge equal requests
	 */
	getResourceIdentifier() {
		return this.proxyModule.originalModule.identifier();
	}
}

registerNotSerializable(LazyCompilationDependency);

class LazyCompilationProxyModule extends Module {
	constructor(context, originalModule, request, client, data, active) {
		super("lazy-compilation-proxy", context, originalModule.layer);
		this.originalModule = originalModule;
		this.request = request;
		this.client = client;
		this.data = data;
		this.active = active;
	}

	/**
	 * @returns {string} a unique identifier of the module
	 */
	identifier() {
		return `lazy-compilation-proxy|${this.originalModule.identifier()}`;
	}

	/**
	 * @param {RequestShortener} requestShortener the request shortener
	 * @returns {string} a user readable identifier of the module
	 */
	readableIdentifier(requestShortener) {
		return `lazy-compilation-proxy ${this.originalModule.readableIdentifier(
			requestShortener
		)}`;
	}

	/**
	 * Assuming this module is in the cache. Update the (cached) module with
	 * the fresh module from the factory. Usually updates internal references
	 * and properties.
	 * @param {Module} module fresh module
	 * @returns {void}
	 */
	updateCacheModule(module) {
		super.updateCacheModule(module);
		const m = /** @type {LazyCompilationProxyModule} */ (module);
		this.originalModule = m.originalModule;
		this.request = m.request;
		this.client = m.client;
		this.data = m.data;
		this.active = m.active;
	}

	/**
	 * @param {LibIdentOptions} options options
	 * @returns {string | null} an identifier for library inclusion
	 */
	libIdent(options) {
		return `${this.originalModule.libIdent(options)}!lazy-compilation-proxy`;
	}

	/**
	 * @param {NeedBuildContext} context context info
	 * @param {function(WebpackError=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
	 * @returns {void}
	 */
	needBuild(context, callback) {
		callback(null, !this.buildInfo || this.buildInfo.active !== this.active);
	}

	/**
	 * @param {WebpackOptions} options webpack options
	 * @param {Compilation} compilation the compilation
	 * @param {ResolverWithOptions} resolver the resolver
	 * @param {InputFileSystem} fs the file system
	 * @param {function(WebpackError=): void} callback callback function
	 * @returns {void}
	 */
	build(options, compilation, resolver, fs, callback) {
		this.buildInfo = {
			active: this.active
		};
		/** @type {BuildMeta} */
		this.buildMeta = {};
		this.clearDependenciesAndBlocks();
		const dep = new CommonJsRequireDependency(this.client);
		this.addDependency(dep);
		if (this.active) {
			const dep = new LazyCompilationDependency(this);
			const block = new AsyncDependenciesBlock({});
			block.addDependency(dep);
			this.addBlock(block);
		}
		callback();
	}

	/**
	 * @returns {Set<string>} types available (do not mutate)
	 */
	getSourceTypes() {
		return TYPES;
	}

	/**
	 * @param {string=} type the source type for which the size should be estimated
	 * @returns {number} the estimated size of the module (must be non-zero)
	 */
	size(type) {
		return 200;
	}

	/**
	 * @param {CodeGenerationContext} context context for code generation
	 * @returns {CodeGenerationResult} result
	 */
	codeGeneration({ runtimeTemplate, chunkGraph, moduleGraph }) {
		const sources = new Map();
		const runtimeRequirements = new Set();
		runtimeRequirements.add(RuntimeGlobals.module);
		const clientDep = /** @type {CommonJsRequireDependency} */ (
			this.dependencies[0]
		);
		const clientModule = moduleGraph.getModule(clientDep);
		const block = this.blocks[0];
		const client = Template.asString([
			`var client = ${runtimeTemplate.moduleExports({
				module: clientModule,
				chunkGraph,
				request: clientDep.userRequest,
				runtimeRequirements
			})}`,
			`var data = ${JSON.stringify(this.data)};`
		]);
		const keepActive = Template.asString([
			`var dispose = client.keepAlive({ data: data, active: ${JSON.stringify(
				!!block
			)}, module: module, onError: onError });`
		]);
		let source;
		if (block) {
			const dep = block.dependencies[0];
			const module = moduleGraph.getModule(dep);
			source = Template.asString([
				client,
				`module.exports = ${runtimeTemplate.moduleNamespacePromise({
					chunkGraph,
					block,
					module,
					request: this.request,
					strict: false, // TODO this should be inherited from the original module
					message: "import()",
					runtimeRequirements
				})};`,
				"if (module.hot) {",
				Template.indent([
					"module.hot.accept();",
					`module.hot.accept(${JSON.stringify(
						chunkGraph.getModuleId(module)
					)}, function() { module.hot.invalidate(); });`,
					"module.hot.dispose(function(data) { delete data.resolveSelf; dispose(data); });",
					"if (module.hot.data && module.hot.data.resolveSelf) module.hot.data.resolveSelf(module.exports);"
				]),
				"}",
				"function onError() { /* ignore */ }",
				keepActive
			]);
		} else {
			source = Template.asString([
				client,
				"var resolveSelf, onError;",
				`module.exports = new Promise(function(resolve, reject) { resolveSelf = resolve; onError = reject; });`,
				"if (module.hot) {",
				Template.indent([
					"module.hot.accept();",
					"if (module.hot.data && module.hot.data.resolveSelf) module.hot.data.resolveSelf(module.exports);",
					"module.hot.dispose(function(data) { data.resolveSelf = resolveSelf; dispose(data); });"
				]),
				"}",
				keepActive
			]);
		}
		sources.set("javascript", new RawSource(source));
		return {
			sources,
			runtimeRequirements
		};
	}

	/**
	 * @param {Hash} hash the hash used to track dependencies
	 * @param {UpdateHashContext} context context
	 * @returns {void}
	 */
	updateHash(hash, context) {
		super.updateHash(hash, context);
		hash.update(this.active ? "active" : "");
		hash.update(JSON.stringify(this.data));
	}
}

registerNotSerializable(LazyCompilationProxyModule);

class LazyCompilationDependencyFactory extends ModuleFactory {
	constructor(factory) {
		super();
		this._factory = factory;
	}

	/**
	 * @param {ModuleFactoryCreateData} data data object
	 * @param {function(Error=, ModuleFactoryResult=): void} callback callback
	 * @returns {void}
	 */
	create(data, callback) {
		const dependency = /** @type {LazyCompilationDependency} */ (
			data.dependencies[0]
		);
		callback(null, {
			module: dependency.proxyModule.originalModule
		});
	}
}

class LazyCompilationPlugin {
	/**
	 * @param {Object} options options
	 * @param {(function(Compiler, function(Error?, BackendApi?): void): void) | function(Compiler): Promise<BackendApi>} options.backend the backend
	 * @param {boolean} options.entries true, when entries are lazy compiled
	 * @param {boolean} options.imports true, when import() modules are lazy compiled
	 * @param {RegExp | string | (function(Module): boolean)} options.test additional filter for lazy compiled entrypoint modules
	 */
	constructor({ backend, entries, imports, test }) {
		this.backend = backend;
		this.entries = entries;
		this.imports = imports;
		this.test = test;
	}
	/**
	 * Apply the plugin
	 * @param {Compiler} compiler the compiler instance
	 * @returns {void}
	 */
	apply(compiler) {
		let backend;
		compiler.hooks.beforeCompile.tapAsync(
			"LazyCompilationPlugin",
			(params, callback) => {
				if (backend !== undefined) return callback();
				const promise = this.backend(compiler, (err, result) => {
					if (err) return callback(err);
					backend = result;
					callback();
				});
				if (promise && promise.then) {
					promise.then(b => {
						backend = b;
						callback();
					}, callback);
				}
			}
		);
		compiler.hooks.thisCompilation.tap(
			"LazyCompilationPlugin",
			(compilation, { normalModuleFactory }) => {
				normalModuleFactory.hooks.module.tap(
					"LazyCompilationPlugin",
					(originalModule, createData, resolveData) => {
						if (
							resolveData.dependencies.every(
								dep =>
									IGNORED_DEPENDENCY_TYPES.has(dep.type) ||
									(this.imports &&
										(dep.type === "import()" ||
											dep.type === "import() context element")) ||
									(this.entries && dep.type === "entry")
							) &&
							!/webpack[/\\]hot[/\\]|webpack-dev-server[/\\]client/.test(
								resolveData.request
							) &&
							checkTest(this.test, originalModule)
						) {
							const moduleInfo = backend.module(originalModule);
							if (!moduleInfo) return;
							const { client, data, active } = moduleInfo;

							return new LazyCompilationProxyModule(
								compiler.context,
								originalModule,
								resolveData.request,
								client,
								data,
								active
							);
						}
					}
				);
				compilation.dependencyFactories.set(
					LazyCompilationDependency,
					new LazyCompilationDependencyFactory()
				);
			}
		);
		compiler.hooks.shutdown.tapAsync("LazyCompilationPlugin", callback => {
			backend.dispose(callback);
		});
	}
}

module.exports = LazyCompilationPlugin;
<commit_msg>Exclude `webpack-hot-middleware/client` from lazy compilation<commit_after>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

"use strict";

const { RawSource } = require("webpack-sources");
const AsyncDependenciesBlock = require("../AsyncDependenciesBlock");
const Dependency = require("../Dependency");
const Module = require("../Module");
const ModuleFactory = require("../ModuleFactory");
const RuntimeGlobals = require("../RuntimeGlobals");
const Template = require("../Template");
const CommonJsRequireDependency = require("../dependencies/CommonJsRequireDependency");
const { registerNotSerializable } = require("../util/serialization");

/** @typedef {import("../../declarations/WebpackOptions")} WebpackOptions */
/** @typedef {import("../Compilation")} Compilation */
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */
/** @typedef {import("../Module").BuildMeta} BuildMeta */
/** @typedef {import("../Module").CodeGenerationContext} CodeGenerationContext */
/** @typedef {import("../Module").CodeGenerationResult} CodeGenerationResult */
/** @typedef {import("../Module").LibIdentOptions} LibIdentOptions */
/** @typedef {import("../Module").NeedBuildContext} NeedBuildContext */
/** @typedef {import("../ModuleFactory").ModuleFactoryCreateData} ModuleFactoryCreateData */
/** @typedef {import("../ModuleFactory").ModuleFactoryResult} ModuleFactoryResult */
/** @typedef {import("../RequestShortener")} RequestShortener */
/** @typedef {import("../ResolverFactory").ResolverWithOptions} ResolverWithOptions */
/** @typedef {import("../WebpackError")} WebpackError */
/** @typedef {import("../util/Hash")} Hash */
/** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */

/**
 * @typedef {Object} BackendApi
 * @property {function(Error=): void} dispose
 * @property {function(Module): { client: string, data: string, active: boolean }} module
 */

const IGNORED_DEPENDENCY_TYPES = new Set([
	"import.meta.webpackHot.accept",
	"import.meta.webpackHot.decline",
	"module.hot.accept",
	"module.hot.decline"
]);

/**
 * @param {undefined|string|RegExp|Function} test test option
 * @param {Module} module the module
 * @returns {boolean} true, if the module should be selected
 */
const checkTest = (test, module) => {
	if (test === undefined) return true;
	if (typeof test === "function") {
		return test(module);
	}
	if (typeof test === "string") {
		const name = module.nameForCondition();
		return name && name.startsWith(test);
	}
	if (test instanceof RegExp) {
		const name = module.nameForCondition();
		return name && test.test(name);
	}
	return false;
};

const TYPES = new Set(["javascript"]);

class LazyCompilationDependency extends Dependency {
	constructor(proxyModule) {
		super();
		this.proxyModule = proxyModule;
	}

	get category() {
		return "esm";
	}

	get type() {
		return "lazy import()";
	}

	/**
	 * @returns {string | null} an identifier to merge equal requests
	 */
	getResourceIdentifier() {
		return this.proxyModule.originalModule.identifier();
	}
}

registerNotSerializable(LazyCompilationDependency);

class LazyCompilationProxyModule extends Module {
	constructor(context, originalModule, request, client, data, active) {
		super("lazy-compilation-proxy", context, originalModule.layer);
		this.originalModule = originalModule;
		this.request = request;
		this.client = client;
		this.data = data;
		this.active = active;
	}

	/**
	 * @returns {string} a unique identifier of the module
	 */
	identifier() {
		return `lazy-compilation-proxy|${this.originalModule.identifier()}`;
	}

	/**
	 * @param {RequestShortener} requestShortener the request shortener
	 * @returns {string} a user readable identifier of the module
	 */
	readableIdentifier(requestShortener) {
		return `lazy-compilation-proxy ${this.originalModule.readableIdentifier(
			requestShortener
		)}`;
	}

	/**
	 * Assuming this module is in the cache. Update the (cached) module with
	 * the fresh module from the factory. Usually updates internal references
	 * and properties.
	 * @param {Module} module fresh module
	 * @returns {void}
	 */
	updateCacheModule(module) {
		super.updateCacheModule(module);
		const m = /** @type {LazyCompilationProxyModule} */ (module);
		this.originalModule = m.originalModule;
		this.request = m.request;
		this.client = m.client;
		this.data = m.data;
		this.active = m.active;
	}

	/**
	 * @param {LibIdentOptions} options options
	 * @returns {string | null} an identifier for library inclusion
	 */
	libIdent(options) {
		return `${this.originalModule.libIdent(options)}!lazy-compilation-proxy`;
	}

	/**
	 * @param {NeedBuildContext} context context info
	 * @param {function(WebpackError=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
	 * @returns {void}
	 */
	needBuild(context, callback) {
		callback(null, !this.buildInfo || this.buildInfo.active !== this.active);
	}

	/**
	 * @param {WebpackOptions} options webpack options
	 * @param {Compilation} compilation the compilation
	 * @param {ResolverWithOptions} resolver the resolver
	 * @param {InputFileSystem} fs the file system
	 * @param {function(WebpackError=): void} callback callback function
	 * @returns {void}
	 */
	build(options, compilation, resolver, fs, callback) {
		this.buildInfo = {
			active: this.active
		};
		/** @type {BuildMeta} */
		this.buildMeta = {};
		this.clearDependenciesAndBlocks();
		const dep = new CommonJsRequireDependency(this.client);
		this.addDependency(dep);
		if (this.active) {
			const dep = new LazyCompilationDependency(this);
			const block = new AsyncDependenciesBlock({});
			block.addDependency(dep);
			this.addBlock(block);
		}
		callback();
	}

	/**
	 * @returns {Set<string>} types available (do not mutate)
	 */
	getSourceTypes() {
		return TYPES;
	}

	/**
	 * @param {string=} type the source type for which the size should be estimated
	 * @returns {number} the estimated size of the module (must be non-zero)
	 */
	size(type) {
		return 200;
	}

	/**
	 * @param {CodeGenerationContext} context context for code generation
	 * @returns {CodeGenerationResult} result
	 */
	codeGeneration({ runtimeTemplate, chunkGraph, moduleGraph }) {
		const sources = new Map();
		const runtimeRequirements = new Set();
		runtimeRequirements.add(RuntimeGlobals.module);
		const clientDep = /** @type {CommonJsRequireDependency} */ (
			this.dependencies[0]
		);
		const clientModule = moduleGraph.getModule(clientDep);
		const block = this.blocks[0];
		const client = Template.asString([
			`var client = ${runtimeTemplate.moduleExports({
				module: clientModule,
				chunkGraph,
				request: clientDep.userRequest,
				runtimeRequirements
			})}`,
			`var data = ${JSON.stringify(this.data)};`
		]);
		const keepActive = Template.asString([
			`var dispose = client.keepAlive({ data: data, active: ${JSON.stringify(
				!!block
			)}, module: module, onError: onError });`
		]);
		let source;
		if (block) {
			const dep = block.dependencies[0];
			const module = moduleGraph.getModule(dep);
			source = Template.asString([
				client,
				`module.exports = ${runtimeTemplate.moduleNamespacePromise({
					chunkGraph,
					block,
					module,
					request: this.request,
					strict: false, // TODO this should be inherited from the original module
					message: "import()",
					runtimeRequirements
				})};`,
				"if (module.hot) {",
				Template.indent([
					"module.hot.accept();",
					`module.hot.accept(${JSON.stringify(
						chunkGraph.getModuleId(module)
					)}, function() { module.hot.invalidate(); });`,
					"module.hot.dispose(function(data) { delete data.resolveSelf; dispose(data); });",
					"if (module.hot.data && module.hot.data.resolveSelf) module.hot.data.resolveSelf(module.exports);"
				]),
				"}",
				"function onError() { /* ignore */ }",
				keepActive
			]);
		} else {
			source = Template.asString([
				client,
				"var resolveSelf, onError;",
				`module.exports = new Promise(function(resolve, reject) { resolveSelf = resolve; onError = reject; });`,
				"if (module.hot) {",
				Template.indent([
					"module.hot.accept();",
					"if (module.hot.data && module.hot.data.resolveSelf) module.hot.data.resolveSelf(module.exports);",
					"module.hot.dispose(function(data) { data.resolveSelf = resolveSelf; dispose(data); });"
				]),
				"}",
				keepActive
			]);
		}
		sources.set("javascript", new RawSource(source));
		return {
			sources,
			runtimeRequirements
		};
	}

	/**
	 * @param {Hash} hash the hash used to track dependencies
	 * @param {UpdateHashContext} context context
	 * @returns {void}
	 */
	updateHash(hash, context) {
		super.updateHash(hash, context);
		hash.update(this.active ? "active" : "");
		hash.update(JSON.stringify(this.data));
	}
}

registerNotSerializable(LazyCompilationProxyModule);

class LazyCompilationDependencyFactory extends ModuleFactory {
	constructor(factory) {
		super();
		this._factory = factory;
	}

	/**
	 * @param {ModuleFactoryCreateData} data data object
	 * @param {function(Error=, ModuleFactoryResult=): void} callback callback
	 * @returns {void}
	 */
	create(data, callback) {
		const dependency = /** @type {LazyCompilationDependency} */ (
			data.dependencies[0]
		);
		callback(null, {
			module: dependency.proxyModule.originalModule
		});
	}
}

class LazyCompilationPlugin {
	/**
	 * @param {Object} options options
	 * @param {(function(Compiler, function(Error?, BackendApi?): void): void) | function(Compiler): Promise<BackendApi>} options.backend the backend
	 * @param {boolean} options.entries true, when entries are lazy compiled
	 * @param {boolean} options.imports true, when import() modules are lazy compiled
	 * @param {RegExp | string | (function(Module): boolean)} options.test additional filter for lazy compiled entrypoint modules
	 */
	constructor({ backend, entries, imports, test }) {
		this.backend = backend;
		this.entries = entries;
		this.imports = imports;
		this.test = test;
	}
	/**
	 * Apply the plugin
	 * @param {Compiler} compiler the compiler instance
	 * @returns {void}
	 */
	apply(compiler) {
		let backend;
		compiler.hooks.beforeCompile.tapAsync(
			"LazyCompilationPlugin",
			(params, callback) => {
				if (backend !== undefined) return callback();
				const promise = this.backend(compiler, (err, result) => {
					if (err) return callback(err);
					backend = result;
					callback();
				});
				if (promise && promise.then) {
					promise.then(b => {
						backend = b;
						callback();
					}, callback);
				}
			}
		);
		compiler.hooks.thisCompilation.tap(
			"LazyCompilationPlugin",
			(compilation, { normalModuleFactory }) => {
				normalModuleFactory.hooks.module.tap(
					"LazyCompilationPlugin",
					(originalModule, createData, resolveData) => {
						if (
							resolveData.dependencies.every(
								dep =>
									IGNORED_DEPENDENCY_TYPES.has(dep.type) ||
									(this.imports &&
										(dep.type === "import()" ||
											dep.type === "import() context element")) ||
									(this.entries && dep.type === "entry")
							) &&
							!/webpack[/\\]hot[/\\]|webpack-dev-server[/\\]client|webpack-hot-middleware[/\\]client/.test(
								resolveData.request
							) &&
							checkTest(this.test, originalModule)
						) {
							const moduleInfo = backend.module(originalModule);
							if (!moduleInfo) return;
							const { client, data, active } = moduleInfo;

							return new LazyCompilationProxyModule(
								compiler.context,
								originalModule,
								resolveData.request,
								client,
								data,
								active
							);
						}
					}
				);
				compilation.dependencyFactories.set(
					LazyCompilationDependency,
					new LazyCompilationDependencyFactory()
				);
			}
		);
		compiler.hooks.shutdown.tapAsync("LazyCompilationPlugin", callback => {
			backend.dispose(callback);
		});
	}
}

module.exports = LazyCompilationPlugin;
<|endoftext|>


<commit_before>import Gulp from 'gulp';
import $ from 'gulp-load-plugins';

$ = $();

Gulp.task('build:scripts',
  () => Gulp.src('estraits.babel.js')
            .pipe($.rename('estraits.js'))
            .pipe($.babel({
              loose: 'all',
              experimental: true
            }))
            .pipe(Gulp.dest('.'))
);
<commit_msg>Update asset pipeline<commit_after>import Gulp from 'gulp';
import GulpLoadPlugins from 'gulp-load-plugins';

const _ = GulpLoadPlugins();

Gulp.task('build:scripts',
  () => Gulp.src('estraits.babel.js')
            .pipe(_.rename('estraits.js'))
            .pipe(_.babel({
              stage: 0,
              loose: 'all'
            }))
            .pipe(Gulp.dest('.'))
);
<|endoftext|>


<commit_before>'use strict'

const assert = require('chai').assert
const temp   = require('temp').track()
const index  = require('./../src/index')

let file = null

describe('index()', function() {

	before(function() {

		file = temp.openSync({
			suffix: '.scss'
		})

	})

	it('should return an error when called with an invalid filePath', function() {

		return index(null, '/src', '/dist', null).then(({ data, savePath }) => {

			throw new Error('Returned without error')

		}, (err) => {

			assert.isNotNull(err)

		})

	})

	it('should return an error when called with a fictive filePath', function() {

		return index('test.scss', '/src', '/dist', null).then(({ data, savePath }) => {

			throw new Error('Returned without error')

		}, (err) => {

			assert.isNotNull(err)

		})

	})

	it('should load SCSS and transform it to CSS, add vendor prefixes and minify when distPath specified', function() {

		return index(file.path, '/src', '/dist', null).then(({ data, savePath }) => {

			assert.isString(savePath)
			assert.strictEqual(data, '')
			assert.strictEqual(savePath.substr(-4), '.css')

		})

	})

	it('should load SCSS, transform it to CSS and add vendor prefixes when distPath not specified', function() {

		return index(file.path, '/src', null, null).then(({ data, savePath }) => {

			assert.isString(savePath)
			assert.strictEqual(data, '')
			assert.strictEqual(savePath.substr(-4), '.css')

		})

	})

	describe('.cache', function() {

		it('should be an array', function() {

			assert.isArray(index.cache)

		})

	})

})<commit_msg>Route parameter must be an object<commit_after>'use strict'

const assert = require('chai').assert
const temp   = require('temp').track()
const index  = require('./../src/index')

let file = null

describe('index()', function() {

	before(function() {

		file = temp.openSync({
			suffix: '.scss'
		})

	})

	it('should return an error when called with an invalid filePath', function() {

		return index(null, '/src', '/dist', {}).then(({ data, savePath }) => {

			throw new Error('Returned without error')

		}, (err) => {

			assert.isNotNull(err)

		})

	})

	it('should return an error when called with a fictive filePath', function() {

		return index('test.scss', '/src', '/dist', {}).then(({ data, savePath }) => {

			throw new Error('Returned without error')

		}, (err) => {

			assert.isNotNull(err)

		})

	})

	it('should load SCSS and transform it to CSS, add vendor prefixes and minify when distPath specified', function() {

		return index(file.path, '/src', '/dist', {}).then(({ data, savePath }) => {

			assert.isString(savePath)
			assert.strictEqual(data, '')
			assert.strictEqual(savePath.substr(-4), '.css')

		})

	})

	it('should load SCSS, transform it to CSS and add vendor prefixes when distPath not specified', function() {

		return index(file.path, '/src', null, {}).then(({ data, savePath }) => {

			assert.isString(savePath)
			assert.strictEqual(data, '')
			assert.strictEqual(savePath.substr(-4), '.css')

		})

	})

	describe('.cache', function() {

		it('should be an array', function() {

			assert.isArray(index.cache)

		})

	})

})<|endoftext|>