<commit_before>/*!
 * A grunt checker for the 'banana' format JSON i18n message files.
 */

/*jshint node:true */
module.exports = function ( grunt ) {
	grunt.registerMultiTask( 'banana', function () {
		var path = require( 'path' ),
			options = this.options( {
				sourceFile: 'en.json',
				documentationFile: 'qqq.json'
			} ),
			messageCount = 0,
			ok = true;

		this.filesSrc.forEach( function ( dir ) {
			var documentationMessagesMetadataIndex,
				sourceMessagesMetadataIndex,
				message,
				documentationIndex,
				documentationMessages = grunt.file.readJSON( path.resolve( dir, options.documentationFile ) ),
				documentationMessageKeys = Object.keys( documentationMessages ),
				documentationMessageBlanks = [],
				sourceMessages = grunt.file.readJSON( path.resolve( dir, options.sourceFile ) ),
				sourceMessageKeys = Object.keys( sourceMessages ),
				sourceIndex = 0,
				count = 0;

			messageCount += sourceMessageKeys.length;

			sourceMessagesMetadataIndex = sourceMessageKeys.indexOf( '@metadata' );
			if ( sourceMessagesMetadataIndex === -1 ) {
				grunt.log.error( 'Source file lacks a metadata block.' );
				ok = false;
				return;
			}
			sourceMessageKeys.splice( sourceMessagesMetadataIndex, 1 );

			documentationMessagesMetadataIndex = documentationMessageKeys.indexOf( '@metadata' );
			if ( documentationMessagesMetadataIndex === -1 ) {
				grunt.log.error( 'Documentation file lacks a metadata block.' );
				ok = false;
				return;
			}
			documentationMessageKeys.splice( documentationMessagesMetadataIndex, 1 );

			while ( sourceMessageKeys.length > 0 ) {
				message = sourceMessageKeys[0];
				documentationIndex = documentationMessageKeys.indexOf( message );

				if ( documentationIndex !== -1 ) {

					if ( documentationMessages[message].trim() === '' ) {
						documentationMessageBlanks.push( message );
					}

					documentationMessageKeys.splice( documentationIndex, 1 );
				}
				sourceMessageKeys.splice( sourceIndex, 1 );
			}

			count = sourceMessageKeys.length;
			if ( count > 0 ) {
				ok = false;

				grunt.log.error(
					count + ' message' + ( count > 1 ? 's lack' : ' lacks' ) + ' documentation.'
				);

				sourceMessageKeys.forEach( function ( message ) {
					grunt.log.error( 'Message "' + message + '" lacks documentation.' );
				} );
			}

			count = documentationMessageBlanks.length;
			if ( count > 0 ) {
				ok = false;

				grunt.log.error(
					count + ' documented message' + ( count > 1 ? 's are' : ' is' ) + ' blank.'
				);

				documentationMessageBlanks.forEach( function ( message ) {
					grunt.log.error( 'Message "' + message + '" is documented with a blank string.' );
				} );
			}

			count = documentationMessageKeys.length;
			if ( count > 0 ) {
				ok = false;

				grunt.log.error(
					count + ' documented message' + ( count > 1 ? 's are' : ' is' ) + ' undefined.'
				);

				documentationMessageKeys.forEach( function ( message ) {
					grunt.log.error( 'Message "' + message + '" is documented but undefined.' );
				} );
			}
		} );

		if ( !ok ) {
			return false;
		}

		grunt.log.ok( messageCount + ' message' + ( messageCount > 1 ? 's' : '' ) + ' checked.' );
	} );
};
<commit_msg>Fix catastrophic logic error<commit_after>/*!
 * A grunt checker for the 'banana' format JSON i18n message files.
 */

/*jshint node:true */
module.exports = function ( grunt ) {
	grunt.registerMultiTask( 'banana', function () {
		var path = require( 'path' ),
			options = this.options( {
				sourceFile: 'en.json',
				documentationFile: 'qqq.json'
			} ),
			messageCount = 0,
			ok = true;

		this.filesSrc.forEach( function ( dir ) {
			var documentationMessagesMetadataIndex,
				sourceMessagesMetadataIndex,
				message,
				documentationIndex,
				documentationMessages = grunt.file.readJSON( path.resolve( dir, options.documentationFile ) ),
				documentationMessageKeys = Object.keys( documentationMessages ),
				documentationMessageBlanks = [],
				sourceMessageMissing = [],
				sourceMessages = grunt.file.readJSON( path.resolve( dir, options.sourceFile ) ),
				sourceMessageKeys = Object.keys( sourceMessages ),
				sourceIndex = 0,
				count = 0;

			messageCount += sourceMessageKeys.length;

			sourceMessagesMetadataIndex = sourceMessageKeys.indexOf( '@metadata' );
			if ( sourceMessagesMetadataIndex === -1 ) {
				grunt.log.error( 'Source file lacks a metadata block.' );
				ok = false;
				return;
			}
			sourceMessageKeys.splice( sourceMessagesMetadataIndex, 1 );

			documentationMessagesMetadataIndex = documentationMessageKeys.indexOf( '@metadata' );
			if ( documentationMessagesMetadataIndex === -1 ) {
				grunt.log.error( 'Documentation file lacks a metadata block.' );
				ok = false;
				return;
			}
			documentationMessageKeys.splice( documentationMessagesMetadataIndex, 1 );

			while ( sourceMessageKeys.length > 0 ) {
				message = sourceMessageKeys[0];
				documentationIndex = documentationMessageKeys.indexOf( message );

				if ( documentationIndex !== -1 ) {

					if ( documentationMessages[message].trim() === '' ) {
						documentationMessageBlanks.push( message );
					}

					documentationMessageKeys.splice( documentationIndex, 1 );
				} else {
					sourceMessageMissing.push( message );
				}
				sourceMessageKeys.splice( sourceIndex, 1 );
			}

			count = sourceMessageMissing.length;
			if ( count > 0 ) {
				ok = false;

				grunt.log.error(
					count + ' message' + ( count > 1 ? 's lack' : ' lacks' ) + ' documentation.'
				);

				sourceMessageMissing.forEach( function ( message ) {
					grunt.log.error( 'Message "' + message + '" lacks documentation.' );
				} );
			}

			count = documentationMessageBlanks.length;
			if ( count > 0 ) {
				ok = false;

				grunt.log.error(
					count + ' documented message' + ( count > 1 ? 's are' : ' is' ) + ' blank.'
				);

				documentationMessageBlanks.forEach( function ( message ) {
					grunt.log.error( 'Message "' + message + '" is documented with a blank string.' );
				} );
			}

			count = documentationMessageKeys.length;
			if ( count > 0 ) {
				ok = false;

				grunt.log.error(
					count + ' documented message' + ( count > 1 ? 's are' : ' is' ) + ' undefined.'
				);

				documentationMessageKeys.forEach( function ( message ) {
					grunt.log.error( 'Message "' + message + '" is documented but undefined.' );
				} );
			}
		} );

		if ( !ok ) {
			return false;
		}

		grunt.log.ok( messageCount + ' message' + ( messageCount > 1 ? 's' : '' ) + ' checked.' );
	} );
};
<|endoftext|>


<commit_before>/*!
 * A Grunt checker for the 'banana' format JSON i18n message files.
 */

/*jshint node:true */
module.exports = function ( grunt ) {
	grunt.registerMultiTask( 'banana', function () {
		var ok,
			path = require( 'path' ),
			fs = require( 'fs' ),
			options = this.options( {
				sourceFile: 'en.json',
				documentationFile: 'qqq.json',

				requireMetadata: true,

				requireCompleteMessageDocumentation: true,
				disallowUnusedDocumentation: true,

				disallowBlankTranslations: true,
				disallowDuplicateTranslations: false,
				disallowUnusedTranslations: false,

				requireCompleteTranslationLanguages: [],
				requireCompleteTranslationMessages: []
			} ),
			messageCount = 0;

		if ( this.filesSrc.length === 0 ) {
			grunt.log.error( 'Target directory does not exist.' );
			return false;
		}

		ok = true;

		this.filesSrc.forEach( function ( dir ) {
			var message, index, offset,
				// Source message data
				sourceMessages, sourceMessageKeys,
				// Documentation message data
				documentationMessages, documentationMessageKeys,
				// Translated message data
				translatedFiles,
				translatedData = {},
				documentationMessageBlanks = [],
				sourceMessageMissing = [],
				count = 0;

			function messages( filename, type ) {
				var messageArray;

				try {
					messageArray = grunt.file.readJSON( path.resolve( dir, filename ) );
				} catch ( e ) {
					grunt.log.error( 'Loading ' + type + ' messages failed: "' + e + '".' );
					ok = false;
					throw e;
				}

				return messageArray;
			}
			function keysNoMetadata( messageArray, type ) {
				var keys, offset;

				try {
					keys = Object.keys( messageArray );
				} catch ( e ) {
					grunt.log.error( 'Loading ' + type + ' messages failed: "' + e + '".' );
					ok = false;
					throw e;
				}

				offset = keys.indexOf( '@metadata' );
				if ( offset === -1 ) {
					if ( options.requireMetadata ) {
						grunt.log.error( 'No metadata block in the ' + type + ' messages file.' );
						ok = false;
					}
				} else {
					keys.splice( offset, 1 );
				}

				return keys;
			}
			sourceMessages = messages( options.sourceFile, 'source' );
			sourceMessageKeys = keysNoMetadata( sourceMessages, 'source' );

			documentationMessages = messages( options.documentationFile, 'documentation' );
			documentationMessageKeys = keysNoMetadata( documentationMessages, 'documentation' );

			// Count after @metadata is removed
			messageCount += sourceMessageKeys.length;

			translatedFiles = fs.readdirSync( dir ).filter( function ( value ) {
				return (
					value !== options.sourceFile &&
					value !== options.documentationFile &&
					value.match( /.*\.json$/ )
				);
			} );

			translatedFiles.forEach( function ( languageFile ) {
				var language = languageFile.match( /(.*)\.json$/ )[ 1 ],
					languageMesages = messages( languageFile, language ),
					keys = keysNoMetadata( languageMesages, language ),
					blanks = [],
					duplicates = [],
					unuseds = [],
					missing = sourceMessageKeys.slice( 0 );

				for ( index in keys ) {
					message = keys[ index ];

					if ( missing.indexOf( message ) !== -1 ) {
						if ( languageMesages[ message ] === sourceMessages[ message ] ) {
							duplicates.push( message );
						}
						missing.splice( missing.indexOf( message ), 1 );
					} else {
						unuseds.push( message );
					}

					if ( typeof languageMesages[ message ] !== 'string' ) {
						continue;
					}
					if ( languageMesages[ message ].trim() === '' ) {
						blanks.push( message );
					}
				}

				translatedData[ language ] = {
					messages: languageMesages,
					keys: keys,
					blank: blanks,
					duplicate: duplicates,
					unused: unuseds,
					missing: missing
				};
			} );

			while ( sourceMessageKeys.length > 0 ) {
				message = sourceMessageKeys[ 0 ];

				offset = documentationMessageKeys.indexOf( message );

				if ( offset !== -1 ) {

					if ( documentationMessages[ message ].trim() === '' ) {
						documentationMessageBlanks.push( message );
					}

					documentationMessageKeys.splice( offset, 1 );
				} else {
					sourceMessageMissing.push( message );
				}
				sourceMessageKeys.splice( 0, 1 );
			}

			if ( options.requireCompleteMessageDocumentation ) {
				count = sourceMessageMissing.length;
				if ( count > 0 ) {
					ok = false;

					grunt.log.error(
						count + ' message' + ( count > 1 ? 's lack' : ' lacks' ) + ' documentation.'
					);

					sourceMessageMissing.forEach( function ( message ) {
						grunt.log.error( 'Message "' + message + '" lacks documentation.' );
					} );
				}
			}

			if ( options.disallowEmptyDocumentation ) {
				count = documentationMessageBlanks.length;
				if ( count > 0 ) {
					ok = false;

					grunt.log.error(
						count + ' documented message' + ( count > 1 ? 's are' : ' is' ) + ' blank.'
					);

					documentationMessageBlanks.forEach( function ( message ) {
						grunt.log.error( 'Message "' + message + '" is documented with a blank string.' );
					} );
				}
			}

			if ( options.disallowUnusedDocumentation ) {
				count = documentationMessageKeys.length;
				if ( count > 0 ) {
					ok = false;

					grunt.log.error(
						count + ' documented message' + ( count > 1 ? 's are' : ' is' ) + ' undefined.'
					);

					documentationMessageKeys.forEach( function ( message ) {
						grunt.log.error( 'Message "' + message + '" is documented but undefined.' );
					} );
				}
			}

			for ( index in translatedData ) {
				if ( !translatedData.hasOwnProperty( index ) ) {
					continue;
				}

				if ( options.disallowBlankTranslations ) {
					count = translatedData[ index ].blank.length;
					if ( count > 0 ) {
						ok = false;
						grunt.log.error( 'The "' + index + '" translation has ' + count + ' blank translation' + ( count > 1 ? 's' : '' ) + ':' );

						// jshint -W083
						translatedData[ index ].blank.forEach( function ( message ) {
							grunt.log.error( 'The translation of "' + message + '" is blank.' );
						} );
						// jshint +W083
					}
				}

				if ( options.disallowDuplicateTranslations ) {
					count = translatedData[ index ].duplicate.length;
					if ( count > 0 ) {
						ok = false;
						grunt.log.error( 'The "' + index + '" translation has ' + count + ' duplicate translation' + ( count > 1 ? 's' : '' ) + ':' );

						// jshint -W083
						translatedData[ index ].duplicate.forEach( function ( message ) {
							grunt.log.error( 'The translation of "' + message + '" is a duplicate of the primary message.' );
						} );
						// jshint +W083
					}
				}

				if ( options.disallowUnusedTranslations ) {
					count = translatedData[ index ].unused.length;
					if ( count > 0 ) {
						ok = false;
						grunt.log.error( 'The "' + index + '" translation has ' + count + ' unused translation' + ( count > 1 ? 's' : '' ) + ':' );

						// jshint -W083
						translatedData[ index ].unused.forEach( function ( message ) {
							grunt.log.error( 'The translation of "' + message + '" is unused.' );
						} );
						// jshint +W083
					}
				}

			}

			if ( options.requireCompleteTranslationLanguages.length ) {
				for ( index in translatedData ) {
					if (
						!translatedData.hasOwnProperty( index ) ||
						( options.requireCompleteTranslationLanguages.indexOf( index ) === -1 )
					) {
						continue;
					}

					count = translatedData[ index ].missing.length;
					if ( count > 0 ) {
						ok = false;
						grunt.log.error( 'The "' + index + '" translation has ' + count + ' missing translation' + ( count > 1 ? 's' : '' ) + ':' );

						// jshint -W083
						translatedData[ index ].missing.forEach( function ( message ) {
							grunt.log.error( 'The translation of "' + message + '" is missing.' );
						} );
						// jshint +W083
					}
				}
			}

			if ( options.requireCompleteTranslationMessages.length ) {
				for ( index in translatedData ) {
					if ( !translatedData.hasOwnProperty( index ) ) {
						continue;
					}

					for ( message in translatedData[ index ].missing ) {
						if ( !translatedData[ index ].missing.hasOwnProperty( sourceMessageKeys[ message ] ) ) {
							continue;
						}

						offset = options.requireCompleteTranslationMessages.indexOf( sourceMessageKeys[ message ] );

						if ( offset === -1 ) {
							translatedData[ index ].missing.splice( offset, 1 );
						}
					}

					count = translatedData[ index ].missing.length;
					if ( count > 0 ) {
						ok = false;
						grunt.log.error( 'The "' + index + '" translation is missing ' + count + ' required message' + ( count > 1 ? 's' : '' ) + ':' );

						// jshint -W083
						translatedData[ index ].missing.forEach( function ( message ) {
							grunt.log.error( 'The required message "' + message + '" is missing.' );
						} );
						// jshint +W083
					}
				}

			}

		} );

		if ( !ok ) {
			return false;
		}

		grunt.log.ok( messageCount + ' message' + ( messageCount > 1 ? 's' : '' ) + ' checked.' );
	} );
};
<commit_msg>Extract the regex for a JSON filename<commit_after>/*!
 * A Grunt checker for the 'banana' format JSON i18n message files.
 */

/*jshint node:true */
module.exports = function ( grunt ) {
	grunt.registerMultiTask( 'banana', function () {
		var ok,
			path = require( 'path' ),
			fs = require( 'fs' ),
			options = this.options( {
				sourceFile: 'en.json',
				documentationFile: 'qqq.json',

				requireMetadata: true,

				requireCompleteMessageDocumentation: true,
				disallowUnusedDocumentation: true,

				disallowBlankTranslations: true,
				disallowDuplicateTranslations: false,
				disallowUnusedTranslations: false,

				requireCompleteTranslationLanguages: [],
				requireCompleteTranslationMessages: []
			} ),
			messageCount = 0;

		if ( this.filesSrc.length === 0 ) {
			grunt.log.error( 'Target directory does not exist.' );
			return false;
		}

		ok = true;

		this.filesSrc.forEach( function ( dir ) {
			var message, index, offset,
				// Source message data
				sourceMessages, sourceMessageKeys,
				// Documentation message data
				documentationMessages, documentationMessageKeys,
				// Translated message data
				translatedFiles,
				jsonFilenameRegex = /(.*)\.json$/,
				translatedData = {},
				documentationMessageBlanks = [],
				sourceMessageMissing = [],
				count = 0;

			function messages( filename, type ) {
				var messageArray;

				try {
					messageArray = grunt.file.readJSON( path.resolve( dir, filename ) );
				} catch ( e ) {
					grunt.log.error( 'Loading ' + type + ' messages failed: "' + e + '".' );
					ok = false;
					throw e;
				}

				return messageArray;
			}
			function keysNoMetadata( messageArray, type ) {
				var keys, offset;

				try {
					keys = Object.keys( messageArray );
				} catch ( e ) {
					grunt.log.error( 'Loading ' + type + ' messages failed: "' + e + '".' );
					ok = false;
					throw e;
				}

				offset = keys.indexOf( '@metadata' );
				if ( offset === -1 ) {
					if ( options.requireMetadata ) {
						grunt.log.error( 'No metadata block in the ' + type + ' messages file.' );
						ok = false;
					}
				} else {
					keys.splice( offset, 1 );
				}

				return keys;
			}
			sourceMessages = messages( options.sourceFile, 'source' );
			sourceMessageKeys = keysNoMetadata( sourceMessages, 'source' );

			documentationMessages = messages( options.documentationFile, 'documentation' );
			documentationMessageKeys = keysNoMetadata( documentationMessages, 'documentation' );

			// Count after @metadata is removed
			messageCount += sourceMessageKeys.length;

			translatedFiles = fs.readdirSync( dir ).filter( function ( value ) {
				return (
					value !== options.sourceFile &&
					value !== options.documentationFile &&
					value.match( jsonFilenameRegex )
				);
			} );

			translatedFiles.forEach( function ( languageFile ) {
				var language = languageFile.match( jsonFilenameRegex )[ 1 ],
					languageMesages = messages( languageFile, language ),
					keys = keysNoMetadata( languageMesages, language ),
					blanks = [],
					duplicates = [],
					unuseds = [],
					missing = sourceMessageKeys.slice( 0 );

				for ( index in keys ) {
					message = keys[ index ];

					if ( missing.indexOf( message ) !== -1 ) {
						if ( languageMesages[ message ] === sourceMessages[ message ] ) {
							duplicates.push( message );
						}
						missing.splice( missing.indexOf( message ), 1 );
					} else {
						unuseds.push( message );
					}

					if ( typeof languageMesages[ message ] !== 'string' ) {
						continue;
					}
					if ( languageMesages[ message ].trim() === '' ) {
						blanks.push( message );
					}
				}

				translatedData[ language ] = {
					messages: languageMesages,
					keys: keys,
					blank: blanks,
					duplicate: duplicates,
					unused: unuseds,
					missing: missing
				};
			} );

			while ( sourceMessageKeys.length > 0 ) {
				message = sourceMessageKeys[ 0 ];

				offset = documentationMessageKeys.indexOf( message );

				if ( offset !== -1 ) {

					if ( documentationMessages[ message ].trim() === '' ) {
						documentationMessageBlanks.push( message );
					}

					documentationMessageKeys.splice( offset, 1 );
				} else {
					sourceMessageMissing.push( message );
				}
				sourceMessageKeys.splice( 0, 1 );
			}

			if ( options.requireCompleteMessageDocumentation ) {
				count = sourceMessageMissing.length;
				if ( count > 0 ) {
					ok = false;

					grunt.log.error(
						count + ' message' + ( count > 1 ? 's lack' : ' lacks' ) + ' documentation.'
					);

					sourceMessageMissing.forEach( function ( message ) {
						grunt.log.error( 'Message "' + message + '" lacks documentation.' );
					} );
				}
			}

			if ( options.disallowEmptyDocumentation ) {
				count = documentationMessageBlanks.length;
				if ( count > 0 ) {
					ok = false;

					grunt.log.error(
						count + ' documented message' + ( count > 1 ? 's are' : ' is' ) + ' blank.'
					);

					documentationMessageBlanks.forEach( function ( message ) {
						grunt.log.error( 'Message "' + message + '" is documented with a blank string.' );
					} );
				}
			}

			if ( options.disallowUnusedDocumentation ) {
				count = documentationMessageKeys.length;
				if ( count > 0 ) {
					ok = false;

					grunt.log.error(
						count + ' documented message' + ( count > 1 ? 's are' : ' is' ) + ' undefined.'
					);

					documentationMessageKeys.forEach( function ( message ) {
						grunt.log.error( 'Message "' + message + '" is documented but undefined.' );
					} );
				}
			}

			for ( index in translatedData ) {
				if ( !translatedData.hasOwnProperty( index ) ) {
					continue;
				}

				if ( options.disallowBlankTranslations ) {
					count = translatedData[ index ].blank.length;
					if ( count > 0 ) {
						ok = false;
						grunt.log.error( 'The "' + index + '" translation has ' + count + ' blank translation' + ( count > 1 ? 's' : '' ) + ':' );

						// jshint -W083
						translatedData[ index ].blank.forEach( function ( message ) {
							grunt.log.error( 'The translation of "' + message + '" is blank.' );
						} );
						// jshint +W083
					}
				}

				if ( options.disallowDuplicateTranslations ) {
					count = translatedData[ index ].duplicate.length;
					if ( count > 0 ) {
						ok = false;
						grunt.log.error( 'The "' + index + '" translation has ' + count + ' duplicate translation' + ( count > 1 ? 's' : '' ) + ':' );

						// jshint -W083
						translatedData[ index ].duplicate.forEach( function ( message ) {
							grunt.log.error( 'The translation of "' + message + '" is a duplicate of the primary message.' );
						} );
						// jshint +W083
					}
				}

				if ( options.disallowUnusedTranslations ) {
					count = translatedData[ index ].unused.length;
					if ( count > 0 ) {
						ok = false;
						grunt.log.error( 'The "' + index + '" translation has ' + count + ' unused translation' + ( count > 1 ? 's' : '' ) + ':' );

						// jshint -W083
						translatedData[ index ].unused.forEach( function ( message ) {
							grunt.log.error( 'The translation of "' + message + '" is unused.' );
						} );
						// jshint +W083
					}
				}

			}

			if ( options.requireCompleteTranslationLanguages.length ) {
				for ( index in translatedData ) {
					if (
						!translatedData.hasOwnProperty( index ) ||
						( options.requireCompleteTranslationLanguages.indexOf( index ) === -1 )
					) {
						continue;
					}

					count = translatedData[ index ].missing.length;
					if ( count > 0 ) {
						ok = false;
						grunt.log.error( 'The "' + index + '" translation has ' + count + ' missing translation' + ( count > 1 ? 's' : '' ) + ':' );

						// jshint -W083
						translatedData[ index ].missing.forEach( function ( message ) {
							grunt.log.error( 'The translation of "' + message + '" is missing.' );
						} );
						// jshint +W083
					}
				}
			}

			if ( options.requireCompleteTranslationMessages.length ) {
				for ( index in translatedData ) {
					if ( !translatedData.hasOwnProperty( index ) ) {
						continue;
					}

					for ( message in translatedData[ index ].missing ) {
						if ( !translatedData[ index ].missing.hasOwnProperty( sourceMessageKeys[ message ] ) ) {
							continue;
						}

						offset = options.requireCompleteTranslationMessages.indexOf( sourceMessageKeys[ message ] );

						if ( offset === -1 ) {
							translatedData[ index ].missing.splice( offset, 1 );
						}
					}

					count = translatedData[ index ].missing.length;
					if ( count > 0 ) {
						ok = false;
						grunt.log.error( 'The "' + index + '" translation is missing ' + count + ' required message' + ( count > 1 ? 's' : '' ) + ':' );

						// jshint -W083
						translatedData[ index ].missing.forEach( function ( message ) {
							grunt.log.error( 'The required message "' + message + '" is missing.' );
						} );
						// jshint +W083
					}
				}

			}

		} );

		if ( !ok ) {
			return false;
		}

		grunt.log.ok( messageCount + ' message' + ( messageCount > 1 ? 's' : '' ) + ' checked.' );
	} );
};
<|endoftext|>


<commit_before>var jetpack = require('fs-jetpack');
var path = require('path');

var load = function(path) {
    if (!jetpack.exists(path)) console.log('ERROR: File not found at "' + path + '"');
    return jetpack.read(path);
};

var loadTree = function(path) {
    return jetpack.find(path, { matching: '*.js' }).reduce(function(code, filePath) {
        return code  + (code != '' && '\n\n' || '') + load(filePath);
    }, '');
};

module.exports = function(options) {
    options = options || {};
    return {
        name: 'concat',
        transform: function(code, id) {
            var dir = path.dirname(id);
            var regex = /\/\/= concat(_tree)? ([^\n\r]+)/gi;
            var result = code.replace(regex, function(match, tree, target) {
                if (options.debug) {
                    console.log('rollup-plugin-concat: processing "' + match + '" in "' + id + '"');
                }
                let targetPath = path.join(dir, target);
                return tree ? loadTree(targetPath) : load(targetPath);
            });
            return {
                code: result,
                map: {mappings: '' }
            };
        }
    };
};<commit_msg>slight tweak to concat whitespace<commit_after>var jetpack = require('fs-jetpack');
var path = require('path');

var load = function(path) {
    if (!jetpack.exists(path)) console.log('ERROR: File not found at "' + path + '"');
    return jetpack.read(path);
};

var loadTree = function(path) {
    return jetpack.find(path, { matching: '*.js' }).reduce(function(code, filePath) {
        return code  + (code != '' && '\n\n' || '') + load(filePath);
    }, '');
};

module.exports = function(options) {
    options = options || {};
    return {
        name: 'concat',
        transform: function(code, id) {
            var dir = path.dirname(id);
            var regex = /\/\/= concat(_tree)? ([^\n\r]+)/gi;
            var result = code.replace(regex, function(match, tree, target) {
                if (options.debug) {
                    console.log('rollup-plugin-concat: processing "' + match + '" in "' + id + '"');
                }
                let targetPath = path.join(dir, target);
                return (tree ? loadTree(targetPath) : load(targetPath)) + '\n';
            });
            return {
                code: result,
                map: {mappings: '' }
            };
        }
    };
};<|endoftext|>


<commit_before>/*
 * grunt-indent
 * https://github.com/stevenbenner/grunt-indent
 *
 * Copyright (c) 2013 Steven Benner
 * Licensed under the MIT license.
 */

'use strict';

var path = require('path');

module.exports = function(grunt) {

  grunt.registerMultiTask('indent', 'Change the indentation of files.', function() {

    // get options object with defaults
    var options = this.options({
      style: 'tab',
      size: 1,
      change: 0
    });

    // define one indent string
    var indent = '';
    if (options.style === 'tab') {
      indent = grunt.util._.repeat('\t', options.size);
    } else if (options.style === 'space') {
      indent = grunt.util._.repeat(' ', options.size);
    }

    // process the files
    this.files.forEach(function(file) {
      file.src.filter(function(filePath) {
        // filter out nonexistent files
        if (!grunt.file.exists(filePath)) {
          grunt.log.warn('Source file "' + filePath + '" not found.');
          return false;
        }
        return true;
      }).forEach(function(src) {
        var dest = file.dest,
          newFile = [];

        // if dest is a folder  use the src file name
        if (grunt.util._.endsWith(dest, '/')) {
          dest += path.basename(src);
        }

        // walk the file line-by-line
        grunt.file.read(src).split(/\r\n?|\n/).forEach(function(line) {
          if (line) {
            if (options.change > 0) {
              line = grunt.util._.repeat(indent, options.change) + line;
            } else if (options.change < 0) {
              var i = options.change;
              while (grunt.util._.startsWith(line, indent) && i < 0) {
                line = line.substring(options.size);
                i++;
              }
            }
          }
          newFile.push(line);
        });

        grunt.file.write(dest, newFile.join(grunt.util.linefeed));
      });
    });

  });

};
<commit_msg>Added success message to grunt output.<commit_after>/*
 * grunt-indent
 * https://github.com/stevenbenner/grunt-indent
 *
 * Copyright (c) 2013 Steven Benner
 * Licensed under the MIT license.
 */

'use strict';

var path = require('path');

module.exports = function(grunt) {

  grunt.registerMultiTask('indent', 'Change the indentation of files.', function() {

    // get options object with defaults
    var options = this.options({
      style: 'tab',
      size: 1,
      change: 0
    });

    // define one indent string
    var indent = '';
    if (options.style === 'tab') {
      indent = grunt.util._.repeat('\t', options.size);
    } else if (options.style === 'space') {
      indent = grunt.util._.repeat(' ', options.size);
    }

    // process the files
    this.files.forEach(function(file) {
      file.src.filter(function(filePath) {
        // filter out nonexistent files
        if (!grunt.file.exists(filePath)) {
          grunt.log.warn('Source file "' + filePath + '" not found.');
          return false;
        }
        return true;
      }).forEach(function(src) {
        var dest = file.dest,
          newFile = [];

        // if dest is a folder  use the src file name
        if (grunt.util._.endsWith(dest, '/')) {
          dest += path.basename(src);
        }

        // walk the file line-by-line
        grunt.file.read(src).split(/\r\n?|\n/).forEach(function(line) {
          if (line) {
            if (options.change > 0) {
              line = grunt.util._.repeat(indent, options.change) + line;
            } else if (options.change < 0) {
              var i = options.change;
              while (grunt.util._.startsWith(line, indent) && i < 0) {
                line = line.substring(options.size);
                i++;
              }
            }
          }
          newFile.push(line);
        });

        grunt.file.write(dest, newFile.join(grunt.util.linefeed));
        grunt.log.writeln('File "' + dest + '" saved.');
      });
    });

  });

};
<|endoftext|>


<commit_before>/*
 * casper-inline
 * https://github.com/tactivos/casper-inline
 *
 * Copyright (c) 2013 Auguest G. casper & IMWEB TEAM
 */

'use strict';

module.exports = function(grunt) {

	var path = require('path');
	var datauri = require('datauri');

	grunt.registerMultiTask('inline', "将标记为inline的';
				}else{
					grunt.log.error('inline > '+inlineFilePath + ' 不存在！');
				}
			}
			grunt.log.debug('ret = : ' + ret +'\n');

			return ret;

		}).replace(/<link.+href=["']([^"']+)["'].*\/?>/g, function(matchedWord, src){
			var ret = matchedWord;

			if(!grunt.file.isPathAbsolute(src) && src.indexOf('__inline')!=-1){

				var inlineFilePath = path.resolve( path.dirname(filepath), src ).replace(/\?.*$/, '');	// 将参数去掉

				grunt.log.writeln('inline > inline stylesheet，href = ' + src, ', 实际路径： ' + inlineFilePath);

				if( grunt.file.exists(inlineFilePath) ){
					var styleSheetContent = grunt.file.read( inlineFilePath );

					styleSheetContent = styleSheetContent.replace(/url\(([^)]+)\)/g, function(matchedWord, imgUrl){
						var imgUrlRelativeToParentFile = matchedWord;
						if(isRemotePath(imgUrl)){
							// return matchedWord;
						}else{
							console.log( 'filepath: '+ filepath);
							console.log( 'imgUrl: '+imgUrl);
							console.log( 'inlineFilePath: '+inlineFilePath);
							var absoluteImgurl = path.resolve( path.dirname(inlineFilePath),imgUrl );
							console.log( 'absoluteImgurl: '+absoluteImgurl);
							imgUrlRelativeToParentFile = path.relative( path.dirname(filepath), absoluteImgurl );
							console.log( 'imgUrlRelativeToParentFile: '+imgUrlRelativeToParentFile);
						}
						// console.log('imgUrlRelativeToParentFile: '+imgUrlRelativeToParentFile);
						return imgUrlRelativeToParentFile;
					});

					ret = '';

				}else{
					grunt.log.error('inline > '+inlineFilePath + ' 不存在！');
				}
			}
			grunt.log.debug('ret = : ' + ret +'\n');

			return ret;
		}).replace(//g, function(matchedWord, src){
			var	ret = matchedWord;

			if(!grunt.file.isPathAbsolute(src) && src.indexOf('__inline')!=-1){

				var inlineFilePath = path.resolve( path.dirname(filepath), src ).replace(/\?.*$/, '');	// 将参数去掉

				grunt.log.writeln('inline > inline img，src = ' + src, ', 实际路径： ' + inlineFilePath);

				if( grunt.file.exists(inlineFilePath) ){
					ret = matchedWord.replace(src, 'data:image/png;base64'+(new datauri(inlineFilePath)).content);
				}else{
					grunt.log.error('inline > '+inlineFilePath + ' 不存在！');
				}
			}
			grunt.log.debug('ret = : ' + ret +'\n');

			return ret;
		});
	}
};
<commit_msg>bug fix:...<commit_after>/*
 * casper-inline
 * https://github.com/tactivos/casper-inline
 *
 * Copyright (c) 2013 Auguest G. casper & IMWEB TEAM
 */

'use strict';

module.exports = function(grunt) {

	var path = require('path');
	var datauri = require('datauri');

	grunt.registerMultiTask('inline', "将标记为inline的';
				}else{
					grunt.log.error('inline > '+inlineFilePath + ' 不存在！');
				}
			}
			grunt.log.debug('ret = : ' + ret +'\n');

			return ret;

		}).replace(/<link.+href=["']([^"']+)["'].*\/?>/g, function(matchedWord, src){
			var ret = matchedWord;

			if(!grunt.file.isPathAbsolute(src) && src.indexOf('__inline')!=-1){

				var inlineFilePath = path.resolve( path.dirname(filepath), src ).replace(/\?.*$/, '');	// 将参数去掉

				grunt.log.writeln('inline > inline stylesheet，href = ' + src, ', 实际路径： ' + inlineFilePath);

				if( grunt.file.exists(inlineFilePath) ){
					var styleSheetContent = grunt.file.read( inlineFilePath );

					styleSheetContent = styleSheetContent.replace(/url\(([^)]+)\)/g, function(matchedWord, imgUrl){
						var imgUrlRelativeToParentFile = matchedWord;
						if(isRemotePath(imgUrl)){
							// return matchedWord;
						}else{
							console.log( 'filepath: '+ filepath);
							console.log( 'imgUrl: '+imgUrl);
							console.log( 'inlineFilePath: '+inlineFilePath);
							var absoluteImgurl = path.resolve( path.dirname(inlineFilePath),imgUrl );
							console.log( 'absoluteImgurl: '+absoluteImgurl);
							imgUrlRelativeToParentFile = path.relative( path.dirname(filepath), absoluteImgurl );
							console.log( 'imgUrlRelativeToParentFile: '+imgUrlRelativeToParentFile);
						}
						// console.log('imgUrlRelativeToParentFile: '+imgUrlRelativeToParentFile);
						return matchedWord.replace(imgUrl, imgUrlRelativeToParentFile);
					});

					ret = '';

				}else{
					grunt.log.error('inline > '+inlineFilePath + ' 不存在！');
				}
			}
			grunt.log.debug('ret = : ' + ret +'\n');

			return ret;
		}).replace(//g, function(matchedWord, src){
			var	ret = matchedWord;

			if(!grunt.file.isPathAbsolute(src) && src.indexOf('__inline')!=-1){

				var inlineFilePath = path.resolve( path.dirname(filepath), src ).replace(/\?.*$/, '');	// 将参数去掉

				grunt.log.writeln('inline > inline img，src = ' + src, ', 实际路径： ' + inlineFilePath);

				if( grunt.file.exists(inlineFilePath) ){
					ret = matchedWord.replace(src, 'data:image/png;base64'+(new datauri(inlineFilePath)).content);
				}else{
					grunt.log.error('inline > '+inlineFilePath + ' 不存在！');
				}
			}
			grunt.log.debug('ret = : ' + ret +'\n');

			return ret;
		});
	}
};
<|endoftext|>


<commit_before>var gulp = require('gulp');
var uglify = require('gulp-uglify');
var cssmin = require('gulp-cssmin');
var rename = require('gulp-rename');

gulp.task('minify', [
	'minify:js',
	'minify:css'
]);

gulp.task('minify:js', [ 'webpack' ], function() {
	return gulp.src([
		'dist/*.js',
		'!dist/locale-all.js' // already minified by webpack task
	])
		.pipe(uglify({
			preserveComments: 'some' // keep comments starting with !
		}))
		.pipe(rename({ extname: '.min.js' }))
		.pipe(gulp.dest('dist/'));
});

gulp.task('minify:css', [ 'webpack' ], function() {
	return gulp.src([
		'dist/*.css',
		'!dist/*.min.css' // avoid double minify
	])
		.pipe(cssmin())
		.pipe(rename({ extname: '.min.css' }))
		.pipe(gulp.dest('dist/'));
});
<commit_msg>avoud double miniify<commit_after>var gulp = require('gulp');
var uglify = require('gulp-uglify');
var cssmin = require('gulp-cssmin');
var rename = require('gulp-rename');

gulp.task('minify', [
	'minify:js',
	'minify:css'
]);

gulp.task('minify:js', [ 'webpack' ], function() {
	return gulp.src([
		'dist/*.js',
		'!dist/*.min.js', // avoid double minify
		'!dist/locale-all.js' // already minified by webpack task
	])
		.pipe(uglify({
			preserveComments: 'some' // keep comments starting with !
		}))
		.pipe(rename({ extname: '.min.js' }))
		.pipe(gulp.dest('dist/'));
});

gulp.task('minify:css', [ 'webpack' ], function() {
	return gulp.src([
		'dist/*.css',
		'!dist/*.min.css' // avoid double minify
	])
		.pipe(cssmin())
		.pipe(rename({ extname: '.min.css' }))
		.pipe(gulp.dest('dist/'));
});
<|endoftext|>


<commit_before>/*
 * grunt-pylint
 * https://github.com/tarjei/grunt-pylint
 *
 * Copyright (c) 2013 Tarjei Husøy
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

  var path = require('path');

  // Use our own uncolor func since grunt.log.uncolor doesnt support codes of the
  // format [7;33m (only [7m and the likes) which is how pylint outputs module headers
  var uncolor = function(str) {
    return str.replace(/\x1B\[\d+[;\d]*m/g, '');
  };

  var getVirtualenvActivationCode = function(virtualenv){
    var activateThisPath;
    var activeThisPathAlternatives = [
      // *nix activate_this path:
      path.join(virtualenv, 'bin', 'activate_this.py'),
      // windows style path:
      path.join(virtualenv, 'Scripts', 'activate_this.py'),
    ];
    grunt.util._.forEach(activeThisPathAlternatives, function(path){
      if (grunt.file.exists(path)){
        activateThisPath = path;
        return false; // stops iteration
      }
    });
    if (activateThisPath === undefined){
      grunt.fail.warn('Tried to activate virtualenv "' + virtualenv + '", but did not ' +
        'find the file "activate_this.py" required for activation, after trying ' +
        'these locations:\n' + activeThisPathAlternatives.join("\n") +
        '\nMake sure this file exist at either of these locations, and try again.');
    }
    var activationCode = 'execfile(r"' + activateThisPath + '", ' +
      'dict(__file__=r"' + activateThisPath + '"))';

    return activationCode;
  };

  var buildBaseArgs = function(options){
    // Build the base argument list sent to python from the options object

    var pylintPath = path.join(__dirname, 'lib');

    var virtualenv = options.virtualenv;
    delete options.virtualenv;

    var activateVirtualenv = "pass";
    if (virtualenv){
      activateVirtualenv = getVirtualenvActivationCode(virtualenv);
    }

    var baseArgs = [
      '-c',
      [
        activateVirtualenv,
        'import sys',
        'sys.path.insert(0, r"'+pylintPath+'")',
        'import pylint',
        'pylint.run_pylint()',
      ].join("; "),
    ];

    var enable = options.enable;
    delete options.enable;

    if (enable){
      baseArgs.push('--enable=' + enable);
    }

    var disable = options.disable;
    delete options.disable;

    if (disable){
      baseArgs.push('--disable=' + disable);
    }

    var messageTemplate = options.messageTemplate;
    delete options.messageTemplate;

    if (messageTemplate){
      var aliases = {
        'short': "line {line}: {msg} ({symbol})",
        'msvs': "{path}({line}): [{msg_id}({symbol}){obj}] {msg}",
        'parseable': "{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}",
      };
      if (aliases[messageTemplate] !== undefined){
        baseArgs.push('--msg-template="' + aliases[messageTemplate] + '"');
      } else {
        baseArgs.push('--msg-template="' + messageTemplate + '"');
      }
    }

    var outputFormat = options.outputFormat;
    delete options.outputFormat;

    if (outputFormat){
      baseArgs.push('--output-format=' + outputFormat);
    }

    var report = options.report;
    delete options.report;

    if (report){
      baseArgs.push('--report=yes');
    } else {
      baseArgs.push('--report=no');
    }

    var rcfile = options.rcfile;
    delete options.rcfile;

    if (rcfile){
      baseArgs.push('--rcfile=' + rcfile);
    }

    var errorsOnly = options.errorsOnly;
    delete options.errorsOnly;

    if (errorsOnly){
      baseArgs.push('--errors-only');
    }

    var ignore = options.ignore;
    delete options.ignore;

    if (ignore){
      baseArgs.push('--ignore=' + ignore);
    }

    // Fail if there's any options remaining now
    for(var prop in options){
      if (options.hasOwnProperty(prop)){
        grunt.fail.warn("Unknown option to pylint: '" + prop + "'");
      }
    }

    return baseArgs;
  };

  grunt.registerMultiTask('pylint', 'Run all python code through pylint', function(){
    var done = this.async();

    var options = this.options({
      'errorsOnly': false,
      'force': false,
      'outputFormat': 'colorized',
      'messageTemplate': 'short',
      'report': false,
    });

    var force = options.force;
    delete options.force;

    // Capture task stdout for writing to file later
    var output = "";
    var outputFile = options.outputFile;
    delete options.outputFile;

    var baseArgs = buildBaseArgs(options);

    var noFailures = true;

    // pylint only supports running against a single module or package at a time,
    // so we haave to trigger it several times if we have several targets
    var runsRemaining = this.filesSrc.length;

    this.filesSrc.forEach(function(module_or_package){

      var args = baseArgs.slice(0);
      args.push(module_or_package);

      grunt.log.verbose.writeln('Running pylint with args: ' + args.join(" "));

      // Spawn the pylint subprocess
      grunt.util.spawn({
        'cmd': 'python',
        'args': args,
      }, function(error, result, code){

        var stdout = grunt.option('no-color') ? uncolor(result.stdout) : result.stdout;
        grunt.log.writeln(stdout);
        output += result.stdout + '\n';

        if (code === 0){
          grunt.log.ok("No lint in " + module_or_package);
        } else {
          grunt.log.verbose.writeln("Stderr from pylint: " + result.stderr);
          noFailures = false;
          if (force){
            grunt.log.warn("Linting errors found, but `force` was used, continuing...");
          }
        }

        runsRemaining -= 1;
        if (runsRemaining === 0){
          if (outputFile){
            var uncoloredOutput = uncolor(output);
            grunt.file.write(outputFile, uncoloredOutput);
            grunt.log.ok("Results written to " + outputFile);
          }
          done(noFailures || force);
        }
      });

    });

  });

};
<commit_msg>Added option to use external pylint<commit_after>/*
 * grunt-pylint
 * https://github.com/tarjei/grunt-pylint
 *
 * Copyright (c) 2013 Tarjei Husøy
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

  var path = require('path');

  // Use our own uncolor func since grunt.log.uncolor doesnt support codes of the
  // format [7;33m (only [7m and the likes) which is how pylint outputs module headers
  var uncolor = function(str) {
    return str.replace(/\x1B\[\d+[;\d]*m/g, '');
  };

  var getVirtualenvActivationCode = function(virtualenv){
    var activateThisPath;
    var activeThisPathAlternatives = [
      // *nix activate_this path:
      path.join(virtualenv, 'bin', 'activate_this.py'),
      // windows style path:
      path.join(virtualenv, 'Scripts', 'activate_this.py'),
    ];
    grunt.util._.forEach(activeThisPathAlternatives, function(path){
      if (grunt.file.exists(path)){
        activateThisPath = path;
        return false; // stops iteration
      }
    });
    if (activateThisPath === undefined){
      grunt.fail.warn('Tried to activate virtualenv "' + virtualenv + '", but did not ' +
        'find the file "activate_this.py" required for activation, after trying ' +
        'these locations:\n' + activeThisPathAlternatives.join("\n") +
        '\nMake sure this file exist at either of these locations, and try again.');
    }
    var activationCode = 'execfile(r"' + activateThisPath + '", ' +
      'dict(__file__=r"' + activateThisPath + '"))';

    return activationCode;
  };

  var buildBaseArgs = function(options){
    // Build the base argument list sent to python from the options object

    var pylintPath = path.join(__dirname, 'lib');

    var externalPylint = options.externalPylint;
    delete options.externalPylint;

    var virtualenv = options.virtualenv;
    delete options.virtualenv;

    var activateVirtualenv = "pass";
    if (virtualenv){
      activateVirtualenv = getVirtualenvActivationCode(virtualenv);
    }

    var pythonCode = [
      activateVirtualenv,
    ];

    if (!externalPylint) {
      pythonCode.push('import sys; sys.path.insert(0, r"'+pylintPath+'")');
    }

    pythonCode.push('import pylint', 'pylint.run_pylint()');

    var baseArgs = [
      '-c',
      pythonCode.join("; ")
    ];

    var enable = options.enable;
    delete options.enable;

    if (enable){
      baseArgs.push('--enable=' + enable);
    }

    var disable = options.disable;
    delete options.disable;

    if (disable){
      baseArgs.push('--disable=' + disable);
    }

    var messageTemplate = options.messageTemplate;
    delete options.messageTemplate;

    if (messageTemplate){
      var aliases = {
        'short': "line {line}: {msg} ({symbol})",
        'msvs': "{path}({line}): [{msg_id}({symbol}){obj}] {msg}",
        'parseable': "{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}",
      };
      if (aliases[messageTemplate] !== undefined){
        baseArgs.push('--msg-template="' + aliases[messageTemplate] + '"');
      } else {
        baseArgs.push('--msg-template="' + messageTemplate + '"');
      }
    }

    var outputFormat = options.outputFormat;
    delete options.outputFormat;

    if (outputFormat){
      baseArgs.push('--output-format=' + outputFormat);
    }

    var report = options.report;
    delete options.report;

    if (report){
      baseArgs.push('--report=yes');
    } else {
      baseArgs.push('--report=no');
    }

    var rcfile = options.rcfile;
    delete options.rcfile;

    if (rcfile){
      baseArgs.push('--rcfile=' + rcfile);
    }

    var errorsOnly = options.errorsOnly;
    delete options.errorsOnly;

    if (errorsOnly){
      baseArgs.push('--errors-only');
    }

    var ignore = options.ignore;
    delete options.ignore;

    if (ignore){
      baseArgs.push('--ignore=' + ignore);
    }

    // Fail if there's any options remaining now
    for(var prop in options){
      if (options.hasOwnProperty(prop)){
        grunt.fail.warn("Unknown option to pylint: '" + prop + "'");
      }
    }

    return baseArgs;
  };

  grunt.registerMultiTask('pylint', 'Run all python code through pylint', function(){
    var done = this.async();

    var options = this.options({
      'errorsOnly': false,
      'force': false,
      'outputFormat': 'colorized',
      'messageTemplate': 'short',
      'report': false,
      'externalPylint': false,
    });

    var force = options.force;
    delete options.force;

    // Capture task stdout for writing to file later
    var output = "";
    var outputFile = options.outputFile;
    delete options.outputFile;

    var baseArgs = buildBaseArgs(options);

    var noFailures = true;

    // pylint only supports running against a single module or package at a time,
    // so we haave to trigger it several times if we have several targets
    var runsRemaining = this.filesSrc.length;

    this.filesSrc.forEach(function(module_or_package){

      var args = baseArgs.slice(0);
      args.push(module_or_package);

      grunt.log.verbose.writeln('Running pylint with args: ' + args.join(" "));

      // Spawn the pylint subprocess
      grunt.util.spawn({
        'cmd': 'python',
        'args': args,
      }, function(error, result, code){

        var stdout = grunt.option('no-color') ? uncolor(result.stdout) : result.stdout;
        grunt.log.writeln(stdout);
        output += result.stdout + '\n';

        if (code === 0){
          grunt.log.ok("No lint in " + module_or_package);
        } else {
          grunt.log.verbose.writeln("Stderr from pylint: " + result.stderr);
          noFailures = false;
          if (force){
            grunt.log.warn("Linting errors found, but `force` was used, continuing...");
          }
        }

        runsRemaining -= 1;
        if (runsRemaining === 0){
          if (outputFile){
            var uncoloredOutput = uncolor(output);
            grunt.file.write(outputFile, uncoloredOutput);
            grunt.log.ok("Results written to " + outputFile);
          }
          done(noFailures || force);
        }
      });

    });

  });

};
<|endoftext|>


<commit_before>// /**
//  * SERVICE
//  * -------------------------------------------------------------
//  *
//  * This task scaffolds a TypeScript Model
//  */
// 'use strict';

// var gulp = require('gulp'),
//   conflict = require('gulp-conflict'),
//   template = require('gulp-template'),
//   rename = require('gulp-rename'),
//   _ = require('underscore.string'),
//   inquirer = require('inquirer'),
//   path = require('path');

// var util = require('../util.js');

// gulp.task('routes', function (done) {
//   // var prompts = [{
//   //   name: 'name',
//   //   message: 'What is the url of your state?'
//   // }];
//   //Ask
//   // inquirer.prompt(prompts, function (answers) {
//     util.modulePrompt(function (module) {
//       util.folderPrompt('', function (folder) {
//         util.getGitConfig(function (gitConfig) {

//           var p = util.getPaths('Routes', ['ts']);
//           var context = util.buildContext([{}, folder, gitConfig]);

//           var fileName = folder.namespace + _.capitalize(module) + 'Routes.ts';

//           console.log(folder);

//           var target = path.join(p.target, 'modules', module);
//           context.module = module;
//           gulp.src(p.source)
//             .pipe(template(context))
//             .pipe(rename(fileName))
//             .pipe(conflict(target))
//             .pipe(gulp.dest(target))
//             .on('end', function () {
//               done();
//               util.onSuccess('Routes', path.join(target, fileName));
//             });

//         });
//       });
//     });
//   // });
// });
<commit_msg>refactor(routes): removes task<commit_after><|endoftext|>


<commit_before>'use strict';

var async = require('async');
var dot = require('dot');
dot.templateSettings.strip = false;

var id = require('../util/id');

var queryTemplate = dot.template([
    'SELECT {{=it.pointsColumns}}',
    'FROM',
    '({{=it.pointsQuery}}) points',
    'JOIN',
    '({{=it.polygonsQuery}}) polygons',
    'ON ST_Contains(polygons.the_geom, points.the_geom)'
].join('\n'));

function query(it) {
    it.pointsColumns = it.columnNames.map(function(name) { return "points." + name; }).join(", ");
    return queryTemplate(it)
}

var TYPE = 'point-in-polygon';

function PointInPolygon(pointsNode, polygonsNode, columns) {
    this.pointsNode = pointsNode;
    this.polygonsNode = polygonsNode;

    this.columns = columns;
}

module.exports = PointInPolygon;
module.exports.TYPE = TYPE;
module.exports.create = function(definition, factory, databaseService, callback) {
    var sources = [definition.params.pointsSource, definition.params.polygonsSource];
    async.map(sources, factory.create.bind(factory), function(err, results) {
        if (err) {
            return callback(err);
        }

        var pointsNode = results[0];
        var polygonsNode = results[1];

        databaseService.getSchema(pointsNode.getQuery(), function(err, columns) {
            if (err) {
                return callback(err);
            }

            var columnNames = columns
                .map(function(column) {
                    return column.name
                })
                .filter(function(columnName) {
                    return columnName !== 'the_geom_webmercator';
                });

            return callback(null, new PointInPolygon(pointsNode, polygonsNode, columnNames));
        });

    });
};

// ------------------------------ PUBLIC API ------------------------------ //

PointInPolygon.prototype.id = function() {
    return id(this.toJSON());
};

PointInPolygon.prototype.getQuery = function() {
    return query({
        pointsQuery: this.pointsNode.getQuery(),
        polygonsQuery: this.polygonsNode.getQuery(),
        columnNames: this.columns
    });
};

PointInPolygon.prototype.getInputNodes = function() {
    return [this.pointsNode, this.polygonsNode];
};

PointInPolygon.prototype.toJSON = function() {
    return {
        type: TYPE,
        pointsNodeId: this.pointsNode.id(),
        polygonsNodeNodeId: this.polygonsNode.id()
    };
};

PointInPolygon.prototype.toDot = function() {
    return {
        type: TYPE,
        color: 'red',
        nodes: {
            pointsNode: this.pointsNode,
            polygonsNodeNode: this.polygonsNode
        },
        attrs: {}
    };
};

// ---------------------------- END PUBLIC API ---------------------------- //

PointInPolygon.prototype.getTargetTable = function() {
    return 'analysis_point_in_polygon_' + this.id();
};
<commit_msg>Fix style<commit_after>'use strict';

var async = require('async');
var dot = require('dot');
dot.templateSettings.strip = false;

var id = require('../util/id');

var queryTemplate = dot.template([
    'SELECT {{=it.pointsColumns}}',
    'FROM',
    '({{=it.pointsQuery}}) points',
    'JOIN',
    '({{=it.polygonsQuery}}) polygons',
    'ON ST_Contains(polygons.the_geom, points.the_geom)'
].join('\n'));

function query(it) {
    it.pointsColumns = it.columnNames.map(function(name) { return 'points.' + name; }).join(', ');
    return queryTemplate(it);
}

var TYPE = 'point-in-polygon';

function PointInPolygon(pointsNode, polygonsNode, columns) {
    this.pointsNode = pointsNode;
    this.polygonsNode = polygonsNode;

    this.columns = columns;
}

module.exports = PointInPolygon;
module.exports.TYPE = TYPE;
module.exports.create = function(definition, factory, databaseService, callback) {
    var sources = [definition.params.pointsSource, definition.params.polygonsSource];
    async.map(sources, factory.create.bind(factory), function(err, results) {
        if (err) {
            return callback(err);
        }

        var pointsNode = results[0];
        var polygonsNode = results[1];

        databaseService.getSchema(pointsNode.getQuery(), function(err, columns) {
            if (err) {
                return callback(err);
            }

            var columnNames = columns
                .map(function(column) {
                    return column.name;
                })
                .filter(function(columnName) {
                    return columnName !== 'the_geom_webmercator';
                });

            return callback(null, new PointInPolygon(pointsNode, polygonsNode, columnNames));
        });

    });
};

// ------------------------------ PUBLIC API ------------------------------ //

PointInPolygon.prototype.id = function() {
    return id(this.toJSON());
};

PointInPolygon.prototype.getQuery = function() {
    return query({
        pointsQuery: this.pointsNode.getQuery(),
        polygonsQuery: this.polygonsNode.getQuery(),
        columnNames: this.columns
    });
};

PointInPolygon.prototype.getInputNodes = function() {
    return [this.pointsNode, this.polygonsNode];
};

PointInPolygon.prototype.toJSON = function() {
    return {
        type: TYPE,
        pointsNodeId: this.pointsNode.id(),
        polygonsNodeNodeId: this.polygonsNode.id()
    };
};

PointInPolygon.prototype.toDot = function() {
    return {
        type: TYPE,
        color: 'red',
        nodes: {
            pointsNode: this.pointsNode,
            polygonsNodeNode: this.polygonsNode
        },
        attrs: {}
    };
};

// ---------------------------- END PUBLIC API ---------------------------- //

PointInPolygon.prototype.getTargetTable = function() {
    return 'analysis_point_in_polygon_' + this.id();
};
<|endoftext|>


<commit_before>'use strict'

/**
 * Test dependencies
 * @ignore
 */
const cwd = process.cwd()
const path = require('path')
const fs = require('fs')
const chai = require('chai')
const chaiAsPromised = require('chai-as-promised')
const sinon = require('sinon')
const sinonChai = require('sinon-chai')

/**
 * Assertions
 * @ignore
 */
chai.use(chaiAsPromised)
chai.should()
chai.use(sinonChai)
const expect = chai.expect

/**
 * Code Under Test
 * @ignore
 */
const { JWK } = require('../src')
const { DataError, OperationError } = require('../src/errors')

/**
 * Test Data
 * @ignore
 */
const { ECPrivateJWK, ECPublicJWK, A256GCMJWK, RSAPrivateJWK, RSAPublicJWK } = require('./keys')

const ECPublicJWKString = fs.readFileSync(path.join(cwd, 'test', 'file_import', 'fileImportJWKTestData.json'), 'utf8')
const InvalidJWKString = fs.readFileSync(path.join(cwd, 'test', 'file_import', 'fileImportJWKTestData.json'), 'utf8')

const ECPublicJWKNoAlg = Object.assign({}, ECPublicJWK)
delete ECPublicJWKNoAlg.alg

const ECPublicJWKNoKid = Object.assign({}, ECPublicJWK)
delete ECPublicJWKNoKid.kid

const ECPublicJWKNoKty = Object.assign({}, ECPublicJWK)
delete ECPublicJWKNoKty.kty

const ECPublicJWKInvalidKty = Object.assign({}, ECPublicJWK)
ECPublicJWKInvalidKty.kty = 'invalid'

const plainTextData = 'data'
const plainTextAad = 'meta'
const encryptedData = {"iv":"RH9i_J861XN7qvgHYZ86ag","ciphertext":"qkrkiw","tag":"kqfdLgy8qopnzeKmC5JwQA"}
const encryptedDataWithAad = {"iv":"zrXJWOthT2tnFErPhWCrfw","ciphertext":"JWwKBg","tag":"txl7BQK4fxEP5cie2OQEZA","aad":"bWV0YQ"}
const signedData = 'MEQCIAIqNr8-7Pozi1D-cigvEKbkP5SpKezzEEDSqM9McIV1AiBd4gioW8njOpr29Ymrvjp46q7hA7lSOjAJpdi5TjHWsg'
const ecThumbprint = '45BLsBiWcghaEf_NF70Gf5oQcYLHaAtks0C48tT5SJ4'
const rsaThumbprint = 'fXSFPtseA8Q5nzSGuHhj5mHyNCGYUmznTbqEV-oo0Fc'
const symThumbprint = '25BH4hLm8A-gw20EHx8QvfDRCt3hKhFRYz9E_2Tge2c'

/**
 * Tests
 * @ignore
 */
describe('JWK', () => {

  describe('constructor', () => {

    it('should throw if no data is passed', () => {
      expect(() => new JWK()).to.throw('Invalid JWK')
    })

    it('should parse string input', () => {
      let jwk = new JWK(ECPublicJWKString)
      jwk.should.be.an.instanceOf(JWK)
    })

    it('should throw if it fails to parse string input', () => {
      expect(() => new JWK('invalid')).to.throw('Invalid JWK JSON String')
    })

    it('should throw if conflicting alg options are passed in', () => {
      expect(() => new JWK(ECPublicJWK, { alg: 'KS256' })).to.throw('Conflicting \'alg\' option')
    })

    it('should not throw if alg passed in twice but not conflicting', () => {
      expect(() => new JWK(ECPublicJWK, { alg: 'ES256' })).to.not.throw
    })

    it('should get alg from options if not on data', () => {
      let jwk = new JWK(ECPublicJWKNoAlg, { alg: 'ES256' })
      jwk.alg.should.equal('ES256')
    })

    it('should throw if alg is not present on data or options', () => {
      expect(() => new JWK(ECPublicJWKNoAlg, {})).to.throw('Valid \'alg\' required for JWK')
    })

    it('should throw if conflicting kid options are passed in', () => {
      expect(() => new JWK(ECPublicJWK, { kid: '1' })).to.throw('Conflicting \'kid\' option')
    })

    it('should not throw if kid passed in twice but not conflicting', () => {
      expect(() => new JWK(ECPublicJWK, { kid: '2' })).to.not.throw
    })

    it('should get kid from options if not on data', () => {
      let jwk = new JWK(ECPublicJWK, { alg: 'ES256', kid: '2' })
      jwk.kid.should.equal('2')
    })

    it('should assign key thumbprint as kid if not present on data or options', () => {
      let jwk = new JWK(ECPublicJWKNoKid)
      console.log(jwk)
      jwk.kid.should.equal(ecThumbprint)
    })

    it('should copy non-standard key metadata', () => {
      let json = Object.assign({}, ECPublicJWK, { meta: 'abc' })
      let jwk = new JWK(json)
      jwk.meta.should.equal('abc')
    })
  })

  describe('importKey', () => {

    it('should return a promise', () => {
      return JWK.importKey(ECPrivateJWK).should.be.fulfilled
    })

    it('should resolve an instance of JWK', () => {
      return JWK.importKey(ECPrivateJWK).should.eventually.be.an.instanceOf(JWK)
    })

    it('should have a cryptoKey property', () => {
      return JWK.importKey(ECPrivateJWK).then(jwk => {
        jwk.should.haveOwnProperty('cryptoKey')
      })
    })
  })

  describe('fromCryptoKey', () => {
    let cryptoKey

    before(() => {
      return JWK.importKey(ECPrivateJWK).then(jwk => {
        cryptoKey = jwk.cryptoKey
      })
    })

    it('should return a promise', () => {
      return JWK.fromCryptoKey(cryptoKey, { alg: 'EC256', kid: 'abcd123$' }).should.eventually.be.an.instanceOf(JWK)
    })

    it('should resolve an instance of JWK', () => {
      return JWK.fromCryptoKey(cryptoKey, { alg: 'EC256', kid: 'abcd123$' }).should.be.fulfilled
    })

    it('should have a cryptoKey property', () => {
      return JWK.fromCryptoKey(cryptoKey, { alg: 'EC256', kid: 'abcd123$' }).then(jwk => {
        jwk.should.haveOwnProperty('cryptoKey')
      })
    })
  })

  describe('sign', () => {
    let jwk

    before(() => {
      return JWK.importKey(ECPrivateJWK).then(key => jwk = key)
    })

    it('should return a promise', () => {
      return jwk.sign(plainTextData).should.be.fulfilled
    })

    it('should resolve a string', () => {
      return jwk.sign(plainTextData).should.eventually.be.a('string')
    })
  })

  describe('verify', () => {
    let jwk

    before(() => {
      return JWK.importKey(ECPublicJWK).then(key => jwk = key)
    })

    it('should return a promise', () => {
      return jwk.verify(plainTextData, signedData).should.be.fulfilled
    })

    it('should resolve a boolean', () => {
      return jwk.verify(plainTextData, signedData).should.eventually.be.true
    })

    it('should fail to verify the signature with incorrect data', () => {
      return jwk.verify(plainTextData + 'a', signedData).should.eventually.be.false
    })
  })

  describe('encrypt', () => {
    let jwk

    before(() => {
      return JWK.importKey(A256GCMJWK).then(key => jwk = key)
    })

    it('should return a promise', () => {
      return jwk.encrypt(plainTextData, Buffer.from('')).should.be.fulfilled
    })

    it('should resolve encrypted data', () => {
      return jwk.encrypt(plainTextData, Buffer.from('')).then(data => {
        data.should.haveOwnProperty('ciphertext')
        data.should.haveOwnProperty('iv')
        data.should.haveOwnProperty('tag')
        data.should.not.haveOwnProperty('aad')
      })
    })

    describe('with aad', () => {

      it('should resolve encrypted data', () => {
        return jwk.encrypt(plainTextData, plainTextAad).then(data => {
          data.should.haveOwnProperty('ciphertext')
          data.should.haveOwnProperty('iv')
          data.should.haveOwnProperty('tag')
          data.should.haveOwnProperty('aad')
        })
      })
    })
  })

  describe('decrypt', () => {
    let jwk

    before(() => {
      return JWK.importKey(A256GCMJWK).then(key => jwk = key)
    })

    it('should return a promise', () => {
      let { ciphertext, iv, tag } = encryptedData
      return jwk.decrypt(ciphertext, iv, tag).should.be.fulfilled
    })

    it('should resolve plain text data', () => {
      let { ciphertext, iv, tag } = encryptedData
      return jwk.decrypt(ciphertext, iv, tag).should.eventually.equal(plainTextData)
    })

    describe('with aad', () => {

      it('should reject if aad is omitted', () => {
        let { ciphertext, iv, tag } = encryptedDataWithAad
        return jwk.decrypt(ciphertext, iv, tag).should.be.rejected
      })

      it('should resolve plain text data', () => {
        let { ciphertext, iv, tag, aad } = encryptedDataWithAad
        return jwk.decrypt(ciphertext, iv, tag, aad).should.eventually.equal(plainTextData)
      })
    })
  })

  describe('thumbprint', () => {
    let ec, rsa, sym, kty, inv

    before(() => {
      return Promise.all([
        JWK.importKey(ECPublicJWK).then(key => ec = key),
        JWK.importKey(RSAPublicJWK).then(key => rsa = key),
        JWK.importKey(A256GCMJWK).then(key => sym = key),
        Promise.resolve(new JWK(ECPublicJWKNoKty)).then(key => kty = key),
        Promise.resolve(new JWK(ECPublicJWKInvalidKty)).then(key => inv = key)
      ])
    })

    it('should return a string', () => {
      rsa.thumbprint().should.be.a('string')
    })

    it('should produce a thumbprint for RSA keys', () => {
      rsa.thumbprint().should.equal(rsaThumbprint)
    })

    it('should produce a thumbprint for EC keys', () => {
      ec.thumbprint().should.equal(ecThumbprint)
    })

    it('should produce a thumbprint for symmetric keys', () => {
      sym.thumbprint().should.equal(symThumbprint)
    })

    it('should throw if the JWK does not have a kty', () => {
      expect(() => kty.thumbprint()).to.throw('Invalid \'kty\'')
    })

    it('should throw if the kty is not valid', () => {
      expect(() => inv.thumbprint()).to.throw('Invalid \'kty\'')
    })
  })

  describe('generateProtected', () => {
    let ec, ecPub, rsa, sym, noAlg, noKid, noOps, useEc
    let jkuParams = { jku: 'https://example.com/jwks' }
    let jwcParams = { jwc: 'compact serialization jwc' }

    before(() => {
      return Promise.all([
        JWK.importKey(ECPrivateJWK).then(key => ec = key),
        JWK.importKey(ECPublicJWK).then(key => ecPub = key),
        JWK.importKey(RSAPrivateJWK).then(key => rsa = key),
        JWK.importKey(A256GCMJWK).then(key => sym = key),
        Promise.resolve(new JWK(ECPrivateJWK)).then(key => { delete key.alg; noAlg = key }),
        Promise.resolve(new JWK(ECPrivateJWK)).then(key => { delete key.kid; noKid = key }),
        JWK.importKey(ECPrivateJWK).then(key => { delete key.key_ops; noOps = key }),
        JWK.importKey(ECPrivateJWK).then(key => { delete key.key_ops; key.use = 'sig'; useEc = key }),
      ])
    })

    it('should return an object', () => {
      let header = ec.generateProtected(jkuParams)
      header.should.be.an('object')
      expect(header).to.not.be.null
    })

    it('should throw if `key_ops` does not contain \'sign\' and `use` is not \'sig\'', () => {
      expect(() => sym.generateProtected(jkuParams)).to.throw('Invalid key usage option')
      expect(() => noOps.generateProtected(jkuParams)).to.throw('Invalid key usage option')
    })

    it('should accept either `key_ops` or `use`', () => {
      ec.generateProtected(jkuParams)
      ec.key_ops.should.include('sign')
      expect(ec.use).to.be.undefined
      useEc.generateProtected(jkuParams)
      useEc.use.should.equal('sig')
      expect(useEc.key_ops).to.be.undefined
    })

    it('should contain an \'alg\'', () => {
      ec.generateProtected(jkuParams)
        .alg.should.equal(ec.alg)
    })

    it('should throw if \'alg\' is omitted', () => {
      expect(() => noAlg.generateProtected(jkuParams)).to.throw('\'alg\' is required')
    })

    it('should contain a \'kid\'', () => {
      ec.generateProtected(jkuParams)
        .kid.should.equal(ec.kid)
    })

    it('should throw if \'kid\' is omitted', () => {
      expect(() => noKid.generateProtected(jkuParams)).to.throw('\'kid\' is required')
    })

    it('should contain a \'jku\' or a \'jwc\'', () => {
      ec.generateProtected(jkuParams)
        .jku.should.equal(jkuParams.jku)
      ec.generateProtected(jwcParams)
        .jwc.should.equal(jwcParams.jwc)
    })

    it('should throw if \'jku\' and \'jwc\' are omitted', () => {
      expect(() => ec.generateProtected()).to.throw('Either \'jku\' or \'jwc\' is required')
    })
  })
})
<commit_msg>chore(JWKSpec): remove stray console.log<commit_after>'use strict'

/**
 * Test dependencies
 * @ignore
 */
const cwd = process.cwd()
const path = require('path')
const fs = require('fs')
const chai = require('chai')
const chaiAsPromised = require('chai-as-promised')
const sinon = require('sinon')
const sinonChai = require('sinon-chai')

/**
 * Assertions
 * @ignore
 */
chai.use(chaiAsPromised)
chai.should()
chai.use(sinonChai)
const expect = chai.expect

/**
 * Code Under Test
 * @ignore
 */
const { JWK } = require('../src')
const { DataError, OperationError } = require('../src/errors')

/**
 * Test Data
 * @ignore
 */
const { ECPrivateJWK, ECPublicJWK, A256GCMJWK, RSAPrivateJWK, RSAPublicJWK } = require('./keys')

const ECPublicJWKString = fs.readFileSync(path.join(cwd, 'test', 'file_import', 'fileImportJWKTestData.json'), 'utf8')
const InvalidJWKString = fs.readFileSync(path.join(cwd, 'test', 'file_import', 'fileImportJWKTestData.json'), 'utf8')

const ECPublicJWKNoAlg = Object.assign({}, ECPublicJWK)
delete ECPublicJWKNoAlg.alg

const ECPublicJWKNoKid = Object.assign({}, ECPublicJWK)
delete ECPublicJWKNoKid.kid

const ECPublicJWKNoKty = Object.assign({}, ECPublicJWK)
delete ECPublicJWKNoKty.kty

const ECPublicJWKInvalidKty = Object.assign({}, ECPublicJWK)
ECPublicJWKInvalidKty.kty = 'invalid'

const plainTextData = 'data'
const plainTextAad = 'meta'
const encryptedData = {"iv":"RH9i_J861XN7qvgHYZ86ag","ciphertext":"qkrkiw","tag":"kqfdLgy8qopnzeKmC5JwQA"}
const encryptedDataWithAad = {"iv":"zrXJWOthT2tnFErPhWCrfw","ciphertext":"JWwKBg","tag":"txl7BQK4fxEP5cie2OQEZA","aad":"bWV0YQ"}
const signedData = 'MEQCIAIqNr8-7Pozi1D-cigvEKbkP5SpKezzEEDSqM9McIV1AiBd4gioW8njOpr29Ymrvjp46q7hA7lSOjAJpdi5TjHWsg'
const ecThumbprint = '45BLsBiWcghaEf_NF70Gf5oQcYLHaAtks0C48tT5SJ4'
const rsaThumbprint = 'fXSFPtseA8Q5nzSGuHhj5mHyNCGYUmznTbqEV-oo0Fc'
const symThumbprint = '25BH4hLm8A-gw20EHx8QvfDRCt3hKhFRYz9E_2Tge2c'

/**
 * Tests
 * @ignore
 */
describe('JWK', () => {

  describe('constructor', () => {

    it('should throw if no data is passed', () => {
      expect(() => new JWK()).to.throw('Invalid JWK')
    })

    it('should parse string input', () => {
      let jwk = new JWK(ECPublicJWKString)
      jwk.should.be.an.instanceOf(JWK)
    })

    it('should throw if it fails to parse string input', () => {
      expect(() => new JWK('invalid')).to.throw('Invalid JWK JSON String')
    })

    it('should throw if conflicting alg options are passed in', () => {
      expect(() => new JWK(ECPublicJWK, { alg: 'KS256' })).to.throw('Conflicting \'alg\' option')
    })

    it('should not throw if alg passed in twice but not conflicting', () => {
      expect(() => new JWK(ECPublicJWK, { alg: 'ES256' })).to.not.throw
    })

    it('should get alg from options if not on data', () => {
      let jwk = new JWK(ECPublicJWKNoAlg, { alg: 'ES256' })
      jwk.alg.should.equal('ES256')
    })

    it('should throw if alg is not present on data or options', () => {
      expect(() => new JWK(ECPublicJWKNoAlg, {})).to.throw('Valid \'alg\' required for JWK')
    })

    it('should throw if conflicting kid options are passed in', () => {
      expect(() => new JWK(ECPublicJWK, { kid: '1' })).to.throw('Conflicting \'kid\' option')
    })

    it('should not throw if kid passed in twice but not conflicting', () => {
      expect(() => new JWK(ECPublicJWK, { kid: '2' })).to.not.throw
    })

    it('should get kid from options if not on data', () => {
      let jwk = new JWK(ECPublicJWK, { alg: 'ES256', kid: '2' })
      jwk.kid.should.equal('2')
    })

    it('should assign key thumbprint as kid if not present on data or options', () => {
      let jwk = new JWK(ECPublicJWKNoKid)
      jwk.kid.should.equal(ecThumbprint)
    })

    it('should copy non-standard key metadata', () => {
      let json = Object.assign({}, ECPublicJWK, { meta: 'abc' })
      let jwk = new JWK(json)
      jwk.meta.should.equal('abc')
    })
  })

  describe('importKey', () => {

    it('should return a promise', () => {
      return JWK.importKey(ECPrivateJWK).should.be.fulfilled
    })

    it('should resolve an instance of JWK', () => {
      return JWK.importKey(ECPrivateJWK).should.eventually.be.an.instanceOf(JWK)
    })

    it('should have a cryptoKey property', () => {
      return JWK.importKey(ECPrivateJWK).then(jwk => {
        jwk.should.haveOwnProperty('cryptoKey')
      })
    })
  })

  describe('fromCryptoKey', () => {
    let cryptoKey

    before(() => {
      return JWK.importKey(ECPrivateJWK).then(jwk => {
        cryptoKey = jwk.cryptoKey
      })
    })

    it('should return a promise', () => {
      return JWK.fromCryptoKey(cryptoKey, { alg: 'EC256', kid: 'abcd123$' }).should.eventually.be.an.instanceOf(JWK)
    })

    it('should resolve an instance of JWK', () => {
      return JWK.fromCryptoKey(cryptoKey, { alg: 'EC256', kid: 'abcd123$' }).should.be.fulfilled
    })

    it('should have a cryptoKey property', () => {
      return JWK.fromCryptoKey(cryptoKey, { alg: 'EC256', kid: 'abcd123$' }).then(jwk => {
        jwk.should.haveOwnProperty('cryptoKey')
      })
    })
  })

  describe('sign', () => {
    let jwk

    before(() => {
      return JWK.importKey(ECPrivateJWK).then(key => jwk = key)
    })

    it('should return a promise', () => {
      return jwk.sign(plainTextData).should.be.fulfilled
    })

    it('should resolve a string', () => {
      return jwk.sign(plainTextData).should.eventually.be.a('string')
    })
  })

  describe('verify', () => {
    let jwk

    before(() => {
      return JWK.importKey(ECPublicJWK).then(key => jwk = key)
    })

    it('should return a promise', () => {
      return jwk.verify(plainTextData, signedData).should.be.fulfilled
    })

    it('should resolve a boolean', () => {
      return jwk.verify(plainTextData, signedData).should.eventually.be.true
    })

    it('should fail to verify the signature with incorrect data', () => {
      return jwk.verify(plainTextData + 'a', signedData).should.eventually.be.false
    })
  })

  describe('encrypt', () => {
    let jwk

    before(() => {
      return JWK.importKey(A256GCMJWK).then(key => jwk = key)
    })

    it('should return a promise', () => {
      return jwk.encrypt(plainTextData, Buffer.from('')).should.be.fulfilled
    })

    it('should resolve encrypted data', () => {
      return jwk.encrypt(plainTextData, Buffer.from('')).then(data => {
        data.should.haveOwnProperty('ciphertext')
        data.should.haveOwnProperty('iv')
        data.should.haveOwnProperty('tag')
        data.should.not.haveOwnProperty('aad')
      })
    })

    describe('with aad', () => {

      it('should resolve encrypted data', () => {
        return jwk.encrypt(plainTextData, plainTextAad).then(data => {
          data.should.haveOwnProperty('ciphertext')
          data.should.haveOwnProperty('iv')
          data.should.haveOwnProperty('tag')
          data.should.haveOwnProperty('aad')
        })
      })
    })
  })

  describe('decrypt', () => {
    let jwk

    before(() => {
      return JWK.importKey(A256GCMJWK).then(key => jwk = key)
    })

    it('should return a promise', () => {
      let { ciphertext, iv, tag } = encryptedData
      return jwk.decrypt(ciphertext, iv, tag).should.be.fulfilled
    })

    it('should resolve plain text data', () => {
      let { ciphertext, iv, tag } = encryptedData
      return jwk.decrypt(ciphertext, iv, tag).should.eventually.equal(plainTextData)
    })

    describe('with aad', () => {

      it('should reject if aad is omitted', () => {
        let { ciphertext, iv, tag } = encryptedDataWithAad
        return jwk.decrypt(ciphertext, iv, tag).should.be.rejected
      })

      it('should resolve plain text data', () => {
        let { ciphertext, iv, tag, aad } = encryptedDataWithAad
        return jwk.decrypt(ciphertext, iv, tag, aad).should.eventually.equal(plainTextData)
      })
    })
  })

  describe('thumbprint', () => {
    let ec, rsa, sym, kty, inv

    before(() => {
      return Promise.all([
        JWK.importKey(ECPublicJWK).then(key => ec = key),
        JWK.importKey(RSAPublicJWK).then(key => rsa = key),
        JWK.importKey(A256GCMJWK).then(key => sym = key),
        Promise.resolve(new JWK(ECPublicJWKNoKty)).then(key => kty = key),
        Promise.resolve(new JWK(ECPublicJWKInvalidKty)).then(key => inv = key)
      ])
    })

    it('should return a string', () => {
      rsa.thumbprint().should.be.a('string')
    })

    it('should produce a thumbprint for RSA keys', () => {
      rsa.thumbprint().should.equal(rsaThumbprint)
    })

    it('should produce a thumbprint for EC keys', () => {
      ec.thumbprint().should.equal(ecThumbprint)
    })

    it('should produce a thumbprint for symmetric keys', () => {
      sym.thumbprint().should.equal(symThumbprint)
    })

    it('should throw if the JWK does not have a kty', () => {
      expect(() => kty.thumbprint()).to.throw('Invalid \'kty\'')
    })

    it('should throw if the kty is not valid', () => {
      expect(() => inv.thumbprint()).to.throw('Invalid \'kty\'')
    })
  })

  describe('generateProtected', () => {
    let ec, ecPub, rsa, sym, noAlg, noKid, noOps, useEc
    let jkuParams = { jku: 'https://example.com/jwks' }
    let jwcParams = { jwc: 'compact serialization jwc' }

    before(() => {
      return Promise.all([
        JWK.importKey(ECPrivateJWK).then(key => ec = key),
        JWK.importKey(ECPublicJWK).then(key => ecPub = key),
        JWK.importKey(RSAPrivateJWK).then(key => rsa = key),
        JWK.importKey(A256GCMJWK).then(key => sym = key),
        Promise.resolve(new JWK(ECPrivateJWK)).then(key => { delete key.alg; noAlg = key }),
        Promise.resolve(new JWK(ECPrivateJWK)).then(key => { delete key.kid; noKid = key }),
        JWK.importKey(ECPrivateJWK).then(key => { delete key.key_ops; noOps = key }),
        JWK.importKey(ECPrivateJWK).then(key => { delete key.key_ops; key.use = 'sig'; useEc = key }),
      ])
    })

    it('should return an object', () => {
      let header = ec.generateProtected(jkuParams)
      header.should.be.an('object')
      expect(header).to.not.be.null
    })

    it('should throw if `key_ops` does not contain \'sign\' and `use` is not \'sig\'', () => {
      expect(() => sym.generateProtected(jkuParams)).to.throw('Invalid key usage option')
      expect(() => noOps.generateProtected(jkuParams)).to.throw('Invalid key usage option')
    })

    it('should accept either `key_ops` or `use`', () => {
      ec.generateProtected(jkuParams)
      ec.key_ops.should.include('sign')
      expect(ec.use).to.be.undefined
      useEc.generateProtected(jkuParams)
      useEc.use.should.equal('sig')
      expect(useEc.key_ops).to.be.undefined
    })

    it('should contain an \'alg\'', () => {
      ec.generateProtected(jkuParams)
        .alg.should.equal(ec.alg)
    })

    it('should throw if \'alg\' is omitted', () => {
      expect(() => noAlg.generateProtected(jkuParams)).to.throw('\'alg\' is required')
    })

    it('should contain a \'kid\'', () => {
      ec.generateProtected(jkuParams)
        .kid.should.equal(ec.kid)
    })

    it('should throw if \'kid\' is omitted', () => {
      expect(() => noKid.generateProtected(jkuParams)).to.throw('\'kid\' is required')
    })

    it('should contain a \'jku\' or a \'jwc\'', () => {
      ec.generateProtected(jkuParams)
        .jku.should.equal(jkuParams.jku)
      ec.generateProtected(jwcParams)
        .jwc.should.equal(jwcParams.jwc)
    })

    it('should throw if \'jku\' and \'jwc\' are omitted', () => {
      expect(() => ec.generateProtected()).to.throw('Either \'jku\' or \'jwc\' is required')
    })
  })
})
<|endoftext|>