Skip to content

Commit

Permalink
feat: utils and preset prefix
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu committed Jul 2, 2022
1 parent 00e0cc0 commit ba0e2ca
Show file tree
Hide file tree
Showing 10 changed files with 177 additions and 98 deletions.
19 changes: 16 additions & 3 deletions packages/core/src/config.ts
@@ -1,4 +1,4 @@
import type { Postprocessor, Preprocessor, ResolvedConfig, Shortcut, ThemeExtender, UserConfig, UserConfigDefaults, UserShortcuts } from './types'
import type { Postprocessor, Preprocessor, Preset, ResolvedConfig, Shortcut, ThemeExtender, UserConfig, UserConfigDefaults, UserShortcuts } from './types'
import { clone, isStaticRule, mergeDeep, normalizeVariant, toArray, uniq } from './utils'
import { extractorSplit } from './extractors'
import { DEAFULT_LAYERS } from './constants'
Expand All @@ -11,12 +11,24 @@ export function resolveShortcuts(shortcuts: UserShortcuts): Shortcut[] {
})
}

export function resolvePreset(preset: Preset): Preset {
if (preset.prefix) {
preset.rules?.forEach((i) => {
if (i[2])
i[2].prefix ||= preset.prefix
else
i[2] = { prefix: preset.prefix }
})
}
return preset
}

export function resolveConfig(
userConfig: UserConfig = {},
defaults: UserConfigDefaults = {},
): ResolvedConfig {
const config = Object.assign({}, defaults, userConfig) as UserConfigDefaults
const rawPresets = (config.presets || []).flatMap(toArray)
const rawPresets = (config.presets || []).flatMap(toArray).map(resolvePreset)

const sortedPresets = [
...rawPresets.filter(p => p.enforce === 'pre'),
Expand Down Expand Up @@ -45,7 +57,8 @@ export function resolveConfig(

rules.forEach((rule, i) => {
if (isStaticRule(rule)) {
rulesStaticMap[rule[0]] = [i, rule[1], rule[2], rule]
const prefix = rule[2]?.prefix || ''
rulesStaticMap[prefix + rule[0]] = [i, rule[1], rule[2], rule]
// delete static rules so we can't skip them in matching
// but keep the order
delete rules[i]
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/generator/index.ts
Expand Up @@ -445,7 +445,10 @@ export class UnoGenerator {

// dynamic rules
const [matcher, handler, meta] = rule
const match = processed.match(matcher)
if (meta?.prefix && !processed.startsWith(meta.prefix))
continue
const unprefixed = meta?.prefix ? processed.slice(meta.prefix.length) : processed
const match = unprefixed.match(matcher)
if (!match)
continue

Expand Down
9 changes: 9 additions & 0 deletions packages/core/src/types.ts
Expand Up @@ -157,6 +157,11 @@ export interface RuleMeta {
*/
autocomplete?: Arrayable<AutoCompleteTemplate>

/**
* Matching prefix before this util
*/
prefix?: string

/**
* Internal rules will only be matched for shortcuts but not the user code.
* @default false
Expand Down Expand Up @@ -449,6 +454,10 @@ export interface Preset<Theme extends {} = {}> extends ConfigBase<Theme> {
* Preset options for other tools like IDE to consume
*/
options?: PresetOptions
/**
* Apply prefix to all utilities
*/
prefix?: string
}

export interface GeneratorOptions {
Expand Down
7 changes: 7 additions & 0 deletions packages/preset-mini/src/index.ts
Expand Up @@ -24,6 +24,12 @@ export interface PresetMiniOptions extends PresetOptions {
* @default 'un-'
*/
variablePrefix?: string
/**
* Utils prefix
*
* @default undefined
*/
prefix?: string
}

export const presetMini = (options: PresetMiniOptions = {}): Preset<Theme> => {
Expand All @@ -40,6 +46,7 @@ export const presetMini = (options: PresetMiniOptions = {}): Preset<Theme> => {
? VarPrefixPostprocessor(options.variablePrefix)
: undefined,
preflights,
prefix: options.prefix,
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/preset-uno/src/index.ts
Expand Up @@ -23,6 +23,7 @@ export const presetUno = (options: PresetUnoOptions = {}): Preset<Theme> => {
],
options,
preflights,
prefix: options.prefix,
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/preset-wind/src/index.ts
Expand Up @@ -26,6 +26,7 @@ export const presetWind = (options: PresetWindOptions = {}): Preset<Theme> => {
variants: variants(options),
options,
preflights,
prefix: options.prefix,
}
}

Expand Down
31 changes: 5 additions & 26 deletions test/__snapshots__/prefix.test.ts.snap
@@ -1,29 +1,8 @@
// Vitest Snapshot v1

exports[`prefix 1`] = `
"/* layer: icons */
.dark .uno\\\\:dark\\\\:i-carbon-moon{--un-icon:url(\\"data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 32 32' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M13.502 5.414a15.075 15.075 0 0 0 11.594 18.194a11.113 11.113 0 0 1-7.975 3.39c-.138 0-.278.005-.418 0a11.094 11.094 0 0 1-3.2-21.584M14.98 3a1.002 1.002 0 0 0-.175.016a13.096 13.096 0 0 0 1.825 25.981c.164.006.328 0 .49 0a13.072 13.072 0 0 0 10.703-5.555a1.01 1.01 0 0 0-.783-1.565A13.08 13.08 0 0 1 15.89 4.38A1.015 1.015 0 0 0 14.98 3Z'/%3E%3C/svg%3E\\");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}
/* layer: shortcuts */
.uno\\\\:btn{margin-right:2.5rem;}
.uno\\\\:btn1{margin-left:2.5rem;margin-right:2.5rem;}
/* layer: default */
.uno\\\\:\\\\!p-5px{padding:5px !important;}
.uno\\\\:hover\\\\:p-4:hover{padding:1rem;}
.uno\\\\:pl-10px{padding-left:10px;}
@media (min-width: 640px){
.uno\\\\:sm\\\\:p-6{padding:1.5rem;}
}"
`;

exports[`tailwind prefix 1`] = `
"/* layer: shortcuts */
.uno-btn{margin-right:2.5rem;}
.uno\\\\:btn1{margin-left:2.5rem;margin-right:2.5rem;}
/* layer: default */
.\\\\!uno\\\\:p-5px{padding:5px !important;}
.hover\\\\:uno\\\\:p-4:hover{padding:1rem;}
.uno\\\\:pl-10px{padding-left:10px;}
@media (min-width: 640px){
.sm\\\\:uno\\\\:p-6{padding:1.5rem;}
}"
exports[`prefix > preset prefix 2`] = `
"/* layer: default */
.hover\\\\:foo-p4:hover{padding:1rem;}
.foo-text-red{--un-text-opacity:1;color:rgba(248,113,113,var(--un-text-opacity));}
.bar-bar{color:bar;}"
`;
29 changes: 29 additions & 0 deletions test/__snapshots__/preprocess.test.ts.snap
@@ -0,0 +1,29 @@
// Vitest Snapshot v1

exports[`preprocess > prefix 1`] = `
"/* layer: icons */
.dark .uno\\\\:dark\\\\:i-carbon-moon{--un-icon:url(\\"data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 32 32' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M13.502 5.414a15.075 15.075 0 0 0 11.594 18.194a11.113 11.113 0 0 1-7.975 3.39c-.138 0-.278.005-.418 0a11.094 11.094 0 0 1-3.2-21.584M14.98 3a1.002 1.002 0 0 0-.175.016a13.096 13.096 0 0 0 1.825 25.981c.164.006.328 0 .49 0a13.072 13.072 0 0 0 10.703-5.555a1.01 1.01 0 0 0-.783-1.565A13.08 13.08 0 0 1 15.89 4.38A1.015 1.015 0 0 0 14.98 3Z'/%3E%3C/svg%3E\\");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}
/* layer: shortcuts */
.uno\\\\:btn{margin-right:2.5rem;}
.uno\\\\:btn1{margin-left:2.5rem;margin-right:2.5rem;}
/* layer: default */
.uno\\\\:\\\\!p-5px{padding:5px !important;}
.uno\\\\:hover\\\\:p-4:hover{padding:1rem;}
.uno\\\\:pl-10px{padding-left:10px;}
@media (min-width: 640px){
.uno\\\\:sm\\\\:p-6{padding:1.5rem;}
}"
`;

exports[`preprocess > tailwind prefix 1`] = `
"/* layer: shortcuts */
.uno-btn{margin-right:2.5rem;}
.uno\\\\:btn1{margin-left:2.5rem;margin-right:2.5rem;}
/* layer: default */
.\\\\!uno\\\\:p-5px{padding:5px !important;}
.hover\\\\:uno\\\\:p-4:hover{padding:1rem;}
.uno\\\\:pl-10px{padding-left:10px;}
@media (min-width: 640px){
.sm\\\\:uno\\\\:p-6{padding:1.5rem;}
}"
`;
96 changes: 28 additions & 68 deletions test/prefix.test.ts
@@ -1,75 +1,35 @@
import { createGenerator } from '@unocss/core'
import presetUno from '@unocss/preset-uno'
import presetIcons from '@unocss/preset-icons'
import { expect, test } from 'vitest'
import { describe, expect, test } from 'vitest'
import presetMini from '@unocss/preset-mini'

test('prefix', async () => {
const positive = [
'uno:pl-10px',
'uno:hover:p-4',
'uno:sm:p-6',
'uno:!p-5px',
'uno:btn',
'uno:btn1',
'uno:dark:i-carbon-moon',
]
describe('prefix', () => {
test('preset prefix', async () => {
const uno = createGenerator({
presets: [
presetMini({ prefix: 'foo-' }),
],
rules: [
['bar', { color: 'bar' }, { prefix: 'bar-' }],
],
})

const negative = [
'pl-10px',
'hover:p-4',
'!p-5px',
'btn',
'btn1',
'i-carbon-moon',
]
const { css, matched } = await uno.generate(new Set([
'text-red',
'foo-text-red',
'hover:p4',
'hover:foo-p4',
'bar',
'bar-bar',
]), { preflights: false })

const uno = createGenerator({
preprocess: m => m.startsWith('uno:') ? m.substr(4) : '',
presets: [
presetUno(),
presetIcons(),
],
shortcuts: {
btn: 'mr-10',
btn1: 'ml-10 btn',
},
})
const { css, matched } = await uno.generate(new Set([...positive, ...negative]), { preflights: false })
expect(matched).eql(new Set(positive))
expect(css).toMatchSnapshot()
})

test('tailwind prefix', async () => {
const positive = [
'uno:pl-10px',
'hover:uno:p-4',
'sm:uno:p-6',
'!uno:p-5px',
'uno-btn',
'uno:btn1',
]

const negative = [
'pl-10px',
'hover:p-4',
'!p-5px',
'btn',
'btn1',
]
expect(matched).toMatchInlineSnapshot(`
Set {
"bar-bar",
"foo-text-red",
"hover:foo-p4",
}
`)

const prefixRE = /uno[:-]/
const uno = createGenerator({
preprocess: m => prefixRE.test(m) ? m.replace(prefixRE, '') : '',
presets: [
presetUno(),
presetIcons(),
],
shortcuts: {
btn: 'mr-10',
btn1: 'ml-10 btn',
},
expect(css).toMatchSnapshot()
})
const { css, matched } = await uno.generate(new Set([...positive, ...negative]), { preflights: false })
expect(matched).eql(new Set(positive))
expect(css).toMatchSnapshot()
})
77 changes: 77 additions & 0 deletions test/preprocess.test.ts
@@ -0,0 +1,77 @@
import { createGenerator } from '@unocss/core'
import presetUno from '@unocss/preset-uno'
import presetIcons from '@unocss/preset-icons'
import { describe, expect, test } from 'vitest'

describe('preprocess', () => {
test('prefix', async () => {
const positive = [
'uno:pl-10px',
'uno:hover:p-4',
'uno:sm:p-6',
'uno:!p-5px',
'uno:btn',
'uno:btn1',
'uno:dark:i-carbon-moon',
]

const negative = [
'pl-10px',
'hover:p-4',
'!p-5px',
'btn',
'btn1',
'i-carbon-moon',
]

const uno = createGenerator({
preprocess: m => m.startsWith('uno:') ? m.substr(4) : '',
presets: [
presetUno(),
presetIcons(),
],
shortcuts: {
btn: 'mr-10',
btn1: 'ml-10 btn',
},
})
const { css, matched } = await uno.generate(new Set([...positive, ...negative]), { preflights: false })
expect(matched).eql(new Set(positive))
expect(css).toMatchSnapshot()
})

test('tailwind prefix', async () => {
const positive = [
'uno:pl-10px',
'hover:uno:p-4',
'sm:uno:p-6',
'!uno:p-5px',
'uno-btn',
'uno:btn1',
]

const negative = [
'pl-10px',
'hover:p-4',
'!p-5px',
'btn',
'btn1',
]

const prefixRE = /uno[:-]/
const uno = createGenerator({
preprocess: m => prefixRE.test(m) ? m.replace(prefixRE, '') : '',
presets: [
presetUno(),
presetIcons(),
],
shortcuts: {
btn: 'mr-10',
btn1: 'ml-10 btn',
},
})
const { css, matched } = await uno.generate(new Set([...positive, ...negative]), { preflights: false })
expect(matched).eql(new Set(positive))
expect(css).toMatchSnapshot()
})
})

0 comments on commit ba0e2ca

Please sign in to comment.