Merge pull request #178 from strongloop/feature/eslint

Use eslint with loopback config
This commit is contained in:
Miroslav Bajtoš 2016-04-05 15:55:58 +02:00
commit b3e5f23865
19 changed files with 530 additions and 581 deletions

View File

@ -1,3 +1,2 @@
node_modules/
coverage/
test/sandbox/

3
.eslintrc Normal file
View File

@ -0,0 +1,3 @@
{
"extends": "loopback"
}

25
.jscsrc
View File

@ -1,25 +0,0 @@
{
"preset": "google",
"requireCurlyBraces": [
"do",
"try",
"catch"
],
"disallowMultipleVarDecl": "exceptUndefined",
"disallowSpacesInsideObjectBrackets": null,
"requireSpaceAfterLineComment": true,
"maximumLineLength": {
"value": 80,
"allowRegex": true
},
"validateJSDoc": {
"checkParamNames": false,
"checkRedundantParams": false,
"requireParamTypes": true
},
"excludeFiles": [
"node_modules/**",
"coverage/**",
"test/sandbox/**"
]
}

View File

@ -1,21 +0,0 @@
{
"node": true,
"browser": true,
"eqnull" : true,
"indent": 2,
"undef": true,
"unused": true,
"quotmark": "single",
"newcap": true,
"nonew": true,
"sub": true,
"unused": "vars",
"globals": {
"describe": false,
"it": false,
"before": false,
"beforeEach": false,
"after": false,
"afterEach": false
}
}

View File

@ -154,7 +154,7 @@ exports.compileToBrowserify = function(options, bundler) {
addInstructionsToBrowserify(compile(options), bundler);
};
/*-- undocumented low-level API --*/
/* -- undocumented low-level API -- */
exports.ConfigLoader = ConfigLoader;
exports.compile = compile;

View File

@ -103,8 +103,8 @@ module.exports = function compile(options) {
components: componentInstructions,
mixins: mixinInstructions,
files: {
boot: bootScripts
}
boot: bootScripts,
},
};
if (options.appId)
@ -215,7 +215,7 @@ function buildAllModelInstructions(rootDir, modelsConfig, sources,
name: name,
config: config,
definition: definition.definition,
sourceFile: definition.sourceFile
sourceFile: definition.sourceFile,
};
});
@ -415,7 +415,6 @@ function tryResolveAppPath(rootDir, relativePath, resolveOptions) {
try {
// NOTE(bajtos) We need to create a proper String object here,
// otherwise we can't attach additional properties to it
/*jshint -W053 */
var filePath = new String(require.resolve(absPath));
filePath.unresolvedPath = absPath;
return filePath;
@ -457,7 +456,7 @@ function loadModelDefinition(rootDir, jsonFile, allFiles) {
return {
definition: definition,
sourceFile: sourceFile
sourceFile: sourceFile,
};
}
@ -495,7 +494,7 @@ function buildMiddlewareInstructions(rootDir, config) {
var item = {
sourceFile: resolved.sourceFile,
config: middlewareConfig
config: middlewareConfig,
};
if (resolved.fragment) {
item.fragment = resolved.fragment;
@ -517,13 +516,13 @@ function buildMiddlewareInstructions(rootDir, config) {
return {
phases: flattenedPhaseNames,
middleware: middlewareList
middleware: middlewareList,
};
}
function resolveMiddlewarePath(rootDir, middleware, config) {
var resolved = {
optional: !!config.optional
optional: !!config.optional,
};
var segments = middleware.split('#');
@ -532,7 +531,7 @@ function resolveMiddlewarePath(rootDir, middleware, config) {
var middlewarePath = pathName;
var opts = {
strict: true,
optional: !!config.optional
optional: !!config.optional,
};
if (fragment) {
@ -552,7 +551,7 @@ function resolveMiddlewarePath(rootDir, middleware, config) {
// node_modules/strong-express-metrics
// instead of
// node_modules/strong-express-metrics/index.js
fullResolve: false
fullResolve: false,
});
var sourceFile = resolveAppScriptPath(rootDir, middlewarePath, resolveOpts);
@ -581,14 +580,13 @@ function resolveMiddlewarePath(rootDir, middleware, config) {
// pathName + '/' + fragment
];
var err;
var err = undefined; // see https://github.com/eslint/eslint/issues/5744
for (var ix in candidates) {
try {
resolved.sourceFile = resolveAppScriptPath(rootDir, candidates[ix], opts);
delete resolved.fragment;
return resolved;
}
catch (e) {
} catch (e) {
// Report the error for the first candidate when no candidate matches
if (!err) err = e;
}
@ -615,7 +613,7 @@ function buildComponentInstructions(rootDir, componentConfig) {
.map(function(name) {
return {
sourceFile: resolveAppScriptPath(rootDir, name, { strict: true }),
config: componentConfig[name]
config: componentConfig[name],
};
});
}
@ -635,11 +633,11 @@ function resolveRelativePaths(relativePaths, appRootDir) {
function getExcludedExtensions() {
return {
'.json': '.json',
'.node': 'node'
'.node': 'node',
};
}
function isPreferredExtension (filename) {
function isPreferredExtension(filename) {
var includeExtensions = require.extensions;
var ext = path.extname(filename);
@ -793,7 +791,9 @@ function normalizeMixinName(str, options) {
case 'classify':
str = String(str).replace(/([A-Z]+)/g, ' $1').trim();
str = String(str).replace(/[\W_]/g, ' ').toLowerCase();
str = str.replace(/(?:^|\s|-)\S/g, function(c) { return c.toUpperCase(); });
str = str.replace(/(?:^|\s|-)\S/g, function(c) {
return c.toUpperCase();
});
str = str.replace(/\s+/g, '');
return str;

View File

@ -32,7 +32,6 @@ ConfigLoader.loadDataSources = function(rootDir, env) {
* @returns {Object}
*/
ConfigLoader.loadModels = function(rootDir, env) {
/*jshint unused:false */
return loadNamed(rootDir, env, 'model-config', mergeModelConfig);
};
@ -98,7 +97,7 @@ function findConfigFiles(appRootDir, env, name) {
var candidates = [
master,
ifExistsWithAnyExt(name + '.local'),
ifExistsWithAnyExt(name + '.' + env)
ifExistsWithAnyExt(name + '.' + env),
];
return candidates.filter(function(c) { return c !== undefined; });
@ -124,7 +123,7 @@ function loadConfigFiles(files) {
var config = require(f);
Object.defineProperty(config, '_filename', {
enumerable: false,
value: f
value: f,
});
return config;
});
@ -166,7 +165,7 @@ function mergeAppConfig(target, config, fileName) {
}
function mergeMiddlewareConfig(target, config, fileName) {
var err;
var err = undefined; // see https://github.com/eslint/eslint/issues/5744
for (var phase in config) {
if (phase in target) {
err = mergePhaseConfig(target[phase], config[phase], phase);
@ -203,7 +202,7 @@ function mergeNamedItems(arr1, arr2, key) {
}
function mergePhaseConfig(target, config, phase) {
var err;
var err = undefined; // see https://github.com/eslint/eslint/issues/5744
for (var mw in config) {
if (mw in target) {
var targetMiddleware = target[mw];

View File

@ -48,15 +48,15 @@ module.exports = function execute(app, instructions, callback) {
// in the next tick of the even loop.
// See http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony
process.nextTick,
], function(err) {
app.booting = false;
], function(err) {
app.booting = false;
if (err) return callback(err);
if (err) return callback(err);
app.emit('booted');
app.emit('booted');
callback();
});
callback();
});
};
function patchAppLoopback(app) {
@ -128,7 +128,7 @@ function setPort(app, instructions) {
instructions.config.port,
process.env.npm_package_config_port,
app.get('port'),
3000
3000,
], function(p) {
return p != null;
});
@ -277,7 +277,7 @@ function runScripts(app, list, callback) {
debug('Exported function detected %s', filepath);
functions.push({
path: filepath,
func: exports
func: exports,
});
}
} catch (err) {

View File

@ -15,8 +15,9 @@
"main": "index.js",
"browser": "browser.js",
"scripts": {
"pretest": "jscs . && jshint .",
"test": "mocha"
"test": "mocha",
"posttest": "npm run lint",
"lint": "eslint ."
},
"license": "MIT",
"dependencies": {
@ -32,9 +33,9 @@
"chai": "^1.10.0",
"coffee-script": "^1.8.0",
"coffeeify": "^0.7.0",
"eslint": "^2.5.3",
"eslint-config-loopback": "^1.0.0",
"fs-extra": "^0.12.0",
"jscs": "^1.7.3",
"jshint": "^2.5.6",
"loopback": "^2.16.3",
"mocha": "^1.19.0",
"supertest": "^0.14.0"

View File

@ -22,14 +22,14 @@ describe('browser support for multiple apps', function() {
{
appDir: app1Dir,
appFile: './app.js',
moduleName: 'browser-app'
moduleName: 'browser-app',
},
{
appDir: app2Dir,
appFile: './app.js',
moduleName: 'browser-app2',
appId: 'browserApp2'
}
appId: 'browserApp2',
},
];
browserifyTestApps(apps, function(err, bundlePath) {
@ -55,7 +55,7 @@ describe('browser support for multiple apps', function() {
function browserifyTestApps(apps, next) {
var b = browserify({
debug: true
debug: true,
});
for (var i in apps) {
@ -65,13 +65,13 @@ function browserifyTestApps(apps, next) {
var appId = apps[i].appId;
appFile = path.join(appDir, appFile);
b.require(appFile, {expose: moduleName});
b.require(appFile, { expose: moduleName });
var opts = appDir;
if (appId) {
opts = {
appId: appId,
appRootDir: appDir
appRootDir: appDir,
};
}
boot.compileToBrowserify(opts, b);

View File

@ -13,7 +13,7 @@ var compileStrategies = {
'default': function(appDir) {
var b = browserify({
basedir: appDir,
debug: true
debug: true,
});
b.require('./app.js', { expose: 'browser-app' });
@ -24,7 +24,7 @@ var compileStrategies = {
var b = browserify({
basedir: appDir,
extensions: ['.coffee'],
debug: true
debug: true,
});
b.transform('coffeeify');
@ -64,7 +64,7 @@ describe('browser support', function() {
it('loads mixins', function(done) {
var appDir = path.resolve(__dirname, './fixtures/browser-app');
var options = {
appRootDir: appDir
appRootDir: appDir,
};
browserifyTestApp(options, function(err, bundlePath) {

File diff suppressed because it is too large Load Diff

View File

@ -37,22 +37,22 @@ describe('executor', function() {
host: '127.0.0.1',
restApiRoot: '/rest-api',
foo: { bar: 'bat' },
baz: true
baz: true,
},
models: [
{
name: 'User',
config: {
dataSource: 'the-db'
}
}
dataSource: 'the-db',
},
},
],
dataSources: {
'the-db': {
connector: 'memory',
defaultForType: 'db'
}
}
defaultForType: 'db',
},
},
});
describe('when booting', function() {
@ -109,9 +109,9 @@ describe('executor', function() {
name: 'Customer',
base: 'User',
},
sourceFile: path.resolve(appdir.PATH, 'models', 'Customer.js')
}
]
sourceFile: path.resolve(appdir.PATH, 'models', 'Customer.js'),
},
],
}));
expect(app.models.Customer).to.exist();
@ -127,9 +127,9 @@ describe('executor', function() {
name: 'Vehicle',
config: undefined,
definition: {
name: 'Vehicle'
name: 'Vehicle',
},
sourceFile: undefined
sourceFile: undefined,
},
{
name: 'Car',
@ -138,9 +138,9 @@ describe('executor', function() {
name: 'Car',
base: 'Vehicle',
},
sourceFile: undefined
sourceFile: undefined,
},
]
],
}));
expect(Object.keys(app.models)).to.eql(['Car']);
@ -166,15 +166,15 @@ describe('executor', function() {
name: 'Customer',
config: { dataSource: 'db' },
definition: { name: 'Customer' },
sourceFile: path.resolve(appdir.PATH, 'models', 'Customer.js')
sourceFile: path.resolve(appdir.PATH, 'models', 'Customer.js'),
},
{
name: 'UniqueName',
config: { dataSource: 'db' },
definition: { name: 'UniqueName' },
sourceFile: undefined
}
]
sourceFile: undefined,
},
],
}));
expect(app.models.Customer._modelsWhenAttached).to.include('UniqueName');
@ -188,9 +188,9 @@ describe('executor', function() {
name: 'LocalCustomer',
config: { dataSource: 'db' },
definition: { name: 'LocalCustomer' },
sourceFile: undefined
}
]
sourceFile: undefined,
},
],
}));
expect(Object.keys(loopback.registry.modelBuilder.models), 'global models')
@ -204,7 +204,7 @@ describe('executor', function() {
'require("doesnt-exist"); module.exports = {};');
function doBoot() {
boot.execute(app, someInstructions({ files: { boot: [file] } }));
boot.execute(app, someInstructions({ files: { boot: [file] }}));
}
expect(doBoot).to.throw(/Cannot find module \'doesnt-exist\'/);
@ -236,7 +236,7 @@ describe('executor', function() {
require.resolve('loopback/common/models/user.json')
),
config: { dataSource: 'db' },
sourceFile: require.resolve('loopback/common/models/user.js')
sourceFile: require.resolve('loopback/common/models/user.js'),
};
builtinModel.definition.redefined = true;
@ -260,7 +260,7 @@ describe('executor', function() {
'barLoaded',
'barSyncLoaded',
'fooLoaded',
'barStarted'
'barStarted',
]);
// bar finished happens in the next tick
@ -272,7 +272,7 @@ describe('executor', function() {
'fooLoaded',
'barStarted',
'barFinished',
'barSyncExecuted'
'barSyncExecuted',
]);
done();
}, 10);
@ -288,7 +288,7 @@ describe('executor', function() {
'fooLoaded',
'barStarted',
'barFinished',
'barSyncExecuted'
'barSyncExecuted',
]);
done();
});
@ -306,11 +306,11 @@ describe('executor', function() {
'function(Model, options) {}');
appdir.writeConfigFileSync('custom-mixins/time-stamps.json', {
name: 'Timestamping'
name: 'Timestamping',
});
options = {
appRootDir: appdir.PATH
appRootDir: appdir.PATH,
};
});
@ -357,8 +357,8 @@ describe('executor', function() {
boot.execute(app, someInstructions({
config: {
port: undefined,
host: undefined
}
host: undefined,
},
}));
}
@ -388,6 +388,7 @@ describe('executor', function() {
it('should prioritize host sources', function() {
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
/*eslint-disable camelcase*/
process.env.npm_config_host = randomHost();
process.env.OPENSHIFT_SLS_IP = randomHost();
process.env.OPENSHIFT_NODEJS_IP = randomHost();
@ -397,9 +398,11 @@ describe('executor', function() {
bootWithDefaults();
assert.equal(app.get('host'), process.env.npm_config_host);
/*eslint-enable camelcase*/
});
it('should prioritize port sources', function() {
/*eslint-disable camelcase*/
process.env.npm_config_port = randomPort();
process.env.OPENSHIFT_SLS_PORT = randomPort();
process.env.OPENSHIFT_NODEJS_PORT = randomPort();
@ -409,6 +412,7 @@ describe('executor', function() {
bootWithDefaults();
assert.equal(app.get('port'), process.env.npm_config_port);
/*eslint-enable camelcase*/
});
function randomHost() {
@ -420,19 +424,19 @@ describe('executor', function() {
}
it('should honor 0 for free port', function() {
boot.execute(app, someInstructions({ config: { port: 0 } }));
boot.execute(app, someInstructions({ config: { port: 0 }}));
assert.equal(app.get('port'), 0);
});
it('should default to port 3000', function() {
boot.execute(app, someInstructions({ config: { port: undefined } }));
boot.execute(app, someInstructions({ config: { port: undefined }}));
assert.equal(app.get('port'), 3000);
});
it('should respect named pipes port values in ENV', function() {
var NAMED_PORT = '\\.\\pipe\\test';
process.env.PORT = NAMED_PORT;
boot.execute(app, someInstructions({ config: { port: 3000 } }));
boot.execute(app, someInstructions({ config: { port: 3000 }}));
assert.equal(app.get('port'), NAMED_PORT);
});
});
@ -479,13 +483,13 @@ describe('executor', function() {
it('should parse config variables in an object', function(done) {
boot.execute(app, simpleMiddlewareConfig('routes',
{ info: { path: '${restApiRoot}' } }
{ info: { path: '${restApiRoot}' }}
));
supertest(app).get('/').end(function(err, res) {
if (err) return done(err);
expect(res.body.info).to.eql({
path: app.get('restApiRoot')
path: app.get('restApiRoot'),
});
done();
});
@ -493,13 +497,13 @@ describe('executor', function() {
it('should parse config variables in a nested object', function(done) {
boot.execute(app, simpleMiddlewareConfig('routes',
{ nested: { info: { path: '${restApiRoot}' } } }
{ nested: { info: { path: '${restApiRoot}' }}}
));
supertest(app).get('/').end(function(err, res) {
if (err) return done(err);
expect(res.body.nested).to.eql({
info: { path: app.get('restApiRoot') }
info: { path: app.get('restApiRoot') },
});
done();
});
@ -509,7 +513,7 @@ describe('executor', function() {
var invalidDataTypes = [undefined, function() {}];
async.each(invalidDataTypes, function(invalidDataType, cb) {
var config = simpleMiddlewareConfig('routes', {
path: invalidDataType
path: invalidDataType,
});
boot.execute(app, config);
@ -525,7 +529,7 @@ describe('executor', function() {
it('should parse valid config variables', function(done) {
var config = simpleMiddlewareConfig('routes', {
props: ['a', '${vVar}', 1, true, function() {}, {x:1, y: '${y}'}]
props: ['a', '${vVar}', 1, true, function() {}, { x: 1, y: '${y}' }],
});
boot.execute(app, config);
@ -552,7 +556,6 @@ describe('executor', function() {
});
describe('with component-config.json', function() {
it('should parse a simple config variable', function(done) {
boot.execute(app, simpleComponentConfig(
{ path: '${restApiRoot}' }
@ -594,13 +597,13 @@ describe('executor', function() {
it('should parse config variables in an object', function(done) {
boot.execute(app, simpleComponentConfig(
{ info: { path: '${restApiRoot}' } }
{ info: { path: '${restApiRoot}' }}
));
supertest(app).get('/component').end(function(err, res) {
if (err) return done(err);
expect(res.body.info).to.eql({
path: app.get('restApiRoot')
path: app.get('restApiRoot'),
});
done();
});
@ -608,18 +611,17 @@ describe('executor', function() {
it('should parse config variables in a nested object', function(done) {
boot.execute(app, simpleComponentConfig(
{ nested: { info: { path: '${restApiRoot}' } } }
{ nested: { info: { path: '${restApiRoot}' }}}
));
supertest(app).get('/component').end(function(err, res) {
if (err) return done(err);
expect(res.body.nested).to.eql({
info: { path: app.get('restApiRoot') }
info: { path: app.get('restApiRoot') },
});
done();
});
});
});
it('calls function exported by boot/init.js', function() {
@ -627,7 +629,7 @@ describe('executor', function() {
'module.exports = function(app) { app.fnCalled = true; };');
delete app.fnCalled;
boot.execute(app, someInstructions({ files: { boot: [file] } }));
boot.execute(app, someInstructions({ files: { boot: [file] }}));
expect(app.fnCalled, 'exported fn was called').to.be.true();
});
@ -642,33 +644,33 @@ describe('executor', function() {
sourceFile: pushNamePath,
config: {
phase: 'initial',
params: 'initial'
}
params: 'initial',
},
},
{
sourceFile: pushNamePath,
config: {
phase: 'custom',
params: 'custom'
}
params: 'custom',
},
},
{
sourceFile: pushNamePath,
config: {
phase: 'routes',
params: 'routes'
}
params: 'routes',
},
},
{
sourceFile: pushNamePath,
config: {
phase: 'routes',
enabled: false,
params: 'disabled'
}
}
]
}
params: 'disabled',
},
},
],
},
}));
supertest(app)
@ -682,7 +684,6 @@ describe('executor', function() {
});
it('configures middleware using shortform', function(done) {
boot.execute(app, someInstructions({
middleware: {
middleware: [
@ -691,11 +692,11 @@ describe('executor', function() {
fragment: 'static',
config: {
phase: 'files',
params: path.join(__dirname, './fixtures/simple-app/client/')
}
}
]
}
params: path.join(__dirname, './fixtures/simple-app/client/'),
},
},
],
},
}));
supertest(app)
@ -725,8 +726,8 @@ describe('executor', function() {
it('configures components', function() {
appdir.writeConfigFileSync('component-config.json', {
'./components/test-component': {
option: 'value'
}
option: 'value',
},
});
appdir.writeFileSync('components/test-component/index.js',
@ -743,7 +744,7 @@ describe('executor', function() {
it('disables component when configuration is not set', function() {
appdir.writeConfigFileSync('component-config.json', {
'./components/test-component': false
'./components/test-component': false,
});
appdir.writeFileSync('components/test-component/index.js',
@ -758,10 +759,10 @@ describe('executor', function() {
it('disable component if overrided by production configuration', function() {
appdir.writeConfigFileSync('component-config.json', {
'./components/test-component': {}
'./components/test-component': {},
});
appdir.writeConfigFileSync('component-config.production.json', {
'./components/test-component': null
'./components/test-component': null,
});
appdir.writeFileSync('components/test-component/index.js',
@ -785,11 +786,11 @@ describe('executor', function() {
sourceFile: passportPath,
fragment: 'initialize',
config: {
phase: 'auth:before'
}
}
]
}
phase: 'auth:before',
},
},
],
},
}));
supertest(app)
@ -808,7 +809,6 @@ describe('executor', function() {
});
});
});
});
function simpleMiddlewareConfig(phase, paths, params) {
@ -819,7 +819,7 @@ function simpleMiddlewareConfig(phase, paths, params) {
var config = {
phase: phase,
params: params
params: params,
};
if (paths) config.paths = paths;
@ -831,9 +831,9 @@ function simpleMiddlewareConfig(phase, paths, params) {
{
sourceFile: path.join(__dirname, './fixtures/simple-middleware.js'),
config: config,
}
]
}
},
],
},
});
}
@ -842,9 +842,9 @@ function simpleComponentConfig(config) {
components: [
{
sourceFile: path.join(__dirname, './fixtures/simple-component.js'),
config: config
}
]
config: config,
},
],
});
}
@ -869,12 +869,12 @@ function someInstructions(values) {
var result = {
config: values.config || {},
models: values.models || [],
dataSources: values.dataSources || { db: { connector: 'memory' } },
dataSources: values.dataSources || { db: { connector: 'memory' }},
middleware: values.middleware || { phases: [], middleware: [] },
components: values.components || [],
files: {
boot: []
}
boot: [],
},
};
if (values.env)
@ -898,6 +898,6 @@ function envAppInstructions() {
fs.copySync(ENV_APP, appdir.PATH);
return boot.compile({
appRootDir: appdir.PATH,
env: 'test'
env: 'test',
});
}

View File

@ -5,5 +5,5 @@ var app = module.exports = loopback();
boot(app, {
appId: 'browserApp2',
appRootDir: __dirname
appRootDir: __dirname,
});

View File

@ -1,5 +1,3 @@
module.exports = function(Model, options) {
Model.timeStampsMixin = true;
};

View File

@ -5,7 +5,7 @@ var framework = {
res.setHeader('passport', 'initialized');
next();
};
}
},
};
var Passport = function() {

View File

@ -1,7 +1,5 @@
module.exports = function(loopbackApp, params) {
loopbackApp.use('/component', function(req, res, next) {
res.send(params);
});
};

View File

@ -27,8 +27,8 @@ appdir.createConfigFilesSync = function(appConfig, dataSources, models) {
dataSources = extend({
db: {
connector: 'memory',
defaultForType: 'db'
}
defaultForType: 'db',
},
}, dataSources);
appdir.writeConfigFileSync ('datasources.json', dataSources);

View File

@ -8,14 +8,14 @@ function createContext() {
localStorage: {
// used by `debug` module
debug: process.env.DEBUG
debug: process.env.DEBUG,
},
// used by DataSource.prototype.ready
setTimeout: setTimeout,
// used by `debug` module
document: { documentElement: { style: {} } },
document: { documentElement: { style: {}}},
// used by `debug` module
navigator: { userAgent: 'sandbox' },
@ -39,9 +39,9 @@ function createContext() {
_logs: {
log: [],
warn: [],
error: []
error: [],
},
}
},
};
// `window` is used by loopback to detect browser runtime
@ -52,9 +52,10 @@ function createContext() {
exports.createContext = createContext;
function printContextLogs(context) {
for (var k in context.console._logs) {
var k, ix; // see https://github.com/eslint/eslint/issues/5744
for (k in context.console._logs) {
var items = context.console._logs[k];
for (var ix in items) {
for (ix in items) {
console[k].apply(console, items[ix]);
}
}