eslint-plugin-greasemonkey 1.10.1 → 1.10.2

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 CHANGED
@@ -7,7 +7,7 @@
7
7
  <a href="https://codeberg.org/adamlui/eslint-plugin-greasemonkey/src/branch/main/docs/LICENSE.md">
8
8
  <img alt="[License: MIT]" src="https://img.shields.io/badge/License-MIT-f1f20a.svg?logo=internetarchive&logoColor=white&labelColor=464646&style=for-the-badge"></a>
9
9
  <a target="_blank" href="https://getnpmstats.com/?package=eslint-plugin-greasemonkey&range=last-year&interval=monthly">
10
- <img src="https://img.shields.io/npm/dm/eslint-plugin-greasemonkey?style=for-the-badge&logo=npm&color=black"></a>
10
+ <img alt="[Monthly npm downloads]" src="https://img.shields.io/npm/dm/eslint-plugin-greasemonkey?style=for-the-badge&logo=npm&color=black"></a>
11
11
  <a href="#usage">
12
12
  <img alt="[Supports ESLint >=9.0.0 / Oxlint >=1.65.0]" src="https://img.shields.io/badge/Supports-ESLint_%3E%3D9.0.0_%2F_Oxlint_%3E%3D1.65.0-4B32C3?style=for-the-badge&logo=eslint"></a>
13
13
  <br><br>
@@ -44,7 +44,7 @@ import greasemonkey from 'eslint-plugin-greasemonkey'
44
44
  export default [
45
45
  // other configs
46
46
  {
47
- files: ['**/*.user.js'], plugins: { greasemonkey }
47
+ files: ['**/*.user.js'], plugins: { greasemonkey },
48
48
  rules: {
49
49
  ...greasemonkey.configs.recommended.rules,
50
50
  'greasemonkey/no-unused-resources': 'off', // selectively disable rule
@@ -60,7 +60,7 @@ export default [
60
60
 
61
61
  ### <img height=15 width=auto src="https://www.google.com/s2/favicons?domain=oxc.rs&sz=64"> Oxlint
62
62
 
63
- Add a bundled config to your `oxlint.config.ts`:
63
+ Add a bundled config to your `oxlint.config.*ts`:
64
64
 
65
65
  ```ts
66
66
  ...
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-greasemonkey",
3
- "version": "1.10.1",
3
+ "version": "1.10.2",
4
4
  "description": "ESLint rules for Greasemonkey/Tampermonkey userscripts",
5
5
  "keywords": [
6
6
  "eslint",
@@ -69,20 +69,22 @@
69
69
  "@eslint/json": "^2.1.1",
70
70
  "@eslint/markdown": "^7.5.1",
71
71
  "@stylistic/eslint-plugin": "^5.10.0",
72
- "eslint": "^10.13.0",
73
- "eslint-plugin-constructors": "^1.5.1",
72
+ "eslint": "^10.14.0",
73
+ "eslint-plugin-constructors": "^1.7.0",
74
74
  "eslint-plugin-eslint-plugin": "^7.6.2",
75
75
  "eslint-plugin-import-x": "^4.17.1",
76
76
  "eslint-plugin-n": "^18.5.0",
77
77
  "eslint-plugin-no-secrets": "^2.3.3",
78
78
  "eslint-plugin-no-unsanitized": "^4.1.5",
79
- "eslint-plugin-package-json": "^1.11.0",
79
+ "eslint-plugin-package-json": "^1.12.0",
80
80
  "eslint-plugin-promise": "^7.3.0",
81
- "eslint-plugin-regexp": "^3.3.1",
82
- "eslint-plugin-security": "^4.2.0",
81
+ "eslint-plugin-regexp": "^3.3.2",
82
+ "eslint-plugin-security": "^4.2.1",
83
83
  "eslint-plugin-sonarjs": "^4.2.2",
84
- "eslint-plugin-woodpecker": "^1.1.1",
85
- "oxlint": "^1.89.0"
84
+ "eslint-plugin-woodpecker": "^1.1.2",
85
+ "oxlint": "^1.90.0",
86
+ "typescript": "npm:@typescript/typescript6@^6.0.2",
87
+ "typescript-latest": "npm:typescript@^7.0.2"
86
88
  },
87
89
  "peerDependencies": {
88
90
  "eslint": ">=9.0.0"
@@ -1,5 +1,6 @@
1
1
  import metadata from '../utils/metadata.js'
2
2
 
3
+ const defaultOptions = { order: [] }
3
4
  const defaultOrder = [
4
5
  'name', 'description', 'author', 'namespace', 'version', 'license', 'icon', 'icon64',
5
6
  'compatible', 'match', 'exclude', 'include', 'connect', 'require', 'resource', 'grant',
@@ -43,21 +44,21 @@ export default {
43
44
  },
44
45
  additionalProperties: false
45
46
  }],
46
- defaultOptions: [{ order: [] }],
47
+ defaultOptions: [defaultOptions],
47
48
  messages: { wrongOrder: '@{{first}} should appear before @{{second}}.' }
48
49
  },
49
50
 
50
51
  create(context) {
51
- const custom = context.options[0]?.order ?? [],
52
- master = custom.length ? defaultOrder.filter(key => !custom.includes(key)) : defaultOrder
52
+ const { order } = { ...defaultOptions, ...context.options[0] },
53
+ master = order.length ? defaultOrder.filter(key => !order.includes(key)) : defaultOrder
53
54
  return {
54
55
  Program() {
55
56
  const sourceCode = context.sourceCode,
56
57
  entries = metadata.extract(sourceCode),
57
- violation = findViolation(entries, master) ?? (custom.length && findViolation(entries, custom))
58
+ violation = findViolation(entries, master) ?? (order.length && findViolation(entries, order))
58
59
  if (!violation) return
59
60
  const [first, second] = violation
60
- const sorted = sortEntries(sortEntries(entries, master), custom)
61
+ const sorted = sortEntries(sortEntries(entries, master), order)
61
62
  const replacements = // oxlint-disable-next-line security/detect-object-injection
62
63
  new Map(sorted.map((entry, idx) => [entries[idx], sourceCode.getText(entry.comment)]))
63
64
  const start = entries[0].comment.range[0]
@@ -1,6 +1,8 @@
1
1
  import metadata from '../utils/metadata.js'
2
2
  import regex from '../data/regex.js'
3
3
 
4
+ const defaultOptions = { spaces: 3 }
5
+
4
6
  export default {
5
7
  meta: {
6
8
  type: 'layout',
@@ -15,27 +17,27 @@ export default {
15
17
  properties: {
16
18
  spaces: {
17
19
  type: 'number',
18
- description: 'Minimum spaces after the longest key (default 3)'
20
+ description: 'Minimum spaces after the longest key'
19
21
  }
20
22
  },
21
23
  additionalProperties: false
22
24
  }],
23
- defaultOptions: [{ spaces: 3 }],
25
+ defaultOptions: [defaultOptions],
24
26
  messages: { misaligned: 'Metadata values should be aligned at column {{column}}.' }
25
27
  },
26
28
 
27
29
  create(context) {
28
- const minSpaces = context.options[0]?.spaces ?? 3
30
+ const { spaces } = { ...defaultOptions, ...context.options[0] }
29
31
  return {
30
32
  Program() {
31
33
  const sourceCode = context.sourceCode,
32
34
  metaEntries = metadata.extract(sourceCode)
33
35
  if (!metaEntries.length) return
34
- let targetCol = metaEntries.reduce((max, entry) => Math.max(max, entry.key.length), 0) + minSpaces +4
36
+ let targetCol = metaEntries.reduce((max, entry) => Math.max(max, entry.key.length), 0) + spaces +4
35
37
  for (const entry of metaEntries)
36
38
  if (entry.key == 'resource' && entry.value.trim()) {
37
39
  const name = entry.value.trim().split(regex.whitespace.all)[0],
38
- requiredCol = 4 + entry.key.length +1 + name.length + minSpaces
40
+ requiredCol = 4 + entry.key.length +1 + name.length + spaces
39
41
  if (requiredCol > targetCol) targetCol = requiredCol
40
42
  }
41
43
  for (const entry of metaEntries) {
@@ -1,3 +1,4 @@
1
+ const defaultOptions = { disallowWildcardTarget: true }
1
2
  const re = {
2
3
  messageTargets: new Set(['window', 'self', 'unsafeWindow']),
3
4
  postmsgValidation: /\b\w+\.(?:origin|source)\b/
@@ -14,7 +15,7 @@ export default {
14
15
  recommended: true,
15
16
  security: true
16
17
  },
17
- defaultOptions: [{ disallowWildcardTarget: true }],
18
+ defaultOptions: [defaultOptions],
18
19
  schema: [{
19
20
  type: 'object',
20
21
  properties: {
@@ -34,9 +35,8 @@ export default {
34
35
  },
35
36
 
36
37
  create(context) {
37
- const opts = context.options[0] || {},
38
- disallowWildcard = opts.disallowWildcardTarget !== false,
39
- sourceCode = context.sourceCode ?? context.getSourceCode()
38
+ const { disallowWildcardTarget } = { ...defaultOptions, ...context.options[0] },
39
+ sourceCode = context.sourceCode ?? context.getSourceCode()
40
40
  function checkPostMessageHandler(node, handlerNode, target) {
41
41
  if (!handlerNode) return
42
42
  const body = sourceCode.getText(handlerNode)
@@ -50,7 +50,7 @@ export default {
50
50
  && node.arguments[0]?.value == 'message'
51
51
  && isMessageTarget(node.callee.object)
52
52
  ) checkPostMessageHandler(node, node.arguments[1], node.callee.object.name)
53
- if (disallowWildcard && node.callee.property?.name == 'postMessage') {
53
+ if (disallowWildcardTarget && node.callee.property?.name == 'postMessage') {
54
54
  const target = node.arguments[1]
55
55
  if (target?.type == 'Literal' && target.value == '*')
56
56
  context.report({ node: target, messageId: 'wildcardTarget' })
@@ -4,7 +4,8 @@ import metadata from '../utils/metadata.js'
4
4
  import apis from '../data/apis.js'
5
5
  import headers from '../data/headers.js'
6
6
 
7
- const specialGrants = new Set(['none', 'unsafeWindow', 'window.close', 'window.focus', 'window.onurlchange']),
7
+ const defaultOptions = { target: 'all', allowedGrants: [] },
8
+ specialGrants = new Set(['none', 'unsafeWindow', 'window.close', 'window.focus', 'window.onurlchange']),
8
9
  allGrants = new Set([...apis.all, ...specialGrants]),
9
10
  sortedGrants = new Map(Array.from(allGrants, grant => [grant.toLowerCase(), grant]))
10
11
 
@@ -23,21 +24,21 @@ export default {
23
24
  anyOf: [
24
25
  { type: 'string', enum: ['all', ...headers.managers] },
25
26
  { type: 'array', items: { type: 'string', enum: headers.managers },
26
- minItems: 1, uniqueItems: true }
27
+ uniqueItems: true }
27
28
  ],
28
29
  description: 'Manager(s) whose grants to validate against (default: all)'
29
30
  },
30
31
  allowedGrants: {
31
32
  anyOf: [
32
33
  { type: 'string' },
33
- { type: 'array', items: { type: 'string' }, minItems: 1, uniqueItems: true }
34
+ { type: 'array', items: { type: 'string' }, uniqueItems: true }
34
35
  ],
35
36
  description: 'Additional @grant values to allow beyond the built-in list'
36
37
  }
37
38
  },
38
39
  additionalProperties: false
39
40
  }],
40
- defaultOptions: [{}],
41
+ defaultOptions: [defaultOptions],
41
42
  messages: {
42
43
  invalidGrant: 'Invalid @grant: "{{grant}}"',
43
44
  unsupportedGrant: '"@{{grant}}" is not supported by {{target}} (only by {{supporting}}).',
@@ -46,18 +47,16 @@ export default {
46
47
  },
47
48
 
48
49
  create(context) {
49
- const options = context.options[0] || {},
50
- rawTarget = options.target ?? 'all',
51
- targets = Array.isArray(rawTarget) ? rawTarget : [rawTarget],
52
- rawAllowed = options.allowedGrants ?? [],
53
- allowedGrants = new Set(Array.isArray(rawAllowed) ? rawAllowed : [rawAllowed])
50
+ const { target, allowedGrants } = { ...defaultOptions, ...context.options[0] },
51
+ targets = Array.isArray(target) ? target : [target],
52
+ allowedGrantSet = new Set(Array.isArray(allowedGrants) ? allowedGrants : [allowedGrants])
54
53
  return {
55
54
  Program() {
56
55
  const metaEntries = metadata.extract(context.sourceCode),
57
56
  grantEntries = metaEntries.filter(entry => entry.key == 'grant')
58
57
  for (const grant of grantEntries) {
59
58
  const grantName = grant.value.trim()
60
- if (specialGrants.has(grantName) || allowedGrants.has(grantName)) continue
59
+ if (specialGrants.has(grantName) || allowedGrantSet.has(grantName)) continue
61
60
  if (!allGrants.has(grantName)) {
62
61
  const suggestion = sortedGrants.get(grantName.toLowerCase())
63
62
  if (suggestion && suggestion != grantName)
@@ -1,6 +1,8 @@
1
1
  import metadata from '../utils/metadata.js'
2
2
  import headers from '../data/headers.js'
3
3
 
4
+ const defaultOptions = { target: 'all', allowedHeaders: [] }
5
+
4
6
  function parseAllowedPattern(entry) {
5
7
  if (typeof entry != 'string' || !entry.startsWith('/')) return null
6
8
  const lastSlash = entry.lastIndexOf('/')
@@ -36,7 +38,7 @@ export default {
36
38
  },
37
39
  additionalProperties: false
38
40
  }],
39
- defaultOptions: [{ allowedHeaders: [] }],
41
+ defaultOptions: [defaultOptions],
40
42
  messages: {
41
43
  invalidHeader: 'Invalid header: "@{{header}}".',
42
44
  unsupportedHeader: '"@{{header}}" is not supported by {{target}} (only by {{supporting}}).'
@@ -44,14 +46,13 @@ export default {
44
46
  },
45
47
 
46
48
  create(context) {
47
- const options = context.options[0] || {},
48
- rawTarget = options.target ?? 'all',
49
- targets = Array.isArray(rawTarget) ? rawTarget : [rawTarget],
50
- allowedList = [options.allowedHeaders ?? []].flat(),
51
- allowedExact = new Set(),
52
- allowedRegexes = [],
53
- validSet = targets.includes('all') ? headers.valid.get('all')
54
- : new Set(targets.flatMap(target => [...headers.valid.get(target)]))
49
+ const { target, allowedHeaders } = { ...defaultOptions, ...context.options[0] },
50
+ targets = Array.isArray(target) ? target : [target],
51
+ allowedList = [allowedHeaders].flat(),
52
+ allowedExact = new Set(),
53
+ allowedRegexes = [],
54
+ validSet = targets.includes('all') ? headers.valid.get('all')
55
+ : new Set(targets.flatMap(target => [...headers.valid.get(target)]))
55
56
  for (const entry of allowedList) {
56
57
  if (entry instanceof RegExp) allowedRegexes.push(entry)
57
58
  else if (typeof entry == 'string') {
@@ -2,7 +2,8 @@ import apis from '../data/apis.js'
2
2
  import headers from '../data/headers.js'
3
3
  import regex from '../data/regex.js'
4
4
 
5
- const re = { connect: /^\s*\/\/\s*@connect\s+(\S+)/, grant: /^\s*\/\/\s*@grant\s+(\S+)/ }
5
+ const defaultOptions = { target: 'all', allowedGrants: [], allowBroadConnect: false },
6
+ re = { connect: /^\s*\/\/\s*@connect\s+(\S+)/, grant: /^\s*\/\/\s*@grant\s+(\S+)/ }
6
7
 
7
8
  export default {
8
9
  meta: {
@@ -13,7 +14,7 @@ export default {
13
14
  recommended: false,
14
15
  security: true
15
16
  },
16
- defaultOptions: [{ target: 'all', allowedGrants: [], allowBroadConnect: false }],
17
+ defaultOptions: [defaultOptions],
17
18
  schema: [{
18
19
  type: 'object',
19
20
  properties: {
@@ -45,14 +46,11 @@ export default {
45
46
  },
46
47
 
47
48
  create(context) {
48
- const opts = context.options[0] ?? {},
49
- target = opts.target ?? 'all',
50
- allowBroadConnect = opts.allowBroadConnect === true,
51
- allowed = new Set(typeof opts.allowedGrants == 'string' ? [opts.allowedGrants]
52
- : opts.allowedGrants ?? []),
53
- sourceCode = context.sourceCode ?? context.getSourceCode(),
54
- managers = target == 'all' ? [...apis.managerSensitive.keys()]
55
- : Array.isArray(target) ? target : [target]
49
+ const { target, allowedGrants, allowBroadConnect } = { ...defaultOptions, ...context.options[0] },
50
+ allowed = new Set(typeof allowedGrants == 'string' ? [allowedGrants] : allowedGrants),
51
+ sourceCode = context.sourceCode ?? context.getSourceCode(),
52
+ managers = target == 'all' ? [...apis.managerSensitive.keys()]
53
+ : Array.isArray(target) ? target : [target]
56
54
  const isSensitive = grant => apis.sensitive.has(grant)
57
55
  || managers.some(mgr => apis.managerSensitive.get(mgr)?.has(grant))
58
56
  const report = (line, messageId, data, col, len) => context.report({
@@ -1,5 +1,7 @@
1
1
  import metadata from '../utils/metadata.js'
2
2
 
3
+ const defaultOptions = { ignoreLanguages: [] }
4
+
3
5
  export default {
4
6
  meta: {
5
7
  type: 'problem',
@@ -14,21 +16,20 @@ export default {
14
16
  ignoreLanguages: {
15
17
  anyOf: [
16
18
  { type: 'string' },
17
- { type: 'array', items: { type: 'string' }, minItems: 1, uniqueItems: true }
19
+ { type: 'array', items: { type: 'string' }, uniqueItems: true }
18
20
  ],
19
21
  description: 'Language code(s) to ignore'
20
22
  }
21
23
  },
22
24
  additionalProperties: false
23
25
  }],
24
- defaultOptions: [{}],
26
+ defaultOptions: [defaultOptions],
25
27
  messages: { missingPair: 'Missing matching @{{missing}} for @{{present}}.' }
26
28
  },
27
29
 
28
30
  create(context) {
29
- const options = context.options[0] || {},
30
- rawLangs = options.ignoreLanguages ?? [],
31
- ignoreLangs = new Set(Array.isArray(rawLangs) ? rawLangs : [rawLangs])
31
+ const { ignoreLanguages } = { ...defaultOptions, ...context.options[0] },
32
+ ignoreLangs = new Set(Array.isArray(ignoreLanguages) ? ignoreLanguages : [ignoreLanguages])
32
33
  return {
33
34
  Program() {
34
35
  const metaEntries = metadata.extract(context.sourceCode),
@@ -1,5 +1,7 @@
1
1
  import metadata from '../utils/metadata.js'
2
2
 
3
+ const defaultOptions = { fields: ['name', 'version'], requireMatchOrInclude: true }
4
+
3
5
  export default {
4
6
  meta: {
5
7
  type: 'problem',
@@ -23,19 +25,17 @@ export default {
23
25
  },
24
26
  additionalProperties: false
25
27
  }],
26
- defaultOptions: [{}],
28
+ defaultOptions: [defaultOptions],
27
29
  messages: { missingField: 'Missing required metadata field: @{{field}}.' }
28
30
  },
29
31
 
30
32
  create(context) {
31
- const options = context.options[0] || {},
32
- requiredFields = options.fields || ['name', 'version'],
33
- requireMatchOrInclude = options.requireMatchOrInclude ?? true
33
+ const { fields, requireMatchOrInclude } = { ...defaultOptions, ...context.options[0] }
34
34
  return {
35
35
  Program(node) {
36
36
  const metaEntries = metadata.extract(context.sourceCode),
37
37
  keys = new Set(metaEntries.map(entry => entry.key))
38
- for (const field of requiredFields)
38
+ for (const field of fields)
39
39
  if (!keys.has(field))
40
40
  context.report({ node, messageId: 'missingField', data: { field }})
41
41
  if (requireMatchOrInclude && !['include', 'match'].some(field => keys.has(field)))
@@ -1,7 +1,11 @@
1
1
  import metadata from '../utils/metadata.js'
2
2
 
3
+ const defaultOptions = { algorithm: 'sha256' }
3
4
  const re = {
4
- hashEntry: /^(sha1|sha256|sha384|sha512|md5)[=-]([a-z0-9+/]+={0,2})$/i, hashFragment: /#([^#]+)$/, hashSep: /[,;]/ }
5
+ hashEntry: /^(sha1|sha256|sha384|sha512|md5)[=-]([a-z0-9+/]+={0,2})$/i,
6
+ hashFragment: /#([^#]+)$/,
7
+ hashSep: /[,;]/
8
+ }
5
9
 
6
10
  export default {
7
11
  meta: {
@@ -23,12 +27,12 @@ export default {
23
27
  minItems: 1, uniqueItems: true
24
28
  }
25
29
  ],
26
- description: 'SRI hash algorithm(s) to accept, as a single string or array (default: sha256)'
30
+ description: 'SRI hash algorithm(s) to accept, as a single string or array'
27
31
  }
28
32
  },
29
33
  additionalProperties: false
30
34
  }],
31
- defaultOptions: [{ algorithm: 'sha256' }],
35
+ defaultOptions: [defaultOptions],
32
36
  messages: {
33
37
  missingHash: 'Missing SRI hash for @{{key}}.',
34
38
  invalidHash: 'Invalid SRI hash for @{{key}}.',
@@ -37,9 +41,9 @@ export default {
37
41
  },
38
42
 
39
43
  create(context) {
40
- const rawAlgorithm = context.options[0]?.algorithm ?? 'sha256',
41
- algorithms = (Array.isArray(rawAlgorithm) ? rawAlgorithm : [rawAlgorithm]).map(a => a.toLowerCase()),
42
- expected = algorithms.join(' or ')
44
+ const { algorithm } = { ...defaultOptions, ...context.options[0] },
45
+ algorithms = (Array.isArray(algorithm) ? algorithm : [algorithm]).map(a => a.toLowerCase()),
46
+ expected = algorithms.join(' or ')
43
47
  return {
44
48
  'Program:exit'() {
45
49
  for (const entry of metadata.extract(context.sourceCode)) {
@@ -1,13 +1,13 @@
1
1
  import metadata from '../utils/metadata.js'
2
2
  import regex from '../data/regex.js'
3
3
 
4
+ const defaultOptions = { versionType: 'semver', allowedGrants: [], allowFutureDate: false },
5
+ urlKeys = ['homepage', 'icon', 'icon64', 'require', 'source', 'website']
4
6
  const re = { // oxlint-disable security/detect-unsafe-regex
5
7
  semVer: /^\d+\.\d+\.\d+(?:-[\da-z.-]+)?(?:\+[\da-z.-]+)?$/i,
6
8
  dateVer: /^\d{4}\.(?:0?[1-9]|1[0-2])\.(?:0?[1-9]|[12]\d|3[01])(?:\.\d+)?$/
7
9
  } // oxlint-enable security/detect-unsafe-regex
8
10
 
9
- const urlKeys = ['homepage', 'icon', 'icon64', 'require', 'source', 'website']
10
-
11
11
  export default {
12
12
  meta: {
13
13
  type: 'problem',
@@ -22,7 +22,7 @@ export default {
22
22
  versionType: {
23
23
  type: 'string',
24
24
  enum: ['semver', 'date'],
25
- description: 'Version format to enforce (default: semver)'
25
+ description: 'Version format to enforce'
26
26
  },
27
27
  allowedGrants: {
28
28
  type: 'array',
@@ -31,7 +31,7 @@ export default {
31
31
  },
32
32
  allowFutureDate: {
33
33
  type: 'boolean',
34
- description: 'Allow @version dates later than the current date (default: false)'
34
+ description: 'Allow @version dates later than the current date'
35
35
  },
36
36
  timezone: {
37
37
  type: 'string',
@@ -40,7 +40,7 @@ export default {
40
40
  },
41
41
  additionalProperties: false
42
42
  }],
43
- defaultOptions: [{}],
43
+ defaultOptions: [defaultOptions],
44
44
  messages: {
45
45
  invalidVersion: 'Invalid @version: must be a valid {{versionType}} (expected format: {{example}}).',
46
46
  futureVersion: 'Invalid @version: date cannot be in the future.',
@@ -50,11 +50,9 @@ export default {
50
50
  },
51
51
 
52
52
  create(context) {
53
- const options = context.options[0] || {},
54
- versionType = options.versionType || 'semver',
55
- allowedGrants = new Set(options.allowedGrants || []),
56
- allowFutureDate = options.allowFutureDate ?? false,
57
- timezone = options.timezone,
53
+ const { versionType, allowedGrants, allowFutureDate, timezone }
54
+ = { ...defaultOptions, ...context.options[0] }
55
+ const allowedGrantsSet = new Set(allowedGrants),
58
56
  example = versionType == 'semver' ? '1.2.3' : 'YYYY.M.D or YYYY.MM.DD'
59
57
  const now = timezone
60
58
  ? Object.fromEntries(new Intl.DateTimeFormat('en', {
@@ -66,7 +64,7 @@ export default {
66
64
  Program() {
67
65
  const metaEntries = metadata.extract(context.sourceCode)
68
66
  for (const entry of metaEntries) {
69
- if (entry.key == 'grant' && allowedGrants.size && !allowedGrants.has(entry.value))
67
+ if (entry.key == 'grant' && allowedGrantsSet.size && !allowedGrantsSet.has(entry.value))
70
68
  context.report({ node: entry.comment, messageId: 'invalidGrant', data: { grant: entry.value }})
71
69
  else if (entry.key == 'resource') {
72
70
  const url = entry.value.trim().split(regex.whitespace.all)[1]
@@ -5,7 +5,8 @@ import license from '../utils/license.js'
5
5
 
6
6
  import regex from '../data/regex.js'
7
7
 
8
- const re = { spdxExpr: /(?<=\s)(?:OR|AND)(?=\s)/i }
8
+ const defaultOptions = { allowedLicenses: [] },
9
+ re = { spdxExpr: /(?<=\s)(?:OR|AND)(?=\s)/i }
9
10
 
10
11
  export default {
11
12
  meta: {
@@ -15,7 +16,7 @@ export default {
15
16
  category: 'Possible Errors',
16
17
  recommended: true
17
18
  },
18
- defaultOptions: [{ allowedLicenses: [] }],
19
+ defaultOptions: [defaultOptions],
19
20
  schema: [{
20
21
  type: 'object',
21
22
  properties: {
@@ -36,9 +37,9 @@ export default {
36
37
  },
37
38
 
38
39
  create(context) {
39
- const opts = context.options[0] ?? {},
40
- allowedLicenses = array.to(opts.allowedLicenses) ?? [],
41
- sourceCode = context.sourceCode ?? context.getSourceCode()
40
+ const { allowedLicenses } = { ...defaultOptions, ...context.options[0] },
41
+ allowedLicensesArray = array.to(allowedLicenses),
42
+ sourceCode = context.sourceCode ?? context.getSourceCode()
42
43
  return {
43
44
  Program() {
44
45
  const text = sourceCode.text,
@@ -61,11 +62,11 @@ export default {
61
62
  for (const part of parts)
62
63
  if (!license.spdx.isValid(part))
63
64
  return context.report({ loc, messageId: 'invalidSPDX', data: { id: part }})
64
- if (allowedLicenses.length) {
65
+ if (allowedLicensesArray.length) {
65
66
  for (const part of parts)
66
- if (!allowedLicenses.includes(part))
67
+ if (!allowedLicensesArray.includes(part))
67
68
  return context.report({ loc, messageId: 'disallowedSPDX',
68
- data: { id: part, allowed: allowedLicenses.join(', ') }})
69
+ data: { id: part, allowed: allowedLicensesArray.join(', ') }})
69
70
  }
70
71
  }
71
72
  }
@@ -1,11 +1,16 @@
1
1
  import metadata from '../utils/metadata.js'
2
2
  import regex from '../data/regex.js'
3
3
 
4
+ const defaultOptions = {
5
+ checkURLfields: true,
6
+ checkMatchPatterns: true,
7
+ allowedSchemes: ['http', 'https', 'file', 'ftp', 'http*'],
8
+ allowBroadPatterns: false
9
+ }
4
10
  const re = {
5
11
  matchPattern: /^(\*|http\*?|https?|file|ftp):\/\/([^/]*)(\/.*)$/,
6
12
  regexForm: /^\/.+\/[a-z]*$/i
7
13
  }
8
-
9
14
  const urlKeys = new Set(['homepage', 'require', 'require-css', 'source', 'website']),
10
15
  matchKeys = new Set(['match', 'exclude-match']),
11
16
  regexOrMatchKeys = new Set(['include', 'exclude'])
@@ -18,12 +23,7 @@ export default {
18
23
  category: 'Possible Errors',
19
24
  recommended: true
20
25
  },
21
- defaultOptions: [{
22
- checkURLfields: true,
23
- checkMatchPatterns: true,
24
- allowedSchemes: ['http', 'https', 'file', 'ftp', 'http*'],
25
- allowBroadPatterns: false
26
- }],
26
+ defaultOptions: [defaultOptions],
27
27
  schema: [{
28
28
  type: 'object',
29
29
  properties: {
@@ -57,11 +57,9 @@ export default {
57
57
  },
58
58
 
59
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
60
+ const { checkURLfields, checkMatchPatterns, allowedSchemes, allowBroadPatterns }
61
+ = { ...defaultOptions, ...context.options[0] }
62
+ const allowedSchemeSet = new Set(allowedSchemes)
65
63
  if (!checkURLfields && !checkMatchPatterns) return {}
66
64
  function validateMatchPattern(value, field, node) {
67
65
  if (value == '<all_urls>') {
@@ -76,7 +74,7 @@ export default {
76
74
  return
77
75
  }
78
76
  const [, scheme, host, path] = m
79
- if (scheme != '*' && !allowedSchemes.has(scheme))
77
+ if (scheme != '*' && !allowedSchemeSet.has(scheme))
80
78
  context.report({ node, messageId: 'invalidScheme', data: { field, value }})
81
79
  else if (scheme == 'file' && host != '')
82
80
  context.report({ node, messageId: 'invalidFileHost', data: { field, value }})
@@ -1,12 +1,12 @@
1
1
  import metadata from '../utils/metadata.js'
2
2
 
3
+ const defaultOptions = { versionType: 'semver', allowFutureDate: false, timezone: '' },
4
+ examples = { date: 'YYYY.M.D or YYYY.M.D.N', semver: '1.2.3' }
3
5
  const re = { // oxlint-disable security/detect-unsafe-regex
4
6
  date: /^\d{4}\.(?:0?[1-9]|1[0-2])\.(?:0?[1-9]|[12]\d|3[01])(?:\.\d+)?$/,
5
7
  semver: /^\d+\.\d+\.\d+(?:-[\da-z.-]+)?(?:\+[\da-z.-]+)?$/i
6
8
  } // oxlint-enable security/detect-unsafe-regex
7
9
 
8
- const examples = { date: 'YYYY.M.D or YYYY.M.D.N', semver: '1.2.3' }
9
-
10
10
  export default {
11
11
  meta: {
12
12
  type: 'problem',
@@ -15,7 +15,7 @@ export default {
15
15
  category: 'Possible Errors',
16
16
  recommended: true
17
17
  },
18
- defaultOptions: [{ versionType: 'semver', allowFutureDate: false, timezone: '' }],
18
+ defaultOptions: [defaultOptions],
19
19
  schema: [{
20
20
  type: 'object',
21
21
  properties: {
@@ -44,11 +44,8 @@ export default {
44
44
  },
45
45
 
46
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
47
+ const { versionType, allowFutureDate, timezone } = { ...defaultOptions, ...context.options[0] },
48
+ example = examples[versionType] // oxlint-disable-line security/detect-object-injection
52
49
  const now = timezone
53
50
  ? Object.fromEntries(new Intl.DateTimeFormat('en', {
54
51
  timeZone: timezone, year: 'numeric', month: 'numeric', day: 'numeric'