eslint-plugin-chrome-extensions 2.6.0 → 2.8.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 (50) hide show
  1. package/README.md +648 -33
  2. package/package.json +11 -9
  3. package/src/data/apis.js +74 -88
  4. package/src/data/manifest.js +3 -1
  5. package/src/data/regex.js +3 -1
  6. package/src/index.d.ts +42 -0
  7. package/src/index.js +0 -4
  8. package/src/rules/css-spacing.js +375 -0
  9. package/src/rules/manifest-key-order.js +1 -2
  10. package/src/rules/manifest-spacing.js +1 -2
  11. package/src/rules/no-content-style-leak.js +12 -11
  12. package/src/rules/no-deprecated-apis.js +2 -2
  13. package/src/rules/no-duplicate-manifest-keys.js +0 -1
  14. package/src/rules/no-global-keyframes.js +84 -0
  15. package/src/rules/no-host-page-selectors.js +105 -0
  16. package/src/rules/no-inline-event-handlers.js +116 -0
  17. package/src/rules/no-inline-scripts.js +65 -0
  18. package/src/rules/no-insecure-fetch.js +7 -6
  19. package/src/rules/no-insecure-messaging.js +4 -4
  20. package/src/rules/no-invalid-locales.js +3 -5
  21. package/src/rules/no-missing-default-locale.js +1 -1
  22. package/src/rules/no-missing-permissions.js +2 -3
  23. package/src/rules/no-missing-web-accessible-resources.js +2 -2
  24. package/src/rules/no-remote-code.js +77 -28
  25. package/src/rules/no-remote-objects.js +55 -0
  26. package/src/rules/no-remote-urls.js +102 -0
  27. package/src/rules/no-scrollbar-styling.js +71 -0
  28. package/src/rules/no-unrestricted-host-permissions.js +1 -1
  29. package/src/rules/no-unused-i18n-keys.js +0 -1
  30. package/src/rules/no-unused-permissions.js +5 -4
  31. package/src/rules/require-license-file.js +1 -2
  32. package/src/rules/require-mv3.js +3 -2
  33. package/src/rules/require-short-name-when-useful.js +0 -1
  34. package/src/rules/valid-filenames.js +3 -3
  35. package/src/rules/valid-license-file.js +2 -4
  36. package/src/rules/valid-manifest-icons.js +2 -2
  37. package/src/rules/valid-mv3-schema.js +5 -5
  38. package/src/rules/valid-url-fields.js +2 -2
  39. package/src/rules/valid-version-field.js +2 -2
  40. package/src/utils/api.js +20 -0
  41. package/src/utils/array.js +1 -0
  42. package/src/utils/extension.js +1 -3
  43. package/src/utils/html.js +29 -0
  44. package/src/utils/i18n.js +2 -2
  45. package/src/utils/init.js +21 -9
  46. package/src/utils/license.js +1 -1
  47. package/src/utils/node.js +3 -1
  48. package/src/utils/pkg.js +19 -2
  49. package/src/utils/rules.js +1 -1
  50. package/src/data/deprecations.js +0 -8
@@ -0,0 +1,71 @@
1
+ import array from '../utils/array.js'
2
+ import node from '../utils/node.js'
3
+
4
+ const re = {
5
+ scrollbarPseudo: /^(?:-webkit-)?scrollbar(?:-[\w-]+)?$/i } // oxlint-disable-line security/detect-unsafe-regex
6
+
7
+ export default {
8
+ meta: {
9
+ type: 'problem',
10
+ docs: {
11
+ description: 'Disallow scrollbar pseudo-elements (e.g. `::-webkit-scrollbar`) '
12
+ + 'on unscoped selectors in content-script CSS',
13
+ category: 'Possible Errors',
14
+ css: true
15
+ },
16
+ defaultOptions: [{ allowedSelectors: [] }],
17
+ schema: [{
18
+ type: 'object',
19
+ properties: {
20
+ allowedSelectors: {
21
+ oneOf: [
22
+ { type: 'string', description: 'A single selector' },
23
+ { type: 'array', items: { type: 'string' }, description: 'List of selectors' }
24
+ ],
25
+ description: 'Selectors to explicitly allow (e.g. `body::-webkit-scrollbar`)'
26
+ }
27
+ },
28
+ additionalProperties: false
29
+ }],
30
+ messages: {
31
+ noScrollbarStyling: 'Scrollbar pseudo-element on `{{selector}}` styles the host page\'s scrollbar. '
32
+ + 'Scope it to a namespaced class (e.g. `.myext-panel::...`) or use `allowedSelectors`.'
33
+ }
34
+ },
35
+
36
+ create(context) {
37
+ const opts = context.options[0] || {},
38
+ allowedSelectors = new Set(array.to(opts.allowedSelectors))
39
+ function checkSelector(selector) {
40
+ if (!selector) return
41
+ const text = getSelectorText(selector).trim() ; if (allowedSelectors.has(text)) return
42
+ let hasScrollbarPseudo = false,
43
+ hasQualifier = false,
44
+ pastFirstCompound = false
45
+ for (const simple of selector.children ?? []) {
46
+ const type = node.plainType(simple)
47
+ if (type == 'Combinator') { pastFirstCompound = true; continue }
48
+ if (type == 'PseudoElementSelector' && re.scrollbarPseudo.test(simple.name ?? '')) {
49
+ hasScrollbarPseudo = true
50
+ continue
51
+ }
52
+ if (pastFirstCompound) continue
53
+ if (['AttributeSelector', 'ClassSelector', 'IdSelector'].includes(type)
54
+ || (type == 'PseudoClassSelector' && (simple.name ?? '').toLowerCase().startsWith('host'))
55
+ ) hasQualifier = true
56
+ }
57
+ if (hasScrollbarPseudo && !hasQualifier)
58
+ context.report({ node: selector, messageId: 'noScrollbarStyling', data: { selector: text }})
59
+ }
60
+ function getSelectorText(selector) {
61
+ const { range } = selector
62
+ if (range?.length == 2) return context.sourceCode.text.slice(range[0], range[1])
63
+ return context.sourceCode.getText(selector)
64
+ }
65
+ function handleRule(node) {
66
+ for (const selector of node.prelude?.children ?? [])
67
+ checkSelector(selector)
68
+ }
69
+ return { Rule: handleRule, CSSRule: handleRule }
70
+ }
71
+ }
@@ -58,7 +58,7 @@ export default {
58
58
  return {
59
59
  'Program:exit'() {
60
60
  for (const key of hostKeys) {
61
- const patterns = manifest[key] // eslint-disable-line security/detect-object-injection
61
+ const patterns = manifest[key] // oxlint-disable-line security/detect-object-injection
62
62
  if (!Array.isArray(patterns)) continue
63
63
  for (const pattern of patterns)
64
64
  if (isBroad(pattern))
@@ -10,7 +10,6 @@ export default {
10
10
  docs: {
11
11
  description: 'Flag i18n keys in `messages.json` that are never referenced',
12
12
  category: 'Stylistic Issues',
13
- recommended: false,
14
13
  manifest: true
15
14
  },
16
15
  schema: [{
@@ -1,3 +1,4 @@
1
+ import apiUtils from '../utils/api.js'
1
2
  import extension from '../utils/extension.js'
2
3
  import fs from 'node:fs'
3
4
  import path from 'node:path'
@@ -14,15 +15,15 @@ function collectUsedAPIs(manifest, root) {
14
15
  if (manifest.action?.default_popup) files.add(manifest.action.default_popup)
15
16
  if (manifest.options_ui?.page) files.add(manifest.options_ui.page)
16
17
  if (manifest.devtools_page) files.add(manifest.devtools_page)
17
- const re = { /* eslint-disable-next-line security/detect-unsafe-regex */
18
+ const re = { /* oxlint-disable-next-line security/detect-unsafe-regex */
18
19
  chromeAPI: /\bchrome\.\w+(?:\.\w+)*/g }
19
20
  const used = new Set()
20
21
  for (const file of files) {
21
22
  try {
22
- const src = // eslint-disable-next-line security/detect-non-literal-fs-filename
23
+ const src = // oxlint-disable-next-line security/detect-non-literal-fs-filename
23
24
  fs.readFileSync(path.resolve(root, file), 'utf8')
24
25
  for (const match of src.matchAll(re.chromeAPI)) used.add(match[0])
25
- } catch {} // eslint-disable-line no-empty
26
+ } catch {}
26
27
  }
27
28
  return used
28
29
  }
@@ -54,7 +55,7 @@ export default {
54
55
  if (perm == permission) { knownPerm = true ; break }
55
56
  if (!knownPerm) return true
56
57
  for (const api of usedAPIs)
57
- if (apis.findPermission(api) == permission) return true
58
+ if (apiUtils.findPermission(api) == permission) return true
58
59
  return false
59
60
  }
60
61
  if (!regex.filename.manifest.test(filename)) {
@@ -7,7 +7,6 @@ export default {
7
7
  type: 'problem',
8
8
  docs: {
9
9
  description: 'Requires a license file in the extension',
10
- recommended: false,
11
10
  manifest: true
12
11
  },
13
12
  schema: [],
@@ -20,7 +19,7 @@ export default {
20
19
  const { filename } = context
21
20
  function check(node) {
22
21
  const pkgRoot = pkg.findRoot(path.dirname(filename))
23
- if (!pkgRoot || !license.find(pkgRoot)) // eslint-disable-line e18e/prefer-array-some
22
+ if (!pkgRoot || !license.find(pkgRoot)) // oxlint-disable-line e18e/prefer-array-some
24
23
  context.report({ node, messageId: 'missingFile' })
25
24
  }
26
25
  return { Program: check, Document: check }
@@ -32,7 +32,8 @@ export default {
32
32
  }
33
33
  }
34
34
  }
35
- const manifest = extension.manifest.read(context) ; if (!manifest || manifest.manifest_version == 3) return {}
36
- return { Program(node) { context.report({ node, messageId: 'notMV3' }) }}
35
+ const manifest = extension.manifest.read(context)
36
+ if (!manifest || manifest.manifest_version == 3) return {}
37
+ return { Program(node) { context.report({ node, messageId: 'notMV3' })}}
37
38
  }
38
39
  }
@@ -12,7 +12,6 @@ export default {
12
12
  docs: {
13
13
  description: 'Requires a `short_name` when `name` exceeds 12 chars',
14
14
  category: 'Stylistic Issues',
15
- recommended: false,
16
15
  manifest: true
17
16
  },
18
17
  schema: [{
@@ -1,10 +1,10 @@
1
- /* eslint-disable security/detect-non-literal-fs-filename -- lint-time project inspection only */
1
+ /* oxlint-disable security/detect-non-literal-fs-filename -- lint-time project inspection only */
2
2
 
3
3
  import extension from '../utils/extension.js'
4
4
  import fs from 'node:fs'
5
5
  import path from 'node:path'
6
6
 
7
- const re = { invalidChars: /[^\w\-.]/, reservedPrefix: /^_/ }
7
+ const re = { invalidChars: /[^\w\-.]/ }
8
8
 
9
9
  const refKeys = [
10
10
  ['action.default_popup', manifest => manifest.action?.default_popup],
@@ -112,7 +112,7 @@ export default {
112
112
  segments = path.relative(extRoot, filename).split(path.sep)
113
113
  if (noReservedPrefix)
114
114
  for (const seg of segments)
115
- if (re.reservedPrefix.test(seg) && seg != '_locales')
115
+ if (seg.startsWith('_') && seg != '_locales')
116
116
  context.report({ loc: { line: 1, column: 1 }, messageId: 'reservedPrefix', data: { name: seg }})
117
117
  if (noSpecialChars)
118
118
  for (const seg of segments)
@@ -1,10 +1,9 @@
1
+ import array from '../utils/array.js'
1
2
  import license from '../utils/license.js'
2
3
  import path from 'node:path'
3
4
 
4
5
  import regex from '../data/regex.js'
5
6
 
6
- function toArray(value) { return value == null ? [] : Array.isArray(value) ? value : [value] }
7
-
8
7
  function validateContent(context, node, { requireSPDX, allowedLicenses }) {
9
8
  const text = context.sourceCode.text,
10
9
  id = license.spdx.parse(text)
@@ -28,7 +27,6 @@ export default {
28
27
  fixable: 'code',
29
28
  docs: {
30
29
  description: 'Validates SPDX identifiers in license files',
31
- recommended: false,
32
30
  license: true
33
31
  },
34
32
  defaultOptions: [{ requireSPDX: false, allowedLicenses: [] }],
@@ -56,7 +54,7 @@ export default {
56
54
  create(context) {
57
55
  const opts = context.options[0],
58
56
  { requireSPDX } = opts,
59
- allowedLicenses = toArray(opts.allowedLicenses),
57
+ allowedLicenses = array.to(opts.allowedLicenses),
60
58
  { filename } = context,
61
59
  basename = path.basename(filename)
62
60
  if (!regex.filename.license.test(basename) || regex.filename.script.test(basename))
@@ -20,7 +20,7 @@ function validateIcons(context, icons, root, opts, loc) {
20
20
  if (typeof file != 'string') continue
21
21
  if (checkSVG && re.svg.test(file))
22
22
  context.report({ loc, messageId: 'svgNotSupported' })
23
- else if (checkFiles && root // eslint-disable-next-line security/detect-non-literal-fs-filename
23
+ else if (checkFiles && root // oxlint-disable-next-line security/detect-non-literal-fs-filename
24
24
  && !fs.existsSync(path.resolve(root, file))
25
25
  ) context.report({ loc, messageId: 'missingFile', data: { file }})
26
26
  }
@@ -98,7 +98,7 @@ export default {
98
98
  if (typeof file != 'string') continue
99
99
  if (checkSVG && re.svg.test(file))
100
100
  context.report({ node: fileNode, messageId: 'svgNotSupported' })
101
- else if (checkFiles && root // eslint-disable-next-line security/detect-non-literal-fs-filename
101
+ else if (checkFiles && root // oxlint-disable-next-line security/detect-non-literal-fs-filename
102
102
  && !fs.existsSync(path.resolve(root, file))
103
103
  ) context.report({ node: fileNode, messageId: 'missingFile', data: { file }})
104
104
  }
@@ -85,10 +85,10 @@ export default {
85
85
  }
86
86
  }
87
87
  }
88
- const manifest = extension.manifest.read(context) ; if (!manifest || manifest.manifest_version != 3) return {}
88
+ const manifest = extension.manifest.read(context) ; if (manifest?.manifest_version != 3) return {}
89
89
  const isFF = manifest.browser_specific_settings != null
90
- return {
91
- Program(node) { /* eslint-disable security/detect-object-injection */
90
+ return { // oxlint-disable security/detect-object-injection
91
+ Program(node) {
92
92
  for (const key of Object.keys(manifest))
93
93
  if (manifestData.keys.mv2Only.has(key))
94
94
  context.report({ node, messageId: 'mv2Key', data: { key, reason: 'MV2-only key' }})
@@ -117,7 +117,7 @@ export default {
117
117
  if (!set.has(value))
118
118
  context.report({ node, messageId: 'invalidDataCollectionValue', data: { value, kind }})
119
119
  }
120
- } /* eslint-enable security/detect-object-injection */
121
- }
120
+ }
121
+ } // oxlint-enable security/detect-object-injection
122
122
  }
123
123
  }
@@ -6,7 +6,7 @@ import regex from '../data/regex.js'
6
6
  const re = {
7
7
  absoluteURL: /^[a-z][a-z0-9+.-]*:\/\/[^\s/]+/i,
8
8
  match: /^(\*|https?|file|ftp):\/\/([^/]*)(\/.*)$/,
9
- template: /\{[^}]+\}/g
9
+ template: /\{[^{}]+\}/g
10
10
  }
11
11
 
12
12
  function collectStrings(obj, path = [], out = []) {
@@ -15,7 +15,7 @@ function collectStrings(obj, path = [], out = []) {
15
15
  else if (Array.isArray(obj))
16
16
  for (const elem of obj) collectStrings(elem, path, out)
17
17
  else if (typeof obj == 'object')
18
- for (const key of Object.keys(obj)) // eslint-disable-next-line security/detect-object-injection
18
+ for (const key of Object.keys(obj)) // oxlint-disable-next-line security/detect-object-injection
19
19
  collectStrings(obj[key], [...path, key], out)
20
20
  return out
21
21
  }
@@ -1,11 +1,11 @@
1
1
  import extension from '../utils/extension.js'
2
2
  import regex from '../data/regex.js'
3
3
 
4
- const re = { /* eslint-disable security/detect-unsafe-regex */
4
+ const re = { // oxlint-disable security/detect-unsafe-regex
5
5
  chromeParts: /^\d+(?:\.\d+){0,3}$/,
6
6
  date: /^\d{4}\.(?:0?[1-9]|1[0-2])\.(?:0?[1-9]|[12]\d|3[01])(?:\.\d+)?$/,
7
7
  semver: /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/
8
- } /* eslint-enable security/detect-unsafe-regex */
8
+ } // oxlint-enable security/detect-unsafe-regex
9
9
 
10
10
  export default {
11
11
  meta: {
@@ -0,0 +1,20 @@
1
+ import apis from '../data/apis.js'
2
+
3
+ export default {
4
+ findDeprecated(api) {
5
+ for (const [deprecated, replacement] of apis.deprecated)
6
+ if (api == deprecated || api.startsWith(`${deprecated}.`))
7
+ return { deprecated, replacement }
8
+ return null
9
+ },
10
+
11
+ findPermission(api) {
12
+ if (apis.permless.has(api)) return null
13
+ for (const [prefix, permission] of apis.permissionMap)
14
+ if (api == prefix || api.startsWith(`${prefix}.`))
15
+ return permission
16
+ return null
17
+ },
18
+
19
+ isExternalMsgListener(api) { return apis.externalMsgListeners.has(api) }
20
+ }
@@ -0,0 +1 @@
1
+ export default { to(val) { return Array.isArray(val) ? val : val == null ? [] : [val] }}
@@ -1,4 +1,4 @@
1
- /* eslint-disable security/detect-non-literal-fs-filename -- lint-time test fixtures only */
1
+ // oxlint-disable security/detect-non-literal-fs-filename
2
2
 
3
3
  import fs from 'node:fs'
4
4
  import path from 'node:path'
@@ -40,8 +40,6 @@ const utils = {
40
40
 
41
41
  manifest: {
42
42
  permission: {
43
- valid: permissions.valid,
44
- sensitive: permissions.sensitive,
45
43
  isValid(name, target = 'mv3') {
46
44
  if (permissions.valid.has(name)) return true
47
45
  if (target == 'mv3') return false
@@ -0,0 +1,29 @@
1
+ import regex from '../data/regex.js'
2
+
3
+ export default {
4
+ isHTML(text) { return text.trimStart().startsWith('<') },
5
+
6
+ isRemote(src, allowedPrefixes) {
7
+ if (!src || allowedPrefixes.some(prefix => src.startsWith(prefix))) return false
8
+ return regex.scheme.test(src)
9
+ },
10
+
11
+ offsetToLoc(text, offset) {
12
+ let line = 1, column = 1
13
+ for (let i = 0; i < offset && i < text.length; i++) {
14
+ if (text[i] == '\n') { line++ ; column = 1 } // oxlint-disable-line security/detect-object-injection
15
+ else column++
16
+ }
17
+ return { line, column }
18
+ },
19
+
20
+ pickAttrVal(match) { return match ? (match[1] ?? match[2] ?? match[3] ?? '') : null },
21
+
22
+ re: {
23
+ attr: {
24
+ data: /\sdata\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i,
25
+ src: /\ssrc\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i,
26
+ type: /\stype\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i
27
+ }
28
+ }
29
+ }
package/src/utils/i18n.js CHANGED
@@ -1,4 +1,4 @@
1
- /* eslint-disable security/detect-non-literal-fs-filename -- lint-time test fixtures only */
1
+ // oxlint-disable security/detect-non-literal-fs-filename
2
2
 
3
3
  import fs from 'node:fs'
4
4
  import path from 'node:path'
@@ -7,7 +7,7 @@ import i18n from '../data/i18n.js'
7
7
 
8
8
  function templateToPattern(body) {
9
9
  const parts = body.split(i18n.re.templateExpr).map(part => part.replace(i18n.re.specialChars, '\\$&'))
10
- return new RegExp(`^${parts.join('.*')}$`) // eslint-disable-line security/detect-non-literal-regexp
10
+ return new RegExp(`^${parts.join('.*')}$`) // oxlint-disable-line security/detect-non-literal-regexp
11
11
  }
12
12
 
13
13
  export default {
package/src/utils/init.js CHANGED
@@ -1,14 +1,20 @@
1
1
  import css from '@eslint/css'
2
2
  import html from '@html-eslint/eslint-plugin'
3
3
  import json from '@eslint/json'
4
+ import pkg from './pkg.js'
4
5
  import rulesUtils from './rules.js'
5
6
  import text from './text.js'
6
7
 
8
+ import cssSpacing from '../rules/css-spacing.js'
7
9
  import manifestKeyOrder from '../rules/manifest-key-order.js'
8
10
  import manifestSpacing from '../rules/manifest-spacing.js'
9
11
  import noContentStyleLeak from '../rules/no-content-style-leak.js'
10
12
  import noDeprecatedAPIs from '../rules/no-deprecated-apis.js'
11
13
  import noDuplicateManifestKeys from '../rules/no-duplicate-manifest-keys.js'
14
+ import noGlobalKeyframes from '../rules/no-global-keyframes.js'
15
+ import noHostPageSelectors from '../rules/no-host-page-selectors.js'
16
+ import noInlineEventHandlers from '../rules/no-inline-event-handlers.js'
17
+ import noInlineScripts from '../rules/no-inline-scripts.js'
12
18
  import noInsecureFetch from '../rules/no-insecure-fetch.js'
13
19
  import noInsecureMessaging from '../rules/no-insecure-messaging.js'
14
20
  import noInvalidLocales from '../rules/no-invalid-locales.js'
@@ -18,6 +24,9 @@ import noMissingI18nKeys from '../rules/no-missing-i18n-keys.js'
18
24
  import noMissingPermissions from '../rules/no-missing-permissions.js'
19
25
  import noMissingWebAccessibleResources from '../rules/no-missing-web-accessible-resources.js'
20
26
  import noRemoteCode from '../rules/no-remote-code.js'
27
+ import noRemoteObjects from '../rules/no-remote-objects.js'
28
+ import noRemoteURLs from '../rules/no-remote-urls.js'
29
+ import noScrollbarStyling from '../rules/no-scrollbar-styling.js'
21
30
  import noSensitivePermissions from '../rules/no-sensitive-permissions.js'
22
31
  import noUnrestrictedHostPermissions from '../rules/no-unrestricted-host-permissions.js'
23
32
  import noUnusedI18nKeys from '../rules/no-unused-i18n-keys.js'
@@ -33,15 +42,10 @@ import validServiceWorker from '../rules/valid-service-worker.js'
33
42
  import validURLfields from '../rules/valid-url-fields.js'
34
43
  import validVersionField from '../rules/valid-version-field.js'
35
44
 
36
- const namespace = 'chrome-extensions'
45
+ const pkgName = pkg.getName(),
46
+ namespace = pkgName.replace(/^@[^/]+\//, '').replace(/^eslint-plugin-/, '')
37
47
 
38
48
  export default {
39
- bundleDeprecation(plugin, [ruleId, bundleNames], pluginName) { /* eslint-disable security/detect-object-injection */
40
- for (const bundleName of bundleNames)
41
- if (plugin.configs[bundleName])
42
- plugin.configs[bundleName].rules[`${pluginName}/${ruleId}`] = 'error'
43
- }, /* eslint-enable security/detect-object-injection */
44
-
45
49
  configs(plugin) {
46
50
  const plugins = { [namespace]: plugin },
47
51
  rules = plugin.rules,
@@ -68,15 +72,20 @@ export default {
68
72
  }
69
73
  },
70
74
 
71
- plugin() { return { rules: {}, configs: {} }},
75
+ plugin() { return { meta: { name: pkgName }, rules: {}, configs: {} }},
72
76
 
73
77
  rules(plugin) {
74
78
  plugin.rules = {
79
+ 'css-spacing': cssSpacing,
75
80
  'manifest-key-order': manifestKeyOrder,
76
81
  'manifest-spacing': manifestSpacing,
77
82
  'no-content-style-leak': noContentStyleLeak,
78
83
  'no-deprecated-apis': noDeprecatedAPIs,
79
84
  'no-duplicate-manifest-keys': noDuplicateManifestKeys,
85
+ 'no-global-keyframes': noGlobalKeyframes,
86
+ 'no-host-page-selectors': noHostPageSelectors,
87
+ 'no-inline-event-handlers': noInlineEventHandlers,
88
+ 'no-inline-scripts': noInlineScripts,
80
89
  'no-insecure-fetch': noInsecureFetch,
81
90
  'no-insecure-messaging': noInsecureMessaging,
82
91
  'no-invalid-locales': noInvalidLocales,
@@ -86,6 +95,9 @@ export default {
86
95
  'no-missing-permissions': noMissingPermissions,
87
96
  'no-missing-web-accessible-resources': noMissingWebAccessibleResources,
88
97
  'no-remote-code': noRemoteCode,
98
+ 'no-remote-objects': noRemoteObjects,
99
+ 'no-remote-urls': noRemoteURLs,
100
+ 'no-scrollbar-styling': noScrollbarStyling,
89
101
  'no-sensitive-permissions': noSensitivePermissions,
90
102
  'no-unrestricted-host-permissions': noUnrestrictedHostPermissions,
91
103
  'no-unused-i18n-keys': noUnusedI18nKeys,
@@ -102,6 +114,6 @@ export default {
102
114
  'valid-version-field': validVersionField
103
115
  }
104
116
  for (const [ruleId, rule] of Object.entries(plugin.rules))
105
- rule.meta.docs.url = `https://codeberg.org/adamlui/eslint-plugin-${namespace}/#${ruleId}`
117
+ rule.meta.docs.url = `https://codeberg.org/adamlui/${pkgName}/#${ruleId}`
106
118
  }
107
119
  }
@@ -1,4 +1,4 @@
1
- /* eslint-disable security/detect-non-literal-fs-filename -- lint-time test fixtures only */
1
+ // oxlint-disable security/detect-non-literal-fs-filename
2
2
 
3
3
  import { createRequire } from 'node:module'
4
4
  import fs from 'node:fs'
package/src/utils/node.js CHANGED
@@ -19,5 +19,7 @@ export default {
19
19
  ancestor = ancestor.parent
20
20
  }
21
21
  return false
22
- }
22
+ },
23
+
24
+ plainType(node, prefix) { return node?.type?.replace(prefix, '') ?? '' }
23
25
  }
package/src/utils/pkg.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { fileURLToPath } from 'node:url'
1
2
  import fs from 'node:fs'
2
3
  import path from 'node:path'
3
4
 
@@ -11,11 +12,27 @@ export default {
11
12
  current = parent
12
13
  continue
13
14
  }
14
- if ( // eslint-disable-next-line security/detect-non-literal-fs-filename
15
+ if ( // oxlint-disable-next-line security/detect-non-literal-fs-filename
15
16
  fs.existsSync(path.join(current, 'package.json'))) return current
16
17
  const parent = path.dirname(current)
17
18
  if (parent === current) return null
18
19
  current = parent
19
20
  }
20
- }
21
+ },
22
+
23
+ getName() { // oxlint-disable security/detect-non-literal-fs-filename
24
+ let current = path.dirname(fileURLToPath(import.meta.url))
25
+ while (current) {
26
+ const pkgPath = path.join(current, 'package.json')
27
+ if (fs.existsSync(pkgPath))
28
+ try {
29
+ const { name } = JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
30
+ if (name) return name
31
+ } catch { /* malformed, keep walking */ }
32
+ const parent = path.dirname(current)
33
+ if (parent === current) return null
34
+ current = parent
35
+ }
36
+ return null
37
+ } // oxlint-enable security/detect-non-literal-fs-filename
21
38
  }
@@ -1,7 +1,7 @@
1
1
  export default {
2
2
  bundle(rules, predicate, pluginName) {
3
3
  return Object.fromEntries(Object.entries(rules)
4
- .filter(([, rule]) => predicate(rule))
4
+ .filter(([, rule]) => !rule.meta.deprecated && predicate(rule))
5
5
  .map(([ruleId]) => [`${pluginName}/${ruleId}`, 'error']))
6
6
  }
7
7
  }
@@ -1,8 +0,0 @@
1
- // Delete a line to end support
2
-
3
- export default {
4
- bundles: {
5
- // <rule-id-a>: [<old-bundle>],
6
- // <rule-id-b>: [<old-bundle>]
7
- }
8
- }