summaryrefslogtreecommitdiff
path: root/tools/node_modules/eslint/lib/shared/runtime-info.js
blob: 169bbc58f13c165a3c38e3dd3412c0a6df1d0a98 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/**
 * @fileoverview Utility to get information about the execution environment.
 * @author Kai Cataldo
 */

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const path = require("path");
const spawn = require("cross-spawn");
const { isEmpty } = require("lodash");
const log = require("../shared/logging");
const packageJson = require("../../package.json");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * Generates and returns execution environment information.
 * @returns {string} A string that contains execution environment information.
 */
function environment() {
    const cache = new Map();

    /**
     * Checks if a path is a child of a directory.
     * @param {string} parentPath - The parent path to check.
     * @param {string} childPath - The path to check.
     * @returns {boolean} Whether or not the given path is a child of a directory.
     */
    function isChildOfDirectory(parentPath, childPath) {
        return !path.relative(parentPath, childPath).startsWith("..");
    }

    /**
     * Synchronously executes a shell command and formats the result.
     * @param {string} cmd - The command to execute.
     * @param {Array} args - The arguments to be executed with the command.
     * @returns {string} The version returned by the command.
     */
    function execCommand(cmd, args) {
        const key = [cmd, ...args].join(" ");

        if (cache.has(key)) {
            return cache.get(key);
        }

        const process = spawn.sync(cmd, args, { encoding: "utf8" });

        if (process.error) {
            throw process.error;
        }

        const result = process.stdout.trim();

        cache.set(key, result);
        return result;
    }

    /**
     * Normalizes a version number.
     * @param {string} versionStr - The string to normalize.
     * @returns {string} The normalized version number.
     */
    function normalizeVersionStr(versionStr) {
        return versionStr.startsWith("v") ? versionStr : `v${versionStr}`;
    }

    /**
     * Gets bin version.
     * @param {string} bin - The bin to check.
     * @returns {string} The normalized version returned by the command.
     */
    function getBinVersion(bin) {
        const binArgs = ["--version"];

        try {
            return normalizeVersionStr(execCommand(bin, binArgs));
        } catch (e) {
            log.error(`Error finding ${bin} version running the command \`${bin} ${binArgs.join(" ")}\``);
            throw e;
        }
    }

    /**
     * Gets installed npm package version.
     * @param {string} pkg - The package to check.
     * @param {boolean} global - Whether to check globally or not.
     * @returns {string} The normalized version returned by the command.
     */
    function getNpmPackageVersion(pkg, { global = false } = {}) {
        const npmBinArgs = ["bin", "-g"];
        const npmLsArgs = ["ls", "--depth=0", "--json", "eslint"];

        if (global) {
            npmLsArgs.push("-g");
        }

        try {
            const parsedStdout = JSON.parse(execCommand("npm", npmLsArgs));

            /*
             * Checking globally returns an empty JSON object, while local checks
             * include the name and version of the local project.
             */
            if (isEmpty(parsedStdout) || !(parsedStdout.dependencies && parsedStdout.dependencies.eslint)) {
                return "Not found";
            }

            const [, processBinPath] = process.argv;
            let npmBinPath;

            try {
                npmBinPath = execCommand("npm", npmBinArgs);
            } catch (e) {
                log.error(`Error finding npm binary path when running command \`npm ${npmBinArgs.join(" ")}\``);
                throw e;
            }

            const isGlobal = isChildOfDirectory(npmBinPath, processBinPath);
            let pkgVersion = parsedStdout.dependencies.eslint.version;

            if ((global && isGlobal) || (!global && !isGlobal)) {
                pkgVersion += " (Currently used)";
            }

            return normalizeVersionStr(pkgVersion);
        } catch (e) {
            log.error(`Error finding ${pkg} version running the command \`npm ${npmLsArgs.join(" ")}\``);
            throw e;
        }
    }

    return [
        "Environment Info:",
        "",
        `Node version: ${getBinVersion("node")}`,
        `npm version: ${getBinVersion("npm")}`,
        `Local ESLint version: ${getNpmPackageVersion("eslint", { global: false })}`,
        `Global ESLint version: ${getNpmPackageVersion("eslint", { global: true })}`
    ].join("\n");
}

/**
 * Returns version of currently executing ESLint.
 * @returns {string} The version from the currently executing ESLint's package.json.
 */
function version() {
    return `v${packageJson.version}`;
}

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

module.exports = {
    environment,
    version
};