eslint 9.28.0 → 9.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @fileoverview SourceCodeVisitor class
3
+ * @author Nicholas C. Zakas
4
+ */
5
+
6
+ "use strict";
7
+
8
+ //-----------------------------------------------------------------------------
9
+ // Helpers
10
+ //-----------------------------------------------------------------------------
11
+
12
+ const emptyArray = Object.freeze([]);
13
+
14
+ //------------------------------------------------------------------------------
15
+ // Exports
16
+ //------------------------------------------------------------------------------
17
+
18
+ /**
19
+ * A structure to hold a list of functions to call for a given name.
20
+ * This is used to allow multiple rules to register functions for a given name
21
+ * without having to know about each other.
22
+ */
23
+ class SourceCodeVisitor {
24
+ /**
25
+ * The functions to call for a given name.
26
+ * @type {Map<string, Function[]>}
27
+ */
28
+ #functions = new Map();
29
+
30
+ /**
31
+ * Adds a function to the list of functions to call for a given name.
32
+ * @param {string} name The name of the function to call.
33
+ * @param {Function} func The function to call.
34
+ * @returns {void}
35
+ */
36
+ add(name, func) {
37
+ if (this.#functions.has(name)) {
38
+ this.#functions.get(name).push(func);
39
+ } else {
40
+ this.#functions.set(name, [func]);
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Gets the list of functions to call for a given name.
46
+ * @param {string} name The name of the function to call.
47
+ * @returns {Function[]} The list of functions to call.
48
+ */
49
+ get(name) {
50
+ if (this.#functions.has(name)) {
51
+ return this.#functions.get(name);
52
+ }
53
+
54
+ return emptyArray;
55
+ }
56
+
57
+ /**
58
+ * Iterates over all names and calls the callback with the name.
59
+ * @param {(name:string) => void} callback The callback to call for each name.
60
+ * @returns {void}
61
+ */
62
+ forEachName(callback) {
63
+ this.#functions.forEach((funcs, name) => {
64
+ callback(name);
65
+ });
66
+ }
67
+
68
+ /**
69
+ * Calls the functions for a given name with the given arguments.
70
+ * @param {string} name The name of the function to call.
71
+ * @param {any[]} args The arguments to pass to the function.
72
+ * @returns {void}
73
+ */
74
+ callSync(name, ...args) {
75
+ if (this.#functions.has(name)) {
76
+ this.#functions.get(name).forEach(func => func(...args));
77
+ }
78
+ }
79
+ }
80
+
81
+ module.exports = { SourceCodeVisitor };
@@ -111,6 +111,7 @@ module.exports = {
111
111
  switch (node.type) {
112
112
  case "MethodDefinition":
113
113
  return !node.static && node.kind !== "constructor";
114
+ case "AccessorProperty":
114
115
  case "PropertyDefinition":
115
116
  return !node.static && enforceForClassFields;
116
117
  default:
@@ -218,6 +219,8 @@ module.exports = {
218
219
  /*
219
220
  * Class field value are implicit functions.
220
221
  */
222
+ "AccessorProperty > *.key:exit": pushContext,
223
+ "AccessorProperty:exit": popContext,
221
224
  "PropertyDefinition > *.key:exit": pushContext,
222
225
  "PropertyDefinition:exit": popContext,
223
226
 
@@ -233,6 +236,10 @@ module.exports = {
233
236
  ThisExpression: markThisUsed,
234
237
  Super: markThisUsed,
235
238
  ...(enforceForClassFields && {
239
+ "AccessorProperty > ArrowFunctionExpression.value":
240
+ enterFunction,
241
+ "AccessorProperty > ArrowFunctionExpression.value:exit":
242
+ exitFunction,
236
243
  "PropertyDefinition > ArrowFunctionExpression.value":
237
244
  enterFunction,
238
245
  "PropertyDefinition > ArrowFunctionExpression.value:exit":
@@ -9,7 +9,6 @@
9
9
  // Requirements
10
10
  //------------------------------------------------------------------------------
11
11
 
12
- const { findVariable } = require("@eslint-community/eslint-utils");
13
12
  const astUtils = require("./utils/ast-utils");
14
13
 
15
14
  //------------------------------------------------------------------------------
@@ -21,43 +20,13 @@ const functionTypesToCheck = new Set([
21
20
  "FunctionExpression",
22
21
  ]);
23
22
 
24
- /**
25
- * Determines whether the given identifier node is a reference to a global variable.
26
- * @param {ASTNode} node `Identifier` node to check.
27
- * @param {Scope} scope Scope to which the node belongs.
28
- * @returns {boolean} True if the identifier is a reference to a global variable.
29
- */
30
- function isGlobalReference(node, scope) {
31
- const variable = findVariable(scope, node);
32
-
33
- return (
34
- variable !== null &&
35
- variable.scope.type === "global" &&
36
- variable.defs.length === 0
37
- );
38
- }
39
-
40
- /**
41
- * Finds function's outer scope.
42
- * @param {Scope} scope Function's own scope.
43
- * @returns {Scope} Function's outer scope.
44
- */
45
- function getOuterScope(scope) {
46
- const upper = scope.upper;
47
-
48
- if (upper.type === "function-expression-name") {
49
- return upper.upper;
50
- }
51
- return upper;
52
- }
53
-
54
23
  /**
55
24
  * Determines whether the given function node is used as a Promise executor.
56
25
  * @param {ASTNode} node The node to check.
57
- * @param {Scope} scope Function's own scope.
26
+ * @param {SourceCode} sourceCode Source code to which the node belongs.
58
27
  * @returns {boolean} `true` if the node is a Promise executor.
59
28
  */
60
- function isPromiseExecutor(node, scope) {
29
+ function isPromiseExecutor(node, sourceCode) {
61
30
  const parent = node.parent;
62
31
 
63
32
  return (
@@ -65,7 +34,7 @@ function isPromiseExecutor(node, scope) {
65
34
  parent.arguments[0] === node &&
66
35
  parent.callee.type === "Identifier" &&
67
36
  parent.callee.name === "Promise" &&
68
- isGlobalReference(parent.callee, getOuterScope(scope))
37
+ sourceCode.isGlobalReference(parent.callee)
69
38
  );
70
39
  }
71
40
 
@@ -203,7 +172,7 @@ module.exports = {
203
172
  upper: funcInfo,
204
173
  shouldCheck:
205
174
  functionTypesToCheck.has(node.type) &&
206
- isPromiseExecutor(node, sourceCode.getScope(node)),
175
+ isPromiseExecutor(node, sourceCode),
207
176
  };
208
177
 
209
178
  if (
@@ -4,6 +4,18 @@
4
4
  */
5
5
  "use strict";
6
6
 
7
+ //------------------------------------------------------------------------------
8
+ // Helpers
9
+ //------------------------------------------------------------------------------
10
+
11
+ const TYPE_NODES = new Set([
12
+ "TSTypeReference",
13
+ "TSInterfaceHeritage",
14
+ "TSClassImplements",
15
+ "TSTypeQuery",
16
+ "TSQualifiedName",
17
+ ]);
18
+
7
19
  //------------------------------------------------------------------------------
8
20
  // Rule Definition
9
21
  //------------------------------------------------------------------------------
@@ -11,6 +23,8 @@
11
23
  /** @type {import('../types').Rule.RuleModule} */
12
24
  module.exports = {
13
25
  meta: {
26
+ dialects: ["javascript", "typescript"],
27
+ language: "javascript",
14
28
  type: "suggestion",
15
29
 
16
30
  docs: {
@@ -100,6 +114,18 @@ module.exports = {
100
114
  return Object.hasOwn(restrictedGlobalMessages, name);
101
115
  }
102
116
 
117
+ /**
118
+ * Check if the given reference occurs within a TypeScript type context.
119
+ * @param {Reference} reference The variable reference to check.
120
+ * @returns {boolean} Whether the reference is in a type context.
121
+ * @private
122
+ */
123
+ function isInTypeContext(reference) {
124
+ const parent = reference.identifier.parent;
125
+
126
+ return TYPE_NODES.has(parent.type);
127
+ }
128
+
103
129
  return {
104
130
  Program(node) {
105
131
  const scope = sourceCode.getScope(node);
@@ -107,13 +133,20 @@ module.exports = {
107
133
  // Report variables declared elsewhere (ex: variables defined as "global" by eslint)
108
134
  scope.variables.forEach(variable => {
109
135
  if (!variable.defs.length && isRestricted(variable.name)) {
110
- variable.references.forEach(reportReference);
136
+ variable.references.forEach(reference => {
137
+ if (!isInTypeContext(reference)) {
138
+ reportReference(reference);
139
+ }
140
+ });
111
141
  }
112
142
  });
113
143
 
114
144
  // Report variables not declared at all
115
145
  scope.through.forEach(reference => {
116
- if (isRestricted(reference.identifier.name)) {
146
+ if (
147
+ isRestricted(reference.identifier.name) &&
148
+ !isInTypeContext(reference)
149
+ ) {
117
150
  reportReference(reference);
118
151
  }
119
152
  });
@@ -40,6 +40,13 @@ module.exports = {
40
40
  },
41
41
  uniqueItems: true,
42
42
  },
43
+ allowProperties: {
44
+ type: "array",
45
+ items: {
46
+ type: "string",
47
+ },
48
+ uniqueItems: true,
49
+ },
43
50
  message: {
44
51
  type: "string",
45
52
  },
@@ -53,7 +60,10 @@ module.exports = {
53
60
  },
54
61
  ],
55
62
  not: {
56
- required: ["allowObjects", "object"],
63
+ anyOf: [
64
+ { required: ["allowObjects", "object"] },
65
+ { required: ["allowProperties", "property"] },
66
+ ],
57
67
  },
58
68
  additionalProperties: false,
59
69
  },
@@ -92,6 +102,7 @@ module.exports = {
92
102
  });
93
103
  } else if (typeof propertyName === "undefined") {
94
104
  globallyRestrictedObjects.set(objectName, {
105
+ allowProperties: option.allowProperties,
95
106
  message: option.message,
96
107
  });
97
108
  } else {
@@ -106,17 +117,17 @@ module.exports = {
106
117
  });
107
118
 
108
119
  /**
109
- * Checks if an object name is in the allowed objects list.
110
- * @param {string} objectName The name of the object to check
111
- * @param {string[]} [allowObjects] The list of objects to allow
112
- * @returns {boolean} True if the object is allowed, false otherwise
120
+ * Checks if a name is in the allowed list.
121
+ * @param {string} name The name to check
122
+ * @param {string[]} [allowedList] The list of allowed names
123
+ * @returns {boolean} True if the name is allowed, false otherwise
113
124
  */
114
- function isAllowedObject(objectName, allowObjects) {
115
- if (!allowObjects) {
125
+ function isAllowed(name, allowedList) {
126
+ if (!allowedList) {
116
127
  return false;
117
128
  }
118
129
 
119
- return allowObjects.includes(objectName);
130
+ return allowedList.includes(name);
120
131
  }
121
132
 
122
133
  /**
@@ -137,7 +148,10 @@ module.exports = {
137
148
  const globalMatchedProperty =
138
149
  globallyRestrictedProperties.get(propertyName);
139
150
 
140
- if (matchedObjectProperty) {
151
+ if (
152
+ matchedObjectProperty &&
153
+ !isAllowed(propertyName, matchedObjectProperty.allowProperties)
154
+ ) {
141
155
  const message = matchedObjectProperty.message
142
156
  ? ` ${matchedObjectProperty.message}`
143
157
  : "";
@@ -153,7 +167,7 @@ module.exports = {
153
167
  });
154
168
  } else if (
155
169
  globalMatchedProperty &&
156
- !isAllowedObject(objectName, globalMatchedProperty.allowObjects)
170
+ !isAllowed(objectName, globalMatchedProperty.allowObjects)
157
171
  ) {
158
172
  const message = globalMatchedProperty.message
159
173
  ? ` ${globalMatchedProperty.message}`
@@ -10,33 +10,16 @@
10
10
  //------------------------------------------------------------------------------
11
11
 
12
12
  const astUtils = require("./utils/ast-utils");
13
- const { findVariable } = require("@eslint-community/eslint-utils");
14
13
 
15
14
  //------------------------------------------------------------------------------
16
15
  // Helpers
17
16
  //------------------------------------------------------------------------------
18
17
 
19
- /**
20
- * Determines whether the given identifier node is a reference to a global variable.
21
- * @param {ASTNode} node `Identifier` node to check.
22
- * @param {Scope} scope Scope to which the node belongs.
23
- * @returns {boolean} True if the identifier is a reference to a global variable.
24
- */
25
- function isGlobalReference(node, scope) {
26
- const variable = findVariable(scope, node);
27
-
28
- return (
29
- variable !== null &&
30
- variable.scope.type === "global" &&
31
- variable.defs.length === 0
32
- );
33
- }
34
-
35
18
  /**
36
19
  * Determines whether the given node is an argument of the specified global method call, at the given `index` position.
37
20
  * E.g., for given `index === 1`, this function checks for `objectName.methodName(foo, node)`, where objectName is a global variable.
38
21
  * @param {ASTNode} node The node to check.
39
- * @param {Scope} scope Scope to which the node belongs.
22
+ * @param {SourceCode} sourceCode Source code to which the node belongs.
40
23
  * @param {string} objectName Name of the global object.
41
24
  * @param {string} methodName Name of the method.
42
25
  * @param {number} index The given position.
@@ -44,7 +27,7 @@ function isGlobalReference(node, scope) {
44
27
  */
45
28
  function isArgumentOfGlobalMethodCall(
46
29
  node,
47
- scope,
30
+ sourceCode,
48
31
  objectName,
49
32
  methodName,
50
33
  index,
@@ -59,9 +42,8 @@ function isArgumentOfGlobalMethodCall(
59
42
  objectName,
60
43
  methodName,
61
44
  ) &&
62
- isGlobalReference(
45
+ sourceCode.isGlobalReference(
63
46
  astUtils.skipChainExpression(callNode.callee).object,
64
- scope,
65
47
  )
66
48
  );
67
49
  }
@@ -69,21 +51,21 @@ function isArgumentOfGlobalMethodCall(
69
51
  /**
70
52
  * Determines whether the given node is used as a property descriptor.
71
53
  * @param {ASTNode} node The node to check.
72
- * @param {Scope} scope Scope to which the node belongs.
54
+ * @param {SourceCode} sourceCode Source code to which the node belongs.
73
55
  * @returns {boolean} `true` if the node is a property descriptor.
74
56
  */
75
- function isPropertyDescriptor(node, scope) {
57
+ function isPropertyDescriptor(node, sourceCode) {
76
58
  if (
77
59
  isArgumentOfGlobalMethodCall(
78
60
  node,
79
- scope,
61
+ sourceCode,
80
62
  "Object",
81
63
  "defineProperty",
82
64
  2,
83
65
  ) ||
84
66
  isArgumentOfGlobalMethodCall(
85
67
  node,
86
- scope,
68
+ sourceCode,
87
69
  "Reflect",
88
70
  "defineProperty",
89
71
  2,
@@ -101,14 +83,14 @@ function isPropertyDescriptor(node, scope) {
101
83
  grandparent.type === "ObjectExpression" &&
102
84
  (isArgumentOfGlobalMethodCall(
103
85
  grandparent,
104
- scope,
86
+ sourceCode,
105
87
  "Object",
106
88
  "create",
107
89
  1,
108
90
  ) ||
109
91
  isArgumentOfGlobalMethodCall(
110
92
  grandparent,
111
- scope,
93
+ sourceCode,
112
94
  "Object",
113
95
  "defineProperties",
114
96
  1,
@@ -124,10 +106,10 @@ function isPropertyDescriptor(node, scope) {
124
106
  /**
125
107
  * Determines whether the given function node is used as a setter function.
126
108
  * @param {ASTNode} node The node to check.
127
- * @param {Scope} scope Scope to which the node belongs.
109
+ * @param {SourceCode} sourceCode Source code to which the node belongs.
128
110
  * @returns {boolean} `true` if the node is a setter.
129
111
  */
130
- function isSetter(node, scope) {
112
+ function isSetter(node, sourceCode) {
131
113
  const parent = node.parent;
132
114
 
133
115
  if (
@@ -144,7 +126,7 @@ function isSetter(node, scope) {
144
126
  parent.value === node &&
145
127
  astUtils.getStaticPropertyName(parent) === "set" &&
146
128
  parent.parent.type === "ObjectExpression" &&
147
- isPropertyDescriptor(parent.parent, scope)
129
+ isPropertyDescriptor(parent.parent, sourceCode)
148
130
  ) {
149
131
  // Setter in a property descriptor
150
132
  return true;
@@ -153,21 +135,6 @@ function isSetter(node, scope) {
153
135
  return false;
154
136
  }
155
137
 
156
- /**
157
- * Finds function's outer scope.
158
- * @param {Scope} scope Function's own scope.
159
- * @returns {Scope} Function's outer scope.
160
- */
161
- function getOuterScope(scope) {
162
- const upper = scope.upper;
163
-
164
- if (upper.type === "function-expression-name") {
165
- return upper.upper;
166
- }
167
-
168
- return upper;
169
- }
170
-
171
138
  //------------------------------------------------------------------------------
172
139
  // Rule Definition
173
140
  //------------------------------------------------------------------------------
@@ -200,11 +167,9 @@ module.exports = {
200
167
  * @returns {void}
201
168
  */
202
169
  function enterFunction(node) {
203
- const outerScope = getOuterScope(sourceCode.getScope(node));
204
-
205
170
  funcInfo = {
206
171
  upper: funcInfo,
207
- isSetter: isSetter(node, outerScope),
172
+ isSetter: isSetter(node, sourceCode),
208
173
  };
209
174
  }
210
175
 
@@ -269,6 +269,8 @@ function isClassRefInClassDecorator(variable, reference) {
269
269
  /** @type {import('../types').Rule.RuleModule} */
270
270
  module.exports = {
271
271
  meta: {
272
+ dialects: ["javascript", "typescript"],
273
+ language: "javascript",
272
274
  type: "problem",
273
275
 
274
276
  docs: {
@@ -199,6 +199,8 @@ function hasNameDisallowedForLetDeclarations(variable) {
199
199
  module.exports = {
200
200
  meta: {
201
201
  type: "suggestion",
202
+ dialects: ["typescript", "javascript"],
203
+ language: "javascript",
202
204
 
203
205
  docs: {
204
206
  description: "Require `let` or `const` instead of `var`",
@@ -346,9 +348,19 @@ module.exports = {
346
348
 
347
349
  return {
348
350
  "VariableDeclaration:exit"(node) {
349
- if (node.kind === "var") {
350
- report(node);
351
+ if (node.kind !== "var") {
352
+ return;
351
353
  }
354
+
355
+ if (
356
+ node.parent.type === "TSModuleBlock" &&
357
+ node.parent.parent.type === "TSModuleDeclaration" &&
358
+ node.parent.parent.global
359
+ ) {
360
+ return;
361
+ }
362
+
363
+ report(node);
352
364
  },
353
365
  };
354
366
  },
@@ -14,7 +14,6 @@ const {
14
14
  CALL,
15
15
  CONSTRUCT,
16
16
  ReferenceTracker,
17
- findVariable,
18
17
  } = require("@eslint-community/eslint-utils");
19
18
  const {
20
19
  RegExpValidator,
@@ -167,22 +166,6 @@ module.exports = {
167
166
  const [{ disallowRedundantWrapping }] = context.options;
168
167
  const sourceCode = context.sourceCode;
169
168
 
170
- /**
171
- * Determines whether the given identifier node is a reference to a global variable.
172
- * @param {ASTNode} node `Identifier` node to check.
173
- * @returns {boolean} True if the identifier is a reference to a global variable.
174
- */
175
- function isGlobalReference(node) {
176
- const scope = sourceCode.getScope(node);
177
- const variable = findVariable(scope, node);
178
-
179
- return (
180
- variable !== null &&
181
- variable.scope.type === "global" &&
182
- variable.defs.length === 0
183
- );
184
- }
185
-
186
169
  /**
187
170
  * Determines whether the given node is a String.raw`` tagged template expression
188
171
  * with a static template literal.
@@ -193,7 +176,7 @@ module.exports = {
193
176
  return (
194
177
  node.type === "TaggedTemplateExpression" &&
195
178
  astUtils.isSpecificMemberAccess(node.tag, "String", "raw") &&
196
- isGlobalReference(
179
+ sourceCode.isGlobalReference(
197
180
  astUtils.skipChainExpression(node.tag).object,
198
181
  ) &&
199
182
  astUtils.isStaticTemplateLiteral(node.quasi)
@@ -112,6 +112,14 @@ class SuppressionsService {
112
112
  }
113
113
  }
114
114
 
115
+ for (const file of Object.keys(suppressions)) {
116
+ const absolutePath = path.resolve(this.cwd, file);
117
+
118
+ if (!fs.existsSync(absolutePath)) {
119
+ delete suppressions[file];
120
+ }
121
+ }
122
+
115
123
  return this.save(suppressions);
116
124
  }
117
125
 
@@ -0,0 +1,109 @@
1
+ /**
2
+ * @fileoverview Common helpers for naming of plugins, formatters and configs
3
+ */
4
+
5
+ "use strict";
6
+
7
+ const NAMESPACE_REGEX = /^@.*\//iu;
8
+
9
+ /**
10
+ * Brings package name to correct format based on prefix
11
+ * @param {string} name The name of the package.
12
+ * @param {string} prefix Can be either "eslint-plugin", "eslint-config" or "eslint-formatter"
13
+ * @returns {string} Normalized name of the package
14
+ * @private
15
+ */
16
+ function normalizePackageName(name, prefix) {
17
+ let normalizedName = name;
18
+
19
+ /**
20
+ * On Windows, name can come in with Windows slashes instead of Unix slashes.
21
+ * Normalize to Unix first to avoid errors later on.
22
+ * https://github.com/eslint/eslint/issues/5644
23
+ */
24
+ if (normalizedName.includes("\\")) {
25
+ normalizedName = normalizedName.replace(/\\/gu, "/");
26
+ }
27
+
28
+ if (normalizedName.charAt(0) === "@") {
29
+ /**
30
+ * it's a scoped package
31
+ * package name is the prefix, or just a username
32
+ */
33
+ const scopedPackageShortcutRegex = new RegExp(
34
+ `^(@[^/]+)(?:/(?:${prefix})?)?$`,
35
+ "u",
36
+ ),
37
+ scopedPackageNameRegex = new RegExp(`^${prefix}(-|$)`, "u");
38
+
39
+ if (scopedPackageShortcutRegex.test(normalizedName)) {
40
+ normalizedName = normalizedName.replace(
41
+ scopedPackageShortcutRegex,
42
+ `$1/${prefix}`,
43
+ );
44
+ } else if (!scopedPackageNameRegex.test(normalizedName.split("/")[1])) {
45
+ /**
46
+ * for scoped packages, insert the prefix after the first / unless
47
+ * the path is already @scope/eslint or @scope/eslint-xxx-yyy
48
+ */
49
+ normalizedName = normalizedName.replace(
50
+ /^@([^/]+)\/(.*)$/u,
51
+ `@$1/${prefix}-$2`,
52
+ );
53
+ }
54
+ } else if (!normalizedName.startsWith(`${prefix}-`)) {
55
+ normalizedName = `${prefix}-${normalizedName}`;
56
+ }
57
+
58
+ return normalizedName;
59
+ }
60
+
61
+ /**
62
+ * Removes the prefix from a fullname.
63
+ * @param {string} fullname The term which may have the prefix.
64
+ * @param {string} prefix The prefix to remove.
65
+ * @returns {string} The term without prefix.
66
+ */
67
+ function getShorthandName(fullname, prefix) {
68
+ if (fullname[0] === "@") {
69
+ let matchResult = new RegExp(`^(@[^/]+)/${prefix}$`, "u").exec(
70
+ fullname,
71
+ );
72
+
73
+ if (matchResult) {
74
+ return matchResult[1];
75
+ }
76
+
77
+ matchResult = new RegExp(`^(@[^/]+)/${prefix}-(.+)$`, "u").exec(
78
+ fullname,
79
+ );
80
+ if (matchResult) {
81
+ return `${matchResult[1]}/${matchResult[2]}`;
82
+ }
83
+ } else if (fullname.startsWith(`${prefix}-`)) {
84
+ return fullname.slice(prefix.length + 1);
85
+ }
86
+
87
+ return fullname;
88
+ }
89
+
90
+ /**
91
+ * Gets the scope (namespace) of a term.
92
+ * @param {string} term The term which may have the namespace.
93
+ * @returns {string} The namespace of the term if it has one.
94
+ */
95
+ function getNamespaceFromTerm(term) {
96
+ const match = term.match(NAMESPACE_REGEX);
97
+
98
+ return match ? match[0] : "";
99
+ }
100
+
101
+ //------------------------------------------------------------------------------
102
+ // Public Interface
103
+ //------------------------------------------------------------------------------
104
+
105
+ module.exports = {
106
+ normalizePackageName,
107
+ getShorthandName,
108
+ getNamespaceFromTerm,
109
+ };