Skip to content

Commit 86a13a1

Browse files
[RUMF-848] add a ESLint rule to enforce declarative modules
1 parent a162dd3 commit 86a13a1

File tree

6 files changed

+121
-2
lines changed

6 files changed

+121
-2
lines changed

.eslintrc.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ module.exports = {
2121
'eslint-plugin-prefer-arrow',
2222
'eslint-plugin-unicorn',
2323
'@typescript-eslint',
24+
'eslint-plugin-local-rules',
2425
],
2526
rules: {
27+
'local-rules/enforce-declarative-modules': 'error',
2628
'@typescript-eslint/array-type': ['error', { default: 'array-simple' }],
2729
'@typescript-eslint/ban-ts-comment': [
2830
'error',

LICENSE-3rdparty.csv

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ dev,emoji-name-map,MIT,Copyright 2016-19 Ionică Bizău <bizauionica@gmail.com>
2929
dev,eslint,MIT,Copyright JS Foundation and other contributors
3030
dev,eslint-config-prettier,MIT,Copyright (c) 2017, 2018, 2019, 2020 Simon Lydell and contributors
3131
dev,eslint-plugin-import,MIT,Copyright (c) 2015 Ben Mosher
32-
dev,eslint-plugin-jsdoc,BSD-3-Clause,Copyright (c) 2018, Gajus Kuizinas (http://gajus.com/)
3332
dev,eslint-plugin-jasmine,MIT,Copyright (c) 2021 Tom Vincent
33+
dev,eslint-plugin-jsdoc,BSD-3-Clause,Copyright (c) 2018, Gajus Kuizinas (http://gajus.com/)
34+
dev,eslint-plugin-local-rules,MIT,Copyright (c) 2017 Clayton Watts
3435
dev,eslint-plugin-prefer-arrow,MIT,Copyright (c) 2018 Triston Jones
3536
dev,eslint-plugin-unicorn,MIT,Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
3637
dev,express,MIT,Copyright 2009-2014 TJ Holowaychuk 2013-2014 Roman Shtylman 2014-2015 Douglas Christopher Wilson

eslint-local-rules.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/* eslint-disable unicorn/filename-case */
2+
const path = require('path')
3+
4+
module.exports = {
5+
'enforce-declarative-modules': {
6+
meta: {
7+
docs: {
8+
description: 'Disallow potential side effects in types, constants and internal files',
9+
recommended: false,
10+
},
11+
schema: [],
12+
},
13+
create(context) {
14+
const filename = path.basename(context.getFilename())
15+
const dotIndex = filename.lastIndexOf('.')
16+
const filenameWithoutExtension = dotIndex < 0 ? filename : filename.slice(0, dotIndex)
17+
if (!isRestrictedFile(filenameWithoutExtension)) {
18+
return {}
19+
}
20+
return {
21+
Program(node) {
22+
node.body.forEach((child) => {
23+
reportRestrictedDeclarations(context, child)
24+
})
25+
},
26+
}
27+
},
28+
},
29+
}
30+
31+
function isRestrictedFile(filenameWithoutExtension) {
32+
return (
33+
filenameWithoutExtension === 'types' ||
34+
filenameWithoutExtension === 'internal' ||
35+
filenameWithoutExtension === 'constants' ||
36+
filenameWithoutExtension.endsWith('.constants') ||
37+
filenameWithoutExtension.endsWith('.types')
38+
)
39+
}
40+
41+
function reportRestrictedDeclarations(context, node) {
42+
switch (node.type) {
43+
case 'TemplateLiteral':
44+
node.expressions.forEach((child) => reportRestrictedDeclarations(context, child))
45+
break
46+
case 'ExportNamedDeclaration':
47+
case 'ExportAllDeclaration':
48+
case 'ImportDeclaration':
49+
if (node.declaration) {
50+
reportRestrictedDeclarations(context, node.declaration)
51+
} else if (node.source && !isRestrictedFile(path.basename(node.source.value))) {
52+
context.report({
53+
node: node.source,
54+
message: `This file can only import types, constants and internal files`,
55+
})
56+
}
57+
break
58+
case 'VariableDeclaration':
59+
node.declarations.forEach((child) => reportRestrictedDeclarations(context, child))
60+
break
61+
case 'VariableDeclarator':
62+
if (node.init) {
63+
reportRestrictedDeclarations(context, node.init)
64+
}
65+
break
66+
case 'ArrayExpression':
67+
node.elements.forEach((child) => reportRestrictedDeclarations(context, child))
68+
break
69+
case 'UnaryExpression':
70+
reportRestrictedDeclarations(context, node.argument)
71+
break
72+
case 'ObjectExpression':
73+
node.properties.forEach((child) => reportRestrictedDeclarations(context, child))
74+
break
75+
case 'SpreadElement':
76+
reportRestrictedDeclarations(context, node.argument)
77+
break
78+
case 'Property':
79+
reportRestrictedDeclarations(context, node.key)
80+
reportRestrictedDeclarations(context, node.value)
81+
break
82+
case 'AssignmentExpression':
83+
case 'BinaryExpression':
84+
reportRestrictedDeclarations(context, node.left)
85+
reportRestrictedDeclarations(context, node.right)
86+
break
87+
case 'TSAsExpression':
88+
case 'ExpressionStatement':
89+
reportRestrictedDeclarations(context, node.expression)
90+
break
91+
case 'MemberExpression':
92+
reportRestrictedDeclarations(context, node.object)
93+
reportRestrictedDeclarations(context, node.property)
94+
break
95+
case 'FunctionExpression':
96+
case 'ArrowFunctionExpression':
97+
case 'FunctionDeclaration':
98+
case 'ClassDeclaration':
99+
case 'TSEnumDeclaration':
100+
case 'TSInterfaceDeclaration':
101+
case 'TSTypeAliasDeclaration':
102+
case 'TSDeclareFunction':
103+
case 'Literal':
104+
case 'Identifier':
105+
break
106+
default:
107+
context.report({ node, message: `${node.type} not allowed in types, constants and internal files` })
108+
break
109+
}
110+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"eslint-plugin-import": "2.22.1",
5454
"eslint-plugin-jasmine": "4.1.2",
5555
"eslint-plugin-jsdoc": "30.7.13",
56+
"eslint-plugin-local-rules": "1.0.1",
5657
"eslint-plugin-prefer-arrow": "1.2.2",
5758
"eslint-plugin-unicorn": "25.0.1",
5859
"express": "4.17.1",

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
"compilerOptions": {
55
"types": ["jasmine"]
66
},
7-
"include": ["packages", "scripts", "test", ".eslintrc.js"],
7+
"include": ["packages", "scripts", "test", ".eslintrc.js", "eslint-local-rules.js"],
88
"exclude": ["packages/*/cjs", "packages/*/esm", "packages/*/bundle", "test/e2e", "test/app"]
99
}

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4268,6 +4268,11 @@ eslint-plugin-jsdoc@30.7.13:
42684268
semver "^7.3.4"
42694269
spdx-expression-parse "^3.0.1"
42704270

4271+
eslint-plugin-local-rules@1.0.1:
4272+
version "1.0.1"
4273+
resolved "https://registry.yarnpkg.com/eslint-plugin-local-rules/-/eslint-plugin-local-rules-1.0.1.tgz#1a1f2659ad9297ea82a469d1f5b92b26d90ea3b0"
4274+
integrity sha512-spxkMBos6/ekNxO7khP2zj0j+tdNIhc3GWjXX397L/5R4ZM5huAIks6ADlZVRyiyUKbo/ABJgH/iP6cu9YYnyg==
4275+
42714276
eslint-plugin-prefer-arrow@1.2.2:
42724277
version "1.2.2"
42734278
resolved "https://registry.yarnpkg.com/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.2.2.tgz#0c6d25a6b94cb3e0110a23d129760af5860edb6e"

0 commit comments

Comments
 (0)