@eagleoutice/eslint-config-flowr 2.1.6 → 2.1.8
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 +4 -2
- package/index.js +14 -14
- package/package.json +1 -2
- package/patterns.js +6 -6
- package/plugin.d.ts +1 -0
- package/plugin.js +3 -1
- package/rules/file-naming.d.ts +3 -0
- package/rules/file-naming.js +37 -0
package/README.md
CHANGED
|
@@ -19,7 +19,9 @@ configuration from your `tsdoc.json`:
|
|
|
19
19
|
## The `flowr` plugin
|
|
20
20
|
|
|
21
21
|
flowR groups its functions in helper objects (`DfEdge`, `Resolve`, `NodeId`, and friends) so that there is one obvious
|
|
22
|
-
entry point per topic. Two rules keep the code on those entry points, both on by default.
|
|
22
|
+
entry point per topic. Two rules keep the code on those entry points, both on by default. A third,
|
|
23
|
+
`flowr/file-naming`, checks file names (`dataflow-graph.ts`, `1-setup.ts`, `Vertex.ts`) and keeps `*.spec.ts` files
|
|
24
|
+
below `test/`.
|
|
23
25
|
|
|
24
26
|
| rule | driven by | finds |
|
|
25
27
|
| :-- | :-- | :-- |
|
|
@@ -80,7 +82,7 @@ The shipped set groups into families:
|
|
|
80
82
|
| family | shape it finds | helper it points at |
|
|
81
83
|
| :-- | :-- | :-- |
|
|
82
84
|
| `edge-*` | `e.types === T`, `(e.types & T) !== 0`, and the bitmask spellings around them | `DfEdge` |
|
|
83
|
-
| `vertex-is*`, `vertex-has-origin` | `v.tag === VertexType.X`, `
|
|
85
|
+
| `vertex-is*`, `vertex-has-origin` | `v.tag === VertexType.X`, `DfgVertex.isFunctionCall(v) && v.origin.includes(o)` | `DfgVertex` |
|
|
84
86
|
| `node-is*` | `n.type === RType.X`, negated and through `?.` | `RSymbol`, `RFunctionCall`, and the rest of the `R*` node objects |
|
|
85
87
|
| `argument-is-*`, `call-is-*`, `list-is-implicit` | a guard written out, `RFunctionCall.is(n) && n.named` | the narrower guard of the same object |
|
|
86
88
|
| `symbol-name-comparison*` | `symbol.content === 'name'`, which misses `pkg::name` because an identifier with a namespace is an array | `Identifier.getName` / `Identifier.matches` |
|
package/index.js
CHANGED
|
@@ -10,7 +10,6 @@ const eslint_plugin_2 = __importDefault(require("@stylistic/eslint-plugin"));
|
|
|
10
10
|
const eslint_plugin_jsdoc_1 = __importDefault(require("eslint-plugin-jsdoc"));
|
|
11
11
|
const eslint_plugin_tsdoc_1 = __importDefault(require("eslint-plugin-tsdoc"));
|
|
12
12
|
const eslint_plugin_unused_imports_1 = __importDefault(require("eslint-plugin-unused-imports"));
|
|
13
|
-
const eslint_plugin_check_file_1 = __importDefault(require("eslint-plugin-check-file"));
|
|
14
13
|
const eslint_plugin_import_x_1 = __importDefault(require("eslint-plugin-import-x"));
|
|
15
14
|
const eslint_plugin_unicorn_1 = __importDefault(require("eslint-plugin-unicorn"));
|
|
16
15
|
const plugin_1 = __importDefault(require("./plugin"));
|
|
@@ -41,7 +40,6 @@ const config = [
|
|
|
41
40
|
'@stylistic': eslint_plugin_2.default,
|
|
42
41
|
'import-x': eslint_plugin_import_x_1.default,
|
|
43
42
|
'unused-imports': eslint_plugin_unused_imports_1.default,
|
|
44
|
-
'check-file': eslint_plugin_check_file_1.default,
|
|
45
43
|
tsdoc: eslint_plugin_tsdoc_1.default,
|
|
46
44
|
jsdoc: eslint_plugin_jsdoc_1.default,
|
|
47
45
|
flowr: plugin_1.default,
|
|
@@ -105,14 +103,21 @@ const config = [
|
|
|
105
103
|
'@stylistic/no-extra-semi': 'error',
|
|
106
104
|
'jsdoc/check-alignment': 'error',
|
|
107
105
|
/*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
106
|
+
* `jsdoc/check-line-alignment` is deliberately NOT enabled. Lining up what the tags declare reads
|
|
107
|
+
* well, but 'always' reaches past the tags: it re-flows every hand-wrapped line to the margin, and
|
|
108
|
+
* that indentation is the author's and carries meaning -- a `@param` description hung under its
|
|
109
|
+
* first line, or the nested call in an R example that only its indentation ties to the call around
|
|
110
|
+
* it:
|
|
110
111
|
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
112
|
+
* ```r
|
|
113
|
+
* test_that("my test", {
|
|
114
|
+
* assert_equal(1 + 1, 2)
|
|
115
|
+
* })
|
|
116
|
+
* ```
|
|
117
|
+
*
|
|
118
|
+
* Neither `preserveMainDescriptionPostDelimiter` nor `disableWrapIndent` stops it doing that to a
|
|
119
|
+
* tag's continuation lines, so the alignment is left to the author, like the delimiter style is.
|
|
114
120
|
*/
|
|
115
|
-
'jsdoc/check-line-alignment': ['error', 'always', { disableWrapIndent: true }],
|
|
116
121
|
/* `@param name - what it is`, the spelling the codebase already uses everywhere */
|
|
117
122
|
'jsdoc/require-hyphen-before-param-description': ['error', 'always'],
|
|
118
123
|
'jsdoc/no-multi-asterisks': 'error',
|
|
@@ -178,12 +183,7 @@ const config = [
|
|
|
178
183
|
throw: { after: false }
|
|
179
184
|
}
|
|
180
185
|
}],
|
|
181
|
-
'
|
|
182
|
-
'**/*.ts': '?(\\d+-)?([A-Z])+([a-z])*((-|.)?([A-Z])+([a-z]))'
|
|
183
|
-
}],
|
|
184
|
-
'check-file/folder-match-with-fex': ['error', {
|
|
185
|
-
'*.spec.{js,jsx,ts,tsx}': 'test/**'
|
|
186
|
-
}],
|
|
186
|
+
'flowr/file-naming': 'error',
|
|
187
187
|
'@stylistic/no-trailing-spaces': 'error',
|
|
188
188
|
'@stylistic/space-infix-ops': ['error', { int32Hint: false }],
|
|
189
189
|
'@typescript-eslint/no-unused-vars': 'off',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eagleoutice/eslint-config-flowr",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.8",
|
|
4
4
|
"description": "Linting rules for flowr and friends",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"repository": {
|
|
@@ -37,7 +37,6 @@
|
|
|
37
37
|
"@stylistic/eslint-plugin": "^5.7.1",
|
|
38
38
|
"@typescript-eslint/eslint-plugin": "^8.54.0",
|
|
39
39
|
"@typescript-eslint/parser": "^8.54.0",
|
|
40
|
-
"eslint-plugin-check-file": "^3.3.1",
|
|
41
40
|
"eslint-plugin-import-x": "^4.17.1",
|
|
42
41
|
"eslint-plugin-jsdoc": "^62.5.0",
|
|
43
42
|
"eslint-plugin-tsdoc": "^0.5.0",
|
package/patterns.js
CHANGED
|
@@ -84,12 +84,12 @@ const discriminatorIs = (id, { property, enumName, enumFile, propertyFile, helpe
|
|
|
84
84
|
};
|
|
85
85
|
};
|
|
86
86
|
/**
|
|
87
|
-
* `v.tag === VertexType.X`. One `
|
|
88
|
-
* enum, so `{{type|last}}` spells the check without a table: `VertexType.Use` to `
|
|
87
|
+
* `v.tag === VertexType.X`. One `DfgVertex` helper answers for every kind, and its members are named after
|
|
88
|
+
* the enum, so `{{type|last}}` spells the check without a table: `VertexType.Use` to `DfgVertex.isUse`.
|
|
89
89
|
*/
|
|
90
90
|
const vertexIs = (id, options) => discriminatorIs(id, {
|
|
91
91
|
property: 'tag', enumName: 'VertexType', enumFile: VERTEX, propertyFile: VERTEX,
|
|
92
|
-
helper: '
|
|
92
|
+
helper: 'DfgVertex', member: 'is{{type|last}}', hint: 'Compare through the vertex helper'
|
|
93
93
|
}, options);
|
|
94
94
|
/**
|
|
95
95
|
* `n.type === RType.X`. Every `RType` member has a helper object of the same name prefixed with `R`
|
|
@@ -220,15 +220,15 @@ const patterns = [
|
|
|
220
220
|
{
|
|
221
221
|
id: 'vertex-has-origin',
|
|
222
222
|
selector: 'LogicalExpression[operator="&&"]'
|
|
223
|
-
+ `${helperCall('left', '
|
|
223
|
+
+ `${helperCall('left', 'DfgVertex', 'isFunctionCall')}`
|
|
224
224
|
+ `${call('right', 'includes')}[right.callee.object.type="MemberExpression"][right.callee.object.property.name="origin"]`,
|
|
225
225
|
capture: { vertex: 'left.arguments.0', origin: 'right.arguments.0' },
|
|
226
|
-
replace: '
|
|
226
|
+
replace: 'DfgVertex.hasOrigin({{vertex}}, {{origin}})',
|
|
227
227
|
declaredIn: { 'left.callee.object': VERTEX, 'right.callee.object.property': VERTEX },
|
|
228
228
|
sameText: [['left.arguments.0', 'right.callee.object.object']],
|
|
229
229
|
/* `hasOrigin` is no type predicate, so the replacement can drop a narrowing the code below relies on */
|
|
230
230
|
fix: false,
|
|
231
|
-
message: 'Use `
|
|
231
|
+
message: 'Use `DfgVertex.hasOrigin`, it is exactly this check. Check the narrowing first, `hasOrigin` returns a plain boolean.'
|
|
232
232
|
},
|
|
233
233
|
edgesFallback('graph-edges-from', 'outgoingEdges', 'edgesFrom'),
|
|
234
234
|
edgesFallback('graph-edges-to', 'ingoingEdges', 'edgesTo'),
|
package/plugin.d.ts
CHANGED
package/plugin.js
CHANGED
|
@@ -5,11 +5,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
/** The flowR-specific rules, see the README for the tags they read. */
|
|
6
6
|
const replacement_pattern_1 = __importDefault(require("./rules/replacement-pattern"));
|
|
7
7
|
const use_instead_1 = __importDefault(require("./rules/use-instead"));
|
|
8
|
+
const file_naming_1 = __importDefault(require("./rules/file-naming"));
|
|
8
9
|
const plugin = {
|
|
9
10
|
meta: { name: '@eagleoutice/eslint-config-flowr' },
|
|
10
11
|
rules: {
|
|
11
12
|
'use-instead': use_instead_1.default,
|
|
12
|
-
'replacement-pattern': replacement_pattern_1.default
|
|
13
|
+
'replacement-pattern': replacement_pattern_1.default,
|
|
14
|
+
'file-naming': file_naming_1.default
|
|
13
15
|
}
|
|
14
16
|
};
|
|
15
17
|
module.exports = plugin;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
const path_1 = __importDefault(require("path"));
|
|
6
|
+
/** `dataflow-graph`, `Vertex`, `1-setup`, `r-bridge.test`: lower-case words, each optionally capitalized */
|
|
7
|
+
const fileName = /^(\d+-)?[A-Z]?[a-z]+([-.][A-Z]?[a-z]+)*$/;
|
|
8
|
+
const specFile = /\.spec\.[jt]sx?$/;
|
|
9
|
+
const rule = {
|
|
10
|
+
meta: {
|
|
11
|
+
type: 'layout',
|
|
12
|
+
docs: {
|
|
13
|
+
description: 'enforce the flowR file naming convention and keep spec files in the test folder',
|
|
14
|
+
url: 'https://github.com/flowr-analysis/flowr-lint#flowrfile-naming'
|
|
15
|
+
},
|
|
16
|
+
schema: [],
|
|
17
|
+
messages: {
|
|
18
|
+
name: 'The filename "{{ name }}" does not match {{ pattern }}',
|
|
19
|
+
spec: 'The spec file "{{ name }}" belongs below test/'
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
create(context) {
|
|
23
|
+
return {
|
|
24
|
+
Program(node) {
|
|
25
|
+
const file = path_1.default.relative(context.cwd, context.physicalFilename).split(path_1.default.sep).join('/');
|
|
26
|
+
const name = path_1.default.posix.basename(file);
|
|
27
|
+
if (name.endsWith('.ts') && !fileName.test(name.slice(0, name.lastIndexOf('.')))) {
|
|
28
|
+
context.report({ node, messageId: 'name', data: { name, pattern: String(fileName) } });
|
|
29
|
+
}
|
|
30
|
+
if (specFile.test(name) && !file.startsWith('test/')) {
|
|
31
|
+
context.report({ node, messageId: 'spec', data: { name } });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
module.exports = rule;
|