@toptal/davinci-cli-shared 1.5.2-alpha-fx-2755-codebase-specific-workflows.131 → 1.5.2-alpha-feature-comm-833-reusing-graphql-fragments-2d4dd9c0.9
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/CHANGELOG.md +64 -0
- package/package.json +7 -2
- package/src/commands.loader.js +25 -3
- package/src/index.js +11 -4
- package/src/utils/convert-to-cli-parameters.js +6 -12
- package/src/utils/davinci-project-config.js +3 -3
- package/src/utils/files.js +19 -1
- package/src/utils/metrics/id-config.js +63 -0
- package/src/utils/metrics/metrics.js +37 -0
- package/src/utils/metrics/usage-data.js +126 -0
- package/src/utils/package.js +41 -0
- package/src/utils/print.js +7 -2
- package/src/utils/prompt.js +7 -7
- package/src/utils/run.js +2 -2
- package/src/utils/yarn-adapter.js +44 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,69 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 1.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#1731](https://github.com/toptal/davinci/pull/1731) [`1c422aae`](https://github.com/toptal/davinci/commit/1c422aae676d25cd2216c8a24af4b7e208728931) Thanks [@separatio](https://github.com/separatio)! - ---
|
|
8
|
+
|
|
9
|
+
- added data collection to Davinci commands
|
|
10
|
+
|
|
11
|
+
## 1.8.1
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- [#1634](https://github.com/toptal/davinci/pull/1634) [`b12d1d19`](https://github.com/toptal/davinci/commit/b12d1d19af02104b9076a53b6e516198e6cda141) Thanks [@OndrejTuma](https://github.com/OndrejTuma)! - ---
|
|
16
|
+
|
|
17
|
+
### checkPrerequirement()
|
|
18
|
+
|
|
19
|
+
- add coersion to semver check of package version
|
|
20
|
+
|
|
21
|
+
## 1.8.0
|
|
22
|
+
|
|
23
|
+
### Minor Changes
|
|
24
|
+
|
|
25
|
+
- [#1578](https://github.com/toptal/davinci/pull/1578) [`e602ae7e`](https://github.com/toptal/davinci/commit/e602ae7edc7d075a4192052d6c04868f08fea0a7) Thanks [@augustobmoura](https://github.com/augustobmoura)! - ---
|
|
26
|
+
|
|
27
|
+
- add cyan command to print
|
|
28
|
+
|
|
29
|
+
## 1.7.0
|
|
30
|
+
|
|
31
|
+
### Minor Changes
|
|
32
|
+
|
|
33
|
+
- [#1543](https://github.com/toptal/davinci/pull/1543) [`9b67beb3`](https://github.com/toptal/davinci/commit/9b67beb3aaf0e24193b2deaba3126c41e3ec9280) Thanks [@denieler](https://github.com/denieler)! - ---
|
|
34
|
+
- add package utils to check package prerequisites (peer dependencies packages to be installed)
|
|
35
|
+
- add yarn list command adapter
|
|
36
|
+
|
|
37
|
+
* [#1542](https://github.com/toptal/davinci/pull/1542) [`04fe7ffa`](https://github.com/toptal/davinci/commit/04fe7ffa161027d081c6ae870716bb06a2fcf73f) Thanks [@LashaJini](https://github.com/LashaJini)! - - Add `getDirectories` function to list directories relative to path
|
|
38
|
+
- Add `getPackageDirectories` function to list davinci package directories
|
|
39
|
+
|
|
40
|
+
## 1.6.0
|
|
41
|
+
|
|
42
|
+
### Minor Changes
|
|
43
|
+
|
|
44
|
+
- [#1517](https://github.com/toptal/davinci/pull/1517) [`fa3423f1`](https://github.com/toptal/davinci/commit/fa3423f1f9211b4d8899064aa4afdf5ca2198483) Thanks [@LashaJini](https://github.com/LashaJini)! - ---
|
|
45
|
+
|
|
46
|
+
- Do not load Jest related eslint rules if project is not using Jest
|
|
47
|
+
- [BREAKING CHANGE] Rename `.eslintrc` to `.eslintrc.js`
|
|
48
|
+
|
|
49
|
+
## 1.5.4
|
|
50
|
+
|
|
51
|
+
### Patch Changes
|
|
52
|
+
|
|
53
|
+
- [#1344](https://github.com/toptal/davinci/pull/1344) [`64c000c2`](https://github.com/toptal/davinci/commit/64c000c25d97df2c8853a6cf80b7ca81461dae24) Thanks [@dmaklygin](https://github.com/dmaklygin)! - - implement workflow documentation generator
|
|
54
|
+
|
|
55
|
+
## 1.5.3
|
|
56
|
+
|
|
57
|
+
### Patch Changes
|
|
58
|
+
|
|
59
|
+
- [#1378](https://github.com/toptal/davinci/pull/1378) [`3013b76d`](https://github.com/toptal/davinci/commit/3013b76d7f957924885f3e5933f5227ff42a09e1) Thanks [@augustobmoura](https://github.com/augustobmoura)! - - fix support for boolean options in convertToCLIParameters
|
|
60
|
+
|
|
61
|
+
## 1.5.2
|
|
62
|
+
|
|
63
|
+
### Patch Changes
|
|
64
|
+
|
|
65
|
+
- [#1343](https://github.com/toptal/davinci/pull/1343) [`1212e098`](https://github.com/toptal/davinci/commit/1212e098c668fc1c87ee9b1824edf0bc80509bc4) Thanks [@dmaklygin](https://github.com/dmaklygin)! - - implement custom helper for davinci workflow package
|
|
66
|
+
|
|
3
67
|
## 1.5.1
|
|
4
68
|
|
|
5
69
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toptal/davinci-cli-shared",
|
|
3
|
-
"version": "1.5.2-alpha-
|
|
3
|
+
"version": "1.5.2-alpha-feature-comm-833-reusing-graphql-fragments-2d4dd9c0.9+2d4dd9c0",
|
|
4
4
|
"description": "Shared CLI code and CLI engine for davinci",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -14,11 +14,16 @@
|
|
|
14
14
|
"author": "Toptal",
|
|
15
15
|
"license": "ISC",
|
|
16
16
|
"dependencies": {
|
|
17
|
+
"analytics-node": "^6.2.0",
|
|
17
18
|
"chalk": "^4.1.2",
|
|
18
19
|
"execa": "^5.1.1",
|
|
20
|
+
"find-yarn-workspace-root": "^2.0.0",
|
|
19
21
|
"inquirer": "^8.2.0",
|
|
20
22
|
"js-yaml": "^4.1.0",
|
|
23
|
+
"read-pkg-up": "7.0.1",
|
|
24
|
+
"semver": "^7.3.7",
|
|
25
|
+
"uuid": "^9.0.0",
|
|
21
26
|
"vorpal": "^1.12.0"
|
|
22
27
|
},
|
|
23
|
-
"gitHead": "
|
|
28
|
+
"gitHead": "2d4dd9c0609199490b7112909eb97cbe1b037717"
|
|
24
29
|
}
|
package/src/commands.loader.js
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
|
+
const metrics = require('./utils/metrics/metrics')
|
|
2
|
+
const { gatherUsageData } = require('./utils/metrics/usage-data')
|
|
1
3
|
const toVorpalArgs = require('./utils/to-vorpal-args')
|
|
2
4
|
|
|
3
|
-
const
|
|
5
|
+
const sendMetrics = async (commandIdentifier, packageCommand) => {
|
|
6
|
+
try {
|
|
7
|
+
// Run identify and metrics in parallel
|
|
8
|
+
await Promise.all([
|
|
9
|
+
metrics.identify(),
|
|
10
|
+
gatherUsageData(commandIdentifier, packageCommand).then(metrics.track),
|
|
11
|
+
])
|
|
12
|
+
} catch (err) {
|
|
13
|
+
// Metrics can fail, we still need to continue the execution
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const commandsLoader = (commandCreators, vorpalInstance, packageCommand) => {
|
|
4
18
|
commandCreators.forEach(commandCreator => {
|
|
5
19
|
const {
|
|
6
20
|
command,
|
|
@@ -8,7 +22,7 @@ const commandsLoader = (commandCreators, vorpalInstance) => {
|
|
|
8
22
|
options = [],
|
|
9
23
|
allowUnknownOptions = false,
|
|
10
24
|
action,
|
|
11
|
-
help
|
|
25
|
+
help,
|
|
12
26
|
} = commandCreator
|
|
13
27
|
|
|
14
28
|
const commandInstance = vorpalInstance
|
|
@@ -27,7 +41,15 @@ const commandsLoader = (commandCreators, vorpalInstance) => {
|
|
|
27
41
|
commandInstance.allowUnknownOptions()
|
|
28
42
|
}
|
|
29
43
|
|
|
30
|
-
commandInstance.description(description).action(
|
|
44
|
+
commandInstance.description(description).action(async (...args) => {
|
|
45
|
+
// Run metrics and the action in parallel
|
|
46
|
+
const [result] = await Promise.all([
|
|
47
|
+
action(...args),
|
|
48
|
+
sendMetrics(command, packageCommand),
|
|
49
|
+
])
|
|
50
|
+
|
|
51
|
+
return result
|
|
52
|
+
})
|
|
31
53
|
})
|
|
32
54
|
}
|
|
33
55
|
|
package/src/index.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
const vorpal = require('vorpal')
|
|
2
2
|
|
|
3
|
+
const { identify, track } = require('../src/utils/metrics/metrics')
|
|
3
4
|
const print = require('../src/utils/print')
|
|
4
5
|
const { run, runSync } = require('../src/utils/run')
|
|
5
6
|
const prompt = require('../src/utils/prompt')
|
|
6
7
|
const {
|
|
7
|
-
convertToCLIParameters
|
|
8
|
+
convertToCLIParameters,
|
|
8
9
|
} = require('../src/utils/convert-to-cli-parameters')
|
|
9
10
|
const davinciProjectConfig = require('./utils/davinci-project-config')
|
|
10
11
|
const files = require('./utils/files')
|
|
12
|
+
const packageUtils = require('./utils/package')
|
|
11
13
|
|
|
12
14
|
const vorpalInstance = vorpal()
|
|
13
15
|
|
|
@@ -32,10 +34,10 @@ const catchErrorCommand = callback => {
|
|
|
32
34
|
})
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
const loadCommands = commandCreators => {
|
|
37
|
+
const loadCommands = (commandCreators, packageCommand) => {
|
|
36
38
|
const commandsLoader = require('./commands.loader')
|
|
37
39
|
|
|
38
|
-
commandsLoader(commandCreators, vorpalInstance)
|
|
40
|
+
commandsLoader(commandCreators, vorpalInstance, packageCommand)
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
module.exports = {
|
|
@@ -50,5 +52,10 @@ module.exports = {
|
|
|
50
52
|
convertToCLIParameters,
|
|
51
53
|
execCommand,
|
|
52
54
|
catchErrorCommand,
|
|
53
|
-
help
|
|
55
|
+
help,
|
|
56
|
+
packageUtils,
|
|
57
|
+
metrics: {
|
|
58
|
+
identify,
|
|
59
|
+
track,
|
|
60
|
+
},
|
|
54
61
|
}
|
|
@@ -1,16 +1,10 @@
|
|
|
1
|
-
const convertToCLIParameters = options =>
|
|
2
|
-
|
|
1
|
+
const convertToCLIParameters = options =>
|
|
2
|
+
Object.entries(options).flatMap(([option, value]) => {
|
|
3
|
+
if (typeof value === 'boolean') {
|
|
4
|
+
return [`--${!value ? 'no-' : ''}${option}`]
|
|
5
|
+
}
|
|
3
6
|
|
|
4
|
-
|
|
5
|
-
result.push(`--${key}`)
|
|
6
|
-
|
|
7
|
-
const hasTrueValue = options[key] === true
|
|
8
|
-
const optionValue = hasTrueValue ? '' : String(options[key])
|
|
9
|
-
|
|
10
|
-
result.push(optionValue)
|
|
7
|
+
return [`--${option}`, String(value)]
|
|
11
8
|
})
|
|
12
9
|
|
|
13
|
-
return result
|
|
14
|
-
}
|
|
15
|
-
|
|
16
10
|
module.exports = { convertToCLIParameters }
|
|
@@ -14,14 +14,14 @@ const davinciProjectConfig = {
|
|
|
14
14
|
conventionalCommits: resolve('master', 'conventional_commits'),
|
|
15
15
|
deploy: resolve('master', 'deploy'),
|
|
16
16
|
deployStaging: resolve('master', 'deploy_staging'),
|
|
17
|
-
publishAsPackage: resolve('master', 'publish_as_package')
|
|
17
|
+
publishAsPackage: resolve('master', 'publish_as_package'),
|
|
18
18
|
},
|
|
19
19
|
pr: {
|
|
20
|
-
contractTesting: resolve('pr', 'contract_testing')
|
|
20
|
+
contractTesting: resolve('pr', 'contract_testing'),
|
|
21
21
|
},
|
|
22
22
|
isProgressiveWebApp: Boolean(resolveKey(data, 'is_progressive_web_app')),
|
|
23
23
|
serviceWorkerFileName: resolveKey(data, 'service_worker_filename'),
|
|
24
|
-
safetyWorkers: resolveKey(data, 'safety_workers')
|
|
24
|
+
safetyWorkers: resolveKey(data, 'safety_workers'),
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
module.exports = davinciProjectConfig
|
package/src/utils/files.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
|
+
const { readdirSync } = require('fs')
|
|
2
3
|
|
|
3
4
|
const getPackageFileContent = (davinciPackageName, filename) =>
|
|
4
5
|
require(getPackageFilePath(davinciPackageName, filename))
|
|
@@ -19,9 +20,26 @@ const getProjectRootFilePath = filename => {
|
|
|
19
20
|
return path.join(currentRunningDir, filename)
|
|
20
21
|
}
|
|
21
22
|
|
|
23
|
+
const projectHasPackage = pkg => {
|
|
24
|
+
try {
|
|
25
|
+
require.resolve(pkg)
|
|
26
|
+
|
|
27
|
+
return true
|
|
28
|
+
} catch {
|
|
29
|
+
return false
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const getDirectories = source =>
|
|
34
|
+
readdirSync(source, { withFileTypes: true })
|
|
35
|
+
.filter(dirent => dirent.isDirectory())
|
|
36
|
+
.map(dirent => dirent.name)
|
|
37
|
+
|
|
22
38
|
module.exports = {
|
|
23
39
|
getPackageFileContent,
|
|
24
40
|
getPackageFilePath,
|
|
25
41
|
getProjectRootFilePath,
|
|
26
|
-
getProjectRootFileContent
|
|
42
|
+
getProjectRootFileContent,
|
|
43
|
+
projectHasPackage,
|
|
44
|
+
getDirectories,
|
|
27
45
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const os = require('os')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const yaml = require('js-yaml')
|
|
5
|
+
const { v1: uuid } = require('uuid')
|
|
6
|
+
|
|
7
|
+
const homeDir = os.homedir()
|
|
8
|
+
const configDir = path.join(homeDir, '.davinci-metrics')
|
|
9
|
+
const configFile = path.join(configDir, 'auth.yml')
|
|
10
|
+
const DEFAULT_FILE_CONTENT = 'allow_tracking: "yes"'
|
|
11
|
+
|
|
12
|
+
const readConfig = () => {
|
|
13
|
+
let configFileContent = DEFAULT_FILE_CONTENT
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
configFileContent = fs.readFileSync(configFile)
|
|
17
|
+
} catch (error) {
|
|
18
|
+
if (error.code === 'ENOENT') {
|
|
19
|
+
// File doesn't exist, lets create a new one automatically
|
|
20
|
+
fs.mkdirSync(configDir, { recursive: true })
|
|
21
|
+
fs.writeFileSync(configFile, configFileContent)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return yaml.load(configFileContent)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const updateConfig = config => {
|
|
29
|
+
const originalConfig = readConfig()
|
|
30
|
+
const newConfig = {
|
|
31
|
+
...originalConfig,
|
|
32
|
+
...config,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
fs.writeFileSync(configFile, yaml.dump(newConfig))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const isTrackingEnabled = () => {
|
|
39
|
+
const config = readConfig()
|
|
40
|
+
|
|
41
|
+
return config.allow_tracking !== 'no' && !process.env.CI
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const getDeviceId = () => {
|
|
45
|
+
const config = readConfig()
|
|
46
|
+
|
|
47
|
+
if (config && typeof config.uuid === 'string') {
|
|
48
|
+
return config.uuid
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const id = uuid()
|
|
52
|
+
|
|
53
|
+
updateConfig({
|
|
54
|
+
uuid: id,
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
return id
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = {
|
|
61
|
+
getDeviceId,
|
|
62
|
+
isTrackingEnabled,
|
|
63
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const Analytics = require('analytics-node')
|
|
2
|
+
|
|
3
|
+
const { getDeviceId, isTrackingEnabled } = require('./id-config')
|
|
4
|
+
const SEGMENT_WRITE_KEY = 'cwNYguEVVqdxFj5NyTKOhlrvaNQjlsXV'
|
|
5
|
+
const anonymousId = getDeviceId()
|
|
6
|
+
|
|
7
|
+
const analytics = new Analytics(SEGMENT_WRITE_KEY, {
|
|
8
|
+
enable: process.env.DAVINCI_SKIP_ANALYTICS !== 'true' && isTrackingEnabled(),
|
|
9
|
+
flushAt: 1,
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
const identify = () =>
|
|
13
|
+
new Promise((resolve, reject) => {
|
|
14
|
+
analytics.identify(
|
|
15
|
+
{
|
|
16
|
+
anonymousId,
|
|
17
|
+
},
|
|
18
|
+
err => (err ? reject(err) : resolve())
|
|
19
|
+
)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const track = properties =>
|
|
23
|
+
new Promise((resolve, reject) => {
|
|
24
|
+
analytics.track(
|
|
25
|
+
{
|
|
26
|
+
anonymousId,
|
|
27
|
+
event: 'davinci-command',
|
|
28
|
+
properties,
|
|
29
|
+
},
|
|
30
|
+
err => (err ? reject(err) : resolve())
|
|
31
|
+
)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
module.exports = {
|
|
35
|
+
identify,
|
|
36
|
+
track,
|
|
37
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const os = require('os')
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const execa = require('execa')
|
|
5
|
+
const readPackageUp = require('read-pkg-up')
|
|
6
|
+
const findYarnWorkspaceRoot = require('find-yarn-workspace-root')
|
|
7
|
+
|
|
8
|
+
const TOPTAL_SSH_REPO_URL_REGEX =
|
|
9
|
+
/^git@github\.com:\/?toptal\/([A-Za-z0-9_.-]+)\.git$/
|
|
10
|
+
|
|
11
|
+
const TOPTAL_HTTPS_REPO_URL_REGEX =
|
|
12
|
+
/^https:\/\/github\.com\/toptal\/([A-Za-z0-9_.-]+)\.git$/
|
|
13
|
+
|
|
14
|
+
const UNKNOWN = '<unknown>'
|
|
15
|
+
|
|
16
|
+
const getProjectName = async () => {
|
|
17
|
+
const gitProcess = await execa('git', ['remote', 'get-url', 'origin'])
|
|
18
|
+
const repoOriginUrl = gitProcess.stdout
|
|
19
|
+
|
|
20
|
+
// Not matching with a pattern means that either:
|
|
21
|
+
// 1. Project is not from toptal, so not relevant information
|
|
22
|
+
// 2. Project is not on github, unlikely for toptal
|
|
23
|
+
// 3. User is using an ssh alias, in this case we cannot catch it in a sane way otherwise
|
|
24
|
+
const toptalRepoFound = [
|
|
25
|
+
TOPTAL_SSH_REPO_URL_REGEX,
|
|
26
|
+
TOPTAL_HTTPS_REPO_URL_REGEX,
|
|
27
|
+
]
|
|
28
|
+
.map(pattern => repoOriginUrl.match(pattern))
|
|
29
|
+
.find(Boolean)
|
|
30
|
+
|
|
31
|
+
return toptalRepoFound?.[1] ?? UNKNOWN
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const getGitBranch = async () => {
|
|
35
|
+
const gitProcess = await execa('git', ['rev-parse', '--abbrev-ref', 'HEAD'])
|
|
36
|
+
|
|
37
|
+
return gitProcess.stdout
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const getOsInfo = () => [os.platform(), os.release(), os.arch()].join(' ')
|
|
41
|
+
|
|
42
|
+
const getPackageManagerFromLockfiles = () => {
|
|
43
|
+
const lockFiles = {
|
|
44
|
+
npm: 'package-lock.json',
|
|
45
|
+
yarn: 'yarn.lock',
|
|
46
|
+
pnpm: 'pnpm-lock.yaml',
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const workspaceRoot = findYarnWorkspaceRoot() ?? process.cwd()
|
|
50
|
+
|
|
51
|
+
for (const [packageManager, lockFile] of Object.entries(lockFiles)) {
|
|
52
|
+
if (fs.existsSync(path.resolve(workspaceRoot, lockFile))) {
|
|
53
|
+
return packageManager
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return UNKNOWN
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const getPackageManager = () => {
|
|
61
|
+
const userAgent = process.env.npm_config_user_agent ?? ''
|
|
62
|
+
const possiblePackageManagers = ['pnpm', 'yarn', 'npm']
|
|
63
|
+
|
|
64
|
+
// Should find a package manager when executed as a package.json script
|
|
65
|
+
const foundPackageManager = possiblePackageManagers.find(packageManager =>
|
|
66
|
+
userAgent.includes(packageManager)
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
return foundPackageManager ?? getPackageManagerFromLockfiles() ?? UNKNOWN
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const hideSensitiveInformation = data => ({
|
|
73
|
+
...data,
|
|
74
|
+
projectName: UNKNOWN,
|
|
75
|
+
packageName: UNKNOWN,
|
|
76
|
+
packageVersion: UNKNOWN,
|
|
77
|
+
gitBranch: UNKNOWN,
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
const sanitizeCommandDefinition = commandStr =>
|
|
81
|
+
commandStr
|
|
82
|
+
.replace(/\[[^\]]*\]/g, '') // Remove optional extra arguments (Eg. [foo...])
|
|
83
|
+
.replace(/<[^>]*>/g, '') // Remove required extra arguments (Eg. <foo>)
|
|
84
|
+
.replace(/ +/g, ' ') // Replace multiple spaces to a single space
|
|
85
|
+
.trim()
|
|
86
|
+
.replace(/ /g, '_')
|
|
87
|
+
|
|
88
|
+
const gatherUsageData = async (commandIdentifier, davinciCommand) => {
|
|
89
|
+
const [projectName, gitBranch, packageInfo] = await Promise.all([
|
|
90
|
+
getProjectName(),
|
|
91
|
+
getGitBranch(),
|
|
92
|
+
readPackageUp(),
|
|
93
|
+
])
|
|
94
|
+
|
|
95
|
+
const {
|
|
96
|
+
packageJson: { name: packageName, version: packageVersion },
|
|
97
|
+
} = packageInfo
|
|
98
|
+
const osInfo = getOsInfo()
|
|
99
|
+
const packageManager = getPackageManager()
|
|
100
|
+
const nodeVersion = process.version
|
|
101
|
+
const commandName = sanitizeCommandDefinition(
|
|
102
|
+
`${davinciCommand}_${commandIdentifier}`
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
const data = {
|
|
106
|
+
commandName,
|
|
107
|
+
projectName,
|
|
108
|
+
packageName,
|
|
109
|
+
packageVersion,
|
|
110
|
+
gitBranch,
|
|
111
|
+
osInfo,
|
|
112
|
+
packageManager,
|
|
113
|
+
nodeVersion,
|
|
114
|
+
davinciPackage: davinciCommand,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (projectName === UNKNOWN) {
|
|
118
|
+
// If the project is unknown we are probably dealing with an external user
|
|
119
|
+
// Hiding sensitive information
|
|
120
|
+
return hideSensitiveInformation(data)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return data
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
module.exports = { gatherUsageData }
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const semver = require('semver')
|
|
2
|
+
const readPkgUp = require('read-pkg-up')
|
|
3
|
+
|
|
4
|
+
const yarnAdapter = require('./yarn-adapter')
|
|
5
|
+
|
|
6
|
+
const getPackagePackageJson = cwd => {
|
|
7
|
+
return readPkgUp.sync({ cwd }).packageJson
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const getInstalledPackageVersion = packageName => {
|
|
11
|
+
const packageInfoList = yarnAdapter.list(packageName)
|
|
12
|
+
|
|
13
|
+
if (!packageInfoList.size) {
|
|
14
|
+
return null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return packageInfoList.get(packageName)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const checkPrerequirement = ({ dependencies, packageName }) => {
|
|
21
|
+
const validVersionOrRange = dependencies[packageName]
|
|
22
|
+
|
|
23
|
+
const installedVersion = getInstalledPackageVersion(packageName)
|
|
24
|
+
|
|
25
|
+
if (!installedVersion) {
|
|
26
|
+
return `Cannot find module '${packageName}'.`
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (semver.satisfies(semver.coerce(installedVersion), validVersionOrRange)) {
|
|
30
|
+
return null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return `Invalid version of package ${packageName} is installed.
|
|
34
|
+
Installed version: ${installedVersion}
|
|
35
|
+
Should be: ${validVersionOrRange}`
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = {
|
|
39
|
+
getPackagePackageJson,
|
|
40
|
+
checkPrerequirement,
|
|
41
|
+
}
|
package/src/utils/print.js
CHANGED
|
@@ -5,11 +5,15 @@ const isObject = obj => typeof obj === 'object'
|
|
|
5
5
|
const inspectObject = obj =>
|
|
6
6
|
util.inspect(obj, {
|
|
7
7
|
colors: false,
|
|
8
|
-
depth: null
|
|
8
|
+
depth: null,
|
|
9
9
|
})
|
|
10
10
|
const prettifyObjectIfExist = args =>
|
|
11
11
|
args.map(arg => (isObject(arg) ? inspectObject(arg) : arg))
|
|
12
12
|
|
|
13
|
+
const cyan = (...args) => {
|
|
14
|
+
console.log(chalk.cyan(...prettifyObjectIfExist(args)))
|
|
15
|
+
}
|
|
16
|
+
|
|
13
17
|
const green = (...args) => {
|
|
14
18
|
console.log(chalk.green(...prettifyObjectIfExist(args)))
|
|
15
19
|
}
|
|
@@ -59,9 +63,10 @@ const prettifyCommands = commands => {
|
|
|
59
63
|
|
|
60
64
|
module.exports = {
|
|
61
65
|
green,
|
|
66
|
+
cyan,
|
|
62
67
|
yellow,
|
|
63
68
|
red,
|
|
64
69
|
grey,
|
|
65
70
|
generatePadding,
|
|
66
|
-
prettifyCommands
|
|
71
|
+
prettifyCommands,
|
|
67
72
|
}
|
package/src/utils/prompt.js
CHANGED
|
@@ -5,8 +5,8 @@ const confirm = async (name, message) => {
|
|
|
5
5
|
{
|
|
6
6
|
type: 'confirm',
|
|
7
7
|
name,
|
|
8
|
-
message
|
|
9
|
-
}
|
|
8
|
+
message,
|
|
9
|
+
},
|
|
10
10
|
])
|
|
11
11
|
|
|
12
12
|
return result[name]
|
|
@@ -18,8 +18,8 @@ const input = async (name, message, validate) => {
|
|
|
18
18
|
type: 'input',
|
|
19
19
|
name,
|
|
20
20
|
message,
|
|
21
|
-
validate
|
|
22
|
-
}
|
|
21
|
+
validate,
|
|
22
|
+
},
|
|
23
23
|
])
|
|
24
24
|
|
|
25
25
|
return result[name]
|
|
@@ -31,8 +31,8 @@ const password = async (name, message, validate) => {
|
|
|
31
31
|
type: 'password',
|
|
32
32
|
name,
|
|
33
33
|
message,
|
|
34
|
-
validate
|
|
35
|
-
}
|
|
34
|
+
validate,
|
|
35
|
+
},
|
|
36
36
|
])
|
|
37
37
|
|
|
38
38
|
return result[name]
|
|
@@ -41,5 +41,5 @@ const password = async (name, message, validate) => {
|
|
|
41
41
|
module.exports = {
|
|
42
42
|
confirm,
|
|
43
43
|
input,
|
|
44
|
-
password
|
|
44
|
+
password,
|
|
45
45
|
}
|
package/src/utils/run.js
CHANGED
|
@@ -8,7 +8,7 @@ const runSync = (command, args, options) => {
|
|
|
8
8
|
|
|
9
9
|
const result = execa.sync(command, args, {
|
|
10
10
|
stdio: 'inherit',
|
|
11
|
-
...options
|
|
11
|
+
...options,
|
|
12
12
|
})
|
|
13
13
|
|
|
14
14
|
console.log('Done.')
|
|
@@ -35,5 +35,5 @@ const run = async (command, args, options) => {
|
|
|
35
35
|
|
|
36
36
|
module.exports = {
|
|
37
37
|
run,
|
|
38
|
-
runSync
|
|
38
|
+
runSync,
|
|
39
39
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const { runSync } = require('./run')
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param {string} treeName
|
|
6
|
+
* @example '@toptal/davinci-engine@7.5.2'
|
|
7
|
+
* @returns {Array} Pair package name and version, ex. ['@toptal/davinci-engine', '16.0.2']
|
|
8
|
+
*/
|
|
9
|
+
const splitNameAndVersion = treeName => {
|
|
10
|
+
const atPos = treeName.lastIndexOf('@')
|
|
11
|
+
|
|
12
|
+
return [treeName.slice(0, atPos), treeName.slice(atPos + 1)]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const list = packageName => {
|
|
16
|
+
const { stdout } = runSync(
|
|
17
|
+
'yarn',
|
|
18
|
+
[
|
|
19
|
+
'list',
|
|
20
|
+
'--json',
|
|
21
|
+
'--no-progress',
|
|
22
|
+
'--non-interactive',
|
|
23
|
+
'--depth=0',
|
|
24
|
+
'--pattern',
|
|
25
|
+
packageName,
|
|
26
|
+
],
|
|
27
|
+
{
|
|
28
|
+
stdio: undefined,
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
const listInfo = JSON.parse(stdout)
|
|
33
|
+
const packagesFound = listInfo?.data?.trees || []
|
|
34
|
+
|
|
35
|
+
if (packagesFound.length <= 0) {
|
|
36
|
+
return new Map()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return new Map(packagesFound.map(pkg => splitNameAndVersion(pkg.name)))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = {
|
|
43
|
+
list,
|
|
44
|
+
}
|