@unocss/transformer-directives 0.34.1 → 0.35.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -2
- package/dist/index.cjs +96 -57
- package/dist/index.d.ts +6 -0
- package/dist/index.mjs +96 -57
- package/package.json +15 -15
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- @unocss-ignore -->
|
|
4
4
|
|
|
5
|
-
UnoCSS transformer for `@apply` directive
|
|
5
|
+
UnoCSS transformer for `@apply` and `theme()` directive
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -25,6 +25,8 @@ export default defineConfig({
|
|
|
25
25
|
|
|
26
26
|
## Usage
|
|
27
27
|
|
|
28
|
+
### `@apply`
|
|
29
|
+
|
|
28
30
|
```css
|
|
29
31
|
.custom-div {
|
|
30
32
|
@apply text-center my-0 font-medium;
|
|
@@ -44,7 +46,7 @@ Will be transformed to:
|
|
|
44
46
|
|
|
45
47
|
> Currently only `@apply` is supported.
|
|
46
48
|
|
|
47
|
-
|
|
49
|
+
#### CSS Variable Style
|
|
48
50
|
|
|
49
51
|
To be compatible with vanilla CSS, you can use CSS Variables to replace the `@apply` directive.
|
|
50
52
|
|
|
@@ -72,6 +74,24 @@ transformerDirective({
|
|
|
72
74
|
})
|
|
73
75
|
```
|
|
74
76
|
|
|
77
|
+
### `theme()`
|
|
78
|
+
|
|
79
|
+
Use the `theme()` function to access your theme config values using dot notation.
|
|
80
|
+
|
|
81
|
+
```css
|
|
82
|
+
.btn-blue {
|
|
83
|
+
background-color: theme('colors.blue.500');
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Will be compiled to:
|
|
88
|
+
|
|
89
|
+
```css
|
|
90
|
+
.btn-blue {
|
|
91
|
+
background-color: #3b82f6;
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
75
95
|
## License
|
|
76
96
|
|
|
77
97
|
MIT License © 2022-PRESENT [hannoeru](https://github.com/hannoeru)
|
package/dist/index.cjs
CHANGED
|
@@ -16,75 +16,114 @@ function transformerDirectives(options = {}) {
|
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
18
|
async function transformDirectives(code, uno, options, filename, originalCode, offset) {
|
|
19
|
-
const {
|
|
20
|
-
|
|
19
|
+
const {
|
|
20
|
+
varStyle = "--at-",
|
|
21
|
+
throwOnMissing = true
|
|
22
|
+
} = options;
|
|
23
|
+
const isApply = code.original.includes("@apply") || varStyle !== false && code.original.includes(varStyle);
|
|
24
|
+
const hasThemeFn = /theme\([^)]*?\)/.test(code.original);
|
|
25
|
+
if (!isApply && !hasThemeFn)
|
|
21
26
|
return;
|
|
22
27
|
const ast = cssTree.parse(originalCode || code.original, {
|
|
23
28
|
parseAtrulePrelude: false,
|
|
24
29
|
positions: true,
|
|
25
30
|
filename
|
|
26
31
|
});
|
|
27
|
-
const calcOffset = (pos) => offset ? pos + offset : pos;
|
|
28
32
|
if (ast.type !== "StyleSheet")
|
|
29
33
|
return;
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
|
|
34
|
+
const calcOffset = (pos) => offset ? pos + offset : pos;
|
|
35
|
+
const handleApply = async (node, childNode) => {
|
|
36
|
+
let body;
|
|
37
|
+
if (childNode.type === "Atrule" && childNode.name === "apply" && childNode.prelude && childNode.prelude.type === "Raw") {
|
|
38
|
+
body = childNode.prelude.value.trim();
|
|
39
|
+
} else if (varStyle !== false && childNode.type === "Declaration" && childNode.property === `${varStyle}apply` && childNode.value.type === "Raw") {
|
|
40
|
+
body = childNode.value.value.trim();
|
|
41
|
+
if (body.match(/^(['"]).*\1$/))
|
|
42
|
+
body = body.slice(1, -1);
|
|
43
|
+
}
|
|
44
|
+
if (!body)
|
|
33
45
|
return;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const selector = _selector?.replace(core.regexScopePlaceholder, " ") || _selector;
|
|
61
|
-
if (parent || selector && selector !== ".\\-") {
|
|
62
|
-
let newSelector = cssTree.generate(node.prelude);
|
|
63
|
-
if (selector && selector !== ".\\-") {
|
|
64
|
-
const selectorAST = cssTree.parse(selector, {
|
|
65
|
-
context: "selector"
|
|
66
|
-
});
|
|
67
|
-
const prelude = cssTree.clone(node.prelude);
|
|
68
|
-
prelude.children.forEach((child) => {
|
|
69
|
-
const parentSelectorAst = cssTree.clone(selectorAST);
|
|
70
|
-
parentSelectorAst.children.forEach((i2) => {
|
|
71
|
-
if (i2.type === "ClassSelector" && i2.name === "\\-")
|
|
72
|
-
Object.assign(i2, cssTree.clone(child));
|
|
73
|
-
});
|
|
74
|
-
Object.assign(child, parentSelectorAst);
|
|
46
|
+
const classNames = core.expandVariantGroup(body).split(/\s+/g);
|
|
47
|
+
const utils = (await Promise.all(classNames.map((i) => uno.parseToken(i, "-")))).filter(core.notNull).flat().sort((a, b) => a[0] - b[0]).sort((a, b) => (a[3] ? uno.parentOrders.get(a[3]) ?? 0 : 0) - (b[3] ? uno.parentOrders.get(b[3]) ?? 0 : 0)).reduce((acc, item) => {
|
|
48
|
+
const target = acc.find((i) => i[1] === item[1] && i[3] === item[3]);
|
|
49
|
+
if (target)
|
|
50
|
+
target[2] += item[2];
|
|
51
|
+
else
|
|
52
|
+
acc.push([...item]);
|
|
53
|
+
return acc;
|
|
54
|
+
}, []);
|
|
55
|
+
if (!utils.length)
|
|
56
|
+
return;
|
|
57
|
+
for (const i of utils) {
|
|
58
|
+
const [, _selector, body2, parent] = i;
|
|
59
|
+
const selector = _selector?.replace(core.regexScopePlaceholder, " ") || _selector;
|
|
60
|
+
if (parent || selector && selector !== ".\\-") {
|
|
61
|
+
let newSelector = cssTree.generate(node.prelude);
|
|
62
|
+
if (selector && selector !== ".\\-") {
|
|
63
|
+
const selectorAST = cssTree.parse(selector, {
|
|
64
|
+
context: "selector"
|
|
65
|
+
});
|
|
66
|
+
const prelude = cssTree.clone(node.prelude);
|
|
67
|
+
prelude.children.forEach((child) => {
|
|
68
|
+
const parentSelectorAst = cssTree.clone(selectorAST);
|
|
69
|
+
parentSelectorAst.children.forEach((i2) => {
|
|
70
|
+
if (i2.type === "ClassSelector" && i2.name === "\\-")
|
|
71
|
+
Object.assign(i2, cssTree.clone(child));
|
|
75
72
|
});
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (parent)
|
|
80
|
-
css = `${parent}{${css}}`;
|
|
81
|
-
code.appendLeft(calcOffset(node.loc.end.offset), css);
|
|
82
|
-
} else {
|
|
83
|
-
code.appendRight(calcOffset(childNode.loc.end.offset), body2);
|
|
73
|
+
Object.assign(child, parentSelectorAst);
|
|
74
|
+
});
|
|
75
|
+
newSelector = cssTree.generate(prelude);
|
|
84
76
|
}
|
|
77
|
+
let css = `${newSelector}{${body2}}`;
|
|
78
|
+
if (parent)
|
|
79
|
+
css = `${parent}{${css}}`;
|
|
80
|
+
code.appendLeft(calcOffset(node.loc.end.offset), css);
|
|
81
|
+
} else {
|
|
82
|
+
code.appendRight(calcOffset(childNode.loc.end.offset), body2);
|
|
85
83
|
}
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
}
|
|
85
|
+
code.remove(calcOffset(childNode.loc.start.offset), calcOffset(childNode.loc.end.offset));
|
|
86
|
+
};
|
|
87
|
+
const handleThemeFn = (node) => {
|
|
88
|
+
if (node.type === "Function" && node.name === "theme" && node.children) {
|
|
89
|
+
const children = node.children.toArray().filter((n) => n.type === "String");
|
|
90
|
+
if (children.length !== 1)
|
|
91
|
+
throw new Error(`theme() expect exact one argument, but got ${children.length}`);
|
|
92
|
+
const matchedThemes = children.map((childNode) => {
|
|
93
|
+
if (childNode.type !== "String")
|
|
94
|
+
return null;
|
|
95
|
+
const keys = childNode.value.split(".");
|
|
96
|
+
let value = uno.config.theme;
|
|
97
|
+
keys.every((key) => {
|
|
98
|
+
if (!Reflect.has(value, key)) {
|
|
99
|
+
value = null;
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
value = value[key];
|
|
103
|
+
return true;
|
|
104
|
+
});
|
|
105
|
+
if (typeof value === "string")
|
|
106
|
+
return value;
|
|
107
|
+
if (throwOnMissing)
|
|
108
|
+
throw new Error(`theme of "${childNode.value}" did not found`);
|
|
109
|
+
return null;
|
|
110
|
+
});
|
|
111
|
+
if (matchedThemes.length !== children.length)
|
|
112
|
+
return;
|
|
113
|
+
code.overwrite(calcOffset(node.loc.start.offset), calcOffset(node.loc.end.offset), matchedThemes.join(" "));
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const stack = [];
|
|
117
|
+
const processNode = async (node, _item, _list) => {
|
|
118
|
+
if (hasThemeFn) {
|
|
119
|
+
handleThemeFn(node);
|
|
120
|
+
} else if (isApply && node.type === "Rule") {
|
|
121
|
+
await Promise.all(node.block.children.map(async (childNode, _childItem) => {
|
|
122
|
+
if (childNode.type === "Raw")
|
|
123
|
+
return transformDirectives(code, uno, options, filename, childNode.value, calcOffset(childNode.loc.start.offset));
|
|
124
|
+
await handleApply(node, childNode);
|
|
125
|
+
}).toArray());
|
|
126
|
+
}
|
|
88
127
|
};
|
|
89
128
|
cssTree.walk(ast, (...args) => stack.push(processNode(...args)));
|
|
90
129
|
await Promise.all(stack);
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,12 @@ interface TransformerDirectivesOptions {
|
|
|
11
11
|
* @default '--at-'
|
|
12
12
|
*/
|
|
13
13
|
varStyle?: false | string;
|
|
14
|
+
/**
|
|
15
|
+
* Throw an error if utils or themes are not found.
|
|
16
|
+
*
|
|
17
|
+
* @default true
|
|
18
|
+
*/
|
|
19
|
+
throwOnMissing?: boolean;
|
|
14
20
|
}
|
|
15
21
|
declare function transformerDirectives(options?: TransformerDirectivesOptions): SourceCodeTransformer;
|
|
16
22
|
declare function transformDirectives(code: MagicString, uno: UnoGenerator, options: TransformerDirectivesOptions, filename?: string, originalCode?: string, offset?: number): Promise<void>;
|
package/dist/index.mjs
CHANGED
|
@@ -12,75 +12,114 @@ function transformerDirectives(options = {}) {
|
|
|
12
12
|
};
|
|
13
13
|
}
|
|
14
14
|
async function transformDirectives(code, uno, options, filename, originalCode, offset) {
|
|
15
|
-
const {
|
|
16
|
-
|
|
15
|
+
const {
|
|
16
|
+
varStyle = "--at-",
|
|
17
|
+
throwOnMissing = true
|
|
18
|
+
} = options;
|
|
19
|
+
const isApply = code.original.includes("@apply") || varStyle !== false && code.original.includes(varStyle);
|
|
20
|
+
const hasThemeFn = /theme\([^)]*?\)/.test(code.original);
|
|
21
|
+
if (!isApply && !hasThemeFn)
|
|
17
22
|
return;
|
|
18
23
|
const ast = parse(originalCode || code.original, {
|
|
19
24
|
parseAtrulePrelude: false,
|
|
20
25
|
positions: true,
|
|
21
26
|
filename
|
|
22
27
|
});
|
|
23
|
-
const calcOffset = (pos) => offset ? pos + offset : pos;
|
|
24
28
|
if (ast.type !== "StyleSheet")
|
|
25
29
|
return;
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
|
|
30
|
+
const calcOffset = (pos) => offset ? pos + offset : pos;
|
|
31
|
+
const handleApply = async (node, childNode) => {
|
|
32
|
+
let body;
|
|
33
|
+
if (childNode.type === "Atrule" && childNode.name === "apply" && childNode.prelude && childNode.prelude.type === "Raw") {
|
|
34
|
+
body = childNode.prelude.value.trim();
|
|
35
|
+
} else if (varStyle !== false && childNode.type === "Declaration" && childNode.property === `${varStyle}apply` && childNode.value.type === "Raw") {
|
|
36
|
+
body = childNode.value.value.trim();
|
|
37
|
+
if (body.match(/^(['"]).*\1$/))
|
|
38
|
+
body = body.slice(1, -1);
|
|
39
|
+
}
|
|
40
|
+
if (!body)
|
|
29
41
|
return;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const selector = _selector?.replace(regexScopePlaceholder, " ") || _selector;
|
|
57
|
-
if (parent || selector && selector !== ".\\-") {
|
|
58
|
-
let newSelector = generate(node.prelude);
|
|
59
|
-
if (selector && selector !== ".\\-") {
|
|
60
|
-
const selectorAST = parse(selector, {
|
|
61
|
-
context: "selector"
|
|
62
|
-
});
|
|
63
|
-
const prelude = clone(node.prelude);
|
|
64
|
-
prelude.children.forEach((child) => {
|
|
65
|
-
const parentSelectorAst = clone(selectorAST);
|
|
66
|
-
parentSelectorAst.children.forEach((i2) => {
|
|
67
|
-
if (i2.type === "ClassSelector" && i2.name === "\\-")
|
|
68
|
-
Object.assign(i2, clone(child));
|
|
69
|
-
});
|
|
70
|
-
Object.assign(child, parentSelectorAst);
|
|
42
|
+
const classNames = expandVariantGroup(body).split(/\s+/g);
|
|
43
|
+
const utils = (await Promise.all(classNames.map((i) => uno.parseToken(i, "-")))).filter(notNull).flat().sort((a, b) => a[0] - b[0]).sort((a, b) => (a[3] ? uno.parentOrders.get(a[3]) ?? 0 : 0) - (b[3] ? uno.parentOrders.get(b[3]) ?? 0 : 0)).reduce((acc, item) => {
|
|
44
|
+
const target = acc.find((i) => i[1] === item[1] && i[3] === item[3]);
|
|
45
|
+
if (target)
|
|
46
|
+
target[2] += item[2];
|
|
47
|
+
else
|
|
48
|
+
acc.push([...item]);
|
|
49
|
+
return acc;
|
|
50
|
+
}, []);
|
|
51
|
+
if (!utils.length)
|
|
52
|
+
return;
|
|
53
|
+
for (const i of utils) {
|
|
54
|
+
const [, _selector, body2, parent] = i;
|
|
55
|
+
const selector = _selector?.replace(regexScopePlaceholder, " ") || _selector;
|
|
56
|
+
if (parent || selector && selector !== ".\\-") {
|
|
57
|
+
let newSelector = generate(node.prelude);
|
|
58
|
+
if (selector && selector !== ".\\-") {
|
|
59
|
+
const selectorAST = parse(selector, {
|
|
60
|
+
context: "selector"
|
|
61
|
+
});
|
|
62
|
+
const prelude = clone(node.prelude);
|
|
63
|
+
prelude.children.forEach((child) => {
|
|
64
|
+
const parentSelectorAst = clone(selectorAST);
|
|
65
|
+
parentSelectorAst.children.forEach((i2) => {
|
|
66
|
+
if (i2.type === "ClassSelector" && i2.name === "\\-")
|
|
67
|
+
Object.assign(i2, clone(child));
|
|
71
68
|
});
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (parent)
|
|
76
|
-
css = `${parent}{${css}}`;
|
|
77
|
-
code.appendLeft(calcOffset(node.loc.end.offset), css);
|
|
78
|
-
} else {
|
|
79
|
-
code.appendRight(calcOffset(childNode.loc.end.offset), body2);
|
|
69
|
+
Object.assign(child, parentSelectorAst);
|
|
70
|
+
});
|
|
71
|
+
newSelector = generate(prelude);
|
|
80
72
|
}
|
|
73
|
+
let css = `${newSelector}{${body2}}`;
|
|
74
|
+
if (parent)
|
|
75
|
+
css = `${parent}{${css}}`;
|
|
76
|
+
code.appendLeft(calcOffset(node.loc.end.offset), css);
|
|
77
|
+
} else {
|
|
78
|
+
code.appendRight(calcOffset(childNode.loc.end.offset), body2);
|
|
81
79
|
}
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
}
|
|
81
|
+
code.remove(calcOffset(childNode.loc.start.offset), calcOffset(childNode.loc.end.offset));
|
|
82
|
+
};
|
|
83
|
+
const handleThemeFn = (node) => {
|
|
84
|
+
if (node.type === "Function" && node.name === "theme" && node.children) {
|
|
85
|
+
const children = node.children.toArray().filter((n) => n.type === "String");
|
|
86
|
+
if (children.length !== 1)
|
|
87
|
+
throw new Error(`theme() expect exact one argument, but got ${children.length}`);
|
|
88
|
+
const matchedThemes = children.map((childNode) => {
|
|
89
|
+
if (childNode.type !== "String")
|
|
90
|
+
return null;
|
|
91
|
+
const keys = childNode.value.split(".");
|
|
92
|
+
let value = uno.config.theme;
|
|
93
|
+
keys.every((key) => {
|
|
94
|
+
if (!Reflect.has(value, key)) {
|
|
95
|
+
value = null;
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
value = value[key];
|
|
99
|
+
return true;
|
|
100
|
+
});
|
|
101
|
+
if (typeof value === "string")
|
|
102
|
+
return value;
|
|
103
|
+
if (throwOnMissing)
|
|
104
|
+
throw new Error(`theme of "${childNode.value}" did not found`);
|
|
105
|
+
return null;
|
|
106
|
+
});
|
|
107
|
+
if (matchedThemes.length !== children.length)
|
|
108
|
+
return;
|
|
109
|
+
code.overwrite(calcOffset(node.loc.start.offset), calcOffset(node.loc.end.offset), matchedThemes.join(" "));
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
const stack = [];
|
|
113
|
+
const processNode = async (node, _item, _list) => {
|
|
114
|
+
if (hasThemeFn) {
|
|
115
|
+
handleThemeFn(node);
|
|
116
|
+
} else if (isApply && node.type === "Rule") {
|
|
117
|
+
await Promise.all(node.block.children.map(async (childNode, _childItem) => {
|
|
118
|
+
if (childNode.type === "Raw")
|
|
119
|
+
return transformDirectives(code, uno, options, filename, childNode.value, calcOffset(childNode.loc.start.offset));
|
|
120
|
+
await handleApply(node, childNode);
|
|
121
|
+
}).toArray());
|
|
122
|
+
}
|
|
84
123
|
};
|
|
85
124
|
walk(ast, (...args) => stack.push(processNode(...args)));
|
|
86
125
|
await Promise.all(stack);
|
package/package.json
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unocss/transformer-directives",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.2",
|
|
4
4
|
"description": "UnoCSS transformer for `@apply` directive",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"unocss",
|
|
7
|
-
"unocss-transformer"
|
|
8
|
-
],
|
|
9
|
-
"homepage": "https://github.com/antfu/unocss/tree/main/packages/transformer-directives#readme",
|
|
10
|
-
"bugs": {
|
|
11
|
-
"url": "https://github.com/antfu/unocss/issues"
|
|
12
|
-
},
|
|
13
|
-
"license": "MIT",
|
|
14
5
|
"author": "hannoeru <me@hanlee.co>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"homepage": "https://github.com/antfu/unocss/tree/main/packages/transformer-directives#readme",
|
|
15
8
|
"repository": {
|
|
16
9
|
"type": "git",
|
|
17
10
|
"url": "git+https://github.com/antfu/unocss.git",
|
|
18
11
|
"directory": "packages/transformer-directives"
|
|
19
12
|
},
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/antfu/unocss/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"unocss",
|
|
18
|
+
"unocss-transformer"
|
|
19
|
+
],
|
|
20
|
+
"sideEffects": false,
|
|
23
21
|
"exports": {
|
|
24
22
|
".": {
|
|
25
23
|
"require": "./dist/index.cjs",
|
|
26
24
|
"import": "./dist/index.mjs"
|
|
27
25
|
}
|
|
28
26
|
},
|
|
27
|
+
"main": "./dist/index.cjs",
|
|
28
|
+
"module": "./dist/index.mjs",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
29
30
|
"files": [
|
|
30
31
|
"dist"
|
|
31
32
|
],
|
|
32
|
-
"sideEffects": false,
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@unocss/core": "0.
|
|
34
|
+
"@unocss/core": "0.35.2",
|
|
35
35
|
"css-tree": "^2.1.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|