eslint-plugin-greasemonkey 1.5.1 → 1.6.0
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/README.md +309 -164
- package/package.json +3 -3
- package/src/data/apis.js +0 -1
- package/src/data/deprecations.js +7 -2
- package/src/data/headers.js +12 -6
- package/src/index.js +20 -27
- package/src/rules/meta-order.js +22 -7
- package/src/rules/meta-spacing.js +21 -2
- package/src/rules/require-required-fields.js +7 -2
- package/src/rules/require-sri-hashing.js +1 -1
- package/src/rules/valid-field-values.js +23 -0
- package/src/utils.js +46 -6
- /package/src/rules/{valid-grants.js → no-invalid-grants.js} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-greasemonkey",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "ESLint rules for Greasemonkey/Tampermonkey userscripts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -63,13 +63,13 @@
|
|
|
63
63
|
"eslint": ">=9.0.0"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@e18e/eslint-plugin": "^0.8.
|
|
66
|
+
"@e18e/eslint-plugin": "^0.8.1",
|
|
67
67
|
"@eslint/js": "^10.0.1",
|
|
68
68
|
"@eslint/json": "^2.1.0",
|
|
69
69
|
"@eslint/markdown": "^8.0.3",
|
|
70
70
|
"@stylistic/eslint-plugin": "^5.10.0",
|
|
71
71
|
"eslint": "^10.10.0",
|
|
72
|
-
"eslint-plugin-constructors": "^1.2.
|
|
72
|
+
"eslint-plugin-constructors": "^1.2.2",
|
|
73
73
|
"eslint-plugin-eslint-plugin": "^7.6.2",
|
|
74
74
|
"eslint-plugin-import-x": "^4.17.1",
|
|
75
75
|
"eslint-plugin-n": "^18.3.0",
|
package/src/data/apis.js
CHANGED
package/src/data/deprecations.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// Delete a line to end support
|
|
2
|
+
|
|
3
|
+
export const rules = { // old: new
|
|
4
|
+
'valid-grants': 'no-invalid-grants', // 2026-9-16 announced removal @ v2
|
|
5
|
+
'require-meta-newline': 'require-blank-line-after-meta-block' // 2026-9-13 announced removal @ v2
|
|
3
6
|
}
|
|
7
|
+
|
|
8
|
+
export default { rules }
|
package/src/data/headers.js
CHANGED
|
@@ -6,17 +6,23 @@ export const universalHeaders = new Set([
|
|
|
6
6
|
])
|
|
7
7
|
|
|
8
8
|
export const managerHeaders = new Map([
|
|
9
|
-
['
|
|
10
|
-
['
|
|
11
|
-
['user-agent', ['tampermonkey']],
|
|
12
|
-
['run-in', ['tampermonkey']],
|
|
9
|
+
['run-in', ['tampermonkey', 'scriptcat']],
|
|
10
|
+
['nocompat', ['tampermonkey']],
|
|
13
11
|
['sandbox', ['tampermonkey']],
|
|
12
|
+
['user-agent', ['tampermonkey']],
|
|
14
13
|
['webRequest', ['tampermonkey']],
|
|
15
|
-
['
|
|
14
|
+
['inject-into', ['violentmonkey', 'scriptcat']],
|
|
15
|
+
['top-level-await', ['violentmonkey']],
|
|
16
|
+
['background', ['scriptcat']],
|
|
17
|
+
['crontab', ['scriptcat']],
|
|
18
|
+
['definition', ['scriptcat']],
|
|
19
|
+
['early-start', ['scriptcat']],
|
|
20
|
+
['require-css', ['scriptcat']],
|
|
21
|
+
['storageName', ['scriptcat']]
|
|
16
22
|
])
|
|
17
23
|
|
|
18
24
|
export const localizedHeaders = ['name', 'description', 'antifeature']
|
|
19
|
-
export const managers = ['tampermonkey', 'violentmonkey', 'greasemonkey']
|
|
25
|
+
export const managers = ['tampermonkey', 'violentmonkey', 'greasemonkey', 'scriptcat']
|
|
20
26
|
export const validHeaders = new Map()
|
|
21
27
|
|
|
22
28
|
validHeaders.set('all', new Set([...universalHeaders, ...managerHeaders.keys()]))
|
package/src/index.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable security/detect-object-injection -- false positives */
|
|
2
|
+
|
|
3
|
+
import { rules as oldRules } from './data/deprecations.js'
|
|
2
4
|
|
|
3
5
|
import metaOrder from './rules/meta-order.js'
|
|
4
6
|
import metaSpacing from './rules/meta-spacing.js'
|
|
5
7
|
import noDuplicateKeys from './rules/no-duplicate-keys.js'
|
|
8
|
+
import noInvalidGrants from './rules/no-invalid-grants.js'
|
|
6
9
|
import noInvalidHeaders from './rules/no-invalid-headers.js'
|
|
7
10
|
import noMissingGrants from './rules/no-missing-grants.js'
|
|
8
11
|
import noMissingResources from './rules/no-missing-resources.js'
|
|
@@ -18,13 +21,15 @@ import requireSRIhashing from './rules/require-sri-hashing.js'
|
|
|
18
21
|
import requireUpdateDownloadPair from './rules/require-update-download-pair.js'
|
|
19
22
|
import requireUserJsFilename from './rules/require-user-js-filename.js'
|
|
20
23
|
import validFieldValues from './rules/valid-field-values.js'
|
|
21
|
-
|
|
24
|
+
|
|
25
|
+
import { initDeprecatedAlias } from './utils.js'
|
|
22
26
|
|
|
23
27
|
const plugin = {
|
|
24
28
|
rules: {
|
|
25
29
|
'meta-order': metaOrder,
|
|
26
30
|
'meta-spacing': metaSpacing,
|
|
27
31
|
'no-duplicate-keys': noDuplicateKeys,
|
|
32
|
+
'no-invalid-grants': noInvalidGrants,
|
|
28
33
|
'no-invalid-headers': noInvalidHeaders,
|
|
29
34
|
'no-missing-grants': noMissingGrants,
|
|
30
35
|
'no-unused-grants': noUnusedGrants,
|
|
@@ -39,27 +44,15 @@ const plugin = {
|
|
|
39
44
|
'require-sri-hashing': requireSRIhashing,
|
|
40
45
|
'require-update-download-pair': requireUpdateDownloadPair,
|
|
41
46
|
'require-user-js-filename': requireUserJsFilename,
|
|
42
|
-
'valid-field-values': validFieldValues
|
|
43
|
-
'valid-grants': validGrants
|
|
47
|
+
'valid-field-values': validFieldValues
|
|
44
48
|
}
|
|
45
49
|
}
|
|
46
50
|
|
|
47
|
-
const docsURL = 'https://codeberg.org/adamlui/eslint-plugin-greasemonkey/#'
|
|
48
51
|
for (const [ruleId, rule] of Object.entries(plugin.rules))
|
|
49
|
-
rule.meta.docs.url =
|
|
52
|
+
rule.meta.docs.url = `https://codeberg.org/adamlui/eslint-plugin-greasemonkey/#${ruleId}`
|
|
50
53
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const newRule = plugin.rules[newName]
|
|
54
|
-
plugin.rules[oldName] = {
|
|
55
|
-
...newRule,
|
|
56
|
-
meta: {
|
|
57
|
-
...newRule.meta,
|
|
58
|
-
docs: { ...newRule.meta.docs, recommended: false },
|
|
59
|
-
deprecated: { message: `Use \`${newName}\` instead.`, replacedBy: [{ rule: { name: newName }}] }
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
} /* eslint-enable security/detect-object-injection */
|
|
54
|
+
for (const entry of Object.entries(oldRules))
|
|
55
|
+
initDeprecatedAlias(plugin, entry)
|
|
63
56
|
|
|
64
57
|
plugin.configs = {
|
|
65
58
|
recommended: {
|
|
@@ -67,6 +60,7 @@ plugin.configs = {
|
|
|
67
60
|
rules: {
|
|
68
61
|
'greasemonkey/meta-spacing': 'error',
|
|
69
62
|
'greasemonkey/no-duplicate-keys': 'error',
|
|
63
|
+
'greasemonkey/no-invalid-grants': 'error',
|
|
70
64
|
'greasemonkey/no-invalid-headers': 'error',
|
|
71
65
|
'greasemonkey/no-missing-grants': 'error',
|
|
72
66
|
'greasemonkey/no-unused-grants': 'error',
|
|
@@ -78,24 +72,23 @@ plugin.configs = {
|
|
|
78
72
|
'greasemonkey/require-required-fields': 'error',
|
|
79
73
|
'greasemonkey/require-space-before-meta-key': 'error',
|
|
80
74
|
'greasemonkey/require-user-js-filename': 'error',
|
|
81
|
-
'greasemonkey/valid-field-values': 'error'
|
|
82
|
-
'greasemonkey/valid-grants': 'error'
|
|
75
|
+
'greasemonkey/valid-field-values': 'error'
|
|
83
76
|
}
|
|
77
|
+
},
|
|
78
|
+
strict: {
|
|
79
|
+
plugins: { greasemonkey: plugin },
|
|
80
|
+
rules: Object.fromEntries(Object.keys(plugin.rules).map(ruleId => [`greasemonkey/${ruleId}`, 'error']))
|
|
84
81
|
}
|
|
85
82
|
}
|
|
86
83
|
|
|
87
|
-
plugin.configs.strict = {
|
|
88
|
-
plugins: { greasemonkey: plugin },
|
|
89
|
-
rules: Object.fromEntries(Object.keys(plugin.rules).map(ruleId => [`greasemonkey/${ruleId}`, 'error']))
|
|
90
|
-
}
|
|
91
|
-
|
|
92
84
|
for (const manager of ['tampermonkey', 'violentmonkey', 'greasemonkey', 'scriptcat'])
|
|
93
|
-
plugin.configs[manager] = {
|
|
85
|
+
plugin.configs[manager] = {
|
|
94
86
|
plugins: { greasemonkey: plugin },
|
|
95
87
|
rules: {
|
|
96
88
|
...plugin.configs.recommended.rules,
|
|
89
|
+
'greasemonkey/no-invalid-grants': ['error', { target: manager }],
|
|
97
90
|
'greasemonkey/no-invalid-headers': ['error', { target: manager }],
|
|
98
|
-
'greasemonkey/
|
|
91
|
+
...(manager == 'tampermonkey' && { 'greasemonkey/require-update-download-pair': 'error' })
|
|
99
92
|
}
|
|
100
93
|
}
|
|
101
94
|
|
package/src/rules/meta-order.js
CHANGED
|
@@ -12,8 +12,8 @@ function findViolation(entries, keys) {
|
|
|
12
12
|
const key = entry.key.split(':', 1)[0]
|
|
13
13
|
return { entry, key, rank: ranks.get(key) }
|
|
14
14
|
}).filter(item => item.rank !== undefined)
|
|
15
|
-
for (let i = 1; i < known.length; i++) // eslint-disable-next-line security/detect-object-injection
|
|
16
|
-
if (known[i -
|
|
15
|
+
for (let i = 1 ; i < known.length ; i++) // eslint-disable-next-line security/detect-object-injection
|
|
16
|
+
if (known[i -1].rank > known[i].rank) return [known[i -1], known[i]]
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
function sortEntries(entries, keys) {
|
|
@@ -21,7 +21,7 @@ function sortEntries(entries, keys) {
|
|
|
21
21
|
return entries.toSorted((a, b) => {
|
|
22
22
|
const aRank = ranks.get(a.key.split(':', 1)[0]),
|
|
23
23
|
bRank = ranks.get(b.key.split(':', 1)[0])
|
|
24
|
-
return (aRank ?? Infinity) -
|
|
24
|
+
return ( aRank ?? Infinity ) -( bRank ?? Infinity )
|
|
25
25
|
})
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -35,16 +35,31 @@ export default {
|
|
|
35
35
|
},
|
|
36
36
|
fixable: 'code',
|
|
37
37
|
schema: [{
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
oneOf: [
|
|
39
|
+
{
|
|
40
|
+
type: 'array', items: { type: 'string' },
|
|
41
|
+
description: 'Metadata keys in preferred order; unspecified keys are ignored'
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
type: 'object',
|
|
45
|
+
properties: {
|
|
46
|
+
order: {
|
|
47
|
+
type: 'array', items: { type: 'string' },
|
|
48
|
+
description: 'Metadata keys in preferred order; unspecified keys are ignored'
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
additionalProperties: false,
|
|
52
|
+
description: 'Options for metadata key ordering'
|
|
53
|
+
}
|
|
54
|
+
]
|
|
41
55
|
}],
|
|
42
56
|
defaultOptions: [[]],
|
|
43
57
|
messages: { wrongOrder: '@{{first}} should appear before @{{second}}' }
|
|
44
58
|
},
|
|
45
59
|
|
|
46
60
|
create(context) {
|
|
47
|
-
const
|
|
61
|
+
const option = context.options[0] ?? [],
|
|
62
|
+
custom = Array.isArray(option) ? option : option.order ?? [],
|
|
48
63
|
master = custom.length ? defaultOrder.filter(key => !custom.includes(key)) : defaultOrder
|
|
49
64
|
return {
|
|
50
65
|
Program() {
|
|
@@ -11,13 +11,32 @@ export default {
|
|
|
11
11
|
recommended: true
|
|
12
12
|
},
|
|
13
13
|
fixable: 'whitespace',
|
|
14
|
-
schema: [{
|
|
14
|
+
schema: [{
|
|
15
|
+
oneOf: [
|
|
16
|
+
{
|
|
17
|
+
type: 'number',
|
|
18
|
+
description: 'Minimum spaces after the longest key (default 3)'
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
type: 'object',
|
|
22
|
+
properties: {
|
|
23
|
+
spaces: {
|
|
24
|
+
type: 'number',
|
|
25
|
+
description: 'Minimum spaces after the longest key (default 3)'
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
additionalProperties: false,
|
|
29
|
+
description: 'Options for metadata spacing'
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}],
|
|
15
33
|
defaultOptions: [3],
|
|
16
34
|
messages: { misaligned: 'Metadata values should be aligned at column {{column}}' }
|
|
17
35
|
},
|
|
18
36
|
|
|
19
37
|
create(context) {
|
|
20
|
-
const
|
|
38
|
+
const option = context.options[0] ?? 3,
|
|
39
|
+
minSpaces = typeof option == 'number' ? option : option.spaces ?? 3
|
|
21
40
|
return {
|
|
22
41
|
Program() {
|
|
23
42
|
const sourceCode = context.sourceCode,
|
|
@@ -15,6 +15,10 @@ export default {
|
|
|
15
15
|
type: 'array',
|
|
16
16
|
items: { type: 'string' },
|
|
17
17
|
description: 'Required metadata fields (default: name, version)'
|
|
18
|
+
},
|
|
19
|
+
requireMatchOrInclude: {
|
|
20
|
+
type: 'boolean',
|
|
21
|
+
description: 'Require a match or include metadata field (default: true)'
|
|
18
22
|
}
|
|
19
23
|
},
|
|
20
24
|
additionalProperties: false
|
|
@@ -25,7 +29,8 @@ export default {
|
|
|
25
29
|
|
|
26
30
|
create(context) {
|
|
27
31
|
const options = context.options[0] || {},
|
|
28
|
-
requiredFields = options.fields || ['name', 'version']
|
|
32
|
+
requiredFields = options.fields || ['name', 'version'],
|
|
33
|
+
requireMatchOrInclude = options.requireMatchOrInclude ?? true
|
|
29
34
|
return {
|
|
30
35
|
Program(node) {
|
|
31
36
|
const metaEntries = extractMetadata(context.sourceCode),
|
|
@@ -33,7 +38,7 @@ export default {
|
|
|
33
38
|
for (const field of requiredFields)
|
|
34
39
|
if (!keys.has(field))
|
|
35
40
|
context.report({ node, messageId: 'missingField', data: { field }})
|
|
36
|
-
if (
|
|
41
|
+
if (requireMatchOrInclude && !['include', 'match'].some(field => keys.has(field)))
|
|
37
42
|
context.report({ node, messageId: 'missingField', data: { field: 'match or include' }})
|
|
38
43
|
}
|
|
39
44
|
}
|
|
@@ -46,7 +46,7 @@ export default {
|
|
|
46
46
|
node: entry.comment, messageId: 'invalidHash',
|
|
47
47
|
data: {
|
|
48
48
|
key: entry.key,
|
|
49
|
-
detected: match ? ` Detected ${match[1].toLowerCase()}
|
|
49
|
+
detected: match ? ` Detected ${match[1].toLowerCase()}.` : '',
|
|
50
50
|
expected: algorithm
|
|
51
51
|
}
|
|
52
52
|
})
|
|
@@ -28,6 +28,14 @@ export default {
|
|
|
28
28
|
type: 'array',
|
|
29
29
|
items: { type: 'string' },
|
|
30
30
|
description: 'Allowed @grant values to check against; if empty, no grant validation'
|
|
31
|
+
},
|
|
32
|
+
allowFutureDate: {
|
|
33
|
+
type: 'boolean',
|
|
34
|
+
description: 'Allow @version dates later than the current date (default: false)'
|
|
35
|
+
},
|
|
36
|
+
timezone: {
|
|
37
|
+
type: 'string',
|
|
38
|
+
description: 'IANA timezone for determining the current date (when omitted, only year and month are checked)'
|
|
31
39
|
}
|
|
32
40
|
},
|
|
33
41
|
additionalProperties: false
|
|
@@ -35,6 +43,7 @@ export default {
|
|
|
35
43
|
defaultOptions: [{}],
|
|
36
44
|
messages: {
|
|
37
45
|
invalidVersion: 'Invalid @version: must be a valid {{versionType}} (expected format: {{example}})',
|
|
46
|
+
futureVersion: 'Invalid @version: date cannot be in the future',
|
|
38
47
|
invalidURL: 'Invalid URL for @{{field}}',
|
|
39
48
|
invalidGrant: 'Unknown @grant: "{{grant}}"'
|
|
40
49
|
}
|
|
@@ -44,7 +53,15 @@ export default {
|
|
|
44
53
|
const options = context.options[0] || {},
|
|
45
54
|
versionType = options.versionType || 'semver',
|
|
46
55
|
allowedGrants = options.allowedGrants || [],
|
|
56
|
+
allowFutureDate = options.allowFutureDate ?? false,
|
|
57
|
+
timezone = options.timezone,
|
|
47
58
|
example = versionType == 'semver' ? '1.2.3' : 'YYYY.M.D or YYYY.MM.DD'
|
|
59
|
+
const now = timezone
|
|
60
|
+
? Object.fromEntries(new Intl.DateTimeFormat('en', {
|
|
61
|
+
timeZone: timezone, year: 'numeric', month: 'numeric', day: 'numeric'
|
|
62
|
+
}).formatToParts().filter(({ type }) => ['year', 'month', 'day'].includes(type))
|
|
63
|
+
.map(({ type, value }) => [type, Number(value)]))
|
|
64
|
+
: { year: new Date().getUTCFullYear(), month: new Date().getUTCMonth() +1 }
|
|
48
65
|
return {
|
|
49
66
|
Program() {
|
|
50
67
|
const metaEntries = extractMetadata(context.sourceCode)
|
|
@@ -66,9 +83,15 @@ export default {
|
|
|
66
83
|
const isValid = versionType == 'semver' ? re.semVer.test(entry.value)
|
|
67
84
|
: versionType == 'date' ? re.dateVer.test(entry.value)
|
|
68
85
|
: false
|
|
86
|
+
const [year, month, day] = versionType == 'date' ? entry.value.split('.').map(Number) : []
|
|
69
87
|
if (!isValid)
|
|
70
88
|
context.report({
|
|
71
89
|
node: entry.comment, messageId: 'invalidVersion', data: { versionType, example }})
|
|
90
|
+
else if (versionType == 'date' && !allowFutureDate && (
|
|
91
|
+
year > now.year || year == now.year && month > now.month
|
|
92
|
+
|| timezone && year == now.year && month == now.month && day > now.day
|
|
93
|
+
))
|
|
94
|
+
context.report({ node: entry.comment, messageId: 'futureVersion' })
|
|
72
95
|
}
|
|
73
96
|
}
|
|
74
97
|
}
|
package/src/utils.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const re = { whitespace: /\s/ }
|
|
2
|
+
|
|
1
3
|
export function extractMetadata(sourceCode) {
|
|
2
4
|
const comments = sourceCode.getAllComments(),
|
|
3
5
|
metaEntries = []
|
|
@@ -7,17 +9,55 @@ export function extractMetadata(sourceCode) {
|
|
|
7
9
|
if (!inMetaBlock && text == '==UserScript==') inMetaBlock = true
|
|
8
10
|
else if (inMetaBlock && text == '==/UserScript==') break
|
|
9
11
|
else if (inMetaBlock && text.startsWith('@')) { // capture entry
|
|
10
|
-
const spaceIdx = text.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
metaEntries.push({ key, value, comment, line: comment.loc.start.line })
|
|
15
|
-
}
|
|
12
|
+
const spaceIdx = text.search(re.whitespace),
|
|
13
|
+
key = spaceIdx == -1 ? text.slice(1).trim() : text.slice(1, spaceIdx).trim(),
|
|
14
|
+
value = spaceIdx == -1 ? '' : text.slice(spaceIdx +1).trim()
|
|
15
|
+
if (key) metaEntries.push({ key, value, comment, line: comment.loc.start.line })
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
return metaEntries
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
export function initDeprecatedAlias(plugin, [oldName, alias]) { /* eslint-disable security/detect-object-injection */
|
|
22
|
+
const entries = typeof alias == 'string' ? [[alias, null]] // old: new
|
|
23
|
+
: Array.isArray(alias) ? alias.flatMap(entry =>
|
|
24
|
+
typeof entry == 'string' ? [[entry, null]] : Object.entries(entry)) // old: [new, ...]
|
|
25
|
+
: Object.entries(alias) // old: { new: { old-option: new-option }}
|
|
26
|
+
const newNames = entries.filter(([name]) => name != oldName).map(([name]) => name),
|
|
27
|
+
newRule = plugin.rules[newNames[0] || oldName]
|
|
28
|
+
plugin.rules[oldName] = {
|
|
29
|
+
...newRule,
|
|
30
|
+
meta: {
|
|
31
|
+
...newRule.meta,
|
|
32
|
+
schema: typeof alias == 'string' ? newRule.meta.schema : [{ type: 'object' }],
|
|
33
|
+
docs: { ...newRule.meta.docs, recommended: false },
|
|
34
|
+
deprecated: {
|
|
35
|
+
message: `Use ${newNames.map(name => `\`${name}\``).join(', ')} instead.`,
|
|
36
|
+
replacedBy: newNames.map(name => ({ rule: { name }}))
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
create(context) {
|
|
40
|
+
const oldOptions = context.options[0]
|
|
41
|
+
return Object.assign({}, ...entries.map(([name, mapping]) => {
|
|
42
|
+
const newRule = plugin.rules[name]
|
|
43
|
+
const options = mapping ? {
|
|
44
|
+
...oldOptions,
|
|
45
|
+
...Object.fromEntries(Object.entries(mapping).map(([oldKey, newKey]) => // old: new
|
|
46
|
+
[newKey, oldOptions?.[oldKey] ?? (newKey == 'reverseDefault' ? true : newKey)]))
|
|
47
|
+
} : oldOptions
|
|
48
|
+
const aliasContext = new Proxy({}, {
|
|
49
|
+
get(_, property) {
|
|
50
|
+
if (property == 'options') return [options]
|
|
51
|
+
const val = context[property]
|
|
52
|
+
return typeof val == 'function' ? val.bind(context) : val
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
return newRule.create(aliasContext)
|
|
56
|
+
}))
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
} /* eslint-enable security/detect-object-injection */
|
|
60
|
+
|
|
21
61
|
export function resolveTemplate(node) {
|
|
22
62
|
if (node.type == 'ConditionalExpression') {
|
|
23
63
|
const branches = [node.consequent, node.alternate].map(resolveTemplate)
|
|
File without changes
|