UNPKG

6.47 kBPlain TextView Raw
1import "source-map-support/register";
2import path from "path";
3import assignIn from "lodash.assignin";
4import merge from "lodash.merge";
5import Module from "module";
6import findUp from "find-up";
7import Configstore from "configstore";
8import TruffleError from "@truffle/error";
9import originalRequire from "original-require";
10import { getInitialConfig, configProps } from "./configDefaults";
11import { EventManager } from "@truffle/events";
12
13const DEFAULT_CONFIG_FILENAME = "truffle-config.js";
14const BACKUP_CONFIG_FILENAME = "truffle.js"; // old config filename
15
16class 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 // possible property descriptors
54 //
55 // supports `default` and `transform` in addition to `get` and `set`
56 //
57 // default: specify function to retrieve default value (used by get)
58 // transform: specify function to transform value when (used by set)
59 const self = this;
60
61 Object.defineProperty(this, propertyName, {
62 get:
63 descriptor.get ||
64 function() {
65 // value is specified
66 if (propertyName in self._values) {
67 return self._values[propertyName];
68 }
69
70 // default getter is specified
71 if (descriptor.default) {
72 return descriptor.default();
73 }
74
75 // descriptor is a function
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 // Do nothing with values that throw.
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 // Only set keys for values that don't throw.
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 // ignore
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 // The require-nocache module used to do this for us, but
202 // it doesn't bundle very well. So we've pulled it out ourselves.
203 // @ts-ignore
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
229export = TruffleConfig;