feat: migrate "cordova info" lib logic to cli (#510)
diff --git a/package.json b/package.json
index 4998e44..3fd10d7 100644
--- a/package.json
+++ b/package.json
@@ -34,10 +34,13 @@
"cordova-create": "^3.0.0-nightly.2019.11.19.1d0d67e0",
"cordova-lib": "^9.0.0",
"editor": "^1.0.0",
+ "execa": "^4.0.2",
+ "fs-extra": "^9.0.1",
"insight": "^0.10.1",
"loud-rejection": "^2.0.0",
"nopt": "^4.0.1",
"semver": "^6.2.0",
+ "systeminformation": "^4.26.9",
"update-notifier": "^2.5.0"
},
"devDependencies": {
diff --git a/src/cli.js b/src/cli.js
index 870b1c4..6f674af 100644
--- a/src/cli.js
+++ b/src/cli.js
@@ -20,6 +20,7 @@
var pkg = require('../package.json');
var telemetry = require('./telemetry');
var help = require('./help');
+const info = require('./info');
var cordova_lib = require('cordova-lib');
var CordovaError = cordova_lib.CordovaError;
var cordova = cordova_lib.cordova;
@@ -340,6 +341,8 @@
return printHelp(remain);
}
+ if (cmd === 'info') return info();
+
// Don't need to do anything with cordova-lib since config was handled above
if (cmd === 'config') return true;
diff --git a/src/info.js b/src/info.js
new file mode 100644
index 0000000..5ad11be
--- /dev/null
+++ b/src/info.js
@@ -0,0 +1,224 @@
+/**
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+const path = require('path');
+const fs = require('fs-extra');
+const execa = require('execa');
+const { osInfo } = require('systeminformation');
+const { cordova, cordova_platforms: { getPlatformApi } } = require('cordova-lib');
+
+const cdvLibUtil = require('cordova-lib/src/cordova/util');
+const cdvPluginUtil = require('cordova-lib/src/cordova/plugin/util');
+
+// Cache
+let _installedPlatformsList = null;
+
+/*
+ * Sections
+ */
+
+async function getCordovaDependenciesInfo () {
+ // get self "Cordova CLI"
+ const cliPkg = require('../package');
+ const cliDependencies = await _getLibDependenciesInfo(cliPkg.dependencies);
+
+ const libPkg = require('cordova-lib/package');
+ const cliLibDep = cliDependencies.find(({ key }) => key === 'lib');
+ cliLibDep.children = await _getLibDependenciesInfo(libPkg.dependencies);
+
+ return {
+ key: 'Cordova Packages',
+ children: [{
+ key: 'cli',
+ value: cliPkg.version,
+ children: cliDependencies
+ }]
+ };
+}
+
+async function getInstalledPlatforms (projectRoot) {
+ return _getInstalledPlatforms(projectRoot).then(platforms => {
+ const key = 'Project Installed Platforms';
+ const children = Object.entries(platforms)
+ .map(([key, value]) => ({ key, value }));
+
+ return { key, children };
+ });
+}
+
+async function getInstalledPlugins (projectRoot) {
+ const key = 'Project Installed Plugins';
+ const children = cdvPluginUtil.getInstalledPlugins(projectRoot)
+ .map(plugin => ({ key: plugin.id, value: plugin.version }));
+
+ return { key, children };
+}
+
+async function getEnvironmentInfo () {
+ const [npmVersion, osInfoResult] = await Promise.all([_getNpmVersion(), osInfo()]);
+ const { platform, distro, release, codename, kernel, arch, build } = osInfoResult;
+
+ const optionalBuildSuffix = build ? ` (${build})` : '';
+
+ const osFormat = [
+ platform === 'darwin' ? codename : distro,
+ release + optionalBuildSuffix,
+ `(${platform} ${kernel})`,
+ `${arch}`
+ ];
+
+ return {
+ key: 'Environment',
+ children: [
+ { key: 'OS', value: osFormat.join(' ') },
+ { key: 'Node', value: process.version },
+ { key: 'npm', value: npmVersion }
+ ]
+ };
+}
+
+async function getPlatformEnvironmentData (projectRoot) {
+ const installedPlatforms = await _getInstalledPlatforms(projectRoot);
+
+ return Object.keys(installedPlatforms)
+ .map(platform => {
+ const platformApi = getPlatformApi(platform);
+
+ const getPlatformInfo = platformApi && platformApi.getEnvironmentInfo
+ ? () => platformApi.getEnvironmentInfo()
+ : _legacyPlatformInfo[platform];
+
+ return { platform, getPlatformInfo };
+ })
+ .filter(o => o.getPlatformInfo)
+ .map(async ({ platform, getPlatformInfo }) => ({
+ key: `${platform} Environment`,
+ children: await getPlatformInfo()
+ }));
+}
+
+async function getProjectSettingsFiles (projectRoot) {
+ const cfgXml = _fetchFileContents(cdvLibUtil.projectConfig(projectRoot));
+
+ // Create package.json snippet
+ const pkgJson = require(path.join(projectRoot, 'package'));
+ const pkgSnippet = [
+ '--- Start of Cordova JSON Snippet ---',
+ JSON.stringify(pkgJson.cordova, null, 2),
+ '--- End of Cordova JSON Snippet ---'
+ ].join('\n');
+
+ return {
+ key: 'Project Setting Files',
+ children: [
+ { key: 'config.xml', value: `${cfgXml}` },
+ { key: 'package.json', value: pkgSnippet }
+ ]
+ };
+}
+
+/*
+ * Section Data Helpers
+ */
+
+async function _getLibDependenciesInfo (dependencies) {
+ const cordovaPrefix = 'cordova-';
+ const versionFor = name => require(`${name}/package`).version;
+
+ return Object.keys(dependencies)
+ .filter(name => name.startsWith(cordovaPrefix))
+ .map(name => ({ key: name.slice(cordovaPrefix.length), value: versionFor(name) }));
+}
+
+async function _getInstalledPlatforms (projectRoot) {
+ if (!_installedPlatformsList) {
+ _installedPlatformsList = await cdvLibUtil.getInstalledPlatformsWithVersions(projectRoot);
+ }
+ return _installedPlatformsList;
+}
+
+async function _getNpmVersion () {
+ return (await execa('npm', ['-v'])).stdout;
+}
+
+function _fetchFileContents (filePath) {
+ if (!fs.existsSync(filePath)) return 'File Not Found';
+
+ return fs.readFileSync(filePath, 'utf-8');
+}
+
+/**
+ * @deprecated will be removed when platforms implement the calls.
+ */
+const _legacyPlatformInfo = {
+ ios: async () => [{
+ key: 'xcodebuild',
+ value: await _failSafeSpawn('xcodebuild', ['-version'])
+ }],
+ android: async () => [{
+ key: 'android',
+ value: await _failSafeSpawn('android', ['list', 'target'])
+ }]
+};
+
+const _failSafeSpawn = (command, args) => execa(command, args).then(
+ ({ stdout }) => stdout,
+ err => `ERROR: ${err.message}`
+);
+
+function _formatNodeList (list, level = 0) {
+ const content = [];
+
+ for (const item of list) {
+ const indent = String.prototype.padStart((4 * level), ' ');
+ let itemString = `${indent}${item.key}:`;
+
+ if ('value' in item) {
+ // Pad multi-line values with a new line on either end
+ itemString += (/[\r\n]/.test(item.value))
+ ? `\n${item.value.trim()}\n`
+ : ` ${item.value}`;
+ } else {
+ // Start of section
+ itemString = `\n${itemString}\n`;
+ }
+
+ content.push(itemString);
+
+ if (item.children) {
+ content.push(..._formatNodeList(item.children, level + 1));
+ }
+ }
+
+ return content;
+}
+
+module.exports = async function () {
+ const projectRoot = cdvLibUtil.cdProjectRoot();
+
+ const results = await Promise.all([
+ getCordovaDependenciesInfo(),
+ getInstalledPlatforms(projectRoot),
+ getInstalledPlugins(projectRoot),
+ getEnvironmentInfo(),
+ ...(await getPlatformEnvironmentData(projectRoot)),
+ getProjectSettingsFiles(projectRoot)
+ ]);
+
+ const content = _formatNodeList(results);
+ cordova.emit('results', content.join('\n'));
+};