<commit_before>require("sugar");

var async = require("async"),
    fs = require("fs"),
    path = require("path"),
    Table = require("./table"),
    LineReader = require('file-line-reader');

module.exports = function(connection) {

    var me = this;

    this.connection = connection;

    this.tables = { };

    this.definition = { };

    ////////////////////////////////////////////////////////////////////////////////////////////
    // TABLE DEFINITION METHODS
    ////////////////////////////////////////////////////////////////////////////////////////////
    this.listSomeTables = function(last, cb) {
        var options = { };

        if (cb == null && Object.isFunction(last)) {
            cb = last;
            last = null;
        }

        if (last) {
            options.ExclusiveStartTableName = last;
        }

        connection.listTables(options, function(err, data) {
            if (err) cb(err);
            else cb(null, { tables: data.TableNames, last: data.LastEvaluatedTableName });
        });
    };

    this.listAllTables = function(cb) {
        var done = false,
            last = null,
            tables = [ ];

        async.whilst(
            function() { return !done; },
            function(cb) {
                me.listSomeTables(last, function(err, data) {
                    if (err) cb(err);
                    else {
                        if (!data.last) done = true;
                        else last = data.last;

                        tables.add(data.tables);
                        cb();
                    }
                });
            },
            function(err) {
                cb(err, tables);
            }
        );
    };

    this.createTable = function(name, columns, key, read, write, indices, locals, cb) {
        var options = {
            TableName: name,
            ProvisionedThroughput: { ReadCapacityUnits: read, WriteCapacityUnits: write }
        };

        try {
            if (columns) {
                options.AttributeDefinitions = Object.keys(columns).map(function(name) {
                    return { AttributeName: name, AttributeType: columns[name] };
                });
            }

            if (Array.isArray(key)) {
                options.KeySchema = [ { AttributeName: key[0], KeyType: "HASH" } ];
                if (key.length > 1) options.KeySchema.push({ AttributeName: key[1], KeyType: "RANGE" });
            }
            else {
                options.KeySchema = [ { AttributeName: key, KeyType: "HASH" } ];
            }

            if (indices) {
                options.GlobalSecondaryIndexes = indices.map(function(index) {
                    var indexOptions = {
                        IndexName: index[0],
                        ProvisionedThroughput: {
                            ReadCapacityUnits: index[2],
                            WriteCapacityUnits: index[3]
                        }
                    };

                    if (Array.isArray(index[1])) {
                        indexOptions.KeySchema = [ { AttributeName: index[1][0], KeyType: "HASH" } ];
                        if (index[1].length > 1) indexOptions.KeySchema.push({ AttributeName: index[1][1], KeyType: "RANGE" });
                    }
                    else indexOptions.KeySchema = [ { AttributeName: index[1][0], KeyType: "HASH" } ];

                    if (Array.isArray(index[4])) indexOptions.Projection = { NonKeyAttributes: index[4], ProjectionType: "INCLUDE" };
                    else if (index[4]) indexOptions.Projection = { ProjectionType: index[4] };
                    else indexOptions.Projection = { ProjectionType: "KEYS_ONLY" };

                    return indexOptions;
                });
            }

            if (locals) {
                options.LocalSecondaryIndexes = locals.map(function(index) {
                    var indexOptions = {
                        IndexName: index[0],
                        KeySchema: [ options.KeySchema.first() ]
                    };

                    indexOptions.KeySchema.push({ AttributeName: index[1], KeyType: "RANGE" });

                    if (Array.isArray(index[2])) indexOptions.Projection = { NonKeyAttributes: index[2], ProjectionType: "INCLUDE" };
                    else if (index[2]) indexOptions.Projection = { ProjectionType: index[2] };
                    else indexOptions.Projection = { ProjectionType: "KEYS_ONLY" };

                    return indexOptions;
                });
            }
        }
        catch (ex) {
            cb(ex);
            return;
        }

        connection.createTable(options, function(err, data) {
            if (err) cb(err);
            else if (data != null) {
                var status = data.TableDescription.TableStatus;
                if (status == "CREATING") {
                    connection.waitFor('tableExists', { TableName: name }, function(err, data) {
                        cb(null, true);
                    });
                }
                else if (status == "ACTIVE") {
                    cb();
                }
                else {
                    cb(new Error(name + " status is " + status));
                }
            }
            else cb();
        });
    };

    this.deleteTable = function(table, cb) {
        connection.deleteTable({
            TableName: table
        }, function(err) {
            if (err) {
                cb();
            }
            else {
                var interval = setInterval(function() {
                    me.describeTable(table, function(err, data) {
                        if (err) {
                            clearInterval(interval);
                            cb();
                        }
                    });
                }, 2500);
            }
        });
    };

    this.describeTable = function(table, cb) {
        connection.describeTable({
            TableName: table
        }, cb);
    };

    ////////////////////////////////////////////////////////////////////////////////////////////
    // SCHEMA MANAGEMENT METHODS
    ////////////////////////////////////////////////////////////////////////////////////////////
    this.load = function(filter, cb) {
        if (cb == null && Object.isFunction(filter)) {
            cb = filter;
            filter = null;
        }

        me.listAllTables(function(err, tables) {
            if (err) cb(err);
            else {
                var candidates = null;
                if (filter) {
                    if (Object.isFunction(filter) || Object.isRegExp(filter)) {
                        tables = tables.filter(filter);
                    }
                    else {
                        candidates = { };
                        Object.keys(filter).forEach((key) => { candidates[filter[key].name] = key; });
                        tables = tables.filter((t) => { return candidates[t] != null; });
                    }
                }

                if (tables.length) {
                    async.forEachSeries(tables, function(table, cb) {
                        me.describeTable(table, function(err, metadata) {
                            if (err) cb(err);
                            else {
                                var name = candidates ? candidates[table] : table;
                                me.tables[name] = new Table(me, metadata);
                                cb();
                            }
                        });
                    }, cb);
                }
                else cb();
            }
        });
    };

    this.define = function(definition) {
        me.definition = definition;
        return me;
    };

    this.defineFromFile = function(path) {
        me.definition = JSON.parse(fs.readFileSync(path).toString());
        return me;
    };

    this.create = function(cb) {
        me.load(me.definition, function(err) {
            if (err) cb(err);
            else {
                async.forEachSeries(Object.keys(me.definition), function(table, cb) {
                    if (me.tables[table] == null) {
                        me.createTable(
                            me.definition[table].name,
                            me.definition[table].columns,
                            me.definition[table].key,
                            me.definition[table].read,
                            me.definition[table].write,
                            me.definition[table].indices,
                            me.definition[table].locals,
                            function(err) {
                                if (err) cb(err);
                                else {
                                    me.describeTable(me.definition[table].name, function(err, metadata) {
                                        if (err) cb(err);
                                        else {
                                            me.tables[table] = new Table(me, metadata);
                                            cb();
                                        }
                                    });
                                }
                            }
                        );
                    }
                    else {
                        cb();
                    }
                }, cb);
            }
        });
    };

    this.drop = function(cb) {
        var tables = Object.values(me.definition).map("name").union(Object.values(me.tables).map("name"));
        async.forEachSeries(tables, function(table, cb) {
            me.deleteTable(table, cb);
        }, cb);
    };

    this.backup = function(dir, cb) {
        fs.lstat(dir, function(err, stat) {
            var isDirectory = (!err && stat.isDirectory());
            if (!isDirectory) {
                cb(new Error(dir + " does not exists as a directory."));
            }
            else {
                async.forEachSeries(Object.values(me.tables), function(table, cb) {
                    table.save(path.join(dir, table.name + ".json"), cb);
                }, cb);
            }
        });
    };

    this.restore = function(dir, cb) {
        fs.lstat(dir, function(err, stat) {
            var isDirectory = (!err && stat.isDirectory());
            if (!isDirectory) {
                cb(new Error(dir + " does not exists as a directory."));
            }
            else {
                async.forEachSeries(Object.values(me.tables), function(table, cb) {
                    table.load(path.join(dir, table.name + ".json"), cb);
                }, cb);
            }
        });
    };

};<commit_msg>Add require<commit_after>require("sugar");

var async = require("async"),
    fs = require("fs"),
    path = require("path"),
    Table = require("./table"),
    LineReader = require('file-line-reader');

module.exports = function(connection) {

    var me = this;

    this.connection = connection;

    this.tables = { };

    this.definition = { };

    ////////////////////////////////////////////////////////////////////////////////////////////
    // TABLE DEFINITION METHODS
    ////////////////////////////////////////////////////////////////////////////////////////////
    this.listSomeTables = function(last, cb) {
        var options = { };

        if (cb == null && Object.isFunction(last)) {
            cb = last;
            last = null;
        }

        if (last) {
            options.ExclusiveStartTableName = last;
        }

        connection.listTables(options, function(err, data) {
            if (err) cb(err);
            else cb(null, { tables: data.TableNames, last: data.LastEvaluatedTableName });
        });
    };

    this.listAllTables = function(cb) {
        var done = false,
            last = null,
            tables = [ ];

        async.whilst(
            function() { return !done; },
            function(cb) {
                me.listSomeTables(last, function(err, data) {
                    if (err) cb(err);
                    else {
                        if (!data.last) done = true;
                        else last = data.last;

                        tables.add(data.tables);
                        cb();
                    }
                });
            },
            function(err) {
                cb(err, tables);
            }
        );
    };

    this.createTable = function(name, columns, key, read, write, indices, locals, cb) {
        var options = {
            TableName: name,
            ProvisionedThroughput: { ReadCapacityUnits: read, WriteCapacityUnits: write }
        };

        try {
            if (columns) {
                options.AttributeDefinitions = Object.keys(columns).map(function(name) {
                    return { AttributeName: name, AttributeType: columns[name] };
                });
            }

            if (Array.isArray(key)) {
                options.KeySchema = [ { AttributeName: key[0], KeyType: "HASH" } ];
                if (key.length > 1) options.KeySchema.push({ AttributeName: key[1], KeyType: "RANGE" });
            }
            else {
                options.KeySchema = [ { AttributeName: key, KeyType: "HASH" } ];
            }

            if (indices) {
                options.GlobalSecondaryIndexes = indices.map(function(index) {
                    var indexOptions = {
                        IndexName: index[0],
                        ProvisionedThroughput: {
                            ReadCapacityUnits: index[2],
                            WriteCapacityUnits: index[3]
                        }
                    };

                    if (Array.isArray(index[1])) {
                        indexOptions.KeySchema = [ { AttributeName: index[1][0], KeyType: "HASH" } ];
                        if (index[1].length > 1) indexOptions.KeySchema.push({ AttributeName: index[1][1], KeyType: "RANGE" });
                    }
                    else indexOptions.KeySchema = [ { AttributeName: index[1][0], KeyType: "HASH" } ];

                    if (Array.isArray(index[4])) indexOptions.Projection = { NonKeyAttributes: index[4], ProjectionType: "INCLUDE" };
                    else if (index[4]) indexOptions.Projection = { ProjectionType: index[4] };
                    else indexOptions.Projection = { ProjectionType: "KEYS_ONLY" };

                    return indexOptions;
                });
            }

            if (locals) {
                options.LocalSecondaryIndexes = locals.map(function(index) {
                    var indexOptions = {
                        IndexName: index[0],
                        KeySchema: [ options.KeySchema.first() ]
                    };

                    indexOptions.KeySchema.push({ AttributeName: index[1], KeyType: "RANGE" });

                    if (Array.isArray(index[2])) indexOptions.Projection = { NonKeyAttributes: index[2], ProjectionType: "INCLUDE" };
                    else if (index[2]) indexOptions.Projection = { ProjectionType: index[2] };
                    else indexOptions.Projection = { ProjectionType: "KEYS_ONLY" };

                    return indexOptions;
                });
            }
        }
        catch (ex) {
            cb(ex);
            return;
        }

        connection.createTable(options, function(err, data) {
            if (err) cb(err);
            else if (data != null) {
                var status = data.TableDescription.TableStatus;
                if (status == "CREATING") {
                    connection.waitFor('tableExists', { TableName: name }, function(err, data) {
                        cb(null, true);
                    });
                }
                else if (status == "ACTIVE") {
                    cb();
                }
                else {
                    cb(new Error(name + " status is " + status));
                }
            }
            else cb();
        });
    };

    this.deleteTable = function(table, cb) {
        connection.deleteTable({
            TableName: table
        }, function(err) {
            if (err) {
                cb();
            }
            else {
                var interval = setInterval(function() {
                    me.describeTable(table, function(err, data) {
                        if (err) {
                            clearInterval(interval);
                            cb();
                        }
                    });
                }, 2500);
            }
        });
    };

    this.describeTable = function(table, cb) {
        connection.describeTable({
            TableName: table
        }, cb);
    };

    ////////////////////////////////////////////////////////////////////////////////////////////
    // SCHEMA MANAGEMENT METHODS
    ////////////////////////////////////////////////////////////////////////////////////////////
    this.load = function(filter, cb) {
        if (cb == null && Object.isFunction(filter)) {
            cb = filter;
            filter = null;
        }

        me.listAllTables(function(err, tables) {
            if (err) cb(err);
            else {
                var candidates = null;
                if (filter) {
                    if (Object.isFunction(filter) || Object.isRegExp(filter)) {
                        tables = tables.filter(filter);
                    }
                    else {
                        candidates = { };
                        Object.keys(filter).forEach((key) => { candidates[filter[key].name] = key; });
                        tables = tables.filter((t) => { return candidates[t] != null; });
                    }
                }

                if (tables.length) {
                    async.forEachSeries(tables, function(table, cb) {
                        me.describeTable(table, function(err, metadata) {
                            if (err) cb(err);
                            else {
                                var name = candidates ? candidates[table] : table;
                                me.tables[name] = new Table(me, metadata);
                                cb();
                            }
                        });
                    }, cb);
                }
                else cb();
            }
        });
    };

    this.define = function(definition) {
        me.definition = definition;
        return me;
    };

    this.defineFromFile = function(filepath) {
        me.definition = JSON.parse(fs.readFileSync(filepath).toString());
        return me;
    };

    this.require = function(filepath) {
        var stat = fs.lstatSync(filepath);
        if (stat.isDirectory()) {
            fs.readDirSync(filepath).filter(/.*js/i).forEach(function(file) {
                var tableName = path.basename(file, '.js');
                me.definition[tableName] = require(path.join(filepath, file));
            });
        }
        else {
            var tableName = path.basename(filepath, '.js');
            me.definition[tableName] = require(filepath);
        }

        return me;
    };

    this.create = function(cb) {
        me.load(me.definition, function(err) {
            if (err) cb(err);
            else {
                async.forEachSeries(Object.keys(me.definition), function(table, cb) {
                    if (me.tables[table] == null) {
                        me.createTable(
                            me.definition[table].name,
                            me.definition[table].columns,
                            me.definition[table].key,
                            me.definition[table].read,
                            me.definition[table].write,
                            me.definition[table].indices,
                            me.definition[table].locals,
                            function(err) {
                                if (err) cb(err);
                                else {
                                    me.describeTable(me.definition[table].name, function(err, metadata) {
                                        if (err) cb(err);
                                        else {
                                            me.tables[table] = new Table(me, metadata);

                                            var Methods = me.definition[table].methods;
                                            if (Methods) {
                                                var methods = new Methods(me.tables[table]);
                                                for (var p in methods) {
                                                    if (Object.isFunction(methods[p]) && me.tables[table][p] == null) {
                                                        me.tables[table][p] = methods[p];
                                                    }
                                                }
                                            }

                                            cb();
                                        }
                                    });
                                }
                            }
                        );
                    }
                    else {
                        cb();
                    }
                }, cb);
            }
        });
    };

    this.drop = function(cb) {
        var tables = Object.values(me.definition).map("name").union(Object.values(me.tables).map("name"));
        async.forEachSeries(tables, function(table, cb) {
            me.deleteTable(table, cb);
        }, cb);
    };

    this.backup = function(dir, cb) {
        fs.lstat(dir, function(err, stat) {
            var isDirectory = (!err && stat.isDirectory());
            if (!isDirectory) {
                cb(new Error(dir + " does not exists as a directory."));
            }
            else {
                async.forEachSeries(Object.values(me.tables), function(table, cb) {
                    table.save(path.join(dir, table.name + ".json"), cb);
                }, cb);
            }
        });
    };

    this.restore = function(dir, cb) {
        fs.lstat(dir, function(err, stat) {
            var isDirectory = (!err && stat.isDirectory());
            if (!isDirectory) {
                cb(new Error(dir + " does not exists as a directory."));
            }
            else {
                async.forEachSeries(Object.values(me.tables), function(table, cb) {
                    table.load(path.join(dir, table.name + ".json"), cb);
                }, cb);
            }
        });
    };

};<|endoftext|>


<commit_before>var objectAssign = require('object-assign'),
    SchemaError = require('./errors/SchemaError.js');

var finalize = function(formalizedSchema, nonFormalizedSchema, fn, sync) {

	// Add the old non-formalized schema - for preventing
	// redundant formalization and for usage by the
	// validator when sending validation errors.
	//
	// Make the property unenumerable.
	Object.defineProperty(formalizedSchema, '_nonFormalizedSchema', {
		value: nonFormalizedSchema,
		enumerable: false,
	});

	// We seal the schema so no futher editing can take place.
	Object.seal(formalizedSchema);

	// Allow for I/O if running async.
	if (fn) {
		if (sync) return fn(formalizedSchema);
		else setImmediate(fn, formalizedSchema);
	}

};

var formalizeObject = function(formalizedSchema, nonFormalizedSchema, fn, sync) {

	// For backwards compatibility be before version 1.0.3
	// we change the allowUnknownKeys to unknownKeys
	if (typeof formalizedSchema.allowUnknownKeys === 'boolean') {
		if (!process.env['ISVALID_SILENCE']) {
			console.error('isvalid: DEPRECATED: Validator allowUnknownKeys has been deprecated in favour of unknownKeys as of version 1.0.4. See README for more info.')
		}
		formalizedSchema.unknownKeys = (formalizedSchema.allowUnknownKeys ? 'allow' : 'deny');
		formalizedSchema.wasAllowUnknownKeys = true;
		delete formalizedSchema.allowUnknownKeys;
	}

	// Set an empty sub-schema if schema is not present.
	formalizedSchema.schema = formalizedSchema.schema || {};

	// Build new formalized schema into this.
	var formalizedSubschema = {};

	var keys = Object.keys(formalizedSchema.schema);

	(function formalizeNextKey(idx) {

		// If idx equals keys length - we are done.
		if (idx == keys.length) {
			formalizedSchema.schema = formalizedSubschema;
			formalizedSchema = finalize(formalizedSchema, nonFormalizedSchema, fn, sync);
			return;
		}

		var key = keys[idx];

		formalizeAny(formalizedSchema.schema[key], function(formalizedKey) {

			// Apply implicit required if sub-schema is required.
			if (formalizedSchema.required === undefined || formalizedSchema.required == 'implicit') {
				if (formalizedKey.required === true) formalizedSchema.required = true;
			}

			formalizedSubschema[key] = formalizedKey;

			if (sync) return formalizeNextKey(idx + 1);
			else setImmediate(formalizeNextKey, idx + 1);

		}, sync);

	})(0);

	return formalizedSchema;

};

var formalizeArray = function(formalizedSchema, nonFormalizedSchema, fn, sync) {

	// formalizedSchema has been pre-processed by formalizeAny, so
	// we only need to formalize the sub-schema.

	return formalizeAny(formalizedSchema.schema, function(formalizedSubschema) {

		formalizedSchema.schema = formalizedSubschema;

		// Apply implicit required if sub-schema has required data.
		if (typeof(formalizedSchema.required) === 'undefined' || formalizedSchema.required === 'implicit') {
			if (formalizedSchema.schema.required === true) formalizedSchema.required = true;
		}

		return finalize(formalizedSchema, nonFormalizedSchema, fn, sync);

	}, sync);

};

var formalizeAny = function(schema, fn, sync) {

	// If no fn we operate sync.
	if (fn === undefined) {
		fn = function(s) { return s; };
		sync = true;
	}

	// If schema is already formalized we just call back.
	if (schema._nonFormalizedSchema !== undefined) return fn(schema);

	if (!schema.type && !schema.custom) {
		if ('Object' == schema.constructor.name) {
			return formalizeObject({ type: Object, schema: schema }, schema, fn, sync);
		}
		if ('Array' == schema.constructor.name) {
			if (schema.length == 0) throw new SchemaError(schema, 'Array must have exactly one schema.');
			return formalizeArray({ type: Array, schema: schema[0] }, schema, fn, sync);
		}
    if (typeof schema === 'function' && ['String', 'Number', 'Boolean', 'Date'].indexOf(schema.name) > -1) {
			return formalizeAny({ type: schema }, fn, sync)
		}
		throw new SchemaError(schema, 'Supported shortcuts are Object, Array, String, Number, Boolean, Date.');
	}

	var formalizedSchema = {};

	var validators = {};

	// Ensure type is supported.
	if (typeof schema.type !== 'undefined' && [ 'Object', 'Array', 'String', 'Number', 'Boolean', 'Date' ].indexOf(schema.type.name) == -1) {
		throw new SchemaError(schema, 'Cannot validate schema of type ' + schema.type.name + '.');
	}

	var common = {
		'type': ['Function'],
		'required': ['Boolean', 'String'],
		'default': true,
		'allowNull': ['Boolean'],
		'errors': [ 'Object' ],
		'custom': [ 'Function', 'Array' ]
	};

	var typeSpecific = {};

	if (schema.type !== undefined) {
		if ('Object' == schema.type.name) typeSpecific = {
			'schema': true,
			'allowUnknownKeys': [ 'Boolean' ], // Deprecated as of version 1.0.4
			'unknownKeys': [ 'String' ]
		};
		if ('Array' == schema.type.name) typeSpecific = {
			'schema': true,
			'len': [ 'String', 'Number' ],
			'unique': [ 'Boolean' ]
		}
		if ('String' == schema.type.name) typeSpecific = {
			'match': [ 'RegExp' ],
			'trim': [ 'Boolean' ],
			'enum': [ 'Array' ]
		}
		if ('Number' == schema.type.name) typeSpecific = {
			'range': [ 'String', 'Number' ]
		}
	}

	// If custom validator is provided allow for options.
	if (schema.custom !== undefined) {
		common = objectAssign(common, { 'options': true })
	}

	validators = objectAssign(common, typeSpecific);

	// Copy validators to formalizedSchema - checking
	// for non-supported validators at the same time.
	for (var key in schema) {
		if (validators[key] === undefined) throw new SchemaError(
			schema,
			'Validator \'' + key + '\' is unknown in this context.'
		);

		if (validators[key] !== true && validators[key].indexOf(schema[key].constructor.name) == -1) {
			throw new SchemaError(
				schema,
				'Validator \'' + key + '\' must be of type(s) ' + validators[key].join(', ') + '.'
			);
		}

		formalizedSchema[key] = schema[key];
	}

	// Convert custom function to array
	if (typeof formalizedSchema.custom === 'function') {
		formalizedSchema.custom = [formalizedSchema.custom];
	}

	// Throw error if required is invalid value
	if (typeof formalizedSchema.required === 'string' && formalizedSchema.required != 'implicit') {
		throw new SchemaError(
			schema,
			'Validator \'required\' must be a Boolean or String of value \'implicit\'.'
		);
	}

	// Check object unknownKeys
	if (typeof formalizedSchema.unknownKeys === 'string' &&
			['allow','deny','remove'].indexOf(formalizedSchema.unknownKeys) == -1) {
		throw new SchemaError(
			schema,
			'Validator \'unknownKeys\' must have value \'allow\', \'deny\' or \'remove\'.'
		);
	}

	// Check string enums
	if (typeof formalizedSchema.enum !== 'undefined') {
		if (formalizedSchema.enum.length < 1) {
			throw new SchemaError(
				schema,
				'Validator \'enum\' must have at least one item.'
			);
		}
		for (var idx in formalizedSchema.enum) {
			if (typeof formalizedSchema.enum[idx] !== 'string') {
				throw new SchemaError(
					schema,
					'Validator \'enum\' must be an array of strings.'
				);
			}
		}
	}

	// Finalize objects and arrays if necessary.
	if (formalizedSchema.type) {
		if ('Object' == formalizedSchema.type.name) return formalizeObject(formalizedSchema, schema, fn, sync);
		if ('Array' == formalizedSchema.type.name) return formalizeArray(formalizedSchema, schema, fn, sync);
	}

	return finalize(formalizedSchema, schema, fn, sync);

};

exports.formalize = module.exports.formalize = formalizeAny;
<commit_msg>Schemas are now optional on Array and Object.<commit_after>var objectAssign = require('object-assign'),
    SchemaError = require('./errors/SchemaError.js');

var finalize = function(formalizedSchema, nonFormalizedSchema, fn, sync) {

	// Add the old non-formalized schema - for preventing
	// redundant formalization and for usage by the
	// validator when sending validation errors.
	//
	// Make the property unenumerable.
	Object.defineProperty(formalizedSchema, '_nonFormalizedSchema', {
		value: nonFormalizedSchema,
		enumerable: false,
	});

	// We seal the schema so no futher editing can take place.
	Object.seal(formalizedSchema);

	// Allow for I/O if running async.
	if (fn) {
		if (sync) return fn(formalizedSchema);
		else setImmediate(fn, formalizedSchema);
	}

};

var formalizeObject = function(formalizedSchema, nonFormalizedSchema, fn, sync) {

	// For backwards compatibility be before version 1.0.3
	// we change the allowUnknownKeys to unknownKeys
	if (typeof formalizedSchema.allowUnknownKeys === 'boolean') {
		if (!process.env['ISVALID_SILENCE']) {
			console.error('isvalid: DEPRECATED: Validator allowUnknownKeys has been deprecated in favour of unknownKeys as of version 1.0.4. See README for more info.')
		}
		formalizedSchema.unknownKeys = (formalizedSchema.allowUnknownKeys ? 'allow' : 'deny');
		formalizedSchema.wasAllowUnknownKeys = true;
		delete formalizedSchema.allowUnknownKeys;
	}

	// If no sub-schema is provided we consider the schema final.
	if (typeof formalizedSchema.schema === 'undefined') return finalize(formalizedSchema, nonFormalizedSchema, fn, sync);

	// Build new formalized schema into this.
	var formalizedSubschema = {};

	var keys = Object.keys(formalizedSchema.schema);

	(function formalizeNextKey(idx) {

		// If idx equals keys length - we are done.
		if (idx == keys.length) {
			formalizedSchema.schema = formalizedSubschema;
			formalizedSchema = finalize(formalizedSchema, nonFormalizedSchema, fn, sync);
			return;
		}

		var key = keys[idx];

		formalizeAny(formalizedSchema.schema[key], function(formalizedKey) {

			// Apply implicit required if sub-schema is required.
			if (formalizedSchema.required === undefined || formalizedSchema.required == 'implicit') {
				if (formalizedKey.required === true) formalizedSchema.required = true;
			}

			formalizedSubschema[key] = formalizedKey;

			if (sync) return formalizeNextKey(idx + 1);
			else setImmediate(formalizeNextKey, idx + 1);

		}, sync);

	})(0);

	return formalizedSchema;

};

var formalizeArray = function(formalizedSchema, nonFormalizedSchema, fn, sync) {

	// formalizedSchema has been pre-processed by formalizeAny, so
	// we only need to formalize the sub-schema.

	// If no sub-schema is provided we consider the schema final.
	if (typeof formalizedSchema.schema === 'undefined') return finalize(formalizedSchema, nonFormalizedSchema, fn, sync);

	return formalizeAny(formalizedSchema.schema, function(formalizedSubschema) {

		formalizedSchema.schema = formalizedSubschema;

		// Apply implicit required if sub-schema has required data.
		if (typeof(formalizedSchema.required) === 'undefined' || formalizedSchema.required === 'implicit') {
			if (formalizedSchema.schema.required === true) formalizedSchema.required = true;
		}

		return finalize(formalizedSchema, nonFormalizedSchema, fn, sync);

	}, sync);

};

var formalizeAny = function(schema, fn, sync) {

	// If no fn we operate sync.
	if (fn === undefined) {
		fn = function(s) { return s; };
		sync = true;
	}

	// If schema is already formalized we just call back.
	if (schema._nonFormalizedSchema !== undefined) return fn(schema);

	if (!schema.type && !schema.custom) {
		if ('Object' == schema.constructor.name) {
			return formalizeObject({ type: Object, schema: schema }, schema, fn, sync);
		}
		if ('Array' == schema.constructor.name) {
			if (schema.length == 0) throw new SchemaError(schema, 'Array must have exactly one schema.');
			return formalizeArray({ type: Array, schema: schema[0] }, schema, fn, sync);
		}
    if (typeof schema === 'function' && ['String', 'Number', 'Boolean', 'Date'].indexOf(schema.name) > -1) {
			return formalizeAny({ type: schema }, fn, sync)
		}
		throw new SchemaError(schema, 'Supported shortcuts are Object, Array, String, Number, Boolean, Date.');
	}

	var formalizedSchema = {};

	var validators = {};

	// Ensure type is supported.
	if (typeof schema.type !== 'undefined' && [ 'Object', 'Array', 'String', 'Number', 'Boolean', 'Date' ].indexOf(schema.type.name) == -1) {
		throw new SchemaError(schema, 'Cannot validate schema of type ' + schema.type.name + '.');
	}

	var common = {
		'type': ['Function'],
		'required': ['Boolean', 'String'],
		'default': true,
		'allowNull': ['Boolean'],
		'errors': [ 'Object' ],
		'custom': [ 'Function', 'Array' ]
	};

	var typeSpecific = {};

	if (schema.type !== undefined) {
		if ('Object' == schema.type.name) typeSpecific = {
			'schema': true,
			'allowUnknownKeys': [ 'Boolean' ], // Deprecated as of version 1.0.4
			'unknownKeys': [ 'String' ]
		};
		if ('Array' == schema.type.name) typeSpecific = {
			'schema': true,
			'len': [ 'String', 'Number' ],
			'unique': [ 'Boolean' ]
		}
		if ('String' == schema.type.name) typeSpecific = {
			'match': [ 'RegExp' ],
			'trim': [ 'Boolean' ],
			'enum': [ 'Array' ]
		}
		if ('Number' == schema.type.name) typeSpecific = {
			'range': [ 'String', 'Number' ]
		}
	}

	// If custom validator is provided allow for options.
	if (schema.custom !== undefined) {
		common = objectAssign(common, { 'options': true })
	}

	validators = objectAssign(common, typeSpecific);

	// Copy validators to formalizedSchema - checking
	// for non-supported validators at the same time.
	for (var key in schema) {
		if (validators[key] === undefined) throw new SchemaError(
			schema,
			'Validator \'' + key + '\' is unknown in this context.'
		);

		if (validators[key] !== true && validators[key].indexOf(schema[key].constructor.name) == -1) {
			throw new SchemaError(
				schema,
				'Validator \'' + key + '\' must be of type(s) ' + validators[key].join(', ') + '.'
			);
		}

		formalizedSchema[key] = schema[key];
	}

	// Convert custom function to array
	if (typeof formalizedSchema.custom === 'function') {
		formalizedSchema.custom = [formalizedSchema.custom];
	}

	// Throw error if required is invalid value
	if (typeof formalizedSchema.required === 'string' && formalizedSchema.required != 'implicit') {
		throw new SchemaError(
			schema,
			'Validator \'required\' must be a Boolean or String of value \'implicit\'.'
		);
	}

	// Check object unknownKeys
	if (typeof formalizedSchema.unknownKeys === 'string' &&
			['allow','deny','remove'].indexOf(formalizedSchema.unknownKeys) == -1) {
		throw new SchemaError(
			schema,
			'Validator \'unknownKeys\' must have value \'allow\', \'deny\' or \'remove\'.'
		);
	}

	// Check string enums
	if (typeof formalizedSchema.enum !== 'undefined') {
		if (formalizedSchema.enum.length < 1) {
			throw new SchemaError(
				schema,
				'Validator \'enum\' must have at least one item.'
			);
		}
		for (var idx in formalizedSchema.enum) {
			if (typeof formalizedSchema.enum[idx] !== 'string') {
				throw new SchemaError(
					schema,
					'Validator \'enum\' must be an array of strings.'
				);
			}
		}
	}

	// Finalize objects and arrays if necessary.
	if (formalizedSchema.type) {
		if ('Object' == formalizedSchema.type.name) return formalizeObject(formalizedSchema, schema, fn, sync);
		if ('Array' == formalizedSchema.type.name) return formalizeArray(formalizedSchema, schema, fn, sync);
	}

	return finalize(formalizedSchema, schema, fn, sync);

};

exports.formalize = module.exports.formalize = formalizeAny;
<|endoftext|>


<commit_before>const webpack = require('webpack');
const { resolve } = require('path');
const { existsSync } = require('fs');
const merge = require('webpack-merge');
const { filter } = require('minimatch');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const CrittersPlugin = require('critters-webpack-plugin');
const RenderHTMLPlugin = require('./render-html-plugin');
const PushManifestPlugin = require('./push-manifest');
const baseConfig = require('./webpack-base-config');
const BabelEsmPlugin = require('babel-esm-plugin');
const { normalizePath } = require('../../util');

const cleanFilename = name => name.replace(/(^\/(routes|components\/(routes|async))\/|(\/index)?\.js$)/g, '');

function clientConfig(env) {
	const { isProd, source, src /*, port? */ } = env;

	let entry = {
		bundle: resolve(__dirname, './../entry'),
		polyfills: resolve(__dirname, './polyfills')
	};

	if (!isProd) {
		entry.bundle = [
			entry.bundle,
			'webpack-dev-server/client',
			'webpack/hot/dev-server'
		];
	}

	return {
		entry: entry,
		output: {
			path: env.dest,
			publicPath: '/',
			filename: isProd ? '[name].[chunkhash:5].js' : '[name].js',
			chunkFilename: '[name].chunk.[chunkhash:5].js'
		},

		resolveLoader: {
			alias: {
				async: resolve(__dirname, './async-component-loader')
			}
		},

		// automatic async components :)
		module: {
			rules: [
				{
					test: /\.jsx?$/,
					include: [
						filter(source('routes')+'/{*.js,*/index.js}'),
						filter(source('components')+'/{routes,async}/{*.js,*/index.js}')
					],
					loader: resolve(__dirname, './async-component-loader'),
					options: {
						name(filename) {
							filename = normalizePath(filename);
							let relative = filename.replace(normalizePath(src), '');
							if (!relative.includes('/routes/')) return false;
							return 'route-' + cleanFilename(relative);
						},
						formatName(filename) {
							filename = normalizePath(filename);
							let relative = filename.replace(normalizePath(source('.')), '');
							return cleanFilename(relative);
						}
					}
				}
			]
		},

		plugins: [
			new CrittersPlugin(),
			...RenderHTMLPlugin(env),
			new PushManifestPlugin(env),
			new CopyWebpackPlugin([
				...(
					existsSync(source('manifest.json'))
					? [{ from:'manifest.json' }]
					: [{
						from: resolve(__dirname, '../../resources/manifest.json'),
						to: 'manifest.json'
					}, {
						from: resolve(__dirname, '../../resources/icon.png'),
						to: 'assets/icon.png'
					}]
				),
				// copy any static files
				existsSync(source('assets')) && { from:'assets', to:'assets' }
			].filter(Boolean))
		]
	};
}

function isProd(config) {
	let limit = 200 * 1000; // 200kb

	const prodConfig = {
		performance: Object.assign({
			hints: 'warning',
			maxAssetSize: limit,
			maxEntrypointSize: limit,
		}, config.pkg.performance),

		plugins: [
			new webpack.DefinePlugin({
				'process.env.ADD_SW': config.sw,
				'process.env.ES_BUILD': false,
				'process.env.ESM': config.esm,
			})
		],

		optimization: {
			minimizer: [
				new UglifyJsPlugin({
					cache: true,
					parallel: true,
					uglifyOptions: {
						sourceMap: true,
						output: { comments:false },
						mangle: true,
						compress: {
							properties: true,
							keep_fargs: false,
							pure_getters: true,
							collapse_vars: true,
							warnings: false,
							// screw_ie8: true,
							sequences: true,
							dead_code: true,
							drop_debugger: true,
							comparisons: true,
							conditionals: true,
							evaluate: true,
							booleans: true,
							loops: true,
							unused: true,
							hoist_funs: true,
							if_return: true,
							join_vars: true,
							// cascade: true,
							drop_console: false,
							pure_funcs: [
								'classCallCheck',
								'_classCallCheck',
								'_possibleConstructorReturn',
								'Object.freeze',
								'invariant',
								'warning'
							]
						},
					},
					sourceMap: true,
				}),
				new OptimizeCssAssetsPlugin({}),
			],
		},
	};

	if (config.sw) {
		prodConfig.plugins.push(
			new SWPrecacheWebpackPlugin({
				filename: 'sw.js',
				navigateFallback: 'index.html',
				navigateFallbackWhitelist: [/^(?!\/__).*/],
				minify: true,
				stripPrefix: config.cwd,
				staticFileGlobsIgnorePatterns: [
					/\.esm\.js$/,
					/polyfills(\..*)?\.js$/,
					/\.map$/,
					/push-manifest\.json$/,
					/.DS_Store/,
					/\.git/
				]
			}),
		);
	}

	if (config.esm) {
		prodConfig.plugins.push(
			new BabelEsmPlugin({
				filename: '[name].[chunkhash:5].esm.js',
				chunkFilename: '[name].chunk.[chunkhash:5].esm.js',
				beforeStartExecution: plugins => {
					plugins.forEach(plugin => {
						if (plugin.constructor.name === 'DefinePlugin' && plugin.definitions) {
							for (const definition in plugin.definitions) {
								if (definition === 'process.env.ES_BUILD') {
									plugin.definitions[definition] = true;
								}
							}
						} else if (plugin.constructor.name === 'DefinePlugin' && !plugin.definitions) {
							throw new Error('WebpackDefinePlugin found but not `process.env.ES_BUILD`.');
						}
					});
				},
			}),
		);

		if (config.sw) {
			prodConfig.plugins.push(
				new SWPrecacheWebpackPlugin({
					filename: 'sw-esm.js',
					navigateFallback: 'index.html',
					navigateFallbackWhitelist: [/^(?!\/__).*/],
					minify: true,
					stripPrefix: config.cwd,
					staticFileGlobsIgnorePatterns: [
						/(\.[\w]{5}\.js)/,
						/polyfills(\..*)?\.js$/,
						/\.map$/,
						/push-manifest\.json$/,
						/.DS_Store/,
						/\.git/
					]
				}),
			);
		}
	}

	if (config.analyze) {
		prodConfig.plugins.push(
			new BundleAnalyzerPlugin()
		);
	}

	return prodConfig;
}

function isDev(config) {
	const { cwd, src } = config;

	return {
		plugins: [
			new webpack.NamedModulesPlugin(),
			new webpack.HotModuleReplacementPlugin()
		],

		devServer: {
			inline: true,
			hot: true,
			compress: true,
			publicPath: '/',
			contentBase: src,
			https: config.https,
			port: process.env.PORT || config.port || 8080,
			host: process.env.HOST || config.host || '0.0.0.0',
			// setup(app) {
			// 	app.use(middleware);
			// },
			disableHostCheck: true,
			historyApiFallback: true,
			quiet: true,
			clientLogLevel: 'none',
			overlay: false,
			stats: 'minimal',
			watchOptions: {
				ignored: [
					resolve(cwd, 'build'),
					resolve(cwd, 'node_modules')
				]
			}
		}
	};
}

module.exports = function (env) {
	return merge(
		baseConfig(env),
		clientConfig(env),
		(env.isProd ? isProd : isDev)(env)
	);
};
<commit_msg>fix: move Critters after HTML<commit_after>const webpack = require('webpack');
const { resolve } = require('path');
const { existsSync } = require('fs');
const merge = require('webpack-merge');
const { filter } = require('minimatch');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const CrittersPlugin = require('critters-webpack-plugin');
const RenderHTMLPlugin = require('./render-html-plugin');
const PushManifestPlugin = require('./push-manifest');
const baseConfig = require('./webpack-base-config');
const BabelEsmPlugin = require('babel-esm-plugin');
const { normalizePath } = require('../../util');

const cleanFilename = name => name.replace(/(^\/(routes|components\/(routes|async))\/|(\/index)?\.js$)/g, '');

function clientConfig(env) {
	const { isProd, source, src /*, port? */ } = env;

	let entry = {
		bundle: resolve(__dirname, './../entry'),
		polyfills: resolve(__dirname, './polyfills')
	};

	if (!isProd) {
		entry.bundle = [
			entry.bundle,
			'webpack-dev-server/client',
			'webpack/hot/dev-server'
		];
	}

	return {
		entry: entry,
		output: {
			path: env.dest,
			publicPath: '/',
			filename: isProd ? '[name].[chunkhash:5].js' : '[name].js',
			chunkFilename: '[name].chunk.[chunkhash:5].js'
		},

		resolveLoader: {
			alias: {
				async: resolve(__dirname, './async-component-loader')
			}
		},

		// automatic async components :)
		module: {
			rules: [
				{
					test: /\.jsx?$/,
					include: [
						filter(source('routes')+'/{*.js,*/index.js}'),
						filter(source('components')+'/{routes,async}/{*.js,*/index.js}')
					],
					loader: resolve(__dirname, './async-component-loader'),
					options: {
						name(filename) {
							filename = normalizePath(filename);
							let relative = filename.replace(normalizePath(src), '');
							if (!relative.includes('/routes/')) return false;
							return 'route-' + cleanFilename(relative);
						},
						formatName(filename) {
							filename = normalizePath(filename);
							let relative = filename.replace(normalizePath(source('.')), '');
							return cleanFilename(relative);
						}
					}
				}
			]
		},

		plugins: [
			...RenderHTMLPlugin(env),
			new PushManifestPlugin(env),
			new CrittersPlugin(),
			new CopyWebpackPlugin([
				...(
					existsSync(source('manifest.json'))
					? [{ from:'manifest.json' }]
					: [{
						from: resolve(__dirname, '../../resources/manifest.json'),
						to: 'manifest.json'
					}, {
						from: resolve(__dirname, '../../resources/icon.png'),
						to: 'assets/icon.png'
					}]
				),
				// copy any static files
				existsSync(source('assets')) && { from:'assets', to:'assets' }
			].filter(Boolean))
		]
	};
}

function isProd(config) {
	let limit = 200 * 1000; // 200kb

	const prodConfig = {
		performance: Object.assign({
			hints: 'warning',
			maxAssetSize: limit,
			maxEntrypointSize: limit,
		}, config.pkg.performance),

		plugins: [
			new webpack.DefinePlugin({
				'process.env.ADD_SW': config.sw,
				'process.env.ES_BUILD': false,
				'process.env.ESM': config.esm,
			})
		],

		optimization: {
			minimizer: [
				new UglifyJsPlugin({
					cache: true,
					parallel: true,
					uglifyOptions: {
						sourceMap: true,
						output: { comments:false },
						mangle: true,
						compress: {
							properties: true,
							keep_fargs: false,
							pure_getters: true,
							collapse_vars: true,
							warnings: false,
							// screw_ie8: true,
							sequences: true,
							dead_code: true,
							drop_debugger: true,
							comparisons: true,
							conditionals: true,
							evaluate: true,
							booleans: true,
							loops: true,
							unused: true,
							hoist_funs: true,
							if_return: true,
							join_vars: true,
							// cascade: true,
							drop_console: false,
							pure_funcs: [
								'classCallCheck',
								'_classCallCheck',
								'_possibleConstructorReturn',
								'Object.freeze',
								'invariant',
								'warning'
							]
						},
					},
					sourceMap: true,
				}),
				new OptimizeCssAssetsPlugin({}),
			],
		},
	};

	if (config.sw) {
		prodConfig.plugins.push(
			new SWPrecacheWebpackPlugin({
				filename: 'sw.js',
				navigateFallback: 'index.html',
				navigateFallbackWhitelist: [/^(?!\/__).*/],
				minify: true,
				stripPrefix: config.cwd,
				staticFileGlobsIgnorePatterns: [
					/\.esm\.js$/,
					/polyfills(\..*)?\.js$/,
					/\.map$/,
					/push-manifest\.json$/,
					/.DS_Store/,
					/\.git/
				]
			}),
		);
	}

	if (config.esm) {
		prodConfig.plugins.push(
			new BabelEsmPlugin({
				filename: '[name].[chunkhash:5].esm.js',
				chunkFilename: '[name].chunk.[chunkhash:5].esm.js',
				beforeStartExecution: plugins => {
					plugins.forEach(plugin => {
						if (plugin.constructor.name === 'DefinePlugin' && plugin.definitions) {
							for (const definition in plugin.definitions) {
								if (definition === 'process.env.ES_BUILD') {
									plugin.definitions[definition] = true;
								}
							}
						} else if (plugin.constructor.name === 'DefinePlugin' && !plugin.definitions) {
							throw new Error('WebpackDefinePlugin found but not `process.env.ES_BUILD`.');
						}
					});
				},
			}),
		);

		if (config.sw) {
			prodConfig.plugins.push(
				new SWPrecacheWebpackPlugin({
					filename: 'sw-esm.js',
					navigateFallback: 'index.html',
					navigateFallbackWhitelist: [/^(?!\/__).*/],
					minify: true,
					stripPrefix: config.cwd,
					staticFileGlobsIgnorePatterns: [
						/(\.[\w]{5}\.js)/,
						/polyfills(\..*)?\.js$/,
						/\.map$/,
						/push-manifest\.json$/,
						/.DS_Store/,
						/\.git/
					]
				}),
			);
		}
	}

	if (config.analyze) {
		prodConfig.plugins.push(
			new BundleAnalyzerPlugin()
		);
	}

	return prodConfig;
}

function isDev(config) {
	const { cwd, src } = config;

	return {
		plugins: [
			new webpack.NamedModulesPlugin(),
			new webpack.HotModuleReplacementPlugin()
		],

		devServer: {
			inline: true,
			hot: true,
			compress: true,
			publicPath: '/',
			contentBase: src,
			https: config.https,
			port: process.env.PORT || config.port || 8080,
			host: process.env.HOST || config.host || '0.0.0.0',
			// setup(app) {
			// 	app.use(middleware);
			// },
			disableHostCheck: true,
			historyApiFallback: true,
			quiet: true,
			clientLogLevel: 'none',
			overlay: false,
			stats: 'minimal',
			watchOptions: {
				ignored: [
					resolve(cwd, 'build'),
					resolve(cwd, 'node_modules')
				]
			}
		}
	};
}

module.exports = function (env) {
	return merge(
		baseConfig(env),
		clientConfig(env),
		(env.isProd ? isProd : isDev)(env)
	);
};
<|endoftext|>


<commit_before>const webpack = require('webpack');
const { resolve } = require('path');
const { existsSync } = require('fs');
const merge = require('webpack-merge');
const { filter } = require('minimatch');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const RenderHTMLPlugin = require('./render-html-plugin');
const PushManifestPlugin = require('./push-manifest');
const baseConfig = require('./webpack-base-config');
const BabelEsmPlugin = require('babel-esm-plugin');
const { normalizePath } = require('../../util');

const cleanFilename = name => name.replace(/(^\/(routes|components\/(routes|async))\/|(\/index)?\.js$)/g, '');

function clientConfig(env) {
	const { isProd, source, src /*, port? */ } = env;

	let entry = {
		bundle: resolve(__dirname, './../entry'),
		polyfills: resolve(__dirname, './polyfills')
	};

	if (!isProd) {
		entry.bundle = [
			entry.bundle,
			'webpack-dev-server/client',
			'webpack/hot/dev-server'
		];
	}

	return {
		entry: entry,
		output: {
			path: env.dest,
			publicPath: '/',
			filename: isProd ? '[name].[chunkhash:5].js' : '[name].js',
			chunkFilename: '[name].chunk.[chunkhash:5].js'
		},

		resolveLoader: {
			alias: {
				async: resolve(__dirname, './async-component-loader')
			}
		},

		// automatic async components :)
		module: {
			rules: [
				{
					test: /\.jsx?$/,
					include: [
						filter(source('routes')+'/{*.js,*/index.js}'),
						filter(source('components')+'/{routes,async}/{*.js,*/index.js}')
					],
					loader: resolve(__dirname, './async-component-loader'),
					options: {
						name(filename) {
							filename = normalizePath(filename);
							let relative = filename.replace(normalizePath(src), '');
							if (!relative.includes('/routes/')) return false;
							return 'route-' + cleanFilename(relative);
						},
						formatName(filename) {
							filename = normalizePath(filename);
							let relative = filename.replace(normalizePath(source('.')), '');
							return cleanFilename(relative);
						}
					}
				}
			]
		},

		plugins: [
			...RenderHTMLPlugin(env),
			new PushManifestPlugin(),
			new CopyWebpackPlugin([
				...(
					existsSync(source('manifest.json'))
					? [{ from:'manifest.json' }]
					: [{
						from: resolve(__dirname, '../../resources/manifest.json'),
						to: 'manifest.json'
					}, {
						from: resolve(__dirname, '../../resources/icon.png'),
						to: 'assets/icon.png'
					}]
				),
				// copy any static files
				existsSync(source('assets')) && { from:'assets', to:'assets' }
			].filter(Boolean))
		]
	};
}

function isProd(config) {
	let limit = 200 * 1000; // 200kb

	const prodConfig = {
		performance: Object.assign({
			hints: 'warning',
			maxAssetSize: limit,
			maxEntrypointSize: limit,
		}, config.pkg.performance),

		plugins: [
			new webpack.DefinePlugin({
				'process.env.ADD_SW': config.sw,
				'process.env.ESM': config.esm,
				'process.env.ES_BUILD': false,
			})
		],

		optimization: {
			minimizer: [
				new UglifyJsPlugin({
					cache: true,
					parallel: true,
					uglifyOptions: {
						sourceMap: true,
						output: { comments:false },
						mangle: true,
						compress: {
							properties: true,
							keep_fargs: false,
							pure_getters: true,
							collapse_vars: true,
							warnings: false,
							// screw_ie8: true,
							sequences: true,
							dead_code: true,
							drop_debugger: true,
							comparisons: true,
							conditionals: true,
							evaluate: true,
							booleans: true,
							loops: true,
							unused: true,
							hoist_funs: true,
							if_return: true,
							join_vars: true,
							// cascade: true,
							drop_console: false,
							pure_funcs: [
								'classCallCheck',
								'_classCallCheck',
								'_possibleConstructorReturn',
								'Object.freeze',
								'invariant',
								'warning'
							]
						},
					},
					sourceMap: true,
				}),
				new OptimizeCssAssetsPlugin({}),
			],
		},
	};

	if (config.sw) {
		prodConfig.plugins.push(
			new SWPrecacheWebpackPlugin({
				filename: 'sw.js',
				navigateFallback: 'index.html',
				navigateFallbackWhitelist: [/^(?!\/__).*/],
				minify: true,
				stripPrefix: config.cwd,
				staticFileGlobsIgnorePatterns: [
					/\.esm\.js$/,
					/polyfills(\..*)?\.js$/,
					/\.map$/,
					/push-manifest\.json$/,
					/.DS_Store/,
					/\.git/
				]
			}),
		);
	}

	if (config.esm) {
		prodConfig.plugins.push(
			new BabelEsmPlugin({
				filename: '[name].[chunkhash:5].esm.js',
				chunkFilename: '[name].chunk.[chunkhash:5].esm.js',
				beforeStartExecution: plugins => {
					plugins.forEach(plugin => {
						if (plugin.constructor.name === 'DefinePlugin' && plugin.definitions) {
							for (const definition in plugin.definitions) {
								if (definition === 'process.env.ES_BUILD') {
									plugin.definitions[definition] = true;
								}
							}
						} else if (plugin.constructor.name === 'DefinePlugin' && !plugin.definitions) {
							throw new Error('WebpackDefinePlugin found but not `process.env.ES_BUILD`.');
						}
					})
				},
			}),
		);
		if (config.sw) {
			prodConfig.plugins.push(
				new SWPrecacheWebpackPlugin({
					filename: 'sw-esm.js',
					navigateFallback: 'index.html',
					navigateFallbackWhitelist: [/^(?!\/__).*/],
					minify: true,
					stripPrefix: config.cwd,
					staticFileGlobsIgnorePatterns: [
						/(\.[\w]{5}\.js)/,
						/polyfills(\..*)?\.js$/,
						/\.map$/,
						/push-manifest\.json$/,
						/.DS_Store/,
						/\.git/
					]
				}),
			);
		}
	}

	if (config.analyze) {
		prodConfig.plugins.push(
			new BundleAnalyzerPlugin()
		);
	}

	return prodConfig;
}

function isDev(config) {
	const { cwd, src } = config;

	return {
		plugins: [
			new webpack.NamedModulesPlugin(),
			new webpack.HotModuleReplacementPlugin()
		],

		devServer: {
			inline: true,
			hot: true,
			compress: true,
			publicPath: '/',
			contentBase: src,
			https: config.https,
			port: process.env.PORT || config.port || 8080,
			host: process.env.HOST || config.host || '0.0.0.0',
			// setup(app) {
			// 	app.use(middleware);
			// },
			disableHostCheck: true,
			historyApiFallback: true,
			quiet: true,
			clientLogLevel: 'none',
			overlay: false,
			stats: 'minimal',
			watchOptions: {
				ignored: [
					resolve(cwd, 'build'),
					resolve(cwd, 'node_modules')
				]
			}
		}
	};
}

module.exports = function (env) {
	return merge(
		baseConfig(env),
		clientConfig(env),
		(env.isProd ? isProd : isDev)(env)
	);
};
<commit_msg>Update webpack-client-config.js<commit_after>const webpack = require('webpack');
const { resolve } = require('path');
const { existsSync } = require('fs');
const merge = require('webpack-merge');
const { filter } = require('minimatch');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const RenderHTMLPlugin = require('./render-html-plugin');
const PushManifestPlugin = require('./push-manifest');
const baseConfig = require('./webpack-base-config');
const BabelEsmPlugin = require('babel-esm-plugin');
const { normalizePath } = require('../../util');

const cleanFilename = name => name.replace(/(^\/(routes|components\/(routes|async))\/|(\/index)?\.js$)/g, '');

function clientConfig(env) {
	const { isProd, source, src /*, port? */ } = env;

	let entry = {
		bundle: resolve(__dirname, './../entry'),
		polyfills: resolve(__dirname, './polyfills')
	};

	if (!isProd) {
		entry.bundle = [
			entry.bundle,
			'webpack-dev-server/client',
			'webpack/hot/dev-server'
		];
	}

	return {
		entry: entry,
		output: {
			path: env.dest,
			publicPath: '/',
			filename: isProd ? '[name].[chunkhash:5].js' : '[name].js',
			chunkFilename: '[name].chunk.[chunkhash:5].js'
		},

		resolveLoader: {
			alias: {
				async: resolve(__dirname, './async-component-loader')
			}
		},

		// automatic async components :)
		module: {
			rules: [
				{
					test: /\.jsx?$/,
					include: [
						filter(source('routes')+'/{*.js,*/index.js}'),
						filter(source('components')+'/{routes,async}/{*.js,*/index.js}')
					],
					loader: resolve(__dirname, './async-component-loader'),
					options: {
						name(filename) {
							filename = normalizePath(filename);
							let relative = filename.replace(normalizePath(src), '');
							if (!relative.includes('/routes/')) return false;
							return 'route-' + cleanFilename(relative);
						},
						formatName(filename) {
							filename = normalizePath(filename);
							let relative = filename.replace(normalizePath(source('.')), '');
							return cleanFilename(relative);
						}
					}
				}
			]
		},

		plugins: [
			...RenderHTMLPlugin(env),
			new PushManifestPlugin(),
			new CopyWebpackPlugin([
				...(
					existsSync(source('manifest.json'))
					? [{ from:'manifest.json' }]
					: [{
						from: resolve(__dirname, '../../resources/manifest.json'),
						to: 'manifest.json'
					}, {
						from: resolve(__dirname, '../../resources/icon.png'),
						to: 'assets/icon.png'
					}]
				),
				// copy any static files
				existsSync(source('assets')) && { from:'assets', to:'assets' }
			].filter(Boolean))
		]
	};
}

function isProd(config) {
	let limit = 200 * 1000; // 200kb

	const prodConfig = {
		performance: Object.assign({
			hints: 'warning',
			maxAssetSize: limit,
			maxEntrypointSize: limit,
		}, config.pkg.performance),

		plugins: [
			new webpack.DefinePlugin({
				'process.env.ADD_SW': config.sw,
				'process.env.ESM': config.esm,
				'process.env.ES_BUILD': false,
			})
		],

		optimization: {
			minimizer: [
				new UglifyJsPlugin({
					cache: true,
					parallel: true,
					uglifyOptions: {
						sourceMap: true,
						output: { comments:false },
						mangle: true,
						compress: {
							properties: true,
							keep_fargs: false,
							pure_getters: true,
							collapse_vars: true,
							warnings: false,
							// screw_ie8: true,
							sequences: true,
							dead_code: true,
							drop_debugger: true,
							comparisons: true,
							conditionals: true,
							evaluate: true,
							booleans: true,
							loops: true,
							unused: true,
							hoist_funs: true,
							if_return: true,
							join_vars: true,
							// cascade: true,
							drop_console: false,
							pure_funcs: [
								'classCallCheck',
								'_classCallCheck',
								'_possibleConstructorReturn',
								'Object.freeze',
								'invariant',
								'warning'
							]
						},
					},
					sourceMap: true,
				}),
				new OptimizeCssAssetsPlugin({}),
			],
		},
	};

	if (config.sw) {
		prodConfig.plugins.push(
			new SWPrecacheWebpackPlugin({
				filename: 'sw.js',
				navigateFallback: 'index.html',
				navigateFallbackWhitelist: [/^(?!\/__).*/],
				minify: true,
				stripPrefix: config.cwd,
				staticFileGlobsIgnorePatterns: [
					/\.esm\.js$/,
					/polyfills(\..*)?\.js$/,
					/\.map$/,
					/push-manifest\.json$/,
					/.DS_Store/,
					/\.git/
				]
			}),
		);
	}

	if (config.esm) {
		prodConfig.plugins.push(
			new BabelEsmPlugin({
				filename: '[name].[chunkhash:5].esm.js',
				chunkFilename: '[name].chunk.[chunkhash:5].esm.js',
				beforeStartExecution: plugins => {
					plugins.forEach(plugin => {
						if (plugin.constructor.name === 'DefinePlugin' && plugin.definitions) {
							for (const definition in plugin.definitions) {
								if (definition === 'process.env.ES_BUILD') {
									plugin.definitions[definition] = true;
								}
							}
						} else if (plugin.constructor.name === 'DefinePlugin' && !plugin.definitions) {
							throw new Error('WebpackDefinePlugin found but not `process.env.ES_BUILD`.');
						}
					});
				},
			}),
		);
		if (config.sw) {
			prodConfig.plugins.push(
				new SWPrecacheWebpackPlugin({
					filename: 'sw-esm.js',
					navigateFallback: 'index.html',
					navigateFallbackWhitelist: [/^(?!\/__).*/],
					minify: true,
					stripPrefix: config.cwd,
					staticFileGlobsIgnorePatterns: [
						/(\.[\w]{5}\.js)/,
						/polyfills(\..*)?\.js$/,
						/\.map$/,
						/push-manifest\.json$/,
						/.DS_Store/,
						/\.git/
					]
				}),
			);
		}
	}

	if (config.analyze) {
		prodConfig.plugins.push(
			new BundleAnalyzerPlugin()
		);
	}

	return prodConfig;
}

function isDev(config) {
	const { cwd, src } = config;

	return {
		plugins: [
			new webpack.NamedModulesPlugin(),
			new webpack.HotModuleReplacementPlugin()
		],

		devServer: {
			inline: true,
			hot: true,
			compress: true,
			publicPath: '/',
			contentBase: src,
			https: config.https,
			port: process.env.PORT || config.port || 8080,
			host: process.env.HOST || config.host || '0.0.0.0',
			// setup(app) {
			// 	app.use(middleware);
			// },
			disableHostCheck: true,
			historyApiFallback: true,
			quiet: true,
			clientLogLevel: 'none',
			overlay: false,
			stats: 'minimal',
			watchOptions: {
				ignored: [
					resolve(cwd, 'build'),
					resolve(cwd, 'node_modules')
				]
			}
		}
	};
}

module.exports = function (env) {
	return merge(
		baseConfig(env),
		clientConfig(env),
		(env.isProd ? isProd : isDev)(env)
	);
};
<|endoftext|>


<commit_before>var path    = require( 'path' )
  , Promise = require( 'bluebird' )
  , async   = require( 'async' )
  , semver  = require( 'semver' )
  , _       = require( 'lodash' )
  , request = require( 'request' )
  , utils   = require( path.join( __dirname, 'utils' ) );

var _keywordRequired = ['cleverstack module', 'cleverstack-module', 'cleverstack seed', 'cleverstack-seed'];

function searchNPMRegistry ( queries ) {
  var def = Promise.defer( )
    , repos = [];

  async.each( queries, function ( q, next ) {
    var search = q.split( '@' );
    if (typeof search[1] === "undefined") {
      search[1] = '*';
    }

    request( {
      url: 'http://registry.npmjs.org/' + encodeURIComponent( search[ 0 ] ),
      json: true
    },
    function ( err, response, res ) {
      if (!!err || (res.error && res.error === "not_found")) {
        return next( );
      }

      var versions = Object.keys( res.versions );
      var maxVersion = semver.maxSatisfying( versions, search[1] );

      var pkg = res.versions[maxVersion];
      if (typeof pkg === "undefined") {
        return next( );
      }

      if (_.intersection( pkg.keywords, _keywordRequired ).length > 0) {
        if (typeof pkg.repository === "object" && pkg.repository.hasOwnProperty( 'url' )) {
          pkg.homepage = pkg.repository.url;
        }

        repos.push( pkg );
      }

      next( );
    } );
  }, function ( err ) {
    if (!!err) {
      return def.reject( err );
    }

    def.resolve( repos );
  } );

  return def.promise;
}

var list = exports.list = function ( ) {
  var def = Promise.defer( );

  request.get( {
    url: 'https://registry.npmjs.org/-/_view/byKeyword?startkey=["cleverstack"]&endkey=["cleverstack",{}]&group_level=2',
    json: true
  },
  function ( err, res, body ) {
    var rows = body.rows.map( function ( r ) {
      return r.key[1];
    } );

    searchNPMRegistry( rows ).then( function ( response ) {
      def.resolve( response );
    } )
    .catch( function ( err ) {
      def.reject( err );
    } );
  } );

  return def.promise;
}

var searchNPM = exports.npm = function ( queries ) {
  var def   = Promise.defer( );

  utils.info( 'Searching through NPM packages...' );

  if (queries.length < 1) {
    list( ).then( function ( res ) {
      def.resolve( res )
    } )
    .catch( function ( err ) {
      def.reject( err );
    } );
  } else {
    searchNPMRegistry( queries ).then( function ( res ) {
      def.resolve( res );
    } )
    .catch( function ( err ) {
      def.reject( err );
    } );
  }

  return def.promise;
}

function listBower( ) {
  var def   = Promise.defer( )
    , repos = [ ];

  utils.info( 'Searching through Bower packages' );

  request( {
    url: 'https://bower-component-list.herokuapp.com/keyword/cleverstack',
    json: true
  },
  function ( err, res, body ) {
    async.each( body, function ( module, next ) {
      request( {
        url: 'https://bower.herokuapp.com/packages/' + encodeURIComponent( module.name ),
        json: true
      },
      function ( e, response, bower ) {
        if (!bower || !bower.url) {
          return next( );
        }

        module.url = bower.url;
        repos.push( module );
        next( );
      } );
    }, function ( ) {
      def.resolve( repos );
    } );
  } );

  return def.promise;
}

var searchBower = exports.bower = function ( query ) {
  var def = Promise.defer( );

  utils.info( 'Searching through Bower packages for ' + query + '...' );

  request( {
    url: 'https://bower.herokuapp.com/packages/' + encodeURIComponent( query ),
    json: true
  },
  function ( e, response, bower ) {
    if (!!e) {
      return def.reject( e );
    }

    def.resolve( bower );
  } );

  return def.promise;
}

var aggregate = exports.aggregate = function ( args ) {
  var def = Promise.defer( )
    , all = [];

  all.push( searchNPM( args ) );
  all.push( listBower( ) );

  args.forEach( function ( arg ) {
    all.push( searchBower( arg ) );
  } );

  Promise.all( all ).then( function ( results ) {
    var npm   = results[0].map( function ( r ) { r.type = 'backend'; return r; } )
      , keys  = results[1]
      , repos = [];

    results.shift( );
    results.shift( );

    ( args.length < 1 ? results[0] : results ).filter( function ( res ) {
      return typeof res === "object" && res.hasOwnProperty( 'url' );
    } ).forEach( function ( rep )  {
      var cleverStackBower = _.find( keys, function ( key ) {
        return key.name === rep.name;
      } );

      if (cleverStackBower) {
        rep = _.merge( cleverStackBower, rep );
        rep.type = 'frontend';
        repos.push( rep );
      }
    } );

    def.resolve( [ npm, repos ] );
  } )
  .catch( function ( err ) {
    def.reject( err );
  } );

  return def.promise;
}
<commit_msg>fix(search): Fixes search for list.<commit_after>var path    = require( 'path' )
  , Promise = require( 'bluebird' )
  , async   = require( 'async' )
  , semver  = require( 'semver' )
  , _       = require( 'lodash' )
  , request = require( 'request' )
  , utils   = require( path.join( __dirname, 'utils' ) );

var _keywordRequired = ['cleverstack module', 'cleverstack-module', 'cleverstack seed', 'cleverstack-seed'];

function searchNPMRegistry ( queries ) {
  var def = Promise.defer( )
    , repos = [];

  async.each( queries, function ( q, next ) {
    var search = q.split( '@' );
    if (typeof search[1] === "undefined") {
      search[1] = '*';
    }

    request( {
      url: 'http://registry.npmjs.org/' + encodeURIComponent( search[ 0 ] ),
      json: true
    },
    function ( err, response, res ) {
      if (!!err || (res.error && res.error === "not_found")) {
        return next( );
      }

      var versions = Object.keys( res.versions );
      var maxVersion = semver.maxSatisfying( versions, search[1] );

      var pkg = res.versions[maxVersion];
      if (typeof pkg === "undefined") {
        return next( );
      }

      if (_.intersection( pkg.keywords, _keywordRequired ).length > 0) {
        if (typeof pkg.repository === "object" && pkg.repository.hasOwnProperty( 'url' )) {
          pkg.homepage = pkg.repository.url;
        }

        repos.push( pkg );
      }

      next( );
    } );
  }, function ( err ) {
    if (!!err) {
      return def.reject( err );
    }

    def.resolve( repos );
  } );

  return def.promise;
}

var list = exports.list = function ( ) {
  var def = Promise.defer( );

  request.get( {
    url: 'https://registry.npmjs.org/-/_view/byKeyword?startkey=["cleverstack"]&endkey=["cleverstack",{}]&group_level=2',
    json: true
  },
  function ( err, res, body ) {
    var rows = body.rows.map( function ( r ) {
      return r.key[1];
    } );

    searchNPMRegistry( rows ).then( function ( response ) {
      def.resolve( response );
    } )
    .catch( function ( err ) {
      def.reject( err );
    } );
  } );

  return def.promise;
}

var searchNPM = exports.npm = function ( queries ) {
  var def   = Promise.defer( );

  utils.info( 'Searching through NPM packages...' );

  if (queries.length < 1) {
    list( ).then( function ( res ) {
      def.resolve( res )
    } )
    .catch( function ( err ) {
      def.reject( err );
    } );
  } else {
    searchNPMRegistry( queries ).then( function ( res ) {
      def.resolve( res );
    } )
    .catch( function ( err ) {
      def.reject( err );
    } );
  }

  return def.promise;
}

function listBower( ) {
  var def   = Promise.defer( )
    , repos = [ ];

  utils.info( 'Searching through Bower packages' );

  request( {
    url: 'https://bower-component-list.herokuapp.com/keyword/cleverstack',
    json: true
  },
  function ( err, res, body ) {
    async.each( body, function ( module, next ) {
      request( {
        url: 'https://bower.herokuapp.com/packages/' + encodeURIComponent( module.name ),
        json: true
      },
      function ( e, response, bower ) {
        if (!bower || !bower.url) {
          return next( );
        }

        module.url = bower.url;
        repos.push( module );
        next( );
      } );
    }, function ( ) {
      def.resolve( repos );
    } );
  } );

  return def.promise;
}

var searchBower = exports.bower = function ( query ) {
  var def = Promise.defer( );

  utils.info( 'Searching through Bower packages for ' + query + '...' );

  request( {
    url: 'https://bower.herokuapp.com/packages/' + encodeURIComponent( query ),
    json: true
  },
  function ( e, response, bower ) {
    if (!!e) {
      return def.reject( e );
    }

    def.resolve( bower );
  } );

  return def.promise;
}

var aggregate = exports.aggregate = function ( args ) {
  var def = Promise.defer( )
    , all = [];

  all.push( searchNPM( args ) );
  all.push( listBower( ) );

  args.forEach( function ( arg ) {
    all.push( searchBower( arg ) );
  } );

  Promise.all( all ).then( function ( results ) {
    var npm   = results[0].map( function ( r ) { r.type = 'backend'; return r; } )
      , keys  = results[1]
      , repos = [];

    results.shift( );
    results.shift( );

    var bower = ( args.length < 1 ? keys : results );
    if (!Array.isArray( bower )) {
      bower = [ ];
    }

    bower.filter( function ( res ) {
      return typeof res === "object" && res.hasOwnProperty( 'url' );
    } ).forEach( function ( rep )  {
      var cleverStackBower = _.find( keys, function ( key ) {
        return key.name === rep.name;
      } );

      if (cleverStackBower) {
        rep = _.merge( cleverStackBower, rep );
        rep.type = 'frontend';
        repos.push( rep );
      }
    } );

    def.resolve( [ npm, repos ] );
  } )
  .catch( function ( err ) {
    def.reject( err );
  } );

  return def.promise;
}
<|endoftext|>