@backstage/eslint-plugin 0.1.2 → 0.1.3
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 +12 -0
- package/lib/getPackages.js +4 -0
- package/lib/visitImports.js +27 -3
- package/package.json +2 -2
- package/rules/no-undeclared-imports.js +142 -52
- package/src/no-undeclared-imports.test.ts +57 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @backstage/eslint-plugin
|
|
2
2
|
|
|
3
|
+
## 0.1.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 911c25de59c: Add support for auto-fixing missing imports detected by the `no-undeclared-imports` rule.
|
|
8
|
+
|
|
9
|
+
## 0.1.3-next.0
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 911c25de59c: Add support for auto-fixing missing imports detected by the `no-undeclared-imports` rule.
|
|
14
|
+
|
|
3
15
|
## 0.1.2
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/lib/getPackages.js
CHANGED
|
@@ -31,6 +31,7 @@ const manypkg = require('@manypkg/get-packages');
|
|
|
31
31
|
* @property {ExtendedPackage} root
|
|
32
32
|
* @property {ExtendedPackage[]} list
|
|
33
33
|
* @property {Map<string, ExtendedPackage>} map
|
|
34
|
+
* @property {() => void} clearCache
|
|
34
35
|
* @property {(path: string) => ExtendedPackage | undefined} byPath
|
|
35
36
|
*/
|
|
36
37
|
|
|
@@ -64,6 +65,9 @@ module.exports = (function () {
|
|
|
64
65
|
pkg => !path.relative(pkg.dir, filePath).startsWith('..'),
|
|
65
66
|
);
|
|
66
67
|
},
|
|
68
|
+
clearCache() {
|
|
69
|
+
result = undefined;
|
|
70
|
+
},
|
|
67
71
|
};
|
|
68
72
|
lastLoadAt = Date.now();
|
|
69
73
|
return result;
|
package/lib/visitImports.js
CHANGED
|
@@ -24,6 +24,16 @@ const getPackages = require('./getPackages');
|
|
|
24
24
|
* @type {object}
|
|
25
25
|
* @property {'local'} type
|
|
26
26
|
* @property {'value' | 'type'} kind
|
|
27
|
+
* @property {import('estree').Node} node
|
|
28
|
+
* @property {string} path
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @typedef ImportDirective
|
|
33
|
+
* @type {object}
|
|
34
|
+
* @property {'directive'} type
|
|
35
|
+
* @property {'value' | 'type'} kind
|
|
36
|
+
* @property {import('estree').Node} node
|
|
27
37
|
* @property {string} path
|
|
28
38
|
*/
|
|
29
39
|
|
|
@@ -32,6 +42,7 @@ const getPackages = require('./getPackages');
|
|
|
32
42
|
* @type {object}
|
|
33
43
|
* @property {'internal'} type
|
|
34
44
|
* @property {'value' | 'type'} kind
|
|
45
|
+
* @property {import('estree').Node} node
|
|
35
46
|
* @property {string} path
|
|
36
47
|
* @property {import('./getPackages').ExtendedPackage} package
|
|
37
48
|
* @property {string} packageName
|
|
@@ -42,6 +53,7 @@ const getPackages = require('./getPackages');
|
|
|
42
53
|
* @type {object}
|
|
43
54
|
* @property {'external'} type
|
|
44
55
|
* @property {'value' | 'type'} kind
|
|
56
|
+
* @property {import('estree').Node} node
|
|
45
57
|
* @property {string} path
|
|
46
58
|
* @property {string} packageName
|
|
47
59
|
*/
|
|
@@ -51,6 +63,7 @@ const getPackages = require('./getPackages');
|
|
|
51
63
|
* @type {object}
|
|
52
64
|
* @property {'builtin'} type
|
|
53
65
|
* @property {'value' | 'type'} kind
|
|
66
|
+
* @property {import('estree').Literal} node
|
|
54
67
|
* @property {string} path
|
|
55
68
|
* @property {string} packageName
|
|
56
69
|
*/
|
|
@@ -58,7 +71,7 @@ const getPackages = require('./getPackages');
|
|
|
58
71
|
/**
|
|
59
72
|
* @callback ImportVisitor
|
|
60
73
|
* @param {ConsideredNode} node
|
|
61
|
-
* @param {LocalImport | InternalImport | ExternalImport | BuiltinImport} import
|
|
74
|
+
* @param {ImportDirective | LocalImport | InternalImport | ExternalImport | BuiltinImport} import
|
|
62
75
|
*/
|
|
63
76
|
|
|
64
77
|
/**
|
|
@@ -68,7 +81,7 @@ const getPackages = require('./getPackages');
|
|
|
68
81
|
|
|
69
82
|
/**
|
|
70
83
|
* @param {ConsideredNode} node
|
|
71
|
-
* @returns {undefined | {path: string, kind: 'type' | 'value'}}
|
|
84
|
+
* @returns {undefined | {path: string, node: import('estree').Literal, kind: 'type' | 'value'}}
|
|
72
85
|
*/
|
|
73
86
|
function getImportInfo(node) {
|
|
74
87
|
/** @type {import('estree').Expression | import('estree').SpreadElement | undefined | null} */
|
|
@@ -95,7 +108,11 @@ function getImportInfo(node) {
|
|
|
95
108
|
|
|
96
109
|
/** @type {any} */
|
|
97
110
|
const anyNode = node;
|
|
98
|
-
return {
|
|
111
|
+
return {
|
|
112
|
+
path: pathNode.value,
|
|
113
|
+
node: pathNode,
|
|
114
|
+
kind: anyNode.importKind ?? 'value',
|
|
115
|
+
};
|
|
99
116
|
}
|
|
100
117
|
|
|
101
118
|
/**
|
|
@@ -122,6 +139,10 @@ module.exports = function visitImports(context, visitor) {
|
|
|
122
139
|
return visitor(node, { type: 'local', ...info });
|
|
123
140
|
}
|
|
124
141
|
|
|
142
|
+
if (info.path.startsWith('directive:')) {
|
|
143
|
+
return visitor(node, { type: 'directive', ...info });
|
|
144
|
+
}
|
|
145
|
+
|
|
125
146
|
const pathParts = info.path.split('/');
|
|
126
147
|
|
|
127
148
|
// Check for match with plain name, then namespaced name
|
|
@@ -143,6 +164,7 @@ module.exports = function visitImports(context, visitor) {
|
|
|
143
164
|
return visitor(node, {
|
|
144
165
|
type: 'builtin',
|
|
145
166
|
kind: info.kind,
|
|
167
|
+
node: info.node,
|
|
146
168
|
path: subPath,
|
|
147
169
|
packageName,
|
|
148
170
|
});
|
|
@@ -151,6 +173,7 @@ module.exports = function visitImports(context, visitor) {
|
|
|
151
173
|
return visitor(node, {
|
|
152
174
|
type: 'external',
|
|
153
175
|
kind: info.kind,
|
|
176
|
+
node: info.node,
|
|
154
177
|
path: subPath,
|
|
155
178
|
packageName,
|
|
156
179
|
});
|
|
@@ -159,6 +182,7 @@ module.exports = function visitImports(context, visitor) {
|
|
|
159
182
|
return visitor(node, {
|
|
160
183
|
type: 'internal',
|
|
161
184
|
kind: info.kind,
|
|
185
|
+
node: info.node,
|
|
162
186
|
path: subPath,
|
|
163
187
|
package: pkg,
|
|
164
188
|
packageName,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/eslint-plugin",
|
|
3
3
|
"description": "Backstage ESLint plugin",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.3",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"minimatch": "^5.1.2"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@backstage/cli": "^0.22.
|
|
25
|
+
"@backstage/cli": "^0.22.6",
|
|
26
26
|
"eslint": "^8.33.0"
|
|
27
27
|
}
|
|
28
28
|
}
|
|
@@ -20,6 +20,7 @@ const path = require('path');
|
|
|
20
20
|
const getPackageMap = require('../lib/getPackages');
|
|
21
21
|
const visitImports = require('../lib/visitImports');
|
|
22
22
|
const minimatch = require('minimatch');
|
|
23
|
+
const { execFileSync } = require('child_process');
|
|
23
24
|
|
|
24
25
|
const depFields = {
|
|
25
26
|
dep: 'dependencies',
|
|
@@ -124,11 +125,13 @@ function getAddFlagForDepsField(depsField) {
|
|
|
124
125
|
module.exports = {
|
|
125
126
|
meta: {
|
|
126
127
|
type: 'problem',
|
|
128
|
+
fixable: 'code',
|
|
127
129
|
messages: {
|
|
128
130
|
undeclared:
|
|
129
131
|
"{{ packageName }} must be declared in {{ depsField }} of {{ packageJsonPath }}, run 'yarn --cwd {{ packagePath }} add{{ addFlag }} {{ packageName }}' from the project root.",
|
|
130
132
|
switch:
|
|
131
133
|
'{{ packageName }} is declared in {{ oldDepsField }}, but should be moved to {{ depsField }} in {{ packageJsonPath }}.',
|
|
134
|
+
switchBack: 'Switch back to import declaration',
|
|
132
135
|
},
|
|
133
136
|
docs: {
|
|
134
137
|
description:
|
|
@@ -150,63 +153,150 @@ module.exports = {
|
|
|
150
153
|
return {};
|
|
151
154
|
}
|
|
152
155
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
// and we skip builtins and local imports
|
|
156
|
-
if (
|
|
157
|
-
imp.kind === 'type' ||
|
|
158
|
-
imp.type === 'builtin' ||
|
|
159
|
-
imp.type === 'local'
|
|
160
|
-
) {
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
156
|
+
/** @type Array<{name: string, flag: string, node: import('estree').Node}> */
|
|
157
|
+
const importsToAdd = [];
|
|
163
158
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
159
|
+
return {
|
|
160
|
+
// All missing imports that we detect are collected as we traverse, and then we use
|
|
161
|
+
// the program exit to execute all install directives that have been found.
|
|
162
|
+
['Program:exit']() {
|
|
163
|
+
/** @type Record<string, Set<string>> */
|
|
164
|
+
const byFlag = {};
|
|
165
|
+
|
|
166
|
+
for (const { name, flag } of importsToAdd) {
|
|
167
|
+
byFlag[flag] = byFlag[flag] ?? new Set();
|
|
168
|
+
byFlag[flag].add(name);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
for (const name of byFlag[''] ?? []) {
|
|
172
|
+
byFlag['--dev']?.delete(name);
|
|
173
|
+
}
|
|
174
|
+
for (const name of byFlag['--peer'] ?? []) {
|
|
175
|
+
byFlag['']?.delete(name);
|
|
176
|
+
byFlag['--dev']?.delete(name);
|
|
177
|
+
}
|
|
168
178
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const conflict = findConflict(
|
|
177
|
-
localPkg.packageJson,
|
|
178
|
-
imp.packageName,
|
|
179
|
-
expectedType,
|
|
180
|
-
);
|
|
181
|
-
|
|
182
|
-
if (conflict) {
|
|
183
|
-
try {
|
|
184
|
-
const fullImport = imp.path
|
|
185
|
-
? `${imp.packageName}/${imp.path}`
|
|
186
|
-
: imp.packageName;
|
|
187
|
-
require.resolve(fullImport, {
|
|
188
|
-
paths: [localPkg.dir],
|
|
179
|
+
for (const [flag, names] of Object.entries(byFlag)) {
|
|
180
|
+
// The security implication of this is a bit interesting, as crafted add-import
|
|
181
|
+
// directives could be used to install malicious packages. However, the same is true
|
|
182
|
+
// for adding malicious packages to package.json, so there's significant difference.
|
|
183
|
+
execFileSync('yarn', ['add', ...(flag ? [flag] : []), ...names], {
|
|
184
|
+
cwd: localPkg.dir,
|
|
185
|
+
stdio: 'inherit',
|
|
189
186
|
});
|
|
190
|
-
}
|
|
191
|
-
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// This switches all import directives back to the original import.
|
|
190
|
+
for (const added of importsToAdd) {
|
|
191
|
+
context.report({
|
|
192
|
+
node: added.node,
|
|
193
|
+
messageId: 'switchBack',
|
|
194
|
+
fix(fixer) {
|
|
195
|
+
return fixer.replaceText(added.node, `'${added.name}'`);
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
importsToAdd.length = 0;
|
|
201
|
+
packages.clearCache();
|
|
202
|
+
},
|
|
203
|
+
...visitImports(context, (node, imp) => {
|
|
204
|
+
// We leave checking of type imports to the repo-tools check,
|
|
205
|
+
// and we skip builtins and local imports
|
|
206
|
+
if (
|
|
207
|
+
imp.kind === 'type' ||
|
|
208
|
+
imp.type === 'builtin' ||
|
|
209
|
+
imp.type === 'local'
|
|
210
|
+
) {
|
|
192
211
|
return;
|
|
193
212
|
}
|
|
194
213
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
214
|
+
// Any import directive that is found is collected for processing later
|
|
215
|
+
if (imp.type === 'directive') {
|
|
216
|
+
const parts = imp.path.split(':');
|
|
217
|
+
if (parts[1] !== 'add-import') {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const [type, name] = parts.slice(2);
|
|
221
|
+
if (!name.match(/^(@[-\w\.~]+\/)?[-\w\.~]*$/i)) {
|
|
222
|
+
throw new Error(
|
|
223
|
+
`Invalid package name to add as dependency: '${name}'`,
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
importsToAdd.push({
|
|
228
|
+
flag: getAddFlagForDepsField(type).trim(),
|
|
229
|
+
name,
|
|
230
|
+
node: imp.node,
|
|
231
|
+
});
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// We skip imports for the package itself
|
|
236
|
+
if (imp.packageName === localPkg.packageJson.name) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const modulePath = path.relative(localPkg.dir, filePath);
|
|
241
|
+
const expectedType = getExpectedDepType(
|
|
242
|
+
localPkg.packageJson,
|
|
243
|
+
imp.packageName,
|
|
244
|
+
modulePath,
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
const conflict = findConflict(
|
|
248
|
+
localPkg.packageJson,
|
|
249
|
+
imp.packageName,
|
|
250
|
+
expectedType,
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
if (conflict) {
|
|
254
|
+
try {
|
|
255
|
+
const fullImport = imp.path
|
|
256
|
+
? `${imp.packageName}/${imp.path}`
|
|
257
|
+
: imp.packageName;
|
|
258
|
+
require.resolve(fullImport, {
|
|
259
|
+
paths: [localPkg.dir],
|
|
260
|
+
});
|
|
261
|
+
} catch {
|
|
262
|
+
// If the dependency doesn't resolve then it's likely a type import, ignore
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const packagePath = path.relative(packages.root.dir, localPkg.dir);
|
|
267
|
+
const packageJsonPath = path.join(packagePath, 'package.json');
|
|
268
|
+
|
|
269
|
+
context.report({
|
|
270
|
+
node,
|
|
271
|
+
messageId: conflict.oldDepsField ? 'switch' : 'undeclared',
|
|
272
|
+
data: {
|
|
273
|
+
...conflict,
|
|
274
|
+
packagePath,
|
|
275
|
+
addFlag: getAddFlagForDepsField(conflict.depsField),
|
|
276
|
+
packageName: imp.packageName,
|
|
277
|
+
packageJsonPath: packageJsonPath,
|
|
278
|
+
},
|
|
279
|
+
// This fix callback is always executed, regardless of whether linting is run with
|
|
280
|
+
// fixes enabled or not. There is no way to determine if fixes are being applied, so
|
|
281
|
+
// instead our fix will replace the import with a directive that will be picked up
|
|
282
|
+
// on the next run. When ESLint applies fixes all rules are re-run to make sure the fixes
|
|
283
|
+
// applied correctly, which means that these directives will be picked up, executed,
|
|
284
|
+
// and switched back to the original import immediately.
|
|
285
|
+
// This is not true for all editor integrations. For example, VSCode translates there fixes
|
|
286
|
+
// to native editor commands, and does not re-run ESLint. This means that the import directive
|
|
287
|
+
// will end up in source code, and the import directive fix needs to be applied manually too.
|
|
288
|
+
// There is to my knowledge no way around this that doesn't get very hacky, so it will do for now.
|
|
289
|
+
fix: conflict.oldDepsField
|
|
290
|
+
? undefined
|
|
291
|
+
: fixer => {
|
|
292
|
+
return fixer.replaceText(
|
|
293
|
+
imp.node,
|
|
294
|
+
`'directive:add-import:${conflict.depsField}:${imp.packageName}'`,
|
|
295
|
+
);
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}),
|
|
300
|
+
};
|
|
211
301
|
},
|
|
212
302
|
};
|
|
@@ -18,6 +18,10 @@ import { RuleTester } from 'eslint';
|
|
|
18
18
|
import { join as joinPath } from 'path';
|
|
19
19
|
import rule from '../rules/no-undeclared-imports';
|
|
20
20
|
|
|
21
|
+
jest.mock('child_process', () => ({
|
|
22
|
+
execFileSync: jest.fn(),
|
|
23
|
+
}));
|
|
24
|
+
|
|
21
25
|
const RULE = 'no-undeclared-imports';
|
|
22
26
|
const FIXTURE = joinPath(__dirname, '__fixtures__/monorepo');
|
|
23
27
|
|
|
@@ -45,6 +49,9 @@ const ERR_SWITCHED = (
|
|
|
45
49
|
'package.json',
|
|
46
50
|
)}.`,
|
|
47
51
|
});
|
|
52
|
+
const ERR_SWITCH_BACK = () => ({
|
|
53
|
+
message: 'Switch back to import declaration',
|
|
54
|
+
});
|
|
48
55
|
|
|
49
56
|
process.chdir(FIXTURE);
|
|
50
57
|
|
|
@@ -130,6 +137,7 @@ ruleTester.run(RULE, rule, {
|
|
|
130
137
|
},
|
|
131
138
|
{
|
|
132
139
|
code: `import 'lodash'`,
|
|
140
|
+
output: `import 'directive:add-import:dependencies:lodash'`,
|
|
133
141
|
filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'),
|
|
134
142
|
errors: [
|
|
135
143
|
ERR_UNDECLARED('lodash', 'dependencies', joinPath('packages', 'bar')),
|
|
@@ -137,6 +145,7 @@ ruleTester.run(RULE, rule, {
|
|
|
137
145
|
},
|
|
138
146
|
{
|
|
139
147
|
code: `import { debounce } from 'lodash'`,
|
|
148
|
+
output: `import { debounce } from 'directive:add-import:dependencies:lodash'`,
|
|
140
149
|
filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'),
|
|
141
150
|
errors: [
|
|
142
151
|
ERR_UNDECLARED('lodash', 'dependencies', joinPath('packages', 'bar')),
|
|
@@ -144,6 +153,7 @@ ruleTester.run(RULE, rule, {
|
|
|
144
153
|
},
|
|
145
154
|
{
|
|
146
155
|
code: `import * as _ from 'lodash'`,
|
|
156
|
+
output: `import * as _ from 'directive:add-import:dependencies:lodash'`,
|
|
147
157
|
filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'),
|
|
148
158
|
errors: [
|
|
149
159
|
ERR_UNDECLARED('lodash', 'dependencies', joinPath('packages', 'bar')),
|
|
@@ -151,6 +161,7 @@ ruleTester.run(RULE, rule, {
|
|
|
151
161
|
},
|
|
152
162
|
{
|
|
153
163
|
code: `import _ from 'lodash'`,
|
|
164
|
+
output: `import _ from 'directive:add-import:dependencies:lodash'`,
|
|
154
165
|
filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'),
|
|
155
166
|
errors: [
|
|
156
167
|
ERR_UNDECLARED('lodash', 'dependencies', joinPath('packages', 'bar')),
|
|
@@ -158,6 +169,7 @@ ruleTester.run(RULE, rule, {
|
|
|
158
169
|
},
|
|
159
170
|
{
|
|
160
171
|
code: `import('lodash')`,
|
|
172
|
+
output: `import('directive:add-import:dependencies:lodash')`,
|
|
161
173
|
filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'),
|
|
162
174
|
errors: [
|
|
163
175
|
ERR_UNDECLARED('lodash', 'dependencies', joinPath('packages', 'bar')),
|
|
@@ -165,6 +177,7 @@ ruleTester.run(RULE, rule, {
|
|
|
165
177
|
},
|
|
166
178
|
{
|
|
167
179
|
code: `require('lodash')`,
|
|
180
|
+
output: `require('directive:add-import:dependencies:lodash')`,
|
|
168
181
|
filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'),
|
|
169
182
|
errors: [
|
|
170
183
|
ERR_UNDECLARED('lodash', 'dependencies', joinPath('packages', 'bar')),
|
|
@@ -172,13 +185,7 @@ ruleTester.run(RULE, rule, {
|
|
|
172
185
|
},
|
|
173
186
|
{
|
|
174
187
|
code: `import 'lodash'`,
|
|
175
|
-
|
|
176
|
-
errors: [
|
|
177
|
-
ERR_UNDECLARED('lodash', 'dependencies', joinPath('packages', 'bar')),
|
|
178
|
-
],
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
code: `import 'lodash'`,
|
|
188
|
+
output: `import 'directive:add-import:devDependencies:lodash'`,
|
|
182
189
|
filename: joinPath(FIXTURE, 'packages/bar/src/index.test.ts'),
|
|
183
190
|
errors: [
|
|
184
191
|
ERR_UNDECLARED(
|
|
@@ -191,6 +198,7 @@ ruleTester.run(RULE, rule, {
|
|
|
191
198
|
},
|
|
192
199
|
{
|
|
193
200
|
code: `import 'react'`,
|
|
201
|
+
output: `import 'directive:add-import:peerDependencies:react'`,
|
|
194
202
|
filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'),
|
|
195
203
|
errors: [
|
|
196
204
|
ERR_UNDECLARED(
|
|
@@ -203,6 +211,7 @@ ruleTester.run(RULE, rule, {
|
|
|
203
211
|
},
|
|
204
212
|
{
|
|
205
213
|
code: `import 'react'`,
|
|
214
|
+
output: `import 'directive:add-import:peerDependencies:react'`,
|
|
206
215
|
filename: joinPath(FIXTURE, 'packages/bar/src/index.test.ts'),
|
|
207
216
|
errors: [
|
|
208
217
|
ERR_UNDECLARED(
|
|
@@ -215,6 +224,7 @@ ruleTester.run(RULE, rule, {
|
|
|
215
224
|
},
|
|
216
225
|
{
|
|
217
226
|
code: `import 'react-dom'`,
|
|
227
|
+
output: `import 'directive:add-import:dependencies:react-dom'`,
|
|
218
228
|
filename: joinPath(FIXTURE, 'packages/foo/src/index.ts'),
|
|
219
229
|
errors: [
|
|
220
230
|
ERR_UNDECLARED(
|
|
@@ -226,6 +236,7 @@ ruleTester.run(RULE, rule, {
|
|
|
226
236
|
},
|
|
227
237
|
{
|
|
228
238
|
code: `import 'react-dom'`,
|
|
239
|
+
output: `import 'directive:add-import:devDependencies:react-dom'`,
|
|
229
240
|
filename: joinPath(FIXTURE, 'packages/foo/src/index.test.ts'),
|
|
230
241
|
errors: [
|
|
231
242
|
ERR_UNDECLARED(
|
|
@@ -238,6 +249,7 @@ ruleTester.run(RULE, rule, {
|
|
|
238
249
|
},
|
|
239
250
|
{
|
|
240
251
|
code: `import '@internal/foo'`,
|
|
252
|
+
output: `import 'directive:add-import:dependencies:@internal/foo'`,
|
|
241
253
|
filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'),
|
|
242
254
|
errors: [
|
|
243
255
|
ERR_UNDECLARED(
|
|
@@ -247,5 +259,43 @@ ruleTester.run(RULE, rule, {
|
|
|
247
259
|
),
|
|
248
260
|
],
|
|
249
261
|
},
|
|
262
|
+
|
|
263
|
+
// Switching back to original import declarations
|
|
264
|
+
{
|
|
265
|
+
code: `import 'directive:add-import:dependencies:lodash'`,
|
|
266
|
+
output: `import 'lodash'`,
|
|
267
|
+
filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'),
|
|
268
|
+
errors: [ERR_SWITCH_BACK()],
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
code: `import { debounce } from 'directive:add-import:dependencies:lodash'`,
|
|
272
|
+
output: `import { debounce } from 'lodash'`,
|
|
273
|
+
filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'),
|
|
274
|
+
errors: [ERR_SWITCH_BACK()],
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
code: `import * as _ from 'directive:add-import:dependencies:lodash'`,
|
|
278
|
+
output: `import * as _ from 'lodash'`,
|
|
279
|
+
filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'),
|
|
280
|
+
errors: [ERR_SWITCH_BACK()],
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
code: `import _ from 'directive:add-import:dependencies:lodash'`,
|
|
284
|
+
output: `import _ from 'lodash'`,
|
|
285
|
+
filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'),
|
|
286
|
+
errors: [ERR_SWITCH_BACK()],
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
code: `import('directive:add-import:dependencies:lodash')`,
|
|
290
|
+
output: `import('lodash')`,
|
|
291
|
+
filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'),
|
|
292
|
+
errors: [ERR_SWITCH_BACK()],
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
code: `require('directive:add-import:dependencies:lodash')`,
|
|
296
|
+
output: `require('lodash')`,
|
|
297
|
+
filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'),
|
|
298
|
+
errors: [ERR_SWITCH_BACK()],
|
|
299
|
+
},
|
|
250
300
|
],
|
|
251
301
|
});
|