bpmnlint 11.12.1 → 11.13.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 +20 -20
- package/README.md +203 -203
- package/bin/bpmnlint.js +437 -437
- package/config/all.js +38 -37
- package/config/correctness.js +12 -12
- package/config/recommended.js +29 -29
- package/lib/index.js +4 -4
- package/lib/linter.js +542 -542
- package/lib/resolver/helper.js +12 -12
- package/lib/resolver/node-resolver.js +135 -135
- package/lib/resolver/static-resolver.js +47 -47
- package/lib/support/compile-config.js +200 -200
- package/lib/test-rule.js +107 -107
- package/lib/testers/helper.js +72 -72
- package/lib/testers/rule-tester.d.ts +36 -36
- package/lib/testers/rule-tester.js +146 -146
- package/lib/traverse.js +56 -56
- package/lib/types.d.ts +80 -80
- package/package.json +74 -73
- package/rules/ad-hoc-sub-process.js +43 -43
- package/rules/conditional-event.js +51 -51
- package/rules/conditional-flows.js +57 -57
- package/rules/end-event-required.js +45 -45
- package/rules/event-based-gateway.js +44 -44
- package/rules/event-sub-process-typed-start-event.js +43 -43
- package/rules/fake-join.js +40 -40
- package/rules/global.js +145 -145
- package/rules/helper.js +123 -123
- package/rules/label-required.js +75 -75
- package/rules/link-event.js +106 -106
- package/rules/no-bpmndi.js +157 -157
- package/rules/no-complex-gateway.js +2 -2
- package/rules/no-disconnected.js +82 -82
- package/rules/no-duplicate-sequence-flows.js +69 -69
- package/rules/no-gateway-join-fork.js +35 -35
- package/rules/no-implicit-end.js +98 -98
- package/rules/no-implicit-split.js +55 -55
- package/rules/no-implicit-start.js +69 -69
- package/rules/no-inclusive-gateway.js +2 -2
- package/rules/no-overlapping-elements.js +196 -196
- package/rules/single-blank-start-event.js +47 -47
- package/rules/single-event-definition.js +33 -33
- package/rules/standard-size.js +235 -0
- package/rules/start-event-required.js +45 -45
- package/rules/sub-process-blank-start-event.js +43 -43
- package/rules/superfluous-gateway.js +36 -36
- package/rules/superfluous-termination.js +70 -70
package/lib/linter.js
CHANGED
|
@@ -1,543 +1,543 @@
|
|
|
1
|
-
const testRule = require('./test-rule');
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* @typedef { import('./types.js').ModdleElement } ModdleElement
|
|
6
|
-
*
|
|
7
|
-
* @typedef { import('./types.js').ReportingCategory } ReportingCategory
|
|
8
|
-
* @typedef { import('./types.js').ReportingCategoryMap } ReportingCategoryMap
|
|
9
|
-
* @typedef { import('./types.js').RuleErrorCategory } RuleErrorCategory
|
|
10
|
-
*
|
|
11
|
-
* @typedef { import('./types.js').Resolver } Resolver
|
|
12
|
-
* @typedef { import('./types.js').Config } Config
|
|
13
|
-
*
|
|
14
|
-
* @typedef { import('./types.js').RuleFactory } RuleFactory
|
|
15
|
-
*
|
|
16
|
-
* @typedef { import('./types.js').RuleDefinition } RuleDefinition
|
|
17
|
-
* @typedef { import('./types.js').RuleConfigs } RuleConfigs
|
|
18
|
-
* @typedef { import('./types.js').RuleConfig } RuleConfig
|
|
19
|
-
*
|
|
20
|
-
* @typedef { {
|
|
21
|
-
* name: string,
|
|
22
|
-
* rule: RuleDefinition,
|
|
23
|
-
* config: any,
|
|
24
|
-
* category: ReportingCategory
|
|
25
|
-
* } } ResolvedRuleDefinition
|
|
26
|
-
*
|
|
27
|
-
* @typedef { import('./types.js').Report } Report
|
|
28
|
-
* @typedef { Report & Pick<RuleDefinition, 'meta'> & {
|
|
29
|
-
* category: ReportingCategory | RuleErrorCategory
|
|
30
|
-
* } } AnnotatedReport
|
|
31
|
-
*
|
|
32
|
-
* @typedef { import('./types.js').TransformRuleFn } TransformRuleFn
|
|
33
|
-
*
|
|
34
|
-
* @typedef { Record<string, AnnotatedReport[]> } LintResults
|
|
35
|
-
*/
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Transform a rule after it is loaded.
|
|
40
|
-
*
|
|
41
|
-
* @param { RuleDefinition } rule
|
|
42
|
-
* @param { { pkg: string, ruleName: string } } options
|
|
43
|
-
*/
|
|
44
|
-
const noopTransformRule = (rule, options) => {
|
|
45
|
-
return rule;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* @typeof { ReportingCategoryMap }
|
|
51
|
-
*/
|
|
52
|
-
const categoryMap = {
|
|
53
|
-
0: 'off',
|
|
54
|
-
1: 'warn',
|
|
55
|
-
2: 'error',
|
|
56
|
-
3: 'info'
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* @type { RuleErrorCategory }
|
|
61
|
-
*/
|
|
62
|
-
const ruleErrorCategory = 'rule-error';
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* @param { {
|
|
66
|
-
* config?: Config,
|
|
67
|
-
* resolver: Resolver,
|
|
68
|
-
* transformRule?: TransformRuleFn
|
|
69
|
-
* } } options
|
|
70
|
-
*/
|
|
71
|
-
function Linter(options) {
|
|
72
|
-
|
|
73
|
-
const {
|
|
74
|
-
config = {},
|
|
75
|
-
resolver,
|
|
76
|
-
transformRule = noopTransformRule
|
|
77
|
-
} = options || {};
|
|
78
|
-
|
|
79
|
-
if (typeof resolver === 'undefined') {
|
|
80
|
-
throw new Error('must provide <options.resolver>');
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
this.config = config;
|
|
84
|
-
this.resolver = resolver;
|
|
85
|
-
|
|
86
|
-
this.transformRule = transformRule;
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* @type { Record<string, RuleDefinition> }
|
|
90
|
-
*/
|
|
91
|
-
this.cachedRules = {};
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* @type { Record<string, Config> }
|
|
95
|
-
*/
|
|
96
|
-
this.cachedConfigs = {};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
module.exports = Linter;
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Applies a rule on the moddleRoot and adds reports to the finalReport
|
|
104
|
-
*
|
|
105
|
-
* @param { ModdleElement } moddleRoot
|
|
106
|
-
*
|
|
107
|
-
* @param { ResolvedRuleDefinition } ruleDefinition
|
|
108
|
-
*
|
|
109
|
-
* @return { AnnotatedReport[] } reports
|
|
110
|
-
*/
|
|
111
|
-
Linter.prototype.applyRule = function applyRule(moddleRoot, ruleDefinition) {
|
|
112
|
-
|
|
113
|
-
const {
|
|
114
|
-
config,
|
|
115
|
-
rule,
|
|
116
|
-
category,
|
|
117
|
-
name
|
|
118
|
-
} = ruleDefinition;
|
|
119
|
-
|
|
120
|
-
try {
|
|
121
|
-
|
|
122
|
-
const reports = testRule({
|
|
123
|
-
moddleRoot,
|
|
124
|
-
rule,
|
|
125
|
-
config
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
return reports.map(function(report) {
|
|
129
|
-
return {
|
|
130
|
-
...report,
|
|
131
|
-
meta: rule.meta,
|
|
132
|
-
category
|
|
133
|
-
};
|
|
134
|
-
});
|
|
135
|
-
} catch (e) {
|
|
136
|
-
console.error('rule <' + name + '> failed with error: ', e);
|
|
137
|
-
|
|
138
|
-
return [
|
|
139
|
-
{
|
|
140
|
-
message: /** @type { Error } */ (e).message,
|
|
141
|
-
category: ruleErrorCategory
|
|
142
|
-
}
|
|
143
|
-
];
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* @param { string } name
|
|
151
|
-
* @param { RuleConfig } config
|
|
152
|
-
*
|
|
153
|
-
* @return { Promise<RuleDefinition> }
|
|
154
|
-
*/
|
|
155
|
-
Linter.prototype.resolveRule = function(name, config) {
|
|
156
|
-
|
|
157
|
-
const {
|
|
158
|
-
pkg,
|
|
159
|
-
ruleName
|
|
160
|
-
} = this.parseRuleName(name);
|
|
161
|
-
|
|
162
|
-
const id = `${pkg}-${ruleName}`;
|
|
163
|
-
|
|
164
|
-
const rule = this.cachedRules[id];
|
|
165
|
-
|
|
166
|
-
if (rule) {
|
|
167
|
-
return Promise.resolve(rule);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return Promise.resolve(this.resolver.resolveRule(pkg, ruleName)).then((ruleFactory) => {
|
|
171
|
-
|
|
172
|
-
if (!ruleFactory) {
|
|
173
|
-
throw new Error(`unknown rule <${name}>`);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const rule = this.cachedRules[id] = this.transformRule(ruleFactory(config), { pkg, ruleName });
|
|
177
|
-
|
|
178
|
-
return rule;
|
|
179
|
-
});
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* @param {string} name
|
|
184
|
-
*
|
|
185
|
-
* @return {Promise<Config>}
|
|
186
|
-
*/
|
|
187
|
-
Linter.prototype.resolveConfig = function(name) {
|
|
188
|
-
|
|
189
|
-
const {
|
|
190
|
-
pkg,
|
|
191
|
-
configName
|
|
192
|
-
} = this.parseConfigName(name);
|
|
193
|
-
|
|
194
|
-
const id = `${pkg}-${configName}`;
|
|
195
|
-
|
|
196
|
-
const config = this.cachedConfigs[id];
|
|
197
|
-
|
|
198
|
-
if (config) {
|
|
199
|
-
return Promise.resolve(config);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
return Promise.resolve(this.resolver.resolveConfig(pkg, configName)).then((config) => {
|
|
203
|
-
|
|
204
|
-
if (!config) {
|
|
205
|
-
throw new Error(`unknown config <${name}>`);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const actualConfig = this.cachedConfigs[id] = this.normalizeConfig(config, pkg);
|
|
209
|
-
|
|
210
|
-
return actualConfig;
|
|
211
|
-
});
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Take a linter config and return list of resolved rules.
|
|
216
|
-
*
|
|
217
|
-
* @param { Config } config
|
|
218
|
-
*
|
|
219
|
-
* @return { Promise<ResolvedRuleDefinition[]> }
|
|
220
|
-
*/
|
|
221
|
-
Linter.prototype.resolveRules = function(config) {
|
|
222
|
-
|
|
223
|
-
return this.resolveConfiguredRules(config).then((rulesConfig) => {
|
|
224
|
-
|
|
225
|
-
// parse rule values
|
|
226
|
-
const parsedRules = Object.entries(rulesConfig).map(([ name, value ]) => {
|
|
227
|
-
const {
|
|
228
|
-
category,
|
|
229
|
-
config
|
|
230
|
-
} = this.parseRuleValue(value);
|
|
231
|
-
|
|
232
|
-
return {
|
|
233
|
-
name,
|
|
234
|
-
category,
|
|
235
|
-
config
|
|
236
|
-
};
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
// filter only for enabled rules
|
|
240
|
-
const enabledRules = parsedRules.filter(definition => definition.category !== 'off');
|
|
241
|
-
|
|
242
|
-
// load enabled rules
|
|
243
|
-
const loaders = enabledRules.map((definition) => {
|
|
244
|
-
|
|
245
|
-
const {
|
|
246
|
-
name,
|
|
247
|
-
config
|
|
248
|
-
} = definition;
|
|
249
|
-
|
|
250
|
-
return this.resolveRule(name, config).then(function(rule) {
|
|
251
|
-
return {
|
|
252
|
-
...definition,
|
|
253
|
-
rule
|
|
254
|
-
};
|
|
255
|
-
});
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
return Promise.all(loaders);
|
|
259
|
-
});
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* @param { Config } config
|
|
264
|
-
*
|
|
265
|
-
* @return { Promise<RuleConfigs> }
|
|
266
|
-
*/
|
|
267
|
-
Linter.prototype.resolveConfiguredRules = function(config) {
|
|
268
|
-
|
|
269
|
-
let parents = config.extends;
|
|
270
|
-
|
|
271
|
-
if (typeof parents === 'string') {
|
|
272
|
-
parents = [ parents ];
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
if (typeof parents === 'undefined') {
|
|
276
|
-
parents = [];
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
return Promise.all(
|
|
280
|
-
parents.map((configName) => {
|
|
281
|
-
return this.resolveConfig(configName).then((config) => {
|
|
282
|
-
return this.resolveConfiguredRules(config);
|
|
283
|
-
});
|
|
284
|
-
})
|
|
285
|
-
).then((inheritedRules) => {
|
|
286
|
-
|
|
287
|
-
const overrideRules = this.normalizeConfig(config, 'bpmnlint').rules;
|
|
288
|
-
|
|
289
|
-
const rules = [ ...inheritedRules, overrideRules ].reduce((rules, currentRules) => {
|
|
290
|
-
return {
|
|
291
|
-
...rules,
|
|
292
|
-
...currentRules
|
|
293
|
-
};
|
|
294
|
-
}, {});
|
|
295
|
-
|
|
296
|
-
return rules;
|
|
297
|
-
});
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Lint the given model root, using the specified linter config.
|
|
303
|
-
*
|
|
304
|
-
* @param { ModdleElement } moddleRoot
|
|
305
|
-
* @param { Config } [config] the bpmnlint configuration to use
|
|
306
|
-
*
|
|
307
|
-
* @return { Promise<LintResults> } lint results, keyed by rule names
|
|
308
|
-
*/
|
|
309
|
-
Linter.prototype.lint = function(moddleRoot, config) {
|
|
310
|
-
|
|
311
|
-
config = config || this.config;
|
|
312
|
-
|
|
313
|
-
// load rules
|
|
314
|
-
return this.resolveRules(config).then((ruleDefinitions) => {
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* @type { LintResults }
|
|
318
|
-
*/
|
|
319
|
-
const allReports = {};
|
|
320
|
-
|
|
321
|
-
ruleDefinitions.forEach((ruleDefinition) => {
|
|
322
|
-
|
|
323
|
-
const {
|
|
324
|
-
name
|
|
325
|
-
} = ruleDefinition;
|
|
326
|
-
|
|
327
|
-
const reports = this.applyRule(moddleRoot, ruleDefinition);
|
|
328
|
-
|
|
329
|
-
if (reports.length) {
|
|
330
|
-
allReports[name] = reports;
|
|
331
|
-
}
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
return allReports;
|
|
335
|
-
});
|
|
336
|
-
};
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* @param { import('./types.js').RuleConfig } value
|
|
340
|
-
*
|
|
341
|
-
* @return { { config: any, category: ReportingCategory } }
|
|
342
|
-
*/
|
|
343
|
-
Linter.prototype.parseRuleValue = function(value) {
|
|
344
|
-
|
|
345
|
-
let category;
|
|
346
|
-
let config;
|
|
347
|
-
|
|
348
|
-
if (Array.isArray(value)) {
|
|
349
|
-
category = value[0];
|
|
350
|
-
config = value[1];
|
|
351
|
-
} else {
|
|
352
|
-
category = value;
|
|
353
|
-
config = {};
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// normalize rule flag to <error> and <warn> which
|
|
357
|
-
// may be upper case or a number at this point
|
|
358
|
-
if (typeof category === 'string') {
|
|
359
|
-
category = category.toLowerCase();
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
category = categoryMap[category] || category;
|
|
363
|
-
|
|
364
|
-
return {
|
|
365
|
-
config,
|
|
366
|
-
category
|
|
367
|
-
};
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
Linter.prototype.parseRuleName = function(name, localPackage = 'bpmnlint') {
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* We recognize the following rule name patterns:
|
|
374
|
-
*
|
|
375
|
-
* {RULE_NAME} => PKG = 'bpmnlint'
|
|
376
|
-
* bpmnlint/{RULE_NAME} => PKG = 'bpmnlint'
|
|
377
|
-
* {PACKAGE_SHORTCUT}/{RULE_NAME} => PKG = 'bpmnlint-plugin-{PACKAGE_SHORTCUT}'
|
|
378
|
-
* bpmnlint-plugin-{PACKAGE_SHORTCUT}/{RULE_NAME} => PKG = 'bpmnlint-plugin-{PACKAGE_SHORTCUT}'
|
|
379
|
-
* @scope/{PACKAGE_SHORTCUT}/{RULE_NAME} => PKG = '@scope/bpmnlint-plugin-{PACKAGE_SHORTCUT}'
|
|
380
|
-
* @scope/bpmnlint-plugin-{PACKAGE_SHORTCUT}/{RULE_NAME} => PKG = '@scope/bpmnlint-plugin-{PACKAGE_SHORTCUT}'
|
|
381
|
-
*/
|
|
382
|
-
|
|
383
|
-
const match = /^(?:(?:(@[^/]+)\/)?([^@]{1}[^/]*)\/)?([^/]+)$/.exec(name);
|
|
384
|
-
|
|
385
|
-
if (!match) {
|
|
386
|
-
throw new Error(`unparseable rule name <${name}>`);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
const [
|
|
390
|
-
_,
|
|
391
|
-
ns,
|
|
392
|
-
packageName,
|
|
393
|
-
ruleName
|
|
394
|
-
] = match;
|
|
395
|
-
|
|
396
|
-
if (!packageName) {
|
|
397
|
-
return {
|
|
398
|
-
pkg: localPackage,
|
|
399
|
-
ruleName
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
const pkg = `${ns ? ns + '/' : '' }${prefixPackage(packageName)}`;
|
|
404
|
-
|
|
405
|
-
return {
|
|
406
|
-
pkg,
|
|
407
|
-
ruleName
|
|
408
|
-
};
|
|
409
|
-
};
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
Linter.prototype.parseConfigName = function(name) {
|
|
413
|
-
|
|
414
|
-
/**
|
|
415
|
-
* We recognize the following config name patterns:
|
|
416
|
-
*
|
|
417
|
-
* bpmnlint:{CONFIG_NAME} => PKG = 'bpmnlint'
|
|
418
|
-
* plugin:{PACKAGE_SHORTCUT}/{CONFIG_NAME} => PKG = 'bpmnlint-plugin-{PACKAGE_SHORTCUT}'
|
|
419
|
-
* plugin:bpmnlint-plugin-{PACKAGE_SHORTCUT}/{CONFIG_NAME} => PKG = 'bpmnlint-plugin-{PACKAGE_SHORTCUT}'
|
|
420
|
-
* plugin:@scope/{PACKAGE_SHORTCUT}/{CONFIG_NAME} => PKG = '@scope/bpmnlint-plugin-{PACKAGE_SHORTCUT}'
|
|
421
|
-
* plugin:@scope/bpmnlint-plugin-{PACKAGE_SHORTCUT}/{CONFIG_NAME} => PKG = '@scope/bpmnlint-plugin-{PACKAGE_SHORTCUT}'
|
|
422
|
-
*/
|
|
423
|
-
|
|
424
|
-
const match = /^(?:(?:plugin:(?:(@[^/]+)\/)?([^@]{1}[^/]*)\/)|bpmnlint:)([^/]+)$/.exec(name);
|
|
425
|
-
|
|
426
|
-
if (!match) {
|
|
427
|
-
throw new Error(`unparseable config name <${name}>`);
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
const [
|
|
431
|
-
_,
|
|
432
|
-
ns,
|
|
433
|
-
packageName,
|
|
434
|
-
configName
|
|
435
|
-
] = match;
|
|
436
|
-
|
|
437
|
-
if (!packageName) {
|
|
438
|
-
return {
|
|
439
|
-
pkg: 'bpmnlint',
|
|
440
|
-
configName
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
const pkg = `${ns ? ns + '/' : '' }${prefixPackage(packageName)}`;
|
|
445
|
-
|
|
446
|
-
return {
|
|
447
|
-
pkg,
|
|
448
|
-
configName
|
|
449
|
-
};
|
|
450
|
-
};
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
Linter.prototype.getSimplePackageName = function(name) {
|
|
454
|
-
|
|
455
|
-
/**
|
|
456
|
-
* We recognize the following package name patterns:
|
|
457
|
-
*
|
|
458
|
-
* bpmnlint => PKG = 'bpmnlint'
|
|
459
|
-
* {PACKAGE_SHORTCUT} => PKG = PACKAGE_SHORTCUT
|
|
460
|
-
* bpmnlint-plugin-{PACKAGE_SHORTCUT}' => PKG = PACKAGE_SHORTCUT
|
|
461
|
-
* @scope/{PACKAGE_SHORTCUT} => PKG = '@scope/{PACKAGE_SHORTCUT}'
|
|
462
|
-
* @scope/bpmnlint-plugin-{PACKAGE_SHORTCUT}' => PKG = '@scope/PACKAGE_SHORTCUT'
|
|
463
|
-
*/
|
|
464
|
-
|
|
465
|
-
const match = /^(?:(@[^/]+)\/)?([^/]+)$/.exec(name);
|
|
466
|
-
|
|
467
|
-
if (!match) {
|
|
468
|
-
throw new Error(`unparseable package name <${name}>`);
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
const [
|
|
472
|
-
_,
|
|
473
|
-
ns,
|
|
474
|
-
packageName
|
|
475
|
-
] = match;
|
|
476
|
-
|
|
477
|
-
return `${ns ? ns + '/' : '' }${unprefixPackage(packageName)}`;
|
|
478
|
-
};
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* Validate and return validated config.
|
|
483
|
-
*
|
|
484
|
-
* @param {Object} config
|
|
485
|
-
* @param {String} localPackage
|
|
486
|
-
*
|
|
487
|
-
* @return {Object} validated config
|
|
488
|
-
*/
|
|
489
|
-
Linter.prototype.normalizeConfig = function(config, localPackage) {
|
|
490
|
-
|
|
491
|
-
const rules = config.rules || {};
|
|
492
|
-
|
|
493
|
-
const validatedRules = Object.keys(rules).reduce((normalizedRules, name) => {
|
|
494
|
-
|
|
495
|
-
const value = rules[name];
|
|
496
|
-
|
|
497
|
-
const {
|
|
498
|
-
pkg,
|
|
499
|
-
ruleName
|
|
500
|
-
} = this.parseRuleName(name, localPackage);
|
|
501
|
-
|
|
502
|
-
const normalizedName = (
|
|
503
|
-
pkg === 'bpmnlint'
|
|
504
|
-
? ruleName
|
|
505
|
-
: `${this.getSimplePackageName(pkg)}/${ruleName}`
|
|
506
|
-
);
|
|
507
|
-
|
|
508
|
-
normalizedRules[normalizedName] = value;
|
|
509
|
-
|
|
510
|
-
return normalizedRules;
|
|
511
|
-
}, {});
|
|
512
|
-
|
|
513
|
-
return {
|
|
514
|
-
...config,
|
|
515
|
-
rules: validatedRules
|
|
516
|
-
};
|
|
517
|
-
};
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
// helpers ///////////////////////////
|
|
521
|
-
|
|
522
|
-
function prefixPackage(pkg) {
|
|
523
|
-
|
|
524
|
-
if (pkg === 'bpmnlint') {
|
|
525
|
-
return 'bpmnlint';
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
if (pkg.startsWith('bpmnlint-plugin-')) {
|
|
529
|
-
return pkg;
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
return `bpmnlint-plugin-${pkg}`;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
function unprefixPackage(pkg) {
|
|
537
|
-
|
|
538
|
-
if (pkg.startsWith('bpmnlint-plugin-')) {
|
|
539
|
-
return pkg.substring('bpmnlint-plugin-'.length);
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
return pkg;
|
|
1
|
+
const testRule = require('./test-rule');
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef { import('./types.js').ModdleElement } ModdleElement
|
|
6
|
+
*
|
|
7
|
+
* @typedef { import('./types.js').ReportingCategory } ReportingCategory
|
|
8
|
+
* @typedef { import('./types.js').ReportingCategoryMap } ReportingCategoryMap
|
|
9
|
+
* @typedef { import('./types.js').RuleErrorCategory } RuleErrorCategory
|
|
10
|
+
*
|
|
11
|
+
* @typedef { import('./types.js').Resolver } Resolver
|
|
12
|
+
* @typedef { import('./types.js').Config } Config
|
|
13
|
+
*
|
|
14
|
+
* @typedef { import('./types.js').RuleFactory } RuleFactory
|
|
15
|
+
*
|
|
16
|
+
* @typedef { import('./types.js').RuleDefinition } RuleDefinition
|
|
17
|
+
* @typedef { import('./types.js').RuleConfigs } RuleConfigs
|
|
18
|
+
* @typedef { import('./types.js').RuleConfig } RuleConfig
|
|
19
|
+
*
|
|
20
|
+
* @typedef { {
|
|
21
|
+
* name: string,
|
|
22
|
+
* rule: RuleDefinition,
|
|
23
|
+
* config: any,
|
|
24
|
+
* category: ReportingCategory
|
|
25
|
+
* } } ResolvedRuleDefinition
|
|
26
|
+
*
|
|
27
|
+
* @typedef { import('./types.js').Report } Report
|
|
28
|
+
* @typedef { Report & Pick<RuleDefinition, 'meta'> & {
|
|
29
|
+
* category: ReportingCategory | RuleErrorCategory
|
|
30
|
+
* } } AnnotatedReport
|
|
31
|
+
*
|
|
32
|
+
* @typedef { import('./types.js').TransformRuleFn } TransformRuleFn
|
|
33
|
+
*
|
|
34
|
+
* @typedef { Record<string, AnnotatedReport[]> } LintResults
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Transform a rule after it is loaded.
|
|
40
|
+
*
|
|
41
|
+
* @param { RuleDefinition } rule
|
|
42
|
+
* @param { { pkg: string, ruleName: string } } options
|
|
43
|
+
*/
|
|
44
|
+
const noopTransformRule = (rule, options) => {
|
|
45
|
+
return rule;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @typeof { ReportingCategoryMap }
|
|
51
|
+
*/
|
|
52
|
+
const categoryMap = {
|
|
53
|
+
0: 'off',
|
|
54
|
+
1: 'warn',
|
|
55
|
+
2: 'error',
|
|
56
|
+
3: 'info'
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @type { RuleErrorCategory }
|
|
61
|
+
*/
|
|
62
|
+
const ruleErrorCategory = 'rule-error';
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @param { {
|
|
66
|
+
* config?: Config,
|
|
67
|
+
* resolver: Resolver,
|
|
68
|
+
* transformRule?: TransformRuleFn
|
|
69
|
+
* } } options
|
|
70
|
+
*/
|
|
71
|
+
function Linter(options) {
|
|
72
|
+
|
|
73
|
+
const {
|
|
74
|
+
config = {},
|
|
75
|
+
resolver,
|
|
76
|
+
transformRule = noopTransformRule
|
|
77
|
+
} = options || {};
|
|
78
|
+
|
|
79
|
+
if (typeof resolver === 'undefined') {
|
|
80
|
+
throw new Error('must provide <options.resolver>');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this.config = config;
|
|
84
|
+
this.resolver = resolver;
|
|
85
|
+
|
|
86
|
+
this.transformRule = transformRule;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @type { Record<string, RuleDefinition> }
|
|
90
|
+
*/
|
|
91
|
+
this.cachedRules = {};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @type { Record<string, Config> }
|
|
95
|
+
*/
|
|
96
|
+
this.cachedConfigs = {};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
module.exports = Linter;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Applies a rule on the moddleRoot and adds reports to the finalReport
|
|
104
|
+
*
|
|
105
|
+
* @param { ModdleElement } moddleRoot
|
|
106
|
+
*
|
|
107
|
+
* @param { ResolvedRuleDefinition } ruleDefinition
|
|
108
|
+
*
|
|
109
|
+
* @return { AnnotatedReport[] } reports
|
|
110
|
+
*/
|
|
111
|
+
Linter.prototype.applyRule = function applyRule(moddleRoot, ruleDefinition) {
|
|
112
|
+
|
|
113
|
+
const {
|
|
114
|
+
config,
|
|
115
|
+
rule,
|
|
116
|
+
category,
|
|
117
|
+
name
|
|
118
|
+
} = ruleDefinition;
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
|
|
122
|
+
const reports = testRule({
|
|
123
|
+
moddleRoot,
|
|
124
|
+
rule,
|
|
125
|
+
config
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return reports.map(function(report) {
|
|
129
|
+
return {
|
|
130
|
+
...report,
|
|
131
|
+
meta: rule.meta,
|
|
132
|
+
category
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
} catch (e) {
|
|
136
|
+
console.error('rule <' + name + '> failed with error: ', e);
|
|
137
|
+
|
|
138
|
+
return [
|
|
139
|
+
{
|
|
140
|
+
message: /** @type { Error } */ (e).message,
|
|
141
|
+
category: ruleErrorCategory
|
|
142
|
+
}
|
|
143
|
+
];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @param { string } name
|
|
151
|
+
* @param { RuleConfig } config
|
|
152
|
+
*
|
|
153
|
+
* @return { Promise<RuleDefinition> }
|
|
154
|
+
*/
|
|
155
|
+
Linter.prototype.resolveRule = function(name, config) {
|
|
156
|
+
|
|
157
|
+
const {
|
|
158
|
+
pkg,
|
|
159
|
+
ruleName
|
|
160
|
+
} = this.parseRuleName(name);
|
|
161
|
+
|
|
162
|
+
const id = `${pkg}-${ruleName}`;
|
|
163
|
+
|
|
164
|
+
const rule = this.cachedRules[id];
|
|
165
|
+
|
|
166
|
+
if (rule) {
|
|
167
|
+
return Promise.resolve(rule);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return Promise.resolve(this.resolver.resolveRule(pkg, ruleName)).then((ruleFactory) => {
|
|
171
|
+
|
|
172
|
+
if (!ruleFactory) {
|
|
173
|
+
throw new Error(`unknown rule <${name}>`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const rule = this.cachedRules[id] = this.transformRule(ruleFactory(config), { pkg, ruleName });
|
|
177
|
+
|
|
178
|
+
return rule;
|
|
179
|
+
});
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* @param {string} name
|
|
184
|
+
*
|
|
185
|
+
* @return {Promise<Config>}
|
|
186
|
+
*/
|
|
187
|
+
Linter.prototype.resolveConfig = function(name) {
|
|
188
|
+
|
|
189
|
+
const {
|
|
190
|
+
pkg,
|
|
191
|
+
configName
|
|
192
|
+
} = this.parseConfigName(name);
|
|
193
|
+
|
|
194
|
+
const id = `${pkg}-${configName}`;
|
|
195
|
+
|
|
196
|
+
const config = this.cachedConfigs[id];
|
|
197
|
+
|
|
198
|
+
if (config) {
|
|
199
|
+
return Promise.resolve(config);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return Promise.resolve(this.resolver.resolveConfig(pkg, configName)).then((config) => {
|
|
203
|
+
|
|
204
|
+
if (!config) {
|
|
205
|
+
throw new Error(`unknown config <${name}>`);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const actualConfig = this.cachedConfigs[id] = this.normalizeConfig(config, pkg);
|
|
209
|
+
|
|
210
|
+
return actualConfig;
|
|
211
|
+
});
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Take a linter config and return list of resolved rules.
|
|
216
|
+
*
|
|
217
|
+
* @param { Config } config
|
|
218
|
+
*
|
|
219
|
+
* @return { Promise<ResolvedRuleDefinition[]> }
|
|
220
|
+
*/
|
|
221
|
+
Linter.prototype.resolveRules = function(config) {
|
|
222
|
+
|
|
223
|
+
return this.resolveConfiguredRules(config).then((rulesConfig) => {
|
|
224
|
+
|
|
225
|
+
// parse rule values
|
|
226
|
+
const parsedRules = Object.entries(rulesConfig).map(([ name, value ]) => {
|
|
227
|
+
const {
|
|
228
|
+
category,
|
|
229
|
+
config
|
|
230
|
+
} = this.parseRuleValue(value);
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
name,
|
|
234
|
+
category,
|
|
235
|
+
config
|
|
236
|
+
};
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// filter only for enabled rules
|
|
240
|
+
const enabledRules = parsedRules.filter(definition => definition.category !== 'off');
|
|
241
|
+
|
|
242
|
+
// load enabled rules
|
|
243
|
+
const loaders = enabledRules.map((definition) => {
|
|
244
|
+
|
|
245
|
+
const {
|
|
246
|
+
name,
|
|
247
|
+
config
|
|
248
|
+
} = definition;
|
|
249
|
+
|
|
250
|
+
return this.resolveRule(name, config).then(function(rule) {
|
|
251
|
+
return {
|
|
252
|
+
...definition,
|
|
253
|
+
rule
|
|
254
|
+
};
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
return Promise.all(loaders);
|
|
259
|
+
});
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* @param { Config } config
|
|
264
|
+
*
|
|
265
|
+
* @return { Promise<RuleConfigs> }
|
|
266
|
+
*/
|
|
267
|
+
Linter.prototype.resolveConfiguredRules = function(config) {
|
|
268
|
+
|
|
269
|
+
let parents = config.extends;
|
|
270
|
+
|
|
271
|
+
if (typeof parents === 'string') {
|
|
272
|
+
parents = [ parents ];
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (typeof parents === 'undefined') {
|
|
276
|
+
parents = [];
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return Promise.all(
|
|
280
|
+
parents.map((configName) => {
|
|
281
|
+
return this.resolveConfig(configName).then((config) => {
|
|
282
|
+
return this.resolveConfiguredRules(config);
|
|
283
|
+
});
|
|
284
|
+
})
|
|
285
|
+
).then((inheritedRules) => {
|
|
286
|
+
|
|
287
|
+
const overrideRules = this.normalizeConfig(config, 'bpmnlint').rules;
|
|
288
|
+
|
|
289
|
+
const rules = [ ...inheritedRules, overrideRules ].reduce((rules, currentRules) => {
|
|
290
|
+
return {
|
|
291
|
+
...rules,
|
|
292
|
+
...currentRules
|
|
293
|
+
};
|
|
294
|
+
}, {});
|
|
295
|
+
|
|
296
|
+
return rules;
|
|
297
|
+
});
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Lint the given model root, using the specified linter config.
|
|
303
|
+
*
|
|
304
|
+
* @param { ModdleElement } moddleRoot
|
|
305
|
+
* @param { Config } [config] the bpmnlint configuration to use
|
|
306
|
+
*
|
|
307
|
+
* @return { Promise<LintResults> } lint results, keyed by rule names
|
|
308
|
+
*/
|
|
309
|
+
Linter.prototype.lint = function(moddleRoot, config) {
|
|
310
|
+
|
|
311
|
+
config = config || this.config;
|
|
312
|
+
|
|
313
|
+
// load rules
|
|
314
|
+
return this.resolveRules(config).then((ruleDefinitions) => {
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* @type { LintResults }
|
|
318
|
+
*/
|
|
319
|
+
const allReports = {};
|
|
320
|
+
|
|
321
|
+
ruleDefinitions.forEach((ruleDefinition) => {
|
|
322
|
+
|
|
323
|
+
const {
|
|
324
|
+
name
|
|
325
|
+
} = ruleDefinition;
|
|
326
|
+
|
|
327
|
+
const reports = this.applyRule(moddleRoot, ruleDefinition);
|
|
328
|
+
|
|
329
|
+
if (reports.length) {
|
|
330
|
+
allReports[name] = reports;
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
return allReports;
|
|
335
|
+
});
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* @param { import('./types.js').RuleConfig } value
|
|
340
|
+
*
|
|
341
|
+
* @return { { config: any, category: ReportingCategory } }
|
|
342
|
+
*/
|
|
343
|
+
Linter.prototype.parseRuleValue = function(value) {
|
|
344
|
+
|
|
345
|
+
let category;
|
|
346
|
+
let config;
|
|
347
|
+
|
|
348
|
+
if (Array.isArray(value)) {
|
|
349
|
+
category = value[0];
|
|
350
|
+
config = value[1];
|
|
351
|
+
} else {
|
|
352
|
+
category = value;
|
|
353
|
+
config = {};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// normalize rule flag to <error> and <warn> which
|
|
357
|
+
// may be upper case or a number at this point
|
|
358
|
+
if (typeof category === 'string') {
|
|
359
|
+
category = category.toLowerCase();
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
category = categoryMap[category] || category;
|
|
363
|
+
|
|
364
|
+
return {
|
|
365
|
+
config,
|
|
366
|
+
category
|
|
367
|
+
};
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
Linter.prototype.parseRuleName = function(name, localPackage = 'bpmnlint') {
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* We recognize the following rule name patterns:
|
|
374
|
+
*
|
|
375
|
+
* {RULE_NAME} => PKG = 'bpmnlint'
|
|
376
|
+
* bpmnlint/{RULE_NAME} => PKG = 'bpmnlint'
|
|
377
|
+
* {PACKAGE_SHORTCUT}/{RULE_NAME} => PKG = 'bpmnlint-plugin-{PACKAGE_SHORTCUT}'
|
|
378
|
+
* bpmnlint-plugin-{PACKAGE_SHORTCUT}/{RULE_NAME} => PKG = 'bpmnlint-plugin-{PACKAGE_SHORTCUT}'
|
|
379
|
+
* @scope/{PACKAGE_SHORTCUT}/{RULE_NAME} => PKG = '@scope/bpmnlint-plugin-{PACKAGE_SHORTCUT}'
|
|
380
|
+
* @scope/bpmnlint-plugin-{PACKAGE_SHORTCUT}/{RULE_NAME} => PKG = '@scope/bpmnlint-plugin-{PACKAGE_SHORTCUT}'
|
|
381
|
+
*/
|
|
382
|
+
|
|
383
|
+
const match = /^(?:(?:(@[^/]+)\/)?([^@]{1}[^/]*)\/)?([^/]+)$/.exec(name);
|
|
384
|
+
|
|
385
|
+
if (!match) {
|
|
386
|
+
throw new Error(`unparseable rule name <${name}>`);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const [
|
|
390
|
+
_,
|
|
391
|
+
ns,
|
|
392
|
+
packageName,
|
|
393
|
+
ruleName
|
|
394
|
+
] = match;
|
|
395
|
+
|
|
396
|
+
if (!packageName) {
|
|
397
|
+
return {
|
|
398
|
+
pkg: localPackage,
|
|
399
|
+
ruleName
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const pkg = `${ns ? ns + '/' : '' }${prefixPackage(packageName)}`;
|
|
404
|
+
|
|
405
|
+
return {
|
|
406
|
+
pkg,
|
|
407
|
+
ruleName
|
|
408
|
+
};
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
Linter.prototype.parseConfigName = function(name) {
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* We recognize the following config name patterns:
|
|
416
|
+
*
|
|
417
|
+
* bpmnlint:{CONFIG_NAME} => PKG = 'bpmnlint'
|
|
418
|
+
* plugin:{PACKAGE_SHORTCUT}/{CONFIG_NAME} => PKG = 'bpmnlint-plugin-{PACKAGE_SHORTCUT}'
|
|
419
|
+
* plugin:bpmnlint-plugin-{PACKAGE_SHORTCUT}/{CONFIG_NAME} => PKG = 'bpmnlint-plugin-{PACKAGE_SHORTCUT}'
|
|
420
|
+
* plugin:@scope/{PACKAGE_SHORTCUT}/{CONFIG_NAME} => PKG = '@scope/bpmnlint-plugin-{PACKAGE_SHORTCUT}'
|
|
421
|
+
* plugin:@scope/bpmnlint-plugin-{PACKAGE_SHORTCUT}/{CONFIG_NAME} => PKG = '@scope/bpmnlint-plugin-{PACKAGE_SHORTCUT}'
|
|
422
|
+
*/
|
|
423
|
+
|
|
424
|
+
const match = /^(?:(?:plugin:(?:(@[^/]+)\/)?([^@]{1}[^/]*)\/)|bpmnlint:)([^/]+)$/.exec(name);
|
|
425
|
+
|
|
426
|
+
if (!match) {
|
|
427
|
+
throw new Error(`unparseable config name <${name}>`);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const [
|
|
431
|
+
_,
|
|
432
|
+
ns,
|
|
433
|
+
packageName,
|
|
434
|
+
configName
|
|
435
|
+
] = match;
|
|
436
|
+
|
|
437
|
+
if (!packageName) {
|
|
438
|
+
return {
|
|
439
|
+
pkg: 'bpmnlint',
|
|
440
|
+
configName
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const pkg = `${ns ? ns + '/' : '' }${prefixPackage(packageName)}`;
|
|
445
|
+
|
|
446
|
+
return {
|
|
447
|
+
pkg,
|
|
448
|
+
configName
|
|
449
|
+
};
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
Linter.prototype.getSimplePackageName = function(name) {
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* We recognize the following package name patterns:
|
|
457
|
+
*
|
|
458
|
+
* bpmnlint => PKG = 'bpmnlint'
|
|
459
|
+
* {PACKAGE_SHORTCUT} => PKG = PACKAGE_SHORTCUT
|
|
460
|
+
* bpmnlint-plugin-{PACKAGE_SHORTCUT}' => PKG = PACKAGE_SHORTCUT
|
|
461
|
+
* @scope/{PACKAGE_SHORTCUT} => PKG = '@scope/{PACKAGE_SHORTCUT}'
|
|
462
|
+
* @scope/bpmnlint-plugin-{PACKAGE_SHORTCUT}' => PKG = '@scope/PACKAGE_SHORTCUT'
|
|
463
|
+
*/
|
|
464
|
+
|
|
465
|
+
const match = /^(?:(@[^/]+)\/)?([^/]+)$/.exec(name);
|
|
466
|
+
|
|
467
|
+
if (!match) {
|
|
468
|
+
throw new Error(`unparseable package name <${name}>`);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const [
|
|
472
|
+
_,
|
|
473
|
+
ns,
|
|
474
|
+
packageName
|
|
475
|
+
] = match;
|
|
476
|
+
|
|
477
|
+
return `${ns ? ns + '/' : '' }${unprefixPackage(packageName)}`;
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Validate and return validated config.
|
|
483
|
+
*
|
|
484
|
+
* @param {Object} config
|
|
485
|
+
* @param {String} localPackage
|
|
486
|
+
*
|
|
487
|
+
* @return {Object} validated config
|
|
488
|
+
*/
|
|
489
|
+
Linter.prototype.normalizeConfig = function(config, localPackage) {
|
|
490
|
+
|
|
491
|
+
const rules = config.rules || {};
|
|
492
|
+
|
|
493
|
+
const validatedRules = Object.keys(rules).reduce((normalizedRules, name) => {
|
|
494
|
+
|
|
495
|
+
const value = rules[name];
|
|
496
|
+
|
|
497
|
+
const {
|
|
498
|
+
pkg,
|
|
499
|
+
ruleName
|
|
500
|
+
} = this.parseRuleName(name, localPackage);
|
|
501
|
+
|
|
502
|
+
const normalizedName = (
|
|
503
|
+
pkg === 'bpmnlint'
|
|
504
|
+
? ruleName
|
|
505
|
+
: `${this.getSimplePackageName(pkg)}/${ruleName}`
|
|
506
|
+
);
|
|
507
|
+
|
|
508
|
+
normalizedRules[normalizedName] = value;
|
|
509
|
+
|
|
510
|
+
return normalizedRules;
|
|
511
|
+
}, {});
|
|
512
|
+
|
|
513
|
+
return {
|
|
514
|
+
...config,
|
|
515
|
+
rules: validatedRules
|
|
516
|
+
};
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
// helpers ///////////////////////////
|
|
521
|
+
|
|
522
|
+
function prefixPackage(pkg) {
|
|
523
|
+
|
|
524
|
+
if (pkg === 'bpmnlint') {
|
|
525
|
+
return 'bpmnlint';
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if (pkg.startsWith('bpmnlint-plugin-')) {
|
|
529
|
+
return pkg;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
return `bpmnlint-plugin-${pkg}`;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
function unprefixPackage(pkg) {
|
|
537
|
+
|
|
538
|
+
if (pkg.startsWith('bpmnlint-plugin-')) {
|
|
539
|
+
return pkg.substring('bpmnlint-plugin-'.length);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
return pkg;
|
|
543
543
|
}
|