UNPKG

10.4 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 config: this,
85 exit(code = 0) { errors_1.exit(code); },
86 log(message = '') {
87 message = typeof message === 'string' ? message : util_1.inspect(message);
88 process.stdout.write(message + '\n');
89 },
90 error(message, options = {}) {
91 errors_1.error(message, options);
92 },
93 warn(message) { errors_1.warn(message); },
94 };
95 const promises = this.plugins.map(p => {
96 return Promise.all((p.hooks[event] || [])
97 .map(async (hook) => {
98 try {
99 const f = ts_node_1.tsPath(p.root, hook);
100 debug('hook', event, f);
101 const search = (m) => {
102 if (typeof m === 'function')
103 return m;
104 if (m.default && typeof m.default === 'function')
105 return m.default;
106 return Object.values(m).find((m) => typeof m === 'function');
107 };
108 await search(require(f)).call(context, Object.assign({}, opts, { config: this }));
109 }
110 catch (err) {
111 if (err && err.oclif && err.oclif.exit !== undefined)
112 throw err;
113 this.warn(err, `runHook ${event}`);
114 }
115 }));
116 });
117 await Promise.all(promises);
118 debug('%s hook done', event);
119 }
120 async runCommand(id, argv = []) {
121 debug('runCommand %s %o', id, argv);
122 const c = this.findCommand(id);
123 if (!c) {
124 await this.runHook('command_not_found', { id });
125 throw new errors_1.CLIError(`command ${id} not found`);
126 }
127 const command = c.load();
128 await this.runHook('prerun', { Command: command, argv });
129 await command.run(argv, this);
130 }
131 scopedEnvVar(k) {
132 return process.env[this.scopedEnvVarKey(k)];
133 }
134 scopedEnvVarTrue(k) {
135 let v = process.env[this.scopedEnvVarKey(k)];
136 return v === '1' || v === 'true';
137 }
138 scopedEnvVarKey(k) {
139 return [this.bin, k]
140 .map(p => p.replace(/@/g, '').replace(/[-\/]/g, '_'))
141 .join('_')
142 .toUpperCase();
143 }
144 findCommand(id, opts = {}) {
145 let command = this.commands.find(c => c.id === id || c.aliases.includes(id));
146 if (command)
147 return command;
148 if (opts.must)
149 errors_1.error(`command ${id} not found`);
150 }
151 findTopic(name, opts = {}) {
152 let topic = this.topics.find(t => t.name === name);
153 if (topic)
154 return topic;
155 if (opts.must)
156 throw new Error(`topic ${name} not found`);
157 }
158 get commands() { return util_2.flatMap(this.plugins, p => p.commands); }
159 get commandIDs() { return util_2.uniq(this.commands.map(c => c.id)); }
160 get topics() {
161 let topics = [];
162 for (let plugin of this.plugins) {
163 for (let topic of util_2.compact(plugin.topics)) {
164 let existing = topics.find(t => t.name === topic.name);
165 if (existing) {
166 existing.description = topic.description || existing.description;
167 existing.hidden = existing.hidden || topic.hidden;
168 }
169 else
170 topics.push(topic);
171 }
172 }
173 // add missing topics
174 for (let c of this.commands.filter(c => !c.hidden)) {
175 let parts = c.id.split(':');
176 while (parts.length) {
177 let name = parts.join(':');
178 if (name && !topics.find(t => t.name === name)) {
179 topics.push({ name, description: c.description });
180 }
181 parts.pop();
182 }
183 }
184 return topics;
185 }
186 dir(category) {
187 const base = process.env[`XDG_${category.toUpperCase()}_HOME`]
188 || (this.windows && process.env.LOCALAPPDATA)
189 || path.join(this.home, category === 'data' ? '.local/share' : '.' + category);
190 return path.join(base, this.dirname);
191 }
192 windowsHome() { return this.windowsHomedriveHome() || this.windowsUserprofileHome(); }
193 windowsHomedriveHome() { return (process.env.HOMEDRIVE && process.env.HOMEPATH && path.join(process.env.HOMEDRIVE, process.env.HOMEPATH)); }
194 windowsUserprofileHome() { return process.env.USERPROFILE; }
195 macosCacheDir() { return this.platform === 'darwin' && path.join(this.home, 'Library', 'Caches', this.dirname) || undefined; }
196 _shell() {
197 let shellPath;
198 const { SHELL, COMSPEC } = process.env;
199 if (SHELL) {
200 shellPath = SHELL.split('/');
201 }
202 else if (this.windows && COMSPEC) {
203 shellPath = COMSPEC.split(/\\|\//);
204 }
205 else {
206 shellPath = ['unknown'];
207 }
208 return shellPath[shellPath.length - 1];
209 }
210 _debug() {
211 if (this.scopedEnvVarTrue('DEBUG'))
212 return 1;
213 try {
214 const { enabled } = require('debug')(this.bin);
215 if (enabled)
216 return 1;
217 }
218 catch (_a) { }
219 return 0;
220 }
221 async loadPlugins(root, type, plugins, options = {}) {
222 if (!plugins.length)
223 return;
224 if (!plugins || !plugins.length)
225 return;
226 debug('loading plugins', plugins);
227 await Promise.all((plugins || []).map(async (plugin) => {
228 try {
229 let opts = { type, root };
230 if (typeof plugin === 'string') {
231 opts.name = plugin;
232 }
233 else {
234 opts.name = plugin.name || opts.name;
235 opts.tag = plugin.tag || opts.tag;
236 opts.root = plugin.root || opts.root;
237 }
238 let instance = new Plugin.Plugin(opts);
239 await instance.load();
240 this.plugins.push(instance);
241 }
242 catch (err) {
243 if (options.must)
244 throw err;
245 this.warn(err, 'loadPlugins');
246 }
247 }));
248 }
249 warn(err, scope) {
250 if (this.warned)
251 return;
252 err.name = `${err.name} Plugin: ${this.name}`;
253 err.detail = util_2.compact([err.detail, `module: ${this._base}`, scope && `task: ${scope}`, `plugin: ${this.name}`, `root: ${this.root}`]).join('\n');
254 process.emitWarning(err);
255 }
256}
257exports.Config = Config;
258async function load(opts = (module.parent && module.parent && module.parent.parent && module.parent.parent.filename) || __dirname) {
259 if (typeof opts === 'string')
260 opts = { root: opts };
261 if (isConfig(opts))
262 return opts;
263 let config = new Config(opts);
264 await config.load();
265 return config;
266}
267exports.load = load;
268function isConfig(o) {
269 return o && !!o._base;
270}