@unocss/transformer-directives 0.34.1 → 0.35.0

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 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
- ### CSS Variable Style
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 &copy; 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 { varStyle = "--at-" } = options;
20
- if (!code.original.includes("@apply") && (varStyle === false || !code.original.includes(varStyle)))
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 stack = [];
31
- const processNode = async (node, _item, _list) => {
32
- if (node.type !== "Rule")
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
- await Promise.all(node.block.children.map(async (childNode, _childItem) => {
35
- if (childNode.type === "Raw")
36
- return transformDirectives(code, uno, options, filename, childNode.value, calcOffset(childNode.loc.start.offset));
37
- let body;
38
- if (childNode.type === "Atrule" && childNode.name === "apply" && childNode.prelude && childNode.prelude.type === "Raw") {
39
- body = childNode.prelude.value.trim();
40
- } else if (varStyle !== false && childNode.type === "Declaration" && childNode.property === `${varStyle}apply` && childNode.value.type === "Raw") {
41
- body = childNode.value.value.trim();
42
- if (body.match(/^(['"]).*\1$/))
43
- body = body.slice(1, -1);
44
- }
45
- if (!body)
46
- return;
47
- const classNames = core.expandVariantGroup(body).split(/\s+/g);
48
- 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) => {
49
- const target = acc.find((i) => i[1] === item[1] && i[3] === item[3]);
50
- if (target)
51
- target[2] += item[2];
52
- else
53
- acc.push([...item]);
54
- return acc;
55
- }, []);
56
- if (!utils.length)
57
- return;
58
- for (const i of utils) {
59
- const [, _selector, body2, parent] = i;
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
- newSelector = cssTree.generate(prelude);
77
- }
78
- let css = `${newSelector}{${body2}}`;
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
- code.remove(calcOffset(childNode.loc.start.offset), calcOffset(childNode.loc.end.offset));
87
- }).toArray());
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 { varStyle = "--at-" } = options;
16
- if (!code.original.includes("@apply") && (varStyle === false || !code.original.includes(varStyle)))
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 stack = [];
27
- const processNode = async (node, _item, _list) => {
28
- if (node.type !== "Rule")
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
- await Promise.all(node.block.children.map(async (childNode, _childItem) => {
31
- if (childNode.type === "Raw")
32
- return transformDirectives(code, uno, options, filename, childNode.value, calcOffset(childNode.loc.start.offset));
33
- let body;
34
- if (childNode.type === "Atrule" && childNode.name === "apply" && childNode.prelude && childNode.prelude.type === "Raw") {
35
- body = childNode.prelude.value.trim();
36
- } else if (varStyle !== false && childNode.type === "Declaration" && childNode.property === `${varStyle}apply` && childNode.value.type === "Raw") {
37
- body = childNode.value.value.trim();
38
- if (body.match(/^(['"]).*\1$/))
39
- body = body.slice(1, -1);
40
- }
41
- if (!body)
42
- return;
43
- const classNames = expandVariantGroup(body).split(/\s+/g);
44
- 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) => {
45
- const target = acc.find((i) => i[1] === item[1] && i[3] === item[3]);
46
- if (target)
47
- target[2] += item[2];
48
- else
49
- acc.push([...item]);
50
- return acc;
51
- }, []);
52
- if (!utils.length)
53
- return;
54
- for (const i of utils) {
55
- const [, _selector, body2, parent] = i;
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
- newSelector = generate(prelude);
73
- }
74
- let css = `${newSelector}{${body2}}`;
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
- code.remove(calcOffset(childNode.loc.start.offset), calcOffset(childNode.loc.end.offset));
83
- }).toArray());
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.34.1",
3
+ "version": "0.35.0",
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
- "main": "./dist/index.cjs",
21
- "module": "./dist/index.mjs",
22
- "types": "./dist/index.d.ts",
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
- "require": "./dist/index.cjs",
26
- "import": "./dist/index.mjs"
23
+ "import": "./dist/index.mjs",
24
+ "require": "./dist/index.cjs"
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.1",
34
+ "@unocss/core": "0.35.0",
35
35
  "css-tree": "^2.1.0"
36
36
  },
37
37
  "devDependencies": {