@backstage/eslint-plugin 0.1.10 → 0.1.11
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/CHANGELOG.md +23 -0
- package/README.md +1 -0
- package/docs/rules/no-mixed-plugin-imports.md +67 -0
- package/index.js +2 -0
- package/knip-report.md +0 -7
- package/lib/getPackages.js +1 -1
- package/package.json +2 -2
- package/rules/no-mixed-plugin-imports.js +249 -0
- package/rules/no-undeclared-imports.js +2 -2
- package/src/__fixtures__/monorepo/packages/foo/package.json +8 -5
- package/src/no-mixed-plugin-imports.test.ts +68 -0
- package/src/no-undeclared-imports.test.ts +4 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# @backstage/eslint-plugin
|
|
2
2
|
|
|
3
|
+
## 0.1.11
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 098ef95: Fix custom rules package scanning performance.
|
|
8
|
+
- 063b2d3: Added new eslint rule to restrict mixed plugin imports.
|
|
9
|
+
|
|
10
|
+
New rule `@backstage/no-mixed-plugin-imports` disallows mixed imports between plugins that are mixing
|
|
11
|
+
the backstage architecture. This rule forces that:
|
|
12
|
+
|
|
13
|
+
- No imports from frontend plugins to backend plugins or other frontend plugins.
|
|
14
|
+
- No imports from backend plugins to frontend plugins or other backend plugins.
|
|
15
|
+
- No imports from common plugins to frontend or backend plugins.
|
|
16
|
+
|
|
17
|
+
The current recommended configuration is giving a warning for mixed imports. This is to be changed in
|
|
18
|
+
the future to an error so please adjust your workspace accordingly.
|
|
19
|
+
|
|
20
|
+
## 0.1.11-next.0
|
|
21
|
+
|
|
22
|
+
### Patch Changes
|
|
23
|
+
|
|
24
|
+
- 098ef95: Fix custom rules package scanning performance.
|
|
25
|
+
|
|
3
26
|
## 0.1.10
|
|
4
27
|
|
|
5
28
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -41,3 +41,4 @@ The following rules are provided by this plugin:
|
|
|
41
41
|
| [@backstage/no-relative-monorepo-imports](./docs/rules/no-relative-monorepo-imports.md) | Forbid relative imports that reach outside of the package in a monorepo. |
|
|
42
42
|
| [@backstage/no-undeclared-imports](./docs/rules/no-undeclared-imports.md) | Forbid imports of external packages that have not been declared in the appropriate dependencies field in `package.json`. |
|
|
43
43
|
| [@backstage/no-top-level-material-ui-4-imports](./docs/rules/no-top-level-material-ui-4-imports.md) | Forbid top level import from Material UI v4 packages. |
|
|
44
|
+
| [@backstage/no-mixed-plugin-imports](./docs/rules/no-mixed-plugin-imports.md) | Disallow mixed plugin imports. |
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# @backstage/no-mixed-plugin-imports
|
|
2
|
+
|
|
3
|
+
Disallow mixed imports between backstage plugins.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
Add the rules as follows, it has no options:
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
"@backstage/no-mixed-plugin-imports": ["error"]
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Rule Details
|
|
14
|
+
|
|
15
|
+
Given the following two target packages:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"name": "@backstage/plugin-foo",
|
|
20
|
+
"backstage": {
|
|
21
|
+
"role": "frontend-plugin"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"name": "@backstage/plugin-bar",
|
|
29
|
+
"backstage": {
|
|
30
|
+
"role": "frontend-plugin"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Fail
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { FooCard } from '@backstage/plugin-foo';
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Pass
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import { FooCard } from '@backstage/plugin-foo-react';
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Options
|
|
48
|
+
|
|
49
|
+
You can ignore specific target packages or files by adding them to the options in the `.eslintrc.js` file:
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
{
|
|
53
|
+
rules: {
|
|
54
|
+
'@backstage/no-mixed-plugin-imports': [
|
|
55
|
+
'error',
|
|
56
|
+
{
|
|
57
|
+
excludedTargetPackages: [
|
|
58
|
+
'@backstage/plugin-foo',
|
|
59
|
+
],
|
|
60
|
+
excludedFiles: [
|
|
61
|
+
'**/*.{test,spec}.[jt]s?(x)'
|
|
62
|
+
],
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
package/index.js
CHANGED
|
@@ -22,6 +22,7 @@ module.exports = {
|
|
|
22
22
|
'@backstage/no-forbidden-package-imports': 'error',
|
|
23
23
|
'@backstage/no-relative-monorepo-imports': 'error',
|
|
24
24
|
'@backstage/no-undeclared-imports': 'error',
|
|
25
|
+
'@backstage/no-mixed-plugin-imports': 'warn',
|
|
25
26
|
},
|
|
26
27
|
},
|
|
27
28
|
},
|
|
@@ -30,5 +31,6 @@ module.exports = {
|
|
|
30
31
|
'no-relative-monorepo-imports': require('./rules/no-relative-monorepo-imports'),
|
|
31
32
|
'no-undeclared-imports': require('./rules/no-undeclared-imports'),
|
|
32
33
|
'no-top-level-material-ui-4-imports': require('./rules/no-top-level-material-ui-4-imports'),
|
|
34
|
+
'no-mixed-plugin-imports': require('./rules/no-mixed-plugin-imports'),
|
|
33
35
|
},
|
|
34
36
|
};
|
package/knip-report.md
CHANGED
package/lib/getPackages.js
CHANGED
|
@@ -21,7 +21,7 @@ const manypkg = require('@manypkg/get-packages');
|
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* @typedef ExtendedPackage
|
|
24
|
-
* @type {import('@manypkg/get-packages').Package & { packageJson: { exports?: Record<string, string>, files?: Array<string>, backstage?: { inline?: boolean } }}} packageJson
|
|
24
|
+
* @type {import('@manypkg/get-packages').Package & { packageJson: { exports?: Record<string, string>, files?: Array<string>, backstage?: { inline?: boolean, role?: string } }}} packageJson
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/eslint-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "Backstage ESLint plugin",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"minimatch": "^9.0.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@backstage/cli": "^0.
|
|
25
|
+
"@backstage/cli": "^0.33.0",
|
|
26
26
|
"@types/estree": "^1.0.5",
|
|
27
27
|
"eslint": "^8.33.0"
|
|
28
28
|
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// @ts-check
|
|
18
|
+
|
|
19
|
+
const visitImports = require('../lib/visitImports');
|
|
20
|
+
const getPackages = require('../lib/getPackages');
|
|
21
|
+
const minimatch = require('minimatch');
|
|
22
|
+
|
|
23
|
+
/** @typedef {import('../lib/getPackages.js').ExtendedPackage} ExtendedPackage */
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {string} pattern
|
|
27
|
+
* @param {string} filePath
|
|
28
|
+
* @returns {boolean}
|
|
29
|
+
*/
|
|
30
|
+
const matchesPattern = (pattern, filePath) => {
|
|
31
|
+
return new minimatch.Minimatch(pattern).match(filePath);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const roleRules = [
|
|
35
|
+
{
|
|
36
|
+
sourceRole: ['frontend-plugin', 'web-library'],
|
|
37
|
+
targetRole: [
|
|
38
|
+
'backend-plugin',
|
|
39
|
+
'node-library',
|
|
40
|
+
'backend-plugin-module',
|
|
41
|
+
'frontend-plugin',
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
sourceRole: ['backend-plugin', 'node-library', 'backend-plugin-module'],
|
|
46
|
+
targetRole: ['frontend-plugin', 'web-library', 'backend-plugin'],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
sourceRole: ['common-library'],
|
|
50
|
+
targetRole: [
|
|
51
|
+
'frontend-plugin',
|
|
52
|
+
'web-library',
|
|
53
|
+
'backend-plugin',
|
|
54
|
+
'node-library',
|
|
55
|
+
'backend-plugin-module',
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
|
61
|
+
module.exports = {
|
|
62
|
+
meta: {
|
|
63
|
+
type: 'problem',
|
|
64
|
+
messages: {
|
|
65
|
+
forbidden:
|
|
66
|
+
'{{sourcePackage}} ({{sourceRole}}) uses forbidden import from {{targetPackage}} ({{targetRole}}).',
|
|
67
|
+
useReactPlugin:
|
|
68
|
+
'Use web library {{targetPackage}}-react or common library instead.',
|
|
69
|
+
useNodePlugin:
|
|
70
|
+
'Use node library {{targetPackage}}-node or common library instead.',
|
|
71
|
+
useCommonPlugin: 'Use common library {{targetPackage}}-common instead.',
|
|
72
|
+
removeImport:
|
|
73
|
+
'Remove this import to avoid mixed plugin imports. Fix the code by refactoring it to use the correct plugin type.',
|
|
74
|
+
},
|
|
75
|
+
docs: {
|
|
76
|
+
description: 'Disallow mixed plugin imports.',
|
|
77
|
+
url: 'https://github.com/backstage/backstage/blob/master/packages/eslint-plugin/docs/rules/no-mixed-plugin-imports.md',
|
|
78
|
+
},
|
|
79
|
+
hasSuggestions: true,
|
|
80
|
+
schema: [
|
|
81
|
+
{
|
|
82
|
+
type: 'object',
|
|
83
|
+
properties: {
|
|
84
|
+
excludedTargetPackages: {
|
|
85
|
+
type: 'array',
|
|
86
|
+
items: { type: 'string' },
|
|
87
|
+
uniqueItems: true,
|
|
88
|
+
},
|
|
89
|
+
excludedFiles: {
|
|
90
|
+
type: 'array',
|
|
91
|
+
items: { type: 'string' },
|
|
92
|
+
uniqueItems: true,
|
|
93
|
+
},
|
|
94
|
+
includedFiles: {
|
|
95
|
+
type: 'array',
|
|
96
|
+
items: { type: 'string' },
|
|
97
|
+
uniqueItems: true,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
additionalProperties: false,
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
create(context) {
|
|
105
|
+
const packages = getPackages(context.cwd);
|
|
106
|
+
if (!packages) {
|
|
107
|
+
return {};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const filePath = context.physicalFilename
|
|
111
|
+
? context.physicalFilename
|
|
112
|
+
: context.filename;
|
|
113
|
+
|
|
114
|
+
/** @type {ExtendedPackage | undefined} */
|
|
115
|
+
const pkg = packages.byPath(filePath);
|
|
116
|
+
if (!pkg) {
|
|
117
|
+
return {};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const options = context.options[0] || {};
|
|
121
|
+
/** @type {string[]} */
|
|
122
|
+
const ignoreTargetPackages = options.excludedTargetPackages || [];
|
|
123
|
+
/** @type {string[]} */
|
|
124
|
+
const excludePatterns = options.excludedFiles || [];
|
|
125
|
+
/** @type {string[]} */
|
|
126
|
+
const includePatterns = options.includedFiles || ['**/src/**'];
|
|
127
|
+
|
|
128
|
+
if (
|
|
129
|
+
!includePatterns.some(pattern => matchesPattern(pattern, filePath)) ||
|
|
130
|
+
excludePatterns.some(pattern => matchesPattern(pattern, filePath))
|
|
131
|
+
) {
|
|
132
|
+
return {};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return visitImports(context, (node, imp) => {
|
|
136
|
+
if (imp.type !== 'internal') {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** @type {ExtendedPackage | undefined} */
|
|
141
|
+
const targetPackage = imp.package;
|
|
142
|
+
const targetName = targetPackage?.packageJson.name;
|
|
143
|
+
const sourceName = pkg.packageJson.name;
|
|
144
|
+
if (sourceName === targetName) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const sourceRole = pkg.packageJson.backstage?.role;
|
|
149
|
+
const targetRole = targetPackage.packageJson.backstage?.role;
|
|
150
|
+
if (!sourceRole || !targetRole) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (
|
|
155
|
+
roleRules.some(
|
|
156
|
+
rule =>
|
|
157
|
+
rule.sourceRole.includes(sourceRole) &&
|
|
158
|
+
rule.targetRole.includes(targetRole) &&
|
|
159
|
+
!ignoreTargetPackages.includes(targetName),
|
|
160
|
+
)
|
|
161
|
+
) {
|
|
162
|
+
const suggest = [];
|
|
163
|
+
|
|
164
|
+
if (
|
|
165
|
+
(sourceRole === 'frontend-plugin' || sourceRole === 'web-library') &&
|
|
166
|
+
targetRole === 'frontend-plugin'
|
|
167
|
+
) {
|
|
168
|
+
suggest.push({
|
|
169
|
+
messageId: 'useReactPlugin',
|
|
170
|
+
data: {
|
|
171
|
+
targetPackage: targetName,
|
|
172
|
+
},
|
|
173
|
+
/** @param {import('eslint').Rule.RuleFixer} fixer */
|
|
174
|
+
fix(fixer) {
|
|
175
|
+
const source = context.sourceCode;
|
|
176
|
+
const nodeSource = source.getText(imp.node);
|
|
177
|
+
const newImport = nodeSource.replace(/'$/, "-react'");
|
|
178
|
+
return fixer.replaceText(imp.node, newImport);
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
suggest.push({
|
|
182
|
+
messageId: 'useCommonPlugin',
|
|
183
|
+
data: {
|
|
184
|
+
targetPackage: targetName,
|
|
185
|
+
},
|
|
186
|
+
/** @param {import('eslint').Rule.RuleFixer} fixer */
|
|
187
|
+
fix(fixer) {
|
|
188
|
+
const source = context.sourceCode;
|
|
189
|
+
const nodeSource = source.getText(imp.node);
|
|
190
|
+
const newImport = nodeSource.replace(/'$/, "-common'");
|
|
191
|
+
return fixer.replaceText(imp.node, newImport);
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
} else if (
|
|
195
|
+
(sourceRole === 'backend-plugin' ||
|
|
196
|
+
sourceRole === 'backend-plugin-module') &&
|
|
197
|
+
targetRole === 'backend-plugin'
|
|
198
|
+
) {
|
|
199
|
+
suggest.push({
|
|
200
|
+
messageId: 'useNodePlugin',
|
|
201
|
+
data: {
|
|
202
|
+
targetPackage: targetName,
|
|
203
|
+
},
|
|
204
|
+
/** @param {import('eslint').Rule.RuleFixer} fixer */
|
|
205
|
+
fix(fixer) {
|
|
206
|
+
const source = context.sourceCode;
|
|
207
|
+
const nodeSource = source.getText(imp.node);
|
|
208
|
+
const newImport = nodeSource.replace(/-backend'$/, "-node'");
|
|
209
|
+
return fixer.replaceText(imp.node, newImport);
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
suggest.push({
|
|
213
|
+
messageId: 'useCommonPlugin',
|
|
214
|
+
data: {
|
|
215
|
+
targetPackage: targetName,
|
|
216
|
+
},
|
|
217
|
+
/** @param {import('eslint').Rule.RuleFixer} fixer */
|
|
218
|
+
fix(fixer) {
|
|
219
|
+
const source = context.sourceCode;
|
|
220
|
+
const nodeSource = source.getText(imp.node);
|
|
221
|
+
const newImport = nodeSource.replace(/-backend'$/, '-common');
|
|
222
|
+
return fixer.replaceText(imp.node, newImport);
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
} else {
|
|
226
|
+
suggest.push({
|
|
227
|
+
messageId: 'removeImport',
|
|
228
|
+
/** @param {import('eslint').Rule.RuleFixer} _fixer */
|
|
229
|
+
fix(_fixer) {
|
|
230
|
+
// Not a fixable case, just give a suggestion to remove the import
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
context.report({
|
|
236
|
+
node: node,
|
|
237
|
+
messageId: 'forbidden',
|
|
238
|
+
data: {
|
|
239
|
+
sourcePackage: pkg.packageJson.name || imp.package.dir,
|
|
240
|
+
sourceRole,
|
|
241
|
+
targetPackage: targetPackage.packageJson.name || imp.package.dir,
|
|
242
|
+
targetRole,
|
|
243
|
+
},
|
|
244
|
+
suggest,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
},
|
|
249
|
+
};
|
|
@@ -319,6 +319,7 @@ module.exports = {
|
|
|
319
319
|
if (importsToAdd.length > 0) {
|
|
320
320
|
addMissingImports(importsToAdd, packages, localPkg);
|
|
321
321
|
|
|
322
|
+
packages.clearCache();
|
|
322
323
|
// This switches all import directives back to the original import.
|
|
323
324
|
for (const added of importsToAdd) {
|
|
324
325
|
context.report({
|
|
@@ -336,6 +337,7 @@ module.exports = {
|
|
|
336
337
|
removeInlineImports(importsToInline, localPkg);
|
|
337
338
|
addForwardedInlineImports(importsToInline, localPkg);
|
|
338
339
|
|
|
340
|
+
packages.clearCache();
|
|
339
341
|
for (const inlined of importsToInline) {
|
|
340
342
|
context.report({
|
|
341
343
|
node: inlined.node,
|
|
@@ -350,8 +352,6 @@ module.exports = {
|
|
|
350
352
|
}
|
|
351
353
|
importsToInline.length = 0;
|
|
352
354
|
}
|
|
353
|
-
|
|
354
|
-
packages.clearCache();
|
|
355
355
|
},
|
|
356
356
|
...visitImports(context, (node, imp) => {
|
|
357
357
|
// We leave checking of type imports to the repo-tools check,
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@internal/foo",
|
|
3
|
+
"backstage": {
|
|
4
|
+
"role": "frontend-plugin"
|
|
5
|
+
},
|
|
6
|
+
"files": [
|
|
7
|
+
"dist",
|
|
8
|
+
"type-utils"
|
|
9
|
+
],
|
|
3
10
|
"dependencies": {
|
|
4
11
|
"@internal/bar": "1.0.0"
|
|
5
12
|
},
|
|
@@ -8,9 +15,5 @@
|
|
|
8
15
|
},
|
|
9
16
|
"peerDependencies": {
|
|
10
17
|
"react": "*"
|
|
11
|
-
}
|
|
12
|
-
"files": [
|
|
13
|
-
"dist",
|
|
14
|
-
"type-utils"
|
|
15
|
-
]
|
|
18
|
+
}
|
|
16
19
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { RuleTester } from 'eslint';
|
|
18
|
+
import path from 'path';
|
|
19
|
+
import rule from '../rules/no-mixed-plugin-imports';
|
|
20
|
+
|
|
21
|
+
const RULE = 'no-mixed-plugin-imports';
|
|
22
|
+
const FIXTURE = path.resolve(__dirname, '__fixtures__/monorepo');
|
|
23
|
+
|
|
24
|
+
const ERR = (
|
|
25
|
+
sourcePackage: string,
|
|
26
|
+
sourceRole: string,
|
|
27
|
+
targetPackage: string,
|
|
28
|
+
targetRole: string,
|
|
29
|
+
) => ({
|
|
30
|
+
message: `${sourcePackage} (${sourceRole}) uses forbidden import from ${targetPackage} (${targetRole}).`,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// cwd must be restored
|
|
34
|
+
const origDir = process.cwd();
|
|
35
|
+
afterAll(() => {
|
|
36
|
+
process.chdir(origDir);
|
|
37
|
+
});
|
|
38
|
+
process.chdir(FIXTURE);
|
|
39
|
+
|
|
40
|
+
const ruleTester = new RuleTester({
|
|
41
|
+
parserOptions: {
|
|
42
|
+
sourceType: 'module',
|
|
43
|
+
ecmaVersion: 2021,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
ruleTester.run(RULE, rule, {
|
|
48
|
+
valid: [
|
|
49
|
+
{
|
|
50
|
+
code: `import '@internal/inline'`,
|
|
51
|
+
filename: path.join(FIXTURE, 'packages/bar/src/index.ts'),
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
invalid: [
|
|
55
|
+
{
|
|
56
|
+
code: `import '@internal/foo'`,
|
|
57
|
+
filename: path.join(FIXTURE, 'packages/bar/src/index.ts'),
|
|
58
|
+
errors: [
|
|
59
|
+
ERR(
|
|
60
|
+
'@internal/bar',
|
|
61
|
+
'frontend-plugin',
|
|
62
|
+
'@internal/foo',
|
|
63
|
+
'frontend-plugin',
|
|
64
|
+
),
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
});
|
|
@@ -24,6 +24,7 @@ jest.mock('child_process', () => ({
|
|
|
24
24
|
|
|
25
25
|
const RULE = 'no-undeclared-imports';
|
|
26
26
|
const FIXTURE = joinPath(__dirname, '__fixtures__/monorepo');
|
|
27
|
+
//
|
|
27
28
|
|
|
28
29
|
const ERR_UNDECLARED = (
|
|
29
30
|
name: string,
|
|
@@ -246,26 +247,14 @@ ruleTester.run(RULE, rule, {
|
|
|
246
247
|
},
|
|
247
248
|
{
|
|
248
249
|
code: `import 'react-dom'`,
|
|
249
|
-
output: `import 'directive:add-import:
|
|
250
|
+
output: `import 'directive:add-import:peerDependencies:react-dom'`,
|
|
250
251
|
filename: joinPath(FIXTURE, 'packages/foo/src/index.ts'),
|
|
251
252
|
errors: [
|
|
252
253
|
ERR_UNDECLARED(
|
|
253
254
|
'react-dom',
|
|
254
|
-
'
|
|
255
|
-
joinPath('packages', 'foo'),
|
|
256
|
-
),
|
|
257
|
-
],
|
|
258
|
-
},
|
|
259
|
-
{
|
|
260
|
-
code: `import 'react-dom'`,
|
|
261
|
-
output: `import 'directive:add-import:devDependencies:react-dom'`,
|
|
262
|
-
filename: joinPath(FIXTURE, 'packages/foo/src/index.test.ts'),
|
|
263
|
-
errors: [
|
|
264
|
-
ERR_UNDECLARED(
|
|
265
|
-
'react-dom',
|
|
266
|
-
'devDependencies',
|
|
255
|
+
'peerDependencies',
|
|
267
256
|
joinPath('packages', 'foo'),
|
|
268
|
-
'--
|
|
257
|
+
'--peer',
|
|
269
258
|
),
|
|
270
259
|
],
|
|
271
260
|
},
|