@shopgate/eslint-config 7.33.0-beta.2 → 7.33.1-beta.1
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
CHANGED
|
@@ -25,19 +25,21 @@ Add the following to the `.eslintrc` in your project:
|
|
|
25
25
|
## Rules
|
|
26
26
|
|
|
27
27
|
1. [General](#general)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
- 1.1 [Commented out code](#commented-out-code)
|
|
29
|
+
- 1.2 [Comma dangle](#comma-dangle)
|
|
30
|
+
- 1.3 [Multiple empty lines](#multiple-empty-lines)
|
|
31
31
|
2. [Comments](#comments)
|
|
32
|
-
|
|
32
|
+
- 2.1 [Capitalized comments](#capitalized-comments)
|
|
33
33
|
3. [Functions](#functions)
|
|
34
|
-
|
|
34
|
+
- 3.1 [Point free](#point-free)
|
|
35
35
|
4. [Objects](#objects)
|
|
36
|
-
|
|
36
|
+
- 4.1 [Single line objects](#single-line-objects)
|
|
37
37
|
5. [Documentation](#documentation)
|
|
38
|
-
|
|
38
|
+
- 5.1 [JSDoc requirement](#jsdoc-required)
|
|
39
39
|
6. [React](#react)
|
|
40
|
-
|
|
40
|
+
- 6.1 [Prop Types](#react-proptypes)
|
|
41
|
+
7. [Extensions](#extensions)
|
|
42
|
+
- 7.1 [Missing config.json](#missing-extension-config)
|
|
41
43
|
|
|
42
44
|
### General
|
|
43
45
|
|
|
@@ -249,3 +251,27 @@ static propTypes = {
|
|
|
249
251
|
```
|
|
250
252
|
|
|
251
253
|
**[⬆ back to top](#rules)**
|
|
254
|
+
|
|
255
|
+
### Extensions
|
|
256
|
+
|
|
257
|
+
<a id="missing-extension-config"></a><a name="7.1"></a>
|
|
258
|
+
|
|
259
|
+
#### Missing config.json
|
|
260
|
+
|
|
261
|
+
The `config.json` inside the `frontend` folder of an extension is generated by the platform SDK
|
|
262
|
+
when the extension is attached and the frontend is started. In a fresh checkout the file doesn't
|
|
263
|
+
exist yet, so imports of it can't be resolved.
|
|
264
|
+
|
|
265
|
+
`rulesdir/missing-extension-config` reports those imports as a warning instead of the
|
|
266
|
+
`import/no-unresolved` error. Don't change the ESLint config of the extension to hide it; the
|
|
267
|
+
warning disappears as soon as the file was generated.
|
|
268
|
+
|
|
269
|
+
```javascript
|
|
270
|
+
// warning as long as config.json wasn't generated
|
|
271
|
+
import config from './config.json';
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
The rule also reports other relative `config` imports that can't be resolved. The PWA monorepo
|
|
275
|
+
raises it to `error` in its own `.eslintrc.js`.
|
|
276
|
+
|
|
277
|
+
**[⬆ back to top](#rules)**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shopgate/eslint-config",
|
|
3
|
-
"version": "7.33.
|
|
3
|
+
"version": "7.33.1-beta.1",
|
|
4
4
|
"description": "Eslint configuration for the Shopgate Connect projects.",
|
|
5
5
|
"author": "Shopgate <support@shopgate.com>",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
24
24
|
"eslint-plugin-react": "^7.37.5",
|
|
25
25
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
26
|
+
"eslint-plugin-rulesdir": "^0.2.2",
|
|
26
27
|
"eslint-plugin-tss-unused-classes": "^1.0.4",
|
|
27
28
|
"typescript": "^5.9.3"
|
|
28
29
|
},
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const os = require('os');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { RuleTester } = require('eslint');
|
|
5
|
+
const rule = require('../missing-extension-config');
|
|
6
|
+
|
|
7
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'missing-extension-config-'));
|
|
8
|
+
const attached = path.join(root, 'attached', 'frontend');
|
|
9
|
+
const detached = path.join(root, 'detached', 'frontend');
|
|
10
|
+
const library = path.join(root, 'library');
|
|
11
|
+
|
|
12
|
+
fs.mkdirSync(path.join(attached, 'components'), { recursive: true });
|
|
13
|
+
fs.mkdirSync(path.join(detached, 'components'), { recursive: true });
|
|
14
|
+
fs.mkdirSync(path.join(detached, 'helpers', 'config'), { recursive: true });
|
|
15
|
+
fs.mkdirSync(library);
|
|
16
|
+
fs.writeFileSync(path.join(root, 'attached', 'extension-config.json'), '{}');
|
|
17
|
+
fs.writeFileSync(path.join(root, 'detached', 'extension-config.json'), '{}');
|
|
18
|
+
fs.writeFileSync(path.join(attached, 'config.json'), '{}');
|
|
19
|
+
fs.writeFileSync(path.join(detached, 'components', 'config.js'), '');
|
|
20
|
+
fs.writeFileSync(path.join(detached, 'helpers', 'config', 'index.ts'), '');
|
|
21
|
+
|
|
22
|
+
afterAll(() => {
|
|
23
|
+
fs.rmSync(root, {
|
|
24
|
+
recursive: true,
|
|
25
|
+
force: true,
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const ruleTester = new RuleTester({
|
|
30
|
+
parserOptions: {
|
|
31
|
+
ecmaVersion: 2020,
|
|
32
|
+
sourceType: 'module',
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
ruleTester.run('missing-extension-config', rule, {
|
|
37
|
+
valid: [
|
|
38
|
+
{
|
|
39
|
+
code: "import config from './config.json';",
|
|
40
|
+
filename: path.join(attached, 'index.js'),
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
code: "import config from './config';",
|
|
44
|
+
filename: path.join(attached, 'index.js'),
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
code: "import { enabled } from '../config';",
|
|
48
|
+
filename: path.join(attached, 'components', 'Component.jsx'),
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
code: "import config from './config';",
|
|
52
|
+
filename: path.join(detached, 'components', 'Component.jsx'),
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
code: "import config from './config';",
|
|
56
|
+
filename: path.join(detached, 'helpers', 'index.js'),
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
code: "import config from './helpers/config.json';",
|
|
60
|
+
filename: path.join(detached, 'index.js'),
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
code: "import config from '@shopgate/pwa-common/helpers/config';",
|
|
64
|
+
filename: path.join(detached, 'index.js'),
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
code: "const config = require('./config.json');",
|
|
68
|
+
filename: path.join(attached, 'index.js'),
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
code: "export { default } from './config.json';",
|
|
72
|
+
filename: path.join(attached, 'index.js'),
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
code: 'const config = require(name);',
|
|
76
|
+
filename: path.join(detached, 'index.js'),
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
code: 'export const enabled = true;',
|
|
80
|
+
filename: path.join(detached, 'index.js'),
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
invalid: [
|
|
84
|
+
{
|
|
85
|
+
code: "import config from './config.json';",
|
|
86
|
+
filename: path.join(detached, 'index.js'),
|
|
87
|
+
errors: [{
|
|
88
|
+
messageId: 'missing',
|
|
89
|
+
data: { source: './config.json' },
|
|
90
|
+
}],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
code: "import config from './config';",
|
|
94
|
+
filename: path.join(detached, 'index.js'),
|
|
95
|
+
errors: [{ messageId: 'missing' }],
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
code: "import { enabled } from '../../config';",
|
|
99
|
+
filename: path.join(detached, 'helpers', 'config', 'index.ts'),
|
|
100
|
+
errors: [{ messageId: 'missing' }],
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
code: "import config from '../config.json';",
|
|
104
|
+
filename: path.join(detached, 'components', 'Component.jsx'),
|
|
105
|
+
errors: [{ messageId: 'missing' }],
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
code: "const config = require('./config.json');",
|
|
109
|
+
filename: path.join(detached, 'index.js'),
|
|
110
|
+
errors: [{ messageId: 'missing' }],
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
code: "const config = require('../config');",
|
|
114
|
+
filename: path.join(detached, 'helpers', 'index.js'),
|
|
115
|
+
errors: [{ messageId: 'missing' }],
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
code: "export { default } from './config.json';",
|
|
119
|
+
filename: path.join(detached, 'index.js'),
|
|
120
|
+
errors: [{ messageId: 'missing' }],
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
code: "export * from './config.json';",
|
|
124
|
+
filename: path.join(detached, 'index.js'),
|
|
125
|
+
errors: [{ messageId: 'missing' }],
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
code: "const config = import('./config.json');",
|
|
129
|
+
filename: path.join(detached, 'index.js'),
|
|
130
|
+
errors: [{ messageId: 'missing' }],
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
code: "import config from './config.json';",
|
|
134
|
+
filename: path.join(detached, 'helpers', 'index.js'),
|
|
135
|
+
errors: [{
|
|
136
|
+
messageId: 'unresolved',
|
|
137
|
+
data: { source: './config.json' },
|
|
138
|
+
}],
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
code: "import config from './config';",
|
|
142
|
+
filename: path.join(library, 'index.js'),
|
|
143
|
+
errors: [{ messageId: 'unresolved' }],
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
code: "const config = require('./config.json');",
|
|
147
|
+
filename: path.join(library, 'index.js'),
|
|
148
|
+
errors: [{ messageId: 'unresolved' }],
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = '^(\\./|(\\.\\./)+)config(\\.json)?$';
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const configImportPattern = require('./helpers/configImportPattern');
|
|
4
|
+
|
|
5
|
+
const CONFIG_IMPORT_REGEX = new RegExp(configImportPattern);
|
|
6
|
+
const MODULE_EXTENSIONS = ['.js', '.jsx', '.ts', '.tsx'];
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Checks whether an import target without file extension points to a script module.
|
|
10
|
+
* @param {string} target The absolute import target.
|
|
11
|
+
* @returns {boolean}
|
|
12
|
+
*/
|
|
13
|
+
const isScriptModule = target => MODULE_EXTENSIONS.some(extension => (
|
|
14
|
+
fs.existsSync(`${target}${extension}`) || fs.existsSync(path.join(target, `index${extension}`))
|
|
15
|
+
));
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Checks whether a relative config import can be resolved.
|
|
19
|
+
* @param {string} target The absolute import target.
|
|
20
|
+
* @returns {boolean}
|
|
21
|
+
*/
|
|
22
|
+
const isResolvable = (target) => {
|
|
23
|
+
if (target.endsWith('.json')) {
|
|
24
|
+
return fs.existsSync(target);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return isScriptModule(target) || fs.existsSync(`${target}.json`);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Checks whether an import target is the config.json which the platform SDK generates inside
|
|
32
|
+
* the frontend and extension folders of an extension.
|
|
33
|
+
* @param {string} target The absolute import target.
|
|
34
|
+
* @returns {boolean}
|
|
35
|
+
*/
|
|
36
|
+
const isGeneratedConfig = target => (
|
|
37
|
+
fs.existsSync(path.join(path.dirname(target), '..', 'extension-config.json'))
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
module.exports = {
|
|
41
|
+
meta: {
|
|
42
|
+
type: 'problem',
|
|
43
|
+
docs: {
|
|
44
|
+
description: 'Warn about imports of an extension config.json that was not generated yet',
|
|
45
|
+
},
|
|
46
|
+
schema: [],
|
|
47
|
+
messages: {
|
|
48
|
+
unresolved: 'Unable to resolve path to module "{{source}}".',
|
|
49
|
+
missing: '"{{source}}" doesn\'t exist yet. The platform SDK generates it for attached extensions ("sgconnect extension attach") when the frontend or backend is started. The warning can be ignored until then; don\'t change code or the ESLint config to hide it.',
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
create: (context) => {
|
|
53
|
+
/**
|
|
54
|
+
* Reports a source node that points to a config which can't be resolved.
|
|
55
|
+
* @param {Object} [sourceNode] The node that holds the import source.
|
|
56
|
+
*/
|
|
57
|
+
const check = (sourceNode) => {
|
|
58
|
+
const source = sourceNode?.value;
|
|
59
|
+
|
|
60
|
+
if (typeof source !== 'string' || !CONFIG_IMPORT_REGEX.test(source)) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const target = path.resolve(path.dirname(context.getFilename()), source);
|
|
65
|
+
|
|
66
|
+
if (isResolvable(target)) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
context.report({
|
|
71
|
+
node: sourceNode,
|
|
72
|
+
messageId: isGeneratedConfig(target) ? 'missing' : 'unresolved',
|
|
73
|
+
data: { source },
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
ImportDeclaration: node => check(node.source),
|
|
79
|
+
ImportExpression: node => check(node.source),
|
|
80
|
+
ExportNamedDeclaration: node => check(node.source),
|
|
81
|
+
ExportAllDeclaration: node => check(node.source),
|
|
82
|
+
CallExpression: (node) => {
|
|
83
|
+
if (node.callee.name === 'require' && node.arguments.length === 1) {
|
|
84
|
+
check(node.arguments[0]);
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
},
|
|
89
|
+
};
|
package/ruleset/extras.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const rulesDirPlugin = require('eslint-plugin-rulesdir');
|
|
3
|
+
const configImportPattern = require('../rules/helpers/configImportPattern');
|
|
4
|
+
|
|
5
|
+
rulesDirPlugin.RULES_DIR = path.join(__dirname, '..', 'rules');
|
|
6
|
+
|
|
1
7
|
module.exports = {
|
|
2
8
|
extends: [
|
|
3
9
|
'plugin:eslint-comments/recommended',
|
|
@@ -5,6 +11,7 @@ module.exports = {
|
|
|
5
11
|
plugins: [
|
|
6
12
|
'extra-rules',
|
|
7
13
|
'json',
|
|
14
|
+
'rulesdir',
|
|
8
15
|
'tss-unused-classes',
|
|
9
16
|
],
|
|
10
17
|
rules: {
|
|
@@ -17,6 +24,12 @@ module.exports = {
|
|
|
17
24
|
'eslint-comments/no-unused-disable': 'error',
|
|
18
25
|
'eslint-comments/no-unused-enable': 'error',
|
|
19
26
|
'tss-unused-classes/unused-classes': 'warn',
|
|
27
|
+
'import/no-unresolved': ['error', {
|
|
28
|
+
commonjs: true,
|
|
29
|
+
caseSensitive: true,
|
|
30
|
+
ignore: [configImportPattern],
|
|
31
|
+
}],
|
|
32
|
+
'rulesdir/missing-extension-config': 'warn',
|
|
20
33
|
'no-restricted-imports': ['warn', {
|
|
21
34
|
paths: [
|
|
22
35
|
{
|
|
@@ -25,7 +38,7 @@ module.exports = {
|
|
|
25
38
|
},
|
|
26
39
|
{
|
|
27
40
|
name: 'glamor',
|
|
28
|
-
message: 'glamor is deprecated and will be removed. Write styles with makeStyles from @shopgate/engage/styles.',
|
|
41
|
+
message: 'glamor is deprecated and will be removed. Write styles with makeStyles and global styles with injectGlobal, both from @shopgate/engage/styles. The shop must be deployed with PWA 7.32.0 or later.',
|
|
29
42
|
},
|
|
30
43
|
],
|
|
31
44
|
}],
|