@backstage/eslint-plugin 0.1.8 → 0.1.9
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 +1 -1
- package/package.json +2 -2
- package/rules/no-undeclared-imports.js +244 -56
- package/src/__fixtures__/monorepo/packages/bar/package.json +4 -3
- package/src/__fixtures__/monorepo/packages/inline/package.json +16 -0
- package/src/__fixtures__/monorepo/packages/inline-dep-invalid-direct/package.json +12 -0
- package/src/__fixtures__/monorepo/packages/inline-dep-invalid-missing/package.json +7 -0
- package/src/__fixtures__/monorepo/packages/inline-dep-valid/package.json +10 -0
- package/src/no-undeclared-imports.test.ts +49 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @backstage/eslint-plugin
|
|
2
2
|
|
|
3
|
+
## 0.1.9
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 08895e3: Added support for linting dependencies on workspace packages with the `backstage.inline` flag.
|
|
8
|
+
|
|
9
|
+
## 0.1.9-next.0
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 08895e3: Added support for linting dependencies on workspace packages with the `backstage.inline` flag.
|
|
14
|
+
|
|
3
15
|
## 0.1.8
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
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
|
|
24
|
+
* @type {import('@manypkg/get-packages').Package & { packageJson: { exports?: Record<string, string>, files?: Array<string>, backstage?: { inline?: boolean } }}} 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.9",
|
|
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.27.1",
|
|
26
26
|
"@types/estree": "^1.0.5",
|
|
27
27
|
"eslint": "^8.33.0"
|
|
28
28
|
}
|
|
@@ -22,11 +22,11 @@ const visitImports = require('../lib/visitImports');
|
|
|
22
22
|
const minimatch = require('minimatch');
|
|
23
23
|
const { execFileSync } = require('child_process');
|
|
24
24
|
|
|
25
|
-
const depFields = {
|
|
25
|
+
const depFields = /** @type {const} */ ({
|
|
26
26
|
dep: 'dependencies',
|
|
27
27
|
dev: 'devDependencies',
|
|
28
28
|
peer: 'peerDependencies',
|
|
29
|
-
};
|
|
29
|
+
});
|
|
30
30
|
|
|
31
31
|
const devModulePatterns = [
|
|
32
32
|
new minimatch.Minimatch('!src/**'),
|
|
@@ -154,6 +154,124 @@ function addVersionQuery(name, flag, packages) {
|
|
|
154
154
|
return `${name}@${mostCommonRange}`;
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Add missing package imports
|
|
159
|
+
* @param {Array<{name: string, flag: string, node: import('estree').Node}>} toAdd
|
|
160
|
+
* @param {import('../lib/getPackages').PackageMap} packages
|
|
161
|
+
* @param {import('../lib/getPackages').ExtendedPackage} localPkg
|
|
162
|
+
*/
|
|
163
|
+
function addMissingImports(toAdd, packages, localPkg) {
|
|
164
|
+
/** @type Record<string, Set<string>> */
|
|
165
|
+
const byFlag = {};
|
|
166
|
+
|
|
167
|
+
for (const { name, flag } of toAdd) {
|
|
168
|
+
byFlag[flag] = byFlag[flag] ?? new Set();
|
|
169
|
+
byFlag[flag].add(name);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
for (const name of byFlag[''] ?? []) {
|
|
173
|
+
byFlag['--dev']?.delete(name);
|
|
174
|
+
}
|
|
175
|
+
for (const name of byFlag['--peer'] ?? []) {
|
|
176
|
+
byFlag['']?.delete(name);
|
|
177
|
+
byFlag['--dev']?.delete(name);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
for (const [flag, names] of Object.entries(byFlag)) {
|
|
181
|
+
// Look up existing version queries in the repo for the same dependency
|
|
182
|
+
const namesWithQuery = [...names].map(name =>
|
|
183
|
+
addVersionQuery(name, flag, packages),
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
// The security implication of this is a bit interesting, as crafted add-import
|
|
187
|
+
// directives could be used to install malicious packages. However, the same is true
|
|
188
|
+
// for adding malicious packages to package.json, so there's no significant difference.
|
|
189
|
+
execFileSync('yarn', ['add', ...(flag ? [flag] : []), ...namesWithQuery], {
|
|
190
|
+
cwd: localPkg.dir,
|
|
191
|
+
stdio: 'inherit',
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Removes dependency entries pointing to inlined workspace packages.
|
|
198
|
+
* @param {Array<{pkg: import('../lib/getPackages').ExtendedPackage, node: import('estree').Node}>} toInline
|
|
199
|
+
* @param {import('../lib/getPackages').ExtendedPackage} localPkg
|
|
200
|
+
*/
|
|
201
|
+
function removeInlineImports(toInline, localPkg) {
|
|
202
|
+
/** @type Set<string> */
|
|
203
|
+
const toRemove = new Set();
|
|
204
|
+
|
|
205
|
+
for (const { pkg } of toInline) {
|
|
206
|
+
const name = pkg.packageJson.name;
|
|
207
|
+
for (const depType of Object.values(depFields)) {
|
|
208
|
+
if (localPkg.packageJson[depType]?.[name]) {
|
|
209
|
+
toRemove.add(name);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (toRemove.size > 0) {
|
|
214
|
+
execFileSync('yarn', ['remove', ...toRemove], {
|
|
215
|
+
cwd: localPkg.dir,
|
|
216
|
+
stdio: 'inherit',
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Adds dependencies that are not properly forwarded from inline dependencies.
|
|
223
|
+
* @param {Array<{pkg: import('../lib/getPackages').ExtendedPackage, node: import('estree').Node}>} toInline
|
|
224
|
+
* @param {import('../lib/getPackages').ExtendedPackage} localPkg
|
|
225
|
+
*/
|
|
226
|
+
function addForwardedInlineImports(toInline, localPkg) {
|
|
227
|
+
const declaredProdDeps = new Set([
|
|
228
|
+
...Object.keys(localPkg.packageJson.dependencies ?? {}),
|
|
229
|
+
...Object.keys(localPkg.packageJson.peerDependencies ?? {}),
|
|
230
|
+
localPkg.packageJson.name, // include self
|
|
231
|
+
]);
|
|
232
|
+
|
|
233
|
+
/** @type Map<string, Map<string, string>> */
|
|
234
|
+
const byFlagByName = new Map();
|
|
235
|
+
|
|
236
|
+
for (const { pkg } of toInline) {
|
|
237
|
+
for (const depType of /** @type {const} */ ([
|
|
238
|
+
'dependencies',
|
|
239
|
+
'peerDependencies',
|
|
240
|
+
])) {
|
|
241
|
+
for (const [depName, depQuery] of Object.entries(
|
|
242
|
+
pkg.packageJson[depType] ?? {},
|
|
243
|
+
)) {
|
|
244
|
+
if (!declaredProdDeps.has(depName)) {
|
|
245
|
+
const flag = getAddFlagForDepsField(depType);
|
|
246
|
+
const byName = byFlagByName.get(flag);
|
|
247
|
+
if (byName) {
|
|
248
|
+
const query = byName.get(depName);
|
|
249
|
+
if (query && query !== depQuery) {
|
|
250
|
+
throw new Error(
|
|
251
|
+
`Conflicting dependency queries for inlined package dep ${depName}, got ${query} and ${depQuery}`,
|
|
252
|
+
);
|
|
253
|
+
} else {
|
|
254
|
+
byName.set(depName, depQuery);
|
|
255
|
+
}
|
|
256
|
+
} else {
|
|
257
|
+
byFlagByName.set(flag, new Map([[depName, depQuery]]));
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
for (const [flag, byName] of byFlagByName) {
|
|
265
|
+
const namesWithQuery = [...byName.entries()].map(
|
|
266
|
+
([name, query]) => `${name}@${query}`,
|
|
267
|
+
);
|
|
268
|
+
execFileSync('yarn', ['add', ...(flag ? [flag] : []), ...namesWithQuery], {
|
|
269
|
+
cwd: localPkg.dir,
|
|
270
|
+
stdio: 'inherit',
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
157
275
|
/** @type {import('eslint').Rule.RuleModule} */
|
|
158
276
|
module.exports = {
|
|
159
277
|
meta: {
|
|
@@ -165,6 +283,8 @@ module.exports = {
|
|
|
165
283
|
switch:
|
|
166
284
|
'{{ packageName }} is declared in {{ oldDepsField }}, but should be moved to {{ depsField }} in {{ packageJsonPath }}.',
|
|
167
285
|
switchBack: 'Switch back to import declaration',
|
|
286
|
+
inlineDirect: `The dependency on the inline package {{ packageName }} must not be declared in package dependencies.`,
|
|
287
|
+
inlineMissing: `Each production dependency from the inline package {{ packageName }} must be re-declared by this package, the following dependencies are missing: {{ missingDeps }}`,
|
|
168
288
|
},
|
|
169
289
|
docs: {
|
|
170
290
|
description:
|
|
@@ -189,57 +309,48 @@ module.exports = {
|
|
|
189
309
|
/** @type Array<{name: string, flag: string, node: import('estree').Node}> */
|
|
190
310
|
const importsToAdd = [];
|
|
191
311
|
|
|
312
|
+
/** @type Array<{pkg: import('../lib/getPackages').ExtendedPackage, node: import('estree').Node}> */
|
|
313
|
+
const importsToInline = [];
|
|
314
|
+
|
|
192
315
|
return {
|
|
193
316
|
// All missing imports that we detect are collected as we traverse, and then we use
|
|
194
317
|
// the program exit to execute all install directives that have been found.
|
|
195
318
|
['Program:exit']() {
|
|
196
|
-
|
|
197
|
-
|
|
319
|
+
if (importsToAdd.length > 0) {
|
|
320
|
+
addMissingImports(importsToAdd, packages, localPkg);
|
|
198
321
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
322
|
+
// This switches all import directives back to the original import.
|
|
323
|
+
for (const added of importsToAdd) {
|
|
324
|
+
context.report({
|
|
325
|
+
node: added.node,
|
|
326
|
+
messageId: 'switchBack',
|
|
327
|
+
fix(fixer) {
|
|
328
|
+
return fixer.replaceText(added.node, `'${added.name}'`);
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
importsToAdd.length = 0;
|
|
210
333
|
}
|
|
211
334
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
addVersionQuery(name, flag, packages),
|
|
216
|
-
);
|
|
217
|
-
|
|
218
|
-
// The security implication of this is a bit interesting, as crafted add-import
|
|
219
|
-
// directives could be used to install malicious packages. However, the same is true
|
|
220
|
-
// for adding malicious packages to package.json, so there's no significant difference.
|
|
221
|
-
execFileSync(
|
|
222
|
-
'yarn',
|
|
223
|
-
['add', ...(flag ? [flag] : []), ...namesWithQuery],
|
|
224
|
-
{
|
|
225
|
-
cwd: localPkg.dir,
|
|
226
|
-
stdio: 'inherit',
|
|
227
|
-
},
|
|
228
|
-
);
|
|
229
|
-
}
|
|
335
|
+
if (importsToInline.length > 0) {
|
|
336
|
+
removeInlineImports(importsToInline, localPkg);
|
|
337
|
+
addForwardedInlineImports(importsToInline, localPkg);
|
|
230
338
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
339
|
+
for (const inlined of importsToInline) {
|
|
340
|
+
context.report({
|
|
341
|
+
node: inlined.node,
|
|
342
|
+
messageId: 'switchBack',
|
|
343
|
+
fix(fixer) {
|
|
344
|
+
return fixer.replaceText(
|
|
345
|
+
inlined.node,
|
|
346
|
+
`'${inlined.pkg.packageJson.name}'`,
|
|
347
|
+
);
|
|
348
|
+
},
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
importsToInline.length = 0;
|
|
240
352
|
}
|
|
241
353
|
|
|
242
|
-
importsToAdd.length = 0;
|
|
243
354
|
packages.clearCache();
|
|
244
355
|
},
|
|
245
356
|
...visitImports(context, (node, imp) => {
|
|
@@ -255,22 +366,99 @@ module.exports = {
|
|
|
255
366
|
|
|
256
367
|
// Any import directive that is found is collected for processing later
|
|
257
368
|
if (imp.type === 'directive') {
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
369
|
+
const [, directive, ...args] = imp.path.split(':');
|
|
370
|
+
|
|
371
|
+
if (directive === 'add-import') {
|
|
372
|
+
const [type, name] = args;
|
|
373
|
+
if (!name.match(/^(@[-\w\.~]+\/)?[-\w\.~]*$/i)) {
|
|
374
|
+
throw new Error(
|
|
375
|
+
`Invalid package name to add as dependency: '${name}'`,
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
importsToAdd.push({
|
|
380
|
+
flag: getAddFlagForDepsField(type).trim(),
|
|
381
|
+
name,
|
|
382
|
+
node: imp.node,
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (directive === 'inline-imports') {
|
|
387
|
+
const [name] = args;
|
|
388
|
+
const pkg = packages.map.get(name);
|
|
389
|
+
if (!pkg) {
|
|
390
|
+
throw new Error(`Unexpectedly missing inline package: ${name}`);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
importsToInline.push({
|
|
394
|
+
pkg: pkg,
|
|
395
|
+
node: imp.node,
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Importing an internal inlined package, whose imports are inlined too
|
|
403
|
+
if (
|
|
404
|
+
imp.type === 'internal' &&
|
|
405
|
+
imp.package.packageJson.backstage?.inline
|
|
406
|
+
) {
|
|
407
|
+
for (const depType of Object.values(depFields)) {
|
|
408
|
+
if (localPkg.packageJson[depType]?.[imp.packageName]) {
|
|
409
|
+
context.report({
|
|
410
|
+
node,
|
|
411
|
+
messageId: 'inlineDirect',
|
|
412
|
+
data: {
|
|
413
|
+
packageName: imp.packageName,
|
|
414
|
+
},
|
|
415
|
+
fix: fixer => {
|
|
416
|
+
return fixer.replaceText(
|
|
417
|
+
imp.node,
|
|
418
|
+
`'directive:inline-imports:${imp.packageName}'`,
|
|
419
|
+
);
|
|
420
|
+
},
|
|
421
|
+
});
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
261
424
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
)
|
|
425
|
+
|
|
426
|
+
const missingDeps = [];
|
|
427
|
+
const declaredProdDeps = new Set([
|
|
428
|
+
...Object.keys(localPkg.packageJson.dependencies ?? {}),
|
|
429
|
+
...Object.keys(localPkg.packageJson.peerDependencies ?? {}),
|
|
430
|
+
localPkg.packageJson.name, // include self
|
|
431
|
+
]);
|
|
432
|
+
for (const depType of /** @type {const} */ ([
|
|
433
|
+
'dependencies',
|
|
434
|
+
'peerDependencies',
|
|
435
|
+
])) {
|
|
436
|
+
for (const depName of Object.keys(
|
|
437
|
+
imp.package.packageJson[depType] ?? {},
|
|
438
|
+
)) {
|
|
439
|
+
if (!declaredProdDeps.has(depName)) {
|
|
440
|
+
missingDeps.push(depName);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (missingDeps.length > 0) {
|
|
446
|
+
context.report({
|
|
447
|
+
node,
|
|
448
|
+
messageId: 'inlineMissing',
|
|
449
|
+
data: {
|
|
450
|
+
packageName: imp.packageName,
|
|
451
|
+
missingDeps: missingDeps.join(', '),
|
|
452
|
+
},
|
|
453
|
+
fix: fixer => {
|
|
454
|
+
return fixer.replaceText(
|
|
455
|
+
imp.node,
|
|
456
|
+
`'directive:inline-imports:${imp.packageName}'`,
|
|
457
|
+
);
|
|
458
|
+
},
|
|
459
|
+
});
|
|
267
460
|
}
|
|
268
461
|
|
|
269
|
-
importsToAdd.push({
|
|
270
|
-
flag: getAddFlagForDepsField(type).trim(),
|
|
271
|
-
name,
|
|
272
|
-
node: imp.node,
|
|
273
|
-
});
|
|
274
462
|
return;
|
|
275
463
|
}
|
|
276
464
|
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@internal/bar",
|
|
3
|
+
"backstage": {
|
|
4
|
+
"role": "frontend-plugin"
|
|
5
|
+
},
|
|
3
6
|
"exports": {
|
|
4
7
|
".": "./src/index.ts",
|
|
5
8
|
"./BarPage": "./src/components/Bar.tsx",
|
|
6
9
|
"./package.json": "./package.json"
|
|
7
10
|
},
|
|
8
|
-
"backstage": {
|
|
9
|
-
"role": "frontend-plugin"
|
|
10
|
-
},
|
|
11
11
|
"dependencies": {
|
|
12
|
+
"inline-dep": "*",
|
|
12
13
|
"react-router": "*"
|
|
13
14
|
},
|
|
14
15
|
"devDependencies": {
|
|
@@ -52,6 +52,12 @@ const ERR_SWITCHED = (
|
|
|
52
52
|
const ERR_SWITCH_BACK = () => ({
|
|
53
53
|
message: 'Switch back to import declaration',
|
|
54
54
|
});
|
|
55
|
+
const ERR_INLINE_DIRECT = (name: string) => ({
|
|
56
|
+
message: `The dependency on the inline package ${name} must not be declared in package dependencies.`,
|
|
57
|
+
});
|
|
58
|
+
const ERR_INLINE_MISSING = (name: string, missing: string) => ({
|
|
59
|
+
message: `Each production dependency from the inline package ${name} must be re-declared by this package, the following dependencies are missing: ${missing}`,
|
|
60
|
+
});
|
|
55
61
|
|
|
56
62
|
// cwd must be restored
|
|
57
63
|
const origDir = process.cwd();
|
|
@@ -102,6 +108,17 @@ ruleTester.run(RULE, rule, {
|
|
|
102
108
|
code: `require('lod' + 'ash')`,
|
|
103
109
|
filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'),
|
|
104
110
|
},
|
|
111
|
+
{
|
|
112
|
+
code: `import '@internal/inline'`,
|
|
113
|
+
filename: joinPath(FIXTURE, 'packages/inline-dep-valid/src/index.ts'),
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
code: `import '@internal/inline'`,
|
|
117
|
+
filename: joinPath(
|
|
118
|
+
FIXTURE,
|
|
119
|
+
'packages/inline-dep-valid/src/index.test.ts',
|
|
120
|
+
),
|
|
121
|
+
},
|
|
105
122
|
],
|
|
106
123
|
invalid: [
|
|
107
124
|
{
|
|
@@ -264,6 +281,29 @@ ruleTester.run(RULE, rule, {
|
|
|
264
281
|
),
|
|
265
282
|
],
|
|
266
283
|
},
|
|
284
|
+
{
|
|
285
|
+
code: `import '@internal/inline'`,
|
|
286
|
+
output: `import 'directive:inline-imports:@internal/inline'`,
|
|
287
|
+
filename: joinPath(
|
|
288
|
+
FIXTURE,
|
|
289
|
+
'packages/inline-dep-invalid-direct/src/index.ts',
|
|
290
|
+
),
|
|
291
|
+
errors: [ERR_INLINE_DIRECT('@internal/inline')],
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
code: `import '@internal/inline'`,
|
|
295
|
+
output: `import 'directive:inline-imports:@internal/inline'`,
|
|
296
|
+
filename: joinPath(
|
|
297
|
+
FIXTURE,
|
|
298
|
+
'packages/inline-dep-invalid-missing/src/index.ts',
|
|
299
|
+
),
|
|
300
|
+
errors: [
|
|
301
|
+
ERR_INLINE_MISSING(
|
|
302
|
+
'@internal/inline',
|
|
303
|
+
'@internal/inline-dep-valid, react',
|
|
304
|
+
),
|
|
305
|
+
],
|
|
306
|
+
},
|
|
267
307
|
|
|
268
308
|
// Switching back to original import declarations
|
|
269
309
|
{
|
|
@@ -302,5 +342,14 @@ ruleTester.run(RULE, rule, {
|
|
|
302
342
|
filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'),
|
|
303
343
|
errors: [ERR_SWITCH_BACK()],
|
|
304
344
|
},
|
|
345
|
+
{
|
|
346
|
+
code: `import 'directive:inline-imports:@internal/inline'`,
|
|
347
|
+
output: `import '@internal/inline'`,
|
|
348
|
+
filename: joinPath(
|
|
349
|
+
FIXTURE,
|
|
350
|
+
'packages/inline-dep-invalid-direct/src/index.ts',
|
|
351
|
+
),
|
|
352
|
+
errors: [ERR_SWITCH_BACK()],
|
|
353
|
+
},
|
|
305
354
|
],
|
|
306
355
|
});
|