@yak/react 0.0.0-stage → 10.0.0-rc.1

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.
Files changed (91) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +114 -2
  3. package/dist/context/baseContext.d.ts +5 -0
  4. package/dist/context/baseContext.js +8 -0
  5. package/dist/context/baseContext.js.map +1 -0
  6. package/dist/context/index.d.ts +20 -0
  7. package/dist/context/index.js +15 -0
  8. package/dist/context/index.js.map +1 -0
  9. package/dist/context/index.server.d.ts +9 -0
  10. package/dist/context/index.server.js +17 -0
  11. package/dist/context/index.server.js.map +1 -0
  12. package/dist/cross-file-resolver/index.d.ts +199 -0
  13. package/dist/cross-file-resolver/index.js +569 -0
  14. package/dist/cross-file-resolver/index.js.map +1 -0
  15. package/dist/index.d.ts +320 -0
  16. package/dist/index.js +2 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/internal.d.ts +452 -0
  19. package/dist/internal.js +363 -0
  20. package/dist/internal.js.map +1 -0
  21. package/dist/isolated-source-eval/index.d.ts +30 -0
  22. package/dist/isolated-source-eval/index.js +317 -0
  23. package/dist/isolated-source-eval/index.js.map +1 -0
  24. package/dist/isolated-source-eval/worker.js +59 -0
  25. package/dist/isolated-source-eval/worker.js.map +1 -0
  26. package/dist/jsx-dev-runtime.d.ts +7 -0
  27. package/dist/jsx-dev-runtime.js +2 -0
  28. package/dist/jsx-dev-runtime.js.map +1 -0
  29. package/dist/jsx-runtime.d.ts +38 -0
  30. package/dist/jsx-runtime.js +2 -0
  31. package/dist/jsx-runtime.js.map +1 -0
  32. package/dist/loaders/turbo-loader.d.ts +15 -0
  33. package/dist/loaders/turbo-loader.js +816 -0
  34. package/dist/loaders/turbo-loader.js.map +1 -0
  35. package/dist/loaders/vite-plugin.d.ts +103 -0
  36. package/dist/loaders/vite-plugin.js +885 -0
  37. package/dist/loaders/vite-plugin.js.map +1 -0
  38. package/dist/loaders/webpack-loader.d.ts +12 -0
  39. package/dist/loaders/webpack-loader.js +710 -0
  40. package/dist/loaders/webpack-loader.js.map +1 -0
  41. package/dist/rsbuild/index.d.ts +29 -0
  42. package/dist/rsbuild/index.js +51 -0
  43. package/dist/rsbuild/index.js.map +1 -0
  44. package/dist/static/chunk-DK3Fl9T5.js +1 -0
  45. package/dist/static/index.d.ts +28 -0
  46. package/dist/static/index.js +2 -0
  47. package/dist/static/index.js.map +1 -0
  48. package/dist/withYak/index.d.ts +112 -0
  49. package/dist/withYak/index.js +171 -0
  50. package/dist/withYak/index.js.map +1 -0
  51. package/loaders/lib/resolveCrossFileSelectors.ts +161 -0
  52. package/loaders/turbo-evaluator.ts +134 -0
  53. package/loaders/turbo-loader.ts +184 -0
  54. package/loaders/vite-plugin.ts +28 -0
  55. package/loaders/webpack-loader.ts +41 -0
  56. package/package.json +155 -3
  57. package/rsbuild/index.ts +91 -0
  58. package/runtime/__tests__/atoms.test.tsx +48 -0
  59. package/runtime/__tests__/attrs.test.tsx +490 -0
  60. package/runtime/__tests__/cssLiteral.test.tsx +503 -0
  61. package/runtime/__tests__/cssProp.test.tsx +392 -0
  62. package/runtime/__tests__/cssPropTest.tsx +227 -0
  63. package/runtime/__tests__/foldedJsxParity.test.tsx +117 -0
  64. package/runtime/__tests__/styled.test.tsx +653 -0
  65. package/runtime/__tests__/tsconfig.json +18 -0
  66. package/runtime/__tests__/typeTest.tsx +374 -0
  67. package/runtime/atoms.tsx +36 -0
  68. package/runtime/context/README.md +84 -0
  69. package/runtime/context/baseContext.tsx +5 -0
  70. package/runtime/context/index.server.tsx +22 -0
  71. package/runtime/context/index.tsx +36 -0
  72. package/runtime/cssLiteral.tsx +214 -0
  73. package/runtime/globalStyle.tsx +54 -0
  74. package/runtime/index.ts +37 -0
  75. package/runtime/internal.ts +42 -0
  76. package/runtime/internals/mergeClassNames.ts +23 -0
  77. package/runtime/internals/mergeCssProp.ts +69 -0
  78. package/runtime/internals/propMarkers.ts +20 -0
  79. package/runtime/internals/unitPostFix.ts +34 -0
  80. package/runtime/jsx-dev-runtime.ts +6 -0
  81. package/runtime/jsx-runtime.ts +48 -0
  82. package/runtime/keyframes.tsx +30 -0
  83. package/runtime/mocks/cssLiteral.ts +74 -0
  84. package/runtime/mocks/globalStyle.ts +14 -0
  85. package/runtime/mocks/keyframes.ts +29 -0
  86. package/runtime/mocks/styled.ts +8 -0
  87. package/runtime/publicStyledApi.ts +159 -0
  88. package/runtime/styled.tsx +372 -0
  89. package/runtime/styledDom.tsx +139 -0
  90. package/withYak/allow-global-css.ts +94 -0
  91. package/withYak/index.ts +268 -0
@@ -0,0 +1,374 @@
1
+ import { css, styled, keyframes, GenericYakComponentOf } from "@yak/react";
2
+ import React, { ReactElement } from "react";
3
+ import { useRef } from "react";
4
+
5
+ declare module "@yak/react" {
6
+ export interface YakTheme {
7
+ mood: "happy" | "sad";
8
+ }
9
+ }
10
+
11
+ const keyframe = keyframes`
12
+ from {
13
+ opacity: 0;
14
+ }
15
+ to {
16
+ opacity: 1;
17
+ }
18
+ `;
19
+
20
+ const mixin = css`
21
+ animation: ${keyframe} 1s;
22
+ color: ${({ theme }) => {
23
+ // verify that theme is typed correctly in styled.tsx
24
+ const mood: "happy" | "sad" = theme.mood;
25
+ return mood === "happy" ? "green" : "red";
26
+ }};
27
+ `;
28
+
29
+ const Button = styled.button`
30
+ color: ${({ theme }) => {
31
+ // verify that theme is typed correctly in styled.tsx
32
+ const mood: "happy" | "sad" = theme.mood;
33
+ return mood === "happy" ? "green" : "red";
34
+ }};
35
+ ${mixin}
36
+ `;
37
+
38
+ // Verify that a HTMLButtonElement Ref can be
39
+ // used for a styled.button component
40
+ const RefTest1 = () => {
41
+ const ref = useRef<HTMLButtonElement>(null);
42
+ return <Button ref={ref}>Hello World</Button>;
43
+ };
44
+
45
+ // Verify that a HTMLButtonElement Ref function can be
46
+ // used for a styled.button component
47
+ const RefTest2 = () => {
48
+ return (
49
+ <Button
50
+ ref={(element) => {
51
+ const button: HTMLButtonElement = element!;
52
+ console.log(button);
53
+ }}
54
+ >
55
+ Hello World
56
+ </Button>
57
+ );
58
+ };
59
+
60
+ // Verify that a ref is inferred correctly
61
+ const Button2 = styled(Button)``;
62
+ const RefTest3 = () => {
63
+ const ref = useRef<HTMLButtonElement>(null);
64
+ return <Button2 ref={ref}>Hello World</Button2>;
65
+ };
66
+
67
+ // Verify that a ref function is inferred correctly
68
+ const RefTest4 = () => {
69
+ return (
70
+ <Button2
71
+ ref={(element) => {
72
+ const button: HTMLButtonElement = element!;
73
+ console.log(button);
74
+ }}
75
+ >
76
+ Hello World
77
+ </Button2>
78
+ );
79
+ };
80
+
81
+ // Verify that props are merged correctly
82
+ const Button3 = styled.button<{ $primary?: boolean }>`
83
+ ${({ $primary }) =>
84
+ $primary &&
85
+ css`
86
+ color: green;
87
+ `}
88
+ `;
89
+ const Button4 = styled(Button3)<{ $secondary?: boolean }>`
90
+ ${({ $secondary }) =>
91
+ $secondary &&
92
+ css`
93
+ color: blue;
94
+ `}
95
+ `;
96
+
97
+ const MergeTest1 = () => {
98
+ return (
99
+ <Button4 type="button" $primary $secondary>
100
+ Click me
101
+ </Button4>
102
+ );
103
+ };
104
+
105
+ const MergeTestErrors = () => {
106
+ switch (Math.random().toString(64)) {
107
+ case "$primary":
108
+ // @ts-expect-error - primary must only allow booleans
109
+ return <Button4 $primary="true" />;
110
+ case "$secondary":
111
+ // @ts-expect-error - secondary must only allow booleans
112
+ return <Button4 $secondary="true" />;
113
+ case "html prop":
114
+ // @ts-expect-error - only valid html attribute values are allowed
115
+ return <Button4 type="submit submit" />;
116
+ case "html prop2":
117
+ return <Button4 type="submit" />;
118
+ case "ref":
119
+ // @ts-expect-error - only valid html elements
120
+ return <Button4 ref={(element: HTMLDivElement | null) => {}} />;
121
+ case "theme":
122
+ // @ts-expect-error - theme is not part of the props
123
+ return <Button4 theme={{ mood: "happy" }} />;
124
+ default:
125
+ return null;
126
+ }
127
+ };
128
+
129
+ // Verify that a ref is not added if the component does not accept it
130
+ const Button5 = (props: { className: string }) => <button {...props}>Hello World</button>;
131
+ const Button6 = styled(Button5)``;
132
+ <Button6
133
+ // @ts-expect-error - should not allow ref on a component that does not accept it
134
+ ref={console.log}
135
+ />;
136
+
137
+ const CompositionOverridingAndMergingTest = () => {
138
+ const case1 = () => {
139
+ const Child = styled.div<{ $child: boolean }>`
140
+ ${({ $child }) =>
141
+ $child &&
142
+ css`
143
+ color: red;
144
+ `}
145
+ `;
146
+
147
+ const Parent = styled.div`
148
+ ${Child} {
149
+ color: blue;
150
+ }
151
+ `;
152
+
153
+ // should not inherit component props of Child
154
+ const _ = (
155
+ <Parent>
156
+ <Child $child />
157
+ </Parent>
158
+ );
159
+ };
160
+
161
+ const case2 = () => {
162
+ const Child = styled.div<{ $child: boolean }>`
163
+ ${({ $child }) =>
164
+ $child &&
165
+ css`
166
+ color: red;
167
+ `}
168
+ `;
169
+
170
+ const Mixin = css<{ $mixin: boolean }>`
171
+ color: ${({ $mixin }) => ($mixin ? `blue` : `red`)};
172
+ `;
173
+
174
+ const Parent = styled.div<{ $mixin: boolean }>`
175
+ ${Child} {
176
+ color: blue;
177
+ }
178
+
179
+ ${Mixin};
180
+ `;
181
+
182
+ // Overriding with Child and Mixin should work
183
+ const _ = (
184
+ <Parent $mixin>
185
+ <Child $child />
186
+ </Parent>
187
+ );
188
+ };
189
+
190
+ const case3 = () => {
191
+ const Child = styled.div<{ $child: boolean }>`
192
+ ${({ $child }) =>
193
+ $child &&
194
+ css`
195
+ color: red;
196
+ `}
197
+ `;
198
+
199
+ const Parent = styled.div`
200
+ ${Child} {
201
+ color: blue;
202
+ }
203
+ color: ${
204
+ // @ts-expect-error - should not allow props which have not been defined (like styled-components)
205
+ ({ $colorMe }) => ($colorMe ? `blue` : `red`)
206
+ };
207
+ `;
208
+
209
+ // Overriding with Child and Mixin should work
210
+ const _ = (
211
+ // @ts-expect-error - should not allow props which have not been defined (like styled-components)
212
+ <Parent $colorMe>
213
+ <Child $child />
214
+ </Parent>
215
+ );
216
+ };
217
+
218
+ const case4 = () => {
219
+ const Wrap = styled.button``;
220
+ const Icon = styled.svg<{ $active: boolean }>``;
221
+ const Button = styled(Wrap)`
222
+ ${Icon} {
223
+ color: red;
224
+ }
225
+ `;
226
+ const Button2 = styled.button`
227
+ ${Icon} {
228
+ color: red;
229
+ }
230
+ `;
231
+
232
+ // @ts-expect-error $active should not be required
233
+ <Button $active={true}>Click me</Button>;
234
+
235
+ // @ts-expect-error $active should not be required
236
+ <Button2 $active={true}>Click me</Button2>;
237
+
238
+ // should be callable without any properties
239
+ <Button>Click me</Button>;
240
+ <Button2>Click me</Button2>;
241
+
242
+ // the types should be equal
243
+ type Equal = typeof Button extends typeof Button2 ? true : false;
244
+ const _: Equal = true;
245
+ };
246
+ };
247
+
248
+ const GenericComponentWorks = () => {
249
+ interface MyGenericProps<T> {
250
+ input: T;
251
+ }
252
+
253
+ type MyGenericComponent<T> = React.ElementType<MyGenericProps<T>> & {
254
+ <TOther>(props: MyGenericProps<TOther>): React.ReactNode;
255
+ };
256
+
257
+ type MyDefaultType = {
258
+ [x: string]: any;
259
+ };
260
+
261
+ const GenericComponent: MyGenericComponent<MyDefaultType> = () => {
262
+ return null;
263
+ };
264
+
265
+ const ConcreteStyledComponent = GenericComponent<"test">;
266
+
267
+ const MyComponent = styled(ConcreteStyledComponent)``;
268
+
269
+ const Test = () => {
270
+ return (
271
+ <div>
272
+ <MyComponent input="test" />
273
+ </div>
274
+ );
275
+ };
276
+ };
277
+
278
+ const KeyframesWithConstants = () => {
279
+ const constString = "rotate(360deg)";
280
+ const constNumber = 360;
281
+
282
+ const rotate = keyframes`
283
+ from {
284
+ transform: rotate(${constNumber}deg);
285
+ }
286
+ to {
287
+ transform: ${constString};
288
+ }
289
+ `;
290
+ };
291
+
292
+ const SelectorMixinsShouldNotAlterType = () => {
293
+ const Button = styled.button<{ $primary?: boolean }>``;
294
+
295
+ const Mixin = css`
296
+ ${Button} {
297
+ color: red;
298
+ }
299
+ `;
300
+
301
+ const X = styled.div`
302
+ ${Button} {
303
+ color: blue;
304
+ }
305
+ ${Mixin};
306
+ `;
307
+
308
+ <X />;
309
+ };
310
+
311
+ const InferenceShouldWorkWithComplexTypes = () => {
312
+ const Title = styled<React.ComponentPropsWithRef<"strong" | "h1">>({} as any)<{
313
+ $primary: boolean;
314
+ }>`
315
+ ${({ $primary }) => {
316
+ if ($primary) {
317
+ return css`
318
+ color: red;
319
+ `;
320
+ }
321
+ }}
322
+ `;
323
+ };
324
+
325
+ const WebComponentsShouldWork = () => {
326
+ const MyWebComponent = styled("my-web-component")<{ $primary?: boolean }>`
327
+ color: red;
328
+ `;
329
+
330
+ // @ts-expect-error - web component props should match the defined props
331
+ <MyWebComponent $primary={42} />;
332
+
333
+ // Should allow all valid HTML attributes
334
+ <MyWebComponent onClick={(e) => console.log("clicked", e.target)} />;
335
+
336
+ // Should allow refs
337
+ <MyWebComponent
338
+ $primary
339
+ ref={(el) => {
340
+ if (el) {
341
+ console.log(el.classList.toggle("active"));
342
+ }
343
+ }}
344
+ />;
345
+
346
+ <MyWebComponent $primary />;
347
+
348
+ return <MyWebComponent />;
349
+ };
350
+
351
+ const GenericYakComponentShouldWork = () => {
352
+ const Component: <T extends object>(props: T) => ReactElement<T> = (props) => (
353
+ <div {...props}>hello</div>
354
+ );
355
+ <Component<{ TEST: "ME" }> TEST="ME" />;
356
+
357
+ const StyledComponent = styled(Component)`` as GenericYakComponentOf<typeof Component>;
358
+ const StyledComponentWithAdditionalProps = styled(Component)`` as GenericYakComponentOf<
359
+ typeof Component,
360
+ {
361
+ $primary?: boolean;
362
+ }
363
+ >;
364
+ <StyledComponent<{ TEST: "ME" }> TEST="ME" />;
365
+ <StyledComponentWithAdditionalProps<{ TEST: "ME" }> TEST="ME" $primary />;
366
+
367
+ const shouldBeSelectable = styled.div`
368
+ ${StyledComponent} {
369
+ &:hover {
370
+ color: blue;
371
+ }
372
+ }
373
+ `;
374
+ };
@@ -0,0 +1,36 @@
1
+ import { ClassNames, ComponentStyles, css } from "./cssLiteral.js";
2
+ import { RuntimeStyleProcessor } from "./publicStyledApi.js";
3
+
4
+ /**
5
+ * Allows to use atomic CSS classes in a styled or css block
6
+ *
7
+ * @usage
8
+ *
9
+ * ```tsx
10
+ * import { styled, atoms } from "@yak/react";
11
+ *
12
+ * const Button = styled.button<{ $primary?: boolean }>`
13
+ * ${atoms("text-teal-600", "text-base", "rounded-md")}
14
+ * ${props => props.$primary && atoms("shadow-md")}
15
+ * `;
16
+ * ```
17
+ */
18
+ export const atoms = <T,>(
19
+ ...atoms: (string | RuntimeStyleProcessor<T> | false)[]
20
+ ): ComponentStyles<T> => {
21
+ const staticClasses = new ClassNames();
22
+ const dynamicFunctions: RuntimeStyleProcessor<T>[] = [];
23
+
24
+ for (const atom of atoms) {
25
+ if (typeof atom === "string") {
26
+ staticClasses.add(atom);
27
+ } else if (typeof atom === "function") {
28
+ dynamicFunctions.push(atom);
29
+ }
30
+ }
31
+
32
+ // the collected classes are passed as css()'s static class name,
33
+ // only the dynamic atoms stay functions
34
+ // @ts-expect-error the internal implementation of css is not typed
35
+ return css(staticClasses.value, ...dynamicFunctions);
36
+ };
@@ -0,0 +1,84 @@
1
+ # Yak Theme Context
2
+
3
+ The yak theme context allows users to configure the theme of their yak application.
4
+
5
+ It works for react-server components and for normal react components.
6
+
7
+ ## Usage
8
+
9
+ The context will be available in every `styled` and `css` literal:
10
+
11
+ ```tsx
12
+ const Headline = styled.h1`
13
+ color: ${(props) => props.theme.colors.primary};
14
+ `;
15
+ ```
16
+
17
+ ## React Server Components
18
+
19
+ To use the yak theme context in a react server component, you need to place a `yak.context.tsx` in
20
+ the same directory as your Next.js config.
21
+
22
+ ```tsx
23
+ // yak.context.tsx
24
+ export async function getYakThemeContext() {
25
+ return {
26
+ colors: {
27
+ primary: "red",
28
+ secondary: "blue",
29
+ },
30
+ };
31
+ }
32
+
33
+ declare module "@yak/react/context" {
34
+ export interface YakTheme extends Awaited<ReturnType<typeof getYakThemeContext>> {}
35
+ }
36
+ ```
37
+
38
+ > **Note:** The `getYakThemeContext` function can be async, which is useful when you need to access
39
+ > server-side APIs like `cookies()` or `headers()` from Next.js 15+. @yak/react automatically handles
40
+ > the promise resolution using React's `use()` hook internally.
41
+
42
+ In your root layout component you have to forward the entire response of `getYakThemeContext`
43
+ context to the client:
44
+
45
+ ```tsx
46
+ // layout.tsx
47
+ import { YakThemeProvider } from "@yak/react";
48
+
49
+ export function Layout({ children }) {
50
+ return <YakThemeProvider>{children}</YakThemeProvider>;
51
+ }
52
+ ```
53
+
54
+ Internally this works because of an alias set by `withYak` inside the NextJs config.
55
+
56
+ ## React Components
57
+
58
+ If you are using server components you can **skip this section**.
59
+
60
+ To use the yak theme context in a normal react component, you can just use the `YakThemeProvider`:
61
+
62
+ ```tsx
63
+ // _app.tsx
64
+ import { YakThemeProvider } from "@yak/react/context";
65
+
66
+ const theme = {
67
+ colors: {
68
+ primary: "red",
69
+ secondary: "blue",
70
+ },
71
+ };
72
+
73
+ declare module "@yak/react/context" {
74
+ export interface YakTheme extends typeof theme { }
75
+ }
76
+
77
+ export function App({ children }) {
78
+ return (
79
+ <YakThemeProvider value={theme}>
80
+ {children}
81
+ </YakThemeProvider>
82
+ );
83
+ }
84
+ ```
@@ -0,0 +1,5 @@
1
+ import type { YakTheme } from "@yak/react";
2
+
3
+ export function getYakThemeContext() {
4
+ return {} as YakTheme | undefined;
5
+ }
@@ -0,0 +1,22 @@
1
+ //
2
+ // This file is the react-server component version of index.tsx
3
+ //
4
+
5
+ // @ts-ignore - in the current @types/react "cache" is not typed
6
+ import React, { ReactNode, cache, use } from "react";
7
+ import { YakTheme, YakThemeProvider as YakThemeClientProvider } from "./index.js";
8
+
9
+ // the following import might be changed by
10
+ // the user config in withYak to point to their own
11
+ // context
12
+ import { getYakThemeContext } from "@yak/react/context/baseContext";
13
+
14
+ /** Request based RSC YAK Context */
15
+ const getYakContext = cache(() => getYakThemeContext());
16
+ export const useTheme = (): YakTheme | undefined => {
17
+ const theme: YakTheme | undefined | Promise<YakTheme> = getYakContext();
18
+ return theme instanceof Promise ? use(theme) : theme;
19
+ };
20
+ export const YakThemeProvider = ({ children }: { children: ReactNode }) => {
21
+ return <YakThemeClientProvider theme={getYakContext()}>{children}</YakThemeClientProvider>;
22
+ };
@@ -0,0 +1,36 @@
1
+ "use client";
2
+ //
3
+ // This file is the client component (browser & ssr) version of index.server.tsx
4
+ //
5
+ import React, { ReactNode, createContext, use } from "react";
6
+
7
+ export interface YakTheme {}
8
+
9
+ /**
10
+ * The yak theme context
11
+ * @see https://github.com/DigitecGalaxus/next-yak/blob/main/packages/yak-react/runtime/context/README.md
12
+ */
13
+ const YakContext = createContext<YakTheme>({});
14
+
15
+ /**
16
+ * Returns the current yak theme context
17
+ *
18
+ * @see https://github.com/DigitecGalaxus/next-yak/blob/main/packages/yak-react/runtime/context/README.md
19
+ */
20
+ export const useTheme = (): YakTheme => {
21
+ const context = use(YakContext);
22
+ return context instanceof Promise ? use(context) : context;
23
+ };
24
+
25
+ /**
26
+ * Yak theme context provider
27
+ *
28
+ * @see https://github.com/DigitecGalaxus/next-yak/blob/main/packages/yak-react/runtime/context/README.md
29
+ */
30
+ export const YakThemeProvider = ({
31
+ children,
32
+ theme = {},
33
+ }: {
34
+ children: ReactNode;
35
+ theme?: YakTheme;
36
+ }) => <YakContext.Provider value={theme}>{children}</YakContext.Provider>;