UNPKG

5.29 kBJavaScriptView Raw
1/**
2 * @fileoverview Plugins manager
3 * @author Nicholas C. Zakas
4 */
5"use strict";
6
7//------------------------------------------------------------------------------
8// Requirements
9//------------------------------------------------------------------------------
10
11const debug = require("debug")("eslint:plugins");
12const naming = require("../util/naming");
13
14//------------------------------------------------------------------------------
15// Private
16//------------------------------------------------------------------------------
17
18const PLUGIN_NAME_PREFIX = "eslint-plugin-";
19
20//------------------------------------------------------------------------------
21// Public Interface
22//------------------------------------------------------------------------------
23
24/**
25 * Plugin class
26 */
27class Plugins {
28
29 /**
30 * Creates the plugins context
31 * @param {Environments} envContext - env context
32 * @param {Rules} rulesContext - rules context
33 */
34 constructor(envContext, rulesContext) {
35 this._plugins = Object.create(null);
36 this._environments = envContext;
37 this._rules = rulesContext;
38 }
39
40 /**
41 * Defines a plugin with a given name rather than loading from disk.
42 * @param {string} pluginName The name of the plugin to load.
43 * @param {Object} plugin The plugin object.
44 * @returns {void}
45 */
46 define(pluginName, plugin) {
47 const pluginNamespace = naming.getNamespaceFromTerm(pluginName),
48 pluginNameWithoutNamespace = naming.removeNamespaceFromTerm(pluginName),
49 pluginNameWithoutPrefix = naming.removePrefixFromTerm(PLUGIN_NAME_PREFIX, pluginNameWithoutNamespace),
50 shortName = pluginNamespace + pluginNameWithoutPrefix;
51
52 // load up environments and rules
53 this._plugins[shortName] = plugin;
54 this._environments.importPlugin(plugin, shortName);
55 this._rules.importPlugin(plugin, shortName);
56 }
57
58 /**
59 * Gets a plugin with the given name.
60 * @param {string} pluginName The name of the plugin to retrieve.
61 * @returns {Object} The plugin or null if not loaded.
62 */
63 get(pluginName) {
64 return this._plugins[pluginName] || null;
65 }
66
67 /**
68 * Returns all plugins that are loaded.
69 * @returns {Object} The plugins cache.
70 */
71 getAll() {
72 return this._plugins;
73 }
74
75 /**
76 * Loads a plugin with the given name.
77 * @param {string} pluginName The name of the plugin to load.
78 * @returns {void}
79 * @throws {Error} If the plugin cannot be loaded.
80 */
81 load(pluginName) {
82 const pluginNamespace = naming.getNamespaceFromTerm(pluginName),
83 pluginNameWithoutNamespace = naming.removeNamespaceFromTerm(pluginName),
84 pluginNameWithoutPrefix = naming.removePrefixFromTerm(PLUGIN_NAME_PREFIX, pluginNameWithoutNamespace),
85 shortName = pluginNamespace + pluginNameWithoutPrefix,
86 longName = pluginNamespace + PLUGIN_NAME_PREFIX + pluginNameWithoutPrefix;
87 let plugin = null;
88
89 if (pluginName.match(/\s+/)) {
90 const whitespaceError = new Error(`Whitespace found in plugin name '${pluginName}'`);
91
92 whitespaceError.messageTemplate = "whitespace-found";
93 whitespaceError.messageData = {
94 pluginName: longName
95 };
96 throw whitespaceError;
97 }
98
99 if (!this._plugins[shortName]) {
100 try {
101 plugin = require(longName);
102 } catch (pluginLoadErr) {
103 try {
104
105 // Check whether the plugin exists
106 require.resolve(longName);
107 } catch (missingPluginErr) {
108
109 // If the plugin can't be resolved, display the missing plugin error (usually a config or install error)
110 debug(`Failed to load plugin ${longName}.`);
111 missingPluginErr.message = `Failed to load plugin ${pluginName}: ${missingPluginErr.message}`;
112 missingPluginErr.messageTemplate = "plugin-missing";
113 missingPluginErr.messageData = {
114 pluginName: longName
115 };
116 throw missingPluginErr;
117 }
118
119 // Otherwise, the plugin exists and is throwing on module load for some reason, so print the stack trace.
120 throw pluginLoadErr;
121 }
122
123 this.define(pluginName, plugin);
124 }
125 }
126
127 /**
128 * Loads all plugins from an array.
129 * @param {string[]} pluginNames An array of plugins names.
130 * @returns {void}
131 * @throws {Error} If a plugin cannot be loaded.
132 * @throws {Error} If "plugins" in config is not an array
133 */
134 loadAll(pluginNames) {
135
136 // if "plugins" in config is not an array, throw an error so user can fix their config.
137 if (!Array.isArray(pluginNames)) {
138 const pluginNotArrayMessage = "ESLint configuration error: \"plugins\" value must be an array";
139
140 debug(`${pluginNotArrayMessage}: ${JSON.stringify(pluginNames)}`);
141
142 throw new Error(pluginNotArrayMessage);
143 }
144
145 // load each plugin by name
146 pluginNames.forEach(this.load, this);
147 }
148}
149
150module.exports = Plugins;