next-yak 0.0.22 → 0.0.24

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
@@ -172,6 +172,33 @@ module.exports = {
172
172
 
173
173
  [Nesting Example (video)](https://github.com/jantimon/next-yak/assets/4113649/33eeeb13-b0cf-499f-a1d3-ba6f51cf4308)
174
174
 
175
+ ## Build Time Constants
176
+
177
+ The downside of dynamic properties is that they require inline style attributes.
178
+ While this is not a problem for most cases, we can't use them for media queries.
179
+
180
+ `next-yak` allows you to define build time constants which can be used in your styles:
181
+
182
+ ```jsx
183
+ import { styled } from 'next-yak';
184
+ import { breakpoints, spacings } from './constants.yak';
185
+
186
+ const Container = styled.div`
187
+ padding: ${spacings.md};
188
+ ${breakpoints.md} {
189
+ padding: ${spacings.lg};
190
+ }
191
+ `;
192
+ ```
193
+
194
+ | Feature | Code | Yak Constant files |
195
+ |---------------- |----------------------------------------------- |---------------------------------------------- |
196
+ | File Extension | `.js`, `.jsx`, `.tsx`, etc. | `.yak.js`, `.yak.jsx`, `.yak.tsx`, etc. |
197
+ | Runs at | Compile time (Bundler) | Runtime (Node or Browser) |
198
+ | Side effects | Allowed | Forbidden |
199
+ | Yak Features | All (`styled`, `css`, ...) | - |
200
+
201
+
175
202
  ## Motivation
176
203
 
177
204
  Most of the existing CSS-in-JS libraries are either slow or have a complex api. This project tries to find a middle ground between speed and api complexity.
@@ -40,7 +40,7 @@ const headline = css\`
40
40
  `
41
41
  )
42
42
  ).toMatchInlineSnapshot(`
43
- "._yak_0 {
43
+ ".headline_0 {
44
44
  font-size: 2rem;
45
45
  font-weight: bold;
46
46
  color: red;
@@ -77,15 +77,15 @@ const headline = css\`
77
77
  `
78
78
  )
79
79
  ).toMatchInlineSnapshot(`
80
- "._yak_0 {
80
+ ".headline_0 {
81
81
  font-size: 2rem;
82
82
  font-weight: bold;
83
83
  color: red;
84
- &:where(._yak_1) {
84
+ &:where(.headline_1) {
85
85
  color: orange;
86
86
  }
87
87
 
88
- &:where(._yak_2) {
88
+ &:where(.headline_2) {
89
89
  color: teal;
90
90
  }
91
91
  &:hover {
@@ -113,9 +113,9 @@ const headline = css\`
113
113
  `
114
114
  )
115
115
  ).toMatchInlineSnapshot(`
116
- "._yak_0 {
116
+ ".headline_0 {
117
117
  /* comment */
118
- &:where(._yak_1) {
118
+ &:where(.headline_1) {
119
119
  color: blue;
120
120
  }
121
121
  }"
@@ -138,12 +138,12 @@ const headline = css\`
138
138
  `
139
139
  )
140
140
  ).toMatchInlineSnapshot(`
141
- "._yak_0 {
142
- &:hover {
143
- color: var(--🦬18fi82j0);
144
- }
145
- }"
146
- `);
141
+ ".headline_0 {
142
+ &:hover {
143
+ color: var(--🦬18fi82j0);
144
+ }
145
+ }"
146
+ `);
147
147
  });
148
148
 
149
149
  it("should support attrs on intrinsic elements", async () => {
@@ -211,14 +211,14 @@ const headline = css\`
211
211
  `
212
212
  )
213
213
  ).toMatchInlineSnapshot(`
214
- "._yak_0 {
215
- transition: color var(--🦬18fi82j0) var(--🦬18fi82j1);
216
- display: block;
217
- &:where(._yak_1) {
218
- color: orange
219
- }
220
- }"
221
- `);
214
+ ".headline_0 {
215
+ transition: color var(--🦬18fi82j0) var(--🦬18fi82j1);
216
+ display: block;
217
+ &:where(.headline_1) {
218
+ color: orange
219
+ }
220
+ }"
221
+ `);
222
222
  });
223
223
 
224
224
  it("should replace breakpoint references with actual media queries", async () => {
@@ -241,18 +241,18 @@ const headline = css\`
241
241
  `
242
242
  )
243
243
  ).toMatchInlineSnapshot(`
244
- "._yak_0 {
245
- color: blue;
246
- @media (min-width: 640px) {
247
- color: red;
248
- }
249
- transition: color var(--🦬18fi82j0) var(--🦬18fi82j1);
250
- display: block;
251
- &:where(._yak_1) {
252
- color: orange
244
+ ".headline_0 {
245
+ color: blue;
246
+ @media (min-width: 640px) {
247
+ color: red;
253
248
  }
254
- }"
255
- `);
249
+ transition: color var(--🦬18fi82j0) var(--🦬18fi82j1);
250
+ display: block;
251
+ &:where(.headline_1) {
252
+ color: orange
253
+ }
254
+ }"
255
+ `);
256
256
  });
257
257
 
258
258
  it("should replace breakpoint references with actual media queries from single quote imports", async () => {
@@ -275,18 +275,18 @@ const headline = css\`
275
275
  `
276
276
  )
277
277
  ).toMatchInlineSnapshot(`
278
- "._yak_0 {
279
- color: blue;
280
- @media (min-width: 640px) {
281
- color: red;
282
- }
283
- transition: color var(--🦬18fi82j0) var(--🦬18fi82j1);
284
- display: block;
285
- &:where(._yak_1) {
286
- color: orange
278
+ ".headline_0 {
279
+ color: blue;
280
+ @media (min-width: 640px) {
281
+ color: red;
287
282
  }
288
- }"
289
- `);
283
+ transition: color var(--🦬18fi82j0) var(--🦬18fi82j1);
284
+ display: block;
285
+ &:where(.headline_1) {
286
+ color: orange
287
+ }
288
+ }"
289
+ `);
290
290
  });
291
291
 
292
292
  it("should prevent double escaped chars", async () => {
@@ -311,16 +311,16 @@ const headline = css\`
311
311
  `
312
312
  )
313
313
  ).toMatchInlineSnapshot(`
314
- "._yak_0 {
315
- :before {
316
- content: \\"\\\\2022\\";
317
- }
318
- :after {
319
- content: \\"\\\\2022\\";
320
- }
321
- content: \\"\\\\\\\\\\"
322
- }"
323
- `);
314
+ ".headline_0 {
315
+ :before {
316
+ content: \\"\\\\2022\\";
317
+ }
318
+ :after {
319
+ content: \\"\\\\2022\\";
320
+ }
321
+ content: \\"\\\\\\\\\\"
322
+ }"
323
+ `);
324
324
  });
325
325
 
326
326
  it("should convert keyframes", async () => {
@@ -481,16 +481,16 @@ const Component2 = styled.div\`
481
481
  ".Component {
482
482
  background-color: red;
483
483
  color: white;
484
- &:where(._yak_0) {
484
+ &:where(.active_0) {
485
485
  background-color: blue;
486
486
  }
487
487
  border: 1px solid black;
488
488
 
489
489
  &:focus {
490
490
  background-color: green;
491
- &:where(._yak_1) {
491
+ &:where(.active_1) {
492
492
  background-color: blue;
493
- &:where(._yak_2) {
493
+ &:where(.active_and_active_2) {
494
494
  background-color: brown;
495
495
  }
496
496
  }
@@ -535,17 +535,17 @@ const Component = styled.div\`
535
535
  ".Component {
536
536
  background-color: red;
537
537
  color: white;
538
- &:where(._yak_0) {
538
+ &:where(.active_0) {
539
539
  background-color: blue;
540
540
  }
541
- &:where(._yak_1) {
541
+ &:where(.not_active_1) {
542
542
  background-color: var(--🦬18fi82j0);
543
543
  }
544
544
  border: 1px solid black;
545
- &:where(._yak_2) {
545
+ &:where(.active_2) {
546
546
  color: orange;
547
547
  }
548
- &:where(._yak_3) {
548
+ &:where(.not_active_3) {
549
549
  transition: color var(--🦬18fi82j1) var(--🦬18fi82j2);
550
550
  }
551
551
  }"
@@ -0,0 +1,170 @@
1
+ import { parse, traverse } from "@babel/core";
2
+ import { NodePath } from "@babel/core";
3
+ import getCssName from "../lib/getCssName.cjs";
4
+ import type { TaggedTemplateExpression } from "@babel/types";
5
+ import { describe, it, expect } from "vitest";
6
+
7
+ function getCssLiteral(code: string) {
8
+ let result: NodePath<TaggedTemplateExpression> | null = null;
9
+ const ast = parse(code);
10
+ if (!ast) {
11
+ throw new Error("Could not parse code");
12
+ }
13
+ traverse(ast, {
14
+ TaggedTemplateExpression(path: NodePath<TaggedTemplateExpression>) {
15
+ if (path.node.tag.type === "Identifier" && path.node.tag.name === "css") {
16
+ result = path;
17
+ }
18
+ },
19
+ });
20
+ if (!result) {
21
+ throw new Error("Could not find css literal");
22
+ }
23
+ return result;
24
+ }
25
+
26
+ describe("getCssName", () => {
27
+ it("should guess the css name from the condition of a logical expression", () => {
28
+ const literal = getCssLiteral(`({$active}) => $active && css\`\``);
29
+ const cssName = getCssName(literal);
30
+ expect(cssName).toBe("active");
31
+ });
32
+
33
+ it("should guess the css name from the condition of a ternary expression", () => {
34
+ const literal = getCssLiteral(`({$active}) => $active ? css\`\` : null`);
35
+ const cssName = getCssName(literal);
36
+ expect(cssName).toBe("active");
37
+ });
38
+
39
+ it("should guess the css name from the condition of a logical expression with negation", () => {
40
+ const literal = getCssLiteral(`({$active}) => !$active && css\`\``);
41
+ const cssName = getCssName(literal);
42
+ expect(cssName).toBe("not_active");
43
+ });
44
+
45
+ it("should guess the css name from the condition of a ternary expression for the else case", () => {
46
+ const literal = getCssLiteral(`({$active}) => $active ? null : css\`\``);
47
+ const cssName = getCssName(literal);
48
+ expect(cssName).toBe("not_active");
49
+ });
50
+
51
+ it("should guess the css name from the condition of a logical expression with multiple conditions", () => {
52
+ const literal = getCssLiteral(
53
+ `({$active, $visible}) => $active && $visible && css\`\``
54
+ );
55
+ const cssName = getCssName(literal);
56
+ expect(cssName).toBe("active_and_visible");
57
+ });
58
+
59
+ it("should guess the css name from the condition of a ternary expression with multiple conditions", () => {
60
+ const literal = getCssLiteral(
61
+ `({$active, $visible}) => $active ? ($visible ? css\`\` : null) : null`
62
+ );
63
+ const cssName = getCssName(literal);
64
+ expect(cssName).toBe("active_and_visible");
65
+ });
66
+
67
+ it("should guess the css name from the condition of a logical expression with negation and multiple conditions", () => {
68
+ const literal = getCssLiteral(
69
+ `({$active, $visible}) => !$active && $visible && css\`\``
70
+ );
71
+ const cssName = getCssName(literal);
72
+ expect(cssName).toBe("not_active_and_visible");
73
+ });
74
+
75
+ it("should guess the css name from the condition of a ternary expression with negation and multiple conditions", () => {
76
+ const literal = getCssLiteral(
77
+ `({$active, $visible}) => !$active ? ($visible ? css\`\`: null) : null`
78
+ );
79
+ const cssName = getCssName(literal);
80
+ expect(cssName).toBe("not_active_and_visible");
81
+ });
82
+
83
+ it("should guess the css name from the condition of a logical expression with negation and multiple conditions for the else case", () => {
84
+ const literal = getCssLiteral(
85
+ `({$active, $visible}) => $active && !$visible && css\`\``
86
+ );
87
+ const cssName = getCssName(literal);
88
+ expect(cssName).toBe("active_and_not_visible");
89
+ });
90
+
91
+ it("should guess the css name from the condition of a ternary expression with negation and multiple conditions for the else case", () => {
92
+ const literal = getCssLiteral(
93
+ `({$active, $visible}) => $active ? ($visible ? null : css\`\`) : null`
94
+ );
95
+ const cssName = getCssName(literal);
96
+ expect(cssName).toBe("active_and_not_visible");
97
+ });
98
+
99
+ it("should guess the css name from the condition of a nested logical expression", () => {
100
+ const literal = getCssLiteral(
101
+ `({ $visible }) => $visible && (({ $active }) => $active && css\`\`)`
102
+ );
103
+ const cssName = getCssName(literal);
104
+ expect(cssName).toBe("visible_and_active");
105
+ });
106
+
107
+ it("should guess the css name from a complex expression using AND and OR operators", () => {
108
+ const literal = getCssLiteral(
109
+ `({$active, $visible, $enabled}) => ($active || $visible) && $enabled && css\`\``
110
+ );
111
+ const cssName = getCssName(literal);
112
+ expect(cssName).toBe("active_or_visible_and_enabled");
113
+ });
114
+
115
+ it("should guess the css name from nested logical expressions", () => {
116
+ const literal = getCssLiteral(
117
+ `({$active, $visible, $enabled}) => ($active && ($visible || $enabled)) && css\`\``
118
+ );
119
+ const cssName = getCssName(literal);
120
+ expect(cssName).toBe("active_and_visible_or_enabled");
121
+ });
122
+
123
+ it("should guess the css name from an expression with multiple OR conditions", () => {
124
+ const literal = getCssLiteral(
125
+ `({$active, $visible, $enabled}) => $active || $visible || $enabled && css\`\``
126
+ );
127
+ const cssName = getCssName(literal);
128
+ expect(cssName).toBe("active_or_visible_or_enabled");
129
+ });
130
+
131
+ it("should guess the css name from a complex combination of AND and OR operators", () => {
132
+ const literal = getCssLiteral(
133
+ `({$active, $visible, $enabled}) => ($active && $visible) || ($visible && $enabled) && css\`\``
134
+ );
135
+ const cssName = getCssName(literal);
136
+ expect(cssName).toBe("active_and_visible_or_visible_and_enabled");
137
+ });
138
+
139
+ it("should guess the css name from a condition using Boolean() function", () => {
140
+ const literal = getCssLiteral(`({$active}) => Boolean($active) && css\`\``);
141
+ const cssName = getCssName(literal);
142
+ expect(cssName).toBe("active");
143
+ });
144
+
145
+ it("should guess the css name from a condition using double negation", () => {
146
+ const literal = getCssLiteral(`({$active}) => !!$active && css\`\``);
147
+ const cssName = getCssName(literal);
148
+ expect(cssName).toBe("active");
149
+ });
150
+
151
+ it("should guess the css name from the variable name", () => {
152
+ const literal = getCssLiteral(`const Mixin = css\`\``);
153
+ const cssName = getCssName(literal);
154
+ expect(cssName).toBe("Mixin");
155
+ });
156
+
157
+ it("should guess the css name from a single member expression and camelCase it", () => {
158
+ const literal = getCssLiteral(`({theme}) => theme.highContrast && css\`\``);
159
+ const cssName = getCssName(literal);
160
+ expect(cssName).toBe("themeHighContrast");
161
+ });
162
+
163
+ it("should guess the css name from a nested member expression and camelCase it", () => {
164
+ const literal = getCssLiteral(
165
+ `({$user}) => $user.auth.loggedIn && css\`\``
166
+ );
167
+ const cssName = getCssName(literal);
168
+ expect(cssName).toBe("userAuthLoggedIn");
169
+ });
170
+ });
@@ -60,7 +60,7 @@ export const Main = () => <h1 className={headline({}).className}>Hello World</h1
60
60
  import { css } from \\"next-yak\\";
61
61
  import __styleYak from \\"./page.yak.module.css!=!./page?./page.yak.module.css\\";
62
62
  type x = number;
63
- const headline = css(__styleYak._yak_0);
63
+ const headline = css(__styleYak.headline_0);
64
64
  export const Main = () => <h1 className={headline({}).className}>Hello World</h1>;"
65
65
  `);
66
66
  });
@@ -91,7 +91,7 @@ const headline = css\`
91
91
  import { css } from \\"next-yak\\";
92
92
  import __styleYak from \\"./page.yak.module.css!=!./page?./page.yak.module.css\\";
93
93
  const x = Math.random();
94
- const headline = css(__styleYak._yak_0, x > 0.5 && css(__styleYak._yak_1));"
94
+ const headline = css(__styleYak.headline_0, x > 0.5 && css(__styleYak.headline_1));"
95
95
  `);
96
96
  });
97
97
 
@@ -125,7 +125,7 @@ const FancyButton = styled(Button)\`
125
125
  import { styled, css } from \\"next-yak\\";
126
126
  import __styleYak from \\"./page.yak.module.css!=!./page?./page.yak.module.css\\";
127
127
  const x = Math.random();
128
- const Button = styled.button(__styleYak.Button, x > 0.5 && css(__styleYak._yak_0));
128
+ const Button = styled.button(__styleYak.Button, x > 0.5 && css(__styleYak.Button_0));
129
129
  const FancyButton = styled(Button)(__styleYak.FancyButton);"
130
130
  `);
131
131
  });
@@ -162,7 +162,7 @@ const FancyButton = styled(Button)\`
162
162
  const x = Math.random();
163
163
  const Button = styled.button(__styleYak.Button, ({
164
164
  theme
165
- }) => theme.mode === \\"dark\\" && css(__styleYak._yak_0));
165
+ }) => theme.mode === \\"dark\\" && css(__styleYak.Button_0));
166
166
  const FancyButton = styled(Button)(__styleYak.FancyButton);"
167
167
  `);
168
168
  });
@@ -240,7 +240,7 @@ const headline = css\`
240
240
  import { css } from \\"next-yak\\";
241
241
  import __styleYak from \\"./page.yak.module.css!=!./page?./page.yak.module.css\\";
242
242
  import { easing } from \\"styleguide\\";
243
- const headline = css(__styleYak._yak_0, css(__styleYak._yak_1), css(__styleYak._yak_2), {
243
+ const headline = css(__styleYak.headline_0, css(__styleYak.headline_1), css(__styleYak.headline_2), {
244
244
  \\"style\\": {
245
245
  \\"--\\\\uD83E\\\\uDDAC18fi82j0\\": ({
246
246
  i
@@ -477,12 +477,12 @@ const headline = css\`
477
477
  `
478
478
  )
479
479
  ).toMatchInlineSnapshot(`
480
- "import styles from \\"./page.module.css\\";
481
- import { css } from \\"next-yak\\";
482
- import __styleYak from \\"./page.yak.module.css!=!./page?./page.yak.module.css\\";
483
- const x = Math.random();
484
- const headline = css(__styleYak._yak_0, x > 0.5 && css(__styleYak._yak_1));"
485
- `);
480
+ "import styles from \\"./page.module.css\\";
481
+ import { css } from \\"next-yak\\";
482
+ import __styleYak from \\"./page.yak.module.css!=!./page?./page.yak.module.css\\";
483
+ const x = Math.random();
484
+ const headline = css(__styleYak.headline_0, x > 0.5 && css(__styleYak.headline_1));"
485
+ `);
486
486
  });
487
487
 
488
488
  it("should show error when a dynamic selector is used after a comma", async () => {
@@ -552,13 +552,13 @@ const Button = styled.button\`
552
552
  `
553
553
  )
554
554
  ).toMatchInlineSnapshot(`
555
- "import styles from \\"./page.module.css\\";
556
- import { styled, css } from \\"next-yak\\";
557
- import __styleYak from \\"./page.yak.module.css!=!./page?./page.yak.module.css\\";
558
- const Icon = styled.svg(__styleYak.Icon);
559
- const Button = styled.button(__styleYak.Button, ({
560
- $primary
561
- }) => $primary && css(__styleYak._yak_0));"
562
- `);
555
+ "import styles from \\"./page.module.css\\";
556
+ import { styled, css } from \\"next-yak\\";
557
+ import __styleYak from \\"./page.yak.module.css!=!./page?./page.yak.module.css\\";
558
+ const Icon = styled.svg(__styleYak.Icon);
559
+ const Button = styled.button(__styleYak.Button, ({
560
+ $primary
561
+ }) => $primary && css(__styleYak.primary_0));"
562
+ `);
563
563
  });
564
564
  });
@@ -5,6 +5,7 @@ const murmurhash2_32_gc = require("./lib/hash.cjs");
5
5
  const { relative, resolve, basename } = require("path");
6
6
  const localIdent = require("./lib/localIdent.cjs");
7
7
  const getStyledComponentName = require("./lib/getStyledComponentName.cjs");
8
+ const getCssName = require("./lib/getCssName.cjs");
8
9
 
9
10
  /** @typedef {{replaces: Record<string, unknown>, rootContext?: string}} YakBabelPluginOptions */
10
11
  /** @typedef {{ css: string | undefined, styled: string | undefined, keyframes: string | undefined }} YakLocalIdentifierNames */
@@ -246,11 +247,13 @@ module.exports = function (babel, options) {
246
247
  const variableName =
247
248
  styledApi || expressionType === "keyframesLiteral"
248
249
  ? getStyledComponentName(path)
250
+ : expressionType === "cssLiteral" ?
251
+ getCssName(path)
249
252
  : null;
250
253
 
251
254
  const identifier = localIdent(
252
255
  variableName || "_yak",
253
- variableName ? null : this.classNameCount++,
256
+ variableName && expressionType !== "cssLiteral" ? null : this.classNameCount++,
254
257
  expressionType === "keyframesLiteral" ? "animation" : "className"
255
258
  );
256
259
 
@@ -5,6 +5,7 @@ const quasiClassifier = require("./lib/quasiClassifier.cjs");
5
5
  const localIdent = require("./lib/localIdent.cjs");
6
6
  const replaceQuasiExpressionTokens = require("./lib/replaceQuasiExpressionTokens.cjs");
7
7
  const getStyledComponentName = require("./lib/getStyledComponentName.cjs");
8
+ const getCssName = require("./lib/getCssName.cjs");
8
9
  const murmurhash2_32_gc = require("./lib/hash.cjs");
9
10
  const { relative } = require("path");
10
11
 
@@ -192,11 +193,12 @@ module.exports = async function cssLoader(source) {
192
193
  const variableName =
193
194
  isStyledLiteral || isStyledCall || isAttrsCall || isKeyFrameLiteral
194
195
  ? getStyledComponentName(path)
196
+ : isCssLiteral ? getCssName(path)
195
197
  : null
196
198
 
197
199
  const literalSelector = localIdent(
198
200
  variableName || "_yak",
199
- variableName ? null : index++,
201
+ variableName && !isCssLiteral ? null : index++,
200
202
  isKeyFrameLiteral ? "keyframes" : "selector"
201
203
  );
202
204
 
@@ -0,0 +1,151 @@
1
+ // @ts-check
2
+
3
+ /** @typedef {import("@babel/types")} babel */
4
+
5
+ /**
6
+ * Extracts the conditions from a path
7
+ * @param {babel.NodePath<babel.types.TaggedTemplateExpression>} path
8
+ */
9
+ function extractConditions(path) {
10
+ /** @type {string[]} */
11
+ const conditions = [];
12
+ const visitedNodes = new Set();
13
+ /**
14
+ * @param {babel.types.Node} node
15
+ * @param {babel.types.Node} previousNode
16
+ * @param {boolean} isNegated
17
+ */
18
+ const getConditions = (node, previousNode, isNegated = false) => {
19
+ if (visitedNodes.has(node)) return;
20
+ visitedNodes.add(node);
21
+ // Support for && and || operators e.g. disabled && "disabled"
22
+ if (node.type === "LogicalExpression") {
23
+ if (node.operator === "&&") {
24
+ getConditions(node.right, previousNode, isNegated);
25
+ conditions.push("and");
26
+ getConditions(node.left, previousNode, isNegated);
27
+ } else if (node.operator === "||") {
28
+ getConditions(node.right, previousNode, isNegated);
29
+ conditions.push("or");
30
+ getConditions(node.left, previousNode, isNegated);
31
+ }
32
+ }
33
+ // Support for ternary operator e.g. disabled ? "disabled" : "enabled"
34
+ else if (node.type === "ConditionalExpression") {
35
+ conditions.push("and");
36
+ getConditions(node.test, previousNode, node.alternate === previousNode);
37
+ }
38
+ // Support for ! operator e.g. !disabled
39
+ else if (node.type === "UnaryExpression" && node.operator === "!") {
40
+ getConditions(node.argument, previousNode, !isNegated);
41
+ }
42
+ // Support for Boolean() function e.g. Boolean(disabled)
43
+ else if (
44
+ node.type === "CallExpression" &&
45
+ node.callee.type === "Identifier" &&
46
+ node.callee.name === "Boolean"
47
+ ) {
48
+ getConditions(node.arguments[0], previousNode, isNegated);
49
+ }
50
+ // Get the name of the variable e.g. disabled
51
+ else if (node.type === "Identifier") {
52
+ conditions.push((isNegated ? "not_" : "") + node.name);
53
+ }
54
+ // Get the name of a member expression e.g. props.disabled
55
+ else if (node.type === "MemberExpression") {
56
+ conditions.push(
57
+ (isNegated ? "not_" : "") + getMemberExpressionName(node)
58
+ );
59
+ }
60
+ };
61
+ /** @type {babel.NodePath | null} */
62
+ let currentPath = path;
63
+ /** @type {babel.NodePath} */
64
+ let previousPath = path;
65
+ while (currentPath) {
66
+ getConditions(currentPath.node, previousPath.node);
67
+ previousPath = currentPath;
68
+ currentPath = currentPath.parentPath;
69
+ }
70
+ if (conditions[0] === "or" || conditions[0] === "and") {
71
+ conditions.shift();
72
+ }
73
+ return conditions.reverse();
74
+ }
75
+
76
+ /**
77
+ * Try to get the name of a css component from a literal expression
78
+ *
79
+ * e.g. const mixin = css`...` -> "mixin"
80
+ *
81
+ * @param {babel.NodePath<babel.types.TaggedTemplateExpression>} taggedTemplateExpressionPath
82
+ * @returns {string | null}
83
+ */
84
+ const getStyledComponentName = (taggedTemplateExpressionPath) => {
85
+ const variableDeclaratorPath = taggedTemplateExpressionPath.findParent(
86
+ (path) => path.isVariableDeclarator()
87
+ );
88
+ if (
89
+ !variableDeclaratorPath ||
90
+ !("id" in variableDeclaratorPath.node) ||
91
+ variableDeclaratorPath.node.id?.type !== "Identifier"
92
+ ) {
93
+ return null;
94
+ }
95
+ return variableDeclaratorPath.node.id.name;
96
+ };
97
+
98
+ /**
99
+ * Try to get the name of a member expression
100
+ *
101
+ * e.g. props.disabled -> "propsDisabled"
102
+ * e.g. props.user.disabled -> "propsUserDisabled
103
+ *
104
+ * @param {babel.types.MemberExpression} node
105
+ * @returns {string}
106
+ */
107
+ function getMemberExpressionName(node) {
108
+ console.log(node.object, node.property.type);
109
+ if (
110
+ !node.object ||
111
+ !node.property ||
112
+ (node.object.type !== "Identifier" &&
113
+ node.object.type !== "MemberExpression")
114
+ ) {
115
+ return "";
116
+ }
117
+ const objectName =
118
+ node.object.type === "Identifier"
119
+ ? node.object.name
120
+ : getMemberExpressionName(node.object);
121
+ const property = node.property;
122
+ let propertyName = "";
123
+ if (property.type === "Identifier") {
124
+ propertyName = property.name;
125
+ } else if (property.type === "StringLiteral") {
126
+ propertyName = property.value;
127
+ }
128
+ if (!propertyName) {
129
+ return "";
130
+ }
131
+ return objectName + propertyName[0].toUpperCase() + propertyName.slice(1);
132
+ }
133
+
134
+ /**
135
+ * Try to get the name of a css literal
136
+ *
137
+ * e.g. ({$disabled}) => $disabled && css`...` -> "is_$disabled"
138
+ *
139
+ * @param {babel.NodePath<babel.types.TaggedTemplateExpression>} literal
140
+ * @returns {string}
141
+ */
142
+ function getCssName(literal) {
143
+ const conditions = extractConditions(literal);
144
+ if (conditions.length === 0) {
145
+ const mixinName = getStyledComponentName(literal);
146
+ return mixinName ? mixinName : "yak";
147
+ }
148
+ return conditions.join("_").replace(/\$/g, "");
149
+ }
150
+
151
+ module.exports = getCssName;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-yak",
3
- "version": "0.0.22",
3
+ "version": "0.0.24",
4
4
  "type": "module",
5
5
  "types": "./dist/",
6
6
  "exports": {