Skip to content

Commit 9811c58

Browse files
feat(eslint-plugin): add rule for deep imports across modules
1 parent 6b301e8 commit 9811c58

File tree

2 files changed

+89
-0
lines changed

2 files changed

+89
-0
lines changed

projects/eslint-plugin/rules/portal/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module.exports = {
77
'portal/deprecation': require('./lib/rules/deprecation'),
88
'portal/empty-line-after-copyright': require('./lib/rules/empty-line-after-copyright'),
99
'portal/no-api-submodule-import': require('./lib/rules/no-api-submodule-import'),
10+
'portal/no-cross-module-deep-import': require('./lib/rules/no-cross-module-deep-import'),
1011
'portal/no-default-export-from-frontend-js-web': require('./lib/rules/no-default-export-from-frontend-js-web'),
1112
'portal/no-document-cookie': require('./lib/rules/no-document-cookie'),
1213
'portal/no-explicit-extend': require('./lib/rules/no-explicit-extend'),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
* SPDX-FileCopyrightText: © 2017 Liferay, Inc. <https://liferay.com>
3+
* SPDX-License-Identifier: MIT
4+
*/
5+
6+
const path = require('path');
7+
8+
function parseImportPath(importPath) {
9+
const parts = importPath.split('/');
10+
11+
let moduleName;
12+
let subpath;
13+
14+
if (importPath.startsWith('@')) {
15+
moduleName = parts.slice(0, 2).join('/');
16+
subpath = parts.slice(2).join('/');
17+
}
18+
else {
19+
moduleName = parts[0];
20+
subpath = parts.slice(1).join('/');
21+
}
22+
23+
return {moduleName, subpath};
24+
}
25+
26+
function checkImportPath(node, nodeScriptsConfig, context) {
27+
if (
28+
node.source &&
29+
node.source.type === 'Literal' &&
30+
!node.source.value.startsWith('.') &&
31+
!node.source.value.startsWith('/')
32+
) {
33+
const importPath = node.source.value;
34+
35+
const {moduleName, subpath} = parseImportPath(importPath);
36+
37+
if (subpath) {
38+
const submodules = nodeScriptsConfig.imports[moduleName];
39+
40+
if (submodules) {
41+
const match = submodules.some(
42+
(submodule) =>
43+
path.join(moduleName, submodule) === importPath
44+
);
45+
46+
if (!match) {
47+
context.report({
48+
message: `'${moduleName}' does not export submodule: '${subpath}'. Do not import files deeply from another module.`,
49+
node,
50+
});
51+
}
52+
}
53+
}
54+
}
55+
}
56+
57+
module.exports = {
58+
create(context) {
59+
const nodeScriptsConfig = context.options[0]?.nodeScriptsConfig;
60+
61+
if (!nodeScriptsConfig) {
62+
return {};
63+
}
64+
65+
return {
66+
ExportDefaultDeclaration(node) {
67+
checkImportPath(node, nodeScriptsConfig, context);
68+
},
69+
ExportNamedDeclaration(node) {
70+
checkImportPath(node, nodeScriptsConfig, context);
71+
},
72+
ImportDeclaration(node) {
73+
checkImportPath(node, nodeScriptsConfig, context);
74+
},
75+
};
76+
},
77+
78+
meta: {
79+
docs: {
80+
category: 'Best Practices',
81+
description: 'you cannot import files deeply from another module',
82+
recommended: false,
83+
},
84+
fixable: null,
85+
schema: [{type: 'object'}],
86+
type: 'problem',
87+
},
88+
};

0 commit comments

Comments
 (0)