UNPKG

10.3 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const errors_1 = require("@oclif/errors");
4const os = require("os");
5const path = require("path");
6const util_1 = require("util");
7const debug_1 = require("./debug");
8const Plugin = require("./plugin");
9const ts_node_1 = require("./ts_node");
10const util_2 = require("./util");
11const debug = debug_1.default();
12const _pjson = require('../package.json');
13class Config {
14 constructor(options) {
15 this.options = options;
16 this._base = `${_pjson.name}@${_pjson.version}`;
17 this.debug = 0;
18 this.plugins = [];
19 this.warned = false;
20 }
21 async load() {
22 await this.loadPlugins(this.options.root, 'core', [{ root: this.options.root }], { must: true });
23 const plugin = this.plugins[0];
24 this.root = plugin.root;
25 this.pjson = plugin.pjson;
26 this.name = this.pjson.name;
27 this.version = this.pjson.version;
28 this.arch = (os.arch() === 'ia32' ? 'x86' : os.arch());
29 this.platform = os.platform();
30 this.windows = this.platform === 'win32';
31 this.bin = this.pjson.oclif.bin || this.name;
32 this.dirname = this.pjson.oclif.dirname || this.name;
33 this.userAgent = `${this.name}/${this.version} ${this.platform}-${this.arch} node-${process.version}`;
34 this.shell = this._shell();
35 this.debug = this._debug();
36 this.home = process.env.HOME || (this.windows && this.windowsHome()) || os.homedir() || os.tmpdir();
37 this.cacheDir = this.scopedEnvVar('CACHE_DIR') || this.macosCacheDir() || this.dir('cache');
38 this.configDir = this.scopedEnvVar('CONFIG_DIR') || this.dir('config');
39 this.dataDir = this.scopedEnvVar('DATA_DIR') || this.dir('data');
40 this.errlog = path.join(this.cacheDir, 'error.log');
41 this.npmRegistry = this.scopedEnvVar('NPM_REGISTRY') || this.pjson.oclif.npmRegistry || 'https://registry.yarnpkg.com';
42 await Promise.all([
43 this.loadCorePlugins(),
44 this.loadUserPlugins(),
45 this.loadDevPlugins(),
46 ]);
47 debug('config done');
48 }
49 async loadCorePlugins() {
50 if (this.pjson.oclif.plugins) {
51 await this.loadPlugins(this.root, 'core', this.pjson.oclif.plugins);
52 }
53 }
54 async loadDevPlugins() {
55 if (this.options.devPlugins !== false) {
56 try {
57 const devPlugins = this.pjson.oclif.devPlugins;
58 if (devPlugins)
59 await this.loadPlugins(this.root, 'dev', devPlugins);
60 }
61 catch (err) {
62 process.emitWarning(err);
63 }
64 }
65 }
66 async loadUserPlugins() {
67 if (this.options.userPlugins !== false) {
68 try {
69 const userPJSONPath = path.join(this.dataDir, 'package.json');
70 const pjson = this.userPJSON = await util_2.loadJSON(userPJSONPath);
71 if (!pjson.oclif)
72 pjson.oclif = { schema: 1 };
73 await this.loadPlugins(userPJSONPath, 'user', pjson.oclif.plugins);
74 }
75 catch (err) {
76 if (err.code !== 'ENOENT')
77 process.emitWarning(err);
78 }
79 }
80 }
81 async runHook(event, opts) {
82 debug('start %s hook', event);
83 const context = {
84 exit(code = 0) { errors_1.exit(code); },
85 log(message = '') {
86 message = typeof message === 'string' ? message : util_1.inspect(message);
87 process.stdout.write(message + '\n');
88 },
89 error(message, options = {}) {
90 errors_1.error(message, options);
91 },
92 warn(message) { errors_1.warn(message); },
93 };
94 const promises = this.plugins.map(p => {
95 return Promise.all((p.hooks[event] || [])
96 .map(async (hook) => {
97 try {
98 const f = ts_node_1.tsPath(p.root, hook);
99 debug('hook', event, f);
100 const search = (m) => {
101 if (typeof m === 'function')
102 return m;
103 if (m.default && typeof m.default === 'function')
104 return m.default;
105 return Object.values(m).find((m) => typeof m === 'function');
106 };
107 await search(require(f)).call(context, Object.assign({}, opts, { config: this }));
108 }
109 catch (err) {
110 if (err && err.oclif && err.oclif.exit !== undefined)
111 throw err;
112 this.warn(err, `runHook ${event}`);
113 }
114 }));
115 });
116 await Promise.all(promises);
117 debug('done %s hook', event);
118 }
119 async runCommand(id, argv = []) {
120 debug('runCommand %s %o', id, argv);
121 const c = this.findCommand(id);
122 if (!c) {
123 await this.runHook('command_not_found', { id });
124 throw new errors_1.CLIError(`command ${id} not found`);
125 }
126 const command = c.load();
127 await this.runHook('prerun', { Command: command, argv });
128 await command.run(argv, this);
129 }
130 scopedEnvVar(k) {
131 return process.env[this.scopedEnvVarKey(k)];
132 }
133 scopedEnvVarTrue(k) {
134 let v = process.env[this.scopedEnvVarKey(k)];
135 return v === '1' || v === 'true';
136 }
137 scopedEnvVarKey(k) {
138 return [this.bin, k]
139 .map(p => p.replace(/@/g, '').replace(/[-\/]/g, '_'))
140 .join('_')
141 .toUpperCase();
142 }
143 findCommand(id, opts = {}) {
144 let command = this.commands.find(c => c.id === id || c.aliases.includes(id));
145 if (command)
146 return command;
147 if (opts.must)
148 errors_1.error(`command ${id} not found`);
149 }
150 findTopic(name, opts = {}) {
151 let topic = this.topics.find(t => t.name === name);
152 if (topic)
153 return topic;
154 if (opts.must)
155 throw new Error(`topic ${name} not found`);
156 }
157 get commands() { return util_2.flatMap(this.plugins, p => p.commands); }
158 get commandIDs() { return util_2.uniq(this.commands.map(c => c.id)); }
159 get topics() {
160 let topics = [];
161 for (let plugin of this.plugins) {
162 for (let topic of util_2.compact(plugin.topics)) {
163 let existing = topics.find(t => t.name === topic.name);
164 if (existing) {
165 existing.description = topic.description || existing.description;
166 existing.hidden = existing.hidden || topic.hidden;
167 }
168 else
169 topics.push(topic);
170 }
171 }
172 // add missing topics
173 for (let c of this.commands.filter(c => !c.hidden)) {
174 let parts = c.id.split(':');
175 while (parts.length) {
176 let name = parts.join(':');
177 if (name && !topics.find(t => t.name === name)) {
178 topics.push({ name });
179 }
180 parts.pop();
181 }
182 }
183 return topics;
184 }
185 dir(category) {
186 const base = process.env[`XDG_${category.toUpperCase()}_HOME`]
187 || (this.windows && process.env.LOCALAPPDATA)
188 || path.join(this.home, category === 'data' ? '.local/share' : '.' + category);
189 return path.join(base, this.dirname);
190 }
191 windowsHome() { return this.windowsHomedriveHome() || this.windowsUserprofileHome(); }
192 windowsHomedriveHome() { return (process.env.HOMEDRIVE && process.env.HOMEPATH && path.join(process.env.HOMEDRIVE, process.env.HOMEPATH)); }
193 windowsUserprofileHome() { return process.env.USERPROFILE; }
194 macosCacheDir() { return this.platform === 'darwin' && path.join(this.home, 'Library', 'Caches', this.dirname) || undefined; }
195 _shell() {
196 let shellPath;
197 const { SHELL, COMSPEC } = process.env;
198 if (SHELL) {
199 shellPath = SHELL.split('/');
200 }
201 else if (this.windows && COMSPEC) {
202 shellPath = COMSPEC.split(/\\|\//);
203 }
204 else {
205 shellPath = ['unknown'];
206 }
207 return shellPath[shellPath.length - 1];
208 }
209 _debug() {
210 if (this.scopedEnvVarTrue('DEBUG'))
211 return 1;
212 try {
213 const { enabled } = require('debug')(this.bin);
214 if (enabled)
215 return 1;
216 }
217 catch (_a) { }
218 return 0;
219 }
220 async loadPlugins(root, type, plugins, options = {}) {
221 if (!plugins.length)
222 return;
223 if (!plugins || !plugins.length)
224 return;
225 debug('loading plugins', plugins);
226 await Promise.all((plugins || []).map(async (plugin) => {
227 try {
228 let opts = { type, root };
229 if (typeof plugin === 'string') {
230 opts.name = plugin;
231 }
232 else {
233 opts.name = plugin.name || opts.name;
234 opts.tag = plugin.tag || opts.tag;
235 opts.root = plugin.root || opts.root;
236 }
237 let instance = new Plugin.Plugin(opts);
238 await instance.load();
239 this.plugins.push(instance);
240 }
241 catch (err) {
242 if (options.must)
243 throw err;
244 this.warn(err, 'loadPlugins');
245 }
246 }));
247 }
248 warn(err, scope) {
249 if (this.warned)
250 return;
251 err.name = `${err.name} Plugin: ${this.name}`;
252 err.detail = util_2.compact([err.detail, `module: ${this._base}`, scope && `task: ${scope}`, `plugin: ${this.name}`, `root: ${this.root}`]).join('\n');
253 process.emitWarning(err);
254 }
255}
256exports.Config = Config;
257async function load(opts = (module.parent && module.parent && module.parent.parent && module.parent.parent.filename) || __dirname) {
258 if (typeof opts === 'string')
259 opts = { root: opts };
260 if (isConfig(opts))
261 return opts;
262 let config = new Config(opts);
263 await config.load();
264 return config;
265}
266exports.load = load;
267function isConfig(o) {
268 return o && !!o._base;
269}