-
Notifications
You must be signed in to change notification settings - Fork 181
/
Copy pathduplicated-imports.js
251 lines (211 loc) · 7.1 KB
/
duplicated-imports.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
const path = require('path')
const BaseChecker = require('../base-checker')
const { severityDescription } = require('../../doc/utils')
const DEFAULT_SEVERITY = 'warn'
const ruleId = 'duplicated-imports'
const meta = {
type: 'miscellaneous',
docs: {
description: `Check if an import is done twice in the same file and there is no alias`,
category: 'Style Guide Rules',
options: [
{
description: severityDescription,
default: DEFAULT_SEVERITY,
},
],
notes: [
{
note: 'Rule reports "(inline) duplicated" if the same object is imported more than once in the same import statement',
},
{
note: 'Rule reports "(globalSamePath) duplicated" if the same object is imported on another import statement from same location',
},
{
note: 'Rule reports "(globalDiffPath) duplicated" if the same object is imported on another import statement, from other location, but no alias',
},
{
note: 'Rule does NOT support this kind of import "import * as Alias from "./filename.sol"',
},
],
},
isDefault: false,
recommended: false,
defaultSetup: 'warn',
fixable: true,
schema: null,
}
class DuplicatedImportsChecker extends BaseChecker {
constructor(reporter) {
super(reporter, ruleId, meta)
this.imports = []
}
ImportDirective(node) {
const normalizedPath = this.normalizePath(node.path)
const importStatement = {
path: '',
objectNames: [],
}
importStatement.path = normalizedPath
importStatement.objectNames = node.symbolAliases
? node.symbolAliases
: this.getObjectName(normalizedPath)
this.imports.push(importStatement)
}
'SourceUnit:exit'(node) {
const duplicates = this.findDuplicates(this.imports)
for (let i = 0; i < duplicates.length; i++) {
this.error(node, `Duplicated Import (${duplicates[i].type}) ${duplicates[i].name}`)
}
}
getObjectName(normalizedPath) {
// get file name
const fileNameWithExtension = path.basename(normalizedPath)
// Remove extension
const objectName = fileNameWithExtension.replace('.sol', '')
return [[objectName, null]]
}
normalizePath(path) {
if (path.startsWith('../')) {
return `./${path}`
}
return path
}
findInlineDuplicates(data) {
const inlineDuplicates = []
data.forEach((entry) => {
const path = entry.path
// To track object names
const objectNamesSet = new Set()
entry.objectNames.forEach(([objectName]) => {
// If object name already been found , it is a duplicated
if (objectNamesSet.has(objectName)) {
inlineDuplicates.push({
name: objectName,
type: 'inline',
paths: [path],
})
} else {
// If it is not found before, we add it
objectNamesSet.add(objectName)
}
})
})
return inlineDuplicates
}
finGlobalDuplicatesSamePath(data) {
const duplicates = []
// Loop through data
data.forEach((entry) => {
const path = entry.path
// Object to track object names on each path
const objectNamesMap = {}
// Loop through each objectName of current object
entry.objectNames.forEach(([objectName]) => {
if (!objectNamesMap[objectName]) {
objectNamesMap[objectName] = []
}
objectNamesMap[objectName].push(path)
})
// Compare this object with the rest to detect duplicates
data.forEach((otherEntry) => {
if (otherEntry !== entry) {
otherEntry.objectNames.forEach(([objectName]) => {
if (
objectNamesMap[objectName] &&
objectNamesMap[objectName].includes(otherEntry.path)
) {
// Add path only if it is not present
const existingDuplicate = duplicates.find(
(duplicate) =>
duplicate.name === objectName &&
duplicate.type === 'global' &&
duplicate.paths.includes(entry.path)
)
if (!existingDuplicate) {
duplicates.push({
name: objectName,
type: 'globalSamePath',
paths: [entry.path], // Just add path once, it is always the same
})
}
}
})
}
})
})
return duplicates
}
finGlobalDuplicatesDiffPathNoAlias(data) {
const duplicates = []
// Loop through data
data.forEach((entry) => {
// Object to track names on each path
entry.objectNames.forEach(([objectName, alias]) => {
// Only compare if there is no alias
if (!alias) {
// Go through rest of objects to search for duplicates
data.forEach((otherEntry) => {
if (otherEntry !== entry) {
otherEntry.objectNames.forEach(([otherObjectName, otherAlias]) => {
// If object name is the same, has no alias and different path
if (
objectName === otherObjectName &&
!otherAlias &&
entry.path !== otherEntry.path
) {
// Check if the name is already in the duplicated array
const existingDuplicate = duplicates.find(
(duplicate) =>
duplicate.name === objectName &&
duplicate.type === 'global' &&
duplicate.paths.includes(entry.path)
)
// Add new object if doesn't exist
if (!existingDuplicate) {
duplicates.push({
name: objectName,
type: 'globalDiffPath',
paths: [entry.path, otherEntry.path],
})
}
// Add path if already exists
if (existingDuplicate && !existingDuplicate.paths.includes(otherEntry.path)) {
existingDuplicate.paths.push(otherEntry.path)
}
}
})
}
})
}
})
})
return duplicates
}
removeDuplicatedObjects(data) {
const uniqueData = data.filter((value, index, self) => {
// Order path arrays to be compared later
const sortedPaths = value.paths.slice().sort()
return (
index ===
self.findIndex(
(t) =>
t.name === value.name &&
t.type === value.type &&
// Compare ordered arrays of paths
JSON.stringify(t.paths.slice().sort()) === JSON.stringify(sortedPaths)
)
)
})
return uniqueData
}
findDuplicates(data) {
/// @TODO THIS LOGIC CAN BE IMPROVED - Not done due lack of time
const duplicates1 = this.findInlineDuplicates(data)
const duplicates2 = this.finGlobalDuplicatesSamePath(data)
const duplicates3 = this.finGlobalDuplicatesDiffPathNoAlias(data)
const duplicates = this.removeDuplicatedObjects(duplicates1.concat(duplicates2, duplicates3))
return duplicates
}
}
module.exports = DuplicatedImportsChecker