@sap/eslint-plugin-cds 2.5.0 → 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/README.md +2 -1
- package/lib/api/index.js +9 -9
- package/lib/conf/all.js +20 -19
- package/lib/conf/index.js +10 -10
- package/lib/conf/recommended.js +17 -16
- package/lib/constants.js +16 -14
- package/lib/index.js +17 -11
- package/lib/parser.js +90 -82
- package/lib/rules/assoc2many-ambiguous-key.js +71 -70
- package/lib/rules/auth-no-empty-restrictions.js +16 -15
- package/lib/rules/auth-use-requires.js +19 -18
- package/lib/rules/auth-valid-restrict-grant.js +49 -46
- package/lib/rules/auth-valid-restrict-keys.js +19 -18
- package/lib/rules/auth-valid-restrict-to.js +68 -64
- package/lib/rules/auth-valid-restrict-where.js +44 -43
- package/lib/rules/extension-restrictions.js +69 -0
- package/lib/rules/index.js +23 -22
- package/lib/rules/latest-cds-version.js +21 -20
- package/lib/rules/min-node-version.js +22 -22
- package/lib/rules/no-db-keywords.js +21 -27
- package/lib/rules/no-dollar-prefixed-names.js +12 -11
- package/lib/rules/no-join-on-draft.js +27 -0
- package/lib/rules/require-2many-oncond.js +8 -8
- package/lib/rules/sql-cast-suggestion.js +13 -12
- package/lib/rules/start-elements-lowercase.js +42 -41
- package/lib/rules/start-entities-uppercase.js +26 -25
- package/lib/rules/valid-csv-header.js +58 -57
- package/lib/types.d.ts +1 -0
- package/lib/utils/Cache.js +17 -17
- package/lib/utils/Colors.js +8 -8
- package/lib/utils/createRule.js +172 -153
- package/lib/utils/findFuzzy.js +37 -38
- package/lib/utils/genDocs.js +224 -242
- package/lib/utils/getConfigPath.js +27 -27
- package/lib/utils/getConfiguredFileTypes.js +4 -4
- package/lib/utils/getFileExtensions.js +3 -3
- package/lib/utils/getProjectRootPath.js +25 -0
- package/lib/utils/isConfiguredFileType.js +11 -11
- package/lib/utils/rules.js +59 -59
- package/lib/utils/runRuleTester.js +76 -71
- package/package.json +7 -1
- package/lib/rules/no-join-on-draft-enabled-entities.js +0 -25
- package/lib/utils/createRuleDocs.js +0 -361
- package/lib/utils/jsonc.js +0 -1
- package/lib/utils/jsoncParser.js +0 -1
package/lib/utils/createRule.js
CHANGED
|
@@ -10,102 +10,107 @@
|
|
|
10
10
|
* @returns {RuleModule}
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
const { SourceCode } = require(
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
13
|
+
const { SourceCode } = require('eslint')
|
|
14
|
+
const fs = require('fs')
|
|
15
|
+
const path = require('path')
|
|
16
|
+
const Cache = require('./Cache')
|
|
17
|
+
const constants = require('../constants')
|
|
18
|
+
const isConfiguredFileType = require('./isConfiguredFileType')
|
|
19
|
+
const getProjectRootPath = require('./getProjectRootPath')
|
|
20
|
+
const cds = require('@sap/cds')
|
|
21
|
+
const { exit } = require('process')
|
|
22
|
+
const LOG = cds.debug('lint:plugin')
|
|
23
|
+
let filePrev = ''
|
|
21
24
|
|
|
22
|
-
const REGEX_COMMENT_START =
|
|
23
|
-
const REGEX_COMMENTS = `${REGEX_COMMENT_START}(enable|disable)(-next)?(-line)?(.+)
|
|
25
|
+
const REGEX_COMMENT_START = '(/\\*|(.+)?//)(\\s?)+eslint-'
|
|
26
|
+
const REGEX_COMMENTS = `${REGEX_COMMENT_START}(enable|disable)(-next)?(-line)?(.+)?`
|
|
24
27
|
|
|
25
28
|
module.exports = (spec) => {
|
|
26
|
-
let { meta, create } = spec
|
|
27
|
-
meta = setMetaDefaults(meta)
|
|
29
|
+
let { meta, create } = spec
|
|
30
|
+
meta = setMetaDefaults(meta)
|
|
28
31
|
|
|
29
32
|
return {
|
|
30
33
|
meta,
|
|
31
34
|
create: (context) => ({
|
|
32
35
|
Program: (node) => {
|
|
33
36
|
try {
|
|
34
|
-
const file = context.getFilename()
|
|
37
|
+
const file = context.getFilename()
|
|
35
38
|
if (file !== filePrev) {
|
|
36
|
-
LOG && LOG(`File: ${context.getFilename()}`)
|
|
39
|
+
LOG && LOG(`File: ${context.getFilename()}`)
|
|
37
40
|
}
|
|
38
|
-
const cdscontext = extendContext(node, context, meta)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
const cdscontext = extendContext(node, context, meta)
|
|
42
|
+
Cache.set('context', cdscontext)
|
|
43
|
+
const { isTest, isValidFile, doEnvironmentChecks, doRootModelChecks } = checkEntryCriteria(meta, cdscontext)
|
|
41
44
|
switch (meta.model) {
|
|
42
|
-
case
|
|
45
|
+
case 'none':
|
|
43
46
|
if (doEnvironmentChecks) {
|
|
44
47
|
if (isTest || !Cache.has(`rule:${cdscontext.id}`)) {
|
|
45
|
-
LOG && LOG(` Model: "${meta.model}" Rule: ${context.id}`)
|
|
46
|
-
Cache.set(`rule:${cdscontext.id}`,
|
|
47
|
-
createReport(node, cdscontext, meta, create)
|
|
48
|
+
LOG && LOG(` Model: "${meta.model}" Rule: ${context.id}`)
|
|
49
|
+
Cache.set(`rule:${cdscontext.id}:${Cache.get('rootpath')}`, 'done')
|
|
50
|
+
createReport(node, cdscontext, meta, create)
|
|
48
51
|
}
|
|
49
52
|
}
|
|
50
|
-
break
|
|
53
|
+
break
|
|
51
54
|
|
|
52
|
-
case
|
|
55
|
+
case 'inferred':
|
|
53
56
|
if (isValidFile && doRootModelChecks) {
|
|
54
|
-
if (isTest || !Cache.has(`rule:${
|
|
55
|
-
LOG && LOG(` Model: "${meta.model}" Rule: ${context.id}`)
|
|
56
|
-
Cache.set(`rule:${cdscontext.id}`,
|
|
57
|
-
createReport(node, cdscontext, meta, create)
|
|
57
|
+
if (isTest || !Cache.has(`rule:${cdscontext.id}:${Cache.get('rootpath')}`)) {
|
|
58
|
+
LOG && LOG(` Model: "${meta.model}" Rule: ${context.id}`)
|
|
59
|
+
Cache.set(`rule:${cdscontext.id}:${Cache.get('rootpath')}`, 'done')
|
|
60
|
+
createReport(node, cdscontext, meta, create)
|
|
58
61
|
} else {
|
|
59
62
|
if (Cache.has(`report:${context.getFilename()}:${context.id}`)) {
|
|
60
|
-
const reports = Cache.get(`report:${context.getFilename()}:${context.id}`)
|
|
63
|
+
const reports = Cache.get(`report:${context.getFilename()}:${context.id}`)
|
|
61
64
|
for (const r of Array.from(reports)) {
|
|
62
|
-
context.report(r)
|
|
65
|
+
context.report(JSON.parse(r))
|
|
63
66
|
}
|
|
64
|
-
Cache.remove(`report:${context.getFilename()}:${context.id}`)
|
|
67
|
+
Cache.remove(`report:${context.getFilename()}:${context.id}`)
|
|
68
|
+
Cache.set(`rule:${cdscontext.id}:${Cache.get('rootpath')}`, 'done')
|
|
65
69
|
}
|
|
66
70
|
}
|
|
67
71
|
}
|
|
68
|
-
break
|
|
72
|
+
break
|
|
69
73
|
|
|
70
74
|
default:
|
|
71
75
|
if (isValidFile) {
|
|
72
|
-
LOG && LOG(` Model: "${meta.model}" Rule: ${context.id}`)
|
|
73
|
-
createReport(node, cdscontext, meta, create)
|
|
76
|
+
LOG && LOG(` Model: "${meta.model}" Rule: ${context.id}`)
|
|
77
|
+
createReport(node, cdscontext, meta, create)
|
|
74
78
|
}
|
|
75
|
-
break
|
|
79
|
+
break
|
|
76
80
|
}
|
|
77
|
-
filePrev = file
|
|
81
|
+
filePrev = file
|
|
78
82
|
} catch (err) {
|
|
79
|
-
|
|
83
|
+
console.error(err)
|
|
80
84
|
}
|
|
81
85
|
}
|
|
82
86
|
})
|
|
83
|
-
}
|
|
84
|
-
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
85
89
|
|
|
86
|
-
function isRunningWithCDSLint() {
|
|
87
|
-
return process.argv[0].endsWith(
|
|
90
|
+
function isRunningWithCDSLint () {
|
|
91
|
+
return process.argv[0].endsWith('node') && process.argv[1].endsWith('cds') && process.argv[2] === 'lint'
|
|
88
92
|
}
|
|
89
93
|
|
|
90
|
-
function
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
const
|
|
94
|
+
function isRunningWithESLint () {
|
|
95
|
+
return process.argv[0].endsWith('node') && process.argv[1].endsWith('eslint')
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function checkEntryCriteria (meta, cdscontext) {
|
|
99
|
+
const isTest = Cache.has('test')
|
|
100
|
+
const hasProjectRoots = Cache.has(`roots:${Cache.get('rootpath')}`)
|
|
101
|
+
const isValidFile = isConfiguredFileType(cdscontext.getFilename(), 'FILES')
|
|
102
|
+
const doRootModelChecks = isTest || (hasProjectRoots && (isRunningWithCDSLint() || isRunningWithESLint() || cdscontext.options.includes('show')))
|
|
98
103
|
// Also lint empty folders (i.e. lintText "" API)
|
|
99
104
|
const doEnvironmentChecks =
|
|
100
|
-
isTest || (
|
|
101
|
-
return { isTest, isValidFile, doRootModelChecks, doEnvironmentChecks }
|
|
105
|
+
isTest || (hasProjectRoots && isRunningWithCDSLint() && cdscontext.getSourceCode().lines[0] === '')
|
|
106
|
+
return { isTest, isValidFile, doRootModelChecks, doEnvironmentChecks }
|
|
102
107
|
}
|
|
103
108
|
|
|
104
|
-
function setMetaDefaults(meta) {
|
|
105
|
-
if (!meta.severity) meta.severity = constants.DEFAULT_RULE_SEVERITY
|
|
106
|
-
if (meta.docs && !meta.docs.category) meta.docs.category = constants.DEFAULT_RULE_CATEGORY
|
|
107
|
-
if (!meta.model) meta.model =
|
|
108
|
-
return meta
|
|
109
|
+
function setMetaDefaults (meta) {
|
|
110
|
+
if (!meta.severity) meta.severity = constants.DEFAULT_RULE_SEVERITY
|
|
111
|
+
if (meta.docs && !meta.docs.category) meta.docs.category = constants.DEFAULT_RULE_CATEGORY
|
|
112
|
+
if (!meta.model) meta.model = 'parsed'
|
|
113
|
+
return meta
|
|
109
114
|
}
|
|
110
115
|
|
|
111
116
|
/**
|
|
@@ -119,9 +124,8 @@ function setMetaDefaults(meta) {
|
|
|
119
124
|
* @param {*} create
|
|
120
125
|
* @returns
|
|
121
126
|
*/
|
|
122
|
-
function createReport(node, cdscontext, meta, create) {
|
|
123
|
-
const handlers = create(cdscontext)
|
|
124
|
-
|
|
127
|
+
function createReport (node, cdscontext, meta, create) {
|
|
128
|
+
const handlers = create(cdscontext)
|
|
125
129
|
/**
|
|
126
130
|
* TODO: Can these be rewritten to have a visitor? Note, that so far,
|
|
127
131
|
* rules without a visitor cannot use eslint disable comments
|
|
@@ -130,129 +134,144 @@ function createReport(node, cdscontext, meta, create) {
|
|
|
130
134
|
* - Environment rules
|
|
131
135
|
*/
|
|
132
136
|
switch (typeof handlers) {
|
|
133
|
-
case
|
|
134
|
-
handlers()
|
|
135
|
-
break
|
|
137
|
+
case 'function':
|
|
138
|
+
handlers()
|
|
139
|
+
break
|
|
140
|
+
|
|
141
|
+
case 'object': {
|
|
142
|
+
if (meta.model !== 'none') {
|
|
143
|
+
const model = cdscontext.getModel()
|
|
136
144
|
|
|
137
|
-
case "object": {
|
|
138
|
-
if (meta.model !== "none") {
|
|
139
|
-
const model = cdscontext.getModel();
|
|
140
145
|
if (model) {
|
|
141
146
|
model.forall((d) => {
|
|
142
147
|
Object.entries(handlers)
|
|
143
148
|
.filter(([type, lazy]) => d.is(type))
|
|
144
149
|
.forEach(([lazy, handler]) => {
|
|
145
150
|
try {
|
|
146
|
-
handler(d)
|
|
151
|
+
handler(d)
|
|
147
152
|
} catch (err) {
|
|
148
|
-
console.log(`Error in rule "${cdscontext.id}" at ${d.name}`, err)
|
|
153
|
+
console.log(`Error in rule "${cdscontext.id}" at ${d.name}`, err)
|
|
149
154
|
}
|
|
150
|
-
})
|
|
151
|
-
})
|
|
155
|
+
})
|
|
156
|
+
})
|
|
152
157
|
}
|
|
153
158
|
}
|
|
154
|
-
break
|
|
159
|
+
break
|
|
155
160
|
}
|
|
156
161
|
}
|
|
157
162
|
}
|
|
158
163
|
|
|
159
|
-
function extendContext(node, context, meta) {
|
|
160
|
-
if (!Cache.has(
|
|
161
|
-
|
|
164
|
+
function extendContext (node, context, meta) {
|
|
165
|
+
if (!Cache.has('test')) {
|
|
166
|
+
const filePath = context.getFilename()
|
|
167
|
+
const rootPath = filePath && fs.existsSync(filePath) ? getProjectRootPath(filePath) : ''
|
|
168
|
+
if (rootPath) {
|
|
169
|
+
Cache.set('rootpath', rootPath)
|
|
170
|
+
}
|
|
162
171
|
}
|
|
163
172
|
|
|
164
173
|
const reportWrapper = (r) => {
|
|
165
|
-
const line = r.loc ? r.loc.start.line : r.node.loc.start.line
|
|
174
|
+
const line = r.loc ? r.loc.start.line : r.node.loc.start.line
|
|
166
175
|
if (!isRuleDisabled(line, context)) {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
176
|
+
if (meta.model === 'inferred') {
|
|
177
|
+
if (!r.file) {
|
|
178
|
+
console.error(`Rule ${context.id} must return a "file" property in the rule report!`)
|
|
179
|
+
exit(1)
|
|
180
|
+
}
|
|
181
|
+
const file = Cache.get('rootpath') ? resolveFilePath(r.file) : r.file
|
|
170
182
|
if (cdscontext.getFilename() === file) {
|
|
171
|
-
delete r.file
|
|
172
|
-
context.report(r)
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
r.message = meta.messages[r.messageId];
|
|
177
|
-
delete r.message;
|
|
178
|
-
}
|
|
179
|
-
if (r) {
|
|
180
|
-
let reports = new Set();
|
|
181
|
-
if (Cache.has(`report:${file}:${context.id}`)) {
|
|
182
|
-
reports = Cache.get(`report:${file}:${context.id}`);
|
|
183
|
-
}
|
|
184
|
-
reports.add(r);
|
|
185
|
-
Cache.set(`report:${file}:${context.id}`, reports);
|
|
186
|
-
}
|
|
183
|
+
delete r.file
|
|
184
|
+
context.report(r)
|
|
185
|
+
}
|
|
186
|
+
if (r.file) {
|
|
187
|
+
cacheReport(r, file, context, meta)
|
|
187
188
|
}
|
|
188
|
-
|
|
189
189
|
} else {
|
|
190
|
-
context.report(r)
|
|
190
|
+
context.report(r)
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
|
-
}
|
|
193
|
+
}
|
|
194
194
|
|
|
195
|
-
const descriptors = Object.getOwnPropertyDescriptors(context)
|
|
195
|
+
const descriptors = Object.getOwnPropertyDescriptors(context)
|
|
196
196
|
descriptors.report = {
|
|
197
197
|
value: reportWrapper,
|
|
198
198
|
writable: false,
|
|
199
199
|
enumerable: true,
|
|
200
200
|
configurable: false
|
|
201
|
-
}
|
|
201
|
+
}
|
|
202
202
|
|
|
203
|
-
const cdscontext = Object.create(Object.getPrototypeOf(context), descriptors)
|
|
203
|
+
const cdscontext = Object.create(Object.getPrototypeOf(context), descriptors)
|
|
204
204
|
cdscontext.getModel =
|
|
205
|
-
meta.model ===
|
|
205
|
+
meta.model === 'inferred' ? context.parserServices.getInferredCsn : context.parserServices.getParsedCsn
|
|
206
206
|
cdscontext.getEnvironment = () => {
|
|
207
|
-
const options = context.options
|
|
208
|
-
return options && options[0] && options[0].environment ? options[0].environment : undefined
|
|
209
|
-
}
|
|
210
|
-
cdscontext.getLocation = context.parserServices.getLocation
|
|
211
|
-
cdscontext.getNode = Object.keys(context.parserServices).length > 0 ? context.parserServices.getNode : () => node
|
|
212
|
-
return cdscontext
|
|
207
|
+
const options = context.options
|
|
208
|
+
return options && options[0] && options[0].environment ? options[0].environment : undefined
|
|
209
|
+
}
|
|
210
|
+
cdscontext.getLocation = context.parserServices.getLocation
|
|
211
|
+
cdscontext.getNode = Object.keys(context.parserServices).length > 0 ? context.parserServices.getNode : () => node
|
|
212
|
+
return cdscontext
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
function isRuleDisabled(line, cdscontext) {
|
|
216
|
-
let isDisabled = false
|
|
215
|
+
function isRuleDisabled (line, cdscontext) {
|
|
216
|
+
let isDisabled = false
|
|
217
217
|
if (cdscontext) {
|
|
218
|
-
const sourcecode = cdscontext.getSourceCode()
|
|
219
|
-
const rulesDisabled = getDisabled(sourcecode.getText(), sourcecode, line)
|
|
220
|
-
const id = cdscontext.id
|
|
221
|
-
isDisabled = line && id in rulesDisabled && rulesDisabled[id] ===
|
|
218
|
+
const sourcecode = cdscontext.getSourceCode()
|
|
219
|
+
const rulesDisabled = getDisabled(sourcecode.getText(), sourcecode, line)
|
|
220
|
+
const id = cdscontext.id
|
|
221
|
+
isDisabled = line && id in rulesDisabled && rulesDisabled[id] === 'off'
|
|
222
|
+
}
|
|
223
|
+
return isDisabled
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function cacheReport (r, file, context, meta) {
|
|
227
|
+
delete r.file
|
|
228
|
+
if (r.node && r.node.range) {
|
|
229
|
+
r.node.range = []
|
|
230
|
+
}
|
|
231
|
+
if (r.messageId) {
|
|
232
|
+
r.message = meta.messages[r.messageId]
|
|
233
|
+
delete r.message
|
|
234
|
+
}
|
|
235
|
+
if (r) {
|
|
236
|
+
let reports = new Set()
|
|
237
|
+
if (Cache.has(`report:${file}:${context.id}`)) {
|
|
238
|
+
reports = Cache.get(`report:${file}:${context.id}`)
|
|
239
|
+
}
|
|
240
|
+
reports.add(JSON.stringify(r))
|
|
241
|
+
Cache.set(`report:${file}:${context.id}`, reports)
|
|
222
242
|
}
|
|
223
|
-
return isDisabled;
|
|
224
243
|
}
|
|
225
244
|
|
|
226
|
-
function getDisabled(code, sourcecode, line) {
|
|
227
|
-
const listDisabled = []
|
|
228
|
-
|
|
229
|
-
const rulesDisabled = Object.keys(rules).reduce((o, key) => ({ ...o, [key]:
|
|
230
|
-
let matches = []
|
|
245
|
+
function getDisabled (code, sourcecode, line) {
|
|
246
|
+
const listDisabled = []
|
|
247
|
+
const rules = Cache.get('rules')
|
|
248
|
+
const rulesDisabled = Object.keys(rules).reduce((o, key) => ({ ...o, [key]: 'on' }), {})
|
|
249
|
+
let matches = []
|
|
231
250
|
if (code) {
|
|
232
|
-
matches = [...code.matchAll(REGEX_COMMENTS)]
|
|
251
|
+
matches = [...code.matchAll(REGEX_COMMENTS)]
|
|
233
252
|
if (matches.length > 0) {
|
|
234
253
|
matches.forEach((match) => {
|
|
235
254
|
if (match) {
|
|
236
|
-
const index = match.index
|
|
237
|
-
match = match[0]
|
|
238
|
-
if (match.includes(
|
|
239
|
-
match = match.split(
|
|
240
|
-
} else if (match.includes(
|
|
241
|
-
match = match.split(
|
|
255
|
+
const index = match.index
|
|
256
|
+
match = match[0]
|
|
257
|
+
if (match.includes('*/')) {
|
|
258
|
+
match = match.split('*/')[0].replace('/*', '')
|
|
259
|
+
} else if (match.includes('//')) {
|
|
260
|
+
match = match.split('//')[1]
|
|
242
261
|
}
|
|
243
262
|
if (match) {
|
|
244
|
-
match = match.trim()
|
|
263
|
+
match = match.trim()
|
|
245
264
|
}
|
|
246
|
-
[
|
|
247
|
-
const loc = sourcecode.getLocFromIndex(index)
|
|
248
|
-
const disableType = match.split(
|
|
249
|
-
let disableRules = match.split(`${disableType} `)[1]
|
|
265
|
+
['disable', 'enable'].forEach((keyword) => {
|
|
266
|
+
const loc = sourcecode.getLocFromIndex(index)
|
|
267
|
+
const disableType = match.split(' ')[0]
|
|
268
|
+
let disableRules = match.split(`${disableType} `)[1]
|
|
250
269
|
disableRules = disableRules
|
|
251
|
-
? disableRules.split(
|
|
252
|
-
: Object.keys(rules).map((rule) => `@sap/cds/${rule}`)
|
|
253
|
-
let comment = {}
|
|
270
|
+
? disableRules.split(',').map((rule) => rule.trim())
|
|
271
|
+
: Object.keys(rules).map((rule) => `@sap/cds/${rule}`)
|
|
272
|
+
let comment = {}
|
|
254
273
|
if ([`eslint-${keyword}`, `eslint-${keyword}-line`, `eslint-${keyword}-next-line`].includes(disableType)) {
|
|
255
|
-
comment = disableType.includes(
|
|
274
|
+
comment = disableType.includes('-next-line')
|
|
256
275
|
? {
|
|
257
276
|
lineComment: loc.line,
|
|
258
277
|
lineDisabled: loc.line + 1,
|
|
@@ -264,41 +283,41 @@ function getDisabled(code, sourcecode, line) {
|
|
|
264
283
|
lineDisabled: loc.line,
|
|
265
284
|
rules: disableRules,
|
|
266
285
|
type: keyword
|
|
267
|
-
}
|
|
268
|
-
if (!disableType.includes(
|
|
269
|
-
comment.lineDisabled =
|
|
286
|
+
}
|
|
287
|
+
if (!disableType.includes('-line')) {
|
|
288
|
+
comment.lineDisabled = 'EOF'
|
|
270
289
|
}
|
|
271
290
|
}
|
|
272
|
-
listDisabled.push(comment)
|
|
273
|
-
})
|
|
291
|
+
listDisabled.push(comment)
|
|
292
|
+
})
|
|
274
293
|
}
|
|
275
|
-
})
|
|
294
|
+
})
|
|
276
295
|
for (const el of listDisabled.filter(
|
|
277
|
-
(d) => d.lineComment > line && (d.lineDisabled ===
|
|
296
|
+
(d) => d.lineComment > line && (d.lineDisabled === 'EOF' || d.lineDisabled === line)
|
|
278
297
|
)) {
|
|
279
|
-
if (el.lineDisabled ===
|
|
280
|
-
el.lineDisabled = getLastLine(code)
|
|
298
|
+
if (el.lineDisabled === 'EOF') {
|
|
299
|
+
el.lineDisabled = getLastLine(code)
|
|
281
300
|
}
|
|
282
301
|
if (el.rules) {
|
|
283
302
|
el.rules.forEach((rule) => {
|
|
284
|
-
if (el.type ===
|
|
285
|
-
rulesDisabled[rule] =
|
|
286
|
-
} else if (el.type ===
|
|
287
|
-
rulesDisabled[rule] =
|
|
303
|
+
if (el.type === 'disable') {
|
|
304
|
+
rulesDisabled[rule] = 'off'
|
|
305
|
+
} else if (el.type === 'enable') {
|
|
306
|
+
rulesDisabled[rule] = 'on'
|
|
288
307
|
}
|
|
289
|
-
})
|
|
308
|
+
})
|
|
290
309
|
}
|
|
291
310
|
}
|
|
292
311
|
}
|
|
293
312
|
}
|
|
294
|
-
return rulesDisabled
|
|
313
|
+
return rulesDisabled
|
|
295
314
|
}
|
|
296
315
|
|
|
297
|
-
function getLastLine(code) {
|
|
298
|
-
const lines = typeof code ===
|
|
299
|
-
return lines.length - 1
|
|
316
|
+
function getLastLine (code) {
|
|
317
|
+
const lines = typeof code === 'string' ? SourceCode.splitLines(code) : code
|
|
318
|
+
return lines.length - 1
|
|
300
319
|
}
|
|
301
320
|
|
|
302
|
-
function resolveFilePath(file) {
|
|
303
|
-
return path.isAbsolute(file) ? file : path.join(Cache.get(
|
|
321
|
+
function resolveFilePath (file) {
|
|
322
|
+
return path.isAbsolute(file) ? file : path.join(Cache.get('rootpath'), file)
|
|
304
323
|
}
|
package/lib/utils/findFuzzy.js
CHANGED
|
@@ -7,86 +7,85 @@
|
|
|
7
7
|
* @returns array with best matches, is never null but might be empty in case no search was possible
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
const cache = {}
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
module.exports = (input, list, log, keepCase=false) => {
|
|
15
|
-
let minDistWords = [];
|
|
12
|
+
module.exports = (input, list, log, keepCase = false, threshold = Number.MAX_SAFE_INTEGER) => {
|
|
13
|
+
let minDistWords = []
|
|
16
14
|
|
|
17
15
|
if (input.length > 50 || list.length > 50) {
|
|
18
|
-
return minDistWords
|
|
16
|
+
return minDistWords
|
|
19
17
|
}
|
|
20
18
|
|
|
21
|
-
let minDist = Number.MAX_SAFE_INTEGER
|
|
19
|
+
let minDist = Number.MAX_SAFE_INTEGER
|
|
22
20
|
|
|
23
|
-
log && log('\nword\t\tlevDist\t\ttime(ms)')
|
|
21
|
+
log && log('\nword\t\tlevDist\t\ttime(ms)')
|
|
24
22
|
|
|
25
|
-
let runtime = 0
|
|
23
|
+
let runtime = 0
|
|
26
24
|
|
|
27
25
|
for (const word of list) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const start = log && Date.now();
|
|
31
|
-
let levDist;
|
|
26
|
+
const start = log && Date.now()
|
|
27
|
+
let levDist
|
|
32
28
|
if (word === word.toUpperCase() && !keepCase) {
|
|
33
|
-
levDist = levDistance(input.toUpperCase(), word)
|
|
29
|
+
levDist = levDistance(input.toUpperCase(), word)
|
|
34
30
|
} else {
|
|
35
|
-
levDist = levDistance(input, word)
|
|
31
|
+
levDist = levDistance(input, word)
|
|
36
32
|
}
|
|
37
33
|
|
|
38
34
|
if (log) {
|
|
39
|
-
const duration = Date.now() - start
|
|
40
|
-
runtime = runtime + duration
|
|
41
|
-
log(`${word}\t\t${levDist}\t\t${duration}`)
|
|
35
|
+
const duration = Date.now() - start
|
|
36
|
+
runtime = runtime + duration
|
|
37
|
+
log(`${word}\t\t${levDist}\t\t${duration}`)
|
|
42
38
|
}
|
|
43
39
|
|
|
44
40
|
if (levDist === minDist) {
|
|
45
|
-
|
|
41
|
+
if (!threshold || (threshold && levDist < threshold)) {
|
|
42
|
+
minDistWords.push(word)
|
|
43
|
+
}
|
|
46
44
|
}
|
|
47
45
|
|
|
48
46
|
if (levDist < minDist) {
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
if (!threshold || (threshold && levDist < threshold)) {
|
|
48
|
+
minDist = levDist
|
|
49
|
+
minDistWords = [word]
|
|
50
|
+
}
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
log && log(`runtime: ${runtime}ms`)
|
|
54
|
+
log && log(`runtime: ${runtime}ms`)
|
|
55
55
|
|
|
56
|
-
return minDistWords.sort()
|
|
56
|
+
return minDistWords.sort()
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
const levDistance = (a, b) => {
|
|
60
|
-
|
|
61
60
|
if (cache[a] && cache[a][b]) {
|
|
62
|
-
return cache[a][b]
|
|
61
|
+
return cache[a][b]
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
if (a.length === 0) {
|
|
66
|
-
return addToCache(a, b, b.length)
|
|
65
|
+
return addToCache(a, b, b.length)
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
if (b.length === 0) {
|
|
70
|
-
return addToCache(a, b, a.length)
|
|
69
|
+
return addToCache(a, b, a.length)
|
|
71
70
|
}
|
|
72
71
|
|
|
73
|
-
const
|
|
74
|
-
const
|
|
72
|
+
const tailA = a.substring(1)
|
|
73
|
+
const tailB = b.substring(1)
|
|
75
74
|
|
|
76
75
|
if (a[0] === b[0]) {
|
|
77
|
-
return levDistance(
|
|
76
|
+
return levDistance(tailA, tailB)
|
|
78
77
|
}
|
|
79
78
|
|
|
80
|
-
const lev1 = levDistance(
|
|
81
|
-
const lev2 = levDistance(a,
|
|
82
|
-
const lev3 = levDistance(
|
|
79
|
+
const lev1 = levDistance(tailA, b)
|
|
80
|
+
const lev2 = levDistance(a, tailB)
|
|
81
|
+
const lev3 = levDistance(tailA, tailB)
|
|
83
82
|
|
|
84
|
-
const levDist = Math.min(lev1, lev2, lev3) + 1
|
|
85
|
-
return addToCache(a, b, levDist)
|
|
83
|
+
const levDist = Math.min(lev1, lev2, lev3) + 1
|
|
84
|
+
return addToCache(a, b, levDist)
|
|
86
85
|
}
|
|
87
86
|
|
|
88
87
|
const addToCache = (a, b, value) => {
|
|
89
|
-
cache[a] = cache[a] || {}
|
|
90
|
-
cache[a][b] = value
|
|
91
|
-
return value
|
|
88
|
+
cache[a] = cache[a] || {}
|
|
89
|
+
cache[a][b] = value
|
|
90
|
+
return value
|
|
92
91
|
}
|