Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

yeoman-generator

Package Overview
Dependencies
Maintainers
5
Versions
168
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

yeoman-generator - npm Package Compare versions

Comparing version 0.14.2 to 0.15.0-pre.1

benchmark/env.js

29

lib/actions/actions.js

@@ -1,4 +0,1 @@

var logger = process.logging || require('../util/log');
var log = logger('generators:action');
var fs = require('fs');

@@ -8,3 +5,3 @@ var path = require('path');

var mkdirp = require('mkdirp');
var isBinaryFile = require('isbinaryfile');
var isBinaryFile = require('isbinaryfile').isBinaryFileSync;
var rimraf = require('rimraf');

@@ -14,7 +11,8 @@ var async = require('async');

var chalk = require('chalk');
var file = require('file-utils');
var actions = module.exports;
actions.log = log;
/**

@@ -154,3 +152,3 @@ * Stores and return the cache root for this class. The cache root is used to

log.create(file.destination);
this.log.create(file.destination);
return this;

@@ -475,2 +473,21 @@ };

var noop = function () { };
var fileLogger = { write: noop, warn: noop };
// Set the file-utils environments
// Set logger as a noop as logging is handled by the yeoman conflicter
remote.src = file.createEnv({
base: cache,
dest: self.destinationRoot(),
logger: fileLogger
});
remote.dest = file.createEnv({
base: self.destinationRoot(),
dest: cache,
logger: fileLogger
});
remote.dest.registerValidationFilter('collision', self.getCollisionFilter());
remote.src.registerValidationFilter('collision', self.getCollisionFilter());
cb(err, remote, files);

@@ -477,0 +494,0 @@ }

@@ -0,0 +0,0 @@ var fs = require('fs');

@@ -0,0 +0,0 @@ var fs = require('fs');

@@ -0,0 +0,0 @@ var _ = require('lodash');

@@ -0,0 +0,0 @@ var path = require('path');

@@ -0,0 +0,0 @@ 'use strict';

@@ -0,1 +1,2 @@

var _ = require('lodash');
var spawn = require('child_process').spawn;

@@ -11,8 +12,9 @@ var win32 = process.platform === 'win32';

module.exports = function spawnCommand(command, args) {
module.exports = function spawnCommand(command, args, opt) {
var winCommand = win32 ? 'cmd' : command;
var winArgs = win32 ? ['/c'].concat(command, args) : args;
opt = opt || {};
return spawn(winCommand, winArgs, { stdio: 'inherit' });
return spawn(winCommand, winArgs, _.defaults({ stdio: 'inherit' }, opt));
};

@@ -0,0 +0,0 @@ var _ = require('lodash');

@@ -0,0 +0,0 @@ var shell = require('shelljs');

@@ -0,0 +0,0 @@ var util = require('util');

@@ -12,3 +12,3 @@ var fs = require('fs');

var engines = require('./util/engines');
var conflicter = require('./util/conflicter');
var Conflicter = require('./util/conflicter');
var Storage = require('./util/storage');

@@ -75,5 +75,9 @@

this.conflicter = conflicter;
this.conflicter = new Conflicter(this.env.adapter);
this.conflicter.force = this.options.force;
// Since log is both a function and an object we need to use Object.defineProperty
// instead of this.env.adapter.log.apply o similar approaches
this.log = this.env.adapter.log;
// determine the app root

@@ -130,3 +134,8 @@ var rootPath = findup('.yo-rc.json');

Base.prototype.shell = require('shelljs');
Base.prototype.prompt = require('./actions/prompt');
Base.prototype.prompt = function (questions, callback) {
this.env.adapter.prompt(questions, callback);
return this;
};
Base.prototype.invoke = require('./actions/invoke');

@@ -594,3 +603,3 @@ Base.prototype.spawnCommand = require('./actions/spawn_command');

o.defaults == null ? '' : 'Default: ' + o.defaults
];
];
});

@@ -597,0 +606,0 @@

@@ -8,3 +8,2 @@ var fs = require('fs');

var _ = require('lodash');
var log = process.logging('generators');

@@ -16,10 +15,5 @@ var debug = require('debug')('generators:environment');

var resolver = require('./resolver');
var TerminalAdapter = require('./adapter');
// TODO(mklabs):
//
// - path manipulation methods ({append,prepend}{Path,Lookup}) can be dryed out.
// - if it gets too huge (more than 200loc), split this into mulitple mixins.
// - register() method too long, split logic for namespace etc. elsewhere
// - flesh out Error handling, consider emitting error instead of throwing,
// even for synchronous operation.
var win32 = process.platform === 'win32';

@@ -37,7 +31,12 @@ /**

*
* An optional adapter can be passed to provide interaction in non-CLI environment
* (e.g. IDE plugins), otherwise a `TerminalAdapter` is instantiated by default
*
*
* @param {String|Array} args
* @param {Object} opts
* @param {Adapter} adaper
*/
var Environment = module.exports = function Environment(args, opts) {
var Environment = module.exports = function Environment(args, opts, adapter) {
events.EventEmitter.call(this);

@@ -48,31 +47,11 @@

this.options = opts || {};
this.adapter = adapter || new TerminalAdapter();
this.cwd = this.options.cwd || process.cwd();
this.store = new Store();
this.lookups = ['.', 'generators', 'lib/generators'];
this.aliases = [];
this.lookups = [];
this.paths = [];
this.appendLookup('.');
this.appendLookup('generators');
// DEPRECATED: use the path `generators` instead
this.appendLookup('lib/generators');
this.appendPath('.');
// adds support for global generators. ensures support across all OS since the
// global node_modules dir always is five levels up from env/index.js
this.appendPath(path.join(__dirname, '../../../../..'));
// adds support for generator resolving when yeoman-generator has been linked
this.appendPath(path.join(path.dirname(process.argv[1]), '../..'));
this._prefixReg = null;
this._prefixes = this._prefixes || [];
this._suffix = this._suffix || '';
this.prefix(this.options.prefix || 'generator-');
this.suffix(this.options.suffix || '*/index.js');
this.plugins('node_modules');
this.appendDefaultPaths();
};

@@ -113,3 +92,3 @@

this.paths.push(filepath);
this.paths.push(path.resolve(filepath));
return this;

@@ -119,16 +98,28 @@ };

/**
* Appends a new `filepath` to the list of lookups path. This should be a
* relative filepath, like `support/scaffold`. Environments are created with
* `lib/generators` as a lookup path by default.
*
* @param {String} filepath
* Appends the defaults node modules paths to the Environment load paths.
*/
Environment.prototype.appendLookup = function appendLookup(filepath) {
if (!filepath) {
return this.error(new Error('Missing filepath.'));
Environment.prototype.appendDefaultPaths = function() {
this.appendPath(process.cwd());
// We tried using NPM to get the global modules path, but it haven't work out
// because of bugs in the parseable implementation of `ls` command and mostly
// performance issues. So, we go with our best bet for now.
if (process.env.NODE_PATH) {
process.env.NODE_PATH.split(/[,;]/g).forEach(this.appendPath, this);
return;
}
this.lookups.push(filepath);
return this;
// global node_modules should be 5 directory up this one (most of the time)
this.appendPath(path.join(__dirname, '../../../../..'));
// adds support for generator resolving when yeoman-generator has been linked
this.appendPath(path.join(path.dirname(process.argv[1]), '../..'));
// Default paths for each system
if (win32) {
this.appendPath(path.join(process.env.APPDATA, 'npm/node_modules'));
} else {
this.appendPath('/usr/lib/node_modules');
}
};

@@ -230,19 +221,2 @@

function isRaw(generator) {
generator = Object.getPrototypeOf(generator.prototype);
var methods = ['option', 'argument', 'hookFor', 'run'];
return methods.filter(function (method) {
return !!generator[method];
}).length !== methods.length;
}
if (isRaw(Generator)) {
var newGenerator = function () {
Base.apply(this, arguments);
};
util.inherits(newGenerator, Base);
newGenerator.prototype.exec = Generator;
Generator = newGenerator;
}
this.store.add(namespace, Generator);

@@ -423,13 +397,7 @@

var self = this;
// cleanup extension and normalize path for differents OS
var ns = path.normalize(filepath.replace(path.extname(filepath), ''));
// extend every lookups folders with searchable file system paths
var lookups = _(this.lookups).map(function (lookup) {
return _.map(self.paths, function (filepath) {
return path.join(filepath, lookup);
});
}).flatten().sortBy('length').value().reverse();
// Sort lookups by length so biggest are removed first
var lookups = _(this.lookups).map(path.normalize).sortBy('length').value().reverse();

@@ -443,3 +411,3 @@ // if `ns` contain a lookup dir in it's path, remove it.

ns = ns
.replace(/[\/\\]?node_modules[\/\\]?/, '') // remove `/node_modules/`
.replace(/(.*generator-)/, '') // remove before `generator-`
.replace(/[\/\\](index|main)$/, '') // remove `/index` or `/main`

@@ -450,15 +418,2 @@ .replace(/\.+/g, '') // remove `.`

// if we still have prefix match at this point, then remove anything before
// that match, this would catch symlinked package with a name begining with
// `generator-*` (one of the configured prefix)
ns = this._prefixes.reduce(function (ns, prefix) {
var pos = ns.lastIndexOf(prefix);
if (pos < 0) {
return ns;
}
return ns.slice(pos + prefix.length);
}, ns);
debug('Resolve namespaces for %s: %s', filepath, ns);

@@ -465,0 +420,0 @@

@@ -5,82 +5,74 @@ var path = require('path');

var glob = require('glob');
var findup = require('findup-sync');
var debug = require('debug')('generators:environment');
/**
* Receives namespaces in an array and tries to find matching generators in the
* load paths.
* Search for generators and their sub generators.
*
* We lookup namespaces in several places, namely `this.lookups`
* list of relatives directory path. A `generator-` prefix is added if a
* namespace wasn't `require()`-able directly, matching `generator-*` kind of
* pattern in npm installed package.
* A generator is a `:lookup/:name/index.js` file placed inside an NPM module.
*
* You can also lookup using glob-like star pattern, eg. `angular:*` gets
* expanded to `angular\*\index.js`.
* Defaults lookups are:
* - ./
* - generators/
* - lib/generators/
*
* The default alias `generator-$1` lookup is added automatically.
*
* ### Examples:
*
* // search for all angular generators in the load path
* env.lookup('angular:*');
*
* // register any valid set of generator in the load paths
* env.lookup('*:*');
*
* @param {String|Array} namespaces
* @param {String} lookupdir
* So this index file `node_modules/generator-dummy/lib/generators/yo/index.js` would be
* registered as `dummy:yo` generator.
*/
exports.lookup = function lookup(namespaces, lookupdir) {
namespaces = Array.isArray(namespaces) ? namespaces : namespaces.split(' ');
exports.lookup = function () {
var generatorsModules = this._getNpmGenerators();
var patterns = [];
debug('Lookup %s', namespaces);
namespaces.forEach(function (ns) {
var filepath = path.join.apply(path, this.alias(ns).split(':'));
this.lookups.forEach(function (lookup) {
generatorsModules.forEach(function (modulePath) {
patterns.push(path.join(modulePath, lookup) + '/*/index.js');
});
});
this.paths.forEach(function (base) {
debug('Looking in %s with filepath %s', base, filepath);
patterns.forEach(function (pattern) {
glob.sync(pattern).forEach(function (filename) {
this._tryRegistering(filename);
}, this);
}, this);
};
// no glob pattern
if (!~filepath.indexOf('*')) {
try {
debug('Attempt to register with direct filepath %s', filepath);
this.register(filepath);
} catch (e) {
// silent fail unless not a loadpath error
if (e.message.indexOf(filepath) === -1) {
console.error('Unable to register %s (Error: %s)', ns, e.message);
}
}
/**
* Search NPM for every available generators.
* Generators are NPM modules who's name start with `generator-` and who're placed in the
* top level `node_module` path. They can be installed globally or locally.
*
* @return {Array} List of the generators path
*/
return;
}
this.lookups.forEach(function (lookupdir) {
var depth = lookupdir && /^\.\/?$/.test(lookupdir) ? '*' : '**';
var prefixes = this._prefixes.filter(function (prefix) {
return !(/\//).test(prefix);
exports._getNpmGenerators = function () {
var modules = [];
this.paths
.map(function (root) {
return findup('node_modules/', { cwd: root });
})
.forEach(function (root) {
if (!root) return;
var found = glob.sync('generator-*', { cwd: root, stat: true })
.map(function( match ) {
return fs.realpathSync(path.join(root, match));
});
modules = modules.concat(found);
});
var pattern = filepath
.replace(/^\*+/, '+(' + prefixes.join('|') + ')*')
.replace(/\*+$/g, path.join(lookupdir, depth, 'index.js'))
.replace(/^\*\//, '');
return modules;
};
debug('Globing for generator %s with pattern %s (cwd: %s)', ns, pattern, base);
glob.sync(pattern, { cwd: base }).forEach(function (filename) {
// now register, warn on failed require
try {
debug('found %s, trying to register', filename);
this.register(path.resolve(base, filename));
} catch (e) {
console.error('Unable to register %s (Error: %s)', filename, e.message);
}
}, this);
}, this);
}, this);
}, this);
/**
* Try registering a Generator to this environment.
* @param {String} generatorReference A generator reference, usually a file path.
*/
return this;
exports._tryRegistering = function (generatorReference) {
try {
debug('found %s, trying to register', generatorReference);
this.register(generatorReference);
} catch (e) {
console.error('Unable to register %s (Error: %s)', generatorReference, e.message);
}
};

@@ -141,75 +133,1 @@

};
/**
* Adds the namespace prefix to this environment, such as `generator-*`,
* used when resolving namespace, replacing the leading `*` in the
* namespace by the configured prefix(es).
*
* ### Examples:
*
* this.prefix('generator-');
*
* @param {String} prefix
*/
exports.prefix = function _prefix(prefix) {
if (!prefix) {
throw new Error('Missing prefix');
}
this._prefixes.push(prefix);
this._prefixReg = new RegExp('^(' + this._prefixes.join('|') + ')');
return this;
};
/**
* Get or set the namespace suffix to this environment, such as `*\index.js`,
* used when resolving namespace, replacing the last `*` in the
* namespace by the configured suffix.
*
* ### Examples:
*
* this.suffix('*\index.js');
* this.suffix();
* // => '*\index.js'
*
* @param {String} suffix
*/
exports.suffix = function _suffix(suffix) {
this._suffix = this._suffix || '';
if (!suffix) {
return this._suffix;
}
this._suffix = suffix;
return this;
};
/**
* Walk up the filesystem looking for a `node_modules` folder, and add it if
* found to the load path.
*
* @param {String} filename
* @param {String} basedir
*/
exports.plugins = function plugins(filename, basedir) {
filename = filename || 'node_modules';
basedir = basedir || process.cwd();
var filepath = path.join(basedir, filename);
if (fs.existsSync(filepath)) {
this.appendPath(filepath);
return this;
}
if (basedir === path.resolve('/')) {
return this;
}
return this.plugins(filename, path.join(basedir, '..'));
};

@@ -0,2 +1,4 @@

var util = require('util');
var _ = require('lodash');
var Base = require('../base');

@@ -30,11 +32,10 @@ /**

Store.prototype._storeAsPath = function _storeAsPath (namespace, path) {
var meta = {
this._meta[namespace] = {
resolved: path,
namespace: namespace
};
this._meta[namespace] = meta;
Object.defineProperty(this._generators, namespace, {
get: function () {
var Generator = require(path);
return _.extend(Generator, meta);
return Generator;
},

@@ -47,8 +48,7 @@ enumerable: true,

Store.prototype._storeAsModule = function _storeAsModule (namespace, Generator) {
var meta = {
this._meta[namespace] = {
resolved: "unknown",
namespace: namespace
};
this._meta[namespace] = meta;
this._generators[namespace] = _.extend(Generator, meta);
this._generators[namespace] = Generator;
};

@@ -63,3 +63,5 @@

Store.prototype.get = function get (namespace) {
return this._generators[namespace];
var Generator = this._generators[namespace];
if (!Generator) return;
return this.normalizeGenerator(namespace, Generator);
};

@@ -81,4 +83,38 @@

Store.prototype.getGeneratorsMeta = function getGeneratorsMeta() {
Store.prototype.getGeneratorsMeta = function getGeneratorsMeta () {
return this._meta;
};
/**
* Normalize a Generator, extending it with related metadata and allowing simple
* function to be registered as generators
* @param {Generator} Generator
* @return {Generator}
*/
Store.prototype.normalizeGenerator = function normalizeGenerator (namespace, Generator) {
if (isRaw(Generator)) {
var newGenerator = function () {
Base.apply(this, arguments);
};
util.inherits(newGenerator, Base);
newGenerator.prototype.exec = Generator;
Generator = newGenerator;
}
_.extend(Generator, this._meta[namespace]);
return Generator;
};
/**
* Check if a Generator implement base Generator prototype.
* @param {Generator|Function} generator
* @return {Boolean}
*/
function isRaw(Generator) {
Generator = Object.getPrototypeOf(Generator.prototype);
var methods = ['option', 'argument', 'hookFor', 'run'];
return methods.filter(function (method) {
return !!Generator[method];
}).length !== methods.length;
}

@@ -0,0 +0,0 @@ var util = require('util');

@@ -0,0 +0,0 @@ var fs = require('fs');

@@ -0,0 +0,0 @@ var chalk = require('chalk');

@@ -1,2 +0,2 @@

var logger = process.logging || require('./log');
'use strict';

@@ -6,14 +6,16 @@ var fs = require('fs');

var events = require('events');
var diff = require('diff');
var prompt = require('../actions/prompt');
var log = logger('conflicter');
var async = require('async');
var isBinaryFile = require('isbinaryfile');
var chalk = require('chalk');
var isBinaryFile = require('isbinaryfile').isBinaryFileSync;
var util = require('util');
var conflicter = module.exports = Object.create(events.EventEmitter.prototype);
var Conflicter = module.exports = function Conflicter(adapter) {
events.EventEmitter.call(this);
this.adapter = adapter;
this.conflicts = [];
};
conflicter.conflicts = [];
util.inherits(Conflicter, events.EventEmitter);
conflicter.add = function add(conflict) {
Conflicter.prototype.add = function add(conflict) {
if (typeof conflict === 'string') {

@@ -38,3 +40,3 @@ conflict = {

conflicter.reset = function reset() {
Conflicter.prototype.reset = function reset() {
this.conflicts = [];

@@ -44,11 +46,11 @@ return this;

conflicter.pop = function pop() {
Conflicter.prototype.pop = function pop() {
return this.conflicts.pop();
};
conflicter.shift = function shift() {
Conflicter.prototype.shift = function shift() {
return this.conflicts.shift();
};
conflicter.resolve = function resolve(cb) {
Conflicter.prototype.resolve = function resolve(cb) {
var resolveConflicts = function (conflict) {

@@ -60,10 +62,10 @@ return function (next) {

conflicter.collision(conflict.file, conflict.content, function (status) {
conflicter.emit('resolved:' + conflict.file, {
this.collision(conflict.file, conflict.content, function (status) {
this.emit('resolved:' + conflict.file, {
status: status,
callback: next
});
});
};
};
}.bind(this));
}.bind(this);
}.bind(this);

@@ -73,6 +75,6 @@ async.series(this.conflicts.map(resolveConflicts), function (err) {

cb();
return self.emit('error', err);
return this.emit('error', err);
}
conflicter.reset();
this.reset();
cb();

@@ -82,3 +84,3 @@ }.bind(this));

conflicter._ask = function (filepath, content, cb) {
Conflicter.prototype._ask = function (filepath, content, cb) {
// for this particular use case, might use prompt module directly to avoid

@@ -97,3 +99,3 @@ // the additional "Are you sure?" prompt

value: function (cb) {
log.force(rfilepath);
self.adapter.log.force(rfilepath);
return cb('force');

@@ -105,3 +107,3 @@ }

value: function (cb) {
log.skip(rfilepath);
self.adapter.log.skip(rfilepath);
return cb('skip');

@@ -113,3 +115,3 @@ }

value: function (cb) {
log.force(rfilepath);
self.adapter.log.force(rfilepath);
self.force = true;

@@ -122,3 +124,3 @@ return cb('force');

value: function (cb) {
log.writeln('Aborting ...');
self.adapter.log.writeln('Aborting ...');
return process.exit(0);

@@ -130,3 +132,3 @@ }

value: function (cb) {
console.log(conflicter.diff(fs.readFileSync(filepath, 'utf8'), content));
self.diff(fs.readFileSync(filepath, 'utf8'), content);
return self._ask(filepath, content, cb);

@@ -143,3 +145,3 @@ }

prompt(config, function (result) {
this.adapter.prompt(config, function (result) {
result.overwrite(function (action) {

@@ -151,8 +153,7 @@ cb(action);

conflicter.collision = function collision(filepath, content, cb) {
var self = this;
Conflicter.prototype.collision = function collision(filepath, content, cb) {
var rfilepath = path.relative(process.cwd(), path.resolve(filepath));
if (!fs.existsSync(filepath)) {
log.create(rfilepath);
this.adapter.log.create(rfilepath);
return cb('create');

@@ -175,4 +176,4 @@ }

if ((!encoding && (actual.toString('hex') === content.toString('hex'))) ||
(actual === content)) {
log.identical(rfilepath);
(actual === content)) {
this.adapter.log.identical(rfilepath);
return cb('identical');

@@ -182,44 +183,15 @@ }

if (self.force) {
log.force(rfilepath);
if (this.force) {
this.adapter.log.force(rfilepath);
return cb('force');
}
log.conflict(rfilepath);
conflicter._ask(filepath, content, cb);
this.adapter.log.conflict(rfilepath);
this._ask(filepath, content, cb);
};
conflicter.colorDiffAdded = chalk.black.bgGreen;
conflicter.colorDiffRemoved = chalk.bgRed;
// below is borrowed code from visionmedia's excellent mocha (and its reporter)
conflicter.diff = function _diff(actual, expected) {
var msg = diff.diffLines(actual, expected).map(function (str) {
if (str.added) {
return conflicter.colorLines('Added', str.value);
}
if (str.removed) {
return conflicter.colorLines('Removed', str.value);
}
return str.value;
}).join('');
// legend
msg = '\n' +
conflicter.colorDiffRemoved('removed') +
' ' +
conflicter.colorDiffAdded('added') +
'\n\n' +
msg +
'\n';
return msg;
Conflicter.prototype.diff = function _diff(actual, expected) {
return this.adapter.diff(actual, expected);
};
conflicter.colorLines = function colorLines(name, str) {
return str.split('\n').map(function (str) {
return conflicter['colorDiff' + name](str);
}).join('\n');
};

@@ -0,0 +0,0 @@ var _ = require('lodash');

@@ -0,0 +0,0 @@ var util = require('util');

@@ -0,0 +0,0 @@ var fs = require('fs');

@@ -1,3 +0,1 @@

process.logging = process.logging || require('./lib/util/log');
// The generator system is a framework for node to author reusable and

@@ -27,5 +25,4 @@ // composable Generators, for a vast majority of use-case.

var Environment = require('./lib/env');
var generators = module.exports = function createEnv(args, opts) {
return new Environment(args, opts);
var generators = module.exports = function createEnv(args, opts, adapter) {
return new Environment(args, opts, adapter);
};

@@ -32,0 +29,0 @@

{
"name": "yeoman-generator",
"version": "0.14.2",
"version": "0.15.0-pre.1",
"description": "Rails-inspired generator system that provides scaffolding for your apps",
"license": "BSD",
"keywords": [

@@ -34,7 +35,7 @@ "development",

"underscore.string": "~2.3.1",
"lodash": "~2.2.1",
"lodash": "~2.4.1",
"mkdirp": "~0.3.5",
"glob": "~3.2.0",
"debug": "~0.7.2",
"isbinaryfile": "~0.1.8",
"isbinaryfile": "~1.0.0",
"dargs": "~0.1.0",

@@ -49,14 +50,15 @@ "async": "~0.2.8",

"download": "~0.1.6",
"request": "~2.27.0",
"request": "~2.29.0",
"file-utils": "~0.1.1"
},
"devDependencies": {
"mocha": "~1.13.0",
"mocha": "~1.15.1",
"proxyquire": "~0.5.1",
"sinon": "~1.7.3",
"markdox": "~0.1.2",
"coveralls": "~2.3.0",
"coveralls": "~2.6.0",
"mocha-lcov-reporter": "0.0.1",
"istanbul": "~0.1.44"
"istanbul": "~0.1.44",
"benchmark": "~1.0.0"
}
}

@@ -55,1 +55,7 @@ # Generator [![Build Status](https://secure.travis-ci.org/yeoman/generator.png?branch=master)](http://travis-ci.org/yeoman/generator) [![Coverage Status](https://coveralls.io/repos/yeoman/generator/badge.png)](https://coveralls.io/r/yeoman/generator)

* [Karma](https://github.com/yeoman/generator-karma#readme)
## License
[BSD license](http://opensource.org/licenses/bsd-license.php)
Copyright (c) Google
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc