Skip to content

Commit

Permalink
New: Make log messages and colors configurable with theme.
Browse files Browse the repository at this point in the history
  • Loading branch information
sttk committed May 20, 2020
1 parent e2d7bce commit a858b41
Show file tree
Hide file tree
Showing 40 changed files with 1,084 additions and 248 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
**/*.babel.js
coverage
docs
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"extends": "gulp",
"rules": {
"max-len": [1, 90],
"max-len": [1, 100],
"max-statements": [1, 40],
"no-console": "off"
}
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ Supported configurations properties:
| flags.series | Run tasks given on the CLI in series (the default is parallel) |
| flags.require | An array of modules to require before running the gulpfile. Any relative paths will be resolved against the `--cwd` directory (if you don't want that behavior, use absolute paths) |
| flags.nodeFlags | An array of flags used to forcibly respawn the process upon startup. For example, if you always want your gulpfiles to run in node's harmony mode, you can set `--harmony` here |
| log.messages.* | Configure log messages. (See [configurable-log-msgs.html](./docs/html/configurable-log-msgs.html)) |
| log.theme.* | Configure log theme. (See [configurable-log-theme.html](./docs/html/configurable-log-theme.html)) |

## Flags

Expand Down
89 changes: 34 additions & 55 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ var Liftoff = require('liftoff');
var interpret = require('interpret');
var v8flags = require('v8flags');
var findRange = require('semver-greatest-satisfied-range');
var ansi = require('./lib/shared/ansi');
var format = require('theming-log').format;

var exit = require('./lib/shared/exit');
var tildify = require('./lib/shared/tildify');
var makeTitle = require('./lib/shared/make-title');
Expand All @@ -17,15 +18,19 @@ var completion = require('./lib/shared/completion');
var verifyDeps = require('./lib/shared/verify-dependencies');
var cliVersion = require('./package.json').version;
var getBlacklist = require('./lib/shared/get-blacklist');
var toConsole = require('./lib/shared/log/to-console');

var loadConfigFiles = require('./lib/shared/config/load-files');
var mergeConfigToCliFlags = require('./lib/shared/config/cli-flags');
var mergeConfigToEnvFlags = require('./lib/shared/config/env-flags');
var copyProps = require('copy-props');

// Logging functions
var logVerify = require('./lib/shared/log/verify');
var logBlacklistError = require('./lib/shared/log/blacklist-error');
var setLogLevels = require('./lib/shared/log/log-levels');
var setLogTheme = require('./lib/shared/log/theming');
var theme = require('./lib/shared/log/theme');
var msgs = require('./lib/shared/log/messages');
var makeHelp = require('./lib/shared/log/make-help');

// Get supported ranges
var ranges = fs.readdirSync(path.join(__dirname, '/lib/versioned/'));
Expand Down Expand Up @@ -54,33 +59,19 @@ var cli = new Liftoff({
},
});

var usage =
'\n' + ansi.bold('Usage:') +
' gulp ' + ansi.blue('[options]') + ' tasks';

var parser = yargs.usage(usage, cliOptions);
var parser = yargs.options(cliOptions);
var opts = parser.argv;

cli.on('require', function(name) {
log.info('Requiring external module', ansi.magenta(name));
log.info(msgs.info.require, name);
});

cli.on('requireFail', function(name, error) {
log.warn(
ansi.yellow('Failed to load external module'),
ansi.magenta(name)
);
/* istanbul ignore else */
if (error) {
log.warn(ansi.yellow(error.toString()));
}
log.warn(msgs.warn.requireFail, name, Boolean(error), error);
});

cli.on('respawn', function(flags, child) {
var nodeFlags = ansi.magenta(flags.join(', '));
var pid = ansi.magenta(child.pid);
log.info('Node flags detected:', nodeFlags);
log.info('Respawned to PID:', pid);
log.info(msgs.info.respawn, flags.join(', '), child.pid);
});

function run() {
Expand All @@ -96,8 +87,11 @@ function run() {
env = mergeConfigToEnvFlags(env, cfg, opts);
env.configProps = cfg;

// Set up event listeners for logging again after configuring.
toConsole(log, opts);
theme = copyProps(cfg.log.theme, theme);
msgs = copyProps(cfg.log.messages, msgs);

setLogLevels(log, opts);
setLogTheme(log, theme);

cli.execute(env, env.nodeFlags, handleArguments);
});
Expand All @@ -117,14 +111,14 @@ function handleArguments(env) {
}

if (opts.help) {
parser.showHelp(console.log);
makeHelp(parser).showHelp(console.log);
exit(0);
}

// Anything that needs to print outside of the logging mechanism should use console.log
if (opts.version) {
console.log('CLI version:', cliVersion);
console.log('Local version:', env.modulePackage.version || 'Unknown');
var gulpVersion = env.modulePackage.version || 'Unknown';
console.log(format(theme, msgs.info.version, cliVersion, gulpVersion));
exit(0);
}

Expand All @@ -134,11 +128,12 @@ function handleArguments(env) {
if (path.resolve(pkgPath) !== path.normalize(pkgPath)) {
pkgPath = path.join(env.cwd, pkgPath);
}
log.info('Verifying plugins in ' + pkgPath);
log.info(msgs.info.verify, pkgPath);
return getBlacklist(function(err, blacklist) {
/* istanbul ignore if */
if (err) {
return logBlacklistError(err);
log.error(msgs.error.failToGetBlacklist, err.message);
exit(1);
}

var blacklisted = verifyDeps(require(pkgPath), blacklist);
Expand All @@ -153,51 +148,35 @@ function handleArguments(env) {
fs.existsSync(path.join(env.cwd, 'package.json'))
&& !fs.existsSync(path.join(env.cwd, 'node_modules'));

/* istanbul ignore next */
var missingGulpMessage =
missingNodeModules
? 'Local modules not found in'
: 'Local gulp not found in';
log.error(
ansi.red(missingGulpMessage),
ansi.magenta(tildify(env.cwd))
);
var hasYarn = fs.existsSync(path.join(env.cwd, 'yarn.lock'));
/* istanbul ignore next */
var installCommand =
missingNodeModules
? hasYarn
? 'yarn install'
: 'npm install'
: hasYarn
? 'yarn add gulp'
: 'npm install gulp';
log.error(ansi.red('Try running: ' + installCommand));
var hasNpm = !hasYarn;

/* istanbul ignore if */
if (missingNodeModules) {
log.error(msgs.error.nodeModulesNotFound, tildify(env.cwd), hasYarn, hasNpm);
} else {
log.error(msgs.error.gulpNotFound, tildify(env.cwd), hasYarn, hasNpm);
}
exit(1);
}

if (!env.configPath) {
log.error(ansi.red('No gulpfile found'));
log.error(msgs.error.gulpfileNotFound);
exit(1);
}

// Chdir before requiring gulpfile to make sure
// we let them chdir as needed
if (process.cwd() !== env.cwd) {
process.chdir(env.cwd);
log.info(
'Working directory changed to',
ansi.magenta(tildify(env.cwd))
);
log.info(msgs.info.cwdChanged, tildify(env.cwd));
}

// Find the correct CLI version to run
var range = findRange(env.modulePackage.version, ranges);

if (!range) {
log.error(
ansi.red('Unsupported gulp version', env.modulePackage.version)
);
log.error(msgs.error.badGulpVersion, env.modulePackage.version);
exit(1);
}

Expand Down
47 changes: 0 additions & 47 deletions lib/shared/cli-options.js
Original file line number Diff line number Diff line change
@@ -1,122 +1,75 @@
'use strict';

var ansi = require('./ansi');

module.exports = {
help: {
alias: 'h',
type: 'boolean',
desc: ansi.gray(
'Show this help.'),
},
version: {
alias: 'v',
type: 'boolean',
desc: ansi.gray(
'Print the global and local gulp versions.'),
},
require: {
type: 'string',
requiresArg: true,
desc: ansi.gray(
'Will require a module before running the gulpfile. ' +
'This is useful for transpilers but also has other applications.'),
},
gulpfile: {
alias: 'f',
type: 'string',
requiresArg: true,
desc: ansi.gray(
'Manually set path of gulpfile. Useful if you have multiple gulpfiles. ' +
'This will set the CWD to the gulpfile directory as well.'),
},
cwd: {
type: 'string',
requiresArg: true,
desc: ansi.gray(
'Manually set the CWD. The search for the gulpfile, ' +
'as well as the relativity of all requires will be from here.'),
},
verify: {
desc: ansi.gray(
'Will verify plugins referenced in project\'s package.json against ' +
'the plugins blacklist.'),
},
tasks: {
alias: 'T',
type: 'boolean',
desc: ansi.gray(
'Print the task dependency tree for the loaded gulpfile.'),
},
'tasks-simple': {
type: 'boolean',
desc: ansi.gray(
'Print a plaintext list of tasks for the loaded gulpfile.'),
},
'tasks-json': {
desc: ansi.gray(
'Print the task dependency tree, ' +
'in JSON format, for the loaded gulpfile.'),
},
'tasks-depth': {
alias: 'depth',
type: 'number',
requiresArg: true,
default: undefined, // To detect if this cli option is specified.
desc: ansi.gray(
'Specify the depth of the task dependency tree.'),
},
'compact-tasks': {
type: 'boolean',
default: undefined, // To detect if this cli option is specified.
desc: ansi.gray(
'Reduce the output of task dependency tree by printing ' +
'only top tasks and their child tasks.'),
},
'sort-tasks': {
type: 'boolean',
default: undefined, // To detect if this cli option is specified.
desc: ansi.gray(
'Will sort top tasks of task dependency tree.'),
},
color: {
type: 'boolean',
desc: ansi.gray(
'Will force gulp and gulp plugins to display colors, ' +
'even when no color support is detected.'),
},
'no-color': {
type: 'boolean',
desc: ansi.gray(
'Will force gulp and gulp plugins to not display colors, ' +
'even when color support is detected.'),
},
silent: {
alias: 'S',
type: 'boolean',
default: undefined, // To detect if this cli option is specified.
desc: ansi.gray(
'Suppress all gulp logging.'),
},
continue: {
type: 'boolean',
default: undefined, // To detect if this cli option is specified.
desc: ansi.gray(
'Continue execution of tasks upon failure.'),
},
series: {
type: 'boolean',
default: undefined, // To detect if this cli option is specified.
desc: ansi.gray(
'Run tasks given on the CLI in series (the default is parallel).'),
},
'log-level': {
alias: 'L',
// Type isn't needed because count acts as a boolean
count: true,
default: undefined, // To detect if this cli option is specified.
desc: ansi.gray(
'Set the loglevel. -L for least verbose and -LLLL for most verbose. ' +
'-LLL is default.'),
},
};
12 changes: 6 additions & 6 deletions lib/shared/completion.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@
var fs = require('fs');
var path = require('path');

var format = require('theming-log').format;
var theme = require('./log/theme');
var msgs = require('./log/messages');

module.exports = function(name) {
if (typeof name !== 'string') {
throw new Error('Missing completion type');
throw new Error(format(theme, msgs.error.noCompletionType));
}
var file = path.join(__dirname, '../../completion', name);
try {
console.log(fs.readFileSync(file, 'utf8'));
process.exit(0);
} catch (err) {
console.log(
'echo "gulp autocompletion rules for',
'\'' + name + '\'',
'not found"'
);
console.log(format(theme, msgs.error.unknownCompletionType, name));
process.exit(5);
}
};
8 changes: 7 additions & 1 deletion lib/shared/config/load-files.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ var copyProps = require('copy-props');
var path = require('path');

function loadConfigFiles(configFiles, configFileOrder) {
var config = {};
var config = {
flags: {},
log: {
theme: {},
messages: {},
},
};

configFileOrder.forEach(loadFile);

Expand Down
8 changes: 6 additions & 2 deletions lib/shared/get-blacklist.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ var https = require('https');

var concat = require('concat-stream');

var format = require('theming-log').format;
var theme = require('./log/theme');
var msgs = require('./log/messages');

var url = 'https://raw.githubusercontent.com/gulpjs/plugins/master/src/blackList.json';

function collect(stream, cb) {
Expand All @@ -20,7 +24,7 @@ function parse(str, cb) {
cb(null, JSON.parse(str));
} catch (err) {
/* istanbul ignore next */
cb(new Error('Invalid Blacklist JSON.'));
cb(new Error(format(theme, msgs.error.invalidBlacklistJson)));
}
}

Expand All @@ -32,7 +36,7 @@ function getBlacklist(cb) {
/* istanbul ignore if */
if (res.statusCode !== 200) {
// TODO: Test different status codes
return cb(new Error('Request failed. Status Code: ' + res.statusCode));
return cb(new Error(format(theme, msgs.error.blacklistRequestFailed, res.statusCode)));
}

res.setEncoding('utf8');
Expand Down
Loading

0 comments on commit a858b41

Please sign in to comment.