@sequencemedia/gulp-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,50 @@
1
+ import copyProps from 'copy-props'
2
+ import {
3
+ join,
4
+ extname,
5
+ resolve,
6
+ dirname
7
+ } from 'node:path'
8
+
9
+ function getConfig (module) {
10
+ if (Reflect.has(module, 'default')) return Reflect.get(module, 'default')
11
+ return module
12
+ }
13
+
14
+ export default async function loadConfigFiles (configFiles = {}, configFileOrder = []) {
15
+ const config = {}
16
+
17
+ const keyList = (
18
+ configFileOrder
19
+ .filter((key) => configFiles[key])
20
+ )
21
+
22
+ while (keyList.length) {
23
+ const key = keyList.shift()
24
+ const filePath = configFiles[key]
25
+
26
+ try {
27
+ const module = (
28
+ extname(filePath) === '.json'
29
+ ? await import(resolve(join(process.cwd(), filePath)), { assert: { type: 'json' } })
30
+ : await import(resolve(join(process.cwd(), filePath)))
31
+ )
32
+
33
+ copyProps(getConfig(module), config, ({ keyChain, value }) => {
34
+ if (keyChain === 'flags.gulpfile') {
35
+ return resolve(dirname(filePath), value)
36
+ }
37
+
38
+ return value
39
+ })
40
+ } catch (e) {
41
+ const {
42
+ code
43
+ } = e
44
+
45
+ if (code !== 'ERR_MODULE_NOT_FOUND') console.error(e)
46
+ }
47
+ }
48
+
49
+ return config
50
+ }
package/lib/exit.mjs ADDED
@@ -0,0 +1,8 @@
1
+ // Fix stdout truncation on windows
2
+ export default function exit (code) {
3
+ if (process.platform === 'win32' && process.stdout.writableLength) {
4
+ process.stdout.once('drain', () => { process.exit(code) })
5
+ return
6
+ }
7
+ process.exit(code)
8
+ }
@@ -0,0 +1,19 @@
1
+ // Format orchestrator errors
2
+ export default function formatError (e) {
3
+ if (!e.error) {
4
+ return e.message
5
+ }
6
+
7
+ // PluginError
8
+ if (typeof e.error.showStack === 'boolean') {
9
+ return e.error.toString()
10
+ }
11
+
12
+ // Normal error
13
+ if (e.error.stack) {
14
+ return e.error.stack
15
+ }
16
+
17
+ // Unknown (string, number, etc.)
18
+ return new Error(String(e.error)).stack
19
+ }
@@ -0,0 +1,7 @@
1
+ const URL = 'https://raw.githubusercontent.com/gulpjs/plugins/master/src/blackList.json'
2
+
3
+ export default async function getBlacklist () {
4
+ const response = await fetch(URL)
5
+
6
+ return response.json()
7
+ }
@@ -0,0 +1,21 @@
1
+ import path from 'node:path'
2
+
3
+ import {
4
+ readFile
5
+ } from 'node:fs/promises'
6
+
7
+ export default async function getCompletion (completionType) {
8
+ if (typeof completionType !== 'string') {
9
+ throw new Error('Completion type not provided')
10
+ }
11
+
12
+ try {
13
+ const filePath = path.join('./completion', completionType)
14
+ const fileData = await readFile(filePath, 'utf8')
15
+ console.log(fileData.toString().trim())
16
+ process.exit(0)
17
+ } catch (e) {
18
+ console.error(`echo "Completion type "${completionType}" not found"`)
19
+ process.exit(5)
20
+ }
21
+ }
@@ -0,0 +1,80 @@
1
+ const DEFAULT_NODE_FACTORY = {
2
+ topNode: getDuplicateOf,
3
+ taskNode: getDuplicateOf,
4
+ childNode: getDuplicateOf
5
+ }
6
+
7
+ function getDuplicateOf (node) {
8
+ return Object.fromEntries(Object.entries(node))
9
+ }
10
+
11
+ function hasCompactTasks ({ compactTasks = false }) {
12
+ return String(compactTasks) === 'true'
13
+ }
14
+
15
+ function getTasksDepth ({ tasksDepth }) {
16
+ return (
17
+ typeof tasksDepth === 'number' && !isNaN(tasksDepth)
18
+ ? Math.max(1, tasksDepth)
19
+ : null
20
+ )
21
+ }
22
+
23
+ function nonRecursiveDuplicateNode (child, parent, nodeFactory) {
24
+ const dupe = nodeFactory.childNode(child)
25
+ dupe.nodes = []
26
+
27
+ parent.nodes.push(dupe)
28
+
29
+ if (child.branch) {
30
+ duplicateNodesFor(child, nonRecursiveDuplicateNode, dupe, nodeFactory)
31
+ }
32
+ }
33
+
34
+ function recursiveDuplicateNode (child, parent, maximumDepth, currentDepth, nodeFactory) {
35
+ const dupe = nodeFactory.childNode(child)
36
+ dupe.nodes = []
37
+
38
+ parent.nodes.push(dupe)
39
+
40
+ if (!maximumDepth || maximumDepth > currentDepth) {
41
+ duplicateNodesFor(child, recursiveDuplicateNode, dupe, maximumDepth, currentDepth + 1, nodeFactory)
42
+ }
43
+ }
44
+
45
+ function duplicateNodesFor ({ nodes }, duplicate, ...args) {
46
+ if (Array.isArray(nodes)) {
47
+ nodes
48
+ .forEach((node) => {
49
+ duplicate(node, ...args)
50
+ })
51
+ }
52
+ }
53
+
54
+ function duplicateNode (node, tree, cliProps, nodeFactory = DEFAULT_NODE_FACTORY) {
55
+ const dupe = nodeFactory.taskNode(node)
56
+ dupe.nodes = []
57
+
58
+ tree.nodes.push(dupe)
59
+
60
+ if (hasCompactTasks(cliProps)) {
61
+ duplicateNodesFor(node, nonRecursiveDuplicateNode, dupe, nodeFactory)
62
+ } else {
63
+ const maximumDepth = getTasksDepth(cliProps)
64
+
65
+ if (!maximumDepth || maximumDepth > 1) {
66
+ duplicateNodesFor(node, recursiveDuplicateNode, dupe, maximumDepth, 2, nodeFactory)
67
+ }
68
+ }
69
+ }
70
+
71
+ function duplicateTree (tree = {}, cliProps = {}, nodeFactory = DEFAULT_NODE_FACTORY) {
72
+ const dupe = nodeFactory.topNode(tree)
73
+ dupe.nodes = []
74
+
75
+ duplicateNodesFor(tree, duplicateNode, dupe, cliProps, nodeFactory)
76
+
77
+ return dupe
78
+ }
79
+
80
+ export default duplicateTree
@@ -0,0 +1,23 @@
1
+ import matchdep from 'matchdep'
2
+
3
+ /**
4
+ * Given a collection of plugin names verifies this collection against
5
+ * the blacklist. Returns an object with:
6
+ * [plugin name]=>[blacklisting reason]
7
+ * or an empty object if none of the dependencies to check are blacklisted.
8
+ *
9
+ * @param packageJson - The package JSON
10
+ * @param blacklistJson - The blacklist JSON
11
+ */
12
+ function getPackageBlacklist (packageJson, blacklistJson) {
13
+ return (
14
+ matchdep
15
+ .filterAll(Object.keys(blacklistJson), packageJson)
16
+ .reduce((blacklist, dependency) => {
17
+ blacklist[dependency] = blacklistJson[dependency]
18
+ return blacklist
19
+ }, {})
20
+ )
21
+ }
22
+
23
+ export default getPackageBlacklist
@@ -0,0 +1,3 @@
1
+ export default function getProcessTitle (argv = []) {
2
+ return ['gulp'].concat(argv).join(' ').trim()
3
+ }
@@ -0,0 +1,41 @@
1
+ import isObject from 'isobject'
2
+
3
+ function isString (value) {
4
+ return typeof value === 'string'
5
+ }
6
+
7
+ function isFunction (value) {
8
+ return value instanceof Function
9
+ }
10
+
11
+ function getDescription (task = {}) {
12
+ if (isString(task.description)) {
13
+ return task.description
14
+ }
15
+
16
+ if (isFunction(task.unwrap)) {
17
+ return getDescription(task.unwrap())
18
+ }
19
+ }
20
+
21
+ function getFlags (task = {}) {
22
+ if (isObject(task.flags)) {
23
+ return task.flags
24
+ }
25
+
26
+ if (isFunction(task.unwrap)) {
27
+ return getFlags(task.unwrap())
28
+ }
29
+ }
30
+
31
+ function getTask (gulp) {
32
+ return function getTaskFor (name) {
33
+ const task = gulp.task(name)
34
+ return {
35
+ description: getDescription(task),
36
+ flags: getFlags(task)
37
+ }
38
+ }
39
+ }
40
+
41
+ export default getTask
package/lib/index.mjs ADDED
@@ -0,0 +1,59 @@
1
+ import stdout from 'mute-stdout'
2
+
3
+ import listenForGulpEvents from './log/listen-for-gulp-events.mjs'
4
+ import listenForSyncEvents from './log/listen-for-sync-events.mjs'
5
+ import registerGulpTasks from './register-gulp-tasks.mjs'
6
+
7
+ import runGulpTasks from './run-gulp-tasks.mjs'
8
+
9
+ function getImportFromModulePath (params) {
10
+ const {
11
+ envProps: {
12
+ modulePath
13
+ }
14
+ } = params
15
+
16
+ return (
17
+ import(modulePath)
18
+ .then(({ default: gulp }) => Object.assign(params, { gulp }))
19
+ )
20
+ }
21
+
22
+ function getImportFromConfigPath (params) {
23
+ const {
24
+ envProps: {
25
+ configPath
26
+ }
27
+ } = params
28
+
29
+ return (
30
+ import(configPath)
31
+ .then((tasks) => Object.assign(params, { tasks }))
32
+ )
33
+ }
34
+
35
+ function isSilent (cliProps) {
36
+ return (cliProps.tasksList || cliProps.tasks || cliProps.tasksJson)
37
+ }
38
+
39
+ async function run (cliProps, envProps, configProps = {}) {
40
+ if (isSilent(cliProps)) stdout.mute()
41
+
42
+ const { gulp, tasks } = (
43
+ await Promise.resolve({ cliProps, envProps, configProps })
44
+ .then(getImportFromModulePath)
45
+ .then(getImportFromConfigPath)
46
+ )
47
+
48
+ listenForGulpEvents(gulp)
49
+ listenForSyncEvents(gulp, cliProps)
50
+ registerGulpTasks(gulp, tasks)
51
+
52
+ stdout.unmute()
53
+
54
+ return (
55
+ runGulpTasks(gulp, cliProps, envProps, configProps)
56
+ )
57
+ }
58
+
59
+ export default run
@@ -0,0 +1,46 @@
1
+ import log from 'gulplog'
2
+ import prettyHrtime from 'pretty-hrtime'
3
+
4
+ import ansi from '../ansi.mjs'
5
+ import formatError from '../format-error.mjs'
6
+
7
+ const errors = new Set()
8
+
9
+ function start (event) {
10
+ const level = event.branch ? 'debug' : 'info'
11
+ log[level](`Starting '${ansi.cyan(event.name)}'...`)
12
+ }
13
+
14
+ function stop (event) {
15
+ const level = event.branch ? 'debug' : 'info'
16
+ log[level](`Finished '${ansi.cyan(event.name)}' after ${ansi.magenta(prettyHrtime(event.duration))}`)
17
+ }
18
+
19
+ function error (event) {
20
+ const level = event.branch ? 'debug' : 'error'
21
+ log[level](`'${ansi.cyan(event.name)}' ${ansi.red('errored after')} ${ansi.magenta(prettyHrtime(event.duration))}`)
22
+
23
+ // Exit if we have logged this before
24
+ if (errors.has(event.error)) return
25
+
26
+ // If we haven't logged this before, log it and add it to the error list
27
+ log.error(formatError(event))
28
+ errors.add(event.error)
29
+ }
30
+
31
+ function stopListeningForGulpEvents (gulp) {
32
+ gulp
33
+ .removeListener('start', start)
34
+ .removeListener('stop', stop)
35
+ .removeListener('error', error)
36
+ }
37
+
38
+ // Wire up logging events
39
+ export default function listenForGulpEvents (gulp) {
40
+ stopListeningForGulpEvents(gulp)
41
+
42
+ gulp
43
+ .on('start', start)
44
+ .on('stop', stop)
45
+ .on('error', error)
46
+ }
@@ -0,0 +1,49 @@
1
+ import log from 'gulplog'
2
+ import fancyLog from 'fancy-log'
3
+
4
+ const noop = () => {}
5
+
6
+ // The sorting of the levels is
7
+ // significant.
8
+ const levels = [
9
+ 'error', // -L: Logs error events.
10
+ 'warn', // -LL: Logs warn and error events.
11
+ 'info', // -LLL: Logs info, warn and error events.
12
+ 'debug' // -LLLL: Logs all log levels.
13
+ ]
14
+
15
+ function stopListeningForLevelEvents () {
16
+ levels
17
+ .forEach((level) => {
18
+ if (level === 'error') {
19
+ log.removeListener(level, noop).removeListener(level, fancyLog.error)
20
+ } else {
21
+ log.removeListener(level, fancyLog)
22
+ }
23
+ })
24
+ }
25
+
26
+ // Wire up log levels
27
+ export default function listenForLevelEvents (cliProps) {
28
+ // Remove previous listeners to enable to call this twice.
29
+ stopListeningForLevelEvents()
30
+
31
+ // Silent?
32
+ if (cliProps.tasksList || cliProps.tasksJson || cliProps.help || cliProps.version || cliProps.silent) {
33
+ // Keep from crashing process when silent
34
+ log.on('error', noop)
35
+ } else {
36
+ // Default level is 3 (info)
37
+ const loglevel = cliProps.logLevel || 3
38
+
39
+ levels
40
+ .filter((level, i) => level && i < loglevel)
41
+ .forEach((level) => {
42
+ if (level === 'error') {
43
+ log.on(level, fancyLog.error)
44
+ } else {
45
+ log.on(level, fancyLog)
46
+ }
47
+ })
48
+ }
49
+ }
@@ -0,0 +1,58 @@
1
+ import log from 'gulplog'
2
+ import ansi from '../ansi.mjs'
3
+
4
+ const tasks = new Map()
5
+
6
+ function warn () {
7
+ if (tasks.size) {
8
+ const taskNames = Array.from(tasks.values()).join(', ')
9
+
10
+ process.exitCode = 1
11
+
12
+ log.warn(
13
+ ansi.red('The following tasks did not complete:')
14
+ )
15
+ log.warn(
16
+ ansi.cyan(taskNames)
17
+ )
18
+ log.warn(
19
+ ansi.red('Did you forget to signal async completion?')
20
+ )
21
+ }
22
+ }
23
+
24
+ function start ({ uid, name }) {
25
+ tasks.set(uid, name)
26
+ }
27
+
28
+ function clear ({ uid }) {
29
+ tasks.delete(uid)
30
+ }
31
+
32
+ function clearAll () {
33
+ tasks.clear()
34
+ }
35
+
36
+ function stopListeningForSyncEvents (gulp) {
37
+ // Ensure to detach
38
+ process
39
+ .removeListener('exit', warn)
40
+
41
+ gulp
42
+ .removeListener('start', start)
43
+ .removeListener('stop', clear)
44
+ .removeListener('error', clear).removeListener('error', clearAll)
45
+ }
46
+
47
+ export default function listenForSyncEvents (gulp, cliProps) {
48
+ stopListeningForSyncEvents(gulp)
49
+
50
+ process
51
+ .once('exit', warn)
52
+
53
+ // When not running in --continue mode we need to clear everything on error
54
+ gulp
55
+ .on('start', start)
56
+ .on('stop', clear)
57
+ .on('error', cliProps.continue ? clear : clearAll)
58
+ }
@@ -0,0 +1,16 @@
1
+ import {
2
+ writeFile
3
+ } from 'node:fs/promises'
4
+
5
+ import getDuplicate from '../get-duplicate.mjs'
6
+
7
+ export default function logTasksJson (tree, cliProps) {
8
+ const dupe = getDuplicate(tree, cliProps)
9
+ const json = JSON.stringify(dupe)
10
+
11
+ // Write to a file if not `true`
12
+ if (cliProps.tasksJson !== true) return writeFile(cliProps.tasksJson, json, 'utf8')
13
+
14
+ // Otherwise
15
+ console.log(json.trim())
16
+ }
@@ -0,0 +1,3 @@
1
+ export default function logTasksList ({ nodes = [] }) {
2
+ console.log(nodes.join('\n').trim())
3
+ }
@@ -0,0 +1,163 @@
1
+ import archy from 'archy'
2
+ import log from 'gulplog'
3
+
4
+ import sortBy from 'array-sort'
5
+ import isObject from 'isobject'
6
+
7
+ import ansi from '../ansi.mjs'
8
+ import getDuplicate from '../get-duplicate.mjs'
9
+
10
+ function getLineInfoCollector (lineInfo) {
11
+ return {
12
+ topTask (node) {
13
+ lineInfo.push({
14
+ name: node.label,
15
+ desc: node.desc,
16
+ type: 'top'
17
+ })
18
+ },
19
+ option (opt) {
20
+ lineInfo.push({
21
+ name: opt.label,
22
+ desc: opt.desc,
23
+ type: 'option'
24
+ })
25
+ },
26
+ childTask (node) {
27
+ lineInfo.push({
28
+ name: node.label,
29
+ type: 'child'
30
+ })
31
+ }
32
+ }
33
+ }
34
+
35
+ function getNodeFactory (getTask, entryObserver) {
36
+ return {
37
+ topNode (node) {
38
+ return {
39
+ label: node.label
40
+ }
41
+ },
42
+
43
+ taskNode (node) {
44
+ const task = getTask(node.label) || {}
45
+
46
+ const newNode = {
47
+ label: node.label,
48
+ desc: typeof task.description === 'string' ? task.description : '',
49
+ cliProps: []
50
+ }
51
+ entryObserver.topTask(newNode)
52
+
53
+ if (isObject(task.flags)) {
54
+ Object.keys(task.flags)
55
+ .sort()
56
+ .filter(Boolean)
57
+ .forEach((flag) => {
58
+ const opt = {
59
+ label: flag,
60
+ desc: typeof task.flags[flag] === 'string' ? task.flags[flag] : ''
61
+ }
62
+ entryObserver.option(opt)
63
+ newNode.cliProps.push(opt)
64
+ newNode.label += '\n' + opt.label // The way of archy for options.
65
+ })
66
+ }
67
+
68
+ return newNode
69
+ },
70
+
71
+ childNode (node) {
72
+ const newChild = {
73
+ label: node.label
74
+ }
75
+ entryObserver.childTask(newChild)
76
+ newChild.label = '' // Because don't use child tasks to calc indents.
77
+
78
+ return newChild
79
+ }
80
+ }
81
+ }
82
+
83
+ function getSpacerForLineIndents (tree, lineInfo) {
84
+ let maxSize = 0
85
+ const sizes = []
86
+
87
+ archy(tree)
88
+ .split('\n')
89
+ .slice(1, -1)
90
+ .forEach((line, index) => {
91
+ const info = lineInfo[index]
92
+ if (info.type === 'top' || info.type === 'option') {
93
+ maxSize = Math.max(maxSize, line.length)
94
+ sizes.push(line.length)
95
+ } else {
96
+ sizes.push(0)
97
+ }
98
+ })
99
+
100
+ maxSize += 3
101
+
102
+ return function getSpacerFor (index) {
103
+ return Array(maxSize - sizes[index]).join(' ')
104
+ }
105
+ }
106
+
107
+ function getLinesContainingOnlyBranches (tree) {
108
+ tree.nodes
109
+ .forEach((node) => {
110
+ node.label = ''
111
+ node.cliProps
112
+ .forEach(() => {
113
+ node.label += '\n'
114
+ })
115
+ })
116
+
117
+ return archy(tree)
118
+ .split('\n')
119
+ .slice(1, -1)
120
+ }
121
+
122
+ function logLines (lines, spacer, lineInfo) {
123
+ lines
124
+ .forEach((branch, index) => {
125
+ const info = lineInfo[index]
126
+
127
+ let line = ansi.white(branch)
128
+
129
+ if (info.type === 'top') {
130
+ line += ansi.cyan(info.name)
131
+ if (info.desc.length > 0) {
132
+ line += spacer(index) + ansi.white(info.desc)
133
+ }
134
+ } else if (info.type === 'option') {
135
+ line += ansi.magenta(info.name)
136
+ if (info.desc.length > 0) {
137
+ line += spacer(index) + ansi.white('…' + info.desc)
138
+ }
139
+ } else { // If (info.type === 'child') {
140
+ line += ansi.white(info.name)
141
+ }
142
+
143
+ log.info(line)
144
+ })
145
+ }
146
+
147
+ // Wire up log tasks
148
+ export default function logTasks (tree, cliProps, getTask) {
149
+ if (cliProps.sortTasks) {
150
+ tree.nodes = sortBy(tree.nodes, 'label')
151
+ }
152
+
153
+ const lineInfo = []
154
+ const entryObserver = getLineInfoCollector(lineInfo)
155
+ const nodeFactory = getNodeFactory(getTask, entryObserver)
156
+
157
+ const dupe = getDuplicate(tree, cliProps, nodeFactory)
158
+ const spacer = getSpacerForLineIndents(dupe, lineInfo)
159
+ const lines = getLinesContainingOnlyBranches(dupe)
160
+
161
+ log.info(dupe.label)
162
+ logLines(lines, spacer, lineInfo)
163
+ }
@@ -0,0 +1,7 @@
1
+ export default function registerGulpTasks (gulp, tasks) {
2
+ Object.entries(tasks)
3
+ .filter(([taskName, task]) => (taskName || task.displayName) && task instanceof Function)
4
+ .forEach(([taskName, task]) => {
5
+ gulp.task(task.displayName || taskName, task)
6
+ })
7
+ }
@@ -0,0 +1,14 @@
1
+ import tildify from './tildify.mjs'
2
+
3
+ import logTasksJson from './log/tasks-json.mjs'
4
+
5
+ export default function runGulpTasksJsonTree (gulp, cliProps, envProps, configProps) {
6
+ const tree = gulp.tree({ deep: true })
7
+ if (configProps.description && typeof configProps.description === 'string') {
8
+ tree.label = configProps.description
9
+ } else {
10
+ tree.label = `Tasks for ${tildify(envProps.configPath)}`
11
+ }
12
+
13
+ return logTasksJson(tree, cliProps)
14
+ }
@@ -0,0 +1,7 @@
1
+ import logTasksList from './log/tasks-list.mjs'
2
+
3
+ export default function runGulpTasksListTree (gulp) {
4
+ const tree = gulp.tree()
5
+
6
+ return logTasksList(tree)
7
+ }