eslint-doc-generator
Advanced tools
Comparing version 0.17.0 to 0.18.0
@@ -1,6 +0,7 @@ | ||
import type { Plugin, ConfigsToRules, ConfigEmojis, RuleSeverity } from './types.js'; | ||
import type { Plugin, ConfigsToRules, ConfigEmojis, RuleSeverity, SEVERITY_TYPE } from './types.js'; | ||
/** | ||
* Get config names that a given rule belongs to. | ||
* @param severity - Include configs that set the rule to this severity. Omit to allow any severity. | ||
*/ | ||
export declare function getConfigsForRule(ruleName: string, configsToRules: ConfigsToRules, pluginPrefix: string, severity: Set<RuleSeverity>): string[]; | ||
export declare function getConfigsForRule(ruleName: string, configsToRules: ConfigsToRules, pluginPrefix: string, severity?: Set<RuleSeverity>): string[]; | ||
/** | ||
@@ -10,1 +11,14 @@ * Parse the options, check for errors, and set defaults. | ||
export declare function parseConfigEmojiOptions(plugin: Plugin, configEmoji?: string[]): ConfigEmojis; | ||
/** | ||
* Find the representation of a config to display. | ||
* @param configEmojis - known list of configs and corresponding emojis | ||
* @param configName - name of the config to find an emoji for | ||
* @param options | ||
* @param options.severity - if present, decorate the config's emoji for the given severity level | ||
* @param options.fallback - if true and no emoji is found, choose whether to fallback to a generic config emoji or a badge | ||
* @returns the string to display for the config | ||
*/ | ||
export declare function findConfigEmoji(configEmojis: ConfigEmojis, configName: string, options?: { | ||
severity?: SEVERITY_TYPE; | ||
fallback?: 'badge' | 'emoji'; | ||
}): string | undefined; |
@@ -1,4 +0,5 @@ | ||
import { EMOJI_CONFIGS, EMOJI_CONFIG } from './emojis.js'; | ||
import { EMOJI_CONFIGS, EMOJI_CONFIG, EMOJI_CONFIG_WARN, EMOJI_CONFIG_OFF, } from './emojis.js'; | ||
/** | ||
* Get config names that a given rule belongs to. | ||
* @param severity - Include configs that set the rule to this severity. Omit to allow any severity. | ||
*/ | ||
@@ -10,9 +11,9 @@ export function getConfigsForRule(ruleName, configsToRules, pluginPrefix, severity) { | ||
const value = rules[`${pluginPrefix}/${ruleName}`]; | ||
const isEnabled = ((typeof value === 'string' || typeof value === 'number') && | ||
severity.has(value)) || | ||
const isSet = ((typeof value === 'string' || typeof value === 'number') && | ||
(!severity || severity.has(value))) || | ||
(typeof value === 'object' && | ||
Array.isArray(value) && | ||
value.length > 0 && | ||
severity.has(value[0])); | ||
if (isEnabled) { | ||
(!severity || severity.has(value[0]))); | ||
if (isSet) { | ||
configNames.push(configName); | ||
@@ -58,1 +59,36 @@ } | ||
} | ||
/** | ||
* Find the representation of a config to display. | ||
* @param configEmojis - known list of configs and corresponding emojis | ||
* @param configName - name of the config to find an emoji for | ||
* @param options | ||
* @param options.severity - if present, decorate the config's emoji for the given severity level | ||
* @param options.fallback - if true and no emoji is found, choose whether to fallback to a generic config emoji or a badge | ||
* @returns the string to display for the config | ||
*/ | ||
export function findConfigEmoji(configEmojis, configName, options) { | ||
let emoji = configEmojis.find((configEmoji) => configEmoji.config === configName)?.emoji; | ||
if (!emoji) { | ||
if (options?.fallback === 'badge') { | ||
emoji = `![${configName}][]`; | ||
} | ||
else if (options?.fallback === 'emoji') { | ||
emoji = EMOJI_CONFIG; | ||
} | ||
else { | ||
// No fallback. | ||
return undefined; // eslint-disable-line unicorn/no-useless-undefined | ||
} | ||
} | ||
switch (options?.severity) { | ||
case 'warn': | ||
return `${emoji}${ | ||
// Conditional is to avoid double emoji. | ||
emoji === EMOJI_CONFIG_WARN ? '' : `<sup>${EMOJI_CONFIG_WARN}</sup>`}`; | ||
case 'off': | ||
// Conditional is to avoid double emoji. | ||
return `${emoji}${emoji === EMOJI_CONFIG_OFF ? '' : `<sup>${EMOJI_CONFIG_OFF}</sup>`}`; | ||
default: | ||
return emoji; | ||
} | ||
} |
@@ -11,2 +11,7 @@ export declare const EMOJI_CONFIGS: { | ||
stylistic: string; | ||
ts: string; | ||
type: string; | ||
typed: string; | ||
types: string; | ||
typescript: string; | ||
warning: string; | ||
@@ -16,2 +21,4 @@ warnings: string; | ||
export declare const EMOJI_CONFIG = "\uD83D\uDCBC"; | ||
export declare const EMOJI_CONFIG_WARN = "\u26A0\uFE0F"; | ||
export declare const EMOJI_CONFIG_OFF = "\uD83D\uDEAB"; | ||
export declare const EMOJI_FIXABLE = "\uD83D\uDD27"; | ||
@@ -18,0 +25,0 @@ export declare const EMOJI_HAS_SUGGESTIONS = "\uD83D\uDCA1"; |
// Default emojis for common configs. | ||
const EMOJI_A11Y = '♿'; | ||
const EMOJI_ERROR = '❗'; | ||
const EMOJI_STYLE = '🎨'; | ||
const EMOJI_TYPESCRIPT = '⌨️'; | ||
const EMOJI_WARNING = '⚠️'; | ||
export const EMOJI_CONFIGS = { | ||
a11y: '⌨️', | ||
accessibility: '⌨️', | ||
a11y: EMOJI_A11Y, | ||
accessibility: EMOJI_A11Y, | ||
all: '🌐', | ||
error: '❗', | ||
errors: '❗', | ||
error: EMOJI_ERROR, | ||
errors: EMOJI_ERROR, | ||
recommended: '✅', | ||
strict: '🔒', | ||
style: '🎨', | ||
stylistic: '🎨', | ||
warning: '⚠️', | ||
warnings: '⚠️', | ||
style: EMOJI_STYLE, | ||
stylistic: EMOJI_STYLE, | ||
ts: EMOJI_TYPESCRIPT, | ||
type: EMOJI_TYPESCRIPT, | ||
typed: EMOJI_TYPESCRIPT, | ||
types: EMOJI_TYPESCRIPT, | ||
typescript: EMOJI_TYPESCRIPT, | ||
warning: EMOJI_WARNING, | ||
warnings: EMOJI_WARNING, | ||
}; | ||
// General configs. | ||
export const EMOJI_CONFIG = '💼'; | ||
export const EMOJI_CONFIG_WARN = '⚠️'; | ||
export const EMOJI_CONFIG_OFF = '🚫'; | ||
// Fixers. | ||
@@ -18,0 +30,0 @@ export const EMOJI_FIXABLE = '🔧'; |
import { EMOJI_DEPRECATED, EMOJI_FIXABLE, EMOJI_HAS_SUGGESTIONS, EMOJI_CONFIG, EMOJI_REQUIRES_TYPE_CHECKING, EMOJI_TYPE, } from './emojis.js'; | ||
import { getConfigsForRule } from './configs.js'; | ||
import { COLUMN_TYPE, SEVERITY_ERROR, } from './types.js'; | ||
import { getConfigsForRule, findConfigEmoji } from './configs.js'; | ||
import { COLUMN_TYPE, SEVERITY_ERROR, SEVERITY_WARN, SEVERITY_OFF, SEVERITY_TYPE, } from './types.js'; | ||
import { RULE_TYPE_MESSAGES_LEGEND, RULE_TYPES } from './rule-type.js'; | ||
@@ -23,9 +23,12 @@ /** | ||
const ruleNames = Object.keys(plugin.rules); | ||
const configsThatEnableAnyRule = Object.entries(configsToRules) | ||
.filter(([configName, _config]) => ruleNames.some((ruleName) => getConfigsForRule(ruleName, configsToRules, pluginPrefix, SEVERITY_ERROR).includes(configName))) | ||
.map(([configName, _config]) => configName); | ||
const configNamesWithoutIgnored = configsThatEnableAnyRule.filter((configName) => !ignoreConfig?.includes(configName)); | ||
const configNamesWithoutIgnored = Object.entries(configsToRules) | ||
.filter(([configName, _config]) => | ||
// Only consider configs that configure at least one of the plugin's rules. | ||
ruleNames.some((ruleName) => getConfigsForRule(ruleName, configsToRules, pluginPrefix).includes(configName))) | ||
// Filter out ignored configs. | ||
.filter(([configName]) => !ignoreConfig?.includes(configName)) | ||
.map(([configName]) => configName); | ||
const legends = []; | ||
if ((configNamesWithoutIgnored.length > 1 || | ||
!configEmojis.find((configEmoji) => configNamesWithoutIgnored?.includes(configEmoji.config))?.emoji) && | ||
!configEmojis.find((configEmoji) => configNamesWithoutIgnored.includes(configEmoji.config))?.emoji) && | ||
// If any configs are using the generic config emoji, then don't display the generic config legend. | ||
@@ -39,10 +42,37 @@ !configEmojis | ||
legends.push(...configNamesWithoutIgnored.flatMap((configName) => { | ||
const emoji = configEmojis.find((configEmoji) => configEmoji.config === configName)?.emoji; | ||
if (!emoji) { | ||
if (!findConfigEmoji(configEmojis, configName)) { | ||
// No legend for this config as it has no emoji. | ||
return []; | ||
} | ||
return [ | ||
`${emoji} Enabled in the \`${configName}\` ${configLinkOrWord}.`, | ||
]; | ||
let hasErrorRule = false; | ||
let hasWarnRule = false; | ||
let hasOffRule = false; | ||
for (const ruleName of ruleNames) { | ||
if (getConfigsForRule(ruleName, configsToRules, pluginPrefix, SEVERITY_ERROR).includes(configName)) { | ||
hasErrorRule = true; | ||
} | ||
if (getConfigsForRule(ruleName, configsToRules, pluginPrefix, SEVERITY_WARN).includes(configName)) { | ||
hasWarnRule = true; | ||
} | ||
if (getConfigsForRule(ruleName, configsToRules, pluginPrefix, SEVERITY_OFF).includes(configName)) { | ||
hasOffRule = true; | ||
} | ||
} | ||
const legendsForThisConfig = []; | ||
if (hasErrorRule) { | ||
legendsForThisConfig.push(`${findConfigEmoji(configEmojis, configName, { | ||
severity: SEVERITY_TYPE.error, | ||
})} Enabled in the \`${configName}\` ${configLinkOrWord}.`); | ||
} | ||
if (hasWarnRule) { | ||
legendsForThisConfig.push(`${findConfigEmoji(configEmojis, configName, { | ||
severity: SEVERITY_TYPE.warn, | ||
})} Warns in the \`${configName}\` ${configLinkOrWord}.`); | ||
} | ||
if (hasOffRule) { | ||
legendsForThisConfig.push(`${findConfigEmoji(configEmojis, configName, { | ||
severity: SEVERITY_TYPE.off, | ||
})} Disabled in the \`${configName}\` ${configLinkOrWord}.`); | ||
} | ||
return legendsForThisConfig; | ||
})); | ||
@@ -49,0 +79,0 @@ return legends; |
import { BEGIN_RULE_LIST_MARKER, END_RULE_LIST_MARKER } from './markers.js'; | ||
import { EMOJI_DEPRECATED, EMOJI_FIXABLE, EMOJI_HAS_SUGGESTIONS, EMOJI_REQUIRES_TYPE_CHECKING, } from './emojis.js'; | ||
import { getConfigsForRule } from './configs.js'; | ||
import { getConfigsForRule, findConfigEmoji } from './configs.js'; | ||
import { getColumns, COLUMN_HEADER } from './rule-list-columns.js'; | ||
@@ -9,3 +9,3 @@ import { findSectionHeader } from './markdown.js'; | ||
import { relative } from 'node:path'; | ||
import { COLUMN_TYPE, SEVERITY_ERROR } from './types.js'; | ||
import { COLUMN_TYPE, SEVERITY_ERROR, SEVERITY_WARN, SEVERITY_OFF, SEVERITY_TYPE, } from './types.js'; | ||
import { markdownTable } from 'markdown-table'; | ||
@@ -42,8 +42,31 @@ import camelCase from 'camelcase'; | ||
const badges = []; | ||
const configsEnabled = getConfigsForRule(rule.name, configsToRules, pluginPrefix, SEVERITY_ERROR).filter((configName) => !ignoreConfig?.includes(configName)); | ||
const configsToRulesWithoutIgnored = Object.fromEntries(Object.entries(configsToRules).filter(([configName]) => !ignoreConfig?.includes(configName))); | ||
const configsEnabled = getConfigsForRule(rule.name, configsToRulesWithoutIgnored, pluginPrefix, SEVERITY_ERROR); | ||
const configsWarn = getConfigsForRule(rule.name, configsToRulesWithoutIgnored, pluginPrefix, SEVERITY_WARN); | ||
const configsOff = getConfigsForRule(rule.name, configsToRulesWithoutIgnored, pluginPrefix, SEVERITY_OFF); | ||
// Find the emoji for each config or otherwise use a badge that can be defined in markdown. | ||
for (const configName of configsEnabled) { | ||
// Find the emoji for the config or otherwise use a badge that can be defined in markdown. | ||
const emoji = configEmojis.find((configEmoji) => configEmoji.config === configName)?.emoji; | ||
badges.push(emoji ?? `![${configName}][]`); | ||
badges.push( | ||
// @ts-expect-error -- will always be a string thanks to fallback | ||
findConfigEmoji(configEmojis, configName, { | ||
severity: SEVERITY_TYPE.error, | ||
fallback: 'badge', | ||
})); | ||
} | ||
for (const configName of configsWarn) { | ||
badges.push( | ||
// @ts-expect-error -- will always be a string thanks to fallback | ||
findConfigEmoji(configEmojis, configName, { | ||
severity: SEVERITY_TYPE.warn, | ||
fallback: 'badge', | ||
})); | ||
} | ||
for (const configName of configsOff) { | ||
badges.push( | ||
// @ts-expect-error -- will always be a string thanks to fallback | ||
findConfigEmoji(configEmojis, configName, { | ||
severity: SEVERITY_TYPE.off, | ||
fallback: 'badge', | ||
})); | ||
} | ||
return badges.join(' '); | ||
@@ -78,4 +101,4 @@ } | ||
const headerStrOrFn = COLUMN_HEADER[columnType]; | ||
const configsThatEnableAnyRule = Object.entries(configsToRules) | ||
.filter(([configName, config]) => Object.keys(config).some((ruleNameWithPrefix) => getConfigsForRule(ruleNameWithPrefix.replace(`${pluginPrefix}/`, ''), configsToRules, pluginPrefix, SEVERITY_ERROR).includes(configName))) | ||
const configsThatSetAnyRule = Object.entries(configsToRules) | ||
.filter(([configName, config]) => Object.keys(config).some((ruleNameWithPrefix) => getConfigsForRule(ruleNameWithPrefix.replace(`${pluginPrefix}/`, ''), configsToRules, pluginPrefix).includes(configName))) | ||
.map(([configName, _config]) => configName); | ||
@@ -85,3 +108,3 @@ return [ | ||
? headerStrOrFn({ | ||
configNames: configsThatEnableAnyRule, | ||
configNames: configsThatSetAnyRule, | ||
configEmojis, | ||
@@ -88,0 +111,0 @@ ignoreConfig, |
@@ -1,4 +0,3 @@ | ||
import type { Plugin, ConfigsToRules, ConfigEmojis } from './types.js'; | ||
import { Plugin, ConfigsToRules, ConfigEmojis, NOTICE_TYPE } from './types.js'; | ||
import { RuleDocTitleFormat } from './rule-doc-title-format.js'; | ||
import { NOTICE_TYPE } from './types.js'; | ||
export declare const NOTICE_TYPE_DEFAULT_PRESENCE_AND_ORDERING: { | ||
@@ -5,0 +4,0 @@ [key in NOTICE_TYPE]: boolean; |
import { END_RULE_HEADER_MARKER } from './markers.js'; | ||
import { EMOJI_DEPRECATED, EMOJI_FIXABLE, EMOJI_HAS_SUGGESTIONS, EMOJI_CONFIG, EMOJI_REQUIRES_TYPE_CHECKING, } from './emojis.js'; | ||
import { getConfigsForRule } from './configs.js'; | ||
import { findConfigEmoji, getConfigsForRule } from './configs.js'; | ||
import { SEVERITY_TYPE, NOTICE_TYPE, SEVERITY_ERROR, SEVERITY_OFF, SEVERITY_WARN, } from './types.js'; | ||
import { RULE_TYPE_MESSAGES_NOTICES } from './rule-type.js'; | ||
import { RULE_DOC_TITLE_FORMAT_DEFAULT, } from './rule-doc-title-format.js'; | ||
import { NOTICE_TYPE, SEVERITY_ERROR, SEVERITY_OFF, SEVERITY_WARN, } from './types.js'; | ||
export const NOTICE_TYPE_DEFAULT_PRESENCE_AND_ORDERING = { | ||
@@ -23,3 +23,2 @@ // Object keys ordered in display order. | ||
// Configs notice varies based on whether the rule is configured in one or more configs. | ||
// eslint-disable-next-line complexity | ||
[NOTICE_TYPE.CONFIGS]: ({ configsEnabled, configsWarn, configsDisabled, configEmojis, urlConfigs, }) => { | ||
@@ -44,12 +43,21 @@ // Add link to configs documentation if provided. | ||
else if (configsEnabled.length > 0) { | ||
emoji = | ||
configEmojis.find((configEmoji) => configEmoji.config === configsEnabled[0])?.emoji ?? EMOJI_CONFIG; | ||
// @ts-expect-error -- will always be a string thanks to fallback | ||
emoji = findConfigEmoji(configEmojis, configsEnabled[0], { | ||
severity: SEVERITY_TYPE.error, | ||
fallback: 'emoji', | ||
}); | ||
} | ||
else if (configsWarn.length > 0) { | ||
emoji = | ||
configEmojis.find((configEmoji) => configEmoji.config === configsWarn[0])?.emoji ?? EMOJI_CONFIG; | ||
// @ts-expect-error -- will always be a string thanks to fallback | ||
emoji = findConfigEmoji(configEmojis, configsWarn[0], { | ||
severity: SEVERITY_TYPE.warn, | ||
fallback: 'emoji', | ||
}); | ||
} | ||
else if (configsDisabled.length > 0) { | ||
emoji = | ||
configEmojis.find((configEmoji) => configEmoji.config === configsDisabled[0])?.emoji ?? EMOJI_CONFIG; | ||
// @ts-expect-error -- will always be a string thanks to fallback | ||
emoji = findConfigEmoji(configEmojis, configsDisabled[0], { | ||
severity: SEVERITY_TYPE.off, | ||
fallback: 'emoji', | ||
}); | ||
} | ||
@@ -85,5 +93,5 @@ // List of configs that enable the rule. | ||
const SENTENCE_WARN = configsWarn.length > 1 | ||
? `This rule will _warn_ in the following ${configsLinkOrWord}: ${configsWarnCSV}.` | ||
? `This rule _warns_ in the following ${configsLinkOrWord}: ${configsWarnCSV}.` | ||
: configsWarn.length === 1 | ||
? `This rule will _warn_ in the \`${configsWarn?.[0]}\` ${configLinkOrWord}.` | ||
? `This rule _warns_ in the \`${configsWarn?.[0]}\` ${configLinkOrWord}.` | ||
: undefined; | ||
@@ -90,0 +98,0 @@ // Complete sentence for configs that disable the rule. |
@@ -10,2 +10,7 @@ import type { TSESLint, JSONSchema } from '@typescript-eslint/utils'; | ||
export declare const SEVERITY_OFF: Set<TSESLint.Linter.RuleLevel>; | ||
export declare enum SEVERITY_TYPE { | ||
'error' = "error", | ||
'warn' = "warn", | ||
'off' = "off" | ||
} | ||
export declare type ConfigsToRules = Record<string, Rules>; | ||
@@ -12,0 +17,0 @@ export interface RuleDetails { |
@@ -5,2 +5,8 @@ // Custom types. | ||
export const SEVERITY_OFF = new Set([0, 'off']); | ||
export var SEVERITY_TYPE; | ||
(function (SEVERITY_TYPE) { | ||
SEVERITY_TYPE["error"] = "error"; | ||
SEVERITY_TYPE["warn"] = "warn"; | ||
SEVERITY_TYPE["off"] = "off"; | ||
})(SEVERITY_TYPE || (SEVERITY_TYPE = {})); | ||
/** | ||
@@ -7,0 +13,0 @@ * Rule doc notices. |
{ | ||
"name": "eslint-doc-generator", | ||
"version": "0.17.0", | ||
"version": "0.18.0", | ||
"description": "Automatic documentation generator for ESLint plugins and rules.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -10,3 +10,3 @@ # eslint-doc-generator | ||
- `README.md` rules table | ||
- Rule doc titles and notices | ||
- Rule doc titles and config/fixable/etc. notices | ||
@@ -46,2 +46,3 @@ Also performs some basic section consistency checks on rule docs: | ||
- Add any [config options](#configuration-options) in the `update:eslint-docs` script only | ||
- Alternative scripts may be needed with [build tools](#build-tools) or [prettier](#prettier) | ||
@@ -94,5 +95,5 @@ ```json | ||
🎨 This rule will _warn_ in the `stylistic` config. | ||
🎨<sup>⚠️</sup> This rule _warns_ in the `stylistic` config. | ||
🎨 This rule is _disabled_ in the `stylistic` config. | ||
🎨<sup>🚫</sup> This rule is _disabled_ in the `stylistic` config. | ||
@@ -137,2 +138,4 @@ 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix). | ||
✅ Enabled in the `recommended` configuration.\ | ||
✅<sup>⚠️</sup> Warns in the `recommended` configuration.\ | ||
✅<sup>🚫</sup> Disabled in the `recommended` configuration.\ | ||
🎨 Enabled in the `stylistic` configuration.\ | ||
@@ -198,12 +201,47 @@ 🔧 Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).\ | ||
Where `no-foo` is the rule name, `Do not use foo` is the rule description, and `eslint-plugin-test` is the plugin name. | ||
Where `no-foo` is the rule name, `Disallow use of foo` is the rule description, and `eslint-plugin-test` is the plugin name. | ||
| Value | Example | | ||
| :-- | :-- | | ||
| `desc` | `# Do not use foo` | | ||
| `desc-parens-name` | `# Do not use foo (no-foo)` | | ||
| `desc-parens-prefix-name` (default) | `# Do not use foo (test/no-foo)` | | ||
| `desc` | `# Disallow use of foo` | | ||
| `desc-parens-name` | `# Disallow use of foo (no-foo)` | | ||
| `desc-parens-prefix-name` (default) | `# Disallow use of foo (test/no-foo)` | | ||
| `name` | `# no-foo` | | ||
`prefix-name` | `# test/no-foo` | | ||
## Compatibility | ||
### Build tools | ||
If you have a build step for your code like [Babel](https://babeljs.io/) or [TypeScript](https://www.typescriptlang.org/), you may need to adjust your scripts to run your build before this tool: | ||
```json | ||
{ | ||
"build": "tsc", | ||
"update:eslint-docs": "npm run build && eslint-doc-generator" | ||
} | ||
``` | ||
### markdownlint | ||
The output of this tool should be compatible with [markdownlint](https://github.com/DavidAnson/markdownlint) which you might use to lint your markdown. However, if any of your ESLint configs disable your rules or set them to warn, you'll need to exempt the [`<sup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sup) (superscript) element from [no-inline-html](https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md033---inline-html): | ||
```json | ||
{ | ||
"no-inline-html": { "allowed_elements": ["sup"] } | ||
} | ||
``` | ||
### prettier | ||
If you use [prettier](https://prettier.io/) to format your markdown, you may need to adjust your scripts to run prettier formatting after running this tool: | ||
```json | ||
{ | ||
"format": "prettier --write .", | ||
"lint:eslint-docs": "npm run update:eslint-docs && git diff --exit-code", | ||
"update:eslint-docs": "eslint-doc-generator && npm run format" | ||
} | ||
``` | ||
## Related | ||
@@ -210,0 +248,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
90427
1587
249