1 | /**
|
---|
2 | * @fileoverview Utility to get information about the execution environment.
|
---|
3 | * @author Kai Cataldo
|
---|
4 | */
|
---|
5 |
|
---|
6 | "use strict";
|
---|
7 |
|
---|
8 | //------------------------------------------------------------------------------
|
---|
9 | // Requirements
|
---|
10 | //------------------------------------------------------------------------------
|
---|
11 |
|
---|
12 | const path = require("path");
|
---|
13 | const spawn = require("cross-spawn");
|
---|
14 | const os = require("os");
|
---|
15 | const log = require("../shared/logging");
|
---|
16 | const packageJson = require("../../package.json");
|
---|
17 |
|
---|
18 | //------------------------------------------------------------------------------
|
---|
19 | // Helpers
|
---|
20 | //------------------------------------------------------------------------------
|
---|
21 |
|
---|
22 | /**
|
---|
23 | * Generates and returns execution environment information.
|
---|
24 | * @returns {string} A string that contains execution environment information.
|
---|
25 | */
|
---|
26 | function environment() {
|
---|
27 | const cache = new Map();
|
---|
28 |
|
---|
29 | /**
|
---|
30 | * Checks if a path is a child of a directory.
|
---|
31 | * @param {string} parentPath The parent path to check.
|
---|
32 | * @param {string} childPath The path to check.
|
---|
33 | * @returns {boolean} Whether or not the given path is a child of a directory.
|
---|
34 | */
|
---|
35 | function isChildOfDirectory(parentPath, childPath) {
|
---|
36 | return !path.relative(parentPath, childPath).startsWith("..");
|
---|
37 | }
|
---|
38 |
|
---|
39 | /**
|
---|
40 | * Synchronously executes a shell command and formats the result.
|
---|
41 | * @param {string} cmd The command to execute.
|
---|
42 | * @param {Array} args The arguments to be executed with the command.
|
---|
43 | * @throws {Error} As may be collected by `cross-spawn.sync`.
|
---|
44 | * @returns {string} The version returned by the command.
|
---|
45 | */
|
---|
46 | function execCommand(cmd, args) {
|
---|
47 | const key = [cmd, ...args].join(" ");
|
---|
48 |
|
---|
49 | if (cache.has(key)) {
|
---|
50 | return cache.get(key);
|
---|
51 | }
|
---|
52 |
|
---|
53 | const process = spawn.sync(cmd, args, { encoding: "utf8" });
|
---|
54 |
|
---|
55 | if (process.error) {
|
---|
56 | throw process.error;
|
---|
57 | }
|
---|
58 |
|
---|
59 | const result = process.stdout.trim();
|
---|
60 |
|
---|
61 | cache.set(key, result);
|
---|
62 | return result;
|
---|
63 | }
|
---|
64 |
|
---|
65 | /**
|
---|
66 | * Normalizes a version number.
|
---|
67 | * @param {string} versionStr The string to normalize.
|
---|
68 | * @returns {string} The normalized version number.
|
---|
69 | */
|
---|
70 | function normalizeVersionStr(versionStr) {
|
---|
71 | return versionStr.startsWith("v") ? versionStr : `v${versionStr}`;
|
---|
72 | }
|
---|
73 |
|
---|
74 | /**
|
---|
75 | * Gets bin version.
|
---|
76 | * @param {string} bin The bin to check.
|
---|
77 | * @throws {Error} As may be collected by `cross-spawn.sync`.
|
---|
78 | * @returns {string} The normalized version returned by the command.
|
---|
79 | */
|
---|
80 | function getBinVersion(bin) {
|
---|
81 | const binArgs = ["--version"];
|
---|
82 |
|
---|
83 | try {
|
---|
84 | return normalizeVersionStr(execCommand(bin, binArgs));
|
---|
85 | } catch (e) {
|
---|
86 | log.error(`Error finding ${bin} version running the command \`${bin} ${binArgs.join(" ")}\``);
|
---|
87 | throw e;
|
---|
88 | }
|
---|
89 | }
|
---|
90 |
|
---|
91 | /**
|
---|
92 | * Gets installed npm package version.
|
---|
93 | * @param {string} pkg The package to check.
|
---|
94 | * @param {boolean} global Whether to check globally or not.
|
---|
95 | * @throws {Error} As may be collected by `cross-spawn.sync`.
|
---|
96 | * @returns {string} The normalized version returned by the command.
|
---|
97 | */
|
---|
98 | function getNpmPackageVersion(pkg, { global = false } = {}) {
|
---|
99 | const npmBinArgs = ["bin", "-g"];
|
---|
100 | const npmLsArgs = ["ls", "--depth=0", "--json", pkg];
|
---|
101 |
|
---|
102 | if (global) {
|
---|
103 | npmLsArgs.push("-g");
|
---|
104 | }
|
---|
105 |
|
---|
106 | try {
|
---|
107 | const parsedStdout = JSON.parse(execCommand("npm", npmLsArgs));
|
---|
108 |
|
---|
109 | /*
|
---|
110 | * Checking globally returns an empty JSON object, while local checks
|
---|
111 | * include the name and version of the local project.
|
---|
112 | */
|
---|
113 | if (Object.keys(parsedStdout).length === 0 || !(parsedStdout.dependencies && parsedStdout.dependencies.eslint)) {
|
---|
114 | return "Not found";
|
---|
115 | }
|
---|
116 |
|
---|
117 | const [, processBinPath] = process.argv;
|
---|
118 | let npmBinPath;
|
---|
119 |
|
---|
120 | try {
|
---|
121 | npmBinPath = execCommand("npm", npmBinArgs);
|
---|
122 | } catch (e) {
|
---|
123 | log.error(`Error finding npm binary path when running command \`npm ${npmBinArgs.join(" ")}\``);
|
---|
124 | throw e;
|
---|
125 | }
|
---|
126 |
|
---|
127 | const isGlobal = isChildOfDirectory(npmBinPath, processBinPath);
|
---|
128 | let pkgVersion = parsedStdout.dependencies.eslint.version;
|
---|
129 |
|
---|
130 | if ((global && isGlobal) || (!global && !isGlobal)) {
|
---|
131 | pkgVersion += " (Currently used)";
|
---|
132 | }
|
---|
133 |
|
---|
134 | return normalizeVersionStr(pkgVersion);
|
---|
135 | } catch (e) {
|
---|
136 | log.error(`Error finding ${pkg} version running the command \`npm ${npmLsArgs.join(" ")}\``);
|
---|
137 | throw e;
|
---|
138 | }
|
---|
139 | }
|
---|
140 |
|
---|
141 | return [
|
---|
142 | "Environment Info:",
|
---|
143 | "",
|
---|
144 | `Node version: ${getBinVersion("node")}`,
|
---|
145 | `npm version: ${getBinVersion("npm")}`,
|
---|
146 | `Local ESLint version: ${getNpmPackageVersion("eslint", { global: false })}`,
|
---|
147 | `Global ESLint version: ${getNpmPackageVersion("eslint", { global: true })}`,
|
---|
148 | `Operating System: ${os.platform()} ${os.release()}`
|
---|
149 | ].join("\n");
|
---|
150 | }
|
---|
151 |
|
---|
152 | /**
|
---|
153 | * Returns version of currently executing ESLint.
|
---|
154 | * @returns {string} The version from the currently executing ESLint's package.json.
|
---|
155 | */
|
---|
156 | function version() {
|
---|
157 | return `v${packageJson.version}`;
|
---|
158 | }
|
---|
159 |
|
---|
160 | //------------------------------------------------------------------------------
|
---|
161 | // Public Interface
|
---|
162 | //------------------------------------------------------------------------------
|
---|
163 |
|
---|
164 | module.exports = {
|
---|
165 | environment,
|
---|
166 | version
|
---|
167 | };
|
---|