eslint-plugin-greasemonkey 1.9.0 → 1.10.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.
@@ -1,8 +1,9 @@
1
1
  import metadata from '../utils/metadata.js'
2
2
  import template from '../utils/template.js'
3
3
 
4
- const re = { specialChars: /[.*+?^${}()|[\]\\]/g, whitespace: /\s+/ },
5
- resourceGetterNames = new Set(['GM_getResourceText', 'GM_getResourceURL'])
4
+ import regex from '../data/regex.js'
5
+
6
+ const resourceGetterNames = new Set(['GM_getResourceText', 'GM_getResourceURL'])
6
7
 
7
8
  export default {
8
9
  meta: {
@@ -29,8 +30,8 @@ export default {
29
30
  const resolved = template.resolve(arg)
30
31
  if (resolved)
31
32
  for (const name of resolved) usedResources.add(name)
32
- else usedPatterns.push(new RegExp( // eslint-disable-line security/detect-non-literal-regexp
33
- `^${arg.quasis.map(quasi => quasi.value.raw.replace(re.specialChars, '\\$&')).join('.*')}$`))
33
+ else usedPatterns.push(new RegExp( // oxlint-disable-line security/detect-non-literal-regexp
34
+ `^${arg.quasis.map(quasi => quasi.value.raw.replace(regex.specialChars, '\\$&')).join('.*')}$`))
34
35
  }
35
36
  },
36
37
  'Program:exit'() {
@@ -38,7 +39,7 @@ export default {
38
39
  resources = metaEntries.filter(entry => entry.key == 'resource')
39
40
  if (!resources.length) return
40
41
  for (const res of resources) {
41
- const name = res.value.trim().split(re.whitespace)[0]
42
+ const name = res.value.trim().split(regex.whitespace.all)[0]
42
43
  if (usedResources.has(name) || usedPatterns.some(patt => patt.test(name))) continue
43
44
  context.report({ node: res.comment, messageId: 'unusedResource', data: { name }})
44
45
  }
@@ -1,5 +1,17 @@
1
1
  const re = { appendMethod: /^append(?:Child)?$/ }
2
2
 
3
+ function isCreateStyle(node) {
4
+ return node?.type == 'CallExpression'
5
+ && node.callee?.property?.name == 'createElement'
6
+ && node.arguments[0]?.value == 'style'
7
+ }
8
+ function isObjectAssign(node) {
9
+ return node?.type == 'CallExpression'
10
+ && node.callee?.type == 'MemberExpression'
11
+ && node.callee.object?.name == 'Object'
12
+ && node.callee.property?.name == 'assign'
13
+ }
14
+
3
15
  export default {
4
16
  meta: {
5
17
  type: 'suggestion',
@@ -16,11 +28,6 @@ export default {
16
28
 
17
29
  create(context) {
18
30
  const styleElems = new Set(), docAliases = new Set()
19
- function isCreateStyle(node) {
20
- return node?.type == 'CallExpression'
21
- && node.callee?.property?.name == 'createElement'
22
- && node.arguments[0]?.value == 'style'
23
- }
24
31
  function isDocumentRef(node) {
25
32
  return node?.type == 'Identifier'
26
33
  && (node.name == 'document' || docAliases.has(node.name))
@@ -28,12 +35,6 @@ export default {
28
35
  function isDocumentMember(node) {
29
36
  return node?.type == 'MemberExpression' && isDocumentRef(node.object)
30
37
  }
31
- function isObjectAssign(node) {
32
- return node?.type == 'CallExpression'
33
- && node.callee?.type == 'MemberExpression'
34
- && node.callee.object?.name == 'Object'
35
- && node.callee.property?.name == 'assign'
36
- }
37
38
  function isStyleTarget(node) {
38
39
  if (isCreateStyle(node)) return true
39
40
  if (node?.type == 'Identifier' && styleElems.has(node.name)
@@ -0,0 +1,27 @@
1
+ import regex from '../data/regex.js'
2
+
3
+ export default {
4
+ meta: {
5
+ type: 'problem',
6
+ docs: {
7
+ description: 'Requires a `@license` metadata field',
8
+ category: 'Possible Errors',
9
+ recommended: false
10
+ },
11
+ schema: [],
12
+ messages: {
13
+ missingLicense: 'No `@license` field found. Add one (e.g. `@license MIT`).'
14
+ }
15
+ },
16
+ create(context) {
17
+ const sourceCode = context.sourceCode ?? context.getSourceCode()
18
+ return {
19
+ Program() {
20
+ const match = sourceCode.text.match(regex.meta.block)
21
+ if (!match) return
22
+ if (!regex.meta.license.test(match[1]))
23
+ context.report({ loc: { line: 1, column: 0 }, messageId: 'missingLicense' })
24
+ }
25
+ }
26
+ }
27
+ }
@@ -1,6 +1,5 @@
1
1
  import metadata from '../utils/metadata.js'
2
-
3
- const re = { leadingWhitespace: /^\s*/ }
2
+ import regex from '../data/regex.js'
4
3
 
5
4
  export default {
6
5
  meta: {
@@ -24,7 +23,7 @@ export default {
24
23
  const commentText = sourceCode.getText(entry.comment),
25
24
  afterSlashes = commentText.slice(2)
26
25
  if (afterSlashes.startsWith(' @')) continue
27
- const rest = afterSlashes.replace(re.leadingWhitespace, ''),
26
+ const rest = afterSlashes.replace(regex.whitespace.leading, ''),
28
27
  keyIdx = commentText.indexOf(`@${entry.key}`),
29
28
  actual = commentText.slice(0, keyIdx +1 + entry.key.length),
30
29
  fixedText = `// ${rest}`
@@ -1,10 +1,10 @@
1
1
  import metadata from '../utils/metadata.js'
2
+ import regex from '../data/regex.js'
2
3
 
3
- const re = { /* eslint-disable security/detect-unsafe-regex */
4
+ const re = { // oxlint-disable security/detect-unsafe-regex
4
5
  semVer: /^\d+\.\d+\.\d+(?:-[\da-z.-]+)?(?:\+[\da-z.-]+)?$/i,
5
- dateVer: /^\d{4}\.(?:0?[1-9]|1[0-2])\.(?:0?[1-9]|[12]\d|3[01])(?:\.\d+)?$/,
6
- whitespace: /\s+/
7
- } /* eslint-enable security/detect-unsafe-regex */
6
+ dateVer: /^\d{4}\.(?:0?[1-9]|1[0-2])\.(?:0?[1-9]|[12]\d|3[01])(?:\.\d+)?$/
7
+ } // oxlint-enable security/detect-unsafe-regex
8
8
 
9
9
  const urlKeys = ['homepage', 'icon', 'icon64', 'require', 'source', 'website']
10
10
 
@@ -69,7 +69,7 @@ export default {
69
69
  if (entry.key == 'grant' && allowedGrants.size && !allowedGrants.has(entry.value))
70
70
  context.report({ node: entry.comment, messageId: 'invalidGrant', data: { grant: entry.value }})
71
71
  else if (entry.key == 'resource') {
72
- const url = entry.value.trim().split(re.whitespace)[1]
72
+ const url = entry.value.trim().split(regex.whitespace.all)[1]
73
73
  if (url) try { new URL(url) }
74
74
  catch {
75
75
  context.report({ node: entry.comment, messageId: 'invalidURL', data: { field: entry.key }})
@@ -0,0 +1,74 @@
1
+ import regex from '../data/regex.js'
2
+
3
+ const re = {
4
+ dataURI: /^data:([^;,]+)(;base64)?,(.*)$/i,
5
+ iconLine: /^[ \t]*\/\/[ \t]*@(icon|icon64|defaulticon)[ \t]+(\S.*)$/,
6
+ scheme: /^([a-z][a-z0-9+.-]*):/i
7
+ }
8
+
9
+ export default {
10
+ meta: {
11
+ type: 'suggestion',
12
+ docs: {
13
+ description: 'Validates `@icon`/`@icon64`/`@defaulticon` values against allowed schemes',
14
+ category: 'Possible Errors',
15
+ recommended: true
16
+ },
17
+ schema: [],
18
+ messages: {
19
+ malformedURL: '`@{{header}}` is not a valid URL.',
20
+ malformedDataURI: '`@{{header}}` data URI is malformed (expected `data:<mime>[;base64],<payload>`).',
21
+ nonImageMIME: '`@{{header}}` data URI MIME type `{{mime}}` is not an image.',
22
+ disallowedScheme: '`@{{header}}` uses unsupported scheme `{{scheme}}:`'
23
+ + ' (use `http(s)`, `data:`, or a relative path).'
24
+ }
25
+ },
26
+
27
+ create(context) {
28
+ const sourceCode = context.sourceCode ?? context.getSourceCode()
29
+
30
+ function validate(value, header, line, col) {
31
+ const loc = { start: { line, column: col }, end: { line, column: col + value.length }}
32
+ const schemeMatch = value.match(re.scheme)
33
+ if (!schemeMatch) return // relative path — accepted
34
+ const scheme = schemeMatch[1].toLowerCase()
35
+ if (scheme == 'http' || scheme == 'https') {
36
+ try { new URL(value) }
37
+ catch { return context.report({ loc, messageId: 'malformedURL', data: { header }}) }
38
+ return
39
+ }
40
+ if (scheme == 'data') {
41
+ const dataMatch = value.match(re.dataURI)
42
+ if (!dataMatch)
43
+ return context.report({ loc, messageId: 'malformedDataURI', data: { header }})
44
+ const mime = dataMatch[1]
45
+ if (!mime.toLowerCase().startsWith('image/'))
46
+ return context.report({ loc, messageId: 'nonImageMIME', data: { header, mime }})
47
+ return
48
+ }
49
+ context.report({ loc, messageId: 'disallowedScheme', data: { header, scheme }})
50
+ }
51
+
52
+ return {
53
+ Program() {
54
+ const text = sourceCode.text,
55
+ metaMatch = text.match(regex.meta.block)
56
+ if (!metaMatch) return
57
+ const lines = metaMatch[1].split(regex.newline),
58
+ iconMatches = []
59
+ for (const [i, line] of lines.entries()) {
60
+ const m = line.match(re.iconLine)
61
+ if (m) iconMatches.push({ i, line, m })
62
+ }
63
+ if (!iconMatches.length) return
64
+ const baseLine = text.slice(0, metaMatch.index).split(regex.newline).length
65
+ for (const { i, line, m } of iconMatches) {
66
+ const header = m[1],
67
+ value = m[2].trim()
68
+ if (!value) continue
69
+ validate(value, header, baseLine + i, line.indexOf(value))
70
+ }
71
+ }
72
+ }
73
+ }
74
+ }
@@ -0,0 +1,73 @@
1
+ // oxlint-disable security/detect-object-injection
2
+
3
+ import array from '../utils/array.js'
4
+ import license from '../utils/license.js'
5
+
6
+ import regex from '../data/regex.js'
7
+
8
+ const re = { spdxExpr: /(?<=\s)(?:OR|AND)(?=\s)/i }
9
+
10
+ export default {
11
+ meta: {
12
+ type: 'problem',
13
+ docs: {
14
+ description: 'Validates `@license` values against the SPDX license list',
15
+ category: 'Possible Errors',
16
+ recommended: true
17
+ },
18
+ defaultOptions: [{ allowedLicenses: [] }],
19
+ schema: [{
20
+ type: 'object',
21
+ properties: {
22
+ allowedLicenses: {
23
+ description: 'SPDX license IDs to restrict to',
24
+ oneOf: [
25
+ { type: 'string', description: 'Single SPDX license ID' },
26
+ { type: 'array', description: 'Array of SPDX license IDs', items: { type: 'string' }}
27
+ ]
28
+ }
29
+ },
30
+ additionalProperties: false
31
+ }],
32
+ messages: {
33
+ invalidSPDX: 'Invalid SPDX identifier `{{id}}` (see https://spdx.org/licenses/).',
34
+ disallowedSPDX: '`@license {{id}}` is not in allowed list: {{allowed}}.'
35
+ }
36
+ },
37
+
38
+ create(context) {
39
+ const opts = context.options[0] ?? {},
40
+ allowedLicenses = array.to(opts.allowedLicenses) ?? [],
41
+ sourceCode = context.sourceCode ?? context.getSourceCode()
42
+ return {
43
+ Program() {
44
+ const text = sourceCode.text,
45
+ metaMatch = text.match(regex.meta.block)
46
+ if (!metaMatch) return
47
+ const lines = text.split('\n'),
48
+ startLine = text.slice(0, metaMatch.index).split('\n').length -1,
49
+ endLine = startLine + metaMatch[0].split('\n').length -1
50
+ let licenseLineIdx = -1, licenseMatch = null
51
+ for (let i = startLine ; i < endLine ; i++) {
52
+ const match = lines[i].match(regex.meta.license)
53
+ if (match) { licenseLineIdx = i ; licenseMatch = match ; break }
54
+ }
55
+ if (!licenseMatch) return
56
+ const id = licenseMatch[1].trim() ; if (regex.url.test(id)) return
57
+ const parts = id.split(re.spdxExpr).map(part => part.trim()).filter(Boolean),
58
+ idCol = lines[licenseLineIdx].indexOf(id),
59
+ loc = { start: { line: licenseLineIdx +1, column: idCol },
60
+ end: { line: licenseLineIdx +1, column: idCol + id.length }}
61
+ for (const part of parts)
62
+ if (!license.spdx.isValid(part))
63
+ return context.report({ loc, messageId: 'invalidSPDX', data: { id: part }})
64
+ if (allowedLicenses.length) {
65
+ for (const part of parts)
66
+ if (!allowedLicenses.includes(part))
67
+ return context.report({ loc, messageId: 'disallowedSPDX',
68
+ data: { id: part, allowed: allowedLicenses.join(', ') }})
69
+ }
70
+ }
71
+ }
72
+ }
73
+ }
@@ -0,0 +1,114 @@
1
+ import metadata from '../utils/metadata.js'
2
+ import regex from '../data/regex.js'
3
+
4
+ const re = {
5
+ matchPattern: /^(\*|http\*?|https?|file|ftp):\/\/([^/]*)(\/.*)$/,
6
+ regexForm: /^\/.+\/[a-z]*$/i
7
+ }
8
+
9
+ const urlKeys = new Set(['homepage', 'require', 'require-css', 'source', 'website']),
10
+ matchKeys = new Set(['match', 'exclude-match']),
11
+ regexOrMatchKeys = new Set(['include', 'exclude'])
12
+
13
+ export default {
14
+ meta: {
15
+ type: 'problem',
16
+ docs: {
17
+ description: 'Validates URLs and match patterns in metadata headers',
18
+ category: 'Possible Errors',
19
+ recommended: true
20
+ },
21
+ defaultOptions: [{
22
+ checkURLfields: true,
23
+ checkMatchPatterns: true,
24
+ allowedSchemes: ['http', 'https', 'file', 'ftp', 'http*'],
25
+ allowBroadPatterns: false
26
+ }],
27
+ schema: [{
28
+ type: 'object',
29
+ properties: {
30
+ checkURLfields: {
31
+ type: 'boolean',
32
+ description: 'Validate URL fields (`@homepage`, `@require`, `@resource`, etc.)'
33
+ },
34
+ checkMatchPatterns: {
35
+ type: 'boolean',
36
+ description: 'Validate match patterns (`@match`, `@include`, `@exclude`, `@exclude-match`)'
37
+ },
38
+ allowedSchemes: {
39
+ type: 'array', items: { type: 'string' },
40
+ description: 'Permitted URL schemes for match patterns'
41
+ },
42
+ allowBroadPatterns: {
43
+ type: 'boolean',
44
+ description: 'Allow unrestricted patterns (`<all_urls>`, `*://*/*`, `http*://*/*`, `https://*/*`)'
45
+ }
46
+ },
47
+ additionalProperties: false
48
+ }],
49
+ messages: {
50
+ invalidURL: '`@{{field}}` has an invalid URL: "{{value}}".',
51
+ invalidMatchPattern:'`@{{field}}` has an invalid match pattern: "{{value}}".',
52
+ invalidScheme: '`@{{field}}` uses a disallowed scheme: "{{value}}".',
53
+ invalidFileHost: '`@{{field}}` uses a `file://` scheme w/ a non-empty host: "{{value}}".',
54
+ invalidHost: '`@{{field}}` has an invalid wildcard host: "{{value}}". Use `*.` prefix instead.',
55
+ allURLs: '`@{{field}}` uses the unrestricted pattern "{{value}}".'
56
+ }
57
+ },
58
+
59
+ create(context) {
60
+ const opts = context.options[0] ?? {},
61
+ checkURLfields = opts.checkURLfields !== false,
62
+ checkMatchPatterns = opts.checkMatchPatterns !== false,
63
+ allowedSchemes = new Set(opts.allowedSchemes ?? ['http', 'https', 'file', 'ftp', 'http*']),
64
+ allowBroadPatterns = opts.allowBroadPatterns === true
65
+ if (!checkURLfields && !checkMatchPatterns) return {}
66
+ function validateMatchPattern(value, field, node) {
67
+ if (value == '<all_urls>') {
68
+ if (!allowBroadPatterns)
69
+ context.report({ node, messageId: 'allURLs', data: { field, value }})
70
+ return
71
+ }
72
+ const m = value.match(re.matchPattern)
73
+ if (!m) {
74
+ if (regexOrMatchKeys.has(field) && re.regexForm.test(value)) return
75
+ context.report({ node, messageId: 'invalidMatchPattern', data: { field, value }})
76
+ return
77
+ }
78
+ const [, scheme, host, path] = m
79
+ if (scheme != '*' && !allowedSchemes.has(scheme))
80
+ context.report({ node, messageId: 'invalidScheme', data: { field, value }})
81
+ else if (scheme == 'file' && host != '')
82
+ context.report({ node, messageId: 'invalidFileHost', data: { field, value }})
83
+ else if (host.includes('*') && host != '*' && !host.startsWith('*.'))
84
+ context.report({ node, messageId: 'invalidHost', data: { field, value }})
85
+ else if (host == '*' && path == '/*' && !allowBroadPatterns)
86
+ context.report({ node, messageId: 'allURLs', data: { field, value }})
87
+ }
88
+ function validateURL(url, field, node) {
89
+ try { new URL(url) }
90
+ catch { context.report({ node, messageId: 'invalidURL', data: { field, value: url }}) }
91
+ }
92
+ return {
93
+ Program() {
94
+ for (const entry of metadata.extract(context.sourceCode)) {
95
+ const key = entry.key, lower = key.toLowerCase()
96
+ if (checkURLfields) {
97
+ if (key == 'resource') {
98
+ const tokens = entry.value.trim().split(regex.whitespace.all),
99
+ url = tokens[1]
100
+ if (url) validateURL(url, key, entry.comment)
101
+ } else if (urlKeys.has(key) || lower.endsWith('url')) {
102
+ const url = entry.value.trim()
103
+ if (url) validateURL(url, key, entry.comment)
104
+ }
105
+ }
106
+ if (checkMatchPatterns && (matchKeys.has(key) || regexOrMatchKeys.has(key))) {
107
+ const value = entry.value.trim()
108
+ if (value) validateMatchPattern(value, key, entry.comment)
109
+ }
110
+ }
111
+ }
112
+ }
113
+ }
114
+ }
@@ -0,0 +1,83 @@
1
+ import metadata from '../utils/metadata.js'
2
+
3
+ const re = { // oxlint-disable security/detect-unsafe-regex
4
+ date: /^\d{4}\.(?:0?[1-9]|1[0-2])\.(?:0?[1-9]|[12]\d|3[01])(?:\.\d+)?$/,
5
+ semver: /^\d+\.\d+\.\d+(?:-[\da-z.-]+)?(?:\+[\da-z.-]+)?$/i
6
+ } // oxlint-enable security/detect-unsafe-regex
7
+
8
+ const examples = { date: 'YYYY.M.D or YYYY.M.D.N', semver: '1.2.3' }
9
+
10
+ export default {
11
+ meta: {
12
+ type: 'problem',
13
+ docs: {
14
+ description: 'Validates the @version metadata field',
15
+ category: 'Possible Errors',
16
+ recommended: true
17
+ },
18
+ defaultOptions: [{ versionType: 'semver', allowFutureDate: false, timezone: '' }],
19
+ schema: [{
20
+ type: 'object',
21
+ properties: {
22
+ versionType: {
23
+ type: 'string',
24
+ enum: ['semver', 'date'],
25
+ description: 'Version format to enforce'
26
+ },
27
+ allowFutureDate: {
28
+ type: 'boolean',
29
+ description: 'Allow `@version` dates later than the current date'
30
+ },
31
+ timezone: {
32
+ type: 'string',
33
+ description: 'IANA timezone for determining the current date '
34
+ + '(when omitted, only year/month are checked)'
35
+ }
36
+ },
37
+ additionalProperties: false
38
+ }],
39
+ messages: {
40
+ invalidVersion: 'Invalid @version: must be a valid {{versionType}} version '
41
+ + '(expected format: {{example}}).',
42
+ futureVersion: 'Invalid @version: date cannot be in the future.'
43
+ }
44
+ },
45
+
46
+ create(context) {
47
+ const opts = context.options[0] ?? {},
48
+ versionType = opts.versionType ?? 'semver',
49
+ allowFutureDate = opts.allowFutureDate ?? false,
50
+ timezone = opts.timezone ?? '',
51
+ example = examples[versionType] // oxlint-disable-line security/detect-object-injection
52
+ const now = timezone
53
+ ? Object.fromEntries(new Intl.DateTimeFormat('en', {
54
+ timeZone: timezone, year: 'numeric', month: 'numeric', day: 'numeric'
55
+ }).formatToParts().filter(({ type }) => ['year', 'month', 'day'].includes(type))
56
+ .map(({ type, value }) => [type, Number(value)]))
57
+ : { year: new Date().getUTCFullYear(), month: new Date().getUTCMonth() + 1 }
58
+ function validate(node, version) {
59
+ if (typeof version != 'string') return
60
+ if (versionType == 'semver') {
61
+ if (!re.semver.test(version))
62
+ context.report({ node, messageId: 'invalidVersion', data: { versionType, example }})
63
+ return
64
+ }
65
+ if (versionType == 'date') {
66
+ if (!re.date.test(version))
67
+ return context.report({ node, messageId: 'invalidVersion', data: { versionType, example }})
68
+ if (allowFutureDate) return
69
+ const [year, month, day] = version.split('.').map(Number)
70
+ if (year > now.year
71
+ || year == now.year && month > now.month
72
+ || timezone && year == now.year && month == now.month && day > now.day
73
+ ) context.report({ node, messageId: 'futureVersion' })
74
+ }
75
+ }
76
+ return {
77
+ Program() {
78
+ for (const entry of metadata.extract(context.sourceCode))
79
+ if (entry.key == 'version') validate(entry.comment, entry.value)
80
+ }
81
+ }
82
+ }
83
+ }
@@ -0,0 +1,11 @@
1
+ import apis from '../data/apis.js'
2
+
3
+ export default {
4
+ supports({ manager, api }) {
5
+ return !apis.all.has(api) ? false
6
+ : api.startsWith('CAT') ? manager == 'scriptcat'
7
+ : apis.tm.has(api) ? manager == 'tampermonkey'
8
+ : manager == 'adguard' ? apis.ag.has(api)
9
+ : true
10
+ }
11
+ }
@@ -0,0 +1 @@
1
+ export default { to(val) { return Array.isArray(val) ? val : val == null ? [] : [val] }}
@@ -1,5 +1,5 @@
1
1
  export default {
2
- initAlias(plugin, [oldName, alias]) { /* eslint-disable security/detect-object-injection */
2
+ initAlias(plugin, [oldName, alias]) { // oxlint-disable security/detect-object-injection
3
3
  const routes = typeof alias == 'string' ? { [alias]: null } // 'old-rule': 'new-rule'
4
4
  : Array.isArray(alias) ? Object.fromEntries(alias.map(entry =>
5
5
  typeof entry == 'string' ? [entry, null] // ['new-rule-1', 'new-rule-2']
@@ -72,5 +72,5 @@ export default {
72
72
  }))
73
73
  }
74
74
  }
75
- } /* eslint-enable security/detect-object-injection */
75
+ } // oxlint-enable security/detect-object-injection
76
76
  }
package/src/utils/init.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import deprecations from './deprecations.js'
2
+ import pkg from './pkg.js'
2
3
  import rulesUtils from './rules.js'
3
4
 
5
+ import headers from '../data/headers.js'
4
6
  import { rules as deprecationsData } from '../data/deprecations.js'
5
7
 
6
8
  import metaOrder from '../rules/meta-order.js'
@@ -12,12 +14,14 @@ import noInvalidGrants from '../rules/no-invalid-grants.js'
12
14
  import noInvalidHeaders from '../rules/no-invalid-headers.js'
13
15
  import noMissingGrants from '../rules/no-missing-grants.js'
14
16
  import noMissingResources from '../rules/no-missing-resources.js'
17
+ import noSensitiveGrants from '../rules/no-sensitive-grants.js'
15
18
  import noUnusedGrants from '../rules/no-unused-grants.js'
16
19
  import noUnusedResources from '../rules/no-unused-resources.js'
17
20
  import preferGMaddStyle from '../rules/prefer-gm-add-style.js'
18
21
  import preferMatch from '../rules/prefer-match.js'
19
22
  import requireBlankLineAfterMetaBlock from '../rules/require-blank-line-after-meta-block.js'
20
23
  import requireHomepagePair from '../rules/require-homepage-pair.js'
24
+ import requireLicenseHeader from '../rules/require-license-header.js'
21
25
  import requireMatchingLocalizedFields from '../rules/require-matching-localized-fields.js'
22
26
  import requireRequiredFields from '../rules/require-required-fields.js'
23
27
  import requireSpaceBeforeMetaKey from '../rules/require-space-before-meta-key.js'
@@ -25,9 +29,13 @@ import requireSRIhashing from '../rules/require-sri-hashing.js'
25
29
  import requireUpdateDownloadPair from '../rules/require-update-download-pair.js'
26
30
  import requireUserJsFilename from '../rules/require-user-js-filename.js'
27
31
  import validFieldValues from '../rules/valid-field-values.js'
32
+ import validIconHeaders from '../rules/valid-icon-headers.js'
33
+ import validLicenseHeader from '../rules/valid-license-header.js'
34
+ import validUrlHeaders from '../rules/valid-url-headers.js'
35
+ import validVersionHeader from '../rules/valid-version-header.js'
28
36
 
29
- const namespace = 'greasemonkey',
30
- managers = ['tampermonkey', 'violentmonkey', 'greasemonkey', 'scriptcat', 'adguard']
37
+ const pkgName = pkg.getName(),
38
+ namespace = pkgName.replace(/^@[^/]+\//, '').replace(/^eslint-plugin-/, '')
31
39
 
32
40
  export default {
33
41
  configs(plugin) {
@@ -38,10 +46,11 @@ export default {
38
46
  recommended: createConfig(rule => rule.meta.docs.recommended),
39
47
  strict: createConfig(() => true),
40
48
  problem: createConfig(rule => rule.meta.type == 'problem'),
41
- security: createConfig(rule => rule.meta.docs.security)
49
+ security: createConfig(rule => rule.meta.docs.security),
50
+ stylistic: createConfig(rule => rule.meta.docs.category == 'Stylistic Issues')
42
51
  }
43
- for (const manager of managers)
44
- plugin.configs[manager] = { // eslint-disable-line security/detect-object-injection
52
+ for (const manager of headers.managers)
53
+ plugin.configs[manager] = { // oxlint-disable-line security/detect-object-injection
45
54
  plugins,
46
55
  rules: {
47
56
  ...plugin.configs.recommended.rules,
@@ -58,7 +67,7 @@ export default {
58
67
  deprecations.initAlias(plugin, entry)
59
68
  },
60
69
 
61
- plugin() { return { rules: {}, configs: {} }},
70
+ plugin() { return { meta: { name: pkgName }, rules: {}, configs: {} }},
62
71
 
63
72
  rules(plugin) {
64
73
  plugin.rules = {
@@ -71,21 +80,27 @@ export default {
71
80
  'no-invalid-headers': noInvalidHeaders,
72
81
  'no-missing-grants': noMissingGrants,
73
82
  'no-missing-resources': noMissingResources,
83
+ 'no-sensitive-grants': noSensitiveGrants,
74
84
  'no-unused-grants': noUnusedGrants,
75
85
  'no-unused-resources': noUnusedResources,
76
86
  'prefer-gm-add-style': preferGMaddStyle,
77
87
  'prefer-match': preferMatch,
78
88
  'require-blank-line-after-meta-block': requireBlankLineAfterMetaBlock,
79
89
  'require-homepage-pair': requireHomepagePair,
90
+ 'require-license-header': requireLicenseHeader,
80
91
  'require-matching-localized-fields': requireMatchingLocalizedFields,
81
92
  'require-required-fields': requireRequiredFields,
82
93
  'require-space-before-meta-key': requireSpaceBeforeMetaKey,
83
94
  'require-sri-hashing': requireSRIhashing,
84
95
  'require-update-download-pair': requireUpdateDownloadPair,
85
96
  'require-user-js-filename': requireUserJsFilename,
86
- 'valid-field-values': validFieldValues
97
+ 'valid-field-values': validFieldValues,
98
+ 'valid-icon-headers': validIconHeaders,
99
+ 'valid-license-header': validLicenseHeader,
100
+ 'valid-url-headers': validUrlHeaders,
101
+ 'valid-version-header': validVersionHeader
87
102
  }
88
103
  for (const [ruleId, rule] of Object.entries(plugin.rules))
89
- rule.meta.docs.url = `https://codeberg.org/adamlui/eslint-plugin-${namespace}/#${ruleId}`
104
+ rule.meta.docs.url = `https://codeberg.org/adamlui/${pkgName}/#${ruleId}`
90
105
  }
91
106
  }