@sfdxy/mule-lint 1.7.1 → 1.8.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.
- package/README.md +43 -7
- package/dist/bin/mule-lint-mcp.d.ts +3 -0
- package/dist/bin/mule-lint-mcp.d.ts.map +1 -0
- package/dist/bin/mule-lint-mcp.js +13 -0
- package/dist/bin/mule-lint-mcp.js.map +1 -0
- package/dist/package.json +6 -3
- package/dist/src/core/ComplexityCalculator.d.ts.map +1 -1
- package/dist/src/core/ComplexityCalculator.js +8 -8
- package/dist/src/core/ComplexityCalculator.js.map +1 -1
- package/dist/src/core/FileScanner.d.ts.map +1 -1
- package/dist/src/core/FileScanner.js +11 -12
- package/dist/src/core/FileScanner.js.map +1 -1
- package/dist/src/core/XPathHelper.d.ts.map +1 -1
- package/dist/src/core/XPathHelper.js +23 -23
- package/dist/src/core/XPathHelper.js.map +1 -1
- package/dist/src/core/XmlParser.js +1 -1
- package/dist/src/core/XmlParser.js.map +1 -1
- package/dist/src/core/YamlParser.d.ts.map +1 -1
- package/dist/src/core/YamlParser.js +1 -1
- package/dist/src/core/YamlParser.js.map +1 -1
- package/dist/src/engine/LintEngine.d.ts.map +1 -1
- package/dist/src/engine/LintEngine.js +6 -4
- package/dist/src/engine/LintEngine.js.map +1 -1
- package/dist/src/formatters/CsvFormatter.d.ts.map +1 -1
- package/dist/src/formatters/CsvFormatter.js +6 -4
- package/dist/src/formatters/CsvFormatter.js.map +1 -1
- package/dist/src/formatters/HtmlFormatter.d.ts.map +1 -1
- package/dist/src/formatters/HtmlFormatter.js +12 -7
- package/dist/src/formatters/HtmlFormatter.js.map +1 -1
- package/dist/src/formatters/SarifFormatter.d.ts.map +1 -1
- package/dist/src/formatters/SarifFormatter.js +20 -10
- package/dist/src/formatters/SarifFormatter.js.map +1 -1
- package/dist/src/formatters/TableFormatter.d.ts.map +1 -1
- package/dist/src/formatters/TableFormatter.js +1 -1
- package/dist/src/formatters/TableFormatter.js.map +1 -1
- package/dist/src/mcp/index.d.ts +13 -0
- package/dist/src/mcp/index.d.ts.map +1 -0
- package/dist/src/mcp/index.js +375 -0
- package/dist/src/mcp/index.js.map +1 -0
- package/dist/src/rules/api-led/ApiLedRules.d.ts.map +1 -1
- package/dist/src/rules/api-led/ApiLedRules.js +3 -1
- package/dist/src/rules/api-led/ApiLedRules.js.map +1 -1
- package/dist/src/rules/base/BaseRule.d.ts.map +1 -1
- package/dist/src/rules/base/BaseRule.js +1 -1
- package/dist/src/rules/base/BaseRule.js.map +1 -1
- package/dist/src/rules/complexity/FlowComplexityRule.d.ts.map +1 -1
- package/dist/src/rules/complexity/FlowComplexityRule.js +3 -5
- package/dist/src/rules/complexity/FlowComplexityRule.js.map +1 -1
- package/dist/src/rules/dataweave/DataWeaveRules.d.ts.map +1 -1
- package/dist/src/rules/dataweave/DataWeaveRules.js +5 -5
- package/dist/src/rules/dataweave/DataWeaveRules.js.map +1 -1
- package/dist/src/rules/dataweave/Java17DWErrorHandlingRule.d.ts +21 -0
- package/dist/src/rules/dataweave/Java17DWErrorHandlingRule.d.ts.map +1 -0
- package/dist/src/rules/dataweave/Java17DWErrorHandlingRule.js +162 -0
- package/dist/src/rules/dataweave/Java17DWErrorHandlingRule.js.map +1 -0
- package/dist/src/rules/documentation/FlowDescriptionRule.d.ts.map +1 -1
- package/dist/src/rules/documentation/FlowDescriptionRule.js +1 -1
- package/dist/src/rules/documentation/FlowDescriptionRule.js.map +1 -1
- package/dist/src/rules/documentation/MissingDocNameRule.d.ts.map +1 -1
- package/dist/src/rules/documentation/MissingDocNameRule.js +1 -1
- package/dist/src/rules/documentation/MissingDocNameRule.js.map +1 -1
- package/dist/src/rules/error-handling/CorrelationIdRule.d.ts.map +1 -1
- package/dist/src/rules/error-handling/CorrelationIdRule.js +1 -1
- package/dist/src/rules/error-handling/CorrelationIdRule.js.map +1 -1
- package/dist/src/rules/error-handling/GenericErrorRule.d.ts.map +1 -1
- package/dist/src/rules/error-handling/GenericErrorRule.js +2 -5
- package/dist/src/rules/error-handling/GenericErrorRule.js.map +1 -1
- package/dist/src/rules/error-handling/GlobalErrorHandlerRule.d.ts +1 -1
- package/dist/src/rules/error-handling/GlobalErrorHandlerRule.d.ts.map +1 -1
- package/dist/src/rules/error-handling/GlobalErrorHandlerRule.js +2 -2
- package/dist/src/rules/error-handling/GlobalErrorHandlerRule.js.map +1 -1
- package/dist/src/rules/error-handling/HttpStatusRule.d.ts.map +1 -1
- package/dist/src/rules/error-handling/HttpStatusRule.js +1 -1
- package/dist/src/rules/error-handling/HttpStatusRule.js.map +1 -1
- package/dist/src/rules/error-handling/MissingErrorHandlerRule.d.ts.map +1 -1
- package/dist/src/rules/error-handling/MissingErrorHandlerRule.js +9 -1
- package/dist/src/rules/error-handling/MissingErrorHandlerRule.js.map +1 -1
- package/dist/src/rules/experimental/ExperimentalRules.d.ts.map +1 -1
- package/dist/src/rules/experimental/ExperimentalRules.js +1 -1
- package/dist/src/rules/experimental/ExperimentalRules.js.map +1 -1
- package/dist/src/rules/http/HttpContentTypeRule.d.ts.map +1 -1
- package/dist/src/rules/http/HttpContentTypeRule.js +1 -1
- package/dist/src/rules/http/HttpContentTypeRule.js.map +1 -1
- package/dist/src/rules/http/HttpTimeoutRule.d.ts.map +1 -1
- package/dist/src/rules/http/HttpTimeoutRule.js +1 -1
- package/dist/src/rules/http/HttpTimeoutRule.js.map +1 -1
- package/dist/src/rules/http/HttpUserAgentRule.d.ts.map +1 -1
- package/dist/src/rules/http/HttpUserAgentRule.js +1 -1
- package/dist/src/rules/http/HttpUserAgentRule.js.map +1 -1
- package/dist/src/rules/index.d.ts +1 -1
- package/dist/src/rules/index.d.ts.map +1 -1
- package/dist/src/rules/index.js +7 -5
- package/dist/src/rules/index.js.map +1 -1
- package/dist/src/rules/logging/LoggerCategoryRule.js +2 -2
- package/dist/src/rules/logging/LoggerCategoryRule.js.map +1 -1
- package/dist/src/rules/logging/LoggerInUntilSuccessfulRule.js +1 -1
- package/dist/src/rules/logging/LoggerInUntilSuccessfulRule.js.map +1 -1
- package/dist/src/rules/logging/LoggerPayloadRule.d.ts.map +1 -1
- package/dist/src/rules/logging/LoggerPayloadRule.js +3 -3
- package/dist/src/rules/logging/LoggerPayloadRule.js.map +1 -1
- package/dist/src/rules/naming/FlowCasingRule.d.ts +1 -0
- package/dist/src/rules/naming/FlowCasingRule.d.ts.map +1 -1
- package/dist/src/rules/naming/FlowCasingRule.js +7 -1
- package/dist/src/rules/naming/FlowCasingRule.js.map +1 -1
- package/dist/src/rules/naming/FlowNamingRule.d.ts.map +1 -1
- package/dist/src/rules/naming/FlowNamingRule.js +12 -1
- package/dist/src/rules/naming/FlowNamingRule.js.map +1 -1
- package/dist/src/rules/naming/VariableNamingRule.d.ts.map +1 -1
- package/dist/src/rules/naming/VariableNamingRule.js +3 -3
- package/dist/src/rules/naming/VariableNamingRule.js.map +1 -1
- package/dist/src/rules/performance/AsyncErrorHandlerRule.d.ts.map +1 -1
- package/dist/src/rules/performance/AsyncErrorHandlerRule.js +1 -1
- package/dist/src/rules/performance/AsyncErrorHandlerRule.js.map +1 -1
- package/dist/src/rules/performance/LargeChoiceBlockRule.d.ts.map +1 -1
- package/dist/src/rules/performance/LargeChoiceBlockRule.js +1 -1
- package/dist/src/rules/performance/LargeChoiceBlockRule.js.map +1 -1
- package/dist/src/rules/performance/ScatterGatherRoutesRule.d.ts.map +1 -1
- package/dist/src/rules/performance/ScatterGatherRoutesRule.js +1 -1
- package/dist/src/rules/performance/ScatterGatherRoutesRule.js.map +1 -1
- package/dist/src/rules/security/HardcodedCredentialsRule.d.ts.map +1 -1
- package/dist/src/rules/security/HardcodedCredentialsRule.js +6 -6
- package/dist/src/rules/security/HardcodedCredentialsRule.js.map +1 -1
- package/dist/src/rules/security/HardcodedHttpRule.d.ts.map +1 -1
- package/dist/src/rules/security/HardcodedHttpRule.js +2 -6
- package/dist/src/rules/security/HardcodedHttpRule.js.map +1 -1
- package/dist/src/rules/security/InsecureTlsRule.d.ts.map +1 -1
- package/dist/src/rules/security/InsecureTlsRule.js +2 -2
- package/dist/src/rules/security/InsecureTlsRule.js.map +1 -1
- package/dist/src/rules/standards/ChoiceAntiPatternRule.d.ts.map +1 -1
- package/dist/src/rules/standards/ChoiceAntiPatternRule.js +8 -2
- package/dist/src/rules/standards/ChoiceAntiPatternRule.js.map +1 -1
- package/dist/src/rules/standards/DeprecatedComponentRule.d.ts.map +1 -1
- package/dist/src/rules/standards/DeprecatedComponentRule.js +1 -1
- package/dist/src/rules/standards/DeprecatedComponentRule.js.map +1 -1
- package/dist/src/rules/standards/DwlStandardsRule.d.ts.map +1 -1
- package/dist/src/rules/standards/DwlStandardsRule.js +1 -1
- package/dist/src/rules/standards/DwlStandardsRule.js.map +1 -1
- package/dist/src/rules/structure/StructureRules.d.ts.map +1 -1
- package/dist/src/rules/structure/StructureRules.js +6 -9
- package/dist/src/rules/structure/StructureRules.js.map +1 -1
- package/dist/src/rules/yaml/YamlRules.d.ts.map +1 -1
- package/dist/src/rules/yaml/YamlRules.js +19 -12
- package/dist/src/rules/yaml/YamlRules.js.map +1 -1
- package/dist/src/types/Config.d.ts.map +1 -1
- package/dist/src/types/Config.js +1 -5
- package/dist/src/types/Config.js.map +1 -1
- package/docs/README.md +95 -0
- package/docs/best-practices/documentation-standards.md +82 -0
- package/docs/best-practices/folder-structure.md +61 -0
- package/docs/best-practices/mulesoft-best-practices.md +902 -0
- package/docs/best-practices/rules-catalog.md +736 -0
- package/docs/linter/architecture.md +294 -0
- package/docs/linter/extending.md +426 -0
- package/docs/linter/folder-structure.md +251 -0
- package/docs/linter/images/html-report-dashboard.png +0 -0
- package/docs/linter/naming-conventions.md +300 -0
- package/docs/linter/rule-engine.md +532 -0
- package/docs/mcp-design.md +80 -0
- package/package.json +6 -3
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
This document describes the architecture, design patterns, and best practices used in mule-lint.
|
|
4
|
+
|
|
5
|
+
## System Overview
|
|
6
|
+
|
|
7
|
+
```mermaid
|
|
8
|
+
flowchart TB
|
|
9
|
+
subgraph CLI["CLI Layer (commander)"]
|
|
10
|
+
A["mule-lint ./path -f sarif"]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
subgraph Engine["LintEngine"]
|
|
14
|
+
B[FileScanner<br/>fast-glob] --> C[XmlParser<br/>xmldom]
|
|
15
|
+
C --> D[Rule Executor]
|
|
16
|
+
B --> E[YamlParser<br/>yaml]
|
|
17
|
+
E --> D
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
subgraph Rules["Rules (40 Total)"]
|
|
21
|
+
D --> R1[Error Handling<br/>MULE-001,003,005,007,009]
|
|
22
|
+
D --> R2[Naming<br/>MULE-002,101,102]
|
|
23
|
+
D --> R3[Security<br/>MULE-004,201,202]
|
|
24
|
+
D --> R4[Logging<br/>MULE-006,301,303]
|
|
25
|
+
D --> R5[HTTP<br/>MULE-401,402,403]
|
|
26
|
+
D --> R6[Performance<br/>MULE-501,502,503]
|
|
27
|
+
D --> R7[Documentation<br/>MULE-601,604]
|
|
28
|
+
D --> R8[Standards<br/>MULE-008,010,701]
|
|
29
|
+
D --> R9[Complexity<br/>MULE-801]
|
|
30
|
+
D --> R10[Structure<br/>MULE-802-804]
|
|
31
|
+
D --> R11[YAML<br/>YAML-001,003,004]
|
|
32
|
+
D --> R12[DataWeave<br/>DW-001,002,003]
|
|
33
|
+
D --> R13[API-Led<br/>API-001,002,003]
|
|
34
|
+
D --> R14[Experimental<br/>EXP-001,002,003]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
subgraph Output["Formatters"]
|
|
38
|
+
J[Table<br/>Human]
|
|
39
|
+
K[JSON<br/>Scripts]
|
|
40
|
+
L[SARIF<br/>AI Agents]
|
|
41
|
+
M[HTML<br/>Reports]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
A --> B
|
|
45
|
+
D --> J
|
|
46
|
+
D --> K
|
|
47
|
+
D --> L
|
|
48
|
+
D --> M
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Data Flow
|
|
52
|
+
|
|
53
|
+
```mermaid
|
|
54
|
+
sequenceDiagram
|
|
55
|
+
participant CLI
|
|
56
|
+
participant Engine as LintEngine
|
|
57
|
+
participant Scanner as FileScanner
|
|
58
|
+
participant Parser as XmlParser
|
|
59
|
+
participant YAML as YamlParser
|
|
60
|
+
participant Rules
|
|
61
|
+
participant Formatter
|
|
62
|
+
|
|
63
|
+
CLI->>Engine: scan(path)
|
|
64
|
+
Engine->>Scanner: scanDirectory(path)
|
|
65
|
+
Scanner-->>Engine: ScannedFile[]
|
|
66
|
+
|
|
67
|
+
loop Each XML File
|
|
68
|
+
Engine->>Parser: parseXml(content)
|
|
69
|
+
Parser-->>Engine: Document
|
|
70
|
+
|
|
71
|
+
loop Each Rule
|
|
72
|
+
Engine->>Rules: validate(doc, context)
|
|
73
|
+
Rules-->>Engine: Issue[]
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
loop YAML Rules
|
|
78
|
+
Engine->>YAML: parseYaml(path)
|
|
79
|
+
YAML-->>Engine: Properties
|
|
80
|
+
Engine->>Rules: validate(props, context)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
Engine->>Formatter: format(report)
|
|
84
|
+
Formatter-->>CLI: string output
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Core Components
|
|
88
|
+
|
|
89
|
+
### LintEngine
|
|
90
|
+
|
|
91
|
+
The central orchestrator that:
|
|
92
|
+
1. Scans directories for XML and YAML files using FileScanner
|
|
93
|
+
2. Parses each file with XmlParser or YamlParser
|
|
94
|
+
3. Executes all enabled rules against each document
|
|
95
|
+
4. Aggregates results into a LintReport
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
const engine = new LintEngine({ rules: ALL_RULES, config });
|
|
99
|
+
const report = await engine.scan('./project');
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### XPathHelper
|
|
103
|
+
|
|
104
|
+
Singleton utility for namespace-aware XPath queries:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
const xpath = XPathHelper.getInstance();
|
|
108
|
+
const flows = xpath.selectNodes('//mule:flow', document);
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Pre-configured namespaces:
|
|
112
|
+
|
|
113
|
+
| Prefix | Namespace |
|
|
114
|
+
|--------|-----------|
|
|
115
|
+
| `mule` | http://www.mulesoft.org/schema/mule/core |
|
|
116
|
+
| `http` | http://www.mulesoft.org/schema/mule/http |
|
|
117
|
+
| `ee` | http://www.mulesoft.org/schema/mule/ee/core |
|
|
118
|
+
| `db` | http://www.mulesoft.org/schema/mule/db |
|
|
119
|
+
| `doc` | http://www.mulesoft.org/schema/mule/documentation |
|
|
120
|
+
| `tls` | http://www.mulesoft.org/schema/mule/tls |
|
|
121
|
+
| `file` | http://www.mulesoft.org/schema/mule/file |
|
|
122
|
+
| `sftp` | http://www.mulesoft.org/schema/mule/sftp |
|
|
123
|
+
| `vm` | http://www.mulesoft.org/schema/mule/vm |
|
|
124
|
+
| `jms` | http://www.mulesoft.org/schema/mule/jms |
|
|
125
|
+
| `apikit` | http://www.mulesoft.org/schema/mule/mule-apikit |
|
|
126
|
+
| `batch` | http://www.mulesoft.org/schema/mule/batch |
|
|
127
|
+
|
|
128
|
+
### BaseRule
|
|
129
|
+
|
|
130
|
+
Abstract base class providing utilities to all rules:
|
|
131
|
+
|
|
132
|
+
```mermaid
|
|
133
|
+
classDiagram
|
|
134
|
+
class BaseRule {
|
|
135
|
+
+id: string
|
|
136
|
+
+name: string
|
|
137
|
+
+severity: Severity
|
|
138
|
+
+category: RuleCategory
|
|
139
|
+
+validate(doc, context): Issue[]
|
|
140
|
+
#select(xpath, doc): Node[]
|
|
141
|
+
#getAttribute(node, name): string
|
|
142
|
+
#createIssue(node, message): Issue
|
|
143
|
+
#getOption(context, key, default): T
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
class FlowNamingRule {
|
|
147
|
+
+validate()
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
class YamlRuleBase {
|
|
151
|
+
+validate()
|
|
152
|
+
#findYamlFiles(): string[]
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
BaseRule <|-- FlowNamingRule
|
|
156
|
+
BaseRule <|-- YamlRuleBase
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Design Patterns
|
|
160
|
+
|
|
161
|
+
### Strategy Pattern (Rules)
|
|
162
|
+
|
|
163
|
+
Each rule is a strategy implementing the same interface:
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
interface Rule {
|
|
167
|
+
id: string;
|
|
168
|
+
name: string;
|
|
169
|
+
severity: Severity;
|
|
170
|
+
validate(doc: Document, context: ValidationContext): Issue[];
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Factory Pattern (Formatters)
|
|
175
|
+
|
|
176
|
+
Formatters are selected via factory function:
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
function getFormatter(type: FormatterType): Formatter {
|
|
180
|
+
switch (type) {
|
|
181
|
+
case 'table': return formatTable;
|
|
182
|
+
case 'json': return formatJson;
|
|
183
|
+
case 'sarif': return formatSarif;
|
|
184
|
+
case 'html': return formatHtml;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Singleton Pattern (XPathHelper)
|
|
190
|
+
|
|
191
|
+
XPathHelper uses singleton to avoid recreating namespace resolver:
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
XPathHelper.getInstance(); // Same instance always
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Directory Structure
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
src/
|
|
201
|
+
├── index.ts # Package entry point
|
|
202
|
+
├── types/ # TypeScript interfaces
|
|
203
|
+
│ ├── Rule.ts # Rule, Issue, Severity
|
|
204
|
+
│ ├── Report.ts # LintReport, FileResult
|
|
205
|
+
│ └── Config.ts # LintConfig, CliOptions
|
|
206
|
+
├── core/ # Core utilities
|
|
207
|
+
│ ├── XPathHelper.ts # Namespace-aware XPath
|
|
208
|
+
│ ├── XmlParser.ts # DOM parsing
|
|
209
|
+
│ ├── YamlParser.ts # YAML parsing
|
|
210
|
+
│ ├── FileScanner.ts # File discovery
|
|
211
|
+
│ └── ComplexityCalculator.ts
|
|
212
|
+
├── engine/ # Orchestration
|
|
213
|
+
│ └── LintEngine.ts # Main engine
|
|
214
|
+
├── rules/ # All rules (40 total)
|
|
215
|
+
│ ├── index.ts # Rule registry
|
|
216
|
+
│ ├── base/ # BaseRule class
|
|
217
|
+
│ ├── api-led/ # API-001, 002, 003
|
|
218
|
+
│ ├── complexity/ # MULE-801
|
|
219
|
+
│ ├── dataweave/ # DW-001, 002, 003
|
|
220
|
+
│ ├── documentation/ # MULE-601, 604
|
|
221
|
+
│ ├── error-handling/ # MULE-001, 003, 005, 007, 009
|
|
222
|
+
│ ├── experimental/ # EXP-001, 002, 003
|
|
223
|
+
│ ├── http/ # MULE-401, 402, 403
|
|
224
|
+
│ ├── logging/ # MULE-006, 301, 303
|
|
225
|
+
│ ├── naming/ # MULE-002, 101, 102
|
|
226
|
+
│ ├── performance/ # MULE-501, 502, 503
|
|
227
|
+
│ ├── security/ # MULE-004, 201, 202
|
|
228
|
+
│ ├── standards/ # MULE-008, 010, 701
|
|
229
|
+
│ ├── structure/ # MULE-802, 803, 804
|
|
230
|
+
│ └── yaml/ # YAML-001, 003, 004
|
|
231
|
+
└── formatters/ # Output formatters
|
|
232
|
+
├── TableFormatter.ts
|
|
233
|
+
├── JsonFormatter.ts
|
|
234
|
+
├── SarifFormatter.ts
|
|
235
|
+
└── HtmlFormatter.ts
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Rule Categories
|
|
239
|
+
|
|
240
|
+
| Category | ID Prefix | Count | Description |
|
|
241
|
+
|----------|-----------|-------|-------------|
|
|
242
|
+
| Error Handling | MULE-00X | 5 | Error handler presence and configuration |
|
|
243
|
+
| Naming | MULE-002, 10X | 3 | Flow, variable, and file naming |
|
|
244
|
+
| Security | MULE-004, 20X | 3 | Hardcoded values and security |
|
|
245
|
+
| Logging | MULE-006, 30X | 3 | Logger configuration |
|
|
246
|
+
| HTTP | MULE-40X | 3 | HTTP request configuration |
|
|
247
|
+
| Performance | MULE-50X | 3 | Performance anti-patterns |
|
|
248
|
+
| Documentation | MULE-60X | 2 | Component documentation |
|
|
249
|
+
| Standards | MULE-008, 010, 70X | 3 | Best practices |
|
|
250
|
+
| Complexity | MULE-80X | 1 | Cyclomatic complexity |
|
|
251
|
+
| Structure | MULE-80X | 3 | Project structure |
|
|
252
|
+
| YAML | YAML-XXX | 3 | Properties validation |
|
|
253
|
+
| DataWeave | DW-XXX | 3 | DWL file validation |
|
|
254
|
+
| API-Led | API-XXX | 3 | API-Led patterns |
|
|
255
|
+
| Experimental | EXP-XXX | 3 | Beta rules |
|
|
256
|
+
|
|
257
|
+
## Extension Points
|
|
258
|
+
|
|
259
|
+
### Adding Rules
|
|
260
|
+
|
|
261
|
+
1. Create class extending `BaseRule`
|
|
262
|
+
2. Implement `validate()` method
|
|
263
|
+
3. Register in `src/rules/index.ts`
|
|
264
|
+
4. Add documentation to `docs/rules-catalog.md`
|
|
265
|
+
|
|
266
|
+
### Adding Formatters
|
|
267
|
+
|
|
268
|
+
1. Create function implementing formatter interface
|
|
269
|
+
2. Add to factory in `src/formatters/index.ts`
|
|
270
|
+
3. Update `FormatterType` in types
|
|
271
|
+
|
|
272
|
+
## Error Handling
|
|
273
|
+
|
|
274
|
+
- **Parse Errors**: Captured and reported, don't stop scan
|
|
275
|
+
- **Rule Errors**: Caught and logged, continue with next rule
|
|
276
|
+
- **File Errors**: Reported in results, continue scanning
|
|
277
|
+
|
|
278
|
+
## Performance Specifications
|
|
279
|
+
|
|
280
|
+
| Metric | Target |
|
|
281
|
+
|--------|--------|
|
|
282
|
+
| Files per second | > 100 |
|
|
283
|
+
| Memory per file | < 10MB |
|
|
284
|
+
| Rule execution | < 50ms per rule |
|
|
285
|
+
| Total for 100 files | < 5 seconds |
|
|
286
|
+
|
|
287
|
+
## Exit Codes
|
|
288
|
+
|
|
289
|
+
| Code | Meaning |
|
|
290
|
+
|------|---------|
|
|
291
|
+
| 0 | No errors or warnings |
|
|
292
|
+
| 1 | At least one error found |
|
|
293
|
+
| 2 | Configuration error |
|
|
294
|
+
| 3 | Critical error (parse failure) |
|
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
# Extending mule-lint
|
|
2
|
+
|
|
3
|
+
> A guide for adding custom rules to mule-lint
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
mule-lint is designed to be easily extensible. You can add new rules by:
|
|
10
|
+
|
|
11
|
+
1. **Built-in rules**: Adding to the core package (for contributors)
|
|
12
|
+
2. **External rules**: Creating a separate npm package
|
|
13
|
+
3. **Local rules**: Adding project-specific rules via configuration
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Creating a New Rule
|
|
18
|
+
|
|
19
|
+
### Step 1: Create Rule File
|
|
20
|
+
|
|
21
|
+
Create a new file in the appropriate category folder:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# For a naming rule
|
|
25
|
+
touch src/rules/naming/ApiNamingRule.ts
|
|
26
|
+
|
|
27
|
+
# For a security rule
|
|
28
|
+
touch src/rules/security/ApiKeyExposureRule.ts
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Step 2: Implement the Rule
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { Document, Node } from '@xmldom/xmldom';
|
|
35
|
+
import { BaseRule, Issue, ValidationContext, Severity, RuleCategory } from '@types';
|
|
36
|
+
|
|
37
|
+
export class ApiNamingRule extends BaseRule {
|
|
38
|
+
// Required properties
|
|
39
|
+
id = 'MULE-103';
|
|
40
|
+
name = 'API Naming Convention';
|
|
41
|
+
description = 'API implementation flows should follow the pattern: {api-name}-{version}-{operation}';
|
|
42
|
+
severity: Severity = 'warning';
|
|
43
|
+
category: RuleCategory = 'naming';
|
|
44
|
+
|
|
45
|
+
// Optional: Link to documentation
|
|
46
|
+
docsUrl = 'https://your-wiki/mule-standards#api-naming';
|
|
47
|
+
|
|
48
|
+
validate(doc: Document, context: ValidationContext): Issue[] {
|
|
49
|
+
const issues: Issue[] = [];
|
|
50
|
+
|
|
51
|
+
// Your XPath query
|
|
52
|
+
const flows = this.select('//mule:flow', doc);
|
|
53
|
+
|
|
54
|
+
// Pattern for API flows
|
|
55
|
+
const apiPattern = /^[\w-]+-v\d+-(?:get|post|put|delete|patch)-[\w-]+$/;
|
|
56
|
+
|
|
57
|
+
for (const flow of flows) {
|
|
58
|
+
const name = this.getAttribute(flow, 'name');
|
|
59
|
+
|
|
60
|
+
// Only check flows that look like API implementations
|
|
61
|
+
if (name && name.includes('-api') && !apiPattern.test(name)) {
|
|
62
|
+
issues.push(this.createIssue(
|
|
63
|
+
flow,
|
|
64
|
+
`API flow "${name}" doesn't follow naming convention: {api-name}-v{N}-{method}-{resource}`,
|
|
65
|
+
{
|
|
66
|
+
suggestion: 'Example: orders-api-v1-get-order-by-id'
|
|
67
|
+
}
|
|
68
|
+
));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return issues;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Step 3: Register the Rule
|
|
78
|
+
|
|
79
|
+
Add to `src/rules/index.ts`:
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { ApiNamingRule } from './naming/ApiNamingRule';
|
|
83
|
+
|
|
84
|
+
export const RULES: Rule[] = [
|
|
85
|
+
// ... existing rules
|
|
86
|
+
new ApiNamingRule(),
|
|
87
|
+
];
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Step 4: Write Tests
|
|
91
|
+
|
|
92
|
+
Create `tests/unit/rules/ApiNamingRule.test.ts`:
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
import { ApiNamingRule } from '../../../src/rules/naming/ApiNamingRule';
|
|
96
|
+
import { parseXml } from '../../../src/core/XmlParser';
|
|
97
|
+
import { createMockContext } from '../../helpers';
|
|
98
|
+
|
|
99
|
+
describe('ApiNamingRule', () => {
|
|
100
|
+
const rule = new ApiNamingRule();
|
|
101
|
+
const context = createMockContext('api.xml');
|
|
102
|
+
|
|
103
|
+
it('should pass for correctly named API flow', () => {
|
|
104
|
+
const xml = `
|
|
105
|
+
<mule xmlns="http://www.mulesoft.org/schema/mule/core">
|
|
106
|
+
<flow name="orders-api-v1-get-order-by-id">
|
|
107
|
+
<logger message="test"/>
|
|
108
|
+
</flow>
|
|
109
|
+
</mule>
|
|
110
|
+
`;
|
|
111
|
+
const doc = parseXml(xml);
|
|
112
|
+
const issues = rule.validate(doc, context);
|
|
113
|
+
|
|
114
|
+
expect(issues).toHaveLength(0);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should fail for incorrectly named API flow', () => {
|
|
118
|
+
const xml = `
|
|
119
|
+
<mule xmlns="http://www.mulesoft.org/schema/mule/core">
|
|
120
|
+
<flow name="orders-api-getOrders">
|
|
121
|
+
<logger message="test"/>
|
|
122
|
+
</flow>
|
|
123
|
+
</mule>
|
|
124
|
+
`;
|
|
125
|
+
const doc = parseXml(xml);
|
|
126
|
+
const issues = rule.validate(doc, context);
|
|
127
|
+
|
|
128
|
+
expect(issues).toHaveLength(1);
|
|
129
|
+
expect(issues[0].ruleId).toBe('MULE-103');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should ignore non-API flows', () => {
|
|
133
|
+
const xml = `
|
|
134
|
+
<mule xmlns="http://www.mulesoft.org/schema/mule/core">
|
|
135
|
+
<flow name="utility-flow">
|
|
136
|
+
<logger message="test"/>
|
|
137
|
+
</flow>
|
|
138
|
+
</mule>
|
|
139
|
+
`;
|
|
140
|
+
const doc = parseXml(xml);
|
|
141
|
+
const issues = rule.validate(doc, context);
|
|
142
|
+
|
|
143
|
+
expect(issues).toHaveLength(0);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Step 5: Document the Rule
|
|
149
|
+
|
|
150
|
+
Create `docs/rules/MULE-103.md`:
|
|
151
|
+
|
|
152
|
+
```markdown
|
|
153
|
+
# MULE-103: API Naming Convention
|
|
154
|
+
|
|
155
|
+
## Overview
|
|
156
|
+
| Property | Value |
|
|
157
|
+
|----------|-------|
|
|
158
|
+
| **ID** | MULE-103 |
|
|
159
|
+
| **Severity** | Warning |
|
|
160
|
+
| **Category** | Naming |
|
|
161
|
+
| **Fixable** | No |
|
|
162
|
+
|
|
163
|
+
## Description
|
|
164
|
+
API implementation flows should follow a consistent naming pattern that includes the API name, version, HTTP method, and resource.
|
|
165
|
+
|
|
166
|
+
## Pattern
|
|
167
|
+
```
|
|
168
|
+
{api-name}-v{version}-{method}-{resource}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Examples
|
|
172
|
+
|
|
173
|
+
### ❌ Bad
|
|
174
|
+
```xml
|
|
175
|
+
<flow name="orders-api-getOrders">
|
|
176
|
+
<flow name="OrdersGetFlow">
|
|
177
|
+
<flow name="get-orders">
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### ✅ Good
|
|
181
|
+
```xml
|
|
182
|
+
<flow name="orders-api-v1-get-orders">
|
|
183
|
+
<flow name="orders-api-v1-post-order">
|
|
184
|
+
<flow name="orders-api-v1-get-order-by-id">
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Configuration
|
|
188
|
+
```json
|
|
189
|
+
{
|
|
190
|
+
"MULE-103": {
|
|
191
|
+
"enabled": true,
|
|
192
|
+
"options": {
|
|
193
|
+
"pattern": "^[\\w-]+-v\\d+-(?:get|post|put|delete|patch)-[\\w-]+$"
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Related Rules
|
|
200
|
+
- MULE-002: Flow Naming Convention
|
|
201
|
+
- MULE-101: Flow Name Casing
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Creating External Rule Packages
|
|
207
|
+
|
|
208
|
+
For organization-specific rules, create a separate npm package:
|
|
209
|
+
|
|
210
|
+
### Package Structure
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
mule-lint-rules-acme/
|
|
214
|
+
├── src/
|
|
215
|
+
│ ├── rules/
|
|
216
|
+
│ │ ├── AcmeLoggingRule.ts
|
|
217
|
+
│ │ └── AcmeSecurityRule.ts
|
|
218
|
+
│ └── index.ts
|
|
219
|
+
├── package.json
|
|
220
|
+
└── tsconfig.json
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### package.json
|
|
224
|
+
|
|
225
|
+
```json
|
|
226
|
+
{
|
|
227
|
+
"name": "@acme/mule-lint-rules",
|
|
228
|
+
"version": "1.0.0",
|
|
229
|
+
"main": "dist/index.js",
|
|
230
|
+
"types": "dist/index.d.ts",
|
|
231
|
+
"peerDependencies": {
|
|
232
|
+
"mule-lint": "^1.0.0"
|
|
233
|
+
},
|
|
234
|
+
"keywords": ["mule-lint", "mule-lint-rules"]
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Export Rules
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
// src/index.ts
|
|
242
|
+
import { Rule } from 'mule-lint';
|
|
243
|
+
import { AcmeLoggingRule } from './rules/AcmeLoggingRule';
|
|
244
|
+
import { AcmeSecurityRule } from './rules/AcmeSecurityRule';
|
|
245
|
+
|
|
246
|
+
export const rules: Rule[] = [
|
|
247
|
+
new AcmeLoggingRule(),
|
|
248
|
+
new AcmeSecurityRule(),
|
|
249
|
+
];
|
|
250
|
+
|
|
251
|
+
// Named exports for individual rules
|
|
252
|
+
export { AcmeLoggingRule, AcmeSecurityRule };
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Configure mule-lint
|
|
256
|
+
|
|
257
|
+
```json
|
|
258
|
+
// .mulelintrc.json
|
|
259
|
+
{
|
|
260
|
+
"extends": ["@acme/mule-lint-rules"],
|
|
261
|
+
"rules": {
|
|
262
|
+
"ACME-001": { "enabled": true },
|
|
263
|
+
"ACME-002": { "enabled": true, "severity": "error" }
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## Local/Project-Specific Rules
|
|
271
|
+
|
|
272
|
+
For one-off project rules without creating a package:
|
|
273
|
+
|
|
274
|
+
### Configuration
|
|
275
|
+
|
|
276
|
+
```json
|
|
277
|
+
// .mulelintrc.json
|
|
278
|
+
{
|
|
279
|
+
"customRulesPath": "./lint-rules",
|
|
280
|
+
"rules": {
|
|
281
|
+
"PROJECT-001": { "enabled": true }
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Local Rule File
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
// lint-rules/ProjectSpecificRule.ts
|
|
290
|
+
import { BaseRule, Issue, ValidationContext } from 'mule-lint';
|
|
291
|
+
import { Document } from '@xmldom/xmldom';
|
|
292
|
+
|
|
293
|
+
export class ProjectSpecificRule extends BaseRule {
|
|
294
|
+
id = 'PROJECT-001';
|
|
295
|
+
name = 'Project Specific Check';
|
|
296
|
+
description = 'Custom check for this project';
|
|
297
|
+
severity = 'warning' as const;
|
|
298
|
+
category = 'standards' as const;
|
|
299
|
+
|
|
300
|
+
validate(doc: Document, context: ValidationContext): Issue[] {
|
|
301
|
+
// Your custom logic
|
|
302
|
+
return [];
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## Rule Best Practices
|
|
310
|
+
|
|
311
|
+
### 1. Be Specific with XPath
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
// ❌ Too broad - may have false positives
|
|
315
|
+
const nodes = this.select('//*[@url]', doc);
|
|
316
|
+
|
|
317
|
+
// ✅ Specific - targets only http:request
|
|
318
|
+
const nodes = this.select('//http:request[@url]', doc);
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### 2. Provide Actionable Messages
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
// ❌ Vague
|
|
325
|
+
message: 'Naming violation detected'
|
|
326
|
+
|
|
327
|
+
// ✅ Specific and actionable
|
|
328
|
+
message: `Flow "${name}" should end with "-flow". Rename to "${name}-flow"`
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### 3. Include Suggestions
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
issues.push(this.createIssue(node, message, {
|
|
335
|
+
suggestion: 'Add category attribute: category="com.acme.integration"'
|
|
336
|
+
}));
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### 4. Support Configuration
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
validate(doc: Document, context: ValidationContext): Issue[] {
|
|
343
|
+
// Read from rule config
|
|
344
|
+
const suffix = context.config.options?.flowSuffix ?? '-flow';
|
|
345
|
+
|
|
346
|
+
// Use in validation
|
|
347
|
+
if (!name.endsWith(suffix)) {
|
|
348
|
+
// ...
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### 5. Handle Edge Cases
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
validate(doc: Document, context: ValidationContext): Issue[] {
|
|
357
|
+
const flows = this.select('//mule:flow', doc);
|
|
358
|
+
|
|
359
|
+
for (const flow of flows) {
|
|
360
|
+
const name = this.getAttribute(flow, 'name');
|
|
361
|
+
|
|
362
|
+
// Guard against missing attributes
|
|
363
|
+
if (!name) continue;
|
|
364
|
+
|
|
365
|
+
// Skip excluded patterns
|
|
366
|
+
if (this.isExcluded(name, context)) continue;
|
|
367
|
+
|
|
368
|
+
// Actual validation
|
|
369
|
+
// ...
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## VS Code Extension Integration
|
|
377
|
+
|
|
378
|
+
When building a VS Code extension, import rules directly:
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
import { LintEngine, RULES, Rule, Issue } from 'mule-lint';
|
|
382
|
+
|
|
383
|
+
class MuleLintDiagnosticProvider {
|
|
384
|
+
private engine: LintEngine;
|
|
385
|
+
|
|
386
|
+
constructor() {
|
|
387
|
+
this.engine = new LintEngine({
|
|
388
|
+
rules: RULES,
|
|
389
|
+
// Or filter rules
|
|
390
|
+
// rules: RULES.filter(r => r.severity === 'error')
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
async provideDiagnostics(document: vscode.TextDocument): Promise<vscode.Diagnostic[]> {
|
|
395
|
+
const issues = await this.engine.scanContent(
|
|
396
|
+
document.getText(),
|
|
397
|
+
document.uri.fsPath
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
return issues.map(issue => this.toDiagnostic(issue));
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
private toDiagnostic(issue: Issue): vscode.Diagnostic {
|
|
404
|
+
return new vscode.Diagnostic(
|
|
405
|
+
new vscode.Range(issue.line - 1, 0, issue.line - 1, 100),
|
|
406
|
+
`[${issue.ruleId}] ${issue.message}`,
|
|
407
|
+
this.toSeverity(issue.severity)
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
## Contributing Rules Upstream
|
|
416
|
+
|
|
417
|
+
To contribute rules to the core mule-lint package:
|
|
418
|
+
|
|
419
|
+
1. Fork the repository
|
|
420
|
+
2. Create a feature branch: `feature/MULE-XXX-rule-name`
|
|
421
|
+
3. Implement the rule following this guide
|
|
422
|
+
4. Write comprehensive tests
|
|
423
|
+
5. Document the rule
|
|
424
|
+
6. Submit a Pull Request
|
|
425
|
+
|
|
426
|
+
See [CONTRIBUTING.md](../CONTRIBUTING.md) for detailed guidelines.
|