envinfo
Advanced tools
Comparing version 4.4.2 to 5.0.0
module.exports = { | ||
env: { | ||
node: true, | ||
es6: true, | ||
jest: true, | ||
}, | ||
@@ -5,0 +7,0 @@ extends: ['airbnb-base/legacy', 'prettier'], |
{ | ||
"name": "envinfo", | ||
"version": "4.4.2", | ||
"version": "5.0.0", | ||
"description": "Info about your dev environment for debugging purposes", | ||
@@ -19,3 +19,4 @@ "repository": "https://github.com/tabrindle/envinfo", | ||
"format": "prettier --write 'src/**/*.js'", | ||
"test": "npm run lint && prettier -l 'src/**/*.js'", | ||
"test": "jest && npm run lint && npm run check:format", | ||
"check:format": "prettier -l 'src/**/*.js'", | ||
"preversion": "yarn webpack && git add ." | ||
@@ -36,2 +37,3 @@ }, | ||
"clipboardy": "^1.2.2", | ||
"eslint": "^4.10.0", | ||
"eslint-config-airbnb-base": "^12.1.0", | ||
@@ -41,4 +43,4 @@ "eslint-config-prettier": "^2.7.0", | ||
"eslint-plugin-prettier": "^2.3.1", | ||
"eslint": "^4.10.0", | ||
"glob": "^7.1.2", | ||
"jest": "^22.4.3", | ||
"minimist": "^1.2.0", | ||
@@ -50,3 +52,4 @@ "object.entries": "^1.0.4", | ||
"uglifyjs-webpack-plugin": "^1.2.1", | ||
"webpack": "^3.11.0", | ||
"webpack": "^4.5.0", | ||
"webpack-cli": "^2.0.14", | ||
"which": "^1.2.14", | ||
@@ -53,0 +56,0 @@ "yamlify-object": "^0.4.5" |
@@ -9,2 +9,15 @@ <p align="center"> | ||
## The problem | ||
- It works on my computer | ||
- "command not found" | ||
- what version of "command" are you running? | ||
- what version of "different command" are you running? | ||
- do you have "insert obscure android sdk version"? | ||
- every github issue reporting template ever: | ||
**Please mention other relevant information such as the browser version, Node.js version, Operating System and programming language.** | ||
## This solution | ||
- Gather all of this information in one spot, quickly, and painlessly. | ||
## Installation | ||
@@ -108,3 +121,3 @@ | ||
``` | ||
returns: | ||
``` | ||
@@ -159,47 +172,21 @@ { | ||
* --clipboard - Optionally copy directly to your clipboard with `envinfo --clipboard`. This feature uses [Clipboardy](https://www.npmjs.com/package/clipboardy) | ||
* --npmPackages - Optionally return packages from your package.json: takes either boolean or comma delimited string in CLI or array via API | ||
`envinfo --npmPackages minimist,which` | ||
```sh | ||
npmPackages: | ||
minimist: | ||
wanted: ^1.2.0 | ||
installed: 1.2.0 | ||
which: | ||
wanted: ^1.2.14 | ||
installed: 1.3.0 | ||
``` | ||
--system Print general system info such as OS, CPU, Memory and Shell | ||
--browsers Get version numbers of installed web browsers | ||
--SDKs Get platforms, build tools and SDKs of iOS and Android | ||
--IDEs Get version numbers of installed IDEs | ||
--languages Get version numbers of installed languages such as Java, Python, PHP, etc | ||
--binaries Get version numbers of node, npm, watchman, etc | ||
--npmPackages Get version numbers of locally installed npm packages - glob, string, or comma delimited list | ||
--npmGlobalPackages Get version numbers of globally installed npm packages | ||
* --npmGlobalPackages - print your npm global packages versions | ||
--duplicates Mark duplicate npm packages inside parentheses eg. (2.1.4) | ||
--fullTree Traverse entire node_modules dependency tree, not just top level | ||
`envinfo --npmGlobalPackages` | ||
```sh | ||
npmGlobalPackages: | ||
exp: 48.0.2 | ||
npm: 5.6.0 | ||
react-native-cli: 2.0.1 | ||
--markdown Print output in markdown format | ||
--json Print output in JSON format | ||
--console Print to console (defaults to on for CLI usage, off for programmatic usage) | ||
--clipboard Copy output to your system clipboard (uses clipboardy) | ||
``` | ||
* --duplicates - will search given packages for duplicates, display in parentheses | ||
```sh | ||
Packages: | ||
minimist: ^1.2.0 => 1.2.0 (1.2.0, 0.0.8) | ||
``` | ||
* --fullTree - will traverse and print the entire flattened dependency tree (optionally also with --duplicates) | ||
```sh | ||
Packages: | ||
... | ||
minimatch: 3.0.4 | ||
minimist: ^1.2.0 => 1.2.0 (1.2.0, 0.0.8) | ||
mkdirp: 0.5.1 | ||
... | ||
``` | ||
## Integration | ||
@@ -210,4 +197,5 @@ | ||
* [React Native](https://github.com/facebook/react-native) (`react-native info`) | ||
* [create-react-app](https://github.com/facebook/create-react-app) (`create-react-app --info`) | ||
* [Exponent Development CLI](https://github.com/expo/exp) (`exp diagnostics`) | ||
* [Create React App](https://github.com/facebook/create-react-app) (`create-react-app --info`) | ||
* [Expo](https://github.com/expo/exp) (`exp diagnostics`) | ||
* [Webpack](https://github.com/webpack/webpack-cli) (`webpack-cli info`) | ||
* [Solidarity](https://github.com/infinitered/solidarity) (`solidarity report`) | ||
@@ -224,1 +212,10 @@ | ||
This project came out of a [PR](https://github.com/facebook/react-native/pull/14428) to the React Native CLI tool - issues are reported frequently without important environment information, like Node/npm versions. | ||
## Alternatives | ||
- type `$ command -v` until you smash your computer | ||
- [screenfetch](https://github.com/KittyKatt/screenFetch) - fetch system and terminal information, and display a pretty ascii logo | ||
- [Solidarity](https://github.com/infinitered/solidarity) - a project based environment checker | ||
- write your own | ||
## License | ||
MIT |
@@ -5,3 +5,2 @@ 'use strict'; | ||
const helpers = require('./helpers'); | ||
const helperMap = require('./map'); | ||
const formatters = require('./formatters'); | ||
@@ -18,45 +17,3 @@ const presets = require('./presets'); | ||
// a map of all the capabilities of envinfo - used as a default input | ||
const capabilities = { | ||
System: ['OS', 'CPU', 'Free Memory', 'Total Memory', 'Shell'], | ||
Binaries: ['Node', 'Yarn', 'npm', 'Watchman'], | ||
Virtualization: ['Docker', 'Parallels', 'Virtualbox', 'VMware Fusion'], | ||
SDKs: ['iOS', 'Android'], | ||
IDEs: ['Android Studio', 'Atom', 'VSCode', 'Sublime Text', 'Xcode'], | ||
Languages: ['Bash', 'Go', 'Elixir', 'PHP', 'Python', 'Ruby'], | ||
Browsers: [ | ||
'Chrome', | ||
'Chrome Canary', | ||
'Firefox', | ||
'Firefox Developer Edition', | ||
'Firefox Nightly', | ||
'Safari', | ||
'Safari Technology Preview', | ||
], | ||
npmPackages: null, | ||
npmGlobalPackages: true, | ||
}; | ||
function main(props, options) { | ||
// set props to passed in props or default all capabilities | ||
const defaults = Object.keys(props).length > 0 ? props : capabilities; | ||
options = options || {}; | ||
// get data by iterating and calling helper functions | ||
const data = Object.entries(defaults).reduce((acc, prop) => { | ||
const category = prop[0]; | ||
const value = prop[1]; | ||
// create an object of all the resolved helper values | ||
return Object.assign(acc, { | ||
[category]: helperMap[category] | ||
? helperMap[category](value, options) // if there is a category level helper | ||
: value.reduce((cat, name) => { | ||
const fn = helperMap[name.toLowerCase().replace(/\s/g, '_')]; | ||
return Object.assign(cat, { | ||
[name]: fn ? fn() : 'Unknown', | ||
}); | ||
}, {}), | ||
}); | ||
}, {}); | ||
function format(data, options) { | ||
// set the default formatter (yaml is default, similar to old table) | ||
@@ -69,6 +26,6 @@ const formatter = (() => { | ||
if (options.console) return console.log(formatter(data, { console: true })); // eslint-disable-line no-console | ||
// call the formatter with console option off first to return, or pipe to clipboard | ||
const formatted = formatter(data, { console: false }); | ||
if (options.console) console.log(formatter(data, { console: true })); // eslint-disable-line no-console | ||
if (options.clipboard) copypasta.writeSync(formatted); | ||
@@ -79,2 +36,55 @@ | ||
function main(props, options) { | ||
options = options || {}; | ||
// set props to passed in props or default to presets.defaults | ||
const defaults = Object.keys(props).length > 0 ? props : presets.defaults; | ||
// collect a list of promises of helper functions | ||
const promises = Object.entries(defaults).reduce((acc, entries) => { | ||
const category = entries[0]; | ||
const value = entries[1]; | ||
const categoryFn = helpers[`get${category}`]; | ||
// check to see if a category level function exists | ||
if (categoryFn) { | ||
if (value) acc.push(categoryFn(value, options)); | ||
// if the value on the category is falsy, don't run it | ||
return acc; | ||
} | ||
// map over a categories helper functions, call and add to the stack | ||
acc = acc.concat( | ||
(value || []).map(v => { | ||
const helperFn = helpers[`get${v.replace(/\s/g, '')}Info`]; | ||
return helperFn ? helperFn() : Promise.resolve(['Unknown']); | ||
}) | ||
); | ||
return acc; | ||
}, []); | ||
// once all tasks are done, map all the data back to the shape of the original config obj | ||
return Promise.all(promises).then(data => { | ||
// reduce promise results to object for addressability. | ||
const dataObj = data.reduce((acc, dataValue) => { | ||
if (dataValue && dataValue[0]) Object.assign(acc, { [dataValue[0]]: dataValue }); | ||
return acc; | ||
}, {}); | ||
// use defaults object for mapping | ||
const reduced = Object.entries(presets.defaults).reduce((acc, entries) => { | ||
const category = entries[0]; | ||
const values = entries[1]; | ||
if (dataObj[category]) return Object.assign(acc, { [category]: dataObj[category][1] }); | ||
return Object.assign(acc, { | ||
[category]: (values || []).reduce((helperAcc, v) => { | ||
if (dataObj[v]) { | ||
dataObj[v].shift(); | ||
if (dataObj[v].length === 1) return Object.assign(helperAcc, { [v]: dataObj[v][0] }); | ||
return Object.assign(helperAcc, { | ||
[v]: { version: dataObj[v][0], path: dataObj[v][1] }, | ||
}); | ||
} | ||
return helperAcc; | ||
}, {}), | ||
}); | ||
}, {}); | ||
return format(reduced, options); | ||
}); | ||
} | ||
// Example usage: | ||
@@ -84,6 +94,18 @@ // $ envinfo --system --npmPackages | ||
// if all option is passed, do not pass go, do not collect 200 dollars, go straight to main | ||
if (options.all) return main(Object.assign({}, capabilities, { npmPackages: true }), options); | ||
if (options.all) | ||
return main( | ||
Object.assign({}, presets.defaults, { npmPackages: true, npmGlobalPackages: true }), | ||
options | ||
); | ||
// if raw, parse the row options and skip to main | ||
if (options.raw) return main(JSON.parse(options.raw), options); | ||
// generic function to make sure passed option exists in capability list | ||
// if helper flag, run just that helper then log the results | ||
if (options.helper) { | ||
const helper = | ||
helpers[`get${options.helper}`] || | ||
helpers[`get${options.helper}Info`] || | ||
helpers[options.helper]; | ||
return helper ? helper().then(console.log) : console.error('Not Found'); | ||
} | ||
// generic function to make sure passed option exists in presets.defaults list | ||
// TODO: This will eventually be replaced with a better fuzzy finder. | ||
@@ -93,6 +115,6 @@ const matches = (list, opt) => list.toLowerCase().includes(opt.toLowerCase()); | ||
const categories = Object.keys(options).filter(o => | ||
Object.keys(capabilities).some(c => matches(c, o)) | ||
Object.keys(presets.defaults).some(c => matches(c, o)) | ||
); | ||
// build the props object for filtering capabilities | ||
const props = Object.entries(capabilities).reduce((acc, entry) => { | ||
// build the props object for filtering presets.defaults | ||
const props = Object.entries(presets.defaults).reduce((acc, entry) => { | ||
if (categories.some(c => matches(c, entry[0]))) { | ||
@@ -103,3 +125,2 @@ return Object.assign(acc, { [entry[0]]: entry[1] || options[entry[0]] }); | ||
}, {}); | ||
// if there is a preset, merge that with the parsed props and options | ||
@@ -106,0 +127,0 @@ if (options.preset) { |
@@ -37,5 +37,5 @@ const yamlify = require('yamlify-object'); | ||
function formatPackages(data) { | ||
return Object.assign( | ||
data, | ||
Object.entries(data.packages || {}).reduce((acc, entry) => { | ||
if (!data.npmPackages) return data; | ||
return Object.assign(data, { | ||
npmPackages: Object.entries(data.npmPackages || {}).reduce((acc, entry) => { | ||
const key = entry[0]; | ||
@@ -51,4 +51,4 @@ const value = entry[1]; | ||
}); | ||
}, {}) | ||
); | ||
}, {}), | ||
}); | ||
} | ||
@@ -81,2 +81,27 @@ | ||
function serializeVersionsAndPaths(data) { | ||
return Object.entries(data).reduce( | ||
(Dacc, Dentry) => | ||
Object.assign( | ||
Dacc, | ||
{ | ||
[Dentry[0]]: Object.entries(Dentry[1]).reduce((acc, entry) => { | ||
const key = entry[0]; | ||
const value = entry[1]; | ||
if (value.version) { | ||
return Object.assign(acc, { | ||
[key]: [value.version, value.path].filter(Boolean).join(' - '), | ||
}); | ||
} | ||
return Object.assign(acc, { | ||
[key]: [value][0], | ||
}); | ||
}, {}), | ||
}, | ||
{} | ||
), | ||
{} | ||
); | ||
} | ||
function yaml(data) { | ||
@@ -118,5 +143,6 @@ return yamlify(data, { | ||
return utils.pipe([ | ||
clean, | ||
formatPackages, | ||
serializeArrays, | ||
clean, | ||
serializeVersionsAndPaths, | ||
yaml, | ||
@@ -128,3 +154,10 @@ options.console ? formatHeaders : utils.noop, | ||
function formatToMarkdown(data) { | ||
return utils.pipe([formatPackages, serializeArrays, clean, yaml, markdown])(data); | ||
return utils.pipe([ | ||
clean, | ||
formatPackages, | ||
serializeArrays, | ||
serializeVersionsAndPaths, | ||
yaml, | ||
markdown, | ||
])(data); | ||
} | ||
@@ -135,3 +168,3 @@ | ||
data = utils.pipe([json])(data); | ||
data = utils.pipe([clean, json])(data); | ||
data = options.console ? `\n${data}\n` : data; | ||
@@ -138,0 +171,0 @@ |
@@ -5,409 +5,381 @@ const childProcess = require('child_process'); | ||
const path = require('path'); | ||
const which = require('which'); | ||
const packages = require('./packages'); | ||
const utils = require('./utils'); | ||
var browserBundleIdentifiers = { | ||
Chrome: 'com.google.Chrome', | ||
'Chrome Canary': 'com.google.Chrome.canary', | ||
Firefox: 'org.mozilla.firefox', | ||
'Firefox Developer Edition': 'org.mozilla.firefoxdeveloperedition', | ||
'Firefox Nightly': 'org.mozilla.nightly', | ||
Safari: 'com.apple.Safari', | ||
'Safari Technology Preview': 'com.apple.SafariTechnologyPreview', | ||
}; | ||
function findDarwinApplication(id) { | ||
var appPath; | ||
try { | ||
appPath = utils.run('mdfind "kMDItemCFBundleIdentifier=="' + id + '""').replace(/(\s)/g, '\\ '); | ||
} catch (error) { | ||
appPath = null; | ||
} | ||
return appPath; | ||
} | ||
function generatePlistBuddyCommand(appPath, options) { | ||
var optionsArray = (options || ['CFBundleShortVersionString']).map(function optionsMap(option) { | ||
return '-c Print:' + option; | ||
}); | ||
return ['/usr/libexec/PlistBuddy'] | ||
.concat(optionsArray) | ||
.concat([appPath]) | ||
.join(' '); | ||
} | ||
function getDarwinApplicationVersion(bundleIdentifier) { | ||
var version; | ||
if (process.platform === 'darwin') { | ||
try { | ||
version = utils.run( | ||
generatePlistBuddyCommand( | ||
path.join(findDarwinApplication(bundleIdentifier), 'Contents', 'Info.plist'), | ||
['CFBundleShortVersionString'] | ||
) | ||
); | ||
} catch (error) { | ||
version = 'Not Found'; | ||
module.exports = Object.assign({}, utils, packages, { | ||
getiOSSDKInfo: () => { | ||
var iOSSDKVersions; | ||
if (process.platform === 'darwin') { | ||
iOSSDKVersions = utils | ||
.run('xcodebuild -showsdks') | ||
.then(sdks => sdks.match(/[\w]+\s[\d|.]+/g)) | ||
.then(utils.uniq) | ||
.catch(() => 'Unknown'); | ||
} else { | ||
iOSSDKVersions = 'N/A'; | ||
} | ||
return version; | ||
} | ||
return 'N/A'; | ||
} | ||
return iOSSDKVersions.then(platforms => ['iOS SDK', { Platforms: platforms }]); | ||
}, | ||
function getAlliOSSDKs() { | ||
var iOSSDKVersions; | ||
getAndroidSDKInfo: () => { | ||
var buildTools = []; | ||
var androidAPIs = []; | ||
return utils | ||
.run( | ||
process.env.ANDROID_HOME ? '$ANDROID_HOME/tools/bin/sdkmanager --list' : 'sdkmanager --list' | ||
) | ||
.then(output => { | ||
const installed = output.split('Available')[0]; | ||
const getBuildVersions = /build-tools;([\d|.]+)[\S\s]/g; | ||
const getAPIVersions = /platforms;android-(\d+)[\S\s]/g; | ||
let matcher; | ||
// eslint-disable-next-line | ||
while ((matcher = getBuildVersions.exec(installed))) { | ||
buildTools.push(matcher[1]); | ||
} | ||
// eslint-disable-next-line | ||
while ((matcher = getAPIVersions.exec(installed))) { | ||
androidAPIs.push(matcher[1]); | ||
} | ||
return [ | ||
'Android SDK', | ||
{ | ||
'Build Tools': buildTools, | ||
'API Levels': androidAPIs, | ||
}, | ||
]; | ||
}) | ||
.catch(() => ({ | ||
buildTools: ['Unknown'], | ||
androidAPIs: ['Unknown'], | ||
})); | ||
}, | ||
if (process.platform === 'darwin') { | ||
try { | ||
var output = utils.run('xcodebuild -showsdks'); | ||
iOSSDKVersions = output.match(/[\w]+\s[\d|.]+/g); | ||
} catch (e) { | ||
iOSSDKVersions = 'Unknown'; | ||
} | ||
} else { | ||
return 'N/A'; | ||
} | ||
return { | ||
Platforms: utils.uniq(iOSSDKVersions), | ||
}; | ||
} | ||
function getAllAndroidSDKs() { | ||
var buildTools = []; | ||
var androidAPIs = []; | ||
try { | ||
// try to use preferred install path | ||
var command = process.env.ANDROID_HOME ? '$ANDROID_HOME/tools/bin/sdkmanager' : 'sdkmanager'; | ||
var installed = utils.run(command + ' --list').split('Available')[0]; | ||
var getBuildVersions = /build-tools;([\d|.]+)[\S\s]/g; | ||
var getAPIVersions = /platforms;android-(\d+)[\S\s]/g; | ||
var matcher; | ||
// eslint-disable-next-line | ||
while ((matcher = getBuildVersions.exec(installed))) { | ||
buildTools.push(matcher[1]); | ||
} | ||
// eslint-disable-next-line | ||
while ((matcher = getAPIVersions.exec(installed))) { | ||
androidAPIs.push(matcher[1]); | ||
} | ||
} catch (err) { | ||
buildTools = ['Unknown']; | ||
androidAPIs = ['Unknown']; | ||
} | ||
return { | ||
'Build Tools': buildTools, | ||
'API Levels': androidAPIs, | ||
}; | ||
} | ||
function getAndroidStudioVersion() { | ||
var androidStudioVersion = 'Not Found'; | ||
if (process.platform === 'darwin') { | ||
try { | ||
getAndroidStudioInfo: () => { | ||
let androidStudioVersion; | ||
if (process.platform === 'darwin') { | ||
androidStudioVersion = utils | ||
.run( | ||
generatePlistBuddyCommand('/Applications/Android\\ Studio.app/Contents/Info.plist', [ | ||
'CFBundleShortVersionString', | ||
'CFBundleVersion', | ||
]) | ||
utils.generatePlistBuddyCommand( | ||
path.join('/', 'Applications', 'Android\\ Studio.app', 'Contents', 'Info.plist'), | ||
['CFBundleShortVersionString', 'CFBundleVersion'] | ||
) | ||
) | ||
.split('\n') | ||
.join(' '); | ||
} catch (err) { | ||
androidStudioVersion = 'Not Found'; | ||
.then(version => version.split('\n').join(' ')); | ||
} else if (process.platform === 'linux') { | ||
androidStudioVersion = Promise.all([ | ||
utils | ||
.run('cat /opt/android-studio/bin/studio.sh | grep "$Home/.AndroidStudio" | head -1') | ||
.then(utils.findVersion), | ||
utils.run('cat /opt/android-studio/build.txt'), | ||
]).then(tasks => { | ||
const linuxVersion = tasks[0]; | ||
const linuxBuildNumber = tasks[1]; | ||
return `${linuxVersion} ${linuxBuildNumber}`; | ||
}); | ||
} else if (process.platform.startsWith('win')) { | ||
androidStudioVersion = Promise.all([ | ||
utils | ||
.run( | ||
'wmic datafile where name="C:\\\\Program Files\\\\Android\\\\Android Studio\\\\bin\\\\studio.exe" get Version' | ||
) | ||
.then(version => version.replace(/(\r\n|\n|\r)/gm, '')), | ||
utils | ||
.run('type "C:\\\\Program Files\\\\Android\\\\Android Studio\\\\build.txt"') | ||
.then(version => version.replace(/(\r\n|\n|\r)/gm, '')), | ||
]).then(tasks => { | ||
const windowsVersion = tasks[0]; | ||
const windowsBuildNumber = tasks[1]; | ||
return `${windowsVersion} ${windowsBuildNumber}`; | ||
}); | ||
} | ||
} else if (process.platform === 'linux') { | ||
return Promise.all(['Android Studio', androidStudioVersion || 'Not Found']); | ||
}, | ||
getAtomInfo: () => { | ||
utils.log('trace', 'getAtomInfo'); | ||
return Promise.all(['Atom', utils.getDarwinApplicationVersion('com.github.atom'), 'N/A']); | ||
}, | ||
getCPUInfo: () => { | ||
utils.log('trace', 'getCPUInfo'); | ||
let info; | ||
try { | ||
var linuxBuildNumber = utils.run('cat /opt/android-studio/build.txt'); | ||
var linuxVersion = utils | ||
.run('cat /opt/android-studio/bin/studio.sh | grep "$Home/.AndroidStudio" | head -1') | ||
.match(/\d\.\d/)[0]; | ||
androidStudioVersion = `${linuxVersion} ${linuxBuildNumber}`; | ||
info = os.arch() + ' ' + os.cpus()[0].model; | ||
} catch (err) { | ||
androidStudioVersion = 'Not Found'; | ||
info = 'Unknown'; | ||
} | ||
} else if (process.platform.startsWith('win')) { | ||
try { | ||
var windowsVersion = utils | ||
.run( | ||
'wmic datafile where name="C:\\\\Program Files\\\\Android\\\\Android Studio\\\\bin\\\\studio.exe" get Version' | ||
) | ||
.replace(/(\r\n|\n|\r)/gm, ''); | ||
var windowsBuildNumber = utils | ||
.run('type "C:\\\\Program Files\\\\Android\\\\Android Studio\\\\build.txt"') | ||
.replace(/(\r\n|\n|\r)/gm, ''); | ||
androidStudioVersion = `${windowsVersion} ${windowsBuildNumber}`; | ||
} catch (err) { | ||
androidStudioVersion = 'Not Found'; | ||
} | ||
} | ||
return androidStudioVersion; | ||
} | ||
return Promise.all(['CPU', info]); | ||
}, | ||
function getAtomVersion() { | ||
return utils.customGenericVersionFunction(() => getDarwinApplicationVersion('com.github.atom')); | ||
} | ||
getBashInfo: () => { | ||
utils.log('trace', 'getBashInfo'); | ||
return Promise.all([ | ||
'Bash', | ||
utils.run('bash --version').then(utils.findVersion), | ||
utils.which('bash'), | ||
]); | ||
}, | ||
function getCPUInfo() { | ||
return utils.customGenericVersionFunction(() => os.arch() + ' ' + os.cpus()[0].model, 'Unknown'); | ||
} | ||
getPHPInfo: () => { | ||
utils.log('trace', 'getPHPInfo'); | ||
return Promise.all(['PHP', utils.run('php -v').then(utils.findVersion), utils.which('php')]); | ||
}, | ||
function getBashVersion() { | ||
var bashVersion; | ||
var bashPath; | ||
try { | ||
bashPath = which.sync('bash'); | ||
bashVersion = utils.run(`${bashPath} --version`).match(utils.versionRegex)[0]; | ||
} catch (error) { | ||
bashVersion = 'Not Found'; | ||
} | ||
return bashVersion; | ||
} | ||
getParallelsInfo: () => { | ||
utils.log('trace', 'getParallelsInfo'); | ||
return Promise.all([ | ||
'Parallels', | ||
utils.run('prlctl --version').then(utils.findVersion), | ||
utils.which('prlctl'), | ||
]); | ||
}, | ||
function getPhpVersion() { | ||
return utils.customGenericVersionFunction(() => utils.run('php -v').split(' ', 2)[1]); | ||
} | ||
getDockerInfo: () => { | ||
utils.log('trace', 'getDockerInfo'); | ||
return Promise.all([ | ||
'Docker', | ||
utils.run('docker --version').then(utils.findVersion), | ||
utils.which('docker'), | ||
]); | ||
}, | ||
function getParallelsVersion() { | ||
return utils.customGenericVersionFunction( | ||
() => utils.run('prlctl --version').match(/[version]+\s([\d|.]+)/)[1] | ||
); | ||
} | ||
getElixirInfo: () => { | ||
utils.log('trace', 'getElixirInfo'); | ||
return Promise.all([ | ||
'Elixir', | ||
utils.run('elixir --version').then(version => version.match(/[Elixir]+\s([\d+.[\d+|.]+)/)[1]), | ||
utils.which('elixir'), | ||
]); | ||
}, | ||
function getDockerVersion() { | ||
return utils.customGenericVersionFunction(() => | ||
utils.run('docker --version').replace('Docker version ', '') | ||
); | ||
} | ||
getMemoryInfo: () => { | ||
utils.log('trace', 'getMemoryInfo'); | ||
return Promise.all([ | ||
'Memory', | ||
`${utils.toReadableBytes(os.freemem())} / ${utils.toReadableBytes(os.totalmem())}`, | ||
]); | ||
}, | ||
function getElixirVersion() { | ||
return utils.customGenericVersionFunction( | ||
() => /[Elixir]+\s([\d|.]+)/g.exec(utils.run('elixir --version'))[1] | ||
); | ||
} | ||
getSublimeTextInfo: () => { | ||
utils.log('trace', 'getSublimeTextInfo'); | ||
return Promise.all([ | ||
'Sublime Text', | ||
utils.run('subl --version').then(version => utils.findVersion(version, /\d+/)), | ||
utils.which('subl'), | ||
]); | ||
}, | ||
function getFreeMemory() { | ||
return utils.toReadableBytes(os.freemem()); | ||
} | ||
getHomeBrewInfo: () => { | ||
utils.log('trace', 'getHomeBrewInfo'); | ||
var homeBrewVersion; | ||
if (process.platform === 'darwin') { | ||
homeBrewVersion = Promise.all([ | ||
'Homebrew', | ||
utils.run('brew --version').then(utils.findVersion), | ||
utils.which('brew'), | ||
]); | ||
} else homeBrewVersion = Promise.resolve('N/A'); | ||
return homeBrewVersion; | ||
}, | ||
function getTotalMemory() { | ||
return utils.toReadableBytes(os.totalmem()); | ||
} | ||
getGoInfo: () => { | ||
utils.log('trace', 'getGoInfo'); | ||
return Promise.all(['Go', utils.run('go version').then(utils.findVersion), utils.which('go')]); | ||
}, | ||
function getSublimeTextVersion() { | ||
return utils.customGenericVersionFunction(() => getDarwinApplicationVersion('com.sublimetext.3')); | ||
} | ||
getRubyInfo: () => { | ||
utils.log('trace', 'getRubyInfo'); | ||
return Promise.all(['Ruby', utils.run('ruby -v').then(utils.findVersion), utils.which('ruby')]); | ||
}, | ||
function getHomeBrewVersion() { | ||
var homeBrewVersion; | ||
if (process.platform === 'darwin') { | ||
try { | ||
homeBrewVersion = utils | ||
.run('brew --version') | ||
.replace('Homebrew ', '') | ||
.split('\n', 1) | ||
.join(); | ||
} catch (error) { | ||
homeBrewVersion = 'Not Found'; | ||
} | ||
} else homeBrewVersion = 'N/A'; | ||
return homeBrewVersion; | ||
} | ||
getNodeInfo: () => { | ||
utils.log('trace', 'getNodeInfo'); | ||
return Promise.all([ | ||
'Node', | ||
utils.run('node -v').then(v => v.replace('v', '')), | ||
utils.which('node').then(utils.condensePath), | ||
]); | ||
}, | ||
function getGoVersion() { | ||
return utils.customGenericVersionFunction(() => | ||
utils | ||
.run('go version') | ||
.replace('go version go', '') | ||
.split(' ', 1) | ||
.join() | ||
); | ||
} | ||
getnpmInfo: () => { | ||
utils.log('trace', 'getnpmInfo'); | ||
return Promise.all(['npm', utils.run('npm -v'), utils.which('npm').then(utils.condensePath)]); | ||
}, | ||
function getRubyVersion() { | ||
return utils.customGenericVersionFunction(() => | ||
utils | ||
.run('ruby --version') | ||
.replace('ruby ', '') | ||
.split(' ', 1) | ||
.join() | ||
); | ||
} | ||
getShellInfo: () => { | ||
utils.log('trace', 'getShellInfo'); | ||
return Promise.all([ | ||
'Shell', | ||
utils.run(`${process.env.SHELL} --version`).then(utils.findVersion), | ||
utils.which(process.env.SHELL), | ||
]); | ||
}, | ||
function getNodeVersion() { | ||
return utils.customGenericVersionFunction(() => utils.run('node --version').replace('v', '')); | ||
} | ||
getOSInfo: () => { | ||
utils.log('trace', 'getOSInfo'); | ||
return (process.platform === 'darwin' | ||
? utils.run('sw_vers -productVersion ') | ||
: Promise.resolve() | ||
).then(version => { | ||
let info = osName(os.platform(), os.release()); | ||
if (version) info += ` ${version}`; | ||
return ['OS', info]; | ||
}); | ||
}, | ||
function getNpmVersion() { | ||
return utils.customGenericVersionFunction(() => utils.run('npm -v')); | ||
} | ||
getWatchmanInfo: () => { | ||
utils.log('trace', 'getWatchmanInfo'); | ||
return Promise.all([ | ||
'Watchman', | ||
utils.which('watchman').then(watchmanPath => utils.run(watchmanPath + ' -v')), | ||
utils.which('watchman'), | ||
]); | ||
}, | ||
function getShell(shellBinary) { | ||
shellBinary = shellBinary || process.env.SHELL; | ||
getVSCodeInfo: () => { | ||
utils.log('trace', 'getVSCodeInfo'); | ||
return Promise.all([ | ||
'VSCode', | ||
utils.run('code --version').then(utils.findVersion), | ||
utils.which('code'), | ||
]); | ||
}, | ||
const shellVersion = utils.customGenericVersionFunction( | ||
() => utils.run(`${shellBinary} --version`).match(utils.versionRegex)[0] | ||
); | ||
getVirtualBoxInfo: () => { | ||
utils.log('trace', 'getVirtualBoxInfo'); | ||
return Promise.all([ | ||
'VirtualBox', | ||
utils.run('vboxmanage --version').then(utils.findVersion), | ||
utils.which('vboxmanage'), | ||
]); | ||
}, | ||
return (shellBinary && shellVersion && `${shellBinary} - ${shellVersion}`) || `¯\\_(ツ)_/¯`; | ||
} | ||
getVMwareFusionInfo: () => { | ||
utils.log('trace', 'getVMwareFusionInfo'); | ||
return Promise.all([ | ||
'VMWare Fusion', | ||
utils.getDarwinApplicationVersion('com.vmware.fusion'), | ||
'N/A', | ||
]); | ||
}, | ||
function getOperatingSystemInfo() { | ||
var operatingSystemInfo; | ||
try { | ||
operatingSystemInfo = osName(os.platform(), os.release()); | ||
getPythonInfo: () => { | ||
utils.log('trace', 'getPythonInfo'); | ||
let pythonVersion; | ||
let pythonPath; | ||
try { | ||
pythonPath = utils.runSync('which python'); | ||
pythonVersion = childProcess | ||
.execFileSync(pythonPath, ['-c', 'import platform; print(platform.python_version());']) | ||
.toString() | ||
.replace(/(\r\n|\n|\r)/gm, ''); | ||
} catch (error) { | ||
pythonVersion = 'Not Found'; | ||
} | ||
return Promise.resolve(['Python', pythonVersion, pythonPath]); | ||
}, | ||
getXcodeInfo: () => { | ||
utils.log('trace', 'getXcodeInfo'); | ||
if (process.platform === 'darwin') { | ||
operatingSystemInfo = operatingSystemInfo + ' ' + utils.run('sw_vers -productVersion '); | ||
return Promise.all([ | ||
'Xcode', | ||
utils | ||
.which('xcodebuild') | ||
.then(xcodePath => utils.run(xcodePath + ' -version')) | ||
.then(version => `${utils.findVersion(version)} - ${version.split('Build version ')[1]}`), | ||
utils.which('xcodebuild'), | ||
]); | ||
} | ||
} catch (err) { | ||
operatingSystemInfo += ' Unknown Version'; | ||
} | ||
return operatingSystemInfo; | ||
} | ||
return Promise.resolve(['Xcode', 'N/A']); | ||
}, | ||
function getWatchmanVersion() { | ||
return utils.customGenericVersionFunction( | ||
() => which.sync('watchman') && utils.run(which.sync('watchman') + ' --version') | ||
); | ||
} | ||
getYarnInfo: () => { | ||
utils.log('trace', 'getYarnInfo'); | ||
return Promise.all([ | ||
'Yarn', | ||
utils.run('yarn -v'), | ||
utils.which('yarn').then(utils.condensePath), | ||
]); | ||
}, | ||
function getVSCodeVersion() { | ||
return utils.customGenericVersionFunction(() => | ||
utils | ||
.run('code --version') | ||
.split('\n', 1) | ||
.join('') | ||
); | ||
} | ||
getChromeInfo: () => { | ||
utils.log('trace', 'getChromeInfo'); | ||
let chromeVersion; | ||
if (process.platform === 'linux') { | ||
try { | ||
chromeVersion = utils.runSync('google-chrome --version').replace(/^.* ([^ ]*)/g, '$1'); | ||
} catch (err) { | ||
chromeVersion = 'Not Found'; | ||
} | ||
} else { | ||
chromeVersion = utils.getDarwinApplicationVersion(utils.browserBundleIdentifiers.Chrome); | ||
} | ||
return Promise.all(['Chrome', chromeVersion, 'N/A']); | ||
}, | ||
function getVirtualBoxVersion() { | ||
return utils.customGenericVersionFunction(() => utils.run('vboxmanage --version')); | ||
} | ||
getChromeCanaryInfo: () => { | ||
utils.log('trace', 'getChromeCanaryInfo'); | ||
const chromeCanaryVersion = utils.getDarwinApplicationVersion( | ||
utils.browserBundleIdentifiers['Chrome Canary'] | ||
); | ||
return Promise.all(['Chrome Canary', chromeCanaryVersion, 'N/A']); | ||
}, | ||
function getVMwareVersion() { | ||
return utils.customGenericVersionFunction(() => getDarwinApplicationVersion('com.vmware.fusion')); | ||
} | ||
getFirefoxDeveloperEditionInfo: () => { | ||
utils.log('trace', 'getFirefoxDeveloperEditionInfo'); | ||
const firefoxDeveloperEdition = utils.getDarwinApplicationVersion( | ||
utils.browserBundleIdentifiers['Firefox Developer Edition'] | ||
); | ||
return Promise.all(['Firefox Developer Edition', firefoxDeveloperEdition, 'N/A']); | ||
}, | ||
function getPythonVersion() { | ||
var pythonVersion; | ||
var pythonPath; | ||
try { | ||
pythonPath = utils.run('which python'); | ||
pythonVersion = childProcess | ||
.execFileSync(pythonPath, ['-c', 'import platform; print(platform.python_version());']) | ||
.toString() | ||
.replace(/(\r\n|\n|\r)/gm, ''); | ||
} catch (error) { | ||
pythonVersion = 'Not Found'; | ||
} | ||
return pythonVersion; | ||
} | ||
getSafariTechnologyPreviewInfo: () => { | ||
utils.log('trace', 'getSafariTechnologyPreviewInfo'); | ||
const safariTechnologyPreview = utils.getDarwinApplicationVersion( | ||
utils.browserBundleIdentifiers['Safari Technology Preview'] | ||
); | ||
return Promise.all(['Safari Technology Preview', safariTechnologyPreview, 'N/A']); | ||
}, | ||
function getXcodeVersion() { | ||
var xcodeVersion; | ||
if (process.platform === 'darwin') { | ||
var xcodePath = which.sync('xcodebuild'); | ||
try { | ||
xcodeVersion = | ||
xcodePath && | ||
utils | ||
.run(xcodePath + ' -version') | ||
.split('\n') | ||
.join(' '); | ||
} catch (err) { | ||
xcodeVersion = 'Not Found'; | ||
} | ||
} else { | ||
xcodeVersion = 'N/A'; | ||
} | ||
return xcodeVersion; | ||
} | ||
getSafariInfo: () => { | ||
utils.log('trace', 'getSafariInfo'); | ||
const safariVersion = utils.getDarwinApplicationVersion(utils.browserBundleIdentifiers.Safari); | ||
return Promise.all(['Safari', safariVersion, 'N/A']); | ||
}, | ||
function getYarnVersion() { | ||
return utils.customGenericVersionFunction(() => utils.run('yarn --version')); | ||
} | ||
function getChromeVersion() { | ||
var chromeVersion; | ||
if (process.platform === 'linux') { | ||
try { | ||
chromeVersion = utils.run('google-chrome --version').replace(/^.* ([^ ]*)/g, '$1'); | ||
} catch (err) { | ||
chromeVersion = 'Not Found'; | ||
getFirefoxInfo: () => { | ||
utils.log('trace', 'getFirefoxInfo'); | ||
var firefoxVersion; | ||
if (process.platform === 'linux') { | ||
try { | ||
firefoxVersion = utils.runSync('firefox --version').replace(/^.* ([^ ]*)/g, '$1'); | ||
} catch (err) { | ||
firefoxVersion = 'Not Found'; | ||
} | ||
} else { | ||
firefoxVersion = utils.getDarwinApplicationVersion(utils.browserBundleIdentifiers.Firefox); | ||
} | ||
} else { | ||
chromeVersion = getDarwinApplicationVersion(browserBundleIdentifiers.Chrome); | ||
} | ||
return chromeVersion; | ||
} | ||
return Promise.all(['Firefox', firefoxVersion, 'N/A']); | ||
}, | ||
function getFirefoxVersion() { | ||
var firefoxVersion; | ||
if (process.platform === 'linux') { | ||
try { | ||
firefoxVersion = utils.run('firefox --version').replace(/^.* ([^ ]*)/g, '$1'); | ||
} catch (err) { | ||
firefoxVersion = 'Not Found'; | ||
getFirefoxNightlyInfo: () => { | ||
utils.log('trace', 'getFirefoxNightlyInfo'); | ||
var firefoxNightlyVersion; | ||
if (process.platform === 'linux') { | ||
try { | ||
firefoxNightlyVersion = utils | ||
.runSync('firefox-trunk --version') | ||
.replace(/^.* ([^ ]*)/g, '$1'); | ||
} catch (err) { | ||
firefoxNightlyVersion = 'Not Found'; | ||
} | ||
} else { | ||
firefoxNightlyVersion = utils.getDarwinApplicationVersion( | ||
utils.browserBundleIdentifiers['Firefox Nightly'] | ||
); | ||
} | ||
} else { | ||
firefoxVersion = getDarwinApplicationVersion(browserBundleIdentifiers.Firefox); | ||
} | ||
return firefoxVersion; | ||
} | ||
function getFirefoxNightlyVersion() { | ||
var firefoxNightlyVersion; | ||
if (process.platform === 'linux') { | ||
try { | ||
firefoxNightlyVersion = utils.run('firefox-trunk --version').replace(/^.* ([^ ]*)/g, '$1'); | ||
} catch (err) { | ||
firefoxNightlyVersion = 'Not Found'; | ||
} | ||
} else { | ||
firefoxNightlyVersion = getDarwinApplicationVersion( | ||
browserBundleIdentifiers['Firefox Nightly'] | ||
); | ||
} | ||
return firefoxNightlyVersion; | ||
} | ||
module.exports = Object.assign(packages, { | ||
browserBundleIdentifiers: browserBundleIdentifiers, | ||
findDarwinApplication: findDarwinApplication, | ||
generatePlistBuddyCommand: generatePlistBuddyCommand, | ||
getAllAndroidSDKs: getAllAndroidSDKs, | ||
getAlliOSSDKs: getAlliOSSDKs, | ||
getAndroidStudioVersion: getAndroidStudioVersion, | ||
getAtomVersion: getAtomVersion, | ||
getBashVersion: getBashVersion, | ||
getCPUInfo: getCPUInfo, | ||
getDarwinApplicationVersion: getDarwinApplicationVersion, | ||
getDockerVersion: getDockerVersion, | ||
getElixirVersion: getElixirVersion, | ||
getFreeMemory: getFreeMemory, | ||
getGoVersion: getGoVersion, | ||
getHomeBrewVersion: getHomeBrewVersion, | ||
getNodeVersion: getNodeVersion, | ||
getNpmVersion: getNpmVersion, | ||
getOperatingSystemInfo: getOperatingSystemInfo, | ||
getPhpVersion: getPhpVersion, | ||
getParallelsVersion: getParallelsVersion, | ||
getPythonVersion: getPythonVersion, | ||
getRubyVersion: getRubyVersion, | ||
getShell: getShell, | ||
getSublimeTextVersion: getSublimeTextVersion, | ||
getTotalMemory: getTotalMemory, | ||
getVirtualBoxVersion: getVirtualBoxVersion, | ||
getVMwareVersion: getVMwareVersion, | ||
getVSCodeVersion: getVSCodeVersion, | ||
getWatchmanVersion: getWatchmanVersion, | ||
getXcodeVersion: getXcodeVersion, | ||
getYarnVersion: getYarnVersion, | ||
getChromeVersion: getChromeVersion, | ||
getFirefoxVersion: getFirefoxVersion, | ||
getFirefoxNightlyVersion: getFirefoxNightlyVersion, | ||
return Promise.all(['Firefox Nightly', firefoxNightlyVersion, 'N/A']); | ||
}, | ||
}); |
const glob = require('glob'); | ||
const path = require('path'); | ||
const utils = require('./utils'); | ||
function getAllPackageJsonPaths(packageGlob) { | ||
if (packageGlob) return glob.sync(`node_modules/${packageGlob}/package.json`); | ||
return glob.sync('node_modules/**/package.json'); | ||
} | ||
function getPackageDuplicates(dependency, allPackageJsonPaths) { | ||
const duplicates = allPackageJsonPaths | ||
// filter by which dependency we are looking for | ||
.filter(packagePath => { | ||
return packagePath.includes(`/${dependency.name}/package.json`); | ||
}) | ||
// map over each occurrence of that dependency in the tree | ||
.map(packageJsonPath => { | ||
var packageJson = utils.getPackageJsonByPath(packageJsonPath); | ||
if (packageJson) return packageJson.version; | ||
return false; | ||
}) | ||
.filter(Boolean) | ||
// remove duplicates | ||
.reduce((p, c) => { | ||
if (!p.includes(c)) p.push(c); | ||
return p; | ||
}, []); | ||
// if there is more than one version found, push the duplicates | ||
if (duplicates.length > 1) dependency.duplicates = duplicates; | ||
return dependency; | ||
} | ||
function getPackageVersion(packageName) { | ||
var name = packageName.trim(); | ||
var dependencyPackageJson = utils.getPackageJsonByName(name); | ||
var installed = dependencyPackageJson ? dependencyPackageJson.version : 'Not Found'; | ||
return installed; | ||
} | ||
function packageReducer(list, dependency) { | ||
const value = dependency.duplicates | ||
? { | ||
[dependency.name]: { | ||
wanted: dependency.wanted, | ||
installed: getPackageVersion(dependency.name), | ||
duplicates: dependency.duplicates, | ||
}, | ||
} | ||
: { | ||
[dependency.name]: { | ||
wanted: dependency.wanted, | ||
installed: getPackageVersion(dependency.name), | ||
}, | ||
}; | ||
return Object.assign(list, value); | ||
} | ||
function getPackageFullTree(allPackageJsonPaths) { | ||
return allPackageJsonPaths | ||
.map(filePath => { | ||
var packageJson = utils.getPackageJsonByPath(filePath); | ||
return { | ||
name: packageJson.name, | ||
installed: packageJson.version, | ||
}; | ||
}) | ||
.reduce((acc, val) => { | ||
return Object.assign(acc, { | ||
[val.name]: acc[val.name] ? acc[val.name].concat([val.installed]) : [val.installed], | ||
}); | ||
}, {}); | ||
} | ||
function mapTopLevelDependencies(dependency) { | ||
return { | ||
name: dependency[0], | ||
wanted: dependency[1], | ||
}; | ||
} | ||
function mapFullPackageTree(dependency) { | ||
return { | ||
name: dependency[0], | ||
installed: dependency[1], | ||
}; | ||
} | ||
function assignWantedVersion(dependency, topLevelDependencies) { | ||
const idx = Object.keys(topLevelDependencies).indexOf(dependency.name); | ||
if (idx > -1) { | ||
return Object.assign(dependency, { | ||
wanted: Object.values(topLevelDependencies)[idx], | ||
}); | ||
} | ||
return dependency; | ||
} | ||
function getPackageInfo(packages, options) { | ||
function getnpmPackages(packages, options) { | ||
utils.log('trace', 'getnpmPackages'); | ||
if (!options) options = {}; | ||
let packageGlob = null; | ||
let tld = null; | ||
@@ -119,98 +26,142 @@ if (typeof packages === 'string') { | ||
} | ||
// get the package.json files from the full tree for either option | ||
const allPackageJsonPaths = | ||
(packageGlob || options.duplicates || options.fullTree) && getAllPackageJsonPaths(); | ||
// get the globbed packages if the packageGlob is truthy | ||
const globbedPackageJsonPaths = packageGlob && getAllPackageJsonPaths(packageGlob); | ||
// load top level package.json for the dependencies | ||
const packageJson = utils.getPackageJsonByPath('package.json') || {}; | ||
const topLevelDependencies = Object.assign( | ||
{}, | ||
packageJson.devDependencies || {}, | ||
packageJson.dependencies || {} | ||
); | ||
const topLevelPackageNames = Object.keys(topLevelDependencies); | ||
// filter the globbed paths by whats actually in your root package.json | ||
const globbedTopLevelDependencies = (globbedPackageJsonPaths || []).filter(p => { | ||
return topLevelPackageNames.includes(p.match(/(?:node_modules)\/(.+)\/(?:.*)/)[1]); | ||
}); | ||
if (packageGlob) { | ||
// --npmPackages "eslint-*" (--duplicates) (--fullTree) | ||
// matches packages against given glob, optionally adding duplicates, or crawling the fullTree | ||
return utils.sortObject( | ||
Object.entries( | ||
options.fullTree | ||
? getPackageFullTree(globbedPackageJsonPaths) | ||
: getPackageFullTree(globbedTopLevelDependencies) | ||
return Promise.all([ | ||
'npmPackages', | ||
utils | ||
.getPackageJsonByPath('package.json') | ||
.then(packageJson => | ||
Object.assign({}, packageJson.devDependencies || {}, packageJson.dependencies || {}) | ||
) | ||
.map(options.fullTree ? mapFullPackageTree : mapTopLevelDependencies) | ||
.map(d => assignWantedVersion(d, topLevelDependencies)) | ||
.map(d => (options.duplicates ? getPackageDuplicates(d, allPackageJsonPaths) : d)) | ||
.reduce(packageReducer, {}) | ||
); | ||
} | ||
if (Array.isArray(packages)) { | ||
// --npmPackages minimist,which (--duplicates) | ||
// only specified top level packages (with duplicates) with wanted and installed | ||
return utils.sortObject( | ||
Object.entries(topLevelDependencies) | ||
.map(mapTopLevelDependencies) | ||
.filter(d => packages.includes(d.name)) | ||
.map(d => (options.duplicates ? getPackageDuplicates(d, allPackageJsonPaths) : d)) | ||
.reduce(packageReducer, {}) | ||
); | ||
} | ||
if (typeof packages === 'boolean') { | ||
// --npmPackages (--duplicates) (--fullTree) | ||
// print all packages (with duplicates) with wanted and installed, optionally crawling full tree | ||
return utils.sortObject( | ||
Object.entries( | ||
options.fullTree ? getPackageFullTree(allPackageJsonPaths) : topLevelDependencies | ||
// determine which paths to get | ||
.then(packageJsonDependencies => { | ||
tld = packageJsonDependencies; | ||
if (options.fullTree || options.duplicates || packageGlob) { | ||
return utils.getAllPackageJsonPaths(packageGlob); | ||
} | ||
return Promise.resolve( | ||
Object.keys(packageJsonDependencies || []).map(dep => | ||
path.join('node_modules', dep, 'package.json') | ||
) | ||
); | ||
}) | ||
// filter by glob or selection | ||
.then(packageJsonPaths => { | ||
if ((packageGlob || typeof packages === 'boolean') && !options.fullTree) { | ||
return Promise.resolve( | ||
(packageJsonPaths || []).filter(p => | ||
Object.keys(tld || []).includes(p.split('/').slice(-2)[0]) | ||
) | ||
); | ||
} | ||
if (Array.isArray(packages)) { | ||
return Promise.resolve( | ||
(packageJsonPaths || []).filter(p => packages.includes(p.split('/').slice(-2)[0])) | ||
); | ||
} | ||
return Promise.resolve(packageJsonPaths); | ||
}) | ||
.then(paths => | ||
Promise.all([ | ||
paths, | ||
Promise.all(paths.map(filePath => utils.getPackageJsonByPath(filePath))), | ||
]) | ||
) | ||
.map(options.fullTree ? mapFullPackageTree : mapTopLevelDependencies) | ||
.map(d => assignWantedVersion(d, topLevelDependencies)) | ||
.map(d => (options.duplicates ? getPackageDuplicates(d, allPackageJsonPaths) : d)) | ||
.reduce(packageReducer, {}) | ||
); | ||
} | ||
// conglomerate the data | ||
.then(result => { | ||
const paths = result[0]; | ||
const files = result[1]; | ||
return {}; | ||
return files.reduce((acc, d, idx) => { | ||
// if the file is a test stub, or doesn't have a name, ignore it. | ||
if (!d || !d.name) return acc; | ||
// create object if its not already created | ||
if (!acc[d.name]) acc[d.name] = {}; | ||
// set duplicates if flag set, if version not already there, && !== installed | ||
if (options.duplicates) { | ||
if (acc[d.name].installed && acc[d.name].installed !== d.version) { | ||
utils.uniq( | ||
(acc[d.name].duplicates = (acc[d.name].duplicates || []).concat(d.version)) | ||
); | ||
} | ||
} | ||
// set the installed version, if its installed top level | ||
if ((paths[idx].match(/node_modules/g) || []).length === 1) | ||
acc[d.name].installed = d.version; | ||
// if it is a top level dependency, get the wanted version | ||
if (tld[d.name]) acc[d.name].wanted = tld[d.name]; | ||
return acc; | ||
}, {}); | ||
}) | ||
.then(versions => utils.sortObject(versions)), | ||
]); | ||
} | ||
function getNpmGlobalPackages(packages) { | ||
function getnpmGlobalPackages(packages) { | ||
utils.log('trace', 'getnpmGlobalPackages', packages); | ||
let packageGlob = null; | ||
if (typeof packages === 'string') { | ||
packages = packages.split(','); | ||
if ( | ||
// detect characters that are not allowed in npm names, but are in globs | ||
// wont work if the exactly once glob @ is used by itself, because of npm namespacing | ||
packages.includes('*') || | ||
packages.includes('?') || | ||
packages.includes('+') || | ||
packages.includes('!') | ||
) { | ||
packageGlob = packages; | ||
} else { | ||
packages = packages.split(','); | ||
} | ||
} else if (!Array.isArray(packages)) { | ||
packages = true; | ||
} | ||
var npmGlobalPackages; | ||
try { | ||
npmGlobalPackages = utils.run('npm list -g --depth=0 --json'); | ||
npmGlobalPackages = JSON.parse(npmGlobalPackages); | ||
} catch (error) { | ||
if (error.stdout) npmGlobalPackages = JSON.parse(error.stdout.toString()); | ||
if (!npmGlobalPackages) return 'Not Found'; | ||
} | ||
npmGlobalPackages = Object.entries(npmGlobalPackages.dependencies).reduce((acc, dep) => { | ||
const name = dep[0]; | ||
const info = dep[1]; | ||
if (!Array.isArray(packages) || packages.some(p => p.toLowerCase() === name.toLowerCase())) | ||
return Object.assign(acc, { | ||
[name]: info.version, | ||
}); | ||
return acc; | ||
}, {}); | ||
return npmGlobalPackages; | ||
return Promise.all([ | ||
'npmGlobalPackages', | ||
utils | ||
// get the location of the npm global node_modules | ||
.run('npm get prefix --global') | ||
// glob all of the package.json files in that directory | ||
.then( | ||
prefix => | ||
new Promise((resolve, reject) => | ||
glob( | ||
// sub packageGlob in to only get globbed packages if not null | ||
path.join(prefix, 'lib', 'node_modules', packageGlob || '*', 'package.json'), | ||
(err, files) => { | ||
if (!err) resolve(files); | ||
reject(err); | ||
} | ||
) | ||
) | ||
) | ||
.then(globResults => | ||
Promise.all( | ||
globResults | ||
// filter out package paths not in list provided in options | ||
.filter( | ||
globbedPath => | ||
typeof packages === 'boolean' || | ||
packageGlob !== null || | ||
packages.includes(globbedPath.split('/').slice(-2)[0]) | ||
) | ||
// get all the package.json by path, return promises | ||
.map(packageJson => utils.getPackageJsonByFullPath(packageJson)) | ||
) | ||
) | ||
// accumulate all the package info in one object. | ||
.then(allPackages => | ||
allPackages.reduce( | ||
(acc, json) => (json ? Object.assign(acc, { [json.name]: json.version }) : acc), | ||
{} | ||
) | ||
), | ||
]); | ||
} | ||
module.exports = { | ||
getPackageInfo: getPackageInfo, | ||
getNpmGlobalPackages: getNpmGlobalPackages, | ||
getnpmPackages: getnpmPackages, | ||
getnpmGlobalPackages: getnpmGlobalPackages, | ||
}; |
module.exports = { | ||
defaults: { | ||
System: ['OS', 'CPU', 'Memory', 'Shell', 'derp'], | ||
Binaries: ['Node', 'Yarn', 'npm', 'Watchman'], | ||
Virtualization: ['Docker', 'Parallels', 'VirtualBox', 'VMware Fusion'], | ||
SDKs: ['iOS SDK', 'Android SDK'], | ||
IDEs: ['Android Studio', 'Atom', 'VSCode', 'Sublime Text', 'Xcode'], | ||
Languages: ['Bash', 'Go', 'Elixir', 'PHP', 'Python', 'Ruby'], | ||
Browsers: [ | ||
'Chrome', | ||
'Chrome Canary', | ||
'Firefox', | ||
'Firefox Developer Edition', | ||
'Firefox Nightly', | ||
'Safari', | ||
'Safari Technology Preview', | ||
], | ||
npmPackages: null, | ||
npmGlobalPackages: null, | ||
}, | ||
jest: { | ||
@@ -3,0 +22,0 @@ System: ['OS', 'CPU'], |
247
src/utils.js
@@ -1,102 +0,181 @@ | ||
var path = require('path'); | ||
var fs = require('fs'); | ||
var childProcess = require('child_process'); | ||
const path = require('path'); | ||
const fs = require('fs'); | ||
const childProcess = require('child_process'); | ||
const libWhich = require('which'); | ||
const glob = require('glob'); | ||
function run(cmd) { | ||
return ( | ||
childProcess | ||
.execSync(cmd, { | ||
const run = cmd => { | ||
return new Promise(resolve => { | ||
childProcess.exec( | ||
cmd, | ||
{ | ||
stdio: [0, 'pipe', 'ignore'], | ||
}) | ||
.toString() || '' | ||
).trim(); | ||
} | ||
}, | ||
(err, out) => { | ||
resolve((err ? '' : out.toString() || '').trim()); | ||
} | ||
); | ||
}); | ||
}; | ||
function customGenericVersionFunction(fn, msg) { | ||
if (msg === undefined) msg = 'Not Found'; | ||
var version; | ||
try { | ||
version = fn(); | ||
} catch (error) { | ||
version = msg; | ||
} | ||
return version; | ||
} | ||
const log = function log(level) { | ||
const args = Object.values(Array.prototype.slice.call(arguments).slice(1)).join(', '); | ||
if ((process.env.ENVINFO_DEBUG || '').toLowerCase() === level) console.log(level, args); | ||
}; | ||
function uniq(arr) { | ||
return Array.from(new Set(arr)); // eslint-disable-line no-undef | ||
} | ||
const fileExists = filePath => { | ||
return new Promise(resolve => { | ||
fs.stat(filePath, err => (err ? resolve(null) : resolve(filePath))); | ||
}); | ||
}; | ||
function toReadableBytes(bytes) { | ||
const i = Math.floor(Math.log(bytes) / Math.log(1024)); | ||
return ( | ||
(!bytes && '0 Bytes') || | ||
(bytes / Math.pow(1024, i)).toFixed(2) + ' ' + ['B', 'KB', 'MB', 'GB', 'TB', 'PB'][i] | ||
const readFile = filePath => { | ||
return new Promise(fileResolved => { | ||
if (!filePath) fileResolved(null); | ||
fs.readFile(filePath, 'utf8', (err, file) => (file ? fileResolved(file) : fileResolved(null))); | ||
}); | ||
}; | ||
const requireJson = filePath => { | ||
return fileExists(filePath) | ||
.then(readFile) | ||
.then(file => (file ? JSON.parse(file) : null)); | ||
}; | ||
const versionRegex = /\d+\.[\d+|.]+/; | ||
const findDarwinApplication = id => { | ||
return run('mdfind "kMDItemCFBundleIdentifier=="' + id + '""').then(v => | ||
v.replace(/(\s)/g, '\\ ') | ||
); | ||
} | ||
}; | ||
function omit(obj, props) { | ||
return Object.keys(obj) | ||
.filter(key => props.indexOf(key) < 0) | ||
.reduce((acc, key) => Object.assign(acc, { [key]: obj[key] }), {}); | ||
} | ||
const generatePlistBuddyCommand = (appPath, options) => { | ||
var optionsArray = (options || ['CFBundleShortVersionString']).map(function optionsMap(option) { | ||
return '-c Print:' + option; | ||
}); | ||
return ['/usr/libexec/PlistBuddy'] | ||
.concat(optionsArray) | ||
.concat([appPath]) | ||
.join(' '); | ||
}; | ||
function pick(obj, props) { | ||
return Object.keys(obj) | ||
.filter(key => props.indexOf(key) >= 0) | ||
.reduce((acc, key) => Object.assign(acc, { [key]: obj[key] }), {}); | ||
} | ||
module.exports = { | ||
run: run, | ||
log: log, | ||
fileExists: fileExists, | ||
readFile: readFile, | ||
requireJson: requireJson, | ||
versionRegex: versionRegex, | ||
findDarwinApplication: findDarwinApplication, | ||
generatePlistBuddyCommand: generatePlistBuddyCommand, | ||
const isObject = val => typeof val === 'object' && !Array.isArray(val); | ||
const pipe = fns => x => fns.reduce((v, f) => f(v), x); | ||
isObject: val => typeof val === 'object' && !Array.isArray(val), | ||
noop: d => d, | ||
pipe: fns => x => fns.reduce((v, f) => f(v), x), | ||
function requireJson(filePath) { | ||
var packageJson; | ||
if (fs.existsSync(filePath)) { | ||
try { | ||
packageJson = JSON.parse(fs.readFileSync(filePath, 'utf8')); | ||
} catch (e) { | ||
return null; | ||
browserBundleIdentifiers: { | ||
Chrome: 'com.google.Chrome', | ||
'Chrome Canary': 'com.google.Chrome.canary', | ||
Firefox: 'org.mozilla.firefox', | ||
'Firefox Developer Edition': 'org.mozilla.firefoxdeveloperedition', | ||
'Firefox Nightly': 'org.mozilla.nightly', | ||
Safari: 'com.apple.Safari', | ||
'Safari Technology Preview': 'com.apple.SafariTechnologyPreview', | ||
}, | ||
runSync: cmd => { | ||
return ( | ||
childProcess | ||
.execSync(cmd, { | ||
stdio: [0, 'pipe', 'ignore'], | ||
}) | ||
.toString() || '' | ||
).trim(); | ||
}, | ||
which: binary => { | ||
return new Promise(resolve => libWhich(binary, (err, binaryPath) => resolve(binaryPath))); | ||
}, | ||
getDarwinApplicationVersion: bundleIdentifier => { | ||
var version; | ||
if (process.platform !== 'darwin') { | ||
version = 'N/A'; | ||
} else { | ||
version = findDarwinApplication(bundleIdentifier).then(appPath => | ||
run( | ||
generatePlistBuddyCommand(path.join(appPath, 'Contents', 'Info.plist'), [ | ||
'CFBundleShortVersionString', | ||
]) | ||
) | ||
); | ||
} | ||
return packageJson; | ||
} | ||
return null; | ||
} | ||
return Promise.resolve(version); | ||
}, | ||
function getPackageJsonByName(dep) { | ||
return this.requireJson(path.join(process.cwd(), 'node_modules', dep, 'package.json')); | ||
} | ||
uniq: arr => { | ||
return Array.from(new Set(arr)); // eslint-disable-line no-undef | ||
}, | ||
function getPackageJsonByPath(filePath) { | ||
return this.requireJson(path.join(process.cwd(), filePath)); | ||
} | ||
toReadableBytes: bytes => { | ||
const i = Math.floor(Math.log(bytes) / Math.log(1024)); | ||
return ( | ||
(!bytes && '0 Bytes') || | ||
(bytes / Math.pow(1024, i)).toFixed(2) + ' ' + ['B', 'KB', 'MB', 'GB', 'TB', 'PB'][i] | ||
); | ||
}, | ||
const noop = d => d; | ||
omit: (obj, props) => { | ||
return Object.keys(obj) | ||
.filter(key => props.indexOf(key) < 0) | ||
.reduce((acc, key) => Object.assign(acc, { [key]: obj[key] }), {}); | ||
}, | ||
function sortObject(obj) { | ||
return Object.keys(obj) | ||
.sort() | ||
.reduce((acc, val) => { | ||
acc[val] = obj[val]; | ||
return acc; | ||
}, {}); | ||
} | ||
pick: (obj, props) => { | ||
return Object.keys(obj) | ||
.filter(key => props.indexOf(key) >= 0) | ||
.reduce((acc, key) => Object.assign(acc, { [key]: obj[key] }), {}); | ||
}, | ||
const versionRegex = /\d+\.[\d+|\.]+/; | ||
getPackageJsonByName: name => { | ||
return requireJson(path.join(process.cwd(), 'node_modules', name, 'package.json')); | ||
}, | ||
module.exports = { | ||
customGenericVersionFunction: customGenericVersionFunction, | ||
getPackageJsonByName: getPackageJsonByName, | ||
getPackageJsonByPath: getPackageJsonByPath, | ||
isObject: isObject, | ||
noop: noop, | ||
omit: omit, | ||
pick: pick, | ||
pipe: pipe, | ||
requireJson: requireJson, | ||
run: run, | ||
sortObject: sortObject, | ||
toReadableBytes: toReadableBytes, | ||
uniq: uniq, | ||
versionRegex: versionRegex, | ||
getPackageJsonByPath: filePath => { | ||
return requireJson(path.join(process.cwd(), filePath)); | ||
}, | ||
getPackageJsonByFullPath: fullPath => { | ||
log('trace', 'getPackageJsonByFullPath', fullPath); | ||
return requireJson(fullPath); | ||
}, | ||
getAllPackageJsonPaths: packageGlob => { | ||
log('trace', 'getAllPackageJsonPaths', packageGlob); | ||
return new Promise(resolve => { | ||
const cb = (err, res) => resolve(res || []); | ||
if (packageGlob) return glob(path.join('node_modules', packageGlob, 'package.json'), cb); | ||
return glob(path.join('node_modules', '**', 'package.json'), cb); | ||
}); | ||
}, | ||
sortObject: obj => { | ||
return Object.keys(obj) | ||
.sort() | ||
.reduce((acc, val) => { | ||
acc[val] = obj[val]; | ||
return acc; | ||
}, {}); | ||
}, | ||
findVersion: (versionString, regex) => { | ||
const matcher = regex || versionRegex; | ||
const matched = versionString.match(matcher); | ||
return matched ? matched[0] : versionString; | ||
}, | ||
condensePath: pathString => { | ||
return pathString.replace(process.env.HOME, '~'); | ||
}, | ||
}; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
531571
1182
19
217
73
7