UNPKG

2.83 kBJavaScriptView Raw
1const {basename, extname, relative} = require('path');
2
3const {pascalCase} = require('change-case');
4const resolve = require('eslint-module-utils/resolve').default;
5
6const {docsUrl} = require('../utilities');
7
8module.exports = {
9 meta: {
10 docs: {
11 description: 'Prevent module imports between components.',
12 category: 'Best Practises',
13 recommended: false,
14 uri: docsUrl('strict-component-boundaries'),
15 },
16 fixable: null,
17 },
18 create(context) {
19 function report(node) {
20 context.report({
21 node,
22 message: `Do not reach into an individual component's folder for nested modules. Import from the closest shared components folder instead.`,
23 });
24 }
25
26 return {
27 ImportDeclaration(node) {
28 const importSource = node.source.value;
29 const resolvedSource = resolve(importSource, context);
30
31 if (
32 isCoreModule(resolvedSource) ||
33 isNotFound(resolvedSource) ||
34 inNodeModules(pathSegmantsFromSource(resolvedSource))
35 ) {
36 return;
37 }
38
39 const pathDifference = relative(context.getFilename(), resolvedSource);
40 const pathDifferenceParts = pathSegmantsFromSource(pathDifference);
41
42 if (
43 hasAnotherComponentInPath(pathDifferenceParts) &&
44 pathDifferenceParts.length > 1 &&
45 !indexFile(pathDifference) &&
46 !validFixtureImport(pathDifferenceParts)
47 ) {
48 report(node);
49 return;
50 }
51
52 if (
53 hasDirectoryInPath(pathDifferenceParts, 'components') &&
54 pathDifferenceParts.length > 2 &&
55 !validFixtureImport(pathDifferenceParts)
56 ) {
57 report(node);
58 }
59 },
60 };
61 },
62};
63
64function isNotFound(resolvedSource) {
65 return resolvedSource === undefined;
66}
67
68function isCoreModule(resolvedSource) {
69 return resolvedSource === null;
70}
71
72function inNodeModules(pathParts) {
73 return Boolean(pathParts.filter((part) => part === 'node_modules').length);
74}
75
76function hasDirectoryInPath(pathParts, directory) {
77 return Boolean(pathParts.filter((part) => part === directory).length);
78}
79
80function validFixtureImport(pathParts) {
81 if (!hasDirectoryInPath(pathParts, 'fixtures')) {
82 return false;
83 }
84
85 const fixtureIndexInPath = pathParts.findIndex((part) => part === 'fixtures');
86 const pathPartsBeforeFixture = pathParts.slice(0, fixtureIndexInPath);
87
88 if (!hasAnotherComponentInPath(pathPartsBeforeFixture)) {
89 return true;
90 }
91
92 return false;
93}
94
95function hasAnotherComponentInPath(pathParts) {
96 return Boolean(pathParts.filter((part) => part === pascalCase(part)).length);
97}
98
99function pathSegmantsFromSource(source) {
100 return source.split('/').filter((part) => part[0] !== '.');
101}
102
103function indexFile(src) {
104 return basename(src, extname(src)) === 'index';
105}