eslint-plugin-greasemonkey 1.8.4 → 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.
Files changed (45) hide show
  1. package/README.md +776 -68
  2. package/docs/LICENSE.md +1 -1
  3. package/package.json +53 -43
  4. package/src/data/apis.js +56 -53
  5. package/src/data/headers.js +65 -31
  6. package/src/data/regex.js +14 -0
  7. package/src/index.d.ts +34 -3
  8. package/src/index.js +5 -80
  9. package/src/rules/meta-order.js +5 -6
  10. package/src/rules/meta-spacing.js +11 -10
  11. package/src/rules/no-duplicate-keys.js +2 -2
  12. package/src/rules/no-insecure-fetch.js +108 -0
  13. package/src/rules/no-insecure-messaging.js +67 -0
  14. package/src/rules/no-invalid-grants.js +12 -10
  15. package/src/rules/no-invalid-headers.js +11 -11
  16. package/src/rules/no-missing-grants.js +6 -8
  17. package/src/rules/no-missing-resources.js +10 -8
  18. package/src/rules/no-sensitive-grants.js +81 -0
  19. package/src/rules/no-unused-grants.js +2 -2
  20. package/src/rules/no-unused-resources.js +10 -8
  21. package/src/rules/prefer-gm-add-style.js +67 -0
  22. package/src/rules/prefer-match.js +4 -3
  23. package/src/rules/require-homepage-pair.js +3 -4
  24. package/src/rules/require-license-header.js +27 -0
  25. package/src/rules/require-matching-localized-fields.js +2 -2
  26. package/src/rules/require-required-fields.js +2 -2
  27. package/src/rules/require-space-before-meta-key.js +4 -5
  28. package/src/rules/require-sri-hashing.js +3 -3
  29. package/src/rules/require-update-download-pair.js +3 -4
  30. package/src/rules/require-user-js-filename.js +2 -2
  31. package/src/rules/valid-field-values.js +7 -7
  32. package/src/rules/valid-icon-headers.js +74 -0
  33. package/src/rules/valid-license-header.js +73 -0
  34. package/src/rules/valid-url-headers.js +114 -0
  35. package/src/rules/valid-version-header.js +83 -0
  36. package/src/utils/api.js +11 -0
  37. package/src/utils/array.js +1 -0
  38. package/src/utils/deprecations.js +76 -0
  39. package/src/utils/init.js +106 -0
  40. package/src/utils/license.js +56 -0
  41. package/src/utils/metadata.js +31 -0
  42. package/src/utils/pkg.js +21 -0
  43. package/src/utils/rules.js +7 -0
  44. package/src/utils/template.js +21 -0
  45. package/src/utils.js +0 -119
package/docs/LICENSE.md CHANGED
@@ -1,4 +1,4 @@
1
- # 🏛️ MIT License
1
+ # MIT License
2
2
 
3
3
  **Copyright © 2026 [Adam Lui](https://codeberg.org/adamlui) & contributors.**
4
4
 
package/package.json CHANGED
@@ -1,22 +1,26 @@
1
1
  {
2
2
  "name": "eslint-plugin-greasemonkey",
3
- "version": "1.8.4",
3
+ "version": "1.10.0",
4
4
  "description": "ESLint rules for Greasemonkey/Tampermonkey userscripts",
5
- "type": "module",
6
- "engines": {
7
- "node": ">=18.18.0"
8
- },
9
- "author": {
10
- "name": "Adam Lui",
11
- "email": "adam@kudoai.org",
12
- "url": "https://codeberg.org/adamlui"
13
- },
5
+ "keywords": [
6
+ "eslint",
7
+ "eslint-plugin",
8
+ "eslintplugin",
9
+ "greasemonkey",
10
+ "metadata",
11
+ "security",
12
+ "tampermonkey",
13
+ "userscript"
14
+ ],
14
15
  "homepage": "https://codeberg.org/adamlui/eslint-plugin-greasemonkey/#readme",
15
16
  "bugs": {
16
17
  "url": "https://codeberg.org/adamlui/eslint-plugin-greasemonkey/issues",
17
18
  "email": "support@adamlui.com"
18
19
  },
19
- "license": "MIT",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://codeberg.org/adamlui/eslint-plugin-greasemonkey.git"
23
+ },
20
24
  "funding": [
21
25
  {
22
26
  "type": "ko-fi",
@@ -27,57 +31,63 @@
27
31
  "url": "https://paypal.me/adamlui"
28
32
  }
29
33
  ],
30
- "main": "src/index.js",
34
+ "license": "MIT",
35
+ "author": {
36
+ "name": "Adam Lui",
37
+ "email": "adam@kudoai.org",
38
+ "url": "https://codeberg.org/adamlui"
39
+ },
40
+ "type": "module",
41
+ "exports": {
42
+ ".": {
43
+ "types": "./src/index.d.ts",
44
+ "default": "./src/index.js"
45
+ }
46
+ },
47
+ "main": "./src/index.js",
48
+ "types": "./src/index.d.ts",
31
49
  "files": [
32
50
  "docs/",
33
51
  "src/"
34
52
  ],
35
- "types": "src/index.d.ts",
36
53
  "scripts": {
37
- "lint": "eslint . --cache && yamllint .",
38
- "lint:all": "eslint . && yamllint .",
39
- "lint:fix": "eslint . --fix --cache && yamllint .",
40
- "lint:fix-all": "eslint . --fix && yamllint .",
41
- "test": "node tests/run.js",
42
- "demo": "node demo/run.js",
43
- "bump:patch": "bash utils/bump.sh patch",
44
- "bump:minor": "bash utils/bump.sh minor",
45
54
  "bump:feat": "npm run bump:minor",
46
- "bump:major": "bash utils/bump.sh major"
55
+ "bump:major": "bash utils/bump.sh major",
56
+ "bump:minor": "bash utils/bump.sh minor",
57
+ "bump:patch": "bash utils/bump.sh patch",
58
+ "demo": "eslint demo",
59
+ "lint": "pre-commit run",
60
+ "lint:all": "pre-commit run --all-files",
61
+ "test": "node tests/run"
47
62
  },
48
- "repository": {
49
- "type": "git",
50
- "url": "git+https://codeberg.org/adamlui/eslint-plugin-greasemonkey.git"
51
- },
52
- "keywords": [
53
- "eslint",
54
- "eslint-plugin",
55
- "eslintplugin",
56
- "greasemonkey",
57
- "metadata",
58
- "tampermonkey",
59
- "userscript"
60
- ],
61
- "peerDependencies": {
62
- "eslint": ">=9.0.0"
63
+ "dependencies": {
64
+ "spdx-license-ids": "^3.0.24",
65
+ "spdx-license-list": "^6.12.0"
63
66
  },
64
67
  "devDependencies": {
65
68
  "@e18e/eslint-plugin": "^0.8.1",
66
- "@eslint/js": "^10.0.1",
67
69
  "@eslint/json": "^2.1.1",
68
70
  "@eslint/markdown": "^7.5.1",
69
71
  "@stylistic/eslint-plugin": "^5.10.0",
70
- "eslint": "^10.12.0",
71
- "eslint-plugin-constructors": "^1.4.2",
72
+ "eslint": "^10.13.0",
73
+ "eslint-plugin-constructors": "^1.5.0",
72
74
  "eslint-plugin-eslint-plugin": "^7.6.2",
73
75
  "eslint-plugin-import-x": "^4.17.1",
74
- "eslint-plugin-n": "^18.4.1",
76
+ "eslint-plugin-n": "^18.5.0",
75
77
  "eslint-plugin-no-secrets": "^2.3.3",
76
78
  "eslint-plugin-no-unsanitized": "^4.1.5",
79
+ "eslint-plugin-package-json": "^1.10.1",
77
80
  "eslint-plugin-promise": "^7.3.0",
78
81
  "eslint-plugin-regexp": "^3.3.1",
79
82
  "eslint-plugin-security": "^4.2.0",
80
- "globals": "^17.13.0",
81
- "yamllint-js": "^0.2.4"
83
+ "eslint-plugin-sonarjs": "^4.2.2",
84
+ "eslint-plugin-woodpecker": "^1.1.0",
85
+ "oxlint": "^1.89.0"
86
+ },
87
+ "peerDependencies": {
88
+ "eslint": ">=9.0.0"
89
+ },
90
+ "engines": {
91
+ "node": ">=18.18.0"
82
92
  }
83
93
  }
package/src/data/apis.js CHANGED
@@ -1,54 +1,57 @@
1
- export const gmLegacyAPIs = new Set([ /* eslint-disable no-secrets/no-secrets */
2
- 'GM_addElement', 'GM_addStyle', 'GM_addValueChangeListener', 'GM_deleteValue', 'GM_download',
3
- 'GM_getResourceText', 'GM_getResourceURL', 'GM_getTab', 'GM_getTabs', 'GM_getValue', 'GM_getValues',
4
- 'GM_info', 'GM_listValues', 'GM_log', 'GM_notification', 'GM_openInTab', 'GM_registerMenuCommand',
5
- 'GM_removeValueChangeListener', 'GM_saveTab', 'GM_setClipboard', 'GM_setValue', 'GM_setValues',
6
- 'GM_unregisterMenuCommand', 'GM_webRequest', 'GM_xmlhttpRequest'
7
- ]) /* eslint-enable no-secrets/no-secrets */
8
-
9
- export const gmModernAPIs = new Set([
10
- 'GM.addElement', 'GM.addStyle', 'GM.addValueChangeListener', 'GM.deleteValue', 'GM.download',
11
- 'GM.getResourceText', 'GM.getResourceUrl', 'GM.getTab', 'GM.getTabs', 'GM.getValue', 'GM.getValues',
12
- 'GM.info', 'GM.listValues', 'GM.log', 'GM.notification', 'GM.openInTab', 'GM.registerMenuCommand',
13
- 'GM.removeValueChangeListener', 'GM.saveTab', 'GM.setClipboard', 'GM.setValue', 'GM.setValues',
14
- 'GM.unregisterMenuCommand', 'GM.webRequest', 'GM.xmlHttpRequest'
15
- ])
16
-
17
- export const scAPIs = new Set([
18
- 'CAT.agent.conversation', 'CAT.agent.dom', 'CAT.agent.mcp', 'CAT.agent.model',
19
- 'CAT.agent.opfs', 'CAT.agent.skills', 'CAT.agent.task',
20
- 'CAT_fileStorage', 'CAT_scriptLoaded', 'CAT_userConfig'
21
- ])
22
-
23
- const tmAPIs = new Set([
24
- 'GM_getTab', 'GM_getTabs', 'GM_saveTab', 'GM_webRequest',
25
- 'GM.getTab', 'GM.getTabs', 'GM.saveTab', 'GM.webRequest'
26
- ])
27
-
28
- const agAPIs = new Set([
29
- 'GM_info', 'GM.info',
30
- 'GM_setValue', 'GM.setValue',
31
- 'GM_getValue', 'GM.getValue',
32
- 'GM_listValues', 'GM.listValues',
33
- 'GM_deleteValue', 'GM.deleteValue',
34
- 'GM_getResourceURL', 'GM.getResourceUrl',
35
- 'GM_setClipboard', 'GM.setClipboard',
36
- 'GM_xmlhttpRequest', 'GM.xmlHttpRequest',
37
- 'GM_openInTab', 'GM.openInTab',
38
- 'GM_notification',
39
- 'unsafeWindow',
40
- 'GM_getResourceText',
41
- 'GM_addStyle',
42
- 'GM_log',
43
- 'GM_addElement', 'GM.addElement'
44
- ])
45
-
46
- export const allAPIs = new Set([...gmLegacyAPIs, ...gmModernAPIs, ...scAPIs])
47
-
48
- export function supportsAPI({ manager, api }) {
49
- return !allAPIs.has(api) ? false
50
- : api.startsWith('CAT') ? manager == 'scriptcat'
51
- : tmAPIs.has(api) ? manager == 'tampermonkey'
52
- : manager == 'adguard' ? agAPIs.has(api)
53
- : true
1
+ const apis = {
2
+ ag: new Set([
3
+ 'GM_info', 'GM.info', 'GM_setValue', 'GM.setValue', 'GM_getValue', 'GM.getValue',
4
+ 'GM_listValues', 'GM.listValues', 'GM_deleteValue', 'GM.deleteValue',
5
+ 'GM_getResourceURL', 'GM.getResourceUrl', 'GM_setClipboard', 'GM.setClipboard',
6
+ 'GM_xmlhttpRequest', 'GM.xmlHttpRequest', 'GM_openInTab', 'GM.openInTab',
7
+ 'GM_notification', 'unsafeWindow', 'GM_getResourceText', 'GM_addStyle', 'GM_log',
8
+ 'GM_addElement', 'GM.addElement'
9
+ ]),
10
+ gm: {
11
+ legacy: new Set([
12
+ 'GM_addElement', 'GM_addStyle', 'GM_addValueChangeListener', 'GM_deleteValue', 'GM_download',
13
+ 'GM_getResourceText', 'GM_getResourceURL', 'GM_getTab', 'GM_getTabs', 'GM_getValue', 'GM_getValues',
14
+ 'GM_info', 'GM_listValues', 'GM_log', 'GM_notification', 'GM_openInTab', 'GM_registerMenuCommand',
15
+ 'GM_removeValueChangeListener', // oxlint-disable-line no-secrets/no-secrets
16
+ 'GM_saveTab', 'GM_setClipboard', 'GM_setValue', 'GM_setValues', 'GM_unregisterMenuCommand', 'GM_webRequest',
17
+ 'GM_xmlhttpRequest'
18
+ ]),
19
+ modern: new Set([
20
+ 'GM.addElement', 'GM.addStyle', 'GM.addValueChangeListener', 'GM.deleteValue', 'GM.download',
21
+ 'GM.getResourceText', 'GM.getResourceUrl', 'GM.getTab', 'GM.getTabs', 'GM.getValue', 'GM.getValues',
22
+ 'GM.info', 'GM.listValues', 'GM.log', 'GM.notification', 'GM.openInTab', 'GM.registerMenuCommand',
23
+ 'GM.removeValueChangeListener', 'GM.saveTab', 'GM.setClipboard', 'GM.setValue', 'GM.setValues',
24
+ 'GM.unregisterMenuCommand', 'GM.webRequest', 'GM.xmlHttpRequest'
25
+ ])
26
+ },
27
+ sc: new Set([
28
+ 'CAT.agent.conversation', 'CAT.agent.dom', 'CAT.agent.mcp', 'CAT.agent.model', 'CAT.agent.opfs',
29
+ 'CAT.agent.skills', 'CAT.agent.task', 'CAT_fileStorage', 'CAT_scriptLoaded', 'CAT_userConfig'
30
+ ]),
31
+ tm: new Set([
32
+ 'GM_getTab', 'GM_getTabs', 'GM_saveTab', 'GM_webRequest',
33
+ 'GM.getTab', 'GM.getTabs', 'GM.saveTab', 'GM.webRequest'
34
+ ]),
35
+ sensitive: new Set([
36
+ 'GM_xmlhttpRequest', 'GM.xmlHttpRequest', 'GM_download', 'GM.download',
37
+ 'GM_openInTab', 'GM.openInTab', 'GM_setClipboard', 'GM.setClipboard',
38
+ 'GM_notification', 'GM.notification', 'GM_addValueChangeListener', 'GM.addValueChangeListener',
39
+ 'GM_getResourceURL', 'GM.getResourceUrl', 'unsafeWindow'
40
+ ]),
41
+ managerSensitive: new Map([
42
+ ['tampermonkey', new Set([
43
+ 'GM_webRequest', 'GM.webRequest', 'GM_getTab', 'GM.getTab', 'GM_getTabs', 'GM.getTabs',
44
+ 'GM_saveTab', 'GM.saveTab'
45
+ ])],
46
+ ['violentmonkey', new Set([])],
47
+ ['greasemonkey', new Set([])],
48
+ ['scriptcat', new Set([
49
+ 'CAT.agent.conversation', 'CAT.agent.dom', 'CAT.agent.mcp', 'CAT.agent.model', 'CAT.agent.opfs',
50
+ 'CAT.agent.skills', 'CAT.agent.task', 'CAT_fileStorage', 'CAT_userConfig'
51
+ ])],
52
+ ['adguard', new Set([])]
53
+ ])
54
54
  }
55
+ apis.all = new Set([...apis.gm.legacy, ...apis.gm.modern, ...apis.sc])
56
+
57
+ export default apis
@@ -1,36 +1,70 @@
1
- export const universalHeaders = new Set([
2
- 'antifeature', 'author', 'compatible', 'connect', 'contributionAmount', 'contributionURL', 'copyright',
3
- 'defaulticon', 'description', 'downloadURL', 'exclude', 'exclude-match', 'grant', 'homepage', 'homepageURL',
4
- 'icon', 'icon64', 'include', 'license', 'match', 'name', 'namespace', 'noframes', 'require', 'resource',
5
- 'run-at', 'source', 'supportURL', 'updateURL', 'version', 'website'
6
- ])
1
+ // oxlint-disable sonarjs/no-clear-text-protocols
7
2
 
8
- export const managerHeaders = new Map([
9
- ['unwrap', ['tampermonkey', 'violentmonkey', 'greasemonkey', 'scriptcat']],
10
- ['run-in', ['tampermonkey', 'scriptcat']],
11
- ['nocompat', ['tampermonkey']],
12
- ['sandbox', ['tampermonkey']],
13
- ['user-agent', ['tampermonkey']],
14
- ['webRequest', ['tampermonkey']],
15
- ['inject-into', ['violentmonkey', 'scriptcat']],
16
- ['top-level-await', ['violentmonkey']],
17
- ['background', ['scriptcat']],
18
- ['crontab', ['scriptcat']],
19
- ['definition', ['scriptcat']],
20
- ['early-start', ['scriptcat']],
21
- ['require-css', ['scriptcat']],
22
- ['storageName', ['scriptcat']]
23
- ])
24
-
25
- export const localizedHeaders = ['name', 'description', 'antifeature']
26
- export const managers = ['tampermonkey', 'violentmonkey', 'greasemonkey', 'scriptcat', 'adguard']
27
- export const validHeaders = new Map()
3
+ const headers = {
4
+ broadConnect: new Set([
5
+ '*',
6
+ '*.com', '*.org', '*.net', '*.io', '*.dev', '*.app', '*.co',
7
+ 'http://*', 'https://*',
8
+ 'http://*/*', 'https://*/*',
9
+ 'http://*/', 'https://*/',
10
+ 'http:*', 'https:*'
11
+ ]),
12
+ localized: ['name', 'description', 'antifeature'],
13
+ manager: new Map([
14
+ ['unwrap', ['tampermonkey', 'violentmonkey', 'greasemonkey', 'scriptcat']],
15
+ ['run-in', ['tampermonkey', 'scriptcat']],
16
+ ['nocompat', ['tampermonkey']],
17
+ ['sandbox', ['tampermonkey']],
18
+ ['user-agent', ['tampermonkey']],
19
+ ['webRequest', ['tampermonkey']],
20
+ ['inject-into', ['violentmonkey', 'scriptcat']],
21
+ ['top-level-await', ['violentmonkey']],
22
+ ['background', ['scriptcat']],
23
+ ['crontab', ['scriptcat']],
24
+ ['definition', ['scriptcat']],
25
+ ['early-start', ['scriptcat']],
26
+ ['require-css', ['scriptcat']],
27
+ ['storageName', ['scriptcat']]
28
+ ]),
29
+ managers: ['tampermonkey', 'violentmonkey', 'greasemonkey', 'scriptcat', 'adguard'],
30
+ universal: new Set([
31
+ 'antifeature', 'author', 'compatible', 'connect', 'contributionAmount', 'contributionURL', 'copyright',
32
+ 'defaulticon', 'description', 'downloadURL', 'exclude', 'exclude-match', 'grant', 'homepage', 'homepageURL',
33
+ 'icon', 'icon64', 'include', 'license', 'match', 'name', 'namespace', 'noframes', 'require', 'resource',
34
+ 'run-at', 'source', 'supportURL', 'updateURL', 'version', 'website'
35
+ ])
36
+ }
28
37
 
29
- validHeaders.set('all', new Set([...universalHeaders, ...managerHeaders.keys()]))
38
+ const knownHeaders = new Set([...headers.universal, ...headers.manager.keys()])
30
39
 
31
- for (const manager of managers) {
32
- const specific = [...managerHeaders]
33
- .filter(([, supportedBy]) => supportedBy.includes(manager))
40
+ headers.valid = new Map([['all', knownHeaders]])
41
+ for (const mgr of headers.managers) {
42
+ const specific = [...headers.manager]
43
+ .filter(([, supportedBy]) => supportedBy.includes(mgr))
34
44
  .map(([header]) => header)
35
- validHeaders.set(manager, new Set([...universalHeaders, ...specific]))
45
+ headers.valid.set(mgr, new Set([...headers.universal, ...specific]))
36
46
  }
47
+
48
+ headers.url = new Map(Object.entries({
49
+ connect: 'host-pattern',
50
+ contributionURL: 'url',
51
+ defaulticon: 'url',
52
+ downloadURL: 'url',
53
+ exclude: 'pattern',
54
+ 'exclude-match': 'pattern',
55
+ homepage: 'url',
56
+ homepageURL: 'url',
57
+ icon: 'url',
58
+ icon64: 'url',
59
+ include: 'pattern',
60
+ match: 'pattern',
61
+ require: 'url',
62
+ 'require-css': 'url',
63
+ resource: 'named-url',
64
+ source: 'url',
65
+ supportURL: 'url',
66
+ updateURL: 'url',
67
+ website: 'url'
68
+ }).filter(([name]) => knownHeaders.has(name)))
69
+
70
+ export default headers
@@ -0,0 +1,14 @@
1
+ export default {
2
+ filename: {
3
+ license: // oxlint-disable-next-line security/detect-unsafe-regex
4
+ /^(?:[\w.-]*-)?(?:LICEN[SC]E|COPYING)(?:[-.][\w.-]+)?$/i
5
+ },
6
+ meta: {
7
+ block: /\/\/\s*==UserScript==([\s\S]*?)\/\/\s*==\/UserScript==/,
8
+ license: /^[ \t]*\/\/[ \t]*@license[ \t]+(\S.*)/m
9
+ },
10
+ newline: /\r?\n/,
11
+ specialChars: /[.*+?^${}()|[\]\\]/g,
12
+ url: /^https?:\/\//,
13
+ whitespace: { all: /\s+/, leading: /^\s*/ }
14
+ }
package/src/index.d.ts CHANGED
@@ -1,6 +1,37 @@
1
+ type RuleSeverity = 0 | 1 | 2 | 'off' | 'warn' | 'error'
2
+ type RuleSetting = RuleSeverity | [RuleSeverity, ...unknown[]]
3
+
4
+ type ConfigName =
5
+ | 'recommended'
6
+ | 'strict'
7
+ | 'problem'
8
+ | 'tampermonkey'
9
+ | 'violentmonkey'
10
+ | 'greasemonkey'
11
+ | 'scriptcat'
12
+ | 'adguard'
13
+
14
+ interface PluginConfig {
15
+ plugins: Record<string, unknown>
16
+ rules: Record<string, RuleSetting>
17
+ }
18
+
19
+ interface PluginRule {
20
+ meta: {
21
+ type?: 'problem' | 'suggestion' | 'layout'
22
+ deprecated?: boolean | {
23
+ message?: string
24
+ replacedBy?: Array<{ rule: { name: string } }>
25
+ }
26
+ docs?: Record<string, unknown>
27
+ }
28
+ create: (context: unknown) => Record<string, (...args: unknown[]) => void>
29
+ }
30
+
1
31
  declare const plugin: {
2
- rules: Record<string, unknown>
3
- configs: { recommended: Record<string, unknown> }
32
+ meta: { name: string }
33
+ rules: Record<string, PluginRule>
34
+ configs: Record<ConfigName, PluginConfig>
4
35
  }
5
36
 
6
- export default plugin
37
+ export { plugin as default, plugin as greasemonkey }
package/src/index.js CHANGED
@@ -1,84 +1,9 @@
1
- /* eslint-disable security/detect-object-injection -- false positives */
1
+ import init from './utils/init.js'
2
2
 
3
- import { rules as oldRules } from './data/deprecations.js'
3
+ const plugin = init.plugin()
4
4
 
5
- import metaOrder from './rules/meta-order.js'
6
- import metaSpacing from './rules/meta-spacing.js'
7
- import noDuplicateKeys from './rules/no-duplicate-keys.js'
8
- import noInvalidGrants from './rules/no-invalid-grants.js'
9
- import noInvalidHeaders from './rules/no-invalid-headers.js'
10
- import noMissingGrants from './rules/no-missing-grants.js'
11
- import noMissingResources from './rules/no-missing-resources.js'
12
- import noUnusedGrants from './rules/no-unused-grants.js'
13
- import noUnusedResources from './rules/no-unused-resources.js'
14
- import preferMatch from './rules/prefer-match.js'
15
- import requireBlankLineAfterMetaBlock from './rules/require-blank-line-after-meta-block.js'
16
- import requireHomepagePair from './rules/require-homepage-pair.js'
17
- import requireMatchingLocalizedFields from './rules/require-matching-localized-fields.js'
18
- import requireRequiredFields from './rules/require-required-fields.js'
19
- import requireSpaceBeforeMetaKey from './rules/require-space-before-meta-key.js'
20
- import requireSRIhashing from './rules/require-sri-hashing.js'
21
- import requireUpdateDownloadPair from './rules/require-update-download-pair.js'
22
- import requireUserJsFilename from './rules/require-user-js-filename.js'
23
- import validFieldValues from './rules/valid-field-values.js'
24
-
25
- import { bundleRules, initDeprecatedAlias } from './utils.js'
26
-
27
- const plugin = {
28
- rules: {
29
- 'meta-order': metaOrder,
30
- 'meta-spacing': metaSpacing,
31
- 'no-duplicate-keys': noDuplicateKeys,
32
- 'no-invalid-grants': noInvalidGrants,
33
- 'no-invalid-headers': noInvalidHeaders,
34
- 'no-missing-grants': noMissingGrants,
35
- 'no-unused-grants': noUnusedGrants,
36
- 'no-missing-resources': noMissingResources,
37
- 'no-unused-resources': noUnusedResources,
38
- 'prefer-match': preferMatch,
39
- 'require-blank-line-after-meta-block': requireBlankLineAfterMetaBlock,
40
- 'require-homepage-pair': requireHomepagePair,
41
- 'require-matching-localized-fields': requireMatchingLocalizedFields,
42
- 'require-required-fields': requireRequiredFields,
43
- 'require-space-before-meta-key': requireSpaceBeforeMetaKey,
44
- 'require-sri-hashing': requireSRIhashing,
45
- 'require-update-download-pair': requireUpdateDownloadPair,
46
- 'require-user-js-filename': requireUserJsFilename,
47
- 'valid-field-values': validFieldValues
48
- }
49
- }
50
-
51
- for (const [ruleId, rule] of Object.entries(plugin.rules))
52
- rule.meta.docs.url = `https://codeberg.org/adamlui/eslint-plugin-greasemonkey/#${ruleId}`
53
-
54
- for (const entry of Object.entries(oldRules))
55
- initDeprecatedAlias(plugin, entry)
56
-
57
- plugin.configs = {
58
- recommended: {
59
- plugins: { greasemonkey: plugin },
60
- rules: bundleRules(plugin.rules, rule => rule.meta.docs.recommended, 'greasemonkey')
61
- },
62
- strict: {
63
- plugins: { greasemonkey: plugin },
64
- rules: bundleRules(plugin.rules, () => true, 'greasemonkey')
65
- },
66
- problem: {
67
- plugins: { greasemonkey: plugin },
68
- rules: bundleRules(plugin.rules, rule => rule.meta.type == 'problem', 'greasemonkey')
69
- }
70
- }
71
-
72
- for (const manager of ['tampermonkey', 'violentmonkey', 'greasemonkey', 'scriptcat', 'adguard'])
73
- plugin.configs[manager] = {
74
- plugins: { greasemonkey: plugin },
75
- rules: {
76
- ...plugin.configs.recommended.rules,
77
- 'greasemonkey/no-invalid-grants': ['error', { target: manager }],
78
- 'greasemonkey/no-invalid-headers': ['error', { target: manager }],
79
- ...(['tampermonkey', 'scriptcat', 'adguard'].includes(manager) &&
80
- { 'greasemonkey/require-update-download-pair': 'error' })
81
- }
82
- }
5
+ init.rules(plugin)
6
+ init.deprecations(plugin)
7
+ init.configs(plugin)
83
8
 
84
9
  export { plugin as default, plugin as greasemonkey }
@@ -1,4 +1,4 @@
1
- import { extractMetadata } from '../utils.js'
1
+ import metadata from '../utils/metadata.js'
2
2
 
3
3
  const defaultOrder = [
4
4
  'name', 'description', 'author', 'namespace', 'version', 'license', 'icon', 'icon64',
@@ -12,7 +12,7 @@ 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
15
+ for (let i = 1 ; i < known.length ; i++) // oxlint-disable-next-line security/detect-object-injection
16
16
  if (known[i -1].rank > known[i].rank) return [known[i -1], known[i]]
17
17
  }
18
18
 
@@ -30,8 +30,7 @@ export default {
30
30
  type: 'layout',
31
31
  docs: {
32
32
  description: 'Enforce an order for metadata keys',
33
- category: 'Stylistic Issues',
34
- recommended: false
33
+ category: 'Stylistic Issues'
35
34
  },
36
35
  fixable: 'code',
37
36
  schema: [{
@@ -54,12 +53,12 @@ export default {
54
53
  return {
55
54
  Program() {
56
55
  const sourceCode = context.sourceCode,
57
- entries = extractMetadata(sourceCode),
56
+ entries = metadata.extract(sourceCode),
58
57
  violation = findViolation(entries, master) ?? (custom.length && findViolation(entries, custom))
59
58
  if (!violation) return
60
59
  const [first, second] = violation
61
60
  const sorted = sortEntries(sortEntries(entries, master), custom)
62
- const replacements = // eslint-disable-next-line security/detect-object-injection
61
+ const replacements = // oxlint-disable-next-line security/detect-object-injection
63
62
  new Map(sorted.map((entry, idx) => [entries[idx], sourceCode.getText(entry.comment)]))
64
63
  const start = entries[0].comment.range[0]
65
64
  const end = entries.at(-1).comment.range[1]
@@ -1,6 +1,5 @@
1
- import { extractMetadata } from '../utils.js'
2
-
3
- const re = { leadingWhitespace: /^\s*/, whitespace: /\s+/ }
1
+ import metadata from '../utils/metadata.js'
2
+ import regex from '../data/regex.js'
4
3
 
5
4
  export default {
6
5
  meta: {
@@ -30,29 +29,31 @@ export default {
30
29
  return {
31
30
  Program() {
32
31
  const sourceCode = context.sourceCode,
33
- metaEntries = extractMetadata(sourceCode)
32
+ metaEntries = metadata.extract(sourceCode)
34
33
  if (!metaEntries.length) return
35
34
  let targetCol = metaEntries.reduce((max, entry) => Math.max(max, entry.key.length), 0) + minSpaces +4
36
35
  for (const entry of metaEntries)
37
36
  if (entry.key == 'resource' && entry.value.trim()) {
38
- const name = entry.value.trim().split(re.whitespace)[0],
37
+ const name = entry.value.trim().split(regex.whitespace.all)[0],
39
38
  requiredCol = 4 + entry.key.length +1 + name.length + minSpaces
40
39
  if (requiredCol > targetCol) targetCol = requiredCol
41
40
  }
42
41
  for (const entry of metaEntries) {
43
42
  if (!entry.value.trim()) continue
44
43
  const commentText = sourceCode.getText(entry.comment),
45
- keyIdx = commentText.indexOf(entry.key) + entry.key.length,
46
- parts = entry.value.trim().split(re.whitespace),
44
+ trimmed = commentText.trimEnd(),
45
+ trailingWs = commentText.slice(trimmed.length),
46
+ keyIdx = trimmed.indexOf(entry.key) + entry.key.length,
47
+ parts = entry.value.trim().split(regex.whitespace.all),
47
48
  named = entry.key == 'resource' && parts.length >= 2,
48
49
  prefix = named ? ` ${parts[0]} ` : '',
49
50
  suffix = named ? parts.slice(1).join(' ') : entry.value.trim(),
50
- fixedText = commentText.slice(0, keyIdx) + prefix
51
+ fixedText = trimmed.slice(0, keyIdx) + prefix
51
52
  + ' '.repeat(targetCol - keyIdx - prefix.length) + suffix
52
- if (commentText !== fixedText)
53
+ if (trimmed !== fixedText)
53
54
  context.report({
54
55
  node: entry.comment, messageId: 'misaligned', data: { column: targetCol },
55
- fix(fixer) { return fixer.replaceText(entry.comment, fixedText) }
56
+ fix(fixer) { return fixer.replaceText(entry.comment, fixedText + trailingWs) }
56
57
  })
57
58
  }
58
59
  }
@@ -1,4 +1,4 @@
1
- import { extractMetadata } from '../utils.js'
1
+ import metadata from '../utils/metadata.js'
2
2
 
3
3
  const multiValKeys = new Set([
4
4
  'compatible', 'connect', 'description', 'exclude', 'exclude-match', 'grant',
@@ -20,7 +20,7 @@ export default {
20
20
  create(context) {
21
21
  return {
22
22
  Program() {
23
- const metaEntries = extractMetadata(context.sourceCode),
23
+ const metaEntries = metadata.extract(context.sourceCode),
24
24
  seenEntries = new Set()
25
25
  for (const entry of metaEntries) {
26
26
  const baseKey = entry.key.split(':')[0]