@toptal/davinci-cli-shared 2.1.2-alpha-GVF-262-disable-source-code-when-live-edit-8e8d1f53.14 → 2.1.2-alpha-chore-replace-vorpal-with-commander-0e62306b.27

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toptal/davinci-cli-shared",
3
- "version": "2.1.2-alpha-GVF-262-disable-source-code-when-live-edit-8e8d1f53.14+8e8d1f53",
3
+ "version": "2.1.2-alpha-chore-replace-vorpal-with-commander-0e62306b.27+0e62306b",
4
4
  "description": "Shared CLI code and CLI engine for davinci",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -23,19 +23,20 @@
23
23
  "dependencies": {
24
24
  "analytics-node": "^6.2.0",
25
25
  "chalk": "^4.1.2",
26
+ "commander": "^10.0.0",
26
27
  "execa": "^5.1.1",
27
28
  "find-yarn-workspace-root": "^2.0.0",
29
+ "gradient-string": "^2.0.0",
28
30
  "inquirer": "^8.2.0",
29
31
  "isomorphic-git": "^1.21.0",
30
32
  "js-yaml": "^4.1.0",
31
33
  "lodash": "^4.17.21",
32
34
  "read-pkg-up": "7.0.1",
33
35
  "semver": "^7.3.7",
34
- "uuid": "^9.0.0",
35
- "vorpal": "^1.12.0"
36
+ "uuid": "^9.0.0"
36
37
  },
37
38
  "devDependencies": {
38
- "@jest/globals": "28.1.0"
39
+ "@jest/globals": "^29.4.2"
39
40
  },
40
- "gitHead": "8e8d1f534b3fc5c61c4b77aba24b5b1c00ea42ed"
41
+ "gitHead": "0e62306b491253cf0aafb2b3b74ac5494c95b4db"
41
42
  }
package/src/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import vorpal from 'vorpal'
1
+ import { createArgument } from 'commander'
2
2
 
3
3
  import cliPrint from './utils/print.cjs'
4
4
  import { run, runSync } from './utils/run.js'
@@ -13,41 +13,38 @@ import {
13
13
  getDirectories,
14
14
  } from './utils/files.js'
15
15
  import { getPackagePackageJson, checkPrerequirement } from './utils/package.js'
16
- import commandsLoader from './commands.loader.js'
17
16
  import { identify, track } from './utils/metrics/metrics.js'
18
17
  import { gatherUsageData } from './utils/metrics/usage-data.js'
18
+ import DavinciCommand from './lib/command.js'
19
19
 
20
20
  const { config: davinciProjectConfig } = davinciConfig
21
21
 
22
- const vorpalInstance = vorpal()
22
+ const program = new DavinciCommand()
23
23
 
24
- export const bootstrap = (options = {}) => {
25
- return vorpalInstance.parse(process.argv, options)
24
+ export const bootstrap = () => {
25
+ return program.parse(process.argv)
26
26
  }
27
27
 
28
- export const execCommand = (...args) => {
29
- return vorpalInstance.exec(...args)
30
- }
28
+ export const getProgram = () => program
31
29
 
32
- export const help = helpText => {
33
- return vorpalInstance.help(helpText)
34
- }
30
+ export const loadCommands = (
31
+ commandCreators,
32
+ packageName,
33
+ parentCommand = program
34
+ ) => {
35
+ parentCommand.setPackageName(packageName)
35
36
 
36
- export const catchErrorCommand = callback => {
37
- /* eslint-disable promise/catch-or-return, promise/valid-params */
38
- vorpalInstance
39
- .catch('[commands...]', 'Catches incorrect commands')
40
- .action((args, cb) => {
41
- callback(args, cb)
42
- })
37
+ parentCommand.createCommands(commandCreators)
43
38
  }
44
39
 
45
- export const loadCommands = (commandCreators, packageCommand) => {
46
- commandsLoader(commandCreators, vorpalInstance, packageCommand)
40
+ export {
41
+ davinciProjectConfig,
42
+ run,
43
+ runSync,
44
+ convertToCLIParameters,
45
+ createArgument,
47
46
  }
48
47
 
49
- export { davinciProjectConfig, run, runSync, convertToCLIParameters }
50
-
51
48
  export const packageUtils = {
52
49
  getPackagePackageJson,
53
50
  checkPrerequirement,
@@ -84,13 +81,12 @@ export default {
84
81
  runSync,
85
82
  prompt,
86
83
  convertToCLIParameters,
87
- execCommand,
88
- catchErrorCommand,
89
- help,
90
84
  packageUtils,
91
85
  metrics: {
92
86
  identify,
93
87
  track,
94
88
  gatherUsageData,
95
89
  },
90
+ createArgument,
91
+ getProgram,
96
92
  }
@@ -0,0 +1,166 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`DavinciCommand createCommandFromLegacy creates a new command from legacy data 1`] = `
4
+ DavinciCommand {
5
+ "_actionHandler": [Function],
6
+ "_addImplicitHelpCommand": undefined,
7
+ "_aliases": [],
8
+ "_allowExcessArguments": true,
9
+ "_allowUnknownOption": true,
10
+ "_args": [
11
+ Argument {
12
+ "_name": "templateName",
13
+ "argChoices": [
14
+ "component",
15
+ "page",
16
+ ],
17
+ "defaultValue": undefined,
18
+ "defaultValueDescription": undefined,
19
+ "description": "Name of template",
20
+ "parseArg": [Function],
21
+ "required": true,
22
+ "variadic": false,
23
+ },
24
+ Argument {
25
+ "_name": "appName",
26
+ "argChoices": undefined,
27
+ "defaultValue": undefined,
28
+ "defaultValueDescription": undefined,
29
+ "description": "Application Name: folder name for the new application",
30
+ "parseArg": undefined,
31
+ "required": true,
32
+ "variadic": false,
33
+ },
34
+ ],
35
+ "_argsDescription": undefined,
36
+ "_combineFlagAndOptionalValue": true,
37
+ "_defaultCommandName": null,
38
+ "_description": "description",
39
+ "_enablePositionalOptions": false,
40
+ "_events": {
41
+ "option:flag": [Function],
42
+ },
43
+ "_eventsCount": 1,
44
+ "_executableDir": null,
45
+ "_executableFile": null,
46
+ "_executableHandler": false,
47
+ "_exitCallback": null,
48
+ "_hasHelpOption": true,
49
+ "_helpCommandDescription": "display help for command",
50
+ "_helpCommandName": "help",
51
+ "_helpCommandnameAndArgs": "help [command]",
52
+ "_helpConfiguration": {},
53
+ "_helpDescription": "display help for command",
54
+ "_helpFlags": "-h, --help",
55
+ "_helpLongFlag": "--help",
56
+ "_helpShortFlag": "-h",
57
+ "_hidden": false,
58
+ "_lifeCycleHooks": {},
59
+ "_maxListeners": undefined,
60
+ "_name": "child",
61
+ "_optionValueSources": {
62
+ "flag": "default",
63
+ },
64
+ "_optionValues": {
65
+ "flag": false,
66
+ },
67
+ "_outputConfiguration": {
68
+ "getErrHelpWidth": [Function],
69
+ "getOutHelpWidth": [Function],
70
+ "outputError": [Function],
71
+ "writeErr": [Function],
72
+ "writeOut": [Function],
73
+ },
74
+ "_packageName": null,
75
+ "_passThroughOptions": false,
76
+ "_scriptPath": null,
77
+ "_showHelpAfterError": false,
78
+ "_showSuggestionAfterError": true,
79
+ "_storeOptionsAsProperties": false,
80
+ "_summary": "",
81
+ "args": [],
82
+ "commands": [],
83
+ "options": [
84
+ Option {
85
+ "argChoices": undefined,
86
+ "conflictsWith": [],
87
+ "defaultValue": false,
88
+ "defaultValueDescription": undefined,
89
+ "description": "description",
90
+ "envVar": undefined,
91
+ "flags": "-f, --flag",
92
+ "hidden": false,
93
+ "implied": undefined,
94
+ "long": "--flag",
95
+ "mandatory": false,
96
+ "negate": false,
97
+ "optional": false,
98
+ "parseArg": undefined,
99
+ "presetArg": undefined,
100
+ "required": false,
101
+ "short": "-f",
102
+ "variadic": false,
103
+ },
104
+ ],
105
+ "parent": DavinciCommand {
106
+ "_actionHandler": null,
107
+ "_addImplicitHelpCommand": undefined,
108
+ "_aliases": [],
109
+ "_allowExcessArguments": true,
110
+ "_allowUnknownOption": false,
111
+ "_args": [],
112
+ "_argsDescription": undefined,
113
+ "_combineFlagAndOptionalValue": true,
114
+ "_defaultCommandName": "child",
115
+ "_description": "",
116
+ "_enablePositionalOptions": false,
117
+ "_events": {},
118
+ "_eventsCount": 0,
119
+ "_executableDir": null,
120
+ "_executableFile": null,
121
+ "_executableHandler": false,
122
+ "_exitCallback": null,
123
+ "_hasHelpOption": true,
124
+ "_helpCommandDescription": "display help for command",
125
+ "_helpCommandName": "help",
126
+ "_helpCommandnameAndArgs": "help [command]",
127
+ "_helpConfiguration": {},
128
+ "_helpDescription": "display help for command",
129
+ "_helpFlags": "-h, --help",
130
+ "_helpLongFlag": "--help",
131
+ "_helpShortFlag": "-h",
132
+ "_hidden": false,
133
+ "_lifeCycleHooks": {},
134
+ "_maxListeners": undefined,
135
+ "_name": "parent",
136
+ "_optionValueSources": {},
137
+ "_optionValues": {},
138
+ "_outputConfiguration": {
139
+ "getErrHelpWidth": [Function],
140
+ "getOutHelpWidth": [Function],
141
+ "outputError": [Function],
142
+ "writeErr": [Function],
143
+ "writeOut": [Function],
144
+ },
145
+ "_packageName": null,
146
+ "_passThroughOptions": false,
147
+ "_scriptPath": null,
148
+ "_showHelpAfterError": false,
149
+ "_showSuggestionAfterError": true,
150
+ "_storeOptionsAsProperties": false,
151
+ "_summary": "",
152
+ "args": [],
153
+ "commands": [
154
+ [Circular],
155
+ ],
156
+ "options": [],
157
+ "parent": null,
158
+ "processedArgs": [],
159
+ "rawArgs": [],
160
+ Symbol(kCapture): false,
161
+ },
162
+ "processedArgs": [],
163
+ "rawArgs": [],
164
+ Symbol(kCapture): false,
165
+ }
166
+ `;
@@ -0,0 +1,138 @@
1
+ import { Argument, Command } from 'commander'
2
+
3
+ import { createMetricsSidecar } from '../utils/metrics/sidecar.js'
4
+
5
+ /**
6
+ * A higher-order function that wraps the provided `action` function and adds telemetry to it.
7
+ * The telemetry consists of creating a metrics sidecar and finishing it when the `action` function is done.
8
+ *
9
+ * @param {Function} action The action to be wrapped
10
+ * @returns {Function} A new function that wraps the provided `action` function
11
+ */
12
+ export const withTelemetry =
13
+ action =>
14
+ async (...args) => {
15
+ const command = args[args.length - 1]
16
+ const commandName = command.getFullCommandName()
17
+ const packageName = command.getPackageName()
18
+
19
+ const metricsSidecar = await createMetricsSidecar(commandName, packageName)
20
+
21
+ try {
22
+ return await action(...args)
23
+ } finally {
24
+ metricsSidecar.finish()
25
+ }
26
+ }
27
+
28
+ /**
29
+ * A custom implementation of the `Command` class from the `commander` package,
30
+ * with added functionality for telemetry and package names.
31
+ */
32
+ class DavinciCommand extends Command {
33
+ constructor(name, packageName = null) {
34
+ super(name)
35
+
36
+ this._packageName = packageName
37
+ }
38
+
39
+ addCommand(cmd, opts) {
40
+ super.addCommand(cmd, opts)
41
+
42
+ cmd._packageName = this._packageName
43
+
44
+ return this
45
+ }
46
+
47
+ /**
48
+ * Creates a new `DavinciCommand` instance with the provided `name`.
49
+ *
50
+ * @param {string} name The name of the new command
51
+ * @returns {DavinciCommand} The newly created `DavinciCommand` instance
52
+ */
53
+ createCommand(name) {
54
+ return new DavinciCommand(name, this._packageName)
55
+ }
56
+
57
+ /**
58
+ * Creates a new command from legacy data.
59
+ *
60
+ * @param {Object} data The legacy data for the new command
61
+ * @returns {DavinciCommand} The newly created `DavinciCommand` instance
62
+ */
63
+ createCommandFromLegacy({
64
+ command,
65
+ description = '',
66
+ args = [],
67
+ options = [],
68
+ allowUnknownOptions = false,
69
+ action,
70
+ isDefault = false,
71
+ }) {
72
+ const currentCommand = this.command(command, { isDefault })
73
+
74
+ args.forEach(argument => {
75
+ if (argument instanceof Argument) {
76
+ currentCommand.addArgument(argument)
77
+ } else {
78
+ currentCommand.argument(...argument)
79
+ }
80
+ })
81
+
82
+ options.forEach(option =>
83
+ currentCommand.option(option.name, option.label, option.default)
84
+ )
85
+
86
+ if (allowUnknownOptions) {
87
+ currentCommand.allowUnknownOption()
88
+ }
89
+
90
+ currentCommand.description(description).action(action)
91
+
92
+ return currentCommand
93
+ }
94
+
95
+ createCommands(commandCreators) {
96
+ commandCreators.forEach(commandCreator => {
97
+ if (typeof commandCreator === 'function') {
98
+ this.addCommand(commandCreator(this))
99
+ } else if (!(commandCreator instanceof Command)) {
100
+ // legacy
101
+ this.createCommandFromLegacy(commandCreator)
102
+ } else {
103
+ this.addCommand(commandCreator)
104
+ }
105
+ })
106
+ }
107
+
108
+ action(fn) {
109
+ super.action(withTelemetry(fn))
110
+
111
+ return this
112
+ }
113
+
114
+ setPackageName(packageName) {
115
+ this._packageName = packageName
116
+ }
117
+
118
+ getPackageName() {
119
+ return this._packageName
120
+ }
121
+
122
+ getFullCommandName() {
123
+ const result = []
124
+
125
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
126
+ for (let command = this; command; command = command.parent) {
127
+ if (!command.parent) {
128
+ continue
129
+ }
130
+
131
+ result.unshift(command._name)
132
+ }
133
+
134
+ return result.join(' ')
135
+ }
136
+ }
137
+
138
+ export default DavinciCommand
@@ -0,0 +1,107 @@
1
+ import { jest } from '@jest/globals'
2
+ import { createArgument } from 'commander'
3
+
4
+ jest.unstable_mockModule('../utils/metrics/sidecar.js', () => ({
5
+ createMetricsSidecar: jest.fn(),
6
+ }))
7
+
8
+ const { createMetricsSidecar } = await import('../utils/metrics/sidecar.js')
9
+ const { default: DavinciCommand, withTelemetry } = await import('./command.js')
10
+
11
+ describe('DavinciCommand', () => {
12
+ let finish
13
+
14
+ beforeEach(() => {
15
+ finish = jest.fn()
16
+
17
+ createMetricsSidecar.mockResolvedValue({
18
+ finish,
19
+ })
20
+ })
21
+
22
+ afterEach(() => {
23
+ jest.clearAllMocks()
24
+ })
25
+
26
+ describe('withTelemetry', () => {
27
+ let command
28
+
29
+ beforeEach(() => {
30
+ command = {
31
+ getFullCommandName: jest.fn().mockReturnValue('FULL_COMMAND_NAME+1'),
32
+ getPackageName: jest.fn().mockReturnValue('PACKAGE_NAME+1'),
33
+ }
34
+ })
35
+
36
+ it('calls the action function with the provided arguments', async () => {
37
+ const action = jest.fn()
38
+ const args = [1, 2, command]
39
+
40
+ await withTelemetry(action)(...args)
41
+
42
+ expect(action).toHaveBeenCalledWith(...args)
43
+ expect(action).toHaveBeenCalledTimes(1)
44
+ })
45
+
46
+ it('creates a metrics sidecar with the correct command name and package name', async () => {
47
+ const action = jest.fn()
48
+
49
+ await withTelemetry(action)(1, 2, command)
50
+
51
+ expect(createMetricsSidecar).toHaveBeenCalledWith(
52
+ 'FULL_COMMAND_NAME+1',
53
+ 'PACKAGE_NAME+1'
54
+ )
55
+ })
56
+
57
+ describe('when the action function is done', () => {
58
+ it('calls finish method', async () => {
59
+ const action = jest.fn()
60
+
61
+ await withTelemetry(action)(1, 2, {
62
+ getFullCommandName: () => {},
63
+ getPackageName: () => {},
64
+ })
65
+
66
+ expect(finish).toHaveBeenCalledTimes(1)
67
+ expect(finish).toHaveBeenCalledWith()
68
+ })
69
+ })
70
+ })
71
+
72
+ describe('createCommand', () => {
73
+ it('creates a new DavinciCommand instance with the correct name and package name', () => {
74
+ const parentCommand = new DavinciCommand('parent', 'foo')
75
+ const childCommand = parentCommand.createCommand('child')
76
+
77
+ expect(childCommand.name()).toBe('child')
78
+ expect(childCommand.getPackageName()).toBe('foo')
79
+ })
80
+ })
81
+
82
+ describe('createCommandFromLegacy', () => {
83
+ it('creates a new command from legacy data', () => {
84
+ const parentCommand = new DavinciCommand('parent')
85
+ const command = parentCommand.createCommandFromLegacy({
86
+ command: 'child',
87
+ description: 'description',
88
+ args: [
89
+ createArgument('<templateName>', 'Name of template').choices([
90
+ 'component',
91
+ 'page',
92
+ ]),
93
+ [
94
+ '<appName>',
95
+ 'Application Name: folder name for the new application',
96
+ ],
97
+ ],
98
+ options: [{ name: '-f, --flag', label: 'description', default: false }],
99
+ allowUnknownOptions: true,
100
+ action: jest.fn(),
101
+ isDefault: true,
102
+ })
103
+
104
+ expect(command).toMatchSnapshot()
105
+ })
106
+ })
107
+ })
@@ -1,5 +1,7 @@
1
1
  export const convertToCLIParameters = options =>
2
- Object.entries(options).flatMap(([option, value]) => {
2
+ Object.entries(options)
3
+ .filter(([, value]) => value !== undefined)
4
+ .flatMap(([option, value]) => {
3
5
  if (typeof value === 'boolean') {
4
6
  return [`--${!value ? 'no-' : ''}${option}`]
5
7
  }
@@ -1,5 +1,6 @@
1
1
  const chalk = require('chalk')
2
2
  const util = require('util')
3
+ const gradient = require('gradient-string')
3
4
 
4
5
  const isObject = obj => typeof obj === 'object'
5
6
  const inspectObject = obj =>
@@ -10,6 +11,8 @@ const inspectObject = obj =>
10
11
  const prettifyObjectIfExist = args =>
11
12
  args.map(arg => (isObject(arg) ? inspectObject(arg) : arg))
12
13
 
14
+ const davinciGradient = gradient("#204ECF", "#00CC83")
15
+
13
16
  const cyan = (...args) => {
14
17
  console.log(chalk.cyan(...prettifyObjectIfExist(args)))
15
18
  }
@@ -69,4 +72,5 @@ module.exports = {
69
72
  grey,
70
73
  generatePadding,
71
74
  prettifyCommands,
75
+ davinciGradient,
72
76
  }
@@ -1,46 +0,0 @@
1
- import { createMetricsSidecar } from './utils/metrics/sidecar.js'
2
- import toVorpalArgs from './utils/to-vorpal-args.js'
3
-
4
- const commandsLoader = (commandCreators, vorpalInstance, commandName) => {
5
- commandCreators.forEach(commandCreator => {
6
- const {
7
- command: commandDescription,
8
- description,
9
- options = [],
10
- allowUnknownOptions = false,
11
- action,
12
- help,
13
- } = commandCreator
14
-
15
- const commandInstance = vorpalInstance
16
- .command(commandDescription)
17
- .parse((commandText, args) =>
18
- commandText.replace(args, toVorpalArgs(args))
19
- )
20
-
21
- options.forEach(option => commandInstance.option(option.name, option.label))
22
-
23
- if (help) {
24
- commandInstance.help(help)
25
- }
26
-
27
- if (allowUnknownOptions) {
28
- commandInstance.allowUnknownOptions()
29
- }
30
-
31
- commandInstance.description(description).action(async (...args) => {
32
- const metricsSidecar = await createMetricsSidecar(
33
- commandDescription,
34
- commandName
35
- )
36
-
37
- try {
38
- return await action(...args)
39
- } finally {
40
- metricsSidecar.finish()
41
- }
42
- })
43
- })
44
- }
45
-
46
- export default commandsLoader