@techsquidtv/eslint-plugin-structured-logging 0.1.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.
- package/LICENSE.md +20 -0
- package/README.md +167 -0
- package/docs/rules/require-logger-inline-attributes.md +22 -0
- package/docs/rules/require-logger-message.md +22 -0
- package/docs/rules/require-logger-primitive-attributes.md +23 -0
- package/docs/rules/require-logger-scoped-dot-notation.md +31 -0
- package/lib/index.d.ts +31 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +55 -0
- package/lib/index.js.map +1 -0
- package/lib/package.js +6 -0
- package/lib/package.js.map +1 -0
- package/lib/rules/index.d.ts +5 -0
- package/lib/rules/index.js +5 -0
- package/lib/rules/logger/require-logger-inline-attributes.d.ts +13 -0
- package/lib/rules/logger/require-logger-inline-attributes.d.ts.map +1 -0
- package/lib/rules/logger/require-logger-inline-attributes.js +46 -0
- package/lib/rules/logger/require-logger-inline-attributes.js.map +1 -0
- package/lib/rules/logger/require-logger-message.d.ts +13 -0
- package/lib/rules/logger/require-logger-message.d.ts.map +1 -0
- package/lib/rules/logger/require-logger-message.js +46 -0
- package/lib/rules/logger/require-logger-message.js.map +1 -0
- package/lib/rules/logger/require-logger-primitive-attributes.d.ts +20 -0
- package/lib/rules/logger/require-logger-primitive-attributes.d.ts.map +1 -0
- package/lib/rules/logger/require-logger-primitive-attributes.js +48 -0
- package/lib/rules/logger/require-logger-primitive-attributes.js.map +1 -0
- package/lib/rules/logger/require-logger-scoped-dot-notation.d.ts +21 -0
- package/lib/rules/logger/require-logger-scoped-dot-notation.d.ts.map +1 -0
- package/lib/rules/logger/require-logger-scoped-dot-notation.js +111 -0
- package/lib/rules/logger/require-logger-scoped-dot-notation.js.map +1 -0
- package/lib/rules/logger/utils.d.ts +156 -0
- package/lib/rules/logger/utils.d.ts.map +1 -0
- package/lib/rules/logger/utils.js +271 -0
- package/lib/rules/logger/utils.js.map +1 -0
- package/lib/utils.d.ts +17 -0
- package/lib/utils.d.ts.map +1 -0
- package/lib/utils.js +7 -0
- package/lib/utils.js.map +1 -0
- package/package.json +80 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
'Software'), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
17
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
18
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
19
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
20
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
<h1 align="center">Structured Logging ESLint Plugin</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">Library-agnostic ESLint rules for writing safer, more consistent structured logs in TypeScript.</p>
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Install the plugin alongside ESLint, TypeScript, and `@typescript-eslint/parser`:
|
|
8
|
+
|
|
9
|
+
```shell
|
|
10
|
+
pnpm add -D @techsquidtv/eslint-plugin-structured-logging eslint typescript @typescript-eslint/parser
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```shell
|
|
14
|
+
npm install --save-dev @techsquidtv/eslint-plugin-structured-logging eslint typescript @typescript-eslint/parser
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```shell
|
|
18
|
+
yarn add --dev @techsquidtv/eslint-plugin-structured-logging eslint typescript @typescript-eslint/parser
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
This package requires Node.js 20.19 or newer, ESLint 9 or newer, and TypeScript 5 or newer.
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
The plugin exports flat ESLint configs. Add one to your `eslint.config.js` or `eslint.config.mjs`:
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
import structuredLogging from "@techsquidtv/eslint-plugin-structured-logging";
|
|
29
|
+
|
|
30
|
+
export default [structuredLogging.configs.recommended];
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
By default the rules match calls on a `logger` or `log` identifier (for example
|
|
34
|
+
`logger.info(...)`). Point them at whatever logging API your codebase uses via
|
|
35
|
+
the shared rule options — see [Rule Options](#rule-options).
|
|
36
|
+
|
|
37
|
+
## Compatibility
|
|
38
|
+
|
|
39
|
+
The rules are library-agnostic. Both common argument orders are supported:
|
|
40
|
+
|
|
41
|
+
- **Message-first** — `logger.info("message", { attributes })` (e.g. winston, Sentry)
|
|
42
|
+
- **Attributes-first** — `logger.info({ attributes }, "message")` (e.g. pino, bunyan) via the `attributesFirst*` options
|
|
43
|
+
|
|
44
|
+
Level methods (`trace`, `debug`, `info`, `warn`, `error`, `fatal`) are matched by
|
|
45
|
+
default and are configurable via `levelMethods` for libraries with additional
|
|
46
|
+
levels (for example winston's `http`, `verbose`, and `silly`).
|
|
47
|
+
|
|
48
|
+
## Configs
|
|
49
|
+
|
|
50
|
+
The plugin exports two configs:
|
|
51
|
+
|
|
52
|
+
- `recommended`: Enables the recommended structured-logging rules as warnings.
|
|
53
|
+
- `strict`: Enables the recommended rules as errors and additionally requires logger attribute values to be statically known primitives where possible.
|
|
54
|
+
|
|
55
|
+
Neither config requires type information.
|
|
56
|
+
|
|
57
|
+
## Rules
|
|
58
|
+
|
|
59
|
+
<!-- begin auto-generated rules list -->
|
|
60
|
+
|
|
61
|
+
⚠️ Configurations set to warn in.\
|
|
62
|
+
💼 Configurations enabled in.\
|
|
63
|
+
✅ Set in the `recommended` configuration.\
|
|
64
|
+
🔒 Set in the `strict` configuration.\
|
|
65
|
+
🔧 Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).
|
|
66
|
+
|
|
67
|
+
| Name | Description | ⚠️ | 💼 | 🔧 |
|
|
68
|
+
| :--------------------------------------------------------------------------------------- | :------------------------------------------------------------------------- | :-- | :-- | :-- |
|
|
69
|
+
| [require-logger-inline-attributes](docs/rules/require-logger-inline-attributes.md) | Require logger attributes to be inline object literals with explicit keys. | ✅ | 🔒 | |
|
|
70
|
+
| [require-logger-message](docs/rules/require-logger-message.md) | Require logger calls to include a static text message. | ✅ | 🔒 | |
|
|
71
|
+
| [require-logger-primitive-attributes](docs/rules/require-logger-primitive-attributes.md) | Require logger attribute values to be primitives or arrays of primitives. | ✅ | 🔒 | |
|
|
72
|
+
| [require-logger-scoped-dot-notation](docs/rules/require-logger-scoped-dot-notation.md) | Require logger messages and attribute keys to use dotted snake case. | ✅ | 🔒 | 🔧 |
|
|
73
|
+
|
|
74
|
+
<!-- end auto-generated rules list -->
|
|
75
|
+
|
|
76
|
+
## Examples
|
|
77
|
+
|
|
78
|
+
Prefer scoped event names with safe, searchable attributes:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
logger.info("checkout.completed", {
|
|
82
|
+
"checkout.id": checkoutId,
|
|
83
|
+
"item.count": 3,
|
|
84
|
+
"guest.is": false,
|
|
85
|
+
"tag.names": ["priority", "guest"],
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Avoid dynamic messages, nested data, and non-primitive values:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
logger.info(`Checkout completed for ${email}`, {
|
|
93
|
+
user: { email },
|
|
94
|
+
items: [{ id: 1 }],
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Rule Options
|
|
99
|
+
|
|
100
|
+
The logger rules share options for matching the logging APIs used in your codebase:
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
{
|
|
104
|
+
"@techsquidtv/structured-logging/require-logger-message": [
|
|
105
|
+
"warn",
|
|
106
|
+
{
|
|
107
|
+
allowedLoggerObjects: [],
|
|
108
|
+
allowedLoggerIdentifiers: ["logger", "log"],
|
|
109
|
+
attributesFirstLoggerObjects: [],
|
|
110
|
+
attributesFirstLoggerIdentifiers: [],
|
|
111
|
+
levelMethods: ["trace", "debug", "info", "warn", "error", "fatal"],
|
|
112
|
+
ignoreDynamicLevelMethods: false,
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
"@techsquidtv/structured-logging/require-logger-primitive-attributes": [
|
|
116
|
+
"warn",
|
|
117
|
+
{
|
|
118
|
+
disallowUnknownAttributeValues: false,
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
"@techsquidtv/structured-logging/require-logger-scoped-dot-notation": [
|
|
122
|
+
"warn",
|
|
123
|
+
{
|
|
124
|
+
attributeKeyFormat: "dotted-snake-case",
|
|
125
|
+
messageFormat: "dotted-snake-case",
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
- `allowedLoggerObjects`: Fully-qualified logger object paths that expose logger level methods, such as `log.child`. Defaults to `[]`.
|
|
132
|
+
- `allowedLoggerIdentifiers`: Identifier names that expose logger level methods. Defaults to `["logger", "log"]`.
|
|
133
|
+
- `attributesFirstLoggerObjects`: Object paths whose logger methods receive attributes before the message. Defaults to `[]`.
|
|
134
|
+
- `attributesFirstLoggerIdentifiers`: Identifier names whose logger methods receive attributes before the message. Defaults to `[]`.
|
|
135
|
+
- `levelMethods`: Method names treated as logger level calls. Defaults to `["trace", "debug", "info", "warn", "error", "fatal"]`.
|
|
136
|
+
- `ignoreDynamicLevelMethods`: Skip computed logger level methods such as `logger[level](...)` instead of matching them. Defaults to `false`.
|
|
137
|
+
- `disallowUnknownAttributeValues`: Available on `@techsquidtv/structured-logging/require-logger-primitive-attributes`. Set to `true` to require statically known primitive attribute values. Defaults to `false`.
|
|
138
|
+
- `attributeKeyFormat`: Available on `@techsquidtv/structured-logging/require-logger-scoped-dot-notation`. Set to `"off"` to allow non-dotted attribute keys. Defaults to `"dotted-snake-case"`.
|
|
139
|
+
- `messageFormat`: Available on `@techsquidtv/structured-logging/require-logger-scoped-dot-notation`. Set to `"off"` to allow prose logger messages. Defaults to `"dotted-snake-case"`.
|
|
140
|
+
|
|
141
|
+
### Example: pino / bunyan (attributes-first)
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
{
|
|
145
|
+
"@techsquidtv/structured-logging/require-logger-message": [
|
|
146
|
+
"warn",
|
|
147
|
+
{ attributesFirstLoggerIdentifiers: ["logger", "log"] },
|
|
148
|
+
],
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Example: winston (extra levels)
|
|
153
|
+
|
|
154
|
+
```js
|
|
155
|
+
{
|
|
156
|
+
"@techsquidtv/structured-logging/require-logger-message": [
|
|
157
|
+
"warn",
|
|
158
|
+
{
|
|
159
|
+
levelMethods: ["error", "warn", "info", "http", "verbose", "debug", "silly"],
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Development
|
|
166
|
+
|
|
167
|
+
See [`.github/CONTRIBUTING.md`](.github/CONTRIBUTING.md), then [`.github/DEVELOPMENT.md`](.github/DEVELOPMENT.md).
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# @techsquidtv/structured-logging/require-logger-inline-attributes
|
|
2
|
+
|
|
3
|
+
📝 Require logger attributes to be inline object literals with explicit keys.
|
|
4
|
+
|
|
5
|
+
💼⚠️ This rule is enabled in the 🔒 `strict` config. This rule _warns_ in the ✅ `recommended` config.
|
|
6
|
+
|
|
7
|
+
<!-- end auto-generated rule header -->
|
|
8
|
+
|
|
9
|
+
## Options
|
|
10
|
+
|
|
11
|
+
<!-- begin auto-generated rule options list -->
|
|
12
|
+
|
|
13
|
+
| Name | Description | Type | Default |
|
|
14
|
+
| :--------------------------------- | :---------------------------------------------------------------------------------------- | :------- | :--------------------------------------------------- |
|
|
15
|
+
| `allowedLoggerIdentifiers` | Bare logger object identifiers accepted as logger callees. | String[] | [`logger`, `log`] |
|
|
16
|
+
| `allowedLoggerObjects` | Fully-qualified logger object paths, such as `log.child`. | String[] | `[]` |
|
|
17
|
+
| `attributesFirstLoggerIdentifiers` | Logger identifiers whose calls pass attributes before the message. | String[] | `[]` |
|
|
18
|
+
| `attributesFirstLoggerObjects` | Logger object paths whose calls pass attributes before the message. | String[] | `[]` |
|
|
19
|
+
| `ignoreDynamicLevelMethods` | Skip computed logger level methods such as `logger[level](...)` instead of matching them. | Boolean | `false` |
|
|
20
|
+
| `levelMethods` | Method names treated as logger level calls, such as `info` or `error`. | String[] | [`trace`, `debug`, `info`, `warn`, `error`, `fatal`] |
|
|
21
|
+
|
|
22
|
+
<!-- end auto-generated rule options list -->
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# @techsquidtv/structured-logging/require-logger-message
|
|
2
|
+
|
|
3
|
+
📝 Require logger calls to include a static text message.
|
|
4
|
+
|
|
5
|
+
💼⚠️ This rule is enabled in the 🔒 `strict` config. This rule _warns_ in the ✅ `recommended` config.
|
|
6
|
+
|
|
7
|
+
<!-- end auto-generated rule header -->
|
|
8
|
+
|
|
9
|
+
## Options
|
|
10
|
+
|
|
11
|
+
<!-- begin auto-generated rule options list -->
|
|
12
|
+
|
|
13
|
+
| Name | Description | Type | Default |
|
|
14
|
+
| :--------------------------------- | :---------------------------------------------------------------------------------------- | :------- | :--------------------------------------------------- |
|
|
15
|
+
| `allowedLoggerIdentifiers` | Bare logger object identifiers accepted as logger callees. | String[] | [`logger`, `log`] |
|
|
16
|
+
| `allowedLoggerObjects` | Fully-qualified logger object paths, such as `log.child`. | String[] | `[]` |
|
|
17
|
+
| `attributesFirstLoggerIdentifiers` | Logger identifiers whose calls pass attributes before the message. | String[] | `[]` |
|
|
18
|
+
| `attributesFirstLoggerObjects` | Logger object paths whose calls pass attributes before the message. | String[] | `[]` |
|
|
19
|
+
| `ignoreDynamicLevelMethods` | Skip computed logger level methods such as `logger[level](...)` instead of matching them. | Boolean | `false` |
|
|
20
|
+
| `levelMethods` | Method names treated as logger level calls, such as `info` or `error`. | String[] | [`trace`, `debug`, `info`, `warn`, `error`, `fatal`] |
|
|
21
|
+
|
|
22
|
+
<!-- end auto-generated rule options list -->
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# @techsquidtv/structured-logging/require-logger-primitive-attributes
|
|
2
|
+
|
|
3
|
+
📝 Require logger attribute values to be primitives or arrays of primitives.
|
|
4
|
+
|
|
5
|
+
💼⚠️ This rule is enabled in the 🔒 `strict` config. This rule _warns_ in the ✅ `recommended` config.
|
|
6
|
+
|
|
7
|
+
<!-- end auto-generated rule header -->
|
|
8
|
+
|
|
9
|
+
## Options
|
|
10
|
+
|
|
11
|
+
<!-- begin auto-generated rule options list -->
|
|
12
|
+
|
|
13
|
+
| Name | Description | Type | Default |
|
|
14
|
+
| :--------------------------------- | :--------------------------------------------------------------------------------------------------- | :------- | :--------------------------------------------------- |
|
|
15
|
+
| `allowedLoggerIdentifiers` | Bare logger object identifiers accepted as logger callees. | String[] | [`logger`, `log`] |
|
|
16
|
+
| `allowedLoggerObjects` | Fully-qualified logger object paths, such as `log.child`. | String[] | `[]` |
|
|
17
|
+
| `attributesFirstLoggerIdentifiers` | Logger identifiers whose calls pass attributes before the message. | String[] | `[]` |
|
|
18
|
+
| `attributesFirstLoggerObjects` | Logger object paths whose calls pass attributes before the message. | String[] | `[]` |
|
|
19
|
+
| `disallowUnknownAttributeValues` | Reject expressions whose static value shape is unknown instead of allowing them as attribute values. | Boolean | `false` |
|
|
20
|
+
| `ignoreDynamicLevelMethods` | Skip computed logger level methods such as `logger[level](...)` instead of matching them. | Boolean | `false` |
|
|
21
|
+
| `levelMethods` | Method names treated as logger level calls, such as `info` or `error`. | String[] | [`trace`, `debug`, `info`, `warn`, `error`, `fatal`] |
|
|
22
|
+
|
|
23
|
+
<!-- end auto-generated rule options list -->
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# @techsquidtv/structured-logging/require-logger-scoped-dot-notation
|
|
2
|
+
|
|
3
|
+
📝 Require logger messages and attribute keys to use dotted snake case.
|
|
4
|
+
|
|
5
|
+
💼⚠️ This rule is enabled in the 🔒 `strict` config. This rule _warns_ in the ✅ `recommended` config.
|
|
6
|
+
|
|
7
|
+
🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
|
|
8
|
+
|
|
9
|
+
<!-- end auto-generated rule header -->
|
|
10
|
+
|
|
11
|
+
## Dotted Snake Case
|
|
12
|
+
|
|
13
|
+
Dotted snake case means at least two dot-separated segments, where each segment
|
|
14
|
+
is lower `snake_case`, such as `payment.capture_failed` or `request.user_id`.
|
|
15
|
+
|
|
16
|
+
## Options
|
|
17
|
+
|
|
18
|
+
<!-- begin auto-generated rule options list -->
|
|
19
|
+
|
|
20
|
+
| Name | Description | Type | Choices | Default |
|
|
21
|
+
| :--------------------------------- | :---------------------------------------------------------------------------------------- | :------- | :------------------------- | :--------------------------------------------------- |
|
|
22
|
+
| `allowedLoggerIdentifiers` | Bare logger object identifiers accepted as logger callees. | String[] | | [`logger`, `log`] |
|
|
23
|
+
| `allowedLoggerObjects` | Fully-qualified logger object paths, such as `log.child`. | String[] | | `[]` |
|
|
24
|
+
| `attributeKeyFormat` | Format required for inline attribute keys. | String | `dotted-snake-case`, `off` | `dotted-snake-case` |
|
|
25
|
+
| `attributesFirstLoggerIdentifiers` | Logger identifiers whose calls pass attributes before the message. | String[] | | `[]` |
|
|
26
|
+
| `attributesFirstLoggerObjects` | Logger object paths whose calls pass attributes before the message. | String[] | | `[]` |
|
|
27
|
+
| `ignoreDynamicLevelMethods` | Skip computed logger level methods such as `logger[level](...)` instead of matching them. | Boolean | | `false` |
|
|
28
|
+
| `levelMethods` | Method names treated as logger level calls, such as `info` or `error`. | String[] | | [`trace`, `debug`, `info`, `warn`, `error`, `fatal`] |
|
|
29
|
+
| `messageFormat` | Format required for logger messages. | String | `dotted-snake-case`, `off` | `dotted-snake-case` |
|
|
30
|
+
|
|
31
|
+
<!-- end auto-generated rule options list -->
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { LoggerMatcherOptions } from "./rules/logger/utils.js";
|
|
2
|
+
import { StructuredLoggingPluginDocs } from "./utils.js";
|
|
3
|
+
import { RequireLoggerPrimitiveAttributesOptions } from "./rules/logger/require-logger-primitive-attributes.js";
|
|
4
|
+
import { RequireLoggerScopedDotNotationOptions } from "./rules/logger/require-logger-scoped-dot-notation.js";
|
|
5
|
+
import { TSESLint } from "@typescript-eslint/utils";
|
|
6
|
+
|
|
7
|
+
//#region src/index.d.ts
|
|
8
|
+
declare const plugin: {
|
|
9
|
+
meta: {
|
|
10
|
+
name: string;
|
|
11
|
+
version: string;
|
|
12
|
+
};
|
|
13
|
+
rules: {
|
|
14
|
+
"require-logger-inline-attributes": TSESLint.RuleModule<"nonInlineAttributes" | "noAttributeSpread" | "staticAttributeKeys", [LoggerMatcherOptions], StructuredLoggingPluginDocs, TSESLint.RuleListener> & {
|
|
15
|
+
name: string;
|
|
16
|
+
};
|
|
17
|
+
"require-logger-message": TSESLint.RuleModule<"messageRequired" | "messageMustBeText", [LoggerMatcherOptions], StructuredLoggingPluginDocs, TSESLint.RuleListener> & {
|
|
18
|
+
name: string;
|
|
19
|
+
};
|
|
20
|
+
"require-logger-primitive-attributes": TSESLint.RuleModule<"primitiveAttributeValue", [RequireLoggerPrimitiveAttributesOptions], StructuredLoggingPluginDocs, TSESLint.RuleListener> & {
|
|
21
|
+
name: string;
|
|
22
|
+
};
|
|
23
|
+
"require-logger-scoped-dot-notation": TSESLint.RuleModule<"dottedSnakeCaseAttributeKey" | "dottedSnakeCaseMessage", [RequireLoggerScopedDotNotationOptions], StructuredLoggingPluginDocs, TSESLint.RuleListener> & {
|
|
24
|
+
name: string;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
configs: Record<string, TSESLint.FlatConfig.Config>;
|
|
28
|
+
};
|
|
29
|
+
//#endregion
|
|
30
|
+
export { plugin as default };
|
|
31
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;cAmDM,MAAA;;;;;;kIAOL,oBAAA;;;;;;;;;;;;;WADe,MAAA,SAAe,QAAA,CAAS,UAAA,CAAW,MAAA;AAAA"}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { version } from "./package.js";
|
|
2
|
+
import { requireLoggerInlineAttributes } from "./rules/logger/require-logger-inline-attributes.js";
|
|
3
|
+
import { requireLoggerMessage } from "./rules/logger/require-logger-message.js";
|
|
4
|
+
import { requireLoggerPrimitiveAttributes } from "./rules/logger/require-logger-primitive-attributes.js";
|
|
5
|
+
import { requireLoggerScopedDotNotation } from "./rules/logger/require-logger-scoped-dot-notation.js";
|
|
6
|
+
import "./rules/index.js";
|
|
7
|
+
//#region src/index.ts
|
|
8
|
+
const rules = {
|
|
9
|
+
"require-logger-inline-attributes": requireLoggerInlineAttributes,
|
|
10
|
+
"require-logger-message": requireLoggerMessage,
|
|
11
|
+
"require-logger-primitive-attributes": requireLoggerPrimitiveAttributes,
|
|
12
|
+
"require-logger-scoped-dot-notation": requireLoggerScopedDotNotation
|
|
13
|
+
};
|
|
14
|
+
const pluginName = "@techsquidtv/structured-logging";
|
|
15
|
+
const strictAttributeRuleName = "require-logger-primitive-attributes";
|
|
16
|
+
function createRecommendedRules({ severity, strictAttributes }) {
|
|
17
|
+
const configRules = {};
|
|
18
|
+
for (const [ruleName, rule] of Object.entries(rules)) {
|
|
19
|
+
const docs = rule.meta.docs;
|
|
20
|
+
if (!docs || !("recommended" in docs) || !docs.recommended) continue;
|
|
21
|
+
const ruleId = `${pluginName}/${ruleName}`;
|
|
22
|
+
configRules[ruleId] = strictAttributes && ruleName === strictAttributeRuleName ? [severity, { disallowUnknownAttributeValues: true }] : severity;
|
|
23
|
+
}
|
|
24
|
+
return configRules;
|
|
25
|
+
}
|
|
26
|
+
const plugin = {
|
|
27
|
+
meta: {
|
|
28
|
+
name: "@techsquidtv/eslint-plugin-structured-logging",
|
|
29
|
+
version
|
|
30
|
+
},
|
|
31
|
+
rules,
|
|
32
|
+
configs: {}
|
|
33
|
+
};
|
|
34
|
+
Object.assign(plugin.configs, {
|
|
35
|
+
recommended: {
|
|
36
|
+
name: `${pluginName}/recommended`,
|
|
37
|
+
plugins: { [pluginName]: plugin },
|
|
38
|
+
rules: createRecommendedRules({
|
|
39
|
+
severity: "warn",
|
|
40
|
+
strictAttributes: false
|
|
41
|
+
})
|
|
42
|
+
},
|
|
43
|
+
strict: {
|
|
44
|
+
name: `${pluginName}/strict`,
|
|
45
|
+
plugins: { [pluginName]: plugin },
|
|
46
|
+
rules: createRecommendedRules({
|
|
47
|
+
severity: "error",
|
|
48
|
+
strictAttributes: true
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
//#endregion
|
|
53
|
+
export { plugin as default };
|
|
54
|
+
|
|
55
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["packageJson.version"],"sources":["../src/index.ts"],"sourcesContent":["import type { TSESLint } from \"@typescript-eslint/utils\";\n\nimport packageJson from \"../package.json\" with { type: \"json\" };\n\nimport {\n\trequireLoggerInlineAttributes,\n\trequireLoggerMessage,\n\trequireLoggerPrimitiveAttributes,\n\trequireLoggerScopedDotNotation,\n} from \"@/rules/index\";\n\nconst rules = {\n\t\"require-logger-inline-attributes\": requireLoggerInlineAttributes,\n\t\"require-logger-message\": requireLoggerMessage,\n\t\"require-logger-primitive-attributes\": requireLoggerPrimitiveAttributes,\n\t\"require-logger-scoped-dot-notation\": requireLoggerScopedDotNotation,\n};\n\ntype Severity = \"warn\" | \"error\";\ntype Rules = NonNullable<TSESLint.FlatConfig.Config[\"rules\"]>;\n\nconst pluginName = \"@techsquidtv/structured-logging\";\nconst strictAttributeRuleName = \"require-logger-primitive-attributes\";\n\nfunction createRecommendedRules({\n\tseverity,\n\tstrictAttributes,\n}: {\n\tseverity: Severity;\n\tstrictAttributes: boolean;\n}): Rules {\n\tconst configRules: Rules = {};\n\n\tfor (const [ruleName, rule] of Object.entries(rules)) {\n\t\tconst docs = rule.meta.docs;\n\n\t\tif (!docs || !(\"recommended\" in docs) || !docs.recommended) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst ruleId = `${pluginName}/${ruleName}`;\n\n\t\tconfigRules[ruleId] =\n\t\t\tstrictAttributes && ruleName === strictAttributeRuleName\n\t\t\t\t? [severity, { disallowUnknownAttributeValues: true }]\n\t\t\t\t: severity;\n\t}\n\n\treturn configRules;\n}\n\nconst plugin = {\n\tmeta: {\n\t\tname: \"@techsquidtv/eslint-plugin-structured-logging\",\n\t\tversion: packageJson.version,\n\t},\n\trules,\n\tconfigs: {} as Record<string, TSESLint.FlatConfig.Config>,\n};\n\nObject.assign(plugin.configs, {\n\trecommended: {\n\t\tname: `${pluginName}/recommended`,\n\t\tplugins: { [pluginName]: plugin },\n\t\trules: createRecommendedRules({\n\t\t\tseverity: \"warn\",\n\t\t\tstrictAttributes: false,\n\t\t}),\n\t} satisfies TSESLint.FlatConfig.Config,\n\tstrict: {\n\t\tname: `${pluginName}/strict`,\n\t\tplugins: { [pluginName]: plugin },\n\t\trules: createRecommendedRules({\n\t\t\tseverity: \"error\",\n\t\t\tstrictAttributes: true,\n\t\t}),\n\t} satisfies TSESLint.FlatConfig.Config,\n});\n\nexport default plugin;\n"],"mappings":";;;;;;;AAWA,MAAM,QAAQ;CACb,oCAAoC;CACpC,0BAA0B;CAC1B,uCAAuC;CACvC,sCAAsC;AACvC;AAKA,MAAM,aAAa;AACnB,MAAM,0BAA0B;AAEhC,SAAS,uBAAuB,EAC/B,UACA,oBAIS;CACT,MAAM,cAAqB,CAAC;CAE5B,KAAK,MAAM,CAAC,UAAU,SAAS,OAAO,QAAQ,KAAK,GAAG;EACrD,MAAM,OAAO,KAAK,KAAK;EAEvB,IAAI,CAAC,QAAQ,EAAE,iBAAiB,SAAS,CAAC,KAAK,aAC9C;EAGD,MAAM,SAAS,GAAG,WAAW,GAAG;EAEhC,YAAY,UACX,oBAAoB,aAAa,0BAC9B,CAAC,UAAU,EAAE,gCAAgC,KAAK,CAAC,IACnD;CACL;CAEA,OAAO;AACR;AAEA,MAAM,SAAS;CACd,MAAM;EACL,MAAM;EACGA;CACV;CACA;CACA,SAAS,CAAC;AACX;AAEA,OAAO,OAAO,OAAO,SAAS;CAC7B,aAAa;EACZ,MAAM,GAAG,WAAW;EACpB,SAAS,GAAG,aAAa,OAAO;EAChC,OAAO,uBAAuB;GAC7B,UAAU;GACV,kBAAkB;EACnB,CAAC;CACF;CACA,QAAQ;EACP,MAAM,GAAG,WAAW;EACpB,SAAS,GAAG,aAAa,OAAO;EAChC,OAAO,uBAAuB;GAC7B,UAAU;GACV,kBAAkB;EACnB,CAAC;CACF;AACD,CAAC"}
|
package/lib/package.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package.js","names":[],"sources":["../package.json"],"sourcesContent":[""],"mappings":""}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { requireLoggerPrimitiveAttributes } from "./logger/require-logger-primitive-attributes.js";
|
|
2
|
+
import { requireLoggerScopedDotNotation } from "./logger/require-logger-scoped-dot-notation.js";
|
|
3
|
+
import { requireLoggerInlineAttributes } from "./logger/require-logger-inline-attributes.js";
|
|
4
|
+
import { requireLoggerMessage } from "./logger/require-logger-message.js";
|
|
5
|
+
export { requireLoggerInlineAttributes, requireLoggerMessage, requireLoggerPrimitiveAttributes, requireLoggerScopedDotNotation };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { requireLoggerInlineAttributes } from "./logger/require-logger-inline-attributes.js";
|
|
2
|
+
import { requireLoggerMessage } from "./logger/require-logger-message.js";
|
|
3
|
+
import { requireLoggerPrimitiveAttributes } from "./logger/require-logger-primitive-attributes.js";
|
|
4
|
+
import { requireLoggerScopedDotNotation } from "./logger/require-logger-scoped-dot-notation.js";
|
|
5
|
+
export { requireLoggerInlineAttributes, requireLoggerMessage, requireLoggerPrimitiveAttributes, requireLoggerScopedDotNotation };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { LoggerMatcherOptions } from "./utils.js";
|
|
2
|
+
import { StructuredLoggingPluginDocs } from "../../utils.js";
|
|
3
|
+
|
|
4
|
+
//#region src/rules/logger/require-logger-inline-attributes.d.ts
|
|
5
|
+
type MessageIds = "nonInlineAttributes" | "noAttributeSpread" | "staticAttributeKeys";
|
|
6
|
+
type RuleOptions = [LoggerMatcherOptions];
|
|
7
|
+
/** Enforces statically inspectable inline attribute objects for logger calls. */
|
|
8
|
+
declare const requireLoggerInlineAttributes: import("@typescript-eslint/utils/ts-eslint").RuleModule<MessageIds, RuleOptions, StructuredLoggingPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
9
|
+
name: string;
|
|
10
|
+
};
|
|
11
|
+
//#endregion
|
|
12
|
+
export { requireLoggerInlineAttributes };
|
|
13
|
+
//# sourceMappingURL=require-logger-inline-attributes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"require-logger-inline-attributes.d.ts","names":[],"sources":["../../../src/rules/logger/require-logger-inline-attributes.ts"],"mappings":";;;;KAUK,UAAA;AAAA,KAIA,WAAA,IAAe,oBAAoB;;cAG3B,6BAAA,+CAA6B,UAAA,CAAA,UAAA,EAAA,WAAA,EAAA,2BAAA,+CAAA,YAAA"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createRule } from "../../utils.js";
|
|
2
|
+
import { DEFAULT_LOGGER_MATCHER_OPTIONS, LOGGER_MATCHER_OPTION_SCHEMA_PROPERTIES, buildLoggerMatcherSets, collectAttrsEntries, getLoggerArguments } from "./utils.js";
|
|
3
|
+
//#region src/rules/logger/require-logger-inline-attributes.ts
|
|
4
|
+
/** Enforces statically inspectable inline attribute objects for logger calls. */
|
|
5
|
+
const requireLoggerInlineAttributes = createRule({
|
|
6
|
+
name: "require-logger-inline-attributes",
|
|
7
|
+
meta: {
|
|
8
|
+
type: "problem",
|
|
9
|
+
docs: {
|
|
10
|
+
description: "Require logger attributes to be inline object literals with explicit keys.",
|
|
11
|
+
recommended: true
|
|
12
|
+
},
|
|
13
|
+
messages: {
|
|
14
|
+
nonInlineAttributes: "Log attributes must be an inline object literal. Inline the attributes object so lint rules can validate its keys and values.",
|
|
15
|
+
noAttributeSpread: "Do not spread into log attributes. List each safe scalar attribute explicitly.",
|
|
16
|
+
staticAttributeKeys: "Attribute keys must be static identifiers or string literals. Replace the computed key with an explicit key."
|
|
17
|
+
},
|
|
18
|
+
schema: [{
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: { ...LOGGER_MATCHER_OPTION_SCHEMA_PROPERTIES },
|
|
21
|
+
additionalProperties: false
|
|
22
|
+
}],
|
|
23
|
+
defaultOptions: [DEFAULT_LOGGER_MATCHER_OPTIONS]
|
|
24
|
+
},
|
|
25
|
+
create(context) {
|
|
26
|
+
const sets = buildLoggerMatcherSets(context.options[0]);
|
|
27
|
+
return { CallExpression(node) {
|
|
28
|
+
const args = getLoggerArguments(node, sets);
|
|
29
|
+
if (!args) return;
|
|
30
|
+
collectAttrsEntries(args.attrsArg, {
|
|
31
|
+
reportMalformedProperties: true,
|
|
32
|
+
reportNonLiteral: true,
|
|
33
|
+
report: (reportNode, messageId) => {
|
|
34
|
+
context.report({
|
|
35
|
+
node: reportNode,
|
|
36
|
+
messageId
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
} };
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
//#endregion
|
|
44
|
+
export { requireLoggerInlineAttributes };
|
|
45
|
+
|
|
46
|
+
//# sourceMappingURL=require-logger-inline-attributes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"require-logger-inline-attributes.js","names":[],"sources":["../../../src/rules/logger/require-logger-inline-attributes.ts"],"sourcesContent":["import { createRule } from \"@/utils\";\nimport {\n\tLOGGER_MATCHER_OPTION_SCHEMA_PROPERTIES,\n\tDEFAULT_LOGGER_MATCHER_OPTIONS,\n\tbuildLoggerMatcherSets,\n\tcollectAttrsEntries,\n\tgetLoggerArguments,\n\ttype LoggerMatcherOptions,\n} from \"@/rules/logger/utils\";\n\ntype MessageIds =\n\t| \"nonInlineAttributes\"\n\t| \"noAttributeSpread\"\n\t| \"staticAttributeKeys\";\ntype RuleOptions = [LoggerMatcherOptions];\n\n/** Enforces statically inspectable inline attribute objects for logger calls. */\nexport const requireLoggerInlineAttributes = createRule<\n\tRuleOptions,\n\tMessageIds\n>({\n\tname: \"require-logger-inline-attributes\",\n\tmeta: {\n\t\ttype: \"problem\",\n\t\tdocs: {\n\t\t\tdescription:\n\t\t\t\t\"Require logger attributes to be inline object literals with explicit keys.\",\n\t\t\trecommended: true,\n\t\t},\n\t\tmessages: {\n\t\t\tnonInlineAttributes:\n\t\t\t\t\"Log attributes must be an inline object literal. Inline the attributes object so lint rules can validate its keys and values.\",\n\t\t\tnoAttributeSpread:\n\t\t\t\t\"Do not spread into log attributes. List each safe scalar attribute explicitly.\",\n\t\t\tstaticAttributeKeys:\n\t\t\t\t\"Attribute keys must be static identifiers or string literals. Replace the computed key with an explicit key.\",\n\t\t},\n\t\tschema: [\n\t\t\t{\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: { ...LOGGER_MATCHER_OPTION_SCHEMA_PROPERTIES },\n\t\t\t\tadditionalProperties: false,\n\t\t\t},\n\t\t],\n\t\tdefaultOptions: [DEFAULT_LOGGER_MATCHER_OPTIONS],\n\t},\n\tcreate(context) {\n\t\tconst sets = buildLoggerMatcherSets(context.options[0]);\n\n\t\treturn {\n\t\t\tCallExpression(node) {\n\t\t\t\tconst args = getLoggerArguments(node, sets);\n\t\t\t\tif (!args) return;\n\n\t\t\t\tcollectAttrsEntries(args.attrsArg, {\n\t\t\t\t\treportMalformedProperties: true,\n\t\t\t\t\treportNonLiteral: true,\n\t\t\t\t\treport: (reportNode, messageId) => {\n\t\t\t\t\t\tcontext.report({\n\t\t\t\t\t\t\tnode: reportNode,\n\t\t\t\t\t\t\tmessageId: messageId as MessageIds,\n\t\t\t\t\t\t});\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t},\n\t\t};\n\t},\n});\n"],"mappings":";;;;AAiBA,MAAa,gCAAgC,WAG3C;CACD,MAAM;CACN,MAAM;EACL,MAAM;EACN,MAAM;GACL,aACC;GACD,aAAa;EACd;EACA,UAAU;GACT,qBACC;GACD,mBACC;GACD,qBACC;EACF;EACA,QAAQ,CACP;GACC,MAAM;GACN,YAAY,EAAE,GAAG,wCAAwC;GACzD,sBAAsB;EACvB,CACD;EACA,gBAAgB,CAAC,8BAA8B;CAChD;CACA,OAAO,SAAS;EACf,MAAM,OAAO,uBAAuB,QAAQ,QAAQ,EAAE;EAEtD,OAAO,EACN,eAAe,MAAM;GACpB,MAAM,OAAO,mBAAmB,MAAM,IAAI;GAC1C,IAAI,CAAC,MAAM;GAEX,oBAAoB,KAAK,UAAU;IAClC,2BAA2B;IAC3B,kBAAkB;IAClB,SAAS,YAAY,cAAc;KAClC,QAAQ,OAAO;MACd,MAAM;MACK;KACZ,CAAC;IACF;GACD,CAAC;EACF,EACD;CACD;AACD,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { LoggerMatcherOptions } from "./utils.js";
|
|
2
|
+
import { StructuredLoggingPluginDocs } from "../../utils.js";
|
|
3
|
+
|
|
4
|
+
//#region src/rules/logger/require-logger-message.d.ts
|
|
5
|
+
type MessageIds = "messageRequired" | "messageMustBeText";
|
|
6
|
+
type RuleOptions = [LoggerMatcherOptions];
|
|
7
|
+
/** Enforces static message arguments for logger calls. */
|
|
8
|
+
declare const requireLoggerMessage: import("@typescript-eslint/utils/ts-eslint").RuleModule<MessageIds, RuleOptions, StructuredLoggingPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
9
|
+
name: string;
|
|
10
|
+
};
|
|
11
|
+
//#endregion
|
|
12
|
+
export { requireLoggerMessage };
|
|
13
|
+
//# sourceMappingURL=require-logger-message.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"require-logger-message.d.ts","names":[],"sources":["../../../src/rules/logger/require-logger-message.ts"],"mappings":";;;;KAUK,UAAA;AAAA,KACA,WAAA,IAAe,oBAAoB;;cAG3B,oBAAA,+CAAoB,UAAA,CAAA,UAAA,EAAA,WAAA,EAAA,2BAAA,+CAAA,YAAA"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createRule } from "../../utils.js";
|
|
2
|
+
import { DEFAULT_LOGGER_MATCHER_OPTIONS, LOGGER_MATCHER_OPTION_SCHEMA_PROPERTIES, buildLoggerMatcherSets, getLoggerArguments, isClearlyNotMessage } from "./utils.js";
|
|
3
|
+
//#region src/rules/logger/require-logger-message.ts
|
|
4
|
+
/** Enforces static message arguments for logger calls. */
|
|
5
|
+
const requireLoggerMessage = createRule({
|
|
6
|
+
name: "require-logger-message",
|
|
7
|
+
meta: {
|
|
8
|
+
type: "problem",
|
|
9
|
+
docs: {
|
|
10
|
+
description: "Require logger calls to include a static text message.",
|
|
11
|
+
recommended: true
|
|
12
|
+
},
|
|
13
|
+
messages: {
|
|
14
|
+
messageRequired: "Logger call must include a static message. Add a descriptive string literal as the message.",
|
|
15
|
+
messageMustBeText: "Logger call message must be static text. Use a string literal message, and put variable data in the attributes object."
|
|
16
|
+
},
|
|
17
|
+
schema: [{
|
|
18
|
+
type: "object",
|
|
19
|
+
properties: { ...LOGGER_MATCHER_OPTION_SCHEMA_PROPERTIES },
|
|
20
|
+
additionalProperties: false
|
|
21
|
+
}],
|
|
22
|
+
defaultOptions: [DEFAULT_LOGGER_MATCHER_OPTIONS]
|
|
23
|
+
},
|
|
24
|
+
create(context) {
|
|
25
|
+
const sets = buildLoggerMatcherSets(context.options[0]);
|
|
26
|
+
return { CallExpression(node) {
|
|
27
|
+
const args = getLoggerArguments(node, sets);
|
|
28
|
+
if (!args) return;
|
|
29
|
+
if (!args.messageArg) {
|
|
30
|
+
context.report({
|
|
31
|
+
node,
|
|
32
|
+
messageId: "messageRequired"
|
|
33
|
+
});
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (isClearlyNotMessage(args.messageArg)) context.report({
|
|
37
|
+
node: args.messageArg,
|
|
38
|
+
messageId: "messageMustBeText"
|
|
39
|
+
});
|
|
40
|
+
} };
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
//#endregion
|
|
44
|
+
export { requireLoggerMessage };
|
|
45
|
+
|
|
46
|
+
//# sourceMappingURL=require-logger-message.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"require-logger-message.js","names":[],"sources":["../../../src/rules/logger/require-logger-message.ts"],"sourcesContent":["import { createRule } from \"@/utils\";\nimport {\n\tLOGGER_MATCHER_OPTION_SCHEMA_PROPERTIES,\n\tDEFAULT_LOGGER_MATCHER_OPTIONS,\n\tbuildLoggerMatcherSets,\n\tgetLoggerArguments,\n\tisClearlyNotMessage,\n\ttype LoggerMatcherOptions,\n} from \"@/rules/logger/utils\";\n\ntype MessageIds = \"messageRequired\" | \"messageMustBeText\";\ntype RuleOptions = [LoggerMatcherOptions];\n\n/** Enforces static message arguments for logger calls. */\nexport const requireLoggerMessage = createRule<RuleOptions, MessageIds>({\n\tname: \"require-logger-message\",\n\tmeta: {\n\t\ttype: \"problem\",\n\t\tdocs: {\n\t\t\tdescription: \"Require logger calls to include a static text message.\",\n\t\t\trecommended: true,\n\t\t},\n\t\tmessages: {\n\t\t\tmessageRequired:\n\t\t\t\t\"Logger call must include a static message. Add a descriptive string literal as the message.\",\n\t\t\tmessageMustBeText:\n\t\t\t\t\"Logger call message must be static text. Use a string literal message, and put variable data in the attributes object.\",\n\t\t},\n\t\tschema: [\n\t\t\t{\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: { ...LOGGER_MATCHER_OPTION_SCHEMA_PROPERTIES },\n\t\t\t\tadditionalProperties: false,\n\t\t\t},\n\t\t],\n\t\tdefaultOptions: [DEFAULT_LOGGER_MATCHER_OPTIONS],\n\t},\n\tcreate(context) {\n\t\tconst sets = buildLoggerMatcherSets(context.options[0]);\n\n\t\treturn {\n\t\t\tCallExpression(node) {\n\t\t\t\tconst args = getLoggerArguments(node, sets);\n\t\t\t\tif (!args) return;\n\n\t\t\t\tif (!args.messageArg) {\n\t\t\t\t\tcontext.report({ node, messageId: \"messageRequired\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (isClearlyNotMessage(args.messageArg)) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tnode: args.messageArg,\n\t\t\t\t\t\tmessageId: \"messageMustBeText\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t},\n});\n"],"mappings":";;;;AAcA,MAAa,uBAAuB,WAAoC;CACvE,MAAM;CACN,MAAM;EACL,MAAM;EACN,MAAM;GACL,aAAa;GACb,aAAa;EACd;EACA,UAAU;GACT,iBACC;GACD,mBACC;EACF;EACA,QAAQ,CACP;GACC,MAAM;GACN,YAAY,EAAE,GAAG,wCAAwC;GACzD,sBAAsB;EACvB,CACD;EACA,gBAAgB,CAAC,8BAA8B;CAChD;CACA,OAAO,SAAS;EACf,MAAM,OAAO,uBAAuB,QAAQ,QAAQ,EAAE;EAEtD,OAAO,EACN,eAAe,MAAM;GACpB,MAAM,OAAO,mBAAmB,MAAM,IAAI;GAC1C,IAAI,CAAC,MAAM;GAEX,IAAI,CAAC,KAAK,YAAY;IACrB,QAAQ,OAAO;KAAE;KAAM,WAAW;IAAkB,CAAC;IACrD;GACD;GAEA,IAAI,oBAAoB,KAAK,UAAU,GACtC,QAAQ,OAAO;IACd,MAAM,KAAK;IACX,WAAW;GACZ,CAAC;EAEH,EACD;CACD;AACD,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { LoggerMatcherOptions } from "./utils.js";
|
|
2
|
+
import { StructuredLoggingPluginDocs } from "../../utils.js";
|
|
3
|
+
|
|
4
|
+
//#region src/rules/logger/require-logger-primitive-attributes.d.ts
|
|
5
|
+
/** Options for primitive logger attribute enforcement. */
|
|
6
|
+
interface RequireLoggerPrimitiveAttributesOptions extends LoggerMatcherOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Reject expressions whose static value shape is unknown instead of
|
|
9
|
+
* allowing them as attribute values.
|
|
10
|
+
*/
|
|
11
|
+
disallowUnknownAttributeValues?: boolean;
|
|
12
|
+
}
|
|
13
|
+
type RuleOptions = [RequireLoggerPrimitiveAttributesOptions];
|
|
14
|
+
/** Enforces primitive values (or arrays of primitives) for logger attributes. */
|
|
15
|
+
declare const requireLoggerPrimitiveAttributes: import("@typescript-eslint/utils/ts-eslint").RuleModule<"primitiveAttributeValue", RuleOptions, StructuredLoggingPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
16
|
+
name: string;
|
|
17
|
+
};
|
|
18
|
+
//#endregion
|
|
19
|
+
export { RequireLoggerPrimitiveAttributesOptions, requireLoggerPrimitiveAttributes };
|
|
20
|
+
//# sourceMappingURL=require-logger-primitive-attributes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"require-logger-primitive-attributes.d.ts","names":[],"sources":["../../../src/rules/logger/require-logger-primitive-attributes.ts"],"mappings":";;;;;UAciB,uCAAA,SAAgD,oBAAoB;;AAArF;;;EAKC,8BAA8B;AAAA;AAAA,KAG1B,WAAA,IAAe,uCAAuC;;cAG9C,gCAAA,+CAAgC,UAAA,4BAAA,WAAA,EAAA,2BAAA,+CAAA,YAAA"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createRule } from "../../utils.js";
|
|
2
|
+
import { DEFAULT_LOGGER_MATCHER_OPTIONS, LOGGER_MATCHER_OPTION_SCHEMA_PROPERTIES, buildLoggerMatcherSets, collectAttrsEntries, getLoggerArguments, isDisallowedAttributeValue } from "./utils.js";
|
|
3
|
+
//#region src/rules/logger/require-logger-primitive-attributes.ts
|
|
4
|
+
/** Enforces primitive values (or arrays of primitives) for logger attributes. */
|
|
5
|
+
const requireLoggerPrimitiveAttributes = createRule({
|
|
6
|
+
name: "require-logger-primitive-attributes",
|
|
7
|
+
meta: {
|
|
8
|
+
type: "problem",
|
|
9
|
+
docs: {
|
|
10
|
+
description: "Require logger attribute values to be primitives or arrays of primitives.",
|
|
11
|
+
recommended: true
|
|
12
|
+
},
|
|
13
|
+
messages: { primitiveAttributeValue: "Attribute \"{{key}}\" must be a primitive or an array of primitives. Use a string, number, or boolean (or an array of those), or flatten nested data into separate attributes." },
|
|
14
|
+
schema: [{
|
|
15
|
+
type: "object",
|
|
16
|
+
properties: {
|
|
17
|
+
...LOGGER_MATCHER_OPTION_SCHEMA_PROPERTIES,
|
|
18
|
+
disallowUnknownAttributeValues: {
|
|
19
|
+
description: "Reject expressions whose static value shape is unknown instead of allowing them as attribute values.",
|
|
20
|
+
type: "boolean"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
additionalProperties: false
|
|
24
|
+
}],
|
|
25
|
+
defaultOptions: [{
|
|
26
|
+
...DEFAULT_LOGGER_MATCHER_OPTIONS,
|
|
27
|
+
disallowUnknownAttributeValues: false
|
|
28
|
+
}]
|
|
29
|
+
},
|
|
30
|
+
create(context) {
|
|
31
|
+
const sets = buildLoggerMatcherSets(context.options[0]);
|
|
32
|
+
return { CallExpression(node) {
|
|
33
|
+
const args = getLoggerArguments(node, sets);
|
|
34
|
+
if (!args) return;
|
|
35
|
+
const entries = collectAttrsEntries(args.attrsArg);
|
|
36
|
+
if (!entries) return;
|
|
37
|
+
for (const entry of entries) if (isDisallowedAttributeValue(entry.valueNode, context.options[0])) context.report({
|
|
38
|
+
node: entry.valueNode,
|
|
39
|
+
messageId: "primitiveAttributeValue",
|
|
40
|
+
data: { key: entry.key }
|
|
41
|
+
});
|
|
42
|
+
} };
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
//#endregion
|
|
46
|
+
export { requireLoggerPrimitiveAttributes };
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=require-logger-primitive-attributes.js.map
|