@mimik/eslint-plugin-logger 1.0.0 → 1.0.2

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.
Files changed (3) hide show
  1. package/README.md +43 -0
  2. package/index.js +40 -5
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -31,9 +31,25 @@ Validates that logger calls (on default-imported modules) use the correct argume
31
31
 
32
32
  ### Valid Signatures
33
33
 
34
+ All three access patterns are supported:
35
+
34
36
  ```js
35
37
  import logger from '@mimik/sumologic-winston-logger';
36
38
 
39
+ // Dot notation
40
+ logger.info('User logged in', correlationId);
41
+
42
+ // Bracket notation with string literal
43
+ logger['info']('User logged in', correlationId);
44
+
45
+ // Bracket notation with variable (any variable name)
46
+ logger[level]('User logged in', correlationId);
47
+ logger[severity]('User logged in', correlationId);
48
+ ```
49
+
50
+ The four argument signatures:
51
+
52
+ ```js
37
53
  // (message, correlationId)
38
54
  logger.info('User logged in', correlationId);
39
55
 
@@ -64,10 +80,14 @@ logger.info('User logged in', { userId: 123 }, correlationId, { type: 'audit' })
64
80
 
65
81
  - The rule only applies to **default imports** (e.g. `import logger from '...'`)
66
82
  - Named imports (`import { logger } from '...'`) are ignored
83
+ - Supports dot notation (`logger.info`), bracket notation with a string literal (`logger['info']`), and bracket notation with a variable (`logger[level]`)
84
+ - For bracket notation with a string literal, the value must be a recognised log level
85
+ - For bracket notation with a variable, the call is always validated (the variable is assumed to hold a valid level)
67
86
  - Minimum 2 arguments, maximum 4
68
87
  - `message` must be a string (when passed as a literal)
69
88
  - `correlationId` must be a string (when passed as a literal)
70
89
  - `options` object must contain a `type` property with a string value
90
+ - When 2 arguments are passed and the 2nd is an object literal, it is treated as `(message, meta)` with a missing `correlationId`
71
91
  - When 3 arguments are passed, the 2nd argument type determines the signature:
72
92
  - Object literal → `(message, meta, correlationId)`
73
93
  - Otherwise → `(message, correlationId, options)`
@@ -104,6 +124,13 @@ Reports an error when the options object is missing a <code>type</code> property
104
124
  <code>type</code> value is a non-string literal. Variable references (e.g. <code>{ type: someVar }</code>)
105
125
  are allowed because their runtime type cannot be determined statically.</p>
106
126
  </dd>
127
+ <dt><a href="#resolveLevel">resolveLevel(callee)</a> ⇒ <code>string</code> | <code>null</code></dt>
128
+ <dd><p>Resolve the log level from a MemberExpression callee.
129
+ Supports dot notation (<code>logger.info</code>), bracket notation with a string literal
130
+ (<code>logger[&#39;info&#39;]</code>), and bracket notation with a variable (<code>logger[level]</code>).
131
+ For string literals the value must be a recognised level; variables are assumed
132
+ to hold a valid level and the variable name is returned for error messages.</p>
133
+ </dd>
107
134
  <dt><a href="#validateStringArg">validateStringArg(context, argNode, level, messageId)</a></dt>
108
135
  <dd><p>Validate that a logger argument is a string when it is a literal.
109
136
  Non-literal nodes (identifiers, call expressions) are skipped because their
@@ -182,6 +209,22 @@ are allowed because their runtime type cannot be determined statically.
182
209
  | optionsArg | <code>Object</code> | The options argument AST node. |
183
210
  | level | <code>string</code> | The logger level (e.g. 'info', 'error'). |
184
211
 
212
+ <a name="resolveLevel"></a>
213
+
214
+ ## resolveLevel(callee) ⇒ <code>string</code> \| <code>null</code>
215
+ Resolve the log level from a MemberExpression callee.
216
+ Supports dot notation (`logger.info`), bracket notation with a string literal
217
+ (`logger['info']`), and bracket notation with a variable (`logger[level]`).
218
+ For string literals the value must be a recognised level; variables are assumed
219
+ to hold a valid level and the variable name is returned for error messages.
220
+
221
+ **Kind**: global function
222
+ **Returns**: <code>string</code> \| <code>null</code> - The resolved level name, or null when the call should be skipped.
223
+
224
+ | Param | Type | Description |
225
+ | --- | --- | --- |
226
+ | callee | <code>Object</code> | The MemberExpression callee node. |
227
+
185
228
  <a name="validateStringArg"></a>
186
229
 
187
230
  ## validateStringArg(context, argNode, level, messageId)
package/index.js CHANGED
@@ -63,6 +63,31 @@ const validateOptions = (context, optionsArg, level) => {
63
63
  }
64
64
  };
65
65
 
66
+ /**
67
+ * Resolve the log level from a MemberExpression callee.
68
+ * Supports dot notation (`logger.info`), bracket notation with a string literal
69
+ * (`logger['info']`), and bracket notation with a variable (`logger[level]`).
70
+ * For string literals the value must be a recognised level; variables are assumed
71
+ * to hold a valid level and the variable name is returned for error messages.
72
+ * @param {Object} callee - The MemberExpression callee node.
73
+ * @returns {string|null} The resolved level name, or null when the call should be skipped.
74
+ */
75
+ const resolveLevel = (callee) => {
76
+ if (!callee.computed) {
77
+ const level = callee.property.name;
78
+ return LEVELS.has(level) ? level : null;
79
+ }
80
+
81
+ const prop = callee.property;
82
+ if (prop.type === 'Literal' && typeof prop.value === 'string') {
83
+ return LEVELS.has(prop.value) ? prop.value : null;
84
+ }
85
+ if (prop.type === 'Identifier') {
86
+ return prop.name;
87
+ }
88
+ return null;
89
+ };
90
+
66
91
  /**
67
92
  * Validate that a logger argument is a string when it is a literal.
68
93
  * Non-literal nodes (identifiers, call expressions) are skipped because their
@@ -101,6 +126,7 @@ const plugin = {
101
126
  tooManyArguments: 'logger.{{ level }}() accepts at most 4 arguments: (message, meta, correlationId, options). Found {{ count }}.',
102
127
  messageNotString: 'logger.{{ level }}() message (1st argument) must be a string. Found {{ actualType }}.',
103
128
  correlationIdNotString: 'logger.{{ level }}() correlationId must be a string. Found {{ actualType }}.',
129
+ missingCorrelationId: 'logger.{{ level }}() called with (message, meta) but missing required correlationId. Expected (message, meta, correlationId).',
104
130
  optionsMissingType: 'logger.{{ level }}() options object must contain a "type" property.',
105
131
  optionsTypeNotString: 'logger.{{ level }}() options "type" property must be a string.',
106
132
  },
@@ -117,10 +143,9 @@ const plugin = {
117
143
  if (node.callee.type !== 'MemberExpression') return;
118
144
  if (node.callee.object.type !== 'Identifier') return;
119
145
  if (!defaultImports.has(node.callee.object.name)) return;
120
- if (node.callee.computed) return;
121
146
 
122
- const level = node.callee.property.name;
123
- if (!LEVELS.has(level)) return;
147
+ const level = resolveLevel(node.callee);
148
+ if (!level) return;
124
149
 
125
150
  const args = node.arguments;
126
151
 
@@ -146,8 +171,18 @@ const plugin = {
146
171
  validateStringArg(context, args[0], level, 'messageNotString');
147
172
 
148
173
  if (args.length === MIN_ARGS) {
149
- // (message, correlationId)
150
- validateStringArg(context, args[1], level, 'correlationIdNotString');
174
+ if (isObjectNode(args[1]) || args[1].type === 'ArrayExpression') {
175
+ // (message, {object}) or (message, [array]) missing correlationId
176
+ context.report({
177
+ node,
178
+ messageId: 'missingCorrelationId',
179
+ data: { level },
180
+ });
181
+ }
182
+ else {
183
+ // (message, correlationId)
184
+ validateStringArg(context, args[1], level, 'correlationIdNotString');
185
+ }
151
186
  }
152
187
  else if (args.length === THREE_ARGS) {
153
188
  // Disambiguate: (message, meta, correlationId) vs (message, correlationId, options)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mimik/eslint-plugin-logger",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "validation of the logger parameters for mimik",
5
5
  "main": "./index.js",
6
6
  "type": "module",