Skip to content

Commit 49584c9

Browse files
committed
feat: improve forSubpaths option (add fromInside and fromOuside sub-options)
1 parent cd90dcf commit 49584c9

File tree

2 files changed

+151
-17
lines changed

2 files changed

+151
-17
lines changed

src/rules/prefer-alias.js

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,22 @@ const getSiblingsMaxNestingLevel = options => {
4949
return -1
5050
}
5151

52+
const optionForSubpathsMatches = (options, currentFileIsInsideAlias) => {
53+
if (options.forSubpaths === true) {
54+
return true
55+
}
56+
if (options.forSubpaths) {
57+
if (currentFileIsInsideAlias && options.forSubpaths.fromInside) {
58+
return true
59+
}
60+
if (!currentFileIsInsideAlias && options.forSubpaths.fromOutside) {
61+
return true
62+
}
63+
}
64+
65+
return false
66+
}
67+
5268
export default {
5369
create: context => {
5470
const currentFile = context.getFilename()
@@ -107,12 +123,17 @@ export default {
107123
matchingAlias &&
108124
P.relative(matchingAlias.path, currentFile).split(P.sep).length - 1
109125

126+
const currentFileIsInsideAlias =
127+
matchingAlias &&
128+
!(P.relative(matchingAlias.path, currentFile) |> startsWith('..'))
129+
110130
const shouldAlias =
111131
!hasAlias &&
112132
((importWithoutAlias |> isParentImport) ||
113133
((importWithoutAlias |> isSiblingImport) &&
114134
currentFileNestingLevel <= siblingsMaxNestingLevel) ||
115-
((importWithoutAlias |> isSubpathImport) && options.forSubpaths))
135+
((importWithoutAlias |> isSubpathImport) &&
136+
optionForSubpathsMatches(options, currentFileIsInsideAlias)))
116137
if (shouldAlias) {
117138
if (!matchingAlias) {
118139
return undefined
@@ -145,7 +166,8 @@ export default {
145166
!isDirectAlias &&
146167
(((importWithoutAlias |> isSiblingImport) &&
147168
currentFileNestingLevel > siblingsMaxNestingLevel) ||
148-
((importWithoutAlias |> isSubpathImport) && !options.forSubpaths))
169+
((importWithoutAlias |> isSubpathImport) &&
170+
!optionForSubpathsMatches(options, currentFileIsInsideAlias)))
149171
if (shouldUnalias) {
150172
return context.report({
151173
fix: fixer =>
@@ -190,8 +212,26 @@ export default {
190212
],
191213
},
192214
forSubpaths: {
193-
default: false,
194-
type: 'boolean',
215+
anyOf: [
216+
{
217+
default: false,
218+
type: 'boolean',
219+
},
220+
{
221+
additionalProperties: false,
222+
properties: {
223+
fromInside: {
224+
default: false,
225+
type: 'boolean',
226+
},
227+
fromOutside: {
228+
default: false,
229+
type: 'boolean',
230+
},
231+
},
232+
type: 'object',
233+
},
234+
],
195235
},
196236
},
197237
type: 'object',

src/rules/prefer-alias.spec.js

Lines changed: 107 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -239,31 +239,125 @@ export default tester(
239239
plugins: [
240240
[
241241
packageName`babel-plugin-module-resolver`,
242-
{ alias: { '@': '.' } },
242+
{ alias: { '@components': './components' } },
243243
],
244244
],
245245
}),
246-
'sub/foo.js': '',
246+
})
247+
248+
const eslintConfig = {
249+
rules: {
250+
'self/self': [
251+
'error',
252+
{
253+
forSubpaths: true,
254+
},
255+
],
256+
},
257+
}
258+
expect(
259+
lint("import foo from './components/foo'", {
260+
eslintConfig,
261+
}),
262+
).toEqual({
263+
messages: [
264+
"Unexpected subpath import './components/foo'. Use '@components/foo' instead",
265+
],
266+
output: "import foo from '@components/foo'",
247267
})
248268
expect(
249269
lint("import foo from './sub/foo'", {
250-
eslintConfig: {
251-
rules: {
252-
'self/self': [
253-
'error',
254-
{
255-
forSubpaths: true,
256-
},
257-
],
270+
eslintConfig,
271+
filename: './components/bar.js',
272+
}),
273+
).toEqual({
274+
messages: [
275+
"Unexpected subpath import './sub/foo'. Use '@components/sub/foo' instead",
276+
],
277+
output: "import foo from '@components/sub/foo'",
278+
})
279+
},
280+
'alias for subpaths from inside': async () => {
281+
await outputFiles({
282+
'.babelrc.json': JSON.stringify({
283+
plugins: [
284+
[
285+
packageName`babel-plugin-module-resolver`,
286+
{ alias: { '@components': './components' } },
287+
],
288+
],
289+
}),
290+
})
291+
292+
const eslintConfig = {
293+
rules: {
294+
'self/self': [
295+
'error',
296+
{
297+
forSubpaths: {
298+
fromInside: true,
299+
},
258300
},
259-
},
301+
],
302+
},
303+
}
304+
expect(
305+
lint("import foo from './sub/foo'", {
306+
eslintConfig,
307+
filename: './components/bar.js',
260308
}),
261309
).toEqual({
262310
messages: [
263-
"Unexpected subpath import './sub/foo'. Use '@/sub/foo' instead",
311+
"Unexpected subpath import './sub/foo'. Use '@components/sub/foo' instead",
264312
],
265-
output: "import foo from '@/sub/foo'",
313+
output: "import foo from '@components/sub/foo'",
266314
})
315+
expect(
316+
lint("import foo from './components/foo'", {
317+
eslintConfig,
318+
}).messages,
319+
).toEqual([])
320+
},
321+
'alias for subpaths from outside': async () => {
322+
await outputFiles({
323+
'.babelrc.json': JSON.stringify({
324+
plugins: [
325+
[
326+
packageName`babel-plugin-module-resolver`,
327+
{ alias: { '@components': './components' } },
328+
],
329+
],
330+
}),
331+
})
332+
333+
const eslintConfig = {
334+
rules: {
335+
'self/self': [
336+
'error',
337+
{
338+
forSubpaths: {
339+
fromOutside: true,
340+
},
341+
},
342+
],
343+
},
344+
}
345+
expect(
346+
lint("import foo from './components/foo'", {
347+
eslintConfig,
348+
}),
349+
).toEqual({
350+
messages: [
351+
"Unexpected subpath import './components/foo'. Use '@components/foo' instead",
352+
],
353+
output: "import foo from '@components/foo'",
354+
})
355+
expect(
356+
lint("import foo from './sub/foo'", {
357+
eslintConfig,
358+
filename: './components/bar.js',
359+
}).messages,
360+
).toEqual([])
267361
},
268362
'alias parent': async () => {
269363
await outputFiles({

0 commit comments

Comments
 (0)