1 | "use strict";
|
2 |
|
3 | const Bluebird = require('bluebird');
|
4 | const npmWrapper = require('./npmWrapper');
|
5 | const ora = require('ora');
|
6 | const path = require('path');
|
7 | const logger = require('./logger').getLogger("lazy-require");
|
8 | const { getCliLocation } = require('../utils');
|
9 | const { requireWithFallback } = require("./requireWithFallback")
|
10 |
|
11 | const packageJson = require(path.resolve(getCliLocation(), 'package.json'));
|
12 | const ongoingCalls = new Map();
|
13 |
|
14 | module.exports = async function memoizedLazyRequireApi(dependency, options = {}) {
|
15 | const packageLocally = npmWrapper.getPackageIfInstalledLocally(dependency);
|
16 | if (packageLocally) {
|
17 | return packageLocally;
|
18 | }
|
19 |
|
20 | let spinner;
|
21 | if (!options.silent) {
|
22 | spinner = ora(`Installing ${dependency} before first usage...`).start();
|
23 | }
|
24 |
|
25 | try {
|
26 | const requiredModule = await memoizedLazyRequire(dependency);
|
27 | if (spinner) {
|
28 | spinner.succeed();
|
29 | }
|
30 | return requiredModule;
|
31 | } catch (error) {
|
32 | logger.warn("failed to install dependency lazily", {dependency, err: error});
|
33 | const depVersionToInstall = getVersionOfLazyDep(dependency);
|
34 | const depWithVersions = `${dependency}@${depVersionToInstall}`;
|
35 |
|
36 | const removeGlobal = process.argv.includes('npx');
|
37 | const globalFlag = removeGlobal ? '' : '-g '
|
38 | const errorMessage = `Installation of ${dependency} failed. This typically means you are running an out-of-date version of Node.js or NPM.` +
|
39 | `Please manually install by running the following command: npm install ${globalFlag}${depWithVersions}`
|
40 |
|
41 | if (spinner) {
|
42 | spinner.fail(errorMessage);
|
43 | }
|
44 |
|
45 | throw error;
|
46 | }
|
47 | };
|
48 |
|
49 | async function memoizedLazyRequire(identifier, options = {}) {
|
50 | if (ongoingCalls.has(identifier)) {
|
51 | return ongoingCalls.get(identifier);
|
52 | }
|
53 |
|
54 | ongoingCalls.set(identifier, lazyRequireImpl(identifier, options));
|
55 | ongoingCalls.get(identifier).catch(err => {
|
56 | ongoingCalls.delete(identifier);
|
57 | });
|
58 |
|
59 | return ongoingCalls.get(identifier);
|
60 | };
|
61 |
|
62 | async function lazyRequireImpl(dependency) {
|
63 | const packageLocally = npmWrapper.getPackageIfInstalledLocally(dependency);
|
64 |
|
65 | if (packageLocally) {
|
66 | return packageLocally;
|
67 | }
|
68 |
|
69 | const depVersionToInstall = getVersionOfLazyDep(dependency);
|
70 |
|
71 | const depWithVersions = `${dependency}@${depVersionToInstall}`;
|
72 |
|
73 | await npmWrapper.installPackageLocally(getCliLocation(), depWithVersions);
|
74 |
|
75 | return requireWithFallback(dependency);
|
76 | }
|
77 |
|
78 | function installAllLazyDependencies() {
|
79 | const allLazyDependencies = Object.keys(packageJson.lazyDependencies);
|
80 |
|
81 | return Bluebird.each(allLazyDependencies, dep => lazyRequireImpl(dep));
|
82 | }
|
83 |
|
84 | if (require.main === module) {
|
85 | installAllLazyDependencies();
|
86 | }
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 | function getVersionOfLazyDep(dependencyToFind) {
|
93 | const depEntry = Object.entries(packageJson.lazyDependencies)
|
94 | .find(([dep]) => dep === dependencyToFind);
|
95 |
|
96 | if (!depEntry) {
|
97 | throw new Error("The given package name is not lazyDependencies");
|
98 | }
|
99 |
|
100 | return depEntry[1];
|
101 | }
|