next-yak 0.0.26 → 0.0.27
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 +3 -2
- package/loaders/__tests__/tsloader.test.ts +198 -4
- package/loaders/babel-yak-plugin.cjs +59 -0
- package/package.json +18 -19
package/README.md
CHANGED
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
- **Standard CSS Syntax**: Write styles in familiar, easy-to-use CSS
|
|
17
17
|
- **Integrates with Atomic CSS**: Easily combines with atomic CSS frameworks like Tailwind CSS for more design options
|
|
18
18
|
|
|
19
|
-
[Preview (Video)](https://github.com/jantimon/next-yak/assets/4113649/
|
|
19
|
+
[Preview (Video)](https://github.com/jantimon/next-yak/assets/4113649/f5a220fc-2a0f-46be-a8e7-c855f7faa337
|
|
20
|
+
)
|
|
20
21
|
|
|
21
22
|
## Installation
|
|
22
23
|
|
|
@@ -263,7 +264,7 @@ const Container = styled.div`
|
|
|
263
264
|
| Yak Features | All (`styled`, `css`, ...) | 🚫 |
|
|
264
265
|
|
|
265
266
|
|
|
266
|
-
[Build time constants (video)](https://github.com/jantimon/next-yak/assets/4113649/
|
|
267
|
+
[Build time constants (video)](https://github.com/jantimon/next-yak/assets/4113649/2c83246c-a03b-4c57-8814-32a7248983ac)
|
|
267
268
|
|
|
268
269
|
## Todos:
|
|
269
270
|
|
|
@@ -228,7 +228,7 @@ import { css } from "next-yak";
|
|
|
228
228
|
import { easing } from "styleguide";
|
|
229
229
|
|
|
230
230
|
const headline = css\`
|
|
231
|
-
transition: color \${({i}) => i * 100 + "ms"} \${easing};
|
|
231
|
+
transition: color \${({i}) => i * 100 + "ms"} \${({easing}) => easing};
|
|
232
232
|
display: block;
|
|
233
233
|
\${css\`color: orange\`}
|
|
234
234
|
\${css\`color: blue\`}
|
|
@@ -245,7 +245,9 @@ const headline = css\`
|
|
|
245
245
|
\\"--\\\\uD83E\\\\uDDAC18fi82j0\\": ({
|
|
246
246
|
i
|
|
247
247
|
}) => i * 100 + \\"ms\\",
|
|
248
|
-
\\"--\\\\uD83E\\\\uDDAC18fi82j1\\":
|
|
248
|
+
\\"--\\\\uD83E\\\\uDDAC18fi82j1\\": ({
|
|
249
|
+
easing
|
|
250
|
+
}) => easing
|
|
249
251
|
}
|
|
250
252
|
});"
|
|
251
253
|
`);
|
|
@@ -269,7 +271,7 @@ const fadeIn = keyframes\`
|
|
|
269
271
|
\`
|
|
270
272
|
|
|
271
273
|
const FadeInButton = styled.button\`
|
|
272
|
-
animation: 1s \${fadeIn} ease-out;
|
|
274
|
+
animation: 1s \${() => fadeIn} ease-out;
|
|
273
275
|
\`
|
|
274
276
|
`
|
|
275
277
|
)
|
|
@@ -280,7 +282,7 @@ const FadeInButton = styled.button\`
|
|
|
280
282
|
const fadeIn = keyframes(__styleYak.fadeIn);
|
|
281
283
|
const FadeInButton = styled.button(__styleYak.FadeInButton, {
|
|
282
284
|
\\"style\\": {
|
|
283
|
-
\\"--\\\\uD83E\\\\uDDAC18fi82j0\\": fadeIn
|
|
285
|
+
\\"--\\\\uD83E\\\\uDDAC18fi82j0\\": () => fadeIn
|
|
284
286
|
}
|
|
285
287
|
});"
|
|
286
288
|
`);
|
|
@@ -459,6 +461,198 @@ const Icon = styled.div\`
|
|
|
459
461
|
`);
|
|
460
462
|
});
|
|
461
463
|
|
|
464
|
+
it("should show error when using a runtime value from top level", async () => {
|
|
465
|
+
await expect(() =>
|
|
466
|
+
tsloader.call(
|
|
467
|
+
loaderContext,
|
|
468
|
+
`
|
|
469
|
+
import { styled, css } from "next-yak";
|
|
470
|
+
|
|
471
|
+
const red = new Token("#E50914");
|
|
472
|
+
const headline = css\`
|
|
473
|
+
color: \${red};
|
|
474
|
+
\`
|
|
475
|
+
`
|
|
476
|
+
)
|
|
477
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(`
|
|
478
|
+
"/some/special/path/page.tsx: line 6: Possible constant used as runtime value for a css variable
|
|
479
|
+
Please move the constant to a .yak import or use an arrow function
|
|
480
|
+
e.g.:
|
|
481
|
+
| import { primaryColor } from './foo.yak'
|
|
482
|
+
| const MyStyledDiv = styled.div\`color: \${primaryColor};\`
|
|
483
|
+
found: \${red}"
|
|
484
|
+
`);
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
it("should show error when using a runtime value from top level in a nested literal", async () => {
|
|
488
|
+
await expect(() =>
|
|
489
|
+
tsloader.call(
|
|
490
|
+
loaderContext,
|
|
491
|
+
`
|
|
492
|
+
import { styled, css } from "next-yak";
|
|
493
|
+
|
|
494
|
+
const $red = "#E50914";
|
|
495
|
+
const Button = styled.button\`
|
|
496
|
+
\${({ $primary, $digits }) => {
|
|
497
|
+
return $primary && css\`
|
|
498
|
+
background-color: #4CAF50;
|
|
499
|
+
color: \${$red};
|
|
500
|
+
\`}}
|
|
501
|
+
\`
|
|
502
|
+
`
|
|
503
|
+
)
|
|
504
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(`
|
|
505
|
+
"/some/special/path/page.tsx: line 9: Possible constant used as runtime value for a css variable
|
|
506
|
+
Please move the constant to a .yak import or use an arrow function
|
|
507
|
+
e.g.:
|
|
508
|
+
| import { primaryColor } from './foo.yak'
|
|
509
|
+
| const MyStyledDiv = styled.div\`color: \${primaryColor};\`
|
|
510
|
+
found: \${$red}"
|
|
511
|
+
`);
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
it("should show error when calling a runtime value from top level in a nested literal", async () => {
|
|
515
|
+
await expect(() =>
|
|
516
|
+
tsloader.call(
|
|
517
|
+
loaderContext,
|
|
518
|
+
`
|
|
519
|
+
import { styled, css } from "next-yak";
|
|
520
|
+
import { red } from "./colors";
|
|
521
|
+
|
|
522
|
+
const Button = styled.button\`
|
|
523
|
+
\${({ $primary, $digits }) => {
|
|
524
|
+
return $primary && css\`
|
|
525
|
+
background-color: #4CAF50;
|
|
526
|
+
color: \${red()};
|
|
527
|
+
\`}}
|
|
528
|
+
\`
|
|
529
|
+
`
|
|
530
|
+
)
|
|
531
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(`
|
|
532
|
+
"/some/special/path/page.tsx: line 9: Possible constant used as runtime value for a css variable
|
|
533
|
+
Please move the constant to a .yak import or use an arrow function
|
|
534
|
+
e.g.:
|
|
535
|
+
| import { primaryColor } from './foo.yak'
|
|
536
|
+
| const MyStyledDiv = styled.div\`color: \${primaryColor};\`
|
|
537
|
+
found: \${red()}"
|
|
538
|
+
`);
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
it("should show error when calling a nested runtime value from top level in a nested literal", async () => {
|
|
542
|
+
await expect(() =>
|
|
543
|
+
tsloader.call(
|
|
544
|
+
loaderContext,
|
|
545
|
+
`
|
|
546
|
+
import { styled, css } from "next-yak";
|
|
547
|
+
import { colors } from "./colors";
|
|
548
|
+
|
|
549
|
+
const Button = styled.button\`
|
|
550
|
+
\${({ $primary, $digits }) => {
|
|
551
|
+
return $primary && css\`
|
|
552
|
+
background-color: #4CAF50;
|
|
553
|
+
color: \${colors.red()};
|
|
554
|
+
\`}}
|
|
555
|
+
\`
|
|
556
|
+
`
|
|
557
|
+
)
|
|
558
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(`
|
|
559
|
+
"/some/special/path/page.tsx: line 9: Possible constant used as runtime value for a css variable
|
|
560
|
+
Please move the constant to a .yak import or use an arrow function
|
|
561
|
+
e.g.:
|
|
562
|
+
| import { primaryColor } from './foo.yak'
|
|
563
|
+
| const MyStyledDiv = styled.div\`color: \${primaryColor};\`
|
|
564
|
+
found: \${colors.red()}"
|
|
565
|
+
`);
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
it("should show error when using a runtime value from another module in a nested literal", async () => {
|
|
569
|
+
await expect(() =>
|
|
570
|
+
tsloader.call(
|
|
571
|
+
loaderContext,
|
|
572
|
+
`
|
|
573
|
+
import { styled, css } from "next-yak";
|
|
574
|
+
import $red from "./colors";
|
|
575
|
+
|
|
576
|
+
const Button = styled.button\`
|
|
577
|
+
\${({ $primary, $digits }) => {
|
|
578
|
+
return $primary && css\`
|
|
579
|
+
background-color: #4CAF50;
|
|
580
|
+
color: \${$red};
|
|
581
|
+
\`}}
|
|
582
|
+
\`
|
|
583
|
+
`
|
|
584
|
+
)
|
|
585
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(`
|
|
586
|
+
"/some/special/path/page.tsx: line 9: Possible constant used as runtime value for a css variable
|
|
587
|
+
Please move the constant to a .yak import or use an arrow function
|
|
588
|
+
e.g.:
|
|
589
|
+
| import { primaryColor } from './foo.yak'
|
|
590
|
+
| const MyStyledDiv = styled.div\`color: \${primaryColor};\`
|
|
591
|
+
found: \${$red}"
|
|
592
|
+
`);
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
it("should show error when using a runtime object value from top level in a nested literal", async () => {
|
|
596
|
+
await expect(() =>
|
|
597
|
+
tsloader.call(
|
|
598
|
+
loaderContext,
|
|
599
|
+
`
|
|
600
|
+
import { styled, css } from "next-yak";
|
|
601
|
+
|
|
602
|
+
const colors = { red: "#E50914" };
|
|
603
|
+
const Button = styled.button\`
|
|
604
|
+
\${({ $primary, $digits }) => {
|
|
605
|
+
return $primary && css\`
|
|
606
|
+
background-color: #4CAF50;
|
|
607
|
+
color: \${colors.red};
|
|
608
|
+
\`}}
|
|
609
|
+
\`
|
|
610
|
+
`
|
|
611
|
+
)
|
|
612
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(`
|
|
613
|
+
"/some/special/path/page.tsx: line 9: Possible constant used as runtime value for a css variable
|
|
614
|
+
Please move the constant to a .yak import or use an arrow function
|
|
615
|
+
e.g.:
|
|
616
|
+
| import { primaryColor } from './foo.yak'
|
|
617
|
+
| const MyStyledDiv = styled.div\`color: \${primaryColor};\`
|
|
618
|
+
found: \${colors.red}"
|
|
619
|
+
`);
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
it("should show no error when using a scoped value", async () => {
|
|
623
|
+
await expect(
|
|
624
|
+
await tsloader.call(
|
|
625
|
+
loaderContext,
|
|
626
|
+
`
|
|
627
|
+
import { styled, css } from "next-yak";
|
|
628
|
+
|
|
629
|
+
const Button = styled.button\`
|
|
630
|
+
\${({ $primary, $digits }) => {
|
|
631
|
+
const indent = $digits * 10 + "px";
|
|
632
|
+
return $primary && css\`
|
|
633
|
+
background-color: #4CAF50;
|
|
634
|
+
text-indent: \${indent};
|
|
635
|
+
\`}}
|
|
636
|
+
\`
|
|
637
|
+
`
|
|
638
|
+
)
|
|
639
|
+
).toMatchInlineSnapshot(`
|
|
640
|
+
"import { styled, css } from \\"next-yak\\";
|
|
641
|
+
import __styleYak from \\"./page.yak.module.css!=!./page?./page.yak.module.css\\";
|
|
642
|
+
const Button = styled.button(__styleYak.Button, ({
|
|
643
|
+
$primary,
|
|
644
|
+
$digits
|
|
645
|
+
}) => {
|
|
646
|
+
const indent = $digits * 10 + \\"px\\";
|
|
647
|
+
return $primary && css(__styleYak.primary_0, {
|
|
648
|
+
\\"style\\": {
|
|
649
|
+
\\"--\\\\uD83E\\\\uDDAC18fi82j0\\": indent
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
});"
|
|
653
|
+
`);
|
|
654
|
+
});
|
|
655
|
+
|
|
462
656
|
it("should ignores empty chunks if they include only a comment", async () => {
|
|
463
657
|
expect(
|
|
464
658
|
await tsloader.call(
|
|
@@ -21,6 +21,7 @@ const getCssName = require("./lib/getCssName.cjs");
|
|
|
21
21
|
* localVarNames: YakLocalIdentifierNames,
|
|
22
22
|
* isImportedInCurrentFile: boolean,
|
|
23
23
|
* classNameCount: number,
|
|
24
|
+
* topLevelConstBindings: Set<string>,
|
|
24
25
|
* varIndex: number,
|
|
25
26
|
* variableNameToStyledCall: Map<string, {
|
|
26
27
|
* wasAdded: boolean,
|
|
@@ -122,8 +123,33 @@ module.exports = function (babel, options) {
|
|
|
122
123
|
this.classNameCount = 0;
|
|
123
124
|
this.varIndex = 0;
|
|
124
125
|
this.variableNameToStyledCall = new Map();
|
|
126
|
+
this.topLevelConstBindings = new Set();
|
|
125
127
|
},
|
|
126
128
|
visitor: {
|
|
129
|
+
Program(path, state) {
|
|
130
|
+
const bindings = Object.entries(path.scope.bindings);
|
|
131
|
+
const constBindings = bindings.filter(([_name, binding]) => {
|
|
132
|
+
if (binding.kind === "module") {
|
|
133
|
+
// it is unclear if this is a const binding
|
|
134
|
+
// however to be safe, all module bindings are considered consts
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
return (
|
|
138
|
+
// let and var might change but this would be very strange react code
|
|
139
|
+
// as states must use useState or useExternalStore
|
|
140
|
+
(binding.kind === "let" ||
|
|
141
|
+
binding.kind === "var" ||
|
|
142
|
+
binding.kind === "const") &&
|
|
143
|
+
// don't consider functions or arrow functions as constants
|
|
144
|
+
(!("init" in binding.path.node) ||
|
|
145
|
+
(!t.isFunctionDeclaration(binding.path.node.init) &&
|
|
146
|
+
!t.isArrowFunctionExpression(binding.path.node.init)))
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
constBindings.forEach(([name]) => {
|
|
150
|
+
this.topLevelConstBindings.add(name);
|
|
151
|
+
});
|
|
152
|
+
},
|
|
127
153
|
/**
|
|
128
154
|
* Store the name of the imported 'css' and 'styled' variables e.g.:
|
|
129
155
|
* - `import { css, styled } from 'next-yak'` -> { css: 'css', styled: 'styled' }
|
|
@@ -326,6 +352,39 @@ module.exports = function (babel, options) {
|
|
|
326
352
|
(type.empty && wasInsideCssValue))
|
|
327
353
|
) {
|
|
328
354
|
wasInsideCssValue = true;
|
|
355
|
+
// to prevent overuse of css variables, we only allow expressions
|
|
356
|
+
// for css variables for arrow function expressions
|
|
357
|
+
if (
|
|
358
|
+
// e.g. styled.div`color: ${x};`
|
|
359
|
+
(t.isIdentifier(expression) &&
|
|
360
|
+
this.topLevelConstBindings.has(expression.name)) ||
|
|
361
|
+
// e.g. styled.div`color: ${x.y};`
|
|
362
|
+
(t.isMemberExpression(expression) &&
|
|
363
|
+
t.isIdentifier(expression.object) &&
|
|
364
|
+
this.topLevelConstBindings.has(expression.object.name)) ||
|
|
365
|
+
// e.g. styled.div`color: ${x()};`
|
|
366
|
+
(t.isCallExpression(expression) &&
|
|
367
|
+
// x()
|
|
368
|
+
((t.isIdentifier(expression.callee) &&
|
|
369
|
+
this.topLevelConstBindings.has(expression.callee.name)) ||
|
|
370
|
+
// x.y()
|
|
371
|
+
(t.isMemberExpression(expression.callee) &&
|
|
372
|
+
t.isIdentifier(expression.callee.object) &&
|
|
373
|
+
this.topLevelConstBindings.has(
|
|
374
|
+
expression.callee.object.name
|
|
375
|
+
))))
|
|
376
|
+
) {
|
|
377
|
+
throw new InvalidPositionError(
|
|
378
|
+
"Possible constant used as runtime value for a css variable\n" +
|
|
379
|
+
"Please move the constant to a .yak import or use an arrow function\n" +
|
|
380
|
+
"e.g.:\n" +
|
|
381
|
+
"| import { primaryColor } from './foo.yak'\n" +
|
|
382
|
+
"| const MyStyledDiv = styled.div`color: ${primaryColor};`",
|
|
383
|
+
expression,
|
|
384
|
+
this.file
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
|
|
329
388
|
if (!cssVariablesInlineStyle) {
|
|
330
389
|
cssVariablesInlineStyle = t.objectExpression([]);
|
|
331
390
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-yak",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.27",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"types": "./dist/",
|
|
6
6
|
"exports": {
|
|
@@ -30,22 +30,6 @@
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
},
|
|
33
|
-
"scripts": {
|
|
34
|
-
"prepublishOnly": "npm run build && npm run test && npm run test:types:code && npm run test:types:test",
|
|
35
|
-
"build": "pnpm run --filter . \"/^build:/\"",
|
|
36
|
-
"build:runtime": "tsup runtime/index.ts --target es2022 --clean --external react --external next-yak/context --format cjs,esm --minify --sourcemap --out-dir dist",
|
|
37
|
-
"build:runtime:types": "tsup runtime/index.ts --target es2022 --clean --external react --dts-only --format cjs,esm --minify --sourcemap --out-dir dist",
|
|
38
|
-
"build:static": "tsup static/index.ts --target es2022 --clean --external react --external next-yak/context --format cjs,esm --minify --sourcemap --out-dir dist/static",
|
|
39
|
-
"build:static:types": "tsup static/index.ts --target es2022 --clean --external react --dts-only --format cjs,esm --minify --sourcemap --out-dir dist/static",
|
|
40
|
-
"build:baseContext": "tsup runtime/context/baseContext.tsx --target es2022 --external react --dts --format cjs,esm --sourcemap --out-dir dist/context/",
|
|
41
|
-
"build:context:client": "tsup runtime/context/index.tsx --target es2022 --external react --external next-yak/context/baseContext --dts --format cjs,esm --sourcemap --out-dir dist/context/",
|
|
42
|
-
"build:context:server": "tsup runtime/context/index.server.tsx --target es2022 --external react --external next-yak/context/baseContext --external ./index.js --format cjs,esm --sourcemap --out-dir dist/context/",
|
|
43
|
-
"build:withYak": "tsup withYak/index.ts --target es2022 --dts --format cjs --sourcemap --out-dir withYak --external ../loaders/tsloader.cjs --external ../loaders/cssloader.cjs",
|
|
44
|
-
"test": "vitest run",
|
|
45
|
-
"test:types:code": "tsc -p tsconfig.json",
|
|
46
|
-
"test:types:test": "tsc -p ./runtime/__tests__/tsconfig.json",
|
|
47
|
-
"test:watch": "vitest --watch"
|
|
48
|
-
},
|
|
49
33
|
"dependencies": {
|
|
50
34
|
"@babel/core": "7.23.2",
|
|
51
35
|
"@babel/plugin-syntax-typescript": "7.22.5",
|
|
@@ -74,5 +58,20 @@
|
|
|
74
58
|
"loaders",
|
|
75
59
|
"runtime",
|
|
76
60
|
"withYak"
|
|
77
|
-
]
|
|
78
|
-
|
|
61
|
+
],
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "pnpm run --filter . \"/^build:/\"",
|
|
64
|
+
"build:runtime": "tsup runtime/index.ts --target es2022 --clean --external react --external next-yak/context --format cjs,esm --minify --sourcemap --out-dir dist",
|
|
65
|
+
"build:runtime:types": "tsup runtime/index.ts --target es2022 --clean --external react --dts-only --format cjs,esm --minify --sourcemap --out-dir dist",
|
|
66
|
+
"build:static": "tsup static/index.ts --target es2022 --clean --external react --external next-yak/context --format cjs,esm --minify --sourcemap --out-dir dist/static",
|
|
67
|
+
"build:static:types": "tsup static/index.ts --target es2022 --clean --external react --dts-only --format cjs,esm --minify --sourcemap --out-dir dist/static",
|
|
68
|
+
"build:baseContext": "tsup runtime/context/baseContext.tsx --target es2022 --external react --dts --format cjs,esm --sourcemap --out-dir dist/context/",
|
|
69
|
+
"build:context:client": "tsup runtime/context/index.tsx --target es2022 --external react --external next-yak/context/baseContext --dts --format cjs,esm --sourcemap --out-dir dist/context/",
|
|
70
|
+
"build:context:server": "tsup runtime/context/index.server.tsx --target es2022 --external react --external next-yak/context/baseContext --external ./index.js --format cjs,esm --sourcemap --out-dir dist/context/",
|
|
71
|
+
"build:withYak": "tsup withYak/index.ts --target es2022 --dts --format cjs --sourcemap --out-dir withYak --external ../loaders/tsloader.cjs --external ../loaders/cssloader.cjs",
|
|
72
|
+
"test": "vitest run",
|
|
73
|
+
"test:types:code": "tsc -p tsconfig.json",
|
|
74
|
+
"test:types:test": "tsc -p ./runtime/__tests__/tsconfig.json",
|
|
75
|
+
"test:watch": "vitest --watch"
|
|
76
|
+
}
|
|
77
|
+
}
|