提交 5ed424a7 编写于 作者: fxy060608's avatar fxy060608

wip(app): nvue styler

上级 dd99273b
...@@ -107,7 +107,6 @@ export function uniAppNVuePlugin({ ...@@ -107,7 +107,6 @@ export function uniAppNVuePlugin({
const { code, messages } = await parse(source, { const { code, messages } = await parse(source, {
filename, filename,
logLevel: 'WARNING', logLevel: 'WARNING',
combinators: false,
}) })
messages.forEach((message) => { messages.forEach((message) => {
if (message.type === 'warning') { if (message.type === 'warning') {
......
...@@ -2,7 +2,6 @@ import { parse } from '../src' ...@@ -2,7 +2,6 @@ import { parse } from '../src'
async function objectifierRule(input: string) { async function objectifierRule(input: string) {
const { code, messages } = await parse(input, { const { code, messages } = await parse(input, {
combinators: true,
logLevel: 'NOTE', logLevel: 'NOTE',
}) })
return { return {
...@@ -18,9 +17,9 @@ describe('nvue-styler: combinators', () => { ...@@ -18,9 +17,9 @@ describe('nvue-styler: combinators', () => {
) )
expect(json).toEqual({ expect(json).toEqual({
bar: { bar: {
'!left': 5, '': {
}, '!left': 5,
'.bar': { },
'.foo': { '.foo': {
'!left': 0, '!left': 0,
'!right': 10, '!right': 10,
...@@ -29,7 +28,7 @@ describe('nvue-styler: combinators', () => { ...@@ -29,7 +28,7 @@ describe('nvue-styler: combinators', () => {
left: 2, left: 2,
}, },
}, },
'.foobar': { '.foo .bar': { left: 1 } }, foobar: { '.foo .bar': { left: 1 } },
}) })
expect(messages.length).toBe(0) expect(messages.length).toBe(0)
}) })
...@@ -41,9 +40,9 @@ describe('nvue-styler: combinators', () => { ...@@ -41,9 +40,9 @@ describe('nvue-styler: combinators', () => {
) )
expect(json).toEqual({ expect(json).toEqual({
bar: { bar: {
left: 5, '': {
}, left: 5,
'.bar': { },
[`.foo${type}`]: { [`.foo${type}`]: {
left: 5, left: 5,
right: 10, right: 10,
...@@ -52,7 +51,7 @@ describe('nvue-styler: combinators', () => { ...@@ -52,7 +51,7 @@ describe('nvue-styler: combinators', () => {
left: 2, left: 2,
}, },
}, },
'.foobar': { [`.foo${type}.bar${type}`]: { left: 1 } }, foobar: { [`.foo${type}.bar${type}`]: { left: 1 } },
}) })
expect(messages.length).toBe(0) expect(messages.length).toBe(0)
} }
......
...@@ -18,10 +18,12 @@ zIndex: 4; ...@@ -18,10 +18,12 @@ zIndex: 4;
}`) }`)
expect(json).toEqual({ expect(json).toEqual({
foo: { foo: {
color: '#FF0000', '': {
width: 200, color: '#FF0000',
position: 'sticky', width: 200,
zIndex: 4, position: 'sticky',
zIndex: 4,
},
}, },
}) })
expect(messages.length).toBe(0) expect(messages.length).toBe(0)
...@@ -39,13 +41,15 @@ zIndex: 4; ...@@ -39,13 +41,15 @@ zIndex: 4;
}`) }`)
expect(json).toEqual({ expect(json).toEqual({
foo: { foo: {
width: '200px', '': {
paddingLeft: 300, width: '200px',
borderWidth: '1pt', paddingLeft: 300,
left: 0, borderWidth: '1pt',
right: '0px', left: 0,
height: '10rpx', right: '0px',
paddingTop: '11upx', height: '10rpx',
paddingTop: '11upx',
},
}, },
}) })
expect(messages[0]).toEqual( expect(messages[0]).toEqual(
...@@ -75,15 +79,19 @@ zIndex: 4; ...@@ -75,15 +79,19 @@ zIndex: 4;
`) `)
expect(json).toEqual({ expect(json).toEqual({
foo: { foo: {
opacity: 1, '': {
opacity: 1,
},
}, },
bar: { bar: {
opacity: 0.5, '': {
opacity: 0.5,
},
}, },
baz: {},
boo: {},
zero: { zero: {
opacity: 0, '': {
opacity: 0,
},
}, },
}) })
expect(messages[0]).toEqual( expect(messages[0]).toEqual(
...@@ -117,13 +125,14 @@ zIndex: 4; ...@@ -117,13 +125,14 @@ zIndex: 4;
`) `)
expect(json).toEqual({ expect(json).toEqual({
foo: { foo: {
zIndex: 1, '': {
zIndex: 1,
},
}, },
bar: {},
baz: {},
boo: {},
zero: { zero: {
zIndex: 0, '': {
zIndex: 0,
},
}, },
}) })
expect(messages[0]).toEqual( expect(messages[0]).toEqual(
...@@ -171,25 +180,34 @@ zIndex: 4; ...@@ -171,25 +180,34 @@ zIndex: 4;
`) `)
expect(json).toEqual({ expect(json).toEqual({
foo: { foo: {
color: '#FF0000', '': {
backgroundColor: '#ff0000', color: '#FF0000',
backgroundColor: '#ff0000',
},
}, },
bar: { bar: {
color: '#FF0000', '': {
backgroundColor: '#ff0000', color: '#FF0000',
backgroundColor: '#ff0000',
},
}, },
baz: { baz: {
color: '#FF0000', '': {
backgroundColor: '#FFB6C1', color: '#FF0000',
backgroundColor: '#FFB6C1',
},
}, },
rgba: { rgba: {
color: 'rgb(23,0,255)', '': {
backgroundColor: 'rgba(234,45,99,0.4)', color: 'rgb(23,0,255)',
backgroundColor: 'rgba(234,45,99,0.4)',
},
}, },
transparent: { transparent: {
color: 'rgba(0,0,0,0)', '': {
color: 'rgba(0,0,0,0)',
},
}, },
errRgba: {},
}) })
expect(messages[0]).toEqual( expect(messages[0]).toEqual(
expect.objectContaining({ expect.objectContaining({
...@@ -233,8 +251,8 @@ zIndex: 4; ...@@ -233,8 +251,8 @@ zIndex: 4;
.bar { flex-wrap: wrap } .bar { flex-wrap: wrap }
`) `)
expect(json).toEqual({ expect(json).toEqual({
foo: { flexWrap: 'nowrap' }, foo: { '': { flexWrap: 'nowrap' } },
bar: { flexWrap: 'wrap' }, bar: { '': { flexWrap: 'wrap' } },
}) })
expect(messages[0]).toEqual( expect(messages[0]).toEqual(
expect.objectContaining({ expect.objectContaining({
...@@ -275,15 +293,20 @@ zIndex: 4; ...@@ -275,15 +293,20 @@ zIndex: 4;
}, },
}, },
foo: { foo: {
transitionProperty: 'marginTop', '': {
transitionProperty: 'marginTop',
},
}, },
bar: { bar: {
transitionProperty: 'height', '': {
transitionProperty: 'height',
},
}, },
foobar: { foobar: {
transitionProperty: 'marginTop,height', '': {
transitionProperty: 'marginTop,height',
},
}, },
baz: {},
}) })
expect(messages[0]).toEqual( expect(messages[0]).toEqual(
expect.objectContaining({ expect.objectContaining({
...@@ -313,11 +336,15 @@ zIndex: 4; ...@@ -313,11 +336,15 @@ zIndex: 4;
}, },
}, },
foo: { foo: {
transitionDuration: 200, '': {
transitionDelay: 500, transitionDuration: 200,
transitionDelay: 500,
},
}, },
bar: { bar: {
transitionDuration: 200, '': {
transitionDuration: 200,
},
}, },
}) })
expect(messages[0]).toEqual( expect(messages[0]).toEqual(
...@@ -358,12 +385,15 @@ zIndex: 4; ...@@ -358,12 +385,15 @@ zIndex: 4;
}, },
}, },
foo: { foo: {
transitionTimingFunction: 'ease-in-out', '': {
transitionTimingFunction: 'ease-in-out',
},
}, },
bar: { bar: {
transitionTimingFunction: 'cubic-bezier(0.88,1,-0.67,1.37)', '': {
transitionTimingFunction: 'cubic-bezier(0.88,1,-0.67,1.37)',
},
}, },
baz: {},
}) })
expect(messages[0]).toEqual( expect(messages[0]).toEqual(
expect.objectContaining({ expect.objectContaining({
...@@ -384,12 +414,14 @@ zIndex: 4; ...@@ -384,12 +414,14 @@ zIndex: 4;
`) `)
expect(json).toEqual({ expect(json).toEqual({
foo: { foo: {
backgroundColor: '#ff0000', '': {
abc: 123, backgroundColor: '#ff0000',
def: '456px', abc: 123,
ghi: '789pt', def: '456px',
AbcDef: 456, ghi: '789pt',
abcDef: 'abc', AbcDef: 456,
abcDef: 'abc',
},
}, },
}) })
expect(messages[0]).toEqual( expect(messages[0]).toEqual(
...@@ -428,9 +460,11 @@ zIndex: 4; ...@@ -428,9 +460,11 @@ zIndex: 4;
`) `)
expect(json).toEqual({ expect(json).toEqual({
foo: { foo: {
color: '#FF0000', '': {
WebkitTransform: 'rotate(90deg)', color: '#FF0000',
width: '200px', WebkitTransform: 'rotate(90deg)',
width: '200px',
},
}, },
}) })
expect(messages[0]).toEqual( expect(messages[0]).toEqual(
......
...@@ -15,16 +15,18 @@ describe('nvue-styler: parse', () => { ...@@ -15,16 +15,18 @@ describe('nvue-styler: parse', () => {
const { json, messages } = await objectifierRoot(code) const { json, messages } = await objectifierRoot(code)
expect(json).toEqual({ expect(json).toEqual({
foo: { foo: {
color: '#FF0000', '': {
backgroundColor: 'rgba(255,255,255,0.6)', color: '#FF0000',
WebkitTransform: 'rotate(90deg)', backgroundColor: 'rgba(255,255,255,0.6)',
width: '200px', WebkitTransform: 'rotate(90deg)',
left: 0, width: '200px',
right: '0px', left: 0,
borderWidth: '1pt', right: '0px',
fontWeight: '100', borderWidth: '1pt',
fontWeight: '100',
},
}, },
bar: { backgroundColor: '#FF0000' }, bar: { '': { backgroundColor: '#FF0000' } },
}) })
expect(messages[0]).toEqual( expect(messages[0]).toEqual(
expect.objectContaining({ expect.objectContaining({
...@@ -54,15 +56,15 @@ describe('nvue-styler: parse', () => { ...@@ -54,15 +56,15 @@ describe('nvue-styler: parse', () => {
test('fix prop value', async () => { test('fix prop value', async () => {
const code = '.foo {font-size: 200px;}' const code = '.foo {font-size: 200px;}'
const { json, messages } = await objectifierRoot(code) const { json, messages } = await objectifierRoot(code)
expect(json).toEqual({ foo: { fontSize: '200px' } }) expect(json).toEqual({ foo: { '': { fontSize: '200px' } } })
expect(messages.length).toBe(0) expect(messages.length).toBe(0)
}) })
test('ensure number type value', async () => { test('ensure number type value', async () => {
const code = '.foo {line-height: 40;}\n\n .bar {line-height: 20px;}' const code = '.foo {line-height: 40;}\n\n .bar {line-height: 20px;}'
const { json, messages } = await objectifierRoot(code) const { json, messages } = await objectifierRoot(code)
expect(json).toEqual({ expect(json).toEqual({
foo: { lineHeight: 40 }, foo: { '': { lineHeight: 40 } },
bar: { lineHeight: '20px' }, bar: { '': { lineHeight: '20px' } },
}) })
expect(messages.length).toBe(0) expect(messages.length).toBe(0)
}) })
...@@ -71,8 +73,8 @@ describe('nvue-styler: parse', () => { ...@@ -71,8 +73,8 @@ describe('nvue-styler: parse', () => {
'.foo, .bar {font-size: 20;}\n\n .foo {color: #ff5000;}\n\n .bar {color: #000000;}' '.foo, .bar {font-size: 20;}\n\n .foo {color: #ff5000;}\n\n .bar {color: #000000;}'
const { json, messages } = await objectifierRoot(code) const { json, messages } = await objectifierRoot(code)
expect(json).toEqual({ expect(json).toEqual({
foo: { fontSize: 20, color: '#ff5000' }, foo: { '': { fontSize: 20, color: '#ff5000' } },
bar: { fontSize: 20, color: '#000000' }, bar: { '': { fontSize: 20, color: '#000000' } },
}) })
expect(messages.length).toBe(0) expect(messages.length).toBe(0)
}) })
...@@ -81,9 +83,9 @@ describe('nvue-styler: parse', () => { ...@@ -81,9 +83,9 @@ describe('nvue-styler: parse', () => {
'.foo, .bar {font-size: 20; color: #000000}\n\n .foo, .bar, .baz {color: #ff5000; height: 30;}' '.foo, .bar {font-size: 20; color: #000000}\n\n .foo, .bar, .baz {color: #ff5000; height: 30;}'
const { json, messages } = await objectifierRoot(code) const { json, messages } = await objectifierRoot(code)
expect(json).toEqual({ expect(json).toEqual({
foo: { fontSize: 20, color: '#ff5000', height: 30 }, foo: { '': { fontSize: 20, color: '#ff5000', height: 30 } },
bar: { fontSize: 20, color: '#ff5000', height: 30 }, bar: { '': { fontSize: 20, color: '#ff5000', height: 30 } },
baz: { color: '#ff5000', height: 30 }, baz: { '': { color: '#ff5000', height: 30 } },
}) })
expect(messages.length).toBe(0) expect(messages.length).toBe(0)
}) })
...@@ -101,10 +103,12 @@ describe('nvue-styler: parse', () => { ...@@ -101,10 +103,12 @@ describe('nvue-styler: parse', () => {
}, },
}, },
foo: { foo: {
transitionDelay: 200, '': {
transitionDuration: 300, transitionDelay: 200,
transitionProperty: 'marginTop', transitionDuration: 300,
transitionTimingFunction: 'ease-in', transitionProperty: 'marginTop',
transitionTimingFunction: 'ease-in',
},
}, },
}) })
expect(messages[0]).toEqual( expect(messages[0]).toEqual(
...@@ -134,10 +138,12 @@ describe('nvue-styler: parse', () => { ...@@ -134,10 +138,12 @@ describe('nvue-styler: parse', () => {
}, },
}, },
foo: { foo: {
transitionDelay: 200, '': {
transitionDuration: 300, transitionDelay: 200,
transitionProperty: 'transform', transitionDuration: 300,
transitionTimingFunction: 'ease-in-out', transitionProperty: 'transform',
transitionTimingFunction: 'ease-in-out',
},
}, },
}) })
expect(messages[0]).toEqual( expect(messages[0]).toEqual(
...@@ -167,10 +173,12 @@ describe('nvue-styler: parse', () => { ...@@ -167,10 +173,12 @@ describe('nvue-styler: parse', () => {
}, },
}, },
foo: { foo: {
transitionDelay: 200, '': {
transitionDuration: 300, transitionDelay: 200,
transitionProperty: 'marginTop,height', transitionDuration: 300,
transitionTimingFunction: 'ease-in-out', transitionProperty: 'marginTop,height',
transitionTimingFunction: 'ease-in-out',
},
}, },
}) })
expect(messages[0]).toEqual( expect(messages[0]).toEqual(
...@@ -206,21 +214,25 @@ describe('nvue-styler: parse', () => { ...@@ -206,21 +214,25 @@ describe('nvue-styler: parse', () => {
}, },
}, },
foo: { foo: {
fontSize: 20, '': {
color: '#ff5000', fontSize: 20,
height: 30, color: '#ff5000',
transitionDelay: 200, height: 30,
transitionDuration: 300, transitionDelay: 200,
transitionProperty: 'marginTop', transitionDuration: 300,
transitionTimingFunction: 'ease-in', transitionProperty: 'marginTop',
transitionTimingFunction: 'ease-in',
},
}, },
bar: { bar: {
color: '#ff5000', '': {
height: 30, color: '#ff5000',
transitionDelay: 200, height: 30,
transitionDuration: 300, transitionDelay: 200,
transitionProperty: 'marginTop', transitionDuration: 300,
transitionTimingFunction: 'ease-in', transitionProperty: 'marginTop',
transitionTimingFunction: 'ease-in',
},
}, },
}) })
expect(messages[0]).toEqual( expect(messages[0]).toEqual(
...@@ -250,11 +262,13 @@ describe('nvue-styler: parse', () => { ...@@ -250,11 +262,13 @@ describe('nvue-styler: parse', () => {
}, },
}, },
foo: { foo: {
fontSize: 20, '': {
transitionDelay: 1000, fontSize: 20,
transitionDuration: 500, transitionDelay: 1000,
transitionProperty: 'marginTop', transitionDuration: 500,
transitionTimingFunction: 'ease-in-out', transitionProperty: 'marginTop',
transitionTimingFunction: 'ease-in-out',
},
}, },
}) })
expect(messages[0]).toEqual( expect(messages[0]).toEqual(
...@@ -276,24 +290,28 @@ describe('nvue-styler: parse', () => { ...@@ -276,24 +290,28 @@ describe('nvue-styler: parse', () => {
const { json } = await objectifierRoot(code) const { json } = await objectifierRoot(code)
expect(json).toEqual({ expect(json).toEqual({
foo: { foo: {
paddingTop: '20px', '': {
paddingRight: '20px', paddingTop: '20px',
paddingBottom: '20px', paddingRight: '20px',
paddingLeft: '20px', paddingBottom: '20px',
marginTop: '30px', paddingLeft: '20px',
marginRight: 40, marginTop: '30px',
marginBottom: '30px', marginRight: 40,
marginLeft: 40, marginBottom: '30px',
marginLeft: 40,
},
}, },
bar: { bar: {
paddingTop: 10, '': {
paddingRight: '20px', paddingTop: 10,
paddingBottom: '30px', paddingRight: '20px',
paddingLeft: 40, paddingBottom: '30px',
marginTop: '10px', paddingLeft: 40,
marginRight: 20, marginTop: '10px',
marginBottom: 30, marginRight: 20,
marginLeft: 20, marginBottom: 30,
marginLeft: 20,
},
}, },
}) })
}) })
...@@ -303,16 +321,20 @@ describe('nvue-styler: parse', () => { ...@@ -303,16 +321,20 @@ describe('nvue-styler: parse', () => {
const { json } = await objectifierRoot(code) const { json } = await objectifierRoot(code)
expect(json).toEqual({ expect(json).toEqual({
foo: { foo: {
paddingTop: '20px', '': {
paddingRight: '20px', paddingTop: '20px',
paddingBottom: '20px', paddingRight: '20px',
paddingLeft: '30px', paddingBottom: '20px',
paddingLeft: '30px',
},
}, },
bar: { bar: {
marginTop: '10px', '': {
marginRight: 20, marginTop: '10px',
marginBottom: '30px', marginRight: 20,
marginLeft: 20, marginBottom: '30px',
marginLeft: 20,
},
}, },
}) })
}) })
...@@ -322,8 +344,10 @@ describe('nvue-styler: parse', () => { ...@@ -322,8 +344,10 @@ describe('nvue-styler: parse', () => {
const { json } = await objectifierRoot(code) const { json } = await objectifierRoot(code)
expect(json).toEqual({ expect(json).toEqual({
'class-a': { 'class-a': {
color: '#0000ff', '': {
'color:last-child:focus': '#ff0000', color: '#0000ff',
'color:last-child:focus': '#ff0000',
},
}, },
}) })
}) })
......
...@@ -5,20 +5,17 @@ import { ...@@ -5,20 +5,17 @@ import {
SUPPORT_CSS_UNIT, SUPPORT_CSS_UNIT,
hyphenateStyleProperty, hyphenateStyleProperty,
isNumber, isNumber,
COMBINATORS_RE,
} from '../utils' } from '../utils'
import { normalizeMap } from './map' import { normalizeMap } from './map'
const normalized = Symbol('normalized') const normalized = Symbol('normalized')
export interface NormalizeOptions { export interface NormalizeOptions {
combinators?: boolean
logLevel?: 'NOTE' | 'WARNING' | 'ERROR' logLevel?: 'NOTE' | 'WARNING' | 'ERROR'
} }
export function normalize(opts: NormalizeOptions = {}): Plugin { export function normalize(opts: NormalizeOptions = {}): Plugin {
if (!hasOwn(opts, 'combinators')) {
opts.combinators = false
}
if (!hasOwn(opts, 'logLevel')) { if (!hasOwn(opts, 'logLevel')) {
opts.logLevel = 'WARNING' opts.logLevel = 'WARNING'
} }
...@@ -27,26 +24,22 @@ export function normalize(opts: NormalizeOptions = {}): Plugin { ...@@ -27,26 +24,22 @@ export function normalize(opts: NormalizeOptions = {}): Plugin {
Declaration: createDeclarationProcessor(opts), Declaration: createDeclarationProcessor(opts),
} }
if (__NODE_JS__) { if (__NODE_JS__) {
plugin.Rule = createRuleProcessor(opts) plugin.Rule = createRuleProcessor()
} }
return plugin return plugin
} }
function createRuleProcessor({ combinators }: NormalizeOptions) { function createRuleProcessor() {
return (rule: Rule, helper: Helpers) => { return (rule: Rule, helper: Helpers) => {
if ((rule as any)[normalized]) { if ((rule as any)[normalized]) {
return return
} }
const regx = combinators
? /^((?:(?:\.[A-Za-z0-9_\-]+)+[\+\~\> ])*)((?:\.[A-Za-z0-9_\-\:]+)+)$/
: /^(\.)([A-Za-z0-9_\-:]+)$/
rule.selector = rule.selectors rule.selector = rule.selectors
.map((selector) => { .map((selector) => {
selector = selector selector = selector
.replace(/\s*([\+\~\>])\s*/g, '$1') .replace(/\s*([\+\~\>])\s*/g, '$1')
.replace(/\s+/, ' ') .replace(/\s+/, ' ')
if (regx.test(selector)) { if (COMBINATORS_RE.test(selector)) {
return selector return selector
} }
rule.warn( rule.warn(
......
import { Container, Root, Document } from 'postcss' import { Container, Root, Document } from 'postcss'
import { extend, hasOwn } from '@vue/shared' import { extend, hasOwn } from '@vue/shared'
import { COMBINATORS_RE } from './utils'
interface ObjectifierContext { interface ObjectifierContext {
'FONT-FACE': Record<string, unknown>[] 'FONT-FACE': Record<string, unknown>[]
...@@ -63,34 +64,38 @@ function transformSelector( ...@@ -63,34 +64,38 @@ function transformSelector(
result: Record<string, unknown | Record<string, unknown>>, result: Record<string, unknown | Record<string, unknown>>,
context: ObjectifierContext context: ObjectifierContext
) { ) {
let className = selector.slice(1) const res = selector.match(COMBINATORS_RE)
const lastDotIndex = className.lastIndexOf('.') if (!res) {
if (lastDotIndex > 0) { return
className = className.substring(lastDotIndex + 1)
} }
const pseudoIndex = className.indexOf(':') let parentSelector = res[1].trim()
let curSelector = res[2].trim().substring(1)
const pseudoIndex = curSelector.indexOf(':')
if (pseudoIndex > -1) { if (pseudoIndex > -1) {
const pseudoClass = className.slice(pseudoIndex) const pseudoClass = curSelector.slice(pseudoIndex)
className = className.slice(0, pseudoIndex) curSelector = curSelector.slice(0, pseudoIndex)
Object.keys(body).forEach(function (name) { Object.keys(body).forEach(function (name) {
body[name + pseudoClass] = body[name] body[name + pseudoClass] = body[name]
delete body[name] delete body[name]
}) })
} }
transition(className, body, context) transition(curSelector, body, context)
if (lastDotIndex > 0) { if (!Object.keys(body).length) {
className = '.' + className return
result = (result[className] || (result[className] = {})) as Record<
string,
unknown
>
className = selector.substring(0, lastDotIndex + 1).trim()
} }
if (result[className]) { result = (result[curSelector] || (result[curSelector] = {})) as Record<
string,
unknown
>
if (result[parentSelector]) {
// clone // clone
result[className] = processImportant(extend({}, result[className], body)) result[parentSelector] = processImportant(
extend({}, result[parentSelector], body)
)
} else { } else {
result[className] = body result[parentSelector] = body
} }
} }
......
import type { Declaration, Source } from 'postcss' import type { Declaration, Source } from 'postcss'
export const COMBINATORS_RE =
/^((?:(?:\.[A-Za-z0-9_\-]+)+[\+\~\> ])*)((?:\.[A-Za-z0-9_\-\:]+)+)$/
export type TransformDecl = (decl: Declaration) => Declaration[] export type TransformDecl = (decl: Declaration) => Declaration[]
export type Normalize = (v: string | number) => { export type Normalize = (v: string | number) => {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册