Skip to content

Commit 6dc74b0

Browse files
perf(core): prefilter dynamic rules (#5232)
1 parent 23eea04 commit 6dc74b0

4 files changed

Lines changed: 84 additions & 1 deletion

File tree

packages-engine/core/src/config.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,39 @@ export function resolveShortcuts<Theme extends object = object>(shortcuts: UserS
1111
})
1212
}
1313

14+
function createDynamicRuleFilters<Theme extends object>(rules: readonly DynamicRule<Theme>[]) {
15+
const filterSources = new Map<string, string[]>()
16+
const fallback: DynamicRule<Theme>[] = []
17+
18+
for (const rule of rules) {
19+
const [matcher, , meta] = rule
20+
// These patterns cannot be safely embedded into a larger alternation.
21+
if (meta?.prefix || matcher.global || matcher.sticky || /\\(?:[1-9]|k<)|\(\?</.test(matcher.source)) {
22+
fallback.push(rule)
23+
continue
24+
}
25+
const sources = filterSources.get(matcher.flags) ?? []
26+
sources.push(`(?:${matcher.source})`)
27+
filterSources.set(matcher.flags, sources)
28+
}
29+
30+
try {
31+
return {
32+
fallback,
33+
filters: Array.from(
34+
filterSources,
35+
([flags, sources]) => new RegExp(sources.join('|'), flags),
36+
),
37+
}
38+
}
39+
catch {
40+
return {
41+
fallback: [...rules],
42+
filters: [],
43+
}
44+
}
45+
}
46+
1447
const __RESOLVED = '_uno_resolved'
1548

1649
/**
@@ -227,6 +260,8 @@ export async function resolveConfig<Theme extends object = object>(
227260
for (const p of sources)
228261
p?.configResolved?.(resolved)
229262

263+
resolved.rulesDynamicFilter = createDynamicRuleFilters(resolved.rulesDynamic)
264+
230265
return resolved
231266
}
232267

packages-engine/core/src/generator.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -629,8 +629,14 @@ class UnoGeneratorInternal<Theme extends object = object> {
629629
}
630630
}
631631

632+
const dynamicRuleFilter = this.config.rulesDynamicFilter
633+
const dynamicRules = dynamicRuleFilter?.filters.length
634+
&& !dynamicRuleFilter.filters.some(filter => filter.test(processed))
635+
? dynamicRuleFilter.fallback
636+
: this.config.rulesDynamic
637+
632638
// match rules
633-
for (const rule of this.config.rulesDynamic) {
639+
for (const rule of dynamicRules) {
634640
const [matcher, handler, meta] = rule
635641
// ignore internal rules
636642
if (meta?.internal && !internal)

packages-engine/core/src/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,14 @@ export interface ResolvedConfig<Theme extends object = object> extends Omit<
922922
rulesSize: number
923923
rules: readonly Rule<Theme>[]
924924
rulesDynamic: readonly DynamicRule<Theme>[]
925+
/**
926+
* Prefilter for dynamic rules.
927+
* @internal
928+
*/
929+
rulesDynamicFilter?: {
930+
fallback: readonly DynamicRule<Theme>[]
931+
filters: readonly RegExp[]
932+
}
925933
rulesStaticMap: Record<string, StaticRule | undefined>
926934
autocomplete: {
927935
templates: (AutoCompleteFunction | AutoCompleteTemplate)[]
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { createGenerator } from '@unocss/core'
2+
import { expect, it } from 'vitest'
3+
4+
it('supports dynamic rules that cannot be combined into a filter', async () => {
5+
const uno = await createGenerator({
6+
rules: [
7+
[/^(foo)-\1$/, () => ({ color: 'red' })],
8+
[/(?<=lookbehind-)value$/, () => ({ color: 'green' })],
9+
[/^(?<name>baz)$/, () => ({ color: 'blue' })],
10+
[/^global-.+$/g, () => ({ color: 'orange' })],
11+
[/^qux$/i, () => ({ color: 'purple' })],
12+
[/^plain-.+$/, () => ({ color: 'black' })],
13+
],
14+
})
15+
16+
const { matched } = await uno.generate(new Set([
17+
'foo-foo',
18+
'lookbehind-value',
19+
'baz',
20+
'global-value',
21+
'QUX',
22+
'plain-value',
23+
'unmatched',
24+
]), { preflights: false })
25+
26+
expect([...matched]).toEqual([
27+
'foo-foo',
28+
'lookbehind-value',
29+
'baz',
30+
'global-value',
31+
'QUX',
32+
'plain-value',
33+
])
34+
})

0 commit comments

Comments
 (0)