UNPKG

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