-
-
Notifications
You must be signed in to change notification settings - Fork 106
/
Copy pathtransform.ts
94 lines (85 loc) Β· 3.1 KB
/
transform.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/* eslint-disable no-continue, no-loop-func, no-cond-assign */
import type { StyleGenerator } from '@xstyled/system'
import { mediaGetters } from './mediaGetters'
// prop name is an ident: word chars, underscore and dash.
const PROP_CHAR = `[-\\w]`
// prop value consists of non-semis and no curly braces unless backslash-escaped.
// This uses [\s\S] instead of . because IE11 doesn't support the s flag.
const VALUE_CHAR = `(?:\\\\[\\s\\S]|[^\\\\;{}])`
// prettier-ignore
const PROP_PATT = (
`(${PROP_CHAR}+)` + // capture prop name
`(\\s*:\\s*)` + // colon & whitespace
`(?=\\S)` + // prop value starts with non-whitespace
`(${VALUE_CHAR}*?)` + // capture prop value (non-greedy)
`(\\s*!important)?` + // capture !important
`(\\s*;)` // semi & whitespace
)
// simplistic but workable media query value.
const MEDIA_CHAR = `[^{]`
// prettier-ignore
const MEDIA_PATT = (
`(@media\\b\\s*)` + // start of media query
`(?=\\S)` + // value starts with non-whitespace
`(${MEDIA_CHAR}+?)` + // capture queries (non-greedy)
`(\\s*\\{)` // brace & whitespace
)
const MATCH_REGEXP = new RegExp(`(?:${PROP_PATT}|${MEDIA_PATT})`, `g`)
// media query prop/value pairs such as (min-width: 1024px)
// prettier-ignore
const QUERY_REGEXP = new RegExp(
`(\\(\\s*)` + // open paren, whitespace
`(${PROP_CHAR}+)` + // capture prop name
`(\\s*:\\s*)` + // colon & whitespace
`([^\\)]*?)` + // capture prop value (non-greedy)
`(\\s*\\))`, // close paren, whitespace
`g`
)
const mediaTransform = (rawValue: string) => {
let matches
let lastIndex = 0
const values = []
while ((matches = QUERY_REGEXP.exec(rawValue))) {
const [, open, prop, colon, value, close] = matches
const getter = (mediaGetters as any)[prop]
if (getter) {
values.push(rawValue.slice(lastIndex, matches.index))
values.push(
(p: object) => `${open}${prop}${colon}${getter(value)(p)}${close}`,
)
lastIndex = matches.index + matches[0].length
}
}
values.push(rawValue.slice(lastIndex, rawValue.length))
return values
}
export const createTransform =
(generator: StyleGenerator) =>
(rawValue: any): any => {
if (typeof rawValue !== 'string') return rawValue
let matches
let lastIndex = 0
const values = []
while ((matches = MATCH_REGEXP.exec(rawValue))) {
const [, prop, colon, value, imp, semi, media, query, brace] = matches
if (media) {
values.push(rawValue.slice(lastIndex, matches.index))
values.push(media)
mediaTransform(query).forEach((v) => values.push(v))
values.push(brace)
lastIndex = matches.index + matches[0].length
} else {
const getter = generator.meta.cssGetters[prop]
if (getter) {
values.push(rawValue.slice(lastIndex, matches.index))
values.push(
(p: object) =>
`${prop}${colon}${getter(value)(p)}${imp || ''}${semi}`,
)
lastIndex = matches.index + matches[0].length
}
}
}
values.push(rawValue.slice(lastIndex, rawValue.length))
return values
}