1 | import "source-map-support/register";
|
2 | import path from "path";
|
3 | import assignIn from "lodash.assignin";
|
4 | import merge from "lodash.merge";
|
5 | import Module from "module";
|
6 | import findUp from "find-up";
|
7 | import Configstore from "configstore";
|
8 | import TruffleError from "@truffle/error";
|
9 | import originalRequire from "original-require";
|
10 | import { getInitialConfig, configProps } from "./configDefaults";
|
11 | import { EventManager } from "@truffle/events";
|
12 |
|
13 | const DEFAULT_CONFIG_FILENAME = "truffle-config.js";
|
14 | const BACKUP_CONFIG_FILENAME = "truffle.js";
|
15 |
|
16 | class TruffleConfig {
|
17 | [key: string]: any;
|
18 |
|
19 | private _deepCopy: string[];
|
20 | private _values: any;
|
21 |
|
22 | constructor(
|
23 | truffleDirectory?: string,
|
24 | workingDirectory?: string,
|
25 | network?: any
|
26 | ) {
|
27 | this._deepCopy = ["compilers"];
|
28 | this._values = getInitialConfig({
|
29 | truffleDirectory,
|
30 | workingDirectory,
|
31 | network
|
32 | });
|
33 |
|
34 | const eventsOptions = this.eventManagerOptions(this);
|
35 | this.events = new EventManager(eventsOptions);
|
36 |
|
37 | const props = configProps({ configObject: this });
|
38 |
|
39 | Object.entries(props).forEach(([propName, descriptor]) =>
|
40 | this.addProp(propName, descriptor)
|
41 | );
|
42 | }
|
43 |
|
44 | public eventManagerOptions(config: TruffleConfig): any {
|
45 | let muteLogging;
|
46 | const { quiet, logger, subscribers } = config;
|
47 |
|
48 | if (quiet) muteLogging = true;
|
49 | return { logger, muteLogging, subscribers };
|
50 | }
|
51 |
|
52 | public addProp(propertyName: string, descriptor: any): void {
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 | const self = this;
|
60 |
|
61 | Object.defineProperty(this, propertyName, {
|
62 | get:
|
63 | descriptor.get ||
|
64 | function() {
|
65 |
|
66 | if (propertyName in self._values) {
|
67 | return self._values[propertyName];
|
68 | }
|
69 |
|
70 |
|
71 | if (descriptor.default) {
|
72 | return descriptor.default();
|
73 | }
|
74 |
|
75 |
|
76 | return descriptor();
|
77 | },
|
78 | set:
|
79 | descriptor.set ||
|
80 | function(value) {
|
81 | self._values[propertyName] = descriptor.transform
|
82 | ? descriptor.transform(value)
|
83 | : value;
|
84 | },
|
85 | configurable: true,
|
86 | enumerable: true
|
87 | });
|
88 | }
|
89 |
|
90 | public normalize(obj: any): any {
|
91 | const clone: any = {};
|
92 |
|
93 | Object.keys(obj).forEach(key => {
|
94 | try {
|
95 | clone[key] = obj[key];
|
96 | } catch (e) {
|
97 |
|
98 | }
|
99 | });
|
100 |
|
101 | return clone;
|
102 | }
|
103 |
|
104 | public with(obj: any): TruffleConfig {
|
105 | const current = this.normalize(this);
|
106 | const normalized = this.normalize(obj);
|
107 |
|
108 | let eventsOptions = this.eventManagerOptions(this);
|
109 | this.events.updateSubscriberOptions(eventsOptions);
|
110 |
|
111 | return assignIn(
|
112 | Object.create(TruffleConfig.prototype),
|
113 | current,
|
114 | normalized
|
115 | );
|
116 | }
|
117 |
|
118 | public merge(obj: any): TruffleConfig {
|
119 | const clone = this.normalize(obj);
|
120 |
|
121 |
|
122 | const propertyNames = Object.keys(obj);
|
123 |
|
124 | propertyNames.forEach(key => {
|
125 | try {
|
126 | if (typeof clone[key] === "object" && this._deepCopy.includes(key)) {
|
127 | this[key] = merge(this[key], clone[key]);
|
128 | } else {
|
129 | this[key] = clone[key];
|
130 | }
|
131 | } catch (e) {
|
132 |
|
133 | }
|
134 | });
|
135 |
|
136 | const eventsOptions = this.eventManagerOptions(this);
|
137 | this.events.updateSubscriberOptions(eventsOptions);
|
138 |
|
139 | return this;
|
140 | }
|
141 |
|
142 | public static default(): TruffleConfig {
|
143 | return new TruffleConfig();
|
144 | }
|
145 |
|
146 | public static search(options: any = {}, filename?: string): string | null {
|
147 | const searchOptions = {
|
148 | cwd: options.working_directory || options.workingDirectory
|
149 | };
|
150 |
|
151 | if (!filename) {
|
152 | const isWin = process.platform === "win32";
|
153 | const defaultConfig = findUp.sync(DEFAULT_CONFIG_FILENAME, searchOptions);
|
154 | const backupConfig = findUp.sync(BACKUP_CONFIG_FILENAME, searchOptions);
|
155 |
|
156 | if (defaultConfig && backupConfig) {
|
157 | console.warn(
|
158 | `Warning: Both ${DEFAULT_CONFIG_FILENAME} and ${BACKUP_CONFIG_FILENAME} were found. Using ${DEFAULT_CONFIG_FILENAME}.`
|
159 | );
|
160 | return defaultConfig;
|
161 | } else if (backupConfig && !defaultConfig) {
|
162 | if (isWin)
|
163 | console.warn(
|
164 | `Warning: Please rename ${BACKUP_CONFIG_FILENAME} to ${DEFAULT_CONFIG_FILENAME} to ensure Windows compatibility.`
|
165 | );
|
166 | return backupConfig;
|
167 | } else {
|
168 | return defaultConfig;
|
169 | }
|
170 | }
|
171 |
|
172 | return findUp.sync(filename, searchOptions);
|
173 | }
|
174 |
|
175 | public static detect(options: any = {}, filename?: string): TruffleConfig {
|
176 | let configFile;
|
177 | const configPath = options.config;
|
178 |
|
179 | if (configPath) {
|
180 | configFile = path.isAbsolute(configPath)
|
181 | ? configPath
|
182 | : path.resolve(configPath);
|
183 | } else {
|
184 | configFile = TruffleConfig.search(options, filename);
|
185 | }
|
186 |
|
187 | if (!configFile) {
|
188 | throw new TruffleError("Could not find suitable configuration file.");
|
189 | }
|
190 |
|
191 | return TruffleConfig.load(configFile, options);
|
192 | }
|
193 |
|
194 | public static load(file: string, options: any = {}): TruffleConfig {
|
195 | const workingDirectory = options.config
|
196 | ? process.cwd()
|
197 | : path.dirname(path.resolve(file));
|
198 |
|
199 | const config = new TruffleConfig(undefined, workingDirectory, undefined);
|
200 |
|
201 |
|
202 |
|
203 |
|
204 | delete require.cache[Module._resolveFilename(file, module)];
|
205 | const staticConfig = originalRequire(file);
|
206 |
|
207 | config.merge(staticConfig);
|
208 | config.merge(options);
|
209 |
|
210 | // When loading a user's config, ensure their subscribers are initialized
|
211 | const eventsOptions = config.eventManagerOptions(config);
|
212 | config.events.updateSubscriberOptions(eventsOptions);
|
213 | config.events.initializeUserSubscribers(eventsOptions);
|
214 |
|
215 | return config;
|
216 | }
|
217 |
|
218 | public static getUserConfig(): Configstore {
|
219 | return new Configstore("truffle", {}, { globalConfigPath: true });
|
220 | }
|
221 |
|
222 | public static getTruffleDataDirectory(): string {
|
223 | const configStore = TruffleConfig.getUserConfig();
|
224 |
|
225 | return path.dirname(configStore.path);
|
226 | }
|
227 | }
|
228 |
|
229 | export = TruffleConfig;
|