bpmnlint 11.0.0 → 11.2.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/bin/bpmnlint.js +31 -28
- package/config/all.js +1 -0
- package/config/recommended.js +1 -0
- package/lib/linter.js +108 -20
- package/lib/resolver/node-resolver.js +28 -17
- package/lib/resolver/static-resolver.js +25 -3
- package/lib/test-rule.js +47 -15
- package/lib/testers/helper.js +2 -1
- package/lib/testers/rule-tester.js +12 -2
- package/lib/traverse.js +9 -6
- package/lib/types.d.ts +78 -2
- package/package.json +8 -5
- package/rules/ad-hoc-sub-process.js +52 -0
- package/rules/conditional-flows.js +9 -2
- package/rules/end-event-required.js +9 -1
- package/rules/event-sub-process-typed-start-event.js +9 -2
- package/rules/fake-join.js +9 -2
- package/rules/global.js +8 -2
- package/rules/helper.js +58 -11
- package/rules/label-required.js +9 -1
- package/rules/link-event.js +7 -2
- package/rules/no-bpmndi.js +36 -9
- package/rules/no-complex-gateway.js +1 -1
- package/rules/no-disconnected.js +9 -4
- package/rules/no-duplicate-sequence-flows.js +10 -4
- package/rules/no-gateway-join-fork.js +8 -2
- package/rules/no-implicit-end.js +8 -2
- package/rules/no-implicit-split.js +9 -3
- package/rules/no-implicit-start.js +8 -1
- package/rules/no-inclusive-gateway.js +1 -1
- package/rules/no-overlapping-elements.js +10 -3
- package/rules/single-blank-start-event.js +9 -2
- package/rules/single-event-definition.js +8 -2
- package/rules/start-event-required.js +9 -1
- package/rules/sub-process-blank-start-event.js +8 -2
- package/rules/superfluous-gateway.js +9 -2
- package/rules/superfluous-termination.js +11 -3
package/bin/bpmnlint.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { readFile } = require('node:fs/promises');
|
|
4
|
+
const { writeFileSync, existsSync } = require('node:fs');
|
|
5
|
+
const { resolve: resolvePath } = require('node:path');
|
|
6
|
+
|
|
2
7
|
const mri = require('mri');
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
8
|
const colors = require('ansi-colors');
|
|
6
9
|
|
|
7
10
|
colors.enabled = require('color-support').hasBasic;
|
|
@@ -14,11 +17,9 @@ const {
|
|
|
14
17
|
magenta
|
|
15
18
|
} = colors;
|
|
16
19
|
|
|
17
|
-
const { promisify } = require('util');
|
|
18
|
-
|
|
19
20
|
const tinyGlob = require('tiny-glob');
|
|
20
|
-
const readFile = promisify(fs.readFile);
|
|
21
21
|
|
|
22
|
+
// @ts-expect-error 'missing <bpmn-moddle> types'
|
|
22
23
|
const BpmnModdle = require('bpmn-moddle');
|
|
23
24
|
|
|
24
25
|
const Linter = require('../lib/linter');
|
|
@@ -30,6 +31,11 @@ const pluralize = require('pluralize');
|
|
|
30
31
|
|
|
31
32
|
const { pathStringify } = require('@bpmn-io/moddle-utils');
|
|
32
33
|
|
|
34
|
+
/**
|
|
35
|
+
* @typedef { import('../lib/types').ModdleElement } ModdleElement
|
|
36
|
+
* @typedef { Error & { element: ModdleElement } } BPMNImportWarning
|
|
37
|
+
*/
|
|
38
|
+
|
|
33
39
|
const CONFIG_NAME = '.bpmnlintrc';
|
|
34
40
|
|
|
35
41
|
const DEFAULT_CONFIG_CONTENTS = `{
|
|
@@ -76,7 +82,7 @@ function glob(files) {
|
|
|
76
82
|
*
|
|
77
83
|
* @param {string} diagramXML
|
|
78
84
|
*
|
|
79
|
-
* @return { {
|
|
85
|
+
* @return { Promise<{ moddleElement: any; warnings: Error[], error?: Error }> } parseResult
|
|
80
86
|
*/
|
|
81
87
|
async function parseDiagram(diagramXML) {
|
|
82
88
|
|
|
@@ -93,10 +99,14 @@ async function parseDiagram(diagramXML) {
|
|
|
93
99
|
} catch (error) {
|
|
94
100
|
|
|
95
101
|
const {
|
|
102
|
+
|
|
103
|
+
// @ts-expect-error
|
|
96
104
|
warnings = []
|
|
97
105
|
} = error;
|
|
98
106
|
|
|
99
107
|
return {
|
|
108
|
+
|
|
109
|
+
// @ts-expect-error
|
|
100
110
|
error,
|
|
101
111
|
warnings
|
|
102
112
|
};
|
|
@@ -215,7 +225,7 @@ function printReports(filePath, results) {
|
|
|
215
225
|
|
|
216
226
|
if (problemCount) {
|
|
217
227
|
console.log();
|
|
218
|
-
console.log(underline(
|
|
228
|
+
console.log(underline(resolvePath(filePath)));
|
|
219
229
|
console.log(table.toString());
|
|
220
230
|
}
|
|
221
231
|
|
|
@@ -230,9 +240,9 @@ async function lintDiagram(diagramPath, config) {
|
|
|
230
240
|
let diagramXML;
|
|
231
241
|
|
|
232
242
|
try {
|
|
233
|
-
diagramXML = await readFile(
|
|
243
|
+
diagramXML = await readFile(resolvePath(diagramPath), 'utf-8');
|
|
234
244
|
} catch (error) {
|
|
235
|
-
|
|
245
|
+
throw errorAndExit(`Error: Failed to read ${diagramPath}\n\n%s`, /** @type { Error } */ (error).message);
|
|
236
246
|
}
|
|
237
247
|
|
|
238
248
|
|
|
@@ -259,7 +269,7 @@ async function lintDiagram(diagramPath, config) {
|
|
|
259
269
|
const {
|
|
260
270
|
element,
|
|
261
271
|
message
|
|
262
|
-
} = warning;
|
|
272
|
+
} = /** @type { BPMNImportWarning } */ (warning);
|
|
263
273
|
|
|
264
274
|
const id = element && element.id;
|
|
265
275
|
|
|
@@ -286,7 +296,7 @@ async function lintDiagram(diagramPath, config) {
|
|
|
286
296
|
|
|
287
297
|
return printReports(diagramPath, allResults);
|
|
288
298
|
} catch (e) {
|
|
289
|
-
|
|
299
|
+
throw errorAndExit(e);
|
|
290
300
|
}
|
|
291
301
|
}
|
|
292
302
|
|
|
@@ -306,17 +316,10 @@ async function lint(files, config, maxWarnings) {
|
|
|
306
316
|
|
|
307
317
|
const problemCount = errorCount + warningCount;
|
|
308
318
|
|
|
309
|
-
|
|
319
|
+
if (problemCount) {
|
|
310
320
|
|
|
311
|
-
|
|
312
|
-
color = boldYellow;
|
|
313
|
-
}
|
|
321
|
+
const color = warningCount ? boldYellow : boldRed;
|
|
314
322
|
|
|
315
|
-
if (errorCount) {
|
|
316
|
-
color = boldRed;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
if (problemCount) {
|
|
320
323
|
console.log();
|
|
321
324
|
console.log(color(
|
|
322
325
|
`✖ ${problemCount} ${pluralize('problem', problemCount)} (${errorCount} ${pluralize('error', errorCount)}, ${warningCount} ${pluralize('warning', warningCount)})`
|
|
@@ -357,17 +360,17 @@ async function run() {
|
|
|
357
360
|
}
|
|
358
361
|
|
|
359
362
|
if (init) {
|
|
360
|
-
if (
|
|
361
|
-
|
|
363
|
+
if (existsSync(CONFIG_NAME)) {
|
|
364
|
+
throw errorAndExit('Not overriding existing .bpmnlintrc');
|
|
362
365
|
}
|
|
363
366
|
|
|
364
|
-
|
|
367
|
+
writeFileSync(CONFIG_NAME, DEFAULT_CONFIG_CONTENTS, 'utf8');
|
|
365
368
|
|
|
366
|
-
|
|
369
|
+
throw infoAndExit(`Created ${magenta(CONFIG_NAME)} file`);
|
|
367
370
|
}
|
|
368
371
|
|
|
369
372
|
if (files.length === 0) {
|
|
370
|
-
|
|
373
|
+
throw errorAndExit('Error: bpmn file path missing');
|
|
371
374
|
}
|
|
372
375
|
|
|
373
376
|
const configPath = configOverridePath || CONFIG_NAME;
|
|
@@ -388,13 +391,13 @@ async function run() {
|
|
|
388
391
|
Learn more about configuring bpmnlint: https://github.com/bpmn-io/bpmnlint#configuration`
|
|
389
392
|
);
|
|
390
393
|
|
|
391
|
-
|
|
394
|
+
throw errorAndExit(message);
|
|
392
395
|
}
|
|
393
396
|
|
|
394
397
|
try {
|
|
395
398
|
config = JSON.parse(configString);
|
|
396
|
-
} catch (
|
|
397
|
-
|
|
399
|
+
} catch (error) {
|
|
400
|
+
throw errorAndExit('Error: Could not parse %s\n\n%s', configPath, /** @type { Error } */ (error).message);
|
|
398
401
|
}
|
|
399
402
|
|
|
400
403
|
const actualFiles = await glob(files);
|
package/config/all.js
CHANGED
package/config/recommended.js
CHANGED
package/lib/linter.js
CHANGED
|
@@ -1,5 +1,54 @@
|
|
|
1
1
|
const testRule = require('./test-rule');
|
|
2
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
|
+
*/
|
|
3
52
|
const categoryMap = {
|
|
4
53
|
0: 'off',
|
|
5
54
|
1: 'warn',
|
|
@@ -7,15 +56,25 @@ const categoryMap = {
|
|
|
7
56
|
3: 'info'
|
|
8
57
|
};
|
|
9
58
|
|
|
59
|
+
/**
|
|
60
|
+
* @type { RuleErrorCategory }
|
|
61
|
+
*/
|
|
10
62
|
const ruleErrorCategory = 'rule-error';
|
|
11
63
|
|
|
12
|
-
|
|
13
|
-
|
|
64
|
+
/**
|
|
65
|
+
* @param { {
|
|
66
|
+
* config?: Config,
|
|
67
|
+
* resolver: Resolver,
|
|
68
|
+
* transformRule?: TransformRuleFn
|
|
69
|
+
* } } options
|
|
70
|
+
*/
|
|
71
|
+
function Linter(options) {
|
|
14
72
|
|
|
15
73
|
const {
|
|
16
|
-
config,
|
|
17
|
-
resolver
|
|
18
|
-
|
|
74
|
+
config = {},
|
|
75
|
+
resolver,
|
|
76
|
+
transformRule = noopTransformRule
|
|
77
|
+
} = options || {};
|
|
19
78
|
|
|
20
79
|
if (typeof resolver === 'undefined') {
|
|
21
80
|
throw new Error('must provide <options.resolver>');
|
|
@@ -24,7 +83,16 @@ function Linter(options = {}) {
|
|
|
24
83
|
this.config = config;
|
|
25
84
|
this.resolver = resolver;
|
|
26
85
|
|
|
86
|
+
this.transformRule = transformRule;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @type { Record<string, RuleDefinition> }
|
|
90
|
+
*/
|
|
27
91
|
this.cachedRules = {};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @type { Record<string, Config> }
|
|
95
|
+
*/
|
|
28
96
|
this.cachedConfigs = {};
|
|
29
97
|
}
|
|
30
98
|
|
|
@@ -34,14 +102,11 @@ module.exports = Linter;
|
|
|
34
102
|
/**
|
|
35
103
|
* Applies a rule on the moddleRoot and adds reports to the finalReport
|
|
36
104
|
*
|
|
37
|
-
* @param {ModdleElement} moddleRoot
|
|
105
|
+
* @param { ModdleElement } moddleRoot
|
|
38
106
|
*
|
|
39
|
-
* @param {
|
|
40
|
-
* @param {Object} ruleDefinition.config
|
|
41
|
-
* @param {Object} ruleDefinition.category
|
|
42
|
-
* @param {Rule} ruleDefinition.rule
|
|
107
|
+
* @param { ResolvedRuleDefinition } ruleDefinition
|
|
43
108
|
*
|
|
44
|
-
* @return {
|
|
109
|
+
* @return { AnnotatedReport[] } reports
|
|
45
110
|
*/
|
|
46
111
|
Linter.prototype.applyRule = function applyRule(moddleRoot, ruleDefinition) {
|
|
47
112
|
|
|
@@ -63,6 +128,7 @@ Linter.prototype.applyRule = function applyRule(moddleRoot, ruleDefinition) {
|
|
|
63
128
|
return reports.map(function(report) {
|
|
64
129
|
return {
|
|
65
130
|
...report,
|
|
131
|
+
meta: rule.meta,
|
|
66
132
|
category
|
|
67
133
|
};
|
|
68
134
|
});
|
|
@@ -71,7 +137,7 @@ Linter.prototype.applyRule = function applyRule(moddleRoot, ruleDefinition) {
|
|
|
71
137
|
|
|
72
138
|
return [
|
|
73
139
|
{
|
|
74
|
-
message: e.message,
|
|
140
|
+
message: /** @type { Error } */ (e).message,
|
|
75
141
|
category: ruleErrorCategory
|
|
76
142
|
}
|
|
77
143
|
];
|
|
@@ -80,6 +146,12 @@ Linter.prototype.applyRule = function applyRule(moddleRoot, ruleDefinition) {
|
|
|
80
146
|
};
|
|
81
147
|
|
|
82
148
|
|
|
149
|
+
/**
|
|
150
|
+
* @param { string } name
|
|
151
|
+
* @param { RuleConfig } config
|
|
152
|
+
*
|
|
153
|
+
* @return { Promise<RuleDefinition> }
|
|
154
|
+
*/
|
|
83
155
|
Linter.prototype.resolveRule = function(name, config) {
|
|
84
156
|
|
|
85
157
|
const {
|
|
@@ -101,12 +173,17 @@ Linter.prototype.resolveRule = function(name, config) {
|
|
|
101
173
|
throw new Error(`unknown rule <${name}>`);
|
|
102
174
|
}
|
|
103
175
|
|
|
104
|
-
const rule = this.cachedRules[id] = ruleFactory(config);
|
|
176
|
+
const rule = this.cachedRules[id] = this.transformRule(ruleFactory(config), { pkg, ruleName });
|
|
105
177
|
|
|
106
178
|
return rule;
|
|
107
179
|
});
|
|
108
180
|
};
|
|
109
181
|
|
|
182
|
+
/**
|
|
183
|
+
* @param {string} name
|
|
184
|
+
*
|
|
185
|
+
* @return {Promise<Config>}
|
|
186
|
+
*/
|
|
110
187
|
Linter.prototype.resolveConfig = function(name) {
|
|
111
188
|
|
|
112
189
|
const {
|
|
@@ -137,9 +214,9 @@ Linter.prototype.resolveConfig = function(name) {
|
|
|
137
214
|
/**
|
|
138
215
|
* Take a linter config and return list of resolved rules.
|
|
139
216
|
*
|
|
140
|
-
* @param {
|
|
217
|
+
* @param { Config } config
|
|
141
218
|
*
|
|
142
|
-
* @return {
|
|
219
|
+
* @return { Promise<ResolvedRuleDefinition[]> }
|
|
143
220
|
*/
|
|
144
221
|
Linter.prototype.resolveRules = function(config) {
|
|
145
222
|
|
|
@@ -182,7 +259,11 @@ Linter.prototype.resolveRules = function(config) {
|
|
|
182
259
|
});
|
|
183
260
|
};
|
|
184
261
|
|
|
185
|
-
|
|
262
|
+
/**
|
|
263
|
+
* @param { Config } config
|
|
264
|
+
*
|
|
265
|
+
* @return { Promise<RuleConfigs> }
|
|
266
|
+
*/
|
|
186
267
|
Linter.prototype.resolveConfiguredRules = function(config) {
|
|
187
268
|
|
|
188
269
|
let parents = config.extends;
|
|
@@ -220,10 +301,10 @@ Linter.prototype.resolveConfiguredRules = function(config) {
|
|
|
220
301
|
/**
|
|
221
302
|
* Lint the given model root, using the specified linter config.
|
|
222
303
|
*
|
|
223
|
-
* @param {ModdleElement} moddleRoot
|
|
224
|
-
* @param {
|
|
304
|
+
* @param { ModdleElement } moddleRoot
|
|
305
|
+
* @param { Config } [config] the bpmnlint configuration to use
|
|
225
306
|
*
|
|
226
|
-
* @return {
|
|
307
|
+
* @return { Promise<LintResults> } lint results, keyed by rule names
|
|
227
308
|
*/
|
|
228
309
|
Linter.prototype.lint = function(moddleRoot, config) {
|
|
229
310
|
|
|
@@ -232,6 +313,9 @@ Linter.prototype.lint = function(moddleRoot, config) {
|
|
|
232
313
|
// load rules
|
|
233
314
|
return this.resolveRules(config).then((ruleDefinitions) => {
|
|
234
315
|
|
|
316
|
+
/**
|
|
317
|
+
* @type { LintResults }
|
|
318
|
+
*/
|
|
235
319
|
const allReports = {};
|
|
236
320
|
|
|
237
321
|
ruleDefinitions.forEach((ruleDefinition) => {
|
|
@@ -251,7 +335,11 @@ Linter.prototype.lint = function(moddleRoot, config) {
|
|
|
251
335
|
});
|
|
252
336
|
};
|
|
253
337
|
|
|
254
|
-
|
|
338
|
+
/**
|
|
339
|
+
* @param { import('./types.js').RuleConfig } value
|
|
340
|
+
*
|
|
341
|
+
* @return { { config: any, category: ReportingCategory } }
|
|
342
|
+
*/
|
|
255
343
|
Linter.prototype.parseRuleValue = function(value) {
|
|
256
344
|
|
|
257
345
|
let category;
|
|
@@ -1,14 +1,23 @@
|
|
|
1
|
-
const Module = require('module');
|
|
2
|
-
const path = require('path');
|
|
1
|
+
const Module = require('node:module');
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
const { cwd } = require('node:process');
|
|
3
4
|
|
|
4
5
|
const { isString } = require('min-dash');
|
|
5
6
|
|
|
7
|
+
/**
|
|
8
|
+
* @typedef { import('../types.js').RuleFactory } RuleFactory
|
|
9
|
+
* @typedef { import('../types.js').Config } Config
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
/**
|
|
7
14
|
* A resolver that locates rules and configurations
|
|
8
15
|
* using node module resolution.
|
|
16
|
+
*
|
|
17
|
+
* @param { { require?: NodeRequire, requireLocal?: NodeRequire } } [options]
|
|
9
18
|
*/
|
|
10
19
|
function NodeResolver(options) {
|
|
11
|
-
this.require = options && options.require || createScopedRequire(
|
|
20
|
+
this.require = options && options.require || createScopedRequire(cwd());
|
|
12
21
|
|
|
13
22
|
this.requireLocal = options && options.requireLocal || require;
|
|
14
23
|
|
|
@@ -22,6 +31,12 @@ function NodeResolver(options) {
|
|
|
22
31
|
module.exports = NodeResolver;
|
|
23
32
|
|
|
24
33
|
|
|
34
|
+
/**
|
|
35
|
+
* @param { string } pkg
|
|
36
|
+
* @param { string } ruleName
|
|
37
|
+
*
|
|
38
|
+
* @return { RuleFactory }
|
|
39
|
+
*/
|
|
25
40
|
NodeResolver.prototype.resolveRule = function(pkg, ruleName) {
|
|
26
41
|
|
|
27
42
|
const originalPkg = pkg;
|
|
@@ -72,6 +87,12 @@ NodeResolver.prototype.resolveRule = function(pkg, ruleName) {
|
|
|
72
87
|
throw new Error('cannot resolve rule <' + ruleName + '> from <' + originalPkg + '>');
|
|
73
88
|
};
|
|
74
89
|
|
|
90
|
+
/**
|
|
91
|
+
* @param { string } pkg
|
|
92
|
+
* @param { string } configName
|
|
93
|
+
*
|
|
94
|
+
* @return { Config }
|
|
95
|
+
*/
|
|
75
96
|
NodeResolver.prototype.resolveConfig = function(pkg, configName) {
|
|
76
97
|
|
|
77
98
|
const originalPkg = pkg;
|
|
@@ -116,19 +137,9 @@ NodeResolver.prototype.normalizePkg = function(pkg) {
|
|
|
116
137
|
|
|
117
138
|
// helpers ////////////////////
|
|
118
139
|
|
|
140
|
+
/**
|
|
141
|
+
* @param {string} cwd
|
|
142
|
+
*/
|
|
119
143
|
function createScopedRequire(cwd) {
|
|
120
|
-
|
|
121
|
-
// shim createRequireFromPath for Node < 10.12
|
|
122
|
-
// shim createRequireFromPath for Node < 12.2.0
|
|
123
|
-
const createRequireFromPath = Module.createRequire || Module.createRequireFromPath || (filename => {
|
|
124
|
-
const mod = new Module(filename, null);
|
|
125
|
-
|
|
126
|
-
mod.filename = filename;
|
|
127
|
-
mod.paths = Module._nodeModulePaths(path.dirname(filename));
|
|
128
|
-
mod._compile('module.exports = require;', filename);
|
|
129
|
-
|
|
130
|
-
return mod.exports;
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
return createRequireFromPath(path.join(cwd, '__placeholder__.js'));
|
|
144
|
+
return Module.createRequire(path.join(cwd, '__placeholder__.js'));
|
|
134
145
|
}
|
|
@@ -1,16 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef { import('../types.js').RuleFactory } RuleFactory
|
|
3
|
+
* @typedef { import('../types.js').Config } Config
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A resolver that resolves rules and packages from a static cache.
|
|
9
|
+
*
|
|
10
|
+
* @param { any } cache
|
|
11
|
+
*/
|
|
1
12
|
function StaticResolver(cache) {
|
|
2
13
|
this.cache = cache;
|
|
3
14
|
}
|
|
4
15
|
|
|
5
16
|
module.exports = StaticResolver;
|
|
6
17
|
|
|
7
|
-
|
|
18
|
+
/**
|
|
19
|
+
* @param { string } pkg
|
|
20
|
+
* @param { string } ruleName
|
|
21
|
+
*
|
|
22
|
+
* @return { RuleFactory }
|
|
23
|
+
*/
|
|
8
24
|
StaticResolver.prototype.resolveRule = function(pkg, ruleName) {
|
|
9
|
-
return this.resolve('rule', pkg, ruleName);
|
|
25
|
+
return /** @type { RuleFactory } */ (this.resolve('rule', pkg, ruleName));
|
|
10
26
|
};
|
|
11
27
|
|
|
28
|
+
/**
|
|
29
|
+
* @param { string } pkg
|
|
30
|
+
* @param { string } configName
|
|
31
|
+
*
|
|
32
|
+
* @return { Config }
|
|
33
|
+
*/
|
|
12
34
|
StaticResolver.prototype.resolveConfig = function(pkg, configName) {
|
|
13
|
-
return this.resolve('config', pkg, configName);
|
|
35
|
+
return /** @type { Config } */ (this.resolve('config', pkg, configName));
|
|
14
36
|
};
|
|
15
37
|
|
|
16
38
|
StaticResolver.prototype.resolve = function(type, pkg, name) {
|
package/lib/test-rule.js
CHANGED
|
@@ -2,33 +2,54 @@ const traverse = require('./traverse');
|
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
4
|
isArray,
|
|
5
|
-
isObject
|
|
5
|
+
isObject,
|
|
6
|
+
isFunction
|
|
6
7
|
} = require('min-dash');
|
|
7
8
|
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* @typedef { import('./types.js').EnterFn } EnterFn
|
|
12
|
+
* @typedef { import('./types.js').ModdleElement } ModdleElement
|
|
13
|
+
* @typedef { import('./types.js').RuleDefinition } RuleDefinition
|
|
14
|
+
* @typedef { import('./types.js').Report } Report
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
|
|
9
18
|
class Reporter {
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param { {
|
|
22
|
+
* moddleRoot: ModdleElement,
|
|
23
|
+
* rule: RuleDefinition
|
|
24
|
+
* } } options
|
|
25
|
+
*/
|
|
10
26
|
constructor({ moddleRoot, rule }) {
|
|
11
27
|
this.rule = rule;
|
|
12
28
|
this.moddleRoot = moddleRoot;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @type { Report[] }
|
|
32
|
+
*/
|
|
13
33
|
this.messages = [];
|
|
34
|
+
|
|
14
35
|
this.report = this.report.bind(this);
|
|
15
36
|
}
|
|
16
37
|
|
|
17
38
|
/**
|
|
18
|
-
* @param {string} id
|
|
19
|
-
* @param {string} message
|
|
20
|
-
* @param {string[]|
|
|
39
|
+
* @param { string } id
|
|
40
|
+
* @param { string } message
|
|
41
|
+
* @param { string[] | { path?: string[], [key: string]: any } } [path]
|
|
21
42
|
*
|
|
22
43
|
* @example
|
|
23
44
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* @example
|
|
45
|
+
* ```javascript
|
|
46
|
+
* reporter.report('foo', 'Foo error', [ 'foo', 'bar', 'baz' ]);
|
|
27
47
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
48
|
+
* reporter.report('foo', 'Foo error', {
|
|
49
|
+
* path: [ 'foo', 'bar', 'baz' ],
|
|
50
|
+
* foo: 'foo'
|
|
31
51
|
* });
|
|
52
|
+
* ```
|
|
32
53
|
*/
|
|
33
54
|
report(id, message, path) {
|
|
34
55
|
let report = {
|
|
@@ -54,21 +75,32 @@ class Reporter {
|
|
|
54
75
|
}
|
|
55
76
|
}
|
|
56
77
|
|
|
78
|
+
/**
|
|
79
|
+
* @param { {
|
|
80
|
+
* moddleRoot: ModdleElement,
|
|
81
|
+
* rule: RuleDefinition,
|
|
82
|
+
* config: any
|
|
83
|
+
* } } options
|
|
84
|
+
*
|
|
85
|
+
* @return { Report[] } lint reports
|
|
86
|
+
*/
|
|
57
87
|
module.exports = function testRule({ moddleRoot, rule }) {
|
|
58
88
|
const reporter = new Reporter({ rule, moddleRoot });
|
|
59
89
|
|
|
60
|
-
const check = rule.check;
|
|
90
|
+
const check = rule.check || {};
|
|
61
91
|
|
|
62
|
-
const
|
|
63
|
-
const
|
|
92
|
+
const leave = 'leave' in check ? check.leave : undefined;
|
|
93
|
+
const enter = 'enter' in check ? check.enter : (
|
|
94
|
+
isFunction(check) ? check : undefined
|
|
95
|
+
);
|
|
64
96
|
|
|
65
97
|
if (!enter && !leave) {
|
|
66
98
|
throw new Error('no check implemented');
|
|
67
99
|
}
|
|
68
100
|
|
|
69
101
|
traverse(moddleRoot, {
|
|
70
|
-
enter: enter ? (node) => enter(node, reporter) :
|
|
71
|
-
leave: leave ? (node) => leave(node, reporter) :
|
|
102
|
+
enter: enter ? (node) => enter(node, reporter) : undefined,
|
|
103
|
+
leave: leave ? (node) => leave(node, reporter) : undefined
|
|
72
104
|
});
|
|
73
105
|
|
|
74
106
|
return reporter.messages;
|
package/lib/testers/helper.js
CHANGED
|
@@ -5,8 +5,13 @@ const assert = require('assert');
|
|
|
5
5
|
const Linter = require('../linter');
|
|
6
6
|
|
|
7
7
|
function createResolver(rule) {
|
|
8
|
+
|
|
8
9
|
return {
|
|
9
|
-
resolveRule: () => Promise.resolve(rule)
|
|
10
|
+
resolveRule: () => Promise.resolve(rule),
|
|
11
|
+
|
|
12
|
+
resolveConfig: () => {
|
|
13
|
+
throw new Error('not implemented');
|
|
14
|
+
}
|
|
10
15
|
};
|
|
11
16
|
}
|
|
12
17
|
|
|
@@ -57,7 +62,7 @@ function verify(ruleName, rule, testCases) {
|
|
|
57
62
|
|
|
58
63
|
testCases.invalid.forEach(({ config = {}, it: _it, moddleElement, name, report }, idx) => (
|
|
59
64
|
|
|
60
|
-
(_it || it)(getTitle(idx, name), function() {
|
|
65
|
+
(_it || it)(getTitle(idx, name), async function() {
|
|
61
66
|
|
|
62
67
|
if (!Array.isArray(report)) {
|
|
63
68
|
report = [
|
|
@@ -65,9 +70,14 @@ function verify(ruleName, rule, testCases) {
|
|
|
65
70
|
];
|
|
66
71
|
}
|
|
67
72
|
|
|
73
|
+
const {
|
|
74
|
+
meta
|
|
75
|
+
} = await linter.resolveRule(name, config);
|
|
76
|
+
|
|
68
77
|
const expectedResults = report.map(report => {
|
|
69
78
|
return {
|
|
70
79
|
...report,
|
|
80
|
+
meta,
|
|
71
81
|
category: 'error'
|
|
72
82
|
};
|
|
73
83
|
});
|
package/lib/traverse.js
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @typedef { import('./types.js').
|
|
3
|
-
*
|
|
2
|
+
* @typedef { import('./types.js').ModdleElement } ModdleElement
|
|
3
|
+
*
|
|
4
|
+
* @typedef { (element: ModdleElement) => boolean | void } EnterFn
|
|
5
|
+
* @typedef { (element: ModdleElement) => void } LeaveFn
|
|
4
6
|
*/
|
|
5
7
|
|
|
8
|
+
|
|
6
9
|
/**
|
|
7
10
|
* Traverse a moddle tree, depth first from top to bottom
|
|
8
11
|
* and call the passed visitor fn.
|
|
9
12
|
*
|
|
10
|
-
* @param {ModdleElement} element
|
|
11
|
-
* @param {{ enter?: EnterFn; leave?: LeaveFn }} options
|
|
13
|
+
* @param { ModdleElement } element
|
|
14
|
+
* @param { { enter?: EnterFn; leave?: LeaveFn } } options
|
|
12
15
|
*/
|
|
13
16
|
module.exports = function traverse(element, options) {
|
|
14
17
|
|
|
15
|
-
const enter = options.enter
|
|
16
|
-
const leave = options.leave
|
|
18
|
+
const enter = options.enter;
|
|
19
|
+
const leave = options.leave;
|
|
17
20
|
|
|
18
21
|
const enterSubTree = enter && enter(element);
|
|
19
22
|
|