@sfdxy/mule-lint 1.7.2 → 1.8.3

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 (157) hide show
  1. package/README.md +35 -0
  2. package/dist/bin/mule-lint-mcp.d.ts +3 -0
  3. package/dist/bin/mule-lint-mcp.d.ts.map +1 -0
  4. package/dist/bin/mule-lint-mcp.js +13 -0
  5. package/dist/bin/mule-lint-mcp.js.map +1 -0
  6. package/dist/package.json +6 -3
  7. package/dist/src/core/ComplexityCalculator.d.ts.map +1 -1
  8. package/dist/src/core/ComplexityCalculator.js +8 -8
  9. package/dist/src/core/ComplexityCalculator.js.map +1 -1
  10. package/dist/src/core/FileScanner.d.ts.map +1 -1
  11. package/dist/src/core/FileScanner.js +11 -12
  12. package/dist/src/core/FileScanner.js.map +1 -1
  13. package/dist/src/core/XPathHelper.d.ts.map +1 -1
  14. package/dist/src/core/XPathHelper.js +23 -23
  15. package/dist/src/core/XPathHelper.js.map +1 -1
  16. package/dist/src/core/XmlParser.js +1 -1
  17. package/dist/src/core/XmlParser.js.map +1 -1
  18. package/dist/src/core/YamlParser.d.ts.map +1 -1
  19. package/dist/src/core/YamlParser.js +1 -1
  20. package/dist/src/core/YamlParser.js.map +1 -1
  21. package/dist/src/engine/LintEngine.d.ts.map +1 -1
  22. package/dist/src/engine/LintEngine.js +6 -4
  23. package/dist/src/engine/LintEngine.js.map +1 -1
  24. package/dist/src/formatters/CsvFormatter.d.ts.map +1 -1
  25. package/dist/src/formatters/CsvFormatter.js +6 -4
  26. package/dist/src/formatters/CsvFormatter.js.map +1 -1
  27. package/dist/src/formatters/HtmlFormatter.d.ts.map +1 -1
  28. package/dist/src/formatters/HtmlFormatter.js +12 -7
  29. package/dist/src/formatters/HtmlFormatter.js.map +1 -1
  30. package/dist/src/formatters/SarifFormatter.d.ts.map +1 -1
  31. package/dist/src/formatters/SarifFormatter.js +20 -10
  32. package/dist/src/formatters/SarifFormatter.js.map +1 -1
  33. package/dist/src/formatters/TableFormatter.d.ts.map +1 -1
  34. package/dist/src/formatters/TableFormatter.js +1 -1
  35. package/dist/src/formatters/TableFormatter.js.map +1 -1
  36. package/dist/src/mcp/index.d.ts +13 -0
  37. package/dist/src/mcp/index.d.ts.map +1 -0
  38. package/dist/src/mcp/index.js +375 -0
  39. package/dist/src/mcp/index.js.map +1 -0
  40. package/dist/src/rules/api-led/ApiLedRules.d.ts.map +1 -1
  41. package/dist/src/rules/api-led/ApiLedRules.js +3 -1
  42. package/dist/src/rules/api-led/ApiLedRules.js.map +1 -1
  43. package/dist/src/rules/base/BaseRule.d.ts.map +1 -1
  44. package/dist/src/rules/base/BaseRule.js +1 -1
  45. package/dist/src/rules/base/BaseRule.js.map +1 -1
  46. package/dist/src/rules/complexity/FlowComplexityRule.d.ts.map +1 -1
  47. package/dist/src/rules/complexity/FlowComplexityRule.js +3 -5
  48. package/dist/src/rules/complexity/FlowComplexityRule.js.map +1 -1
  49. package/dist/src/rules/dataweave/DataWeaveRules.d.ts.map +1 -1
  50. package/dist/src/rules/dataweave/DataWeaveRules.js +5 -5
  51. package/dist/src/rules/dataweave/DataWeaveRules.js.map +1 -1
  52. package/dist/src/rules/dataweave/Java17DWErrorHandlingRule.d.ts +21 -0
  53. package/dist/src/rules/dataweave/Java17DWErrorHandlingRule.d.ts.map +1 -0
  54. package/dist/src/rules/dataweave/Java17DWErrorHandlingRule.js +162 -0
  55. package/dist/src/rules/dataweave/Java17DWErrorHandlingRule.js.map +1 -0
  56. package/dist/src/rules/documentation/FlowDescriptionRule.d.ts.map +1 -1
  57. package/dist/src/rules/documentation/FlowDescriptionRule.js +1 -1
  58. package/dist/src/rules/documentation/FlowDescriptionRule.js.map +1 -1
  59. package/dist/src/rules/documentation/MissingDocNameRule.d.ts.map +1 -1
  60. package/dist/src/rules/documentation/MissingDocNameRule.js +1 -1
  61. package/dist/src/rules/documentation/MissingDocNameRule.js.map +1 -1
  62. package/dist/src/rules/error-handling/CorrelationIdRule.d.ts.map +1 -1
  63. package/dist/src/rules/error-handling/CorrelationIdRule.js +1 -1
  64. package/dist/src/rules/error-handling/CorrelationIdRule.js.map +1 -1
  65. package/dist/src/rules/error-handling/GenericErrorRule.d.ts.map +1 -1
  66. package/dist/src/rules/error-handling/GenericErrorRule.js +2 -5
  67. package/dist/src/rules/error-handling/GenericErrorRule.js.map +1 -1
  68. package/dist/src/rules/error-handling/GlobalErrorHandlerRule.d.ts.map +1 -1
  69. package/dist/src/rules/error-handling/GlobalErrorHandlerRule.js +1 -1
  70. package/dist/src/rules/error-handling/GlobalErrorHandlerRule.js.map +1 -1
  71. package/dist/src/rules/error-handling/HttpStatusRule.d.ts.map +1 -1
  72. package/dist/src/rules/error-handling/HttpStatusRule.js +1 -1
  73. package/dist/src/rules/error-handling/HttpStatusRule.js.map +1 -1
  74. package/dist/src/rules/error-handling/MissingErrorHandlerRule.d.ts.map +1 -1
  75. package/dist/src/rules/error-handling/MissingErrorHandlerRule.js +1 -1
  76. package/dist/src/rules/error-handling/MissingErrorHandlerRule.js.map +1 -1
  77. package/dist/src/rules/experimental/ExperimentalRules.d.ts.map +1 -1
  78. package/dist/src/rules/experimental/ExperimentalRules.js +1 -1
  79. package/dist/src/rules/experimental/ExperimentalRules.js.map +1 -1
  80. package/dist/src/rules/http/HttpContentTypeRule.d.ts.map +1 -1
  81. package/dist/src/rules/http/HttpContentTypeRule.js +1 -1
  82. package/dist/src/rules/http/HttpContentTypeRule.js.map +1 -1
  83. package/dist/src/rules/http/HttpTimeoutRule.d.ts.map +1 -1
  84. package/dist/src/rules/http/HttpTimeoutRule.js +1 -1
  85. package/dist/src/rules/http/HttpTimeoutRule.js.map +1 -1
  86. package/dist/src/rules/http/HttpUserAgentRule.d.ts.map +1 -1
  87. package/dist/src/rules/http/HttpUserAgentRule.js +1 -1
  88. package/dist/src/rules/http/HttpUserAgentRule.js.map +1 -1
  89. package/dist/src/rules/index.d.ts +1 -1
  90. package/dist/src/rules/index.d.ts.map +1 -1
  91. package/dist/src/rules/index.js +7 -5
  92. package/dist/src/rules/index.js.map +1 -1
  93. package/dist/src/rules/logging/LoggerCategoryRule.js +2 -2
  94. package/dist/src/rules/logging/LoggerCategoryRule.js.map +1 -1
  95. package/dist/src/rules/logging/LoggerInUntilSuccessfulRule.js +1 -1
  96. package/dist/src/rules/logging/LoggerInUntilSuccessfulRule.js.map +1 -1
  97. package/dist/src/rules/logging/LoggerPayloadRule.d.ts.map +1 -1
  98. package/dist/src/rules/logging/LoggerPayloadRule.js +3 -3
  99. package/dist/src/rules/logging/LoggerPayloadRule.js.map +1 -1
  100. package/dist/src/rules/naming/FlowCasingRule.d.ts.map +1 -1
  101. package/dist/src/rules/naming/FlowCasingRule.js +1 -1
  102. package/dist/src/rules/naming/FlowCasingRule.js.map +1 -1
  103. package/dist/src/rules/naming/FlowNamingRule.d.ts.map +1 -1
  104. package/dist/src/rules/naming/FlowNamingRule.js +3 -1
  105. package/dist/src/rules/naming/FlowNamingRule.js.map +1 -1
  106. package/dist/src/rules/naming/VariableNamingRule.d.ts.map +1 -1
  107. package/dist/src/rules/naming/VariableNamingRule.js +3 -3
  108. package/dist/src/rules/naming/VariableNamingRule.js.map +1 -1
  109. package/dist/src/rules/performance/AsyncErrorHandlerRule.d.ts.map +1 -1
  110. package/dist/src/rules/performance/AsyncErrorHandlerRule.js +1 -1
  111. package/dist/src/rules/performance/AsyncErrorHandlerRule.js.map +1 -1
  112. package/dist/src/rules/performance/LargeChoiceBlockRule.d.ts.map +1 -1
  113. package/dist/src/rules/performance/LargeChoiceBlockRule.js +1 -1
  114. package/dist/src/rules/performance/LargeChoiceBlockRule.js.map +1 -1
  115. package/dist/src/rules/performance/ScatterGatherRoutesRule.d.ts.map +1 -1
  116. package/dist/src/rules/performance/ScatterGatherRoutesRule.js +1 -1
  117. package/dist/src/rules/performance/ScatterGatherRoutesRule.js.map +1 -1
  118. package/dist/src/rules/security/HardcodedCredentialsRule.d.ts.map +1 -1
  119. package/dist/src/rules/security/HardcodedCredentialsRule.js +2 -2
  120. package/dist/src/rules/security/HardcodedCredentialsRule.js.map +1 -1
  121. package/dist/src/rules/security/HardcodedHttpRule.d.ts.map +1 -1
  122. package/dist/src/rules/security/HardcodedHttpRule.js +2 -6
  123. package/dist/src/rules/security/HardcodedHttpRule.js.map +1 -1
  124. package/dist/src/rules/security/InsecureTlsRule.d.ts.map +1 -1
  125. package/dist/src/rules/security/InsecureTlsRule.js +2 -2
  126. package/dist/src/rules/security/InsecureTlsRule.js.map +1 -1
  127. package/dist/src/rules/standards/ChoiceAntiPatternRule.d.ts.map +1 -1
  128. package/dist/src/rules/standards/ChoiceAntiPatternRule.js +2 -2
  129. package/dist/src/rules/standards/ChoiceAntiPatternRule.js.map +1 -1
  130. package/dist/src/rules/standards/DeprecatedComponentRule.d.ts.map +1 -1
  131. package/dist/src/rules/standards/DeprecatedComponentRule.js +1 -1
  132. package/dist/src/rules/standards/DeprecatedComponentRule.js.map +1 -1
  133. package/dist/src/rules/standards/DwlStandardsRule.d.ts.map +1 -1
  134. package/dist/src/rules/standards/DwlStandardsRule.js +1 -1
  135. package/dist/src/rules/standards/DwlStandardsRule.js.map +1 -1
  136. package/dist/src/rules/structure/StructureRules.d.ts.map +1 -1
  137. package/dist/src/rules/structure/StructureRules.js +6 -9
  138. package/dist/src/rules/structure/StructureRules.js.map +1 -1
  139. package/dist/src/rules/yaml/YamlRules.d.ts.map +1 -1
  140. package/dist/src/rules/yaml/YamlRules.js +13 -9
  141. package/dist/src/rules/yaml/YamlRules.js.map +1 -1
  142. package/dist/src/types/Config.d.ts.map +1 -1
  143. package/dist/src/types/Config.js +1 -5
  144. package/dist/src/types/Config.js.map +1 -1
  145. package/docs/README.md +95 -0
  146. package/docs/best-practices/documentation-standards.md +82 -0
  147. package/docs/best-practices/folder-structure.md +61 -0
  148. package/docs/best-practices/mulesoft-best-practices.md +902 -0
  149. package/docs/best-practices/rules-catalog.md +736 -0
  150. package/docs/linter/architecture.md +294 -0
  151. package/docs/linter/extending.md +426 -0
  152. package/docs/linter/folder-structure.md +251 -0
  153. package/docs/linter/images/html-report-dashboard.png +0 -0
  154. package/docs/linter/naming-conventions.md +300 -0
  155. package/docs/linter/rule-engine.md +532 -0
  156. package/docs/mcp-design.md +80 -0
  157. package/package.json +7 -4
@@ -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.