UNPKG

5.47 kBJavaScriptView Raw
1"use strict";
2/**
3 * @license
4 * Copyright (c) 2020 The Polymer Project Authors. All rights reserved.
5 * This code may only be used under the BSD style license found at
6 * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
7 * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
8 * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
9 * Google as part of the polymer project is also subject to an additional IP
10 * rights grant found at http://polymer.github.io/PATENTS.txt
11 */
12var __importDefault = (this && this.__importDefault) || function (mod) {
13 return (mod && mod.__esModule) ? mod : { "default": mod };
14};
15Object.defineProperty(exports, "__esModule", { value: true });
16exports.installOnDemand = exports.getOnDemandDependencies = exports.onDemandDependenciesFromPackageJSON = exports.getPackageRoot = exports.getPackageJSONPath = exports.assertResolvable = void 0;
17const child_process_1 = require("child_process");
18const fs_1 = require("fs");
19const path_1 = __importDefault(require("path"));
20const pkg_install_1 = require("pkg-install");
21const pkg_up_1 = __importDefault(require("pkg-up"));
22/**
23 * Asynchronously checks to see if a module is resolvable. This gives the
24 * invoker information that is similar to what they would get from using
25 * require.resolve. However, require.resolve is backed by an unclearable
26 * internal cache, which this helper bypasses via a child process.
27 *
28 * @see https://github.com/nodejs/node/issues/31803
29 */
30exports.assertResolvable = async (id) => {
31 await new Promise(async (resolve, reject) => {
32 child_process_1.exec(`${process.execPath} -e "require.resolve(process.env.ID)"`, {
33 cwd: await exports.getPackageRoot() || process.cwd(),
34 env: Object.assign(Object.assign({}, process.env), { ID: id })
35 }, (error) => {
36 if (error != null) {
37 reject(error);
38 return;
39 }
40 resolve();
41 });
42 });
43};
44exports.getPackageJSONPath = async () => {
45 // NOTE: This used to search starting with module.path, but module.path was
46 // not added until Node.js v11. In order to preserve Node.js v10 compatibility
47 // we use __dirname instead, which should be mostly the same thing (docs are
48 // fuzzy on the specific differences, unfortunately).
49 // @see https://nodejs.org/docs/latest/api/modules.html#modules_module_path
50 // @see https://nodejs.org/docs/latest/api/modules.html#modules_dirname
51 return pkg_up_1.default({ cwd: __dirname });
52};
53exports.getPackageRoot = async () => {
54 const packageJSONPath = await exports.getPackageJSONPath();
55 return packageJSONPath != null ? path_1.default.dirname(packageJSONPath) : null;
56};
57/**
58 * Extract a map of allowed "on-demand" dependencies from a given
59 * package.json-shaped object.
60 */
61exports.onDemandDependenciesFromPackageJSON = (packageJSON) => {
62 const onDemandDependencies = new Map();
63 const onDemandList = (packageJSON === null || packageJSON === void 0 ? void 0 : packageJSON.installsOnDemand) || [];
64 for (const packageName of onDemandList) {
65 onDemandDependencies.set(packageName, '*');
66 }
67 return onDemandDependencies;
68};
69/**
70 * So-called "on-demand" dependencies are any packages that match the
71 * following requirements:
72 *
73 * - They are enumerated in the non-normative package.json field
74 * "installsOnDemand"
75 *
76 * This function resolves a map of package names and semver ranges including all
77 * packages that match these requirements.
78 */
79exports.getOnDemandDependencies = (() => {
80 let cached = null;
81 return async () => {
82 if (cached == null) {
83 const packageJSONPath = await exports.getPackageJSONPath();
84 if (packageJSONPath != null) {
85 const rawPackageJSON = await fs_1.promises.readFile(packageJSONPath, {
86 encoding: 'utf-8',
87 });
88 const packageJSON = JSON.parse(rawPackageJSON.toString());
89 cached = exports.onDemandDependenciesFromPackageJSON(packageJSON);
90 }
91 }
92 return cached;
93 };
94})();
95/**
96 * Install an "on-demand" package, resolving after the package has been
97 * installed. Only packages designated as installable on-demand can be
98 * installed this way (see documentation for "getOnDemandDependenies" for more
99 * details). An attempt to install any other package this way will be rejected.
100 *
101 * On-demand packages are installed to this package's node_modules directory.
102 * Any package that can already be resolved from this package's root directory
103 * will be skipped.
104 */
105exports.installOnDemand = async (packageName) => {
106 try {
107 await exports.assertResolvable(packageName);
108 return;
109 }
110 catch (_error) {
111 }
112 let dependencies = new Map();
113 try {
114 dependencies = await exports.getOnDemandDependencies();
115 }
116 catch (error) {
117 console.error(error);
118 }
119 if (!dependencies.has(packageName)) {
120 throw new Error(`Package "${packageName}" cannot be installed on demand. ${dependencies}`);
121 }
122 const version = dependencies.get(packageName);
123 await pkg_install_1.install({ [packageName]: version }, {
124 stdio: 'inherit',
125 cwd: (await exports.getPackageRoot()) || process.cwd(),
126 noSave: true,
127 });
128 console.log(`Package "${packageName}@${version} installed."`);
129};
130//# sourceMappingURL=install.js.map
\No newline at end of file