1 | /**
|
2 | * @fileoverview Utility for executing npm commands.
|
3 | * @author Ian VanSchooten
|
4 | */
|
5 |
|
6 | ;
|
7 |
|
8 | //------------------------------------------------------------------------------
|
9 | // Requirements
|
10 | //------------------------------------------------------------------------------
|
11 |
|
12 | const fs = require("fs"),
|
13 | path = require("path"),
|
14 | shell = require("shelljs"),
|
15 | log = require("../logging");
|
16 |
|
17 | //------------------------------------------------------------------------------
|
18 | // Helpers
|
19 | //------------------------------------------------------------------------------
|
20 |
|
21 | /**
|
22 | * Find the closest package.json file, starting at process.cwd (by default),
|
23 | * and working up to root.
|
24 | *
|
25 | * @param {string} [startDir=process.cwd()] Starting directory
|
26 | * @returns {string} Absolute path to closest package.json file
|
27 | */
|
28 | function findPackageJson(startDir) {
|
29 | let dir = path.resolve(startDir || process.cwd());
|
30 |
|
31 | do {
|
32 | const pkgfile = path.join(dir, "package.json");
|
33 |
|
34 | if (!shell.test("-f", pkgfile)) {
|
35 | dir = path.join(dir, "..");
|
36 | continue;
|
37 | }
|
38 | return pkgfile;
|
39 | } while (dir !== path.resolve(dir, ".."));
|
40 | return null;
|
41 | }
|
42 |
|
43 | //------------------------------------------------------------------------------
|
44 | // Private
|
45 | //------------------------------------------------------------------------------
|
46 |
|
47 | /**
|
48 | * Install node modules synchronously and save to devDependencies in package.json
|
49 | * @param {string|string[]} packages Node module or modules to install
|
50 | * @returns {void}
|
51 | */
|
52 | function installSyncSaveDev(packages) {
|
53 | if (Array.isArray(packages)) {
|
54 | packages = packages.join(" ");
|
55 | }
|
56 | shell.exec("npm i --save-dev " + packages, {stdio: "inherit"});
|
57 | }
|
58 |
|
59 | /**
|
60 | * Check whether node modules are include in a project's package.json.
|
61 | *
|
62 | * @param {string[]} packages Array of node module names
|
63 | * @param {Object} opt Options Object
|
64 | * @param {boolean} opt.dependencies Set to true to check for direct dependencies
|
65 | * @param {boolean} opt.devDependencies Set to true to check for development dependencies
|
66 | * @param {boolean} opt.startdir Directory to begin searching from
|
67 | * @returns {Object} An object whose keys are the module names
|
68 | * and values are booleans indicating installation.
|
69 | */
|
70 | function check(packages, opt) {
|
71 | let deps = [];
|
72 | const pkgJson = (opt) ? findPackageJson(opt.startDir) : findPackageJson();
|
73 | let fileJson;
|
74 |
|
75 | if (!pkgJson) {
|
76 | throw new Error("Could not find a package.json file. Run 'npm init' to create one.");
|
77 | }
|
78 |
|
79 | try {
|
80 | fileJson = JSON.parse(fs.readFileSync(pkgJson, "utf8"));
|
81 | } catch (e) {
|
82 | log.info("Could not read package.json file. Please check that the file contains valid JSON.");
|
83 | throw new Error(e);
|
84 | }
|
85 |
|
86 | if (opt.devDependencies && typeof fileJson.devDependencies === "object") {
|
87 | deps = deps.concat(Object.keys(fileJson.devDependencies));
|
88 | }
|
89 | if (opt.dependencies && typeof fileJson.dependencies === "object") {
|
90 | deps = deps.concat(Object.keys(fileJson.dependencies));
|
91 | }
|
92 | return packages.reduce(function(status, pkg) {
|
93 | status[pkg] = deps.indexOf(pkg) !== -1;
|
94 | return status;
|
95 | }, {});
|
96 | }
|
97 |
|
98 | /**
|
99 | * Check whether node modules are included in the dependencies of a project's
|
100 | * package.json.
|
101 | *
|
102 | * Convienience wrapper around check().
|
103 | *
|
104 | * @param {string[]} packages Array of node modules to check.
|
105 | * @param {string} rootDir The directory contianing a package.json
|
106 | * @returns {Object} An object whose keys are the module names
|
107 | * and values are booleans indicating installation.
|
108 | */
|
109 | function checkDeps(packages, rootDir) {
|
110 | return check(packages, {dependencies: true, startDir: rootDir});
|
111 | }
|
112 |
|
113 | /**
|
114 | * Check whether node modules are included in the devDependencies of a project's
|
115 | * package.json.
|
116 | *
|
117 | * Convienience wrapper around check().
|
118 | *
|
119 | * @param {string[]} packages Array of node modules to check.
|
120 | * @returns {Object} An object whose keys are the module names
|
121 | * and values are booleans indicating installation.
|
122 | */
|
123 | function checkDevDeps(packages) {
|
124 | return check(packages, {devDependencies: true});
|
125 | }
|
126 |
|
127 | /**
|
128 | * Check whether package.json is found in current path.
|
129 | *
|
130 | * @param {string=} startDir Starting directory
|
131 | * @returns {boolean} Whether a package.json is found in current path.
|
132 | */
|
133 | function checkPackageJson(startDir) {
|
134 | return !!findPackageJson(startDir);
|
135 | }
|
136 |
|
137 | //------------------------------------------------------------------------------
|
138 | // Public Interface
|
139 | //------------------------------------------------------------------------------
|
140 |
|
141 | module.exports = {
|
142 | installSyncSaveDev,
|
143 | checkDeps,
|
144 | checkDevDeps,
|
145 | checkPackageJson
|
146 | };
|