Skip to content

Commit

Permalink
feat(eslint-plugin): blocklist meta with message (#3783)
Browse files Browse the repository at this point in the history
Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
  • Loading branch information
enkot and antfu committed May 10, 2024
1 parent 2fd5f51 commit 20ed897
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 18 deletions.
16 changes: 14 additions & 2 deletions packages/core/src/generator/index.ts
@@ -1,5 +1,5 @@
import { createNanoEvents } from '../utils/events'
import type { CSSEntries, CSSObject, DynamicRule, ExtendedTokenInfo, ExtractorContext, GenerateOptions, GenerateResult, ParsedUtil, PreflightContext, PreparedRule, RawUtil, ResolvedConfig, RuleContext, RuleMeta, SafeListContext, Shortcut, ShortcutValue, StringifiedUtil, UserConfig, UserConfigDefaults, UtilObject, Variant, VariantContext, VariantHandler, VariantHandlerContext, VariantMatchedResult } from '../types'
import type { BlocklistMeta, BlocklistValue, CSSEntries, CSSObject, DynamicRule, ExtendedTokenInfo, ExtractorContext, GenerateOptions, GenerateResult, ParsedUtil, PreflightContext, PreparedRule, RawUtil, ResolvedConfig, RuleContext, RuleMeta, SafeListContext, Shortcut, ShortcutValue, StringifiedUtil, UserConfig, UserConfigDefaults, UtilObject, Variant, VariantContext, VariantHandler, VariantHandlerContext, VariantMatchedResult } from '../types'
import { resolveConfig } from '../config'
import { BetterMap, CONTROL_SHORTCUT_NO_MERGE, CountableSet, TwoKeyMap, e, entriesToCss, expandVariantGroup, isCountableSet, isRawUtil, isStaticShortcut, isString, noop, normalizeCSSEntries, normalizeCSSValues, notNull, toArray, uniq, warnOnce } from '../utils'
import { version } from '../../package.json'
Expand Down Expand Up @@ -747,7 +747,19 @@ export class UnoGenerator<Theme extends object = object> {
}

isBlocked(raw: string): boolean {
return !raw || this.config.blocklist.some(e => typeof e === 'function' ? e(raw) : isString(e) ? e === raw : e.test(raw))
return !raw || this.config.blocklist
.map(e => Array.isArray(e) ? e[0] : e)
.some(e => typeof e === 'function' ? e(raw) : isString(e) ? e === raw : e.test(raw))
}

getBlocked(raw: string): [BlocklistValue, BlocklistMeta | undefined] | undefined {
const rule = this.config.blocklist
.find((e) => {
const v = Array.isArray(e) ? e[0] : e
return typeof v === 'function' ? v(raw) : isString(v) ? v === raw : v.test(raw)
})

return rule ? (Array.isArray(rule) ? rule : [rule, undefined]) : undefined
}
}

Expand Down
9 changes: 8 additions & 1 deletion packages/core/src/types.ts
Expand Up @@ -211,7 +211,14 @@ export interface Preflight<Theme extends object = object> {
layer?: string
}

export type BlocklistRule = string | RegExp | ((selector: string) => boolean | null | undefined)
export interface BlocklistMeta {
/**
* Custom message to show why this selector is blocked.
*/
message?: string
}
export type BlocklistValue = string | RegExp | ((selector: string) => boolean | null | undefined)
export type BlocklistRule = BlocklistValue | [BlocklistValue, BlocklistMeta]

export interface VariantHandlerContext {
/**
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-plugin/fixtures/uno.config.ts
Expand Up @@ -9,6 +9,6 @@ export default defineConfig({
transformerVariantGroup(),
],
blocklist: [
(i) => i.includes('blocked')
[(i) => i.includes('blocked'), { message: 'use non-blocked-rule' }]
]
})
12 changes: 7 additions & 5 deletions packages/eslint-plugin/src/rules/blocklist.ts
Expand Up @@ -15,7 +15,7 @@ export default createRule({
recommended: 'recommended',
},
messages: {
'in-blocklist': 'Utility \'{{ name }}\' is in blocklist',
'in-blocklist': '\"{{name}}\" is in blocklist{{reason}}',
},
schema: [],
},
Expand All @@ -26,12 +26,13 @@ export default createRule({
return
const input = node.value
const blocked = syncAction('blocklist', input, context.filename)
blocked.forEach((i) => {
blocked.forEach(([name, meta]) => {
context.report({
node,
messageId: 'in-blocklist',
data: {
name: i,
name,
reason: meta?.message ? `: ${meta.message}` : '',
},
})
})
Expand Down Expand Up @@ -69,12 +70,13 @@ export default createRule({
if (!node?.key?.name)
continue
const blocked = syncAction('blocklist', node.key.name, context.filename)
blocked.forEach((i) => {
blocked.forEach(([name, meta]) => {
context.report({
node,
messageId: 'in-blocklist',
data: {
name: i,
name,
reason: meta?.message ? `: ${meta.message}` : '',
},
})
})
Expand Down
16 changes: 9 additions & 7 deletions packages/eslint-plugin/src/worker.ts
@@ -1,6 +1,6 @@
import process from 'node:process'
import { loadConfig } from '@unocss/config'
import type { UnoGenerator } from '@unocss/core'
import type { BlocklistMeta, UnoGenerator } from '@unocss/core'
import { createGenerator } from '@unocss/core'
import { runAsWorker } from 'synckit'
import { sortRules } from '../../shared-integration/src/sort-rules'
Expand Down Expand Up @@ -35,24 +35,26 @@ async function actionSort(classes: string) {

async function actionBlocklist(classes: string, id?: string) {
const uno = await getGenerator()
const blocked = new Set<string>()
const blocked = new Map<string, BlocklistMeta | undefined>()

const extracted = await uno.applyExtractors(classes, id)
const values = [...extracted.values()]

const matchBlocked = async (raw: string) => {
if (blocked.has(raw))
return
if (uno.isBlocked(raw)) {
blocked.add(raw)
let rule = uno.getBlocked(raw)
if (rule) {
blocked.set(raw, rule[1])
return
}
let current = raw
for (const p of uno.config.preprocess)
current = p(raw)!
const applied = await uno.matchVariants(raw, current)
if (applied && uno.isBlocked(applied[1]))
blocked.add(raw)
rule = applied && uno.getBlocked(applied[1])
if (rule)
blocked.set(raw, rule[1])
}

await Promise.all(values.map(matchBlocked))
Expand All @@ -74,7 +76,7 @@ export async function runAsync(action: string, ...args: any[]): Promise<any> {
}

export function run(action: 'sort', classes: string): string
export function run(action: 'blocklist', classes: string, id?: string): string[]
export function run(action: 'blocklist', classes: string, id?: string): [string, BlocklistMeta | undefined][]
export function run(action: string, ...args: any[]): any {
// @ts-expect-error cast
return runAsync(action, ...args)
Expand Down
9 changes: 7 additions & 2 deletions test/eslint-plugin-worker.test.ts
Expand Up @@ -11,13 +11,18 @@ describe('worker', () => {
],
blocklist: [
'block',
/^text-/,
[/^text-/, { message: 'foo' }],
i => i.includes('green'),
],
})
setGenerator(uno)
const rs = await runAsync('blocklist', 'block !block w-3px bg-green-500 text-red-500')
expect(rs).toEqual(['block', 'bg-green-500', 'text-red-500', '!block'])
expect(rs).toEqual([
['block', undefined],
['bg-green-500', undefined],
['text-red-500', { message: 'foo' }],
['!block', undefined],
])
})
it('sort', async () => {
const uno = createGenerator({
Expand Down

0 comments on commit 20ed897

Please sign in to comment.