From e16d675606867a5a3a64c24e7fa48bb0a664723d Mon Sep 17 00:00:00 2001 From: Blaine Bublitz Date: Fri, 22 Mar 2024 21:04:29 -0700 Subject: [PATCH] feat: Support theming and translations via config files (#260) chore!: Remove `description` in config to rely on translations --------- Co-authored-by: sttk --- .eslintrc | 2 +- .gitignore | 1 + README.md | 2 + index.js | 134 ++--- lib/shared/completion.js | 12 +- lib/shared/log/tasks.js | 61 ++- lib/shared/log/to-console.js | 129 +++-- lib/shared/options/cli-options.js | 61 +-- lib/shared/options/make-help.js | 24 + lib/shared/translate.js | 302 +++++++++++ lib/versioned/^3.7.0/index.js | 21 +- lib/versioned/^3.7.0/log/events.js | 26 +- lib/versioned/^4.0.0/index.js | 28 +- lib/versioned/^4.0.0/log/events.js | 21 +- lib/versioned/^4.0.0/log/sync-task.js | 10 +- package.json | 5 +- test/config-description.js | 72 --- test/config-message-function.js | 475 ++++++++++++++++++ test/execution-errors.js | 76 ++- test/expected/config/theming/bad-flag.txt | 41 ++ test/expected/config/theming/flags.txt | 18 + test/expected/config/theming/usage.txt | 39 ++ test/expected/flags-tasks-depth1.txt | 4 + test/fixtures/.gulp.js | 8 +- .../config/theming/ARGV_ERROR/.gulp.js | 9 + .../config/theming/ARGV_ERROR/gulpfile.js | 3 + .../theming/COMPLETION_TYPE_MISSING/.gulp.js | 9 + .../COMPLETION_TYPE_MISSING/gulpfile.js | 3 + .../theming/COMPLETION_TYPE_UNKNOWN/.gulp.js | 9 + .../COMPLETION_TYPE_UNKNOWN/gulpfile.js | 3 + .../config/theming/CWD_CHANGED/.gulp.js | 12 + .../config/theming/CWD_CHANGED/gulpfile.js | 3 + .../config/theming/DESCRIPTION/.gulp.js | 9 + .../config/theming/DESCRIPTION/gulpfile.js | 3 + .../theming/DESCRIPTION/remove/.gulp.js | 9 + .../theming/DESCRIPTION/remove/gulpfile.js | 3 + .../config/theming/EXEC_ERROR/.gulp.js | 13 + .../config/theming/EXEC_ERROR/gulpfile.js | 3 + .../fixtures/config/theming/GULPFILE/.gulp.js | 13 + .../config/theming/GULPFILE/gulpfile.js | 3 + .../config/theming/LOADER_FAILURE/.gulp.js | 14 + .../theming/LOADER_FAILURE/gulpfile.coffee | 1 + .../config/theming/LOADER_SUCCESS/.gulp.js | 12 + .../theming/LOADER_SUCCESS/gulpfile.babel.js | 3 + .../config/theming/MISSING_GULP/.gulp.js | 16 + .../config/theming/MISSING_GULPFILE/.gulp.js | 9 + .../theming/MISSING_NODE_MODULES/.gulp.js | 14 + .../theming/MISSING_NODE_MODULES/package.json | 7 + .../config/theming/NODE_FLAGS/.gulp.js | 12 + .../config/theming/NODE_FLAGS/gulpfile.js | 3 + .../config/theming/PRELOAD_BEFORE/.gulp.js | 12 + .../config/theming/PRELOAD_BEFORE/gulpfile.js | 3 + .../config/theming/PRELOAD_BEFORE/preload.js | 1 + .../config/theming/PRELOAD_FAILURE/.gulp.js | 12 + .../theming/PRELOAD_FAILURE/gulpfile.js | 3 + .../config/theming/PRELOAD_SUCCESS/.gulp.js | 12 + .../theming/PRELOAD_SUCCESS/gulpfile.js | 3 + .../config/theming/PRELOAD_SUCCESS/preload.js | 1 + .../config/theming/RESPAWNED/.gulp.js | 12 + .../config/theming/RESPAWNED/gulpfile.js | 3 + .../config/theming/TASK_ERROR/.gulp.js | 16 + .../config/theming/TASK_ERROR/gulpfile.js | 3 + .../config/theming/TASK_FAILURE/.gulp.js | 16 + .../config/theming/TASK_FAILURE/gulpfile.js | 3 + .../config/theming/TASK_MISSING/.gulp.js | 13 + .../config/theming/TASK_MISSING/gulpfile.js | 3 + .../config/theming/TASK_START/.gulp.js | 12 + .../config/theming/TASK_START/gulpfile.js | 3 + .../config/theming/TASK_STOP/.gulp.js | 12 + .../config/theming/TASK_STOP/gulpfile.js | 3 + .../config/theming/TASK_SYNC/.gulp.js | 9 + .../config/theming/TASK_SYNC/gulpfile.js | 3 + .../theming/UNSUPPORTED_GULP_VERSION/.gulp.js | 9 + .../UNSUPPORTED_GULP_VERSION/gulpfile.js | 3 + .../node_modules/gulp/index.js | 0 .../node_modules/gulp/package.json | 5 + test/fixtures/config/theming/USAGE/.gulp.js | 9 + test/fixtures/config/theming/flags/.gulp.js | 76 +++ test/fixtures/errors/yarn/yarn.lock | 0 test/fixtures/gulpfiles/.gulp.js | 8 +- test/fixtures/logging.js | 5 +- test/flags-help.js | 11 + test/flags-tasks.js | 22 +- test/lib/config-cli-flags.js | 2 +- test/lib/format-hrtime.js | 2 +- ...k-task-not-found.js => normalize-error.js} | 0 86 files changed, 1744 insertions(+), 336 deletions(-) create mode 100644 lib/shared/options/make-help.js create mode 100644 lib/shared/translate.js delete mode 100644 test/config-description.js create mode 100644 test/config-message-function.js create mode 100644 test/expected/config/theming/bad-flag.txt create mode 100644 test/expected/config/theming/flags.txt create mode 100644 test/expected/config/theming/usage.txt create mode 100644 test/expected/flags-tasks-depth1.txt create mode 100644 test/fixtures/config/theming/ARGV_ERROR/.gulp.js create mode 100644 test/fixtures/config/theming/ARGV_ERROR/gulpfile.js create mode 100644 test/fixtures/config/theming/COMPLETION_TYPE_MISSING/.gulp.js create mode 100644 test/fixtures/config/theming/COMPLETION_TYPE_MISSING/gulpfile.js create mode 100644 test/fixtures/config/theming/COMPLETION_TYPE_UNKNOWN/.gulp.js create mode 100644 test/fixtures/config/theming/COMPLETION_TYPE_UNKNOWN/gulpfile.js create mode 100644 test/fixtures/config/theming/CWD_CHANGED/.gulp.js create mode 100644 test/fixtures/config/theming/CWD_CHANGED/gulpfile.js create mode 100644 test/fixtures/config/theming/DESCRIPTION/.gulp.js create mode 100644 test/fixtures/config/theming/DESCRIPTION/gulpfile.js create mode 100644 test/fixtures/config/theming/DESCRIPTION/remove/.gulp.js create mode 100644 test/fixtures/config/theming/DESCRIPTION/remove/gulpfile.js create mode 100644 test/fixtures/config/theming/EXEC_ERROR/.gulp.js create mode 100644 test/fixtures/config/theming/EXEC_ERROR/gulpfile.js create mode 100644 test/fixtures/config/theming/GULPFILE/.gulp.js create mode 100644 test/fixtures/config/theming/GULPFILE/gulpfile.js create mode 100644 test/fixtures/config/theming/LOADER_FAILURE/.gulp.js create mode 100644 test/fixtures/config/theming/LOADER_FAILURE/gulpfile.coffee create mode 100644 test/fixtures/config/theming/LOADER_SUCCESS/.gulp.js create mode 100644 test/fixtures/config/theming/LOADER_SUCCESS/gulpfile.babel.js create mode 100644 test/fixtures/config/theming/MISSING_GULP/.gulp.js create mode 100644 test/fixtures/config/theming/MISSING_GULPFILE/.gulp.js create mode 100644 test/fixtures/config/theming/MISSING_NODE_MODULES/.gulp.js create mode 100644 test/fixtures/config/theming/MISSING_NODE_MODULES/package.json create mode 100644 test/fixtures/config/theming/NODE_FLAGS/.gulp.js create mode 100644 test/fixtures/config/theming/NODE_FLAGS/gulpfile.js create mode 100644 test/fixtures/config/theming/PRELOAD_BEFORE/.gulp.js create mode 100644 test/fixtures/config/theming/PRELOAD_BEFORE/gulpfile.js create mode 100644 test/fixtures/config/theming/PRELOAD_BEFORE/preload.js create mode 100644 test/fixtures/config/theming/PRELOAD_FAILURE/.gulp.js create mode 100644 test/fixtures/config/theming/PRELOAD_FAILURE/gulpfile.js create mode 100644 test/fixtures/config/theming/PRELOAD_SUCCESS/.gulp.js create mode 100644 test/fixtures/config/theming/PRELOAD_SUCCESS/gulpfile.js create mode 100644 test/fixtures/config/theming/PRELOAD_SUCCESS/preload.js create mode 100644 test/fixtures/config/theming/RESPAWNED/.gulp.js create mode 100644 test/fixtures/config/theming/RESPAWNED/gulpfile.js create mode 100644 test/fixtures/config/theming/TASK_ERROR/.gulp.js create mode 100644 test/fixtures/config/theming/TASK_ERROR/gulpfile.js create mode 100644 test/fixtures/config/theming/TASK_FAILURE/.gulp.js create mode 100644 test/fixtures/config/theming/TASK_FAILURE/gulpfile.js create mode 100644 test/fixtures/config/theming/TASK_MISSING/.gulp.js create mode 100644 test/fixtures/config/theming/TASK_MISSING/gulpfile.js create mode 100644 test/fixtures/config/theming/TASK_START/.gulp.js create mode 100644 test/fixtures/config/theming/TASK_START/gulpfile.js create mode 100644 test/fixtures/config/theming/TASK_STOP/.gulp.js create mode 100644 test/fixtures/config/theming/TASK_STOP/gulpfile.js create mode 100644 test/fixtures/config/theming/TASK_SYNC/.gulp.js create mode 100644 test/fixtures/config/theming/TASK_SYNC/gulpfile.js create mode 100644 test/fixtures/config/theming/UNSUPPORTED_GULP_VERSION/.gulp.js create mode 100644 test/fixtures/config/theming/UNSUPPORTED_GULP_VERSION/gulpfile.js create mode 100644 test/fixtures/config/theming/UNSUPPORTED_GULP_VERSION/node_modules/gulp/index.js create mode 100644 test/fixtures/config/theming/UNSUPPORTED_GULP_VERSION/node_modules/gulp/package.json create mode 100644 test/fixtures/config/theming/USAGE/.gulp.js create mode 100644 test/fixtures/config/theming/flags/.gulp.js create mode 100644 test/fixtures/errors/yarn/yarn.lock rename test/lib/{check-task-not-found.js => normalize-error.js} (100%) diff --git a/.eslintrc b/.eslintrc index 331ecbe0..c35bface 100644 --- a/.eslintrc +++ b/.eslintrc @@ -2,7 +2,7 @@ "extends": "gulp", "rules": { "max-len": [1, 130], - "max-statements": [1, 40], + "max-statements": [1, 65], "no-console": "off" } } diff --git a/.gitignore b/.gitignore index 3b569a93..c626059e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.log node_modules !test/fixtures/errors/bad-gulp-version/node_modules/ +!test/fixtures/config/theming/UNSUPPORTED_GULP_VERSION/node_modules/ build *.node components diff --git a/README.md b/README.md index 1e9089e0..bfb6c6d1 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,8 @@ Supported configurations properties: | flags.tasksDepth | Set default depth of task dependency tree. | | flags.silent | Silence logging by default | | flags.series | Run tasks given on the CLI in series (the default is parallel) | +| message(data) | A function used to translate messages that pass through gulp-cli. Can receive an object like `{ tag: Symbol(), ...props }` where the `tag` is a symbol from `@gulpjs/messages`. The string returned from this function will be logged. If `false` is explicitly returned, no message will be logged. | +| timestamp(data) | A function used to provide timestamps for gulp-cli. Can receive an object like `{ tag: Symbol(), ...props }` where the `tag` is a symbol from `@gulpjs/messages`. The string returned from this function will be output before any messages. If `false` is explicitly returned, no timestamp will be output. | ## Flags diff --git a/index.js b/index.js index 7b286c60..3491a7a6 100644 --- a/index.js +++ b/index.js @@ -8,18 +8,20 @@ var yargs = require('yargs'); var Liftoff = require('liftoff'); var interpret = require('interpret'); var v8flags = require('v8flags'); +var messages = require('@gulpjs/messages'); var findRange = require('semver-greatest-satisfied-range'); -var chalk = require('chalk'); var exit = require('./lib/shared/exit'); -var tildify = require('./lib/shared/tildify'); + var arrayFind = require('./lib/shared/array-find'); var makeTitle = require('./lib/shared/make-title'); +var makeHelp = require('./lib/shared/options/make-help'); var cliOptions = require('./lib/shared/options/cli-options'); var completion = require('./lib/shared/completion'); var cliVersion = require('./package.json').version; var toConsole = require('./lib/shared/log/to-console'); var mergeCliOpts = require('./lib/shared/config/cli-flags'); +var buildTranslations = require('./lib/shared/translate'); // Get supported ranges var ranges = fs.readdirSync(path.join(__dirname, '/lib/versioned/')); @@ -31,7 +33,6 @@ process.env.INIT_CWD = process.cwd(); var cli = new Liftoff({ name: 'gulp', processTitle: makeTitle('gulp', process.argv.slice(2)), - completions: completion, extensions: interpret.jsVariants, v8flags: v8flags, configFiles: [ @@ -49,38 +50,34 @@ var cli = new Liftoff({ ], }); -var usage = - '\n' + chalk.bold('Usage:') + - ' gulp ' + chalk.blue('[options]') + ' tasks'; - var parser = yargs .help(false) .version(false) .detectLocale(false) - .usage(usage) + .showHelpOnFail(false) + .exitProcess(false) + .fail(onFail) .options(cliOptions); var opts = parser.parse(); // Set up event listeners for logging temporarily. -toConsole(log, opts); +// TODO: Rework console logging before we can set up proper config +// Possibly by batching messages in gulplog until listeners are attached +var cleanupListeners = toConsole(log, opts, buildTranslations()); cli.on('preload:before', function(name) { - log.info('Preloading external module:', chalk.magenta(name)); + log.info({ tag: messages.PRELOAD_BEFORE, name: name }); }); cli.on('preload:success', function(name) { - log.info('Preloaded external module:', chalk.magenta(name)); + log.info({ tag: messages.PRELOAD_SUCCESS, name: name }); }); cli.on('preload:failure', function(name, error) { - log.warn( - chalk.yellow('Failed to preload external module:'), - chalk.magenta(name) - ); - /* istanbul ignore else */ + log.warn({ tag: messages.PRELOAD_FAILURE, name: name }); if (error) { - log.warn(chalk.yellow(error.toString())); + log.warn({ tag: messages.PRELOAD_ERROR, error: error }); } }); @@ -90,26 +87,20 @@ cli.on('loader:success', function(name) { // However, we don't want to show the mjs-stub loader in the logs /* istanbul ignore else */ if (path.basename(name, '.js') !== 'mjs-stub') { - log.info('Loaded external module:', chalk.magenta(name)); + log.info({ tag: messages.LOADER_SUCCESS, name: name }); } }); cli.on('loader:failure', function(name, error) { - log.warn( - chalk.yellow('Failed to load external module:'), - chalk.magenta(name) - ); - /* istanbul ignore else */ + log.warn({ tag: messages.LOADER_FAILURE, name: name }); if (error) { - log.warn(chalk.yellow(error.toString())); + log.warn({ tag: messages.LOADER_ERROR, error: error }); } }); cli.on('respawn', function(flags, child) { - var nodeFlags = chalk.magenta(flags.join(', ')); - var pid = chalk.magenta(child.pid); - log.info('Node flags detected:', nodeFlags); - log.info('Respawned to PID:', pid); + log.info({ tag: messages.NODE_FLAGS, flags: flags }); + log.info({ tag: messages.RESPAWNED, pid: child.pid }); }); function run() { @@ -117,7 +108,6 @@ function run() { cwd: opts.cwd, configPath: opts.gulpfile, preload: opts.preload, - completion: opts.completion, }, onPrepare); } @@ -127,22 +117,50 @@ function isDefined(cfg) { return cfg != null; } +function onFail(message, error) { + // Run Liftoff#prepare to get the env. Primarily to load themes. + cli.prepare({}, function (env) { + // We only use the first config found, which is a departure from + // the previous implementation that merged with the home + var cfg = arrayFind(env.config, isDefined); + var translate = buildTranslations(cfg); + + var errorMsg = translate.message({ tag: messages.ARGV_ERROR, message: message, error: error }); + if (errorMsg) { + console.error(errorMsg); + } + + makeHelp(parser, translate).showHelp(console.error); + exit(1); + }); +} + function onPrepare(env) { // We only use the first config found, which is a departure from // the previous implementation that merged with the home var cfg = arrayFind(env.config, isDefined); var flags = mergeCliOpts(opts, cfg); - // Set up event listeners for logging after configuring. - toConsole(log, flags); + // Remove the previous listeners since we have appropriate config now + cleanupListeners(); + + var translate = buildTranslations(cfg); + + // Set up event listeners for logging again after configuring. + toConsole(log, flags, translate); cli.execute(env, cfg.nodeFlags, function (env) { - onExecute(env, cfg, flags); + onExecute(env, flags, translate); }); } // The actual logic -function onExecute(env, cfg, flags) { +function onExecute(env, flags, translate) { + // Moved the completion logic outside of Liftoff since we need to include translations + if (flags.completion) { + return completion(flags.completion, translate); + } + // This translates the --continue flag in gulp // To the settle env variable for undertaker // We use the process.env so the user's gulpfile @@ -152,7 +170,7 @@ function onExecute(env, cfg, flags) { } if (flags.help) { - parser.showHelp(console.log); + makeHelp(parser, translate).showHelp(console.log); exit(0); } @@ -164,36 +182,31 @@ function onExecute(env, cfg, flags) { } if (!env.modulePath) { - /* istanbul ignore next */ var missingNodeModules = 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( - chalk.red(missingGulpMessage), - chalk.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(chalk.red('Try running: ' + installCommand)); + if (missingNodeModules) { + log.error({ tag: messages.MISSING_NODE_MODULES, cwd: env.cwd }); + if (hasYarn) { + log.error({ tag: messages.YARN_INSTALL }) + } else { + log.error({ tag: messages.NPM_INSTALL }) + } + } else { + log.error({ tag: messages.MISSING_GULP, cwd: env.cwd }); + if (hasYarn) { + log.error({ tag: messages.YARN_INSTALL_GULP }); + } else { + log.error({ tag: messages.NPM_INSTALL_GULP }); + } + } exit(1); } if (!env.configPath) { - log.error(chalk.red('No gulpfile found')); + log.error({ tag: messages.MISSING_GULPFILE }); exit(1); } @@ -201,23 +214,18 @@ function onExecute(env, cfg, flags) { // we let them chdir as needed if (process.cwd() !== env.cwd) { process.chdir(env.cwd); - log.info( - 'Working directory changed to', - chalk.magenta(tildify(env.cwd)) - ); + log.info({ tag: messages.CWD_CHANGED, cwd: env.cwd }); } // Find the correct CLI version to run var range = findRange(env.modulePackage.version, ranges); if (!range) { - log.error( - chalk.red('Unsupported gulp version', env.modulePackage.version) - ); + log.error({ tag: messages.UNSUPPORTED_GULP_VERSION, version: env.modulePackage.version }); exit(1); } // Load and execute the CLI version var versionedDir = path.join(__dirname, '/lib/versioned/', range, '/'); - require(versionedDir)(env, cfg, flags); + require(versionedDir)(env, flags, translate); } diff --git a/lib/shared/completion.js b/lib/shared/completion.js index 3a47023d..d637d140 100644 --- a/lib/shared/completion.js +++ b/lib/shared/completion.js @@ -3,20 +3,18 @@ var fs = require('fs'); var path = require('path'); -module.exports = function(name) { +var messages = require('@gulpjs/messages'); + +module.exports = function(name, translate) { if (typeof name !== 'string') { - throw new Error('Missing completion type'); + throw new Error(translate.message({ tag: messages.COMPLETION_TYPE_MISSING })); } 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(translate.message({ tag: messages.COMPLETION_TYPE_UNKNOWN, name: name })); process.exit(5); } }; diff --git a/lib/shared/log/tasks.js b/lib/shared/log/tasks.js index 4721774d..0590ef4a 100644 --- a/lib/shared/log/tasks.js +++ b/lib/shared/log/tasks.js @@ -1,10 +1,11 @@ 'use strict'; -var log = require('gulplog'); -var chalk = require('chalk'); +var stringWidth = require('string-width'); +var messages = require('@gulpjs/messages'); + var isObject = require('../is-object'); -function logTasks(tree, opts, getTask) { +function logTasks(tree, opts, getTask, translate) { if (opts.sortTasks) { tree.nodes = tree.nodes.sort(compareByLabel); } @@ -34,16 +35,19 @@ function logTasks(tree, opts, getTask) { tree.nodes.forEach(function(node, idx, arr) { var isLast = idx === arr.length - 1; var w = createTreeLines(node, lines, opts, 1, '', isLast); - maxLabelWidth = Math.max(maxLabelWidth, w || 0); + maxLabelWidth = Math.max(maxLabelWidth, w); }); lines.forEach(function(line) { var s = line.label; if (line.desc) { - var spaces = ' '.repeat(maxLabelWidth - line.label.length) + ' '; + var spaces = ' '.repeat(maxLabelWidth - line.width) + ' '; s += spaces + line.desc; } - log.info(s); + if (s) { + // We don't need timestamps here + console.log(s); + } }); } @@ -60,7 +64,8 @@ function logTasks(tree, opts, getTask) { var maxLabelWidth = addTaskToLines(task, lines, isLast, isLeaf); if (!isLeaf) { - bars += (isLast ? ' ' : '│ '); + bars += (isLast ? ' ' : translate.message({ tag: messages.BOX_DRAWINGS_LIGHT_VERTICAL })); + bars += ' ' node.nodes.forEach(function(node, idx, arr) { var isLast = idx === arr.length - 1; createTreeLines(node, lines, opts, depth + 1, bars, isLast); @@ -71,25 +76,31 @@ function logTasks(tree, opts, getTask) { } function addTaskToLines(task, lines, isLast, isLeaf) { - var taskBars = task.bars + (isLast ? '└' : '├') + '─'; + var taskBars = task.bars + (isLast + ? translate.message({ tag: messages.BOX_DRAWINGS_LIGHT_UP_AND_RIGHT }) + : translate.message({ tag: messages.BOX_DRAWINGS_LIGHT_VERTICAL_AND_RIGHT })) + + translate.message({ tag: messages.BOX_DRAWINGS_LIGHT_HORIZONTAL }); if (isLeaf) { - taskBars += '─ '; + taskBars += translate.message({ tag: messages.BOX_DRAWINGS_LIGHT_HORIZONTAL }); } else { - taskBars += '┬ '; + taskBars += translate.message({ tag: messages.BOX_DRAWINGS_LIGHT_DOWN_AND_HORIZONTAL }); } + taskBars += ' '; var line = {}; if (task.depth === 1) { - line.label = chalk.white(taskBars) + chalk.white(task.label); + line.label = taskBars + translate.message({ tag: messages.TASK_NAME, name: task.label }); } else { - line.label = chalk.white(taskBars) + chalk.cyan(task.label); + line.label = taskBars + translate.message({ tag: messages.TASK_NAME, name: task.label }); } + line.width = stringWidth(line.label); + if (typeof task.desc === 'string' && task.desc) { - line.desc = chalk.white(task.desc); + line.desc = translate.message({ tag: messages.TASK_DESCRIPTION, description: task.desc }); } lines.push(line); - var maxLabelWidth = line.label.length + var maxLabelWidth = line.width; if (!isObject(task.flags)) { return maxLabelWidth; @@ -97,29 +108,33 @@ function logTasks(tree, opts, getTask) { var flagBars = task.bars; if (isLast) { - flagBars += ' '; + flagBars += ' '; } else { - flagBars += '│ '; + flagBars += translate.message({ tag: messages.BOX_DRAWINGS_LIGHT_VERTICAL }); } + flagBars += ' '; if (isLeaf) { - flagBars += ' '; + flagBars += ' '; } else { - flagBars += '│ '; + flagBars += translate.message({ tag: messages.BOX_DRAWINGS_LIGHT_VERTICAL }); } + flagBars += ' '; Object.entries(task.flags).sort(flagSorter).forEach(addFlagsToLines); function addFlagsToLines(ent) { if (typeof ent[0] !== 'string' || !ent[0]) return; var line = {}; - lines.push(line); - line.label = chalk.white(flagBars) + chalk.magenta(ent[0]); + line.label = flagBars + translate.message({ tag: messages.TASK_FLAG, flag: ent[0] }); + line.width = stringWidth(line.label); - maxLabelWidth = Math.max(maxLabelWidth, line.label.length); + maxLabelWidth = Math.max(maxLabelWidth, line.width); - if (typeof ent[1] !== 'string' || !ent[1]) return; - line.desc = chalk.white('…' + ent[1]); + if (typeof ent[1] === 'string' && ent[1] !== '') { + line.desc = translate.message({ tag: messages.TASK_FLAG_DESCRIPTION, description: ent[1] }); + } + lines.push(line); } return maxLabelWidth; diff --git a/lib/shared/log/to-console.js b/lib/shared/log/to-console.js index 43650af1..a3be2c48 100644 --- a/lib/shared/log/to-console.js +++ b/lib/shared/log/to-console.js @@ -1,58 +1,109 @@ 'use strict'; -var fancyLog = require('fancy-log'); - /* istanbul ignore next */ function noop() {} -// The sorting of the levels is -// significant. -var levels = [ - 'error', // -L: Logs error events. - 'warn', // -LL: Logs warn and error events. - 'info', // -LLL: Logs info, warn and error events. - 'debug', // -LLLL: Logs all log levels. -]; - -function cleanup(log) { - levels.forEach(removeListeners); - - function removeListeners(level) { - if (level === 'error') { - log.removeListener(level, noop); - log.removeListener(level, fancyLog.error); - } else { - log.removeListener(level, fancyLog); - } - } -} - -function toConsole(log, opts) { - // Remove previous listeners to enable to call this twice. - cleanup(log); - +function toConsole(log, opts, translate) { // Return immediately if logging is // not desired. if (opts.tasksSimple || opts.tasksJson || opts.help || opts.version || opts.silent) { // Keep from crashing process when silent. log.on('error', noop); - return; + return function () { + log.removeListener('error', noop); + }; } // Default loglevel to info level (3). var loglevel = opts.logLevel || 3; - levels - .filter(function(item, i) { - return i < loglevel; - }) - .forEach(function(level) { - if (level === 'error') { - log.on(level, fancyLog.error); - } else { - log.on(level, fancyLog); + // -L: Logs error events. + if (loglevel > 0) { + log.on('error', onError); + } + + // -LL: Logs warn and error events. + if (loglevel > 1) { + log.on('warn', onWarn); + } + + // -LLL: Logs info, warn and error events. + if (loglevel > 2) { + log.on('info', onInfo); + } + + if (loglevel > 3) { + log.on('debug', onDebug); + } + + return function () { + log.removeListener('error', onError); + log.removeListener('warn', onWarn); + log.removeListener('info', onInfo); + log.removeListener('debug', onDebug); + }; + + function onError(msg) { + // Get message and timestamp before printing anything to avoid + // logging a half message if there's an error in one of them + var message = translate.message(msg); + var timestamp = translate.timestamp(msg); + + if (message) { + // Ensure timestamp is not written without a message + if (timestamp) { + process.stderr.write(timestamp + ' '); } - }); + console.error(message); + } + } + + // onWarn, onInfo, and onDebug are currently all the same + // implementation but separated to change independently + function onWarn(msg) { + // Get message and timestamp before printing anything to avoid + // logging a half message if there's an error in one of them + var message = translate.message(msg); + var timestamp = translate.timestamp(msg); + + if (message) { + // Ensure timestamp is not written without a message + if (timestamp) { + process.stdout.write(timestamp + ' '); + } + console.log(message); + } + } + + function onInfo(msg) { + // Get message and timestamp before printing anything to avoid + // logging a half message if there's an error in one of them + var message = translate.message(msg); + var timestamp = translate.timestamp(msg); + + if (message) { + // Ensure timestamp is not written without a message + if (timestamp) { + process.stdout.write(timestamp + ' '); + } + console.log(message); + } + } + + function onDebug(msg) { + // Get message and timestamp before printing anything to avoid + // logging a half message if there's an error in one of them + var message = translate.message(msg); + var timestamp = translate.timestamp(msg); + + if (message) { + // Ensure timestamp is not written without a message + if (timestamp) { + process.stdout.write(timestamp + ' '); + } + console.log(message); + } + } } module.exports = toConsole; diff --git a/lib/shared/options/cli-options.js b/lib/shared/options/cli-options.js index 8105d461..b84c7948 100644 --- a/lib/shared/options/cli-options.js +++ b/lib/shared/options/cli-options.js @@ -1,118 +1,93 @@ 'use strict'; -var chalk = require('chalk'); +var messages = require('@gulpjs/messages'); var options = { help: { alias: 'h', type: 'boolean', - desc: chalk.gray( - 'Show this help.'), + tag: messages.FLAG_HELP, }, version: { alias: 'v', type: 'boolean', - desc: chalk.gray( - 'Print the global and local gulp versions.'), + tag: messages.FLAG_VERSION, }, preload: { type: 'string', requiresArg: true, - desc: chalk.gray( - 'Will preload a module before running the gulpfile. ' + - 'This is useful for transpilers but also has other applications.'), + tag: messages.FLAG_PRELOAD, }, gulpfile: { alias: 'f', type: 'string', requiresArg: true, - desc: chalk.gray( - 'Manually set path of gulpfile. Useful if you have multiple gulpfiles. ' + - 'This will set the CWD to the gulpfile directory as well.'), + tag: messages.FLAG_GULPFILE, }, cwd: { type: 'string', requiresArg: true, - desc: chalk.gray( - 'Manually set the CWD. The search for the gulpfile, ' + - 'as well as the relativity of all requires will be from here.'), + tag: messages.FLAG_CWD, }, tasks: { alias: 'T', type: 'boolean', - desc: chalk.gray( - 'Print the task dependency tree for the loaded gulpfile.'), + tag: messages.FLAG_TASKS, }, 'tasks-simple': { type: 'boolean', - desc: chalk.gray( - 'Print a plaintext list of tasks for the loaded gulpfile.'), + tag: messages.FLAG_TASKS_SIMPLE, }, 'tasks-json': { - desc: chalk.gray( - 'Print the task dependency tree, ' + - 'in JSON format, for the loaded gulpfile.'), + tag: messages.FLAG_TASKS_JSON, }, 'tasks-depth': { alias: 'depth', type: 'number', requiresArg: true, default: undefined, // To detect if this cli option is specified. - desc: chalk.gray( - 'Specify the depth of the task dependency tree.'), + tag: messages.FLAG_TASKS_DEPTH, }, 'compact-tasks': { type: 'boolean', default: undefined, // To detect if this cli option is specified. - desc: chalk.gray( - 'Reduce the output of task dependency tree by printing ' + - 'only top tasks and their child tasks.'), + tag: messages.FLAG_COMPACT_TASKS, }, 'sort-tasks': { type: 'boolean', default: undefined, // To detect if this cli option is specified. - desc: chalk.gray( - 'Will sort top tasks of task dependency tree.'), + tag: messages.FLAG_SORT_TASKS, }, color: { type: 'boolean', - desc: chalk.gray( - 'Will force gulp and gulp plugins to display colors, ' + - 'even when no color support is detected.'), + tag: messages.FLAG_COLOR, }, 'no-color': { type: 'boolean', - desc: chalk.gray( - 'Will force gulp and gulp plugins to not display colors, ' + - 'even when color support is detected.'), + tag: messages.FLAG_NO_COLOR, }, silent: { alias: 'S', type: 'boolean', default: undefined, // To detect if this cli option is specified. - desc: chalk.gray( - 'Suppress all gulp logging.'), + tag: messages.FLAG_SILENT, }, continue: { type: 'boolean', default: undefined, // To detect if this cli option is specified. - desc: chalk.gray( - 'Continue execution of tasks upon failure.'), + tag: messages.FLAG_CONTINUE, }, series: { type: 'boolean', default: undefined, // To detect if this cli option is specified. - desc: chalk.gray( - 'Run tasks given on the CLI in series (the default is parallel).'), + tag: messages.FLAG_SERIES, }, '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: chalk.gray( - 'Set the loglevel. -L for least verbose and -LLLL for most verbose. ' + - '-LLL is default.'), + tag: messages.FLAG_LOG_LEVEL, } }; diff --git a/lib/shared/options/make-help.js b/lib/shared/options/make-help.js new file mode 100644 index 00000000..7a56387f --- /dev/null +++ b/lib/shared/options/make-help.js @@ -0,0 +1,24 @@ +'use strict'; + +var cliOptions = require('./cli-options'); + +var messages = require('@gulpjs/messages'); + +function makeHelp(parser, translate) { + var usage = translate.message({ tag: messages.USAGE }); + if (usage) { + parser.usage(usage); + } + + Object.keys(cliOptions).forEach(function (flag) { + var opt = cliOptions[flag]; + var description = translate.message({ tag: opt.tag }); + if (description) { + parser.describe(flag, description); + } + }); + + return parser; +} + +module.exports = makeHelp; diff --git a/lib/shared/translate.js b/lib/shared/translate.js new file mode 100644 index 00000000..00c29ae6 --- /dev/null +++ b/lib/shared/translate.js @@ -0,0 +1,302 @@ +'use strict'; + +var util = require('util'); + +var chalk = require('chalk'); +var messages = require('@gulpjs/messages'); + +var tildify = require('./tildify'); +var formatTime = require('./log/format-hrtime'); + +function Timestamp() { + this.now = new Date(); +} + +Timestamp.prototype[util.inspect.custom] = function (depth, opts) { + var timestamp = this.now.toLocaleTimeString('en', { hour12: false }); + return '[' + opts.stylize(timestamp, 'date') + ']'; +}; + +function getDefaultMessage(data) { + switch (data.tag) { + case messages.PRELOAD_BEFORE: { + return 'Preloading external module: ' + chalk.magenta(data.name); + } + case messages.PRELOAD_SUCCESS: { + return 'Preloaded external module: ' + chalk.magenta(data.name) + } + case messages.PRELOAD_FAILURE: { + return chalk.yellow('Failed to preload external module: ') + chalk.magenta(data.name); + } + case messages.PRELOAD_ERROR: { + return chalk.yellow(data.error.toString()); + } + case messages.LOADER_SUCCESS: { + return 'Loaded external module: ' + chalk.magenta(data.name); + } + case messages.LOADER_FAILURE: { + return chalk.yellow('Failed to load external module: ') + chalk.magenta(data.name); + } + case messages.LOADER_ERROR: { + return chalk.yellow(data.error.toString()); + } + case messages.NODE_FLAGS: { + var nodeFlags = chalk.magenta(data.flags.join(', ')); + return 'Node flags detected: ' + nodeFlags; + } + case messages.RESPAWNED: { + var pid = chalk.magenta(data.pid); + return 'Respawned to PID: ' + pid; + } + case messages.MISSING_GULPFILE: { + return chalk.red('No gulpfile found'); + } + case messages.CWD_CHANGED: { + return 'Working directory changed to ' + chalk.magenta(tildify(data.cwd)); + } + case messages.UNSUPPORTED_GULP_VERSION: { + return chalk.red('Unsupported gulp version', data.version) + } + case messages.DESCRIPTION: { + return 'Tasks for ' + chalk.magenta(tildify(data.path)); + } + case messages.GULPFILE: { + return 'Using gulpfile ' + chalk.magenta(tildify(data.path)); + } + case messages.TASK_START: { + return "Starting '" + chalk.cyan(data.task) + "'..." + } + case messages.TASK_STOP: { + return "Finished '" + chalk.cyan(data.task) + "' after " + chalk.magenta(formatTime(data.duration)); + } + case messages.TASK_FAILURE: { + return "'" + chalk.cyan(data.task) + "' " + chalk.red('errored after') + ' ' + chalk.magenta(formatTime(data.duration)); + } + case messages.TASK_MISSING: { + if (data.similar) { + return chalk.red('Task never defined: ' + data.task + ' - did you mean? ' + data.similar.join(', ')) + + '\nTo list available tasks, try running: gulp --tasks'; + } else { + return chalk.red('Task never defined: ' + data.task) + + '\nTo list available tasks, try running: gulp --tasks'; + } + } + case messages.TASK_SYNC: { + return chalk.red('The following tasks did not complete: ') + chalk.cyan(data.tasks) + "\n" + + chalk.red('Did you forget to signal async completion?'); + } + case messages.MISSING_NODE_MODULES: { + return chalk.red('Local modules not found in') + ' ' + chalk.magenta(tildify(data.cwd)); + } + case messages.MISSING_GULP: { + return chalk.red('Local gulp not found in') + ' ' + chalk.magenta(tildify(data.cwd)); + } + case messages.YARN_INSTALL: { + return chalk.red('Try running: yarn install'); + } + case messages.NPM_INSTALL: { + return chalk.red('Try running: npm install'); + } + case messages.YARN_INSTALL_GULP: { + return chalk.red('Try running: yarn add gulp'); + } + case messages.NPM_INSTALL_GULP: { + return chalk.red('Try running: npm install gulp'); + } + case messages.COMPLETION_TYPE_MISSING: { + return 'Missing completion type'; + } + case messages.COMPLETION_TYPE_UNKNOWN: { + return 'echo "gulp autocompletion rules for' + " '" + data.name + "' " + 'not found"' + } + case messages.ARGV_ERROR: { + return data.message; + } + case messages.EXEC_ERROR: { + return data.message; + } + case messages.TASK_ERROR: { + return data.message; + } + case messages.USAGE: { + return '\n' + chalk.bold('Usage:') + ' gulp ' + chalk.blue('[options]') + ' tasks'; + } + case messages.FLAG_HELP: { + return chalk.gray('Show this help.'); + } + case messages.FLAG_VERSION: { + return chalk.gray('Print the global and local gulp versions.'); + } + case messages.FLAG_PRELOAD: { + return chalk.gray( + 'Will preload a module before running the gulpfile. ' + + 'This is useful for transpilers but also has other applications.' + ); + } + case messages.FLAG_GULPFILE: { + return chalk.gray( + 'Manually set path of gulpfile. Useful if you have multiple gulpfiles. ' + + 'This will set the CWD to the gulpfile directory as well.' + ) + } + case messages.FLAG_CWD: { + return chalk.gray( + 'Manually set the CWD. The search for the gulpfile, ' + + 'as well as the relativity of all requires will be from here.' + ); + } + case messages.FLAG_TASKS: { + return chalk.gray('Print the task dependency tree for the loaded gulpfile.'); + } + case messages.FLAG_TASKS_SIMPLE: { + return chalk.gray('Print a plaintext list of tasks for the loaded gulpfile.'); + } + case messages.FLAG_TASKS_JSON: { + return chalk.gray( + 'Print the task dependency tree, ' + + 'in JSON format, for the loaded gulpfile.' + ); + } + case messages.FLAG_TASKS_DEPTH: { + return chalk.gray('Specify the depth of the task dependency tree.'); + } + case messages.FLAG_COMPACT_TASKS: { + return chalk.gray( + 'Reduce the output of task dependency tree by printing ' + + 'only top tasks and their child tasks.' + ); + } + case messages.FLAG_SORT_TASKS: { + return chalk.gray('Will sort top tasks of task dependency tree.'); + } + case messages.FLAG_COLOR: { + return chalk.gray( + 'Will force gulp and gulp plugins to display colors, ' + + 'even when no color support is detected.' + ); + } + case messages.FLAG_NO_COLOR: { + return chalk.gray( + 'Will force gulp and gulp plugins to not display colors, ' + + 'even when color support is detected.' + ); + } + case messages.FLAG_SILENT: { + return chalk.gray('Suppress all gulp logging.'); + } + case messages.FLAG_CONTINUE: { + return chalk.gray('Continue execution of tasks upon failure.'); + } + case messages.FLAG_SERIES: { + return chalk.gray('Run tasks given on the CLI in series (the default is parallel).'); + } + case messages.FLAG_LOG_LEVEL: { + return chalk.gray( + 'Set the loglevel. -L for least verbose and -LLLL for most verbose. ' + + '-LLL is default.' + ); + } + case messages.TASK_NAME: { + return chalk.cyan(data.name); + } + case messages.TASK_DESCRIPTION: { + return chalk.white(data.description); + } + case messages.TASK_FLAG: { + return chalk.magenta(data.flag); + } + case messages.TASK_FLAG_DESCRIPTION: { + return chalk.white('…' + data.description); + } + case messages.BOX_DRAWINGS_LIGHT_UP_AND_RIGHT: { + return chalk.white('└'); + } + case messages.BOX_DRAWINGS_LIGHT_VERTICAL_AND_RIGHT: { + return chalk.white('├'); + } + case messages.BOX_DRAWINGS_LIGHT_HORIZONTAL: { + return chalk.white('─'); + } + case messages.BOX_DRAWINGS_LIGHT_DOWN_AND_HORIZONTAL: { + return chalk.white('┬'); + } + case messages.BOX_DRAWINGS_LIGHT_VERTICAL: { + return chalk.white('│'); + } + default: { + return data; + } + } +} + +function getDefaultTimestamp() { + return util.inspect(new Timestamp(), { colors: !!chalk.supportsColor }); +} + +function buildTranslations(cfg) { + cfg = cfg || {}; + + return { + message: function (data) { + // Don't allow an `undefined` message through + if (typeof data === 'undefined') { + data = Object.create(null); + } + + var message; + if (typeof cfg.message === 'function') { + try { + message = cfg.message(data); + } catch (err) { + console.error('A problem occurred with the user-defined `message()` function.'); + console.error('Please correct your `.gulp.*` config file.'); + } + } + + // If the user has provided a message, return it + if (message) { + return message; + } + + // Users can filter messages by explicitly returning `false` + if (message === false) { + return ''; + } + + // If the user hasn't returned a message or silenced it with `false` + // get the default message. Will return the first argument if the message + // is not defined in the `@gulpjs/messages` package + return getDefaultMessage(data); + }, + timestamp: function (data) { + // Don't allow an `undefined` message through + if (typeof data === 'undefined') { + data = Object.create(null); + } + + var time; + if (typeof cfg.timestamp === 'function') { + try { + time = cfg.timestamp(data); + } catch (err) { + console.error('A problem occurred with the user-defined `timestamp()` function.'); + console.error('Please correct your `.gulp.*` config file.'); + } + } + + // If the user has provided a timestamp, return it + if (time) { + return time; + } + + // Users can filter timestamps by explicitly returning `false` + if (time === false) { + return ''; + } + + return getDefaultTimestamp(); + } + } +} + +module.exports = buildTranslations; diff --git a/lib/versioned/^3.7.0/index.js b/lib/versioned/^3.7.0/index.js index 397bd579..bce66146 100644 --- a/lib/versioned/^3.7.0/index.js +++ b/lib/versioned/^3.7.0/index.js @@ -4,12 +4,11 @@ var fs = require('fs'); var log = require('gulplog'); var stdout = require('mute-stdout'); -var chalk = require('chalk'); +var messages = require('@gulpjs/messages'); var taskTree = require('./task-tree'); var copyTree = require('../../shared/log/copy-tree'); -var tildify = require('../../shared/tildify'); var logTasks = require('../../shared/log/tasks'); var exit = require('../../shared/exit'); var logEvents = require('./log/events'); @@ -17,7 +16,7 @@ var logTasksSimple = require('./log/tasks-simple'); var registerExports = require('../../shared/register-exports'); var requireOrImport = require('../../shared/require-or-import'); -function execute(env, cfg, opts) { +function execute(env, opts, translate) { var tasks = opts._; var toRun = tasks.length ? tasks : ['default']; @@ -35,7 +34,7 @@ function execute(env, cfg, opts) { exit(1); } - log.info('Using gulpfile', chalk.magenta(tildify(env.configPath))); + log.info({ tag: messages.GULPFILE, path: env.configPath }); var gulpInst = require(env.modulePath); logEvents(gulpInst); @@ -51,22 +50,14 @@ function execute(env, cfg, opts) { } if (opts.tasks) { tree = taskTree(gulpInst.tasks); - if (cfg.description && typeof cfg.description === 'string') { - tree.label = cfg.description; - } else { - tree.label = 'Tasks for ' + chalk.magenta(tildify(env.configPath)); - } + tree.label = translate.message({ tag: messages.DESCRIPTION, path: env.configPath }); return logTasks(tree, opts, function(task) { return gulpInst.tasks[task].fn; - }); + }, translate); } if (opts.tasksJson) { tree = taskTree(gulpInst.tasks); - if (cfg.description && typeof cfg.description === 'string') { - tree.label = cfg.description; - } else { - tree.label = 'Tasks for ' + tildify(env.configPath); - } + tree.label = translate.message({ tag: messages.DESCRIPTION, path: env.configPath }); var output = JSON.stringify(copyTree(tree, opts)); diff --git a/lib/versioned/^3.7.0/log/events.js b/lib/versioned/^3.7.0/log/events.js index a20de7ce..3fb63398 100644 --- a/lib/versioned/^3.7.0/log/events.js +++ b/lib/versioned/^3.7.0/log/events.js @@ -1,8 +1,7 @@ 'use strict'; var log = require('gulplog'); -var formatTime = require('../../../shared/log/format-hrtime'); -var chalk = require('chalk'); +var messages = require('@gulpjs/messages'); var exit = require('../../../shared/exit'); var formatError = require('../format-error'); @@ -26,33 +25,20 @@ function logEvents(gulpInst) { gulpInst.on('task_start', function(e) { // TODO: batch these // so when 5 tasks start at once it only logs one time with all 5 - log.info('Starting', '\'' + chalk.cyan(e.task) + '\'...'); + log.info({ tag: messages.TASK_START, task: e.task }); }); gulpInst.on('task_stop', function(e) { - var time = formatTime(e.hrDuration); - log.info( - 'Finished', '\'' + chalk.cyan(e.task) + '\'', - 'after', chalk.magenta(time) - ); + log.info({ tag: messages.TASK_STOP, task: e.task, duration: e.hrDuration }); }); gulpInst.on('task_err', function(e) { - var msg = formatError(e); - var time = formatTime(e.hrDuration); - log.error( - '\'' + chalk.cyan(e.task) + '\'', - chalk.red('errored after'), - chalk.magenta(time) - ); - log.error(msg); + log.error({ tag: messages.TASK_FAILURE, task: e.task, duration: e.hrDuration }); + log.error({ tag: messages.TASK_ERROR, message: formatError(e) }); }); gulpInst.on('task_not_found', function(err) { - log.error( - chalk.red('Task \'' + err.task + '\' is not in your gulpfile') - ); - log.error('Please check the documentation for proper gulpfile formatting'); + log.error({ tag: messages.TASK_MISSING, task: err.task }); exit(1); }); } diff --git a/lib/versioned/^4.0.0/index.js b/lib/versioned/^4.0.0/index.js index c1888bb3..4adb9164 100644 --- a/lib/versioned/^4.0.0/index.js +++ b/lib/versioned/^4.0.0/index.js @@ -4,10 +4,9 @@ var fs = require('fs'); var log = require('gulplog'); var stdout = require('mute-stdout'); +var messages = require('@gulpjs/messages'); -var chalk = require('chalk'); var exit = require('../../shared/exit'); -var tildify = require('../../shared/tildify'); var logTasks = require('../../shared/log/tasks'); var logEvents = require('./log/events'); @@ -20,7 +19,7 @@ var copyTree = require('../../shared/log/copy-tree'); var getTask = require('./log/get-task'); var requireOrImport = require('../../shared/require-or-import'); -function execute(env, cfg, opts) { +function execute(env, opts, translate) { var tasks = opts._; var toRun = tasks.length ? tasks : ['default']; @@ -54,21 +53,13 @@ function execute(env, cfg, opts) { } if (opts.tasks) { tree = gulpInst.tree({ deep: true }); - if (cfg.description && typeof cfg.description === 'string') { - tree.label = cfg.description; - } else { - tree.label = 'Tasks for ' + chalk.magenta(tildify(env.configPath)); - } + tree.label = translate.message({ tag: messages.DESCRIPTION, path: env.configPath }); - return logTasks(tree, opts, getTask(gulpInst)); + return logTasks(tree, opts, getTask(gulpInst), translate); } if (opts.tasksJson) { tree = gulpInst.tree({ deep: true }); - if (cfg.description && typeof cfg.description === 'string') { - tree.label = cfg.description; - } else { - tree.label = 'Tasks for ' + tildify(env.configPath); - } + tree.label = translate.message({ tag: messages.DESCRIPTION, path: env.configPath }); var output = JSON.stringify(copyTree(tree, opts)); @@ -78,7 +69,7 @@ function execute(env, cfg, opts) { return fs.writeFileSync(opts.tasksJson, output, 'utf-8'); } try { - log.info('Using gulpfile', chalk.magenta(tildify(env.configPath))); + log.info({ tag: messages.GULPFILE, path: env.configPath }); var runMethod = opts.series ? 'series' : 'parallel'; gulpInst[runMethod](toRun)(function(err) { if (err) { @@ -88,10 +79,9 @@ function execute(env, cfg, opts) { } catch (err) { normalizeError(err); if (err.task) { - log.error(chalk.red(err.message)); - log.error(chalk.red('To list available tasks, try running: gulp --tasks')); - } else /* istanbul ignore next */ { - log.error(chalk.red(err.message)); + log.error({ tag: messages.TASK_MISSING, task: err.task, similar: err.similar }); + } else { + log.error({ tag: messages.EXEC_ERROR, message: err.message, error: err }); } exit(1); } diff --git a/lib/versioned/^4.0.0/log/events.js b/lib/versioned/^4.0.0/log/events.js index 0fb82c5c..af20f656 100644 --- a/lib/versioned/^4.0.0/log/events.js +++ b/lib/versioned/^4.0.0/log/events.js @@ -1,9 +1,8 @@ 'use strict'; var log = require('gulplog'); -var formatTime = require('../../../shared/log/format-hrtime'); +var messages = require('@gulpjs/messages'); -var chalk = require('chalk'); var formatError = require('../format-error'); // Wire up logging events @@ -16,32 +15,22 @@ function logEvents(gulpInst) { // TODO: batch these // so when 5 tasks start at once it only logs one time with all 5 var level = evt.branch ? 'debug' : 'info'; - log[level]('Starting', '\'' + chalk.cyan(evt.name) + '\'...'); + log[level]({ tag: messages.TASK_START, task: evt.name }); }); gulpInst.on('stop', function(evt) { - var time = formatTime(evt.duration); /* istanbul ignore next */ var level = evt.branch ? 'debug' : 'info'; - log[level]( - 'Finished', '\'' + chalk.cyan(evt.name) + '\'', - 'after', chalk.magenta(time) - ); + log[level]({ tag: messages.TASK_STOP, task: evt.name, duration: evt.duration }); }); gulpInst.on('error', function(evt) { - var msg = formatError(evt); - var time = formatTime(evt.duration); var level = evt.branch ? 'debug' : 'error'; - log[level]( - '\'' + chalk.cyan(evt.name) + '\'', - chalk.red('errored after'), - chalk.magenta(time) - ); + log[level]({ tag: messages.TASK_FAILURE, task: evt.name, duration: evt.duration }); // If we haven't logged this before, log it and add to list if (loggedErrors.indexOf(evt.error) === -1) { - log.error(msg); + log.error({ tag: messages.TASK_ERROR, message: formatError(evt) }); loggedErrors.push(evt.error); } }); diff --git a/lib/versioned/^4.0.0/log/sync-task.js b/lib/versioned/^4.0.0/log/sync-task.js index ff380f10..70c38a78 100644 --- a/lib/versioned/^4.0.0/log/sync-task.js +++ b/lib/versioned/^4.0.0/log/sync-task.js @@ -1,7 +1,7 @@ 'use strict'; var log = require('gulplog'); -var chalk = require('chalk'); +var messages = require('@gulpjs/messages'); var tasks = {}; @@ -18,13 +18,7 @@ function warn() { process.exitCode = 1; - log.warn( - chalk.red('The following tasks did not complete:'), - chalk.cyan(taskNames) - ); - log.warn( - chalk.red('Did you forget to signal async completion?') - ); + log.warn({ tag: messages.TASK_SYNC, tasks: taskNames }); } function start(e) { diff --git a/package.json b/package.json index dace639b..d67c575d 100644 --- a/package.json +++ b/package.json @@ -30,15 +30,16 @@ "cover": "nyc mocha --async-only --timeout 5000 test/lib test" }, "dependencies": { + "@gulpjs/messages": "^1.0.0", "chalk": "^4.1.2", "copy-props": "^4.0.0", - "fancy-log": "^2.0.0", - "gulplog": "^2.0.1", + "gulplog": "^2.1.0", "interpret": "^3.1.1", "liftoff": "^5.0.0", "mute-stdout": "^2.0.0", "replace-homedir": "^2.0.0", "semver-greatest-satisfied-range": "^2.0.0", + "string-width": "^4.2.3", "v8flags": "^4.0.0", "yargs": "^16.2.0" }, diff --git a/test/config-description.js b/test/config-description.js deleted file mode 100644 index 2a185459..00000000 --- a/test/config-description.js +++ /dev/null @@ -1,72 +0,0 @@ -'use strict'; - -var expect = require('expect'); -var exec = require('child_process').exec; -var path = require('path'); -var fs = require('fs'); - -var sliceLines = require('./tool/slice-lines'); -var eraseTime = require('./tool/erase-time'); -var gulp = require('./tool/gulp-cmd'); - -var baseDir = path.join(__dirname, 'fixtures', 'config'); -var expectedDir = path.join(__dirname, 'expected', 'config'); - -describe('config: description', function() { - - it('Should configure with a .gulp.* file in cwd', function(done) { - var opts = { cwd: path.join(baseDir, 'foo/bar') }; - exec(gulp('--tasks'), opts, cb); - - function cb(err, stdout, stderr) { - expect(err).toBeNull(); - expect(stderr).toEqual(''); - var expected = fs.readFileSync(path.join(expectedDir, 'output0.txt'), 'utf-8'); - expect(eraseTime(stdout)).toEqual(expected); - done(err); - } - }); - - it('Should configure with a .gulp.* file in cwd found up', function(done) { - var opts = { cwd: path.join(baseDir, 'foo/bar/baz') }; - exec(gulp('--tasks'), opts, cb); - - function cb(err, stdout, stderr) { - expect(err).toBeNull(); - expect(stderr).toEqual(''); - var expected = fs.readFileSync(path.join(expectedDir, 'output0.txt'), 'utf-8'); - expect(sliceLines(stdout, 1)).toEqual(expected); - done(err); - } - }); - - it('Should configure with a .gulp.* file in cwd even if it is not a project root', function(done) { - var opts = { cwd: path.join(baseDir, 'foo/bar/quux') }; - exec(gulp('--tasks'), opts, cb); - - function cb(err, stdout, stderr) { - expect(err).toBeNull(); - expect(stderr).toEqual(''); - var expected = fs.readFileSync(path.join(expectedDir, 'output2.txt'), 'utf-8'); - expect(sliceLines(stdout, 2)).toEqual(expected); - done(err); - } - }); - - it('Should configure with a .gulp.* file in cwd by --cwd', function(done) { - var opts = { cwd: path.join(baseDir, 'qux') }; - exec(gulp( - '--tasks', - '--gulpfile ../foo/bar/gulpfile.js', - '--cwd .' - ), opts, cb); - - function cb(err, stdout, stderr) { - expect(err).toBeNull(); - expect(stderr).toEqual(''); - var expected = fs.readFileSync(path.join(expectedDir, 'output1.txt'), 'utf-8'); - expect(eraseTime(stdout)).toEqual(expected); - done(err); - } - }); -}); diff --git a/test/config-message-function.js b/test/config-message-function.js new file mode 100644 index 00000000..e39c1ede --- /dev/null +++ b/test/config-message-function.js @@ -0,0 +1,475 @@ +'use strict'; + +var expect = require('expect'); +var exec = require('child_process').exec; +var path = require('path'); +var fs = require('fs'); +var os = require('os'); + +var tildify = require('../lib/shared/tildify'); + +var baseDir = path.join(__dirname, 'fixtures/config/theming'); +var expectedDir = path.join(__dirname, 'expected/config/theming'); + +var eraseTime = require('./tool/erase-time'); +var eraseLapse = require('./tool/erase-lapse'); +var gulp = require('./tool/gulp-cmd'); + +describe('config: message function', function() { + + it('can change USAGE with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'USAGE'); + var expected = fs.readFileSync(path.join(expectedDir, 'usage.txt'), 'utf8'); + + var opts = { cwd: cwd }; + exec(gulp('--help'), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).toBeNull(); + expect(stderr).toEqual(''); + expect(stdout).toEqual(expected); + done(err); + } + }); + + it('can change flag descriptions with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'flags'); + var expected = fs.readFileSync(path.join(expectedDir, 'flags.txt'), 'utf8'); + + var opts = { cwd: cwd }; + exec(gulp('--help'), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).toBeNull(); + expect(stderr).toEqual(''); + expect(stdout).toEqual(expected); + done(err); + } + }); + + it('can change DESCRIPTION with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'DESCRIPTION'); + var expected = '**DESCRIPTION**\n' + + '└── default\n'; + + var opts = { cwd: cwd }; + exec(gulp('--tasks'), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).toBeNull(); + expect(stderr).toEqual(''); + expect(stdout).toEqual(expected); + done(err); + } + }); + + it('can remove DESCRIPTION line output with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'DESCRIPTION/remove'); + var expected = '└── default\n'; + + var opts = { cwd: cwd }; + exec(gulp('--tasks'), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).toBeNull(); + expect(stderr).toEqual(''); + expect(stdout).toEqual(expected); + done(err); + } + }); + + it('can change DESCRIPTION for tasks-json with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'DESCRIPTION'); + var expected = JSON.stringify({ + label: '**DESCRIPTION**', + nodes: [{ + label: 'default', + type: 'task', + nodes: [], + }], + }) + '\n'; + + var opts = { cwd: cwd }; + exec(gulp('--tasks-json'), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).toBeNull(); + expect(stderr).toEqual(''); + expect(stdout).toEqual(expected); + done(err); + } + }); + + it('can change PRELOAD_BEFORE with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'PRELOAD_BEFORE'); + var expected = 'PRELOADING **./preload**!\n'; + + var opts = { cwd: cwd }; + exec(gulp('--preload ./preload'), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).toBeNull(); + expect(stderr).toEqual(''); + expect(eraseLapse(eraseTime(stdout))).toEqual(expected); + done(err); + } + }); + + it('can change PRELOAD_SUCCESS with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'PRELOAD_SUCCESS'); + var expected = 'PRELOADED **./preload**!\n'; + + var opts = { cwd: cwd }; + exec(gulp('--preload ./preload'), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).toBeNull(); + expect(stderr).toEqual(''); + expect(eraseLapse(eraseTime(stdout))).toEqual(expected); + done(err); + } + }); + + it('can change LOADER_SUCCESS with .gulp.*', function(done) { + this.timeout(0); + + var cwd = path.join(baseDir, 'LOADER_SUCCESS'); + var expected = 'LOADED **@babel/register**!\n'; + + var opts = { cwd: cwd }; + exec(gulp('-f gulpfile.babel.js'), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).toBeNull(); + expect(stderr).toEqual(''); + expect(eraseLapse(eraseTime(stdout))).toEqual(expected); + done(err); + } + }); + + it('can change NODE_FLAGS with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'NODE_FLAGS'); + var expected = 'RESPAWNED BY **--lazy**!\n'; + + var opts = { cwd: cwd }; + exec(gulp('--lazy'), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).toBeNull(); + expect(stderr).toEqual(''); + expect(eraseLapse(eraseTime(stdout))).toEqual(expected); + done(err); + } + }); + + it('can change RESPAWNED with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'RESPAWNED'); + var expected = 'RESPAWN!\n'; + + var opts = { cwd: cwd }; + exec(gulp('--lazy'), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).toBeNull(); + expect(stderr).toEqual(''); + expect(eraseLapse(eraseTime(stdout))).toEqual(expected); + done(err); + } + }); + + it('can change CWD_CHANGED with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'CWD_CHANGED'); + var expected = 'CHANGE CWD TO **' + cwd + '**\n'; + + var opts = { cwd: baseDir }; + exec(gulp('--cwd ' + cwd), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).toBeNull(); + expect(stderr).toEqual(''); + expect(eraseLapse(eraseTime(stdout))).toEqual(expected); + done(err); + } + }); + + it('can change GULPFILE with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'GULPFILE'); + var expected = 'USING GULPFILE **abcxyz**\n'; + + var opts = { cwd: cwd }; + exec(gulp(), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).toBeNull(); + expect(stderr).toEqual(''); + expect(eraseLapse(eraseTime(stdout))).toEqual(expected); + done(err); + } + }); + + it('can change TASK_START with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'TASK_START'); + var expected = 'START **default**\n'; + + var opts = { cwd: cwd }; + exec(gulp(), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).toBeNull(); + expect(stderr).toEqual(''); + expect(eraseLapse(eraseTime(stdout))).toEqual(expected); + done(err); + } + }); + + it('can change TASK_STOP with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'TASK_STOP'); + var expected = 'STOP **default**\n'; + + var opts = { cwd: cwd }; + exec(gulp(), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).toBeNull(); + expect(stderr).toEqual(''); + expect(eraseTime(stdout)).toEqual(expected); + done(err); + } + }); + + it('can change PRELOAD_FAILURE with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'PRELOAD_FAILURE'); + var expected = 'FAILED TO PRELOAD **null-module**\n' + + var opts = { cwd: cwd }; + exec(gulp('--preload null-module'), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).toBeNull(); + expect(stderr).toEqual(''); + expect(eraseLapse(eraseTime(stdout))).toEqual(expected); + done(err); + } + }); + + it('can change LOADER_FAILURE with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'LOADER_FAILURE'); + var expected = 'FAIL TO LOAD **coffeescript/register**\n'; + + var opts = { cwd: cwd }; + exec(gulp('-f gulpfile.coffee'), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).not.toBeNull(); + expect(stderr).not.toEqual(''); + expect(eraseTime(stdout)).toEqual(expected); + done(); + } + }); + + it('can change TASK_SYNC with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'TASK_SYNC'); + var gulpfile = tildify(path.join(cwd, 'gulpfile.js')); + var expected = 'Using gulpfile ' + gulpfile + '\n' + + 'Starting \'default\'...\n' + + 'TASK **default** DID NOT COMPLETE\n'; + + var opts = { cwd: cwd }; + exec(gulp(), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).not.toBeNull(); + expect(stderr).toEqual(''); + expect(eraseTime(stdout)).toEqual(expected); + done(); + } + }); + + it('can change ARGV_ERROR with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'ARGV_ERROR'); + var expected = fs.readFileSync(path.join(expectedDir, 'bad-flag.txt'), 'utf8'); + + var opts = { cwd: cwd }; + exec(gulp('-f'), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).not.toBeNull(); + expect(stdout).toEqual(''); + expect(stderr).toEqual(expected); + done(); + } + }); + + it('can change MISSING_GULP with .gulp.*', function(done) { + var dir = path.join(baseDir, 'MISSING_GULP'); + var cwd = os.tmpdir(); + fs.copyFileSync(path.join(dir, '.gulp.js'), path.join(cwd, '.gulp.js')); + + var opts = { cwd: cwd }; + exec(gulp(), opts, cb); + + if (os.platform() === 'darwin') { + cwd = path.join('/private', cwd); + } + var expected = 'GULP NOT FOUND IN **' + cwd + '**\n'; + + function cb(err, stdout, stderr) { + try { + expect(err).not.toBeNull(); + expect(stdout).toEqual(''); + expect(stderr).toEqual(expected); + done(); + } finally { + fs.unlinkSync(path.join(cwd, '.gulp.js')); + } + } + }); + + it('can change MISSING_NODE_MODULES with .gulp.*', function(done) { + var dir = path.join(baseDir, 'MISSING_NODE_MODULES'); + var cwd = os.tmpdir(); + fs.copyFileSync(path.join(dir, '.gulp.js'), path.join(cwd, '.gulp.js')); + fs.copyFileSync(path.join(dir, 'package.json'), path.join(cwd, 'package.json')); + var opts = { cwd: cwd }; + exec(gulp(), opts, cb); + + if (os.platform() === 'darwin') { + cwd = path.join('/private', cwd); + } + var expected = 'LOCAL MODULE NOT FOUND **' + cwd + '**\n'; + + function cb(err, stdout, stderr) { + try { + expect(err).not.toBeNull(); + expect(stdout).toEqual(''); + expect(eraseTime(stderr)).toEqual(expected); + done(); + } finally { + fs.unlinkSync(path.join(cwd, '.gulp.js')); + fs.unlinkSync(path.join(cwd, 'package.json')); + } + } + }); + + it('can change MISSING_GULPFILE with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'MISSING_GULPFILE'); + var expected = 'NO GULPFILE\n'; + + var opts = { cwd: cwd }; + exec(gulp(), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).not.toBeNull(); + expect(stdout).toEqual(''); + expect(eraseTime(stderr)).toEqual(expected); + done(); + } + }); + + it('can change UNSUPPORTED_GULP_VERSION with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'UNSUPPORTED_GULP_VERSION'); + var expected = 'BAD GULP VERSION **1.2.3**\n'; + + var opts = { cwd: cwd }; + exec(gulp(), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).not.toBeNull(); + expect(stdout).toEqual(''); + expect(eraseTime(stderr)).toEqual(expected); + done(); + } + }); + + it('can change TASK_FAILURE with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'TASK_FAILURE'); + var expectedStderr = 'TASK FAILURE: **default**\n'; + + var opts = { cwd: cwd }; + exec(gulp(), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).not.toBeNull(); + expect(stdout).toEqual(''); + expect(stderr).toEqual(expectedStderr); + done(); + } + }); + + it('can change TASK_ERROR with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'TASK_ERROR'); + var expectedStderr = '**TASK ERROR**\n'; + + var opts = { cwd: cwd }; + exec(gulp(), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).not.toBeNull(); + expect(stdout).toEqual(''); + expect(stderr).toEqual(expectedStderr); + done(); + } + }); + + it('can change TASK_MISSING with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'TASK_MISSING'); + var expectedStdout = 'Using gulpfile!\n'; + var expectedStderr = 'TASK IS NOT FOUND: **defaults** SIMILAR ##default##'; + + var opts = { cwd: cwd }; + exec(gulp('defaults'), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).not.toBeNull(); + expect(eraseTime(stderr)).toMatch(expectedStderr); + expect(eraseTime(stdout)).toEqual(expectedStdout); + done(); + } + }); + + // Would need to hook gulp to test this + it.skip('can change EXEC_ERROR with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'EXEC_ERROR'); + var expected = 'FAIL TO RUN\n'; + + var opts = { cwd: cwd }; + exec(gulp(), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).not.toBeNull(); + expect(eraseTime(stderr)).toEqual(expected); + expect(stdout).toEqual(''); + done(); + } + }); + + it('can change COMPLETION_TYPE_MISSING with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'COMPLETION_TYPE_MISSING'); + var expected = 'NO COMPLETION TYPE'; + + var opts = { cwd: cwd }; + exec(gulp('--completion'), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).not.toBeNull(); + expect(stderr).toMatch(expected); + expect(stdout).toEqual(''); + done(); + } + }); + + it('can change COMPLETION_TYPE_UNKNOWN with .gulp.*', function(done) { + var cwd = path.join(baseDir, 'COMPLETION_TYPE_UNKNOWN'); + var expected = 'GULP COMPLETION TYPE **xxx** IS NOT FOUND\n'; + + var opts = { cwd: cwd }; + exec(gulp('--completion=xxx'), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).not.toBeNull(); + expect(stderr).toEqual(''); + expect(stdout).toEqual(expected); + done(); + } + }); +}); diff --git a/test/execution-errors.js b/test/execution-errors.js index bd607e43..7b2fd1ec 100644 --- a/test/execution-errors.js +++ b/test/execution-errors.js @@ -4,6 +4,7 @@ var expect = require('expect'); var exec = require('child_process').exec; var path = require('path'); var os = require('os'); +var fs = require('fs'); var tildify = require('../lib/shared/tildify'); @@ -57,7 +58,7 @@ describe('execution error', function() { } }); - it('should output an error if gulp is not found', function(done) { + it('should output an error if gulp is not found (npm)', function(done) { var opts = { cwd: os.tmpdir() }; exec(gulp(), opts, cb); @@ -70,6 +71,79 @@ describe('execution error', function() { } }); + it('should output an error if gulp is not found (yarn)', function(done) { + var cwd = os.tmpdir(); + var yarnOrig= path.join(__dirname, 'fixtures/errors/yarn/yarn.lock'); + var yarnLock = path.join(cwd, 'yarn.lock'); + + fs.copyFileSync(yarnOrig, yarnLock); + + var opts = { cwd: cwd }; + exec(gulp(), opts, cb); + + function cb(err, stdout, stderr) { + try { + expect(err).not.toBeNull(); + expect(err.code).toEqual(1); + expect(sliceLines(stderr, 0, 1)).toMatch('Local gulp not found in '); + expect(sliceLines(stderr, 1, 2)).toEqual('Try running: yarn add gulp'); + done(); + } finally { + fs.unlinkSync(yarnLock); + } + } + }); + + it('should output an error if local modules are not found (npm)', function(done) { + var cwd = os.tmpdir(); + var pkgOrig = path.join(__dirname, 'fixtures/errors/package.json'); + var pkgJson = path.join(cwd, 'package.json'); + + fs.copyFileSync(pkgOrig, pkgJson); + + var opts = { cwd: cwd }; + exec(gulp(), opts, cb); + + function cb(err, stdout, stderr) { + try { + expect(err).not.toBeNull(); + expect(err.code).toEqual(1); + expect(sliceLines(stderr, 0, 1)).toMatch('Local modules not found in '); + expect(sliceLines(stderr, 1, 2)).toEqual('Try running: npm install'); + done(); + } finally { + fs.unlinkSync(pkgJson); + } + } + }); + + it('should output an error if local modules are not found (yarn)', function(done) { + var cwd = os.tmpdir(); + var pkgOrig = path.join(__dirname, 'fixtures/errors/package.json'); + var pkgJson = path.join(cwd, 'package.json'); + var yarnOrig= path.join(__dirname, 'fixtures/errors/yarn/yarn.lock'); + var yarnLock = path.join(cwd, 'yarn.lock'); + + fs.copyFileSync(pkgOrig, pkgJson); + fs.copyFileSync(yarnOrig, yarnLock); + + var opts = { cwd: cwd }; + exec(gulp(), opts, cb); + + function cb(err, stdout, stderr) { + try { + expect(err).not.toBeNull(); + expect(err.code).toEqual(1); + expect(sliceLines(stderr, 0, 1)).toMatch('Local modules not found in '); + expect(sliceLines(stderr, 1, 2)).toEqual('Try running: yarn install'); + done(); + } finally { + fs.unlinkSync(pkgJson); + fs.unlinkSync(yarnLock); + } + } + }); + it('should log a same error once', function(done) { var dir = path.join(__dirname, 'fixtures/gulpfiles'); var gulpfileName = 'gulpfile-dedup-errorlog.js'; diff --git a/test/expected/config/theming/bad-flag.txt b/test/expected/config/theming/bad-flag.txt new file mode 100644 index 00000000..5d37e6fa --- /dev/null +++ b/test/expected/config/theming/bad-flag.txt @@ -0,0 +1,41 @@ +**Not enough arguments following: f** + +Usage: gulp [options] tasks + +Options: + -h, --help Show this help. [boolean] + -v, --version Print the global and local gulp versions.[boolean] + --preload Will preload a module before running the gulpfile. + This is useful for transpilers but also has other + applications. [string] + -f, --gulpfile Manually set path of gulpfile. Useful if you have + multiple gulpfiles. This will set the CWD to the + gulpfile directory as well. [string] + --cwd Manually set the CWD. The search for the gulpfile, + as well as the relativity of all requires will be + from here. [string] + -T, --tasks Print the task dependency tree for the loaded + gulpfile. [boolean] + --tasks-simple Print a plaintext list of tasks for the loaded + gulpfile. [boolean] + --tasks-json Print the task dependency tree, in JSON format, + for the loaded gulpfile. + --tasks-depth, --depth Specify the depth of the task dependency tree. + [number] + --compact-tasks Reduce the output of task dependency tree by + printing only top tasks and their child tasks. + [boolean] + --sort-tasks Will sort top tasks of task dependency tree. + [boolean] + --color Will force gulp and gulp plugins to display + colors, even when no color support is detected. + [boolean] + --no-color Will force gulp and gulp plugins to not display + colors, even when color support is detected. + [boolean] + -S, --silent Suppress all gulp logging. [boolean] + --continue Continue execution of tasks upon failure.[boolean] + --series Run tasks given on the CLI in series (the default + is parallel). [boolean] + -L, --log-level Set the loglevel. -L for least verbose and -LLLL + for most verbose. -LLL is default. [count] diff --git a/test/expected/config/theming/flags.txt b/test/expected/config/theming/flags.txt new file mode 100644 index 00000000..a9da43fc --- /dev/null +++ b/test/expected/config/theming/flags.txt @@ -0,0 +1,18 @@ +Options: + -h, --help **HELP** [boolean] + -v, --version **VERSION** [boolean] + --preload **PRELOAD** [string] + -f, --gulpfile **GULPFILE** [string] + --cwd **CWD** [string] + -T, --tasks **TASKS** [boolean] + --tasks-simple **TASKS SIMPLE** [boolean] + --tasks-json **TASKS JSON** + --tasks-depth, --depth **TASKS DEPTH** [number] + --compact-tasks **COMPACT TASKS** [boolean] + --sort-tasks **SORT_TASKS** [boolean] + --color **COLOR** [boolean] + --no-color **NO COLOR** [boolean] + -S, --silent **SILENT** [boolean] + --continue **CONTINUE** [boolean] + --series **SERIES** [boolean] + -L, --log-level **LOG LEVEL** [count] diff --git a/test/expected/config/theming/usage.txt b/test/expected/config/theming/usage.txt new file mode 100644 index 00000000..b03f78bd --- /dev/null +++ b/test/expected/config/theming/usage.txt @@ -0,0 +1,39 @@ +GULP USAGE + +Options: + -h, --help Show this help. [boolean] + -v, --version Print the global and local gulp versions.[boolean] + --preload Will preload a module before running the gulpfile. + This is useful for transpilers but also has other + applications. [string] + -f, --gulpfile Manually set path of gulpfile. Useful if you have + multiple gulpfiles. This will set the CWD to the + gulpfile directory as well. [string] + --cwd Manually set the CWD. The search for the gulpfile, + as well as the relativity of all requires will be + from here. [string] + -T, --tasks Print the task dependency tree for the loaded + gulpfile. [boolean] + --tasks-simple Print a plaintext list of tasks for the loaded + gulpfile. [boolean] + --tasks-json Print the task dependency tree, in JSON format, + for the loaded gulpfile. + --tasks-depth, --depth Specify the depth of the task dependency tree. + [number] + --compact-tasks Reduce the output of task dependency tree by + printing only top tasks and their child tasks. + [boolean] + --sort-tasks Will sort top tasks of task dependency tree. + [boolean] + --color Will force gulp and gulp plugins to display + colors, even when no color support is detected. + [boolean] + --no-color Will force gulp and gulp plugins to not display + colors, even when color support is detected. + [boolean] + -S, --silent Suppress all gulp logging. [boolean] + --continue Continue execution of tasks upon failure.[boolean] + --series Run tasks given on the CLI in series (the default + is parallel). [boolean] + -L, --log-level Set the loglevel. -L for least verbose and -LLLL + for most verbose. -LLL is default. [count] diff --git a/test/expected/flags-tasks-depth1.txt b/test/expected/flags-tasks-depth1.txt new file mode 100644 index 00000000..d973a446 --- /dev/null +++ b/test/expected/flags-tasks-depth1.txt @@ -0,0 +1,4 @@ +gulp-cli/test/fixtures/gulpfiles +├── taskC +├── taskB +└── default diff --git a/test/fixtures/.gulp.js b/test/fixtures/.gulp.js index a1dacc5f..c2ca66b9 100644 --- a/test/fixtures/.gulp.js +++ b/test/fixtures/.gulp.js @@ -1,3 +1,9 @@ +var messages = require('@gulpjs/messages'); + module.exports = { - description: "gulp-cli/test/fixtures" + message: function (data) { + if (data.tag === messages.DESCRIPTION) { + return "gulp-cli/test/fixtures"; + } + } }; diff --git a/test/fixtures/config/theming/ARGV_ERROR/.gulp.js b/test/fixtures/config/theming/ARGV_ERROR/.gulp.js new file mode 100644 index 00000000..674530d6 --- /dev/null +++ b/test/fixtures/config/theming/ARGV_ERROR/.gulp.js @@ -0,0 +1,9 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.ARGV_ERROR) { + return '**' + data.message + '**'; + } + } +}; diff --git a/test/fixtures/config/theming/ARGV_ERROR/gulpfile.js b/test/fixtures/config/theming/ARGV_ERROR/gulpfile.js new file mode 100644 index 00000000..db73dd16 --- /dev/null +++ b/test/fixtures/config/theming/ARGV_ERROR/gulpfile.js @@ -0,0 +1,3 @@ +exports.default = function(done) { + done(); +} diff --git a/test/fixtures/config/theming/COMPLETION_TYPE_MISSING/.gulp.js b/test/fixtures/config/theming/COMPLETION_TYPE_MISSING/.gulp.js new file mode 100644 index 00000000..0d1b6b4d --- /dev/null +++ b/test/fixtures/config/theming/COMPLETION_TYPE_MISSING/.gulp.js @@ -0,0 +1,9 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.COMPLETION_TYPE_MISSING) { + return 'NO COMPLETION TYPE'; + } + } +}; diff --git a/test/fixtures/config/theming/COMPLETION_TYPE_MISSING/gulpfile.js b/test/fixtures/config/theming/COMPLETION_TYPE_MISSING/gulpfile.js new file mode 100644 index 00000000..db73dd16 --- /dev/null +++ b/test/fixtures/config/theming/COMPLETION_TYPE_MISSING/gulpfile.js @@ -0,0 +1,3 @@ +exports.default = function(done) { + done(); +} diff --git a/test/fixtures/config/theming/COMPLETION_TYPE_UNKNOWN/.gulp.js b/test/fixtures/config/theming/COMPLETION_TYPE_UNKNOWN/.gulp.js new file mode 100644 index 00000000..5db07839 --- /dev/null +++ b/test/fixtures/config/theming/COMPLETION_TYPE_UNKNOWN/.gulp.js @@ -0,0 +1,9 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.COMPLETION_TYPE_UNKNOWN) { + return 'GULP COMPLETION TYPE **' + data.name + '** IS NOT FOUND'; + } + } +}; diff --git a/test/fixtures/config/theming/COMPLETION_TYPE_UNKNOWN/gulpfile.js b/test/fixtures/config/theming/COMPLETION_TYPE_UNKNOWN/gulpfile.js new file mode 100644 index 00000000..db73dd16 --- /dev/null +++ b/test/fixtures/config/theming/COMPLETION_TYPE_UNKNOWN/gulpfile.js @@ -0,0 +1,3 @@ +exports.default = function(done) { + done(); +} diff --git a/test/fixtures/config/theming/CWD_CHANGED/.gulp.js b/test/fixtures/config/theming/CWD_CHANGED/.gulp.js new file mode 100644 index 00000000..abdb16ba --- /dev/null +++ b/test/fixtures/config/theming/CWD_CHANGED/.gulp.js @@ -0,0 +1,12 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.CWD_CHANGED) { + return 'CHANGE CWD TO **' + data.cwd + '**'; + } + + // Silence all other messages for test + return false; + } +}; diff --git a/test/fixtures/config/theming/CWD_CHANGED/gulpfile.js b/test/fixtures/config/theming/CWD_CHANGED/gulpfile.js new file mode 100644 index 00000000..db73dd16 --- /dev/null +++ b/test/fixtures/config/theming/CWD_CHANGED/gulpfile.js @@ -0,0 +1,3 @@ +exports.default = function(done) { + done(); +} diff --git a/test/fixtures/config/theming/DESCRIPTION/.gulp.js b/test/fixtures/config/theming/DESCRIPTION/.gulp.js new file mode 100644 index 00000000..6bfacc09 --- /dev/null +++ b/test/fixtures/config/theming/DESCRIPTION/.gulp.js @@ -0,0 +1,9 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.DESCRIPTION) { + return '**DESCRIPTION**'; + } + } +}; diff --git a/test/fixtures/config/theming/DESCRIPTION/gulpfile.js b/test/fixtures/config/theming/DESCRIPTION/gulpfile.js new file mode 100644 index 00000000..db73dd16 --- /dev/null +++ b/test/fixtures/config/theming/DESCRIPTION/gulpfile.js @@ -0,0 +1,3 @@ +exports.default = function(done) { + done(); +} diff --git a/test/fixtures/config/theming/DESCRIPTION/remove/.gulp.js b/test/fixtures/config/theming/DESCRIPTION/remove/.gulp.js new file mode 100644 index 00000000..8031c96d --- /dev/null +++ b/test/fixtures/config/theming/DESCRIPTION/remove/.gulp.js @@ -0,0 +1,9 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.DESCRIPTION) { + return false; + } + } +}; diff --git a/test/fixtures/config/theming/DESCRIPTION/remove/gulpfile.js b/test/fixtures/config/theming/DESCRIPTION/remove/gulpfile.js new file mode 100644 index 00000000..db73dd16 --- /dev/null +++ b/test/fixtures/config/theming/DESCRIPTION/remove/gulpfile.js @@ -0,0 +1,3 @@ +exports.default = function(done) { + done(); +} diff --git a/test/fixtures/config/theming/EXEC_ERROR/.gulp.js b/test/fixtures/config/theming/EXEC_ERROR/.gulp.js new file mode 100644 index 00000000..853d150c --- /dev/null +++ b/test/fixtures/config/theming/EXEC_ERROR/.gulp.js @@ -0,0 +1,13 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.EXEC_ERROR) { + return 'FAIL TO RUN'; + } + + if (data.tag === messages.GULPFILE) { + throw new Error('Crash before task execution'); + } + } +}; diff --git a/test/fixtures/config/theming/EXEC_ERROR/gulpfile.js b/test/fixtures/config/theming/EXEC_ERROR/gulpfile.js new file mode 100644 index 00000000..db73dd16 --- /dev/null +++ b/test/fixtures/config/theming/EXEC_ERROR/gulpfile.js @@ -0,0 +1,3 @@ +exports.default = function(done) { + done(); +} diff --git a/test/fixtures/config/theming/GULPFILE/.gulp.js b/test/fixtures/config/theming/GULPFILE/.gulp.js new file mode 100644 index 00000000..cbc5684f --- /dev/null +++ b/test/fixtures/config/theming/GULPFILE/.gulp.js @@ -0,0 +1,13 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.GULPFILE) { + return 'USING GULPFILE **abcxyz**'; + } + + // Silence all other messages for test + return false; + } +}; + diff --git a/test/fixtures/config/theming/GULPFILE/gulpfile.js b/test/fixtures/config/theming/GULPFILE/gulpfile.js new file mode 100644 index 00000000..db73dd16 --- /dev/null +++ b/test/fixtures/config/theming/GULPFILE/gulpfile.js @@ -0,0 +1,3 @@ +exports.default = function(done) { + done(); +} diff --git a/test/fixtures/config/theming/LOADER_FAILURE/.gulp.js b/test/fixtures/config/theming/LOADER_FAILURE/.gulp.js new file mode 100644 index 00000000..6ae4c154 --- /dev/null +++ b/test/fixtures/config/theming/LOADER_FAILURE/.gulp.js @@ -0,0 +1,14 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.LOADER_FAILURE) { + return 'FAIL TO LOAD **' + data.name + '**'; + } + + if (data.tag === messages.LOADER_ERROR) { + // Silence for test + return false; + } + } +}; diff --git a/test/fixtures/config/theming/LOADER_FAILURE/gulpfile.coffee b/test/fixtures/config/theming/LOADER_FAILURE/gulpfile.coffee new file mode 100644 index 00000000..1f8b8541 --- /dev/null +++ b/test/fixtures/config/theming/LOADER_FAILURE/gulpfile.coffee @@ -0,0 +1 @@ +console.log 'hello' diff --git a/test/fixtures/config/theming/LOADER_SUCCESS/.gulp.js b/test/fixtures/config/theming/LOADER_SUCCESS/.gulp.js new file mode 100644 index 00000000..2079b8ac --- /dev/null +++ b/test/fixtures/config/theming/LOADER_SUCCESS/.gulp.js @@ -0,0 +1,12 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.LOADER_SUCCESS) { + return 'LOADED **' + data.name + '**!'; + } + + // Silence all other messages for test + return false; + } +}; diff --git a/test/fixtures/config/theming/LOADER_SUCCESS/gulpfile.babel.js b/test/fixtures/config/theming/LOADER_SUCCESS/gulpfile.babel.js new file mode 100644 index 00000000..db73dd16 --- /dev/null +++ b/test/fixtures/config/theming/LOADER_SUCCESS/gulpfile.babel.js @@ -0,0 +1,3 @@ +exports.default = function(done) { + done(); +} diff --git a/test/fixtures/config/theming/MISSING_GULP/.gulp.js b/test/fixtures/config/theming/MISSING_GULP/.gulp.js new file mode 100644 index 00000000..a01c5ca4 --- /dev/null +++ b/test/fixtures/config/theming/MISSING_GULP/.gulp.js @@ -0,0 +1,16 @@ +module.exports = { + message: function (data) { + // Using `Symbol.for()` to avoid node_modules + if (data.tag === Symbol.for('GULP_CLI_MISSING_GULP')) { + return 'GULP NOT FOUND IN **' + data.cwd + '**'; + } + + if (data.tag === Symbol.for('GULP_CLI_NPM_INSTALL_GULP')) { + // Silence for test + return false; + } + }, + timestamp: function () { + return false; + } +}; diff --git a/test/fixtures/config/theming/MISSING_GULPFILE/.gulp.js b/test/fixtures/config/theming/MISSING_GULPFILE/.gulp.js new file mode 100644 index 00000000..f5c441a7 --- /dev/null +++ b/test/fixtures/config/theming/MISSING_GULPFILE/.gulp.js @@ -0,0 +1,9 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.MISSING_GULPFILE) { + return 'NO GULPFILE'; + } + } +}; diff --git a/test/fixtures/config/theming/MISSING_NODE_MODULES/.gulp.js b/test/fixtures/config/theming/MISSING_NODE_MODULES/.gulp.js new file mode 100644 index 00000000..80cca25d --- /dev/null +++ b/test/fixtures/config/theming/MISSING_NODE_MODULES/.gulp.js @@ -0,0 +1,14 @@ +module.exports = { + message: function (data) { + // Using `Symbol.for()` to avoid node_modules + if (data.tag === Symbol.for('GULP_CLI_MISSING_NODE_MODULES')) { + return 'LOCAL MODULE NOT FOUND **' + data.cwd + '**'; + } + + if (data.tag === Symbol.for('GULP_CLI_NPM_INSTALL')) { + // Silence for test + return false; + } + } +}; + diff --git a/test/fixtures/config/theming/MISSING_NODE_MODULES/package.json b/test/fixtures/config/theming/MISSING_NODE_MODULES/package.json new file mode 100644 index 00000000..71897b01 --- /dev/null +++ b/test/fixtures/config/theming/MISSING_NODE_MODULES/package.json @@ -0,0 +1,7 @@ +{ + "name": "node-modules-not-found", + "private": true, + "version": "1.0.0", + "description": "", + "main": ".gulp.js" +} diff --git a/test/fixtures/config/theming/NODE_FLAGS/.gulp.js b/test/fixtures/config/theming/NODE_FLAGS/.gulp.js new file mode 100644 index 00000000..33127d58 --- /dev/null +++ b/test/fixtures/config/theming/NODE_FLAGS/.gulp.js @@ -0,0 +1,12 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.NODE_FLAGS) { + return 'RESPAWNED BY **' + data.flags + '**!'; + } + + // Silence all other messages for test + return false; + } +}; diff --git a/test/fixtures/config/theming/NODE_FLAGS/gulpfile.js b/test/fixtures/config/theming/NODE_FLAGS/gulpfile.js new file mode 100644 index 00000000..db73dd16 --- /dev/null +++ b/test/fixtures/config/theming/NODE_FLAGS/gulpfile.js @@ -0,0 +1,3 @@ +exports.default = function(done) { + done(); +} diff --git a/test/fixtures/config/theming/PRELOAD_BEFORE/.gulp.js b/test/fixtures/config/theming/PRELOAD_BEFORE/.gulp.js new file mode 100644 index 00000000..a9ccf495 --- /dev/null +++ b/test/fixtures/config/theming/PRELOAD_BEFORE/.gulp.js @@ -0,0 +1,12 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.PRELOAD_BEFORE) { + return 'PRELOADING **' + data.name + '**!'; + } + + // Silence all other messages for test + return false; + } +}; diff --git a/test/fixtures/config/theming/PRELOAD_BEFORE/gulpfile.js b/test/fixtures/config/theming/PRELOAD_BEFORE/gulpfile.js new file mode 100644 index 00000000..db73dd16 --- /dev/null +++ b/test/fixtures/config/theming/PRELOAD_BEFORE/gulpfile.js @@ -0,0 +1,3 @@ +exports.default = function(done) { + done(); +} diff --git a/test/fixtures/config/theming/PRELOAD_BEFORE/preload.js b/test/fixtures/config/theming/PRELOAD_BEFORE/preload.js new file mode 100644 index 00000000..3b5d0c74 --- /dev/null +++ b/test/fixtures/config/theming/PRELOAD_BEFORE/preload.js @@ -0,0 +1 @@ +global.preload = 'hello preload!'; diff --git a/test/fixtures/config/theming/PRELOAD_FAILURE/.gulp.js b/test/fixtures/config/theming/PRELOAD_FAILURE/.gulp.js new file mode 100644 index 00000000..fc2a1570 --- /dev/null +++ b/test/fixtures/config/theming/PRELOAD_FAILURE/.gulp.js @@ -0,0 +1,12 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.PRELOAD_FAILURE) { + return 'FAILED TO PRELOAD **' + data.name + '**'; + } + + // Silence everything else for test + return false; + } +}; diff --git a/test/fixtures/config/theming/PRELOAD_FAILURE/gulpfile.js b/test/fixtures/config/theming/PRELOAD_FAILURE/gulpfile.js new file mode 100644 index 00000000..db73dd16 --- /dev/null +++ b/test/fixtures/config/theming/PRELOAD_FAILURE/gulpfile.js @@ -0,0 +1,3 @@ +exports.default = function(done) { + done(); +} diff --git a/test/fixtures/config/theming/PRELOAD_SUCCESS/.gulp.js b/test/fixtures/config/theming/PRELOAD_SUCCESS/.gulp.js new file mode 100644 index 00000000..177818cc --- /dev/null +++ b/test/fixtures/config/theming/PRELOAD_SUCCESS/.gulp.js @@ -0,0 +1,12 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.PRELOAD_SUCCESS) { + return 'PRELOADED **' + data.name + '**!'; + } + + // Silence all other messages for test + return false; + } +}; diff --git a/test/fixtures/config/theming/PRELOAD_SUCCESS/gulpfile.js b/test/fixtures/config/theming/PRELOAD_SUCCESS/gulpfile.js new file mode 100644 index 00000000..db73dd16 --- /dev/null +++ b/test/fixtures/config/theming/PRELOAD_SUCCESS/gulpfile.js @@ -0,0 +1,3 @@ +exports.default = function(done) { + done(); +} diff --git a/test/fixtures/config/theming/PRELOAD_SUCCESS/preload.js b/test/fixtures/config/theming/PRELOAD_SUCCESS/preload.js new file mode 100644 index 00000000..3b5d0c74 --- /dev/null +++ b/test/fixtures/config/theming/PRELOAD_SUCCESS/preload.js @@ -0,0 +1 @@ +global.preload = 'hello preload!'; diff --git a/test/fixtures/config/theming/RESPAWNED/.gulp.js b/test/fixtures/config/theming/RESPAWNED/.gulp.js new file mode 100644 index 00000000..1b79a3c7 --- /dev/null +++ b/test/fixtures/config/theming/RESPAWNED/.gulp.js @@ -0,0 +1,12 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.RESPAWNED) { + return 'RESPAWN!'; + } + + // Silence all other messages for test + return false; + } +}; diff --git a/test/fixtures/config/theming/RESPAWNED/gulpfile.js b/test/fixtures/config/theming/RESPAWNED/gulpfile.js new file mode 100644 index 00000000..db73dd16 --- /dev/null +++ b/test/fixtures/config/theming/RESPAWNED/gulpfile.js @@ -0,0 +1,3 @@ +exports.default = function(done) { + done(); +} diff --git a/test/fixtures/config/theming/TASK_ERROR/.gulp.js b/test/fixtures/config/theming/TASK_ERROR/.gulp.js new file mode 100644 index 00000000..77f9004a --- /dev/null +++ b/test/fixtures/config/theming/TASK_ERROR/.gulp.js @@ -0,0 +1,16 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.TASK_ERROR) { + return '**TASK ERROR**'; + } + + // Silence everything else for test + return false; + }, + timestamp: function () { + // Silence timestamps for test + return false; + } +}; diff --git a/test/fixtures/config/theming/TASK_ERROR/gulpfile.js b/test/fixtures/config/theming/TASK_ERROR/gulpfile.js new file mode 100644 index 00000000..eeb90fd6 --- /dev/null +++ b/test/fixtures/config/theming/TASK_ERROR/gulpfile.js @@ -0,0 +1,3 @@ +exports.default = function(done) { + throw new Error('FAIL!'); +} diff --git a/test/fixtures/config/theming/TASK_FAILURE/.gulp.js b/test/fixtures/config/theming/TASK_FAILURE/.gulp.js new file mode 100644 index 00000000..af699b5e --- /dev/null +++ b/test/fixtures/config/theming/TASK_FAILURE/.gulp.js @@ -0,0 +1,16 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.TASK_FAILURE) { + return 'TASK FAILURE: **' + data.task + '**'; + } + + // Silence everything else for test + return false; + }, + timestamp: function () { + // Silence timestamps for test + return false; + } +}; diff --git a/test/fixtures/config/theming/TASK_FAILURE/gulpfile.js b/test/fixtures/config/theming/TASK_FAILURE/gulpfile.js new file mode 100644 index 00000000..eeb90fd6 --- /dev/null +++ b/test/fixtures/config/theming/TASK_FAILURE/gulpfile.js @@ -0,0 +1,3 @@ +exports.default = function(done) { + throw new Error('FAIL!'); +} diff --git a/test/fixtures/config/theming/TASK_MISSING/.gulp.js b/test/fixtures/config/theming/TASK_MISSING/.gulp.js new file mode 100644 index 00000000..153f09fa --- /dev/null +++ b/test/fixtures/config/theming/TASK_MISSING/.gulp.js @@ -0,0 +1,13 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.GULPFILE) { + return 'Using gulpfile!'; + } + + if (data.tag === messages.TASK_MISSING) { + return 'TASK IS NOT FOUND: **' + data.task + '** SIMILAR ##' + data.similar.join('') + '##'; + } + } +}; diff --git a/test/fixtures/config/theming/TASK_MISSING/gulpfile.js b/test/fixtures/config/theming/TASK_MISSING/gulpfile.js new file mode 100644 index 00000000..db73dd16 --- /dev/null +++ b/test/fixtures/config/theming/TASK_MISSING/gulpfile.js @@ -0,0 +1,3 @@ +exports.default = function(done) { + done(); +} diff --git a/test/fixtures/config/theming/TASK_START/.gulp.js b/test/fixtures/config/theming/TASK_START/.gulp.js new file mode 100644 index 00000000..671c2c5c --- /dev/null +++ b/test/fixtures/config/theming/TASK_START/.gulp.js @@ -0,0 +1,12 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.TASK_START) { + return 'START **' + data.task + '**'; + } + + // Silence all other messages for test + return false; + } +}; diff --git a/test/fixtures/config/theming/TASK_START/gulpfile.js b/test/fixtures/config/theming/TASK_START/gulpfile.js new file mode 100644 index 00000000..db73dd16 --- /dev/null +++ b/test/fixtures/config/theming/TASK_START/gulpfile.js @@ -0,0 +1,3 @@ +exports.default = function(done) { + done(); +} diff --git a/test/fixtures/config/theming/TASK_STOP/.gulp.js b/test/fixtures/config/theming/TASK_STOP/.gulp.js new file mode 100644 index 00000000..23eee49b --- /dev/null +++ b/test/fixtures/config/theming/TASK_STOP/.gulp.js @@ -0,0 +1,12 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.TASK_STOP) { + return 'STOP **' + data.task + '**'; + } + + // Silence all other messages for test + return false; + } +}; diff --git a/test/fixtures/config/theming/TASK_STOP/gulpfile.js b/test/fixtures/config/theming/TASK_STOP/gulpfile.js new file mode 100644 index 00000000..db73dd16 --- /dev/null +++ b/test/fixtures/config/theming/TASK_STOP/gulpfile.js @@ -0,0 +1,3 @@ +exports.default = function(done) { + done(); +} diff --git a/test/fixtures/config/theming/TASK_SYNC/.gulp.js b/test/fixtures/config/theming/TASK_SYNC/.gulp.js new file mode 100644 index 00000000..a4911fe5 --- /dev/null +++ b/test/fixtures/config/theming/TASK_SYNC/.gulp.js @@ -0,0 +1,9 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.TASK_SYNC) { + return 'TASK **' + data.tasks + '** DID NOT COMPLETE'; + } + } +}; diff --git a/test/fixtures/config/theming/TASK_SYNC/gulpfile.js b/test/fixtures/config/theming/TASK_SYNC/gulpfile.js new file mode 100644 index 00000000..1e950f9a --- /dev/null +++ b/test/fixtures/config/theming/TASK_SYNC/gulpfile.js @@ -0,0 +1,3 @@ +exports.default = function(done) { + //done(); +} diff --git a/test/fixtures/config/theming/UNSUPPORTED_GULP_VERSION/.gulp.js b/test/fixtures/config/theming/UNSUPPORTED_GULP_VERSION/.gulp.js new file mode 100644 index 00000000..47159f8f --- /dev/null +++ b/test/fixtures/config/theming/UNSUPPORTED_GULP_VERSION/.gulp.js @@ -0,0 +1,9 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.UNSUPPORTED_GULP_VERSION) { + return 'BAD GULP VERSION **' + data.version + '**'; + } + } +}; diff --git a/test/fixtures/config/theming/UNSUPPORTED_GULP_VERSION/gulpfile.js b/test/fixtures/config/theming/UNSUPPORTED_GULP_VERSION/gulpfile.js new file mode 100644 index 00000000..db73dd16 --- /dev/null +++ b/test/fixtures/config/theming/UNSUPPORTED_GULP_VERSION/gulpfile.js @@ -0,0 +1,3 @@ +exports.default = function(done) { + done(); +} diff --git a/test/fixtures/config/theming/UNSUPPORTED_GULP_VERSION/node_modules/gulp/index.js b/test/fixtures/config/theming/UNSUPPORTED_GULP_VERSION/node_modules/gulp/index.js new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/config/theming/UNSUPPORTED_GULP_VERSION/node_modules/gulp/package.json b/test/fixtures/config/theming/UNSUPPORTED_GULP_VERSION/node_modules/gulp/package.json new file mode 100644 index 00000000..3585a2f6 --- /dev/null +++ b/test/fixtures/config/theming/UNSUPPORTED_GULP_VERSION/node_modules/gulp/package.json @@ -0,0 +1,5 @@ +{ + "name": "gulp", + "description": "Test Package for Testing!", + "version": "1.2.3" +} diff --git a/test/fixtures/config/theming/USAGE/.gulp.js b/test/fixtures/config/theming/USAGE/.gulp.js new file mode 100644 index 00000000..0c064d41 --- /dev/null +++ b/test/fixtures/config/theming/USAGE/.gulp.js @@ -0,0 +1,9 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.USAGE) { + return 'GULP USAGE'; + } + } +}; diff --git a/test/fixtures/config/theming/flags/.gulp.js b/test/fixtures/config/theming/flags/.gulp.js new file mode 100644 index 00000000..72020819 --- /dev/null +++ b/test/fixtures/config/theming/flags/.gulp.js @@ -0,0 +1,76 @@ +var messages = require('@gulpjs/messages'); + +module.exports = { + message: function (data) { + if (data.tag === messages.FLAG_HELP) { + return '**HELP**'; + } + + if (data.tag === messages.FLAG_VERSION) { + return '**VERSION**'; + } + + if (data.tag === messages.FLAG_PRELOAD) { + return '**PRELOAD**'; + } + + if (data.tag === messages.FLAG_GULPFILE) { + return '**GULPFILE**'; + } + + if (data.tag === messages.FLAG_CWD) { + return '**CWD**'; + } + + if (data.tag === messages.FLAG_TASKS) { + return '**TASKS**'; + } + + if (data.tag === messages.FLAG_TASKS_SIMPLE) { + return '**TASKS SIMPLE**'; + } + + if (data.tag === messages.FLAG_TASKS_JSON) { + return '**TASKS JSON**'; + } + + if (data.tag === messages.FLAG_TASKS_DEPTH) { + return '**TASKS DEPTH**'; + } + + if (data.tag === messages.FLAG_COMPACT_TASKS) { + return '**COMPACT TASKS**'; + } + + if (data.tag === messages.FLAG_SORT_TASKS) { + return '**SORT_TASKS**'; + } + + if (data.tag === messages.FLAG_COLOR) { + return '**COLOR**'; + } + + if (data.tag === messages.FLAG_NO_COLOR) { + return '**NO COLOR**'; + } + + if (data.tag === messages.FLAG_SILENT) { + return '**SILENT**'; + } + + if (data.tag === messages.FLAG_CONTINUE) { + return '**CONTINUE**'; + } + + if (data.tag === messages.FLAG_SERIES) { + return '**SERIES**'; + } + + if (data.tag === messages.FLAG_LOG_LEVEL) { + return '**LOG LEVEL**'; + } + + // Silence all other messages for test + return false; + } +}; diff --git a/test/fixtures/errors/yarn/yarn.lock b/test/fixtures/errors/yarn/yarn.lock new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/gulpfiles/.gulp.js b/test/fixtures/gulpfiles/.gulp.js index 7aa69cc2..33b2e061 100644 --- a/test/fixtures/gulpfiles/.gulp.js +++ b/test/fixtures/gulpfiles/.gulp.js @@ -1,3 +1,9 @@ +var messages = require('@gulpjs/messages'); + module.exports = { - description: "gulp-cli/test/fixtures/gulpfiles" + message: function (data) { + if (data.tag === messages.DESCRIPTION) { + return "gulp-cli/test/fixtures/gulpfiles"; + } + } }; diff --git a/test/fixtures/logging.js b/test/fixtures/logging.js index 6f5c5566..7b28ae8a 100644 --- a/test/fixtures/logging.js +++ b/test/fixtures/logging.js @@ -2,10 +2,13 @@ var log = require('gulplog'); var yargs = require('yargs'); var toConsole = require('../../lib/shared/log/to-console'); var cliOptions = require('../../lib/shared/options/cli-options'); +var buildTranslations = require('../../lib/shared/translate'); var opts = yargs.options(cliOptions).parse(); -toConsole(log, opts); +var translate = buildTranslations(); + +toConsole(log, opts, translate); log.debug('test debug'); log.info('test info'); diff --git a/test/flags-help.js b/test/flags-help.js index b1947b41..3007e288 100644 --- a/test/flags-help.js +++ b/test/flags-help.js @@ -58,4 +58,15 @@ describe('flag: --help', function() { } }); + it('show error message and help if options are invalid', function(done) { + var opts = { cwd: baseDir }; + exec(gulp('-f'), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).not.toBeNull(); + expect(stdout).toEqual(''); + expect(stderr).toEqual('Not enough arguments following: f\n' + outputText); + done(); + } + }); }); diff --git a/test/flags-tasks.js b/test/flags-tasks.js index 270b5ffc..68bd2a6a 100644 --- a/test/flags-tasks.js +++ b/test/flags-tasks.js @@ -31,7 +31,7 @@ describe('flag: --tasks', function() { } }); - it('print the task list with description and flags', function(done) { + it('prints the task list with description and flags', function(done) { var opts = { cwd: baseDir }; exec(gulp( '--tasks', @@ -50,7 +50,7 @@ describe('flag: --tasks', function() { } }); - it('print the task list by gulp.task(s).unwrap and gulp.task(s)', function(done) { + it('prints the task list by gulp.task(s).unwrap and gulp.task(s)', function(done) { var opts = { cwd: baseDir }; exec(gulp( '--tasks', @@ -122,6 +122,24 @@ describe('flag: --tasks', function() { } }); + it('prints the top task only if negative tasks depth is specified', function(done) { + var opts = { cwd: baseDir }; + exec(gulp( + '--tasks', + '--gulpfile ./test/fixtures/gulpfiles/gulpfile-4.js', + '--tasks-depth -1' + ), opts, cb); + + function cb(err, stdout, stderr) { + expect(err).toBeNull(); + expect(stderr).toEqual(''); + var filepath = path.join(expectedDir, 'flags-tasks-depth1.txt'); + var expected = fs.readFileSync(filepath, 'utf-8'); + expect(sliceLines(stdout, 1)).toEqual(expected); + done(err); + } + }); + it('prints the task list with --depth flag', function(done) { var opts = { cwd: baseDir }; exec(gulp( diff --git a/test/lib/config-cli-flags.js b/test/lib/config-cli-flags.js index e70a0d92..d78e07a1 100644 --- a/test/lib/config-cli-flags.js +++ b/test/lib/config-cli-flags.js @@ -3,7 +3,7 @@ var expect = require('expect'); var mergeCliOpts = require('../../lib/shared/config/cli-flags'); -describe('lib: config/cli-flags', function() { +describe('lib: cli-flags', function() { it('Should copy only config props specified to cli flags', function(done) { var opts = {}; diff --git a/test/lib/format-hrtime.js b/test/lib/format-hrtime.js index 71eb9989..d520c3a1 100644 --- a/test/lib/format-hrtime.js +++ b/test/lib/format-hrtime.js @@ -3,7 +3,7 @@ var expect = require('expect'); var formatHrtime = require('../../lib/shared/log/format-hrtime'); -describe('format-hrtime', function() { +describe('lib: format-hrtime', function() { describe('should convert hrtime to string: unit is "h"', function() { it('should be no decimal part if integer part greater than 10', function(done) { expect(formatHrtime([36000, 100])).toEqual('10 h'); diff --git a/test/lib/check-task-not-found.js b/test/lib/normalize-error.js similarity index 100% rename from test/lib/check-task-not-found.js rename to test/lib/normalize-error.js