1 | import debug from "debug";
|
2 | import * as path from "path";
|
3 | import * as semver from "semver";
|
4 |
|
5 | import { BuidlerContext } from "../context";
|
6 |
|
7 | import { BuidlerError } from "./errors";
|
8 | import { ERRORS } from "./errors-list";
|
9 | import { ExecutionMode, getExecutionMode } from "./execution-mode";
|
10 |
|
11 | const log = debug("buidler:core:plugins");
|
12 |
|
13 | interface PackageJson {
|
14 | name: string;
|
15 | version: string;
|
16 | peerDependencies: {
|
17 | [name: string]: string;
|
18 | };
|
19 | }
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 | export function usePlugin(
|
29 | buidlerContext: BuidlerContext,
|
30 | pluginName: string,
|
31 | from?: string
|
32 | ) {
|
33 | log("Loading plugin %s", pluginName);
|
34 |
|
35 | const executionMode = getExecutionMode();
|
36 |
|
37 | if (from === undefined) {
|
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 | if (executionMode === ExecutionMode.EXECUTION_MODE_GLOBAL_INSTALLATION) {
|
71 | from = __dirname;
|
72 | } else {
|
73 | from = buidlerContext.getConfigPath();
|
74 | }
|
75 | }
|
76 |
|
77 | let globalFlag = "";
|
78 | let globalWarning = "";
|
79 | if (executionMode === ExecutionMode.EXECUTION_MODE_GLOBAL_INSTALLATION) {
|
80 | globalFlag = " --global";
|
81 | globalWarning =
|
82 | "You are using a global installation of Buidler. Plugins and their dependencies must also be global.\n";
|
83 | }
|
84 |
|
85 | const pluginPackageJson = readPackageJson(pluginName, from);
|
86 |
|
87 | if (pluginPackageJson === undefined) {
|
88 | const installExtraFlags = globalFlag;
|
89 |
|
90 | throw new BuidlerError(ERRORS.PLUGINS.NOT_INSTALLED, {
|
91 | plugin: pluginName,
|
92 | extraMessage: globalWarning,
|
93 | extraFlags: installExtraFlags,
|
94 | });
|
95 | }
|
96 |
|
97 |
|
98 | pluginName = pluginPackageJson.name;
|
99 |
|
100 | if (buidlerContext.loadedPlugins.includes(pluginName)) {
|
101 | return;
|
102 | }
|
103 |
|
104 | if (pluginPackageJson.peerDependencies !== undefined) {
|
105 | for (const [dependencyName, versionSpec] of Object.entries(
|
106 | pluginPackageJson.peerDependencies
|
107 | )) {
|
108 | const dependencyPackageJson = readPackageJson(dependencyName, from);
|
109 |
|
110 | let installExtraFlags = globalFlag;
|
111 |
|
112 | if (versionSpec.match(/^[0-9]/) !== null) {
|
113 | installExtraFlags += " --save-exact";
|
114 | }
|
115 |
|
116 | if (dependencyPackageJson === undefined) {
|
117 | throw new BuidlerError(ERRORS.PLUGINS.MISSING_DEPENDENCY, {
|
118 | plugin: pluginName,
|
119 | dependency: dependencyName,
|
120 | extraMessage: globalWarning,
|
121 | extraFlags: installExtraFlags,
|
122 | versionSpec,
|
123 | });
|
124 | }
|
125 |
|
126 | const installedVersion = dependencyPackageJson.version;
|
127 |
|
128 | if (
|
129 | !semver.satisfies(installedVersion, versionSpec, {
|
130 | includePrerelease: true,
|
131 | })
|
132 | ) {
|
133 | throw new BuidlerError(ERRORS.PLUGINS.DEPENDENCY_VERSION_MISMATCH, {
|
134 | plugin: pluginName,
|
135 | dependency: dependencyName,
|
136 | extraMessage: globalWarning,
|
137 | extraFlags: installExtraFlags,
|
138 | versionSpec,
|
139 | installedVersion,
|
140 | });
|
141 | }
|
142 | }
|
143 | }
|
144 |
|
145 | const options = from !== undefined ? { paths: [from] } : undefined;
|
146 | const pluginPath = require.resolve(pluginName, options);
|
147 | loadPluginFile(pluginPath);
|
148 |
|
149 | buidlerContext.setPluginAsLoaded(pluginName);
|
150 | }
|
151 |
|
152 | export function loadPluginFile(absolutePluginFilePath: string) {
|
153 | log("Loading plugin file %s", absolutePluginFilePath);
|
154 | const imported = require(absolutePluginFilePath);
|
155 | const plugin = imported.default !== undefined ? imported.default : imported;
|
156 | if (typeof plugin === "function") {
|
157 | plugin();
|
158 | }
|
159 | }
|
160 |
|
161 | export function readPackageJson(
|
162 | packageName: string,
|
163 | from?: string
|
164 | ): PackageJson | undefined {
|
165 | try {
|
166 | const options = from !== undefined ? { paths: [from] } : undefined;
|
167 | const packageJsonPath = require.resolve(
|
168 | path.join(packageName, "package.json"),
|
169 | options
|
170 | );
|
171 |
|
172 | return require(packageJsonPath);
|
173 | } catch (error) {
|
174 | return undefined;
|
175 | }
|
176 | }
|
177 |
|
178 | export function ensurePluginLoadedWithUsePlugin() {
|
179 |
|
180 | }
|