From 81576e649a0cfb4b84bf28f43fa7d8ec24bc0700 Mon Sep 17 00:00:00 2001 From: Blaine Bublitz Date: Fri, 22 Mar 2024 21:15:43 -0700 Subject: [PATCH] feat: Add versioned handler for gulp v5 --- lib/versioned/^5.0.0/format-error.js | 24 +++++++ lib/versioned/^5.0.0/index.js | 89 ++++++++++++++++++++++++ lib/versioned/^5.0.0/log/events.js | 39 +++++++++++ lib/versioned/^5.0.0/log/get-task.js | 43 ++++++++++++ lib/versioned/^5.0.0/log/sync-task.js | 46 ++++++++++++ lib/versioned/^5.0.0/log/tasks-simple.js | 7 ++ 6 files changed, 248 insertions(+) create mode 100644 lib/versioned/^5.0.0/format-error.js create mode 100644 lib/versioned/^5.0.0/index.js create mode 100644 lib/versioned/^5.0.0/log/events.js create mode 100644 lib/versioned/^5.0.0/log/get-task.js create mode 100644 lib/versioned/^5.0.0/log/sync-task.js create mode 100644 lib/versioned/^5.0.0/log/tasks-simple.js diff --git a/lib/versioned/^5.0.0/format-error.js b/lib/versioned/^5.0.0/format-error.js new file mode 100644 index 00000000..3d8a7718 --- /dev/null +++ b/lib/versioned/^5.0.0/format-error.js @@ -0,0 +1,24 @@ +'use strict'; + +// Format orchestrator errors +/* istanbul ignore next */ +function formatError(e) { + if (!e.error) { + return e.message; + } + + // PluginError + if (typeof e.error.showStack === 'boolean') { + return e.error.toString(); + } + + // Normal error + if (e.error.stack) { + return e.error.stack; + } + + // Unknown (string, number, etc.) + return new Error(String(e.error)).stack; +} + +module.exports = formatError; diff --git a/lib/versioned/^5.0.0/index.js b/lib/versioned/^5.0.0/index.js new file mode 100644 index 00000000..d3258b3b --- /dev/null +++ b/lib/versioned/^5.0.0/index.js @@ -0,0 +1,89 @@ +'use strict'; + +var fs = require('fs'); + +var log = require('gulplog'); +var stdout = require('mute-stdout'); +var messages = require('@gulpjs/messages'); + +var exit = require('../../shared/exit'); + +var logTasks = require('../../shared/log/tasks'); +var logEvents = require('./log/events'); +var logSyncTask = require('./log/sync-task'); +var logTasksSimple = require('./log/tasks-simple'); +var registerExports = require('../../shared/register-exports'); + +var copyTree = require('../../shared/log/copy-tree'); +var getTask = require('./log/get-task'); +var requireOrImport = require('../../shared/require-or-import'); + +function execute(env, opts, translate) { + var tasks = opts._; + var toRun = tasks.length ? tasks : ['default']; + + if (opts.tasksSimple || opts.tasks || opts.tasksJson) { + // Mute stdout if we are listing tasks + stdout.mute(); + } + + var gulpInst = require(env.modulePath); + logEvents(gulpInst); + logSyncTask(gulpInst, opts); + + // This is what actually loads up the gulpfile + requireOrImport(env.configPath, function(err, exported) { + // Before import(), if require() failed we got an unhandled exception on the module level. + // So console.error() & exit() were added here to mimic the old behavior as close as possible. + if (err) { + console.error(err); + exit(1); + } + + registerExports(gulpInst, exported); + + // Always unmute stdout after gulpfile is required + stdout.unmute(); + + var tree; + if (opts.tasksSimple) { + tree = gulpInst.tree(); + return logTasksSimple(tree.nodes); + } + if (opts.tasks) { + tree = gulpInst.tree({ deep: true }); + tree.label = translate.message({ tag: messages.DESCRIPTION, path: env.configPath }); + + return logTasks(tree, opts, getTask(gulpInst), translate); + } + if (opts.tasksJson) { + tree = gulpInst.tree({ deep: true }); + tree.label = translate.message({ tag: messages.DESCRIPTION, path: env.configPath }); + + var output = JSON.stringify(copyTree(tree, opts)); + + if (typeof opts.tasksJson === 'boolean' && opts.tasksJson) { + return console.log(output); + } + return fs.writeFileSync(opts.tasksJson, output, 'utf-8'); + } + try { + log.info({ tag: messages.GULPFILE, path: env.configPath }); + var runMethod = opts.series ? 'series' : 'parallel'; + gulpInst[runMethod](toRun)(function(err) { + if (err) { + exit(1); + } + }); + } catch (err) { + if (err.task) { + 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); + } + }); +} + +module.exports = execute; diff --git a/lib/versioned/^5.0.0/log/events.js b/lib/versioned/^5.0.0/log/events.js new file mode 100644 index 00000000..af20f656 --- /dev/null +++ b/lib/versioned/^5.0.0/log/events.js @@ -0,0 +1,39 @@ +'use strict'; + +var log = require('gulplog'); +var messages = require('@gulpjs/messages'); + +var formatError = require('../format-error'); + +// Wire up logging events +function logEvents(gulpInst) { + + var loggedErrors = []; + + gulpInst.on('start', function(evt) { + /* istanbul ignore next */ + // 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]({ tag: messages.TASK_START, task: evt.name }); + }); + + gulpInst.on('stop', function(evt) { + /* istanbul ignore next */ + var level = evt.branch ? 'debug' : 'info'; + log[level]({ tag: messages.TASK_STOP, task: evt.name, duration: evt.duration }); + }); + + gulpInst.on('error', function(evt) { + var level = evt.branch ? 'debug' : 'error'; + 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({ tag: messages.TASK_ERROR, message: formatError(evt) }); + loggedErrors.push(evt.error); + } + }); +} + +module.exports = logEvents; diff --git a/lib/versioned/^5.0.0/log/get-task.js b/lib/versioned/^5.0.0/log/get-task.js new file mode 100644 index 00000000..144acb47 --- /dev/null +++ b/lib/versioned/^5.0.0/log/get-task.js @@ -0,0 +1,43 @@ +'use strict'; + +var isObject = require('../../../shared/is-object'); + +function getTask(gulpInst) { + return function(name) { + var task = gulpInst.task(name); + return { + description: getDescription(task), + flags: getFlags(task), + }; + }; +} + +function getDescription(task) { + if (typeof task.description === 'string') { + return task.description; + } + /* istanbul ignore else */ + if (typeof task.unwrap === 'function') { + var origFn = task.unwrap(); + if (typeof origFn.description === 'string') { + return origFn.description; + } + } + return undefined; +} + +function getFlags(task) { + if (isObject(task.flags)) { + return task.flags; + } + /* istanbul ignore else */ + if (typeof task.unwrap === 'function') { + var origFn = task.unwrap(); + if (isObject(origFn.flags)) { + return origFn.flags; + } + } + return undefined; +} + +module.exports = getTask; diff --git a/lib/versioned/^5.0.0/log/sync-task.js b/lib/versioned/^5.0.0/log/sync-task.js new file mode 100644 index 00000000..70c38a78 --- /dev/null +++ b/lib/versioned/^5.0.0/log/sync-task.js @@ -0,0 +1,46 @@ +'use strict'; + +var log = require('gulplog'); +var messages = require('@gulpjs/messages'); + +var tasks = {}; + +function warn() { + var taskKeys = Object.keys(tasks); + + if (!taskKeys.length) { + return; + } + + var taskNames = taskKeys.map(function(key) { + return tasks[key]; + }).join(', '); + + process.exitCode = 1; + + log.warn({ tag: messages.TASK_SYNC, tasks: taskNames }); +} + +function start(e) { + tasks[e.uid] = e.name; +} + +function clear(e) { + delete tasks[e.uid]; +} + +function clearAll() { + tasks = {}; +} + +function logSyncTask(gulpInst, opts) { + + process.once('exit', warn); + gulpInst.on('start', start); + gulpInst.on('stop', clear); + // When not running in --continue mode, we need to clear everything on error to avoid + // false positives. + gulpInst.on('error', opts.continue ? clear : clearAll); +} + +module.exports = logSyncTask; diff --git a/lib/versioned/^5.0.0/log/tasks-simple.js b/lib/versioned/^5.0.0/log/tasks-simple.js new file mode 100644 index 00000000..65cde515 --- /dev/null +++ b/lib/versioned/^5.0.0/log/tasks-simple.js @@ -0,0 +1,7 @@ +'use strict'; + +function logTasksSimple(nodes) { + console.log(nodes.join('\n').trim()); +} + +module.exports = logTasksSimple;