@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,490 @@
1
+ import React, { FunctionComponent } from "react";
2
+ import { render } from "@testing-library/react";
3
+ import { beforeEach, expect, describe, it, vi } from "vitest";
4
+ import { YakThemeProvider } from "../context";
5
+ import { styled as styledFn } from "../styled";
6
+
7
+ // This transform is usually done by the SWC plugin.
8
+ // However this `attrs.test.tsx` does not compile
9
+ // the code before testing
10
+ const styled = Object.assign(styledFn, {
11
+ div: styledFn("div"),
12
+ h1: styledFn("h1"),
13
+ button: styledFn("button"),
14
+ a: styledFn("a"),
15
+ p: styledFn("p"),
16
+ span: styledFn("span"),
17
+ }) as typeof styledFn;
18
+
19
+ beforeEach(() => {
20
+ vi.spyOn(console, "warn");
21
+ });
22
+
23
+ type DataAttributes = { [key: `data-${string}`]: any };
24
+
25
+ const getSnapshot = (element: React.ReactElement) => render(element).container.firstChild;
26
+
27
+ it("works fine with an empty object", () => {
28
+ const Comp = styled.div.attrs({})``;
29
+ expect(getSnapshot(<Comp />)).toMatchInlineSnapshot(`<div />`);
30
+ });
31
+
32
+ it("works fine with a function that returns an empty object", () => {
33
+ const Comp = styled.div.attrs(() => ({}))``;
34
+ expect(getSnapshot(<Comp />)).toMatchInlineSnapshot(`<div />`);
35
+ });
36
+
37
+ it("pass a simple attr via object", () => {
38
+ const Comp = styled.button.attrs({
39
+ type: "button",
40
+ })``;
41
+ expect(getSnapshot(<Comp />)).toMatchInlineSnapshot(`
42
+ <button
43
+ type="button"
44
+ />
45
+ `);
46
+ });
47
+
48
+ it("pass a simple attr via function with object return", () => {
49
+ const Comp = styled.button.attrs(() => ({
50
+ type: "button",
51
+ }))``;
52
+ expect(getSnapshot(<Comp />)).toMatchInlineSnapshot(`
53
+ <button
54
+ type="button"
55
+ />
56
+ `);
57
+ });
58
+
59
+ it("pass a React component", () => {
60
+ const ReactComponent: FunctionComponent = () => <p>React Component</p>;
61
+
62
+ type ButtonProps = {
63
+ component: FunctionComponent;
64
+ };
65
+
66
+ const Button = ({ component: ChildComponent }: ButtonProps) => (
67
+ <button>
68
+ <ChildComponent />
69
+ </button>
70
+ );
71
+
72
+ const Comp = styled(Button).attrs<Partial<ButtonProps>>(() => ({
73
+ component: ReactComponent,
74
+ }))``;
75
+
76
+ expect(getSnapshot(<Comp />)).toMatchInlineSnapshot(`
77
+ <button>
78
+ <p>
79
+ React Component
80
+ </p>
81
+ </button>
82
+ `);
83
+ });
84
+
85
+ it("should not call a function passed to attrs as an object value", () => {
86
+ const stub = vi.fn(() => "div");
87
+
88
+ const Comp = styled.button.attrs<{ foo?: typeof stub }>(() => ({
89
+ foo: stub,
90
+ }))``;
91
+
92
+ render(<Comp />);
93
+
94
+ expect(stub).not.toHaveBeenCalled();
95
+ });
96
+
97
+ it("pass props to the attr function", () => {
98
+ const Comp = styled.button.attrs<{ $submit?: boolean }>((p) => ({
99
+ type: p.$submit ? "submit" : "button",
100
+ }))``;
101
+
102
+ expect(getSnapshot(<Comp />)).toMatchInlineSnapshot(`
103
+ <button
104
+ type="button"
105
+ />
106
+ `);
107
+ expect(getSnapshot(<Comp $submit />)).toMatchInlineSnapshot(`
108
+ <button
109
+ type="submit"
110
+ />
111
+ `);
112
+ });
113
+
114
+ it("should replace props with attrs", () => {
115
+ const Comp = styled.button.attrs<{ $submit?: boolean }>((p) => ({
116
+ type: p.$submit ? "submit" : "button",
117
+ tabIndex: 0,
118
+ }))``;
119
+
120
+ expect(getSnapshot(<Comp />)).toMatchInlineSnapshot(`
121
+ <button
122
+ tabindex="0"
123
+ type="button"
124
+ />
125
+ `);
126
+ expect(getSnapshot(<Comp type="reset" />)).toMatchInlineSnapshot(`
127
+ <button
128
+ tabindex="0"
129
+ type="button"
130
+ />
131
+ `);
132
+ expect(getSnapshot(<Comp type="reset" tabIndex={-1} />)).toMatchInlineSnapshot(`
133
+ <button
134
+ tabindex="0"
135
+ type="button"
136
+ />
137
+ `);
138
+ });
139
+
140
+ it("should merge className", () => {
141
+ const Comp = styled.div.attrs(() => ({
142
+ className: "meow nya",
143
+ }))``;
144
+
145
+ expect(getSnapshot(<Comp />)).toMatchInlineSnapshot(`
146
+ <div
147
+ class="meow nya"
148
+ />
149
+ `);
150
+ });
151
+
152
+ it("should merge className from folded attrs", () => {
153
+ const Inner = styled.div.attrs({ className: "foo" })``;
154
+
155
+ const Comp = styled(Inner).attrs(() => ({
156
+ className: "meow nya",
157
+ }))``;
158
+
159
+ expect(getSnapshot(<Comp className="something" />)).toMatchInlineSnapshot(`
160
+ <div
161
+ class="something foo meow nya"
162
+ />
163
+ `);
164
+ });
165
+
166
+ it("should merge className even if its a function", () => {
167
+ const Comp = styled.div.attrs<{ $purr?: boolean }>((p) => ({
168
+ className: `meow ${p.$purr ? "purr" : "nya"}`,
169
+ }))``;
170
+
171
+ expect(getSnapshot(<Comp />)).toMatchInlineSnapshot(`
172
+ <div
173
+ class="meow nya"
174
+ />
175
+ `);
176
+ expect(getSnapshot(<Comp $purr />)).toMatchInlineSnapshot(`
177
+ <div
178
+ class="meow purr"
179
+ />
180
+ `);
181
+ });
182
+
183
+ it("should merge style", () => {
184
+ const Comp = styled.div.attrs(() => ({
185
+ style: { color: "red", background: "blue" },
186
+ }))``;
187
+
188
+ expect(getSnapshot(<Comp style={{ color: "green", borderStyle: "dotted" }} />))
189
+ .toMatchInlineSnapshot(`
190
+ <div
191
+ style="color: red; border-style: dotted; background: blue;"
192
+ />
193
+ `);
194
+ });
195
+
196
+ it("should work with data and aria attributes", () => {
197
+ const Comp = styled.div.attrs<DataAttributes>(() => ({
198
+ "data-foo": "bar",
199
+ "aria-label": "A simple FooBar",
200
+ }))``;
201
+ expect(getSnapshot(<Comp />)).toMatchInlineSnapshot(`
202
+ <div
203
+ aria-label="A simple FooBar"
204
+ data-foo="bar"
205
+ />
206
+ `);
207
+ });
208
+
209
+ it("merge attrs when inheriting SC", () => {
210
+ let attrsCallCount = 0;
211
+ const Parent = styled.button.attrs(() => {
212
+ // Parent should be called first and only once
213
+ // to behave exactly like styled-components
214
+ expect(attrsCallCount).toEqual(0);
215
+ attrsCallCount++;
216
+ return {
217
+ type: "button",
218
+ tabIndex: 0,
219
+ };
220
+ })``;
221
+ const Child = styled(Parent).attrs(() => {
222
+ // Child should be called second and only once
223
+ // to behave exactly like styled-components
224
+ expect(attrsCallCount).toEqual(1);
225
+ attrsCallCount++;
226
+ return {
227
+ type: "submit",
228
+ };
229
+ })``;
230
+ expect(getSnapshot(<Child />)).toMatchInlineSnapshot(`
231
+ <button
232
+ tabindex="0"
233
+ type="submit"
234
+ />
235
+ `);
236
+ });
237
+
238
+ it("overrides attrs of the wrapped component and appends its style class", () => {
239
+ // styled(SubmitButton) renders as a single flattened wrapper — this pins
240
+ // that flattening keeps styled-components semantics: the outer attrs win
241
+ // and the outer class comes after the inner one
242
+ // @ts-expect-error calling with a class name string mimics the compiled css form
243
+ const SubmitButton = styled.button.attrs({ type: "submit" })("submit_btn");
244
+ // @ts-expect-error calling with a class name string mimics the compiled css form
245
+ const ResetButton = styled(SubmitButton).attrs({ type: "reset" })("reset_btn");
246
+
247
+ expect(getSnapshot(<SubmitButton />)).toMatchInlineSnapshot(`
248
+ <button
249
+ class="submit_btn"
250
+ type="submit"
251
+ />
252
+ `);
253
+ expect(getSnapshot(<ResetButton />)).toMatchInlineSnapshot(`
254
+ <button
255
+ class="submit_btn reset_btn"
256
+ type="reset"
257
+ />
258
+ `);
259
+ });
260
+
261
+ it("pass attrs to style block", () => {
262
+ /* Would be a React Router Link in real life */
263
+ const Comp = styled.a.attrs<DataAttributes>(() => ({
264
+ href: "#",
265
+ "data-active-class-name": "--is-active",
266
+ // @ts-expect-error
267
+ }))("c1", {
268
+ style: {
269
+ "--testVar": (props: any) => props["data-active-class-name"] && "c2",
270
+ },
271
+ });
272
+ expect(getSnapshot(<Comp />)).toMatchInlineSnapshot(`
273
+ <a
274
+ class="c1"
275
+ data-active-class-name="--is-active"
276
+ href="#"
277
+ style="--testVar: c2;"
278
+ />
279
+ `);
280
+ });
281
+
282
+ it("should pass through children as a normal prop", () => {
283
+ const Comp = styled.div.attrs(() => ({
284
+ children: "Probably a bad idea",
285
+ }))``;
286
+ expect(getSnapshot(<Comp />)).toMatchInlineSnapshot(`
287
+ <div>
288
+ Probably a bad idea
289
+ </div>
290
+ `);
291
+ });
292
+
293
+ it("should pass through complex children as well", () => {
294
+ const Comp = styled.div.attrs(() => ({
295
+ children: <span>Probably a bad idea</span>,
296
+ }))``;
297
+ expect(getSnapshot(<Comp />)).toMatchInlineSnapshot(`
298
+ <div>
299
+ <span>
300
+ Probably a bad idea
301
+ </span>
302
+ </div>
303
+ `);
304
+ });
305
+
306
+ it("should override children", () => {
307
+ const X = styled.div``;
308
+ type XProps = React.ComponentProps<typeof X>["children"] & React.JSX.Element;
309
+ const Comp = styled.div.attrs(() => ({
310
+ children: <span>Amazing</span>,
311
+ }))``;
312
+ expect(getSnapshot(<Comp>Something else</Comp>)).toMatchInlineSnapshot(`
313
+ <div>
314
+ <span>
315
+ Amazing
316
+ </span>
317
+ </div>
318
+ `);
319
+ });
320
+
321
+ it('should shallow merge "style" prop + attr instead of overwriting', () => {
322
+ const Paragraph = styled.p.attrs<{ $fontScale?: number }>((p) => ({
323
+ style: {
324
+ ...p.style,
325
+ fontSize: `${p.$fontScale}em`,
326
+ },
327
+ }))<{ $fontScale: number }>``;
328
+
329
+ const Text: FunctionComponent<Partial<React.ComponentProps<typeof Paragraph>>> = (props) => {
330
+ const fontScale = 4;
331
+
332
+ return (
333
+ <Paragraph $fontScale={fontScale} {...props}>
334
+ {props.children}
335
+ </Paragraph>
336
+ );
337
+ };
338
+
339
+ const BlueText = styled(Text).attrs(() => ({
340
+ style: {
341
+ color: "blue",
342
+ },
343
+ }))``;
344
+
345
+ expect(getSnapshot(<BlueText>Hello</BlueText>)).toMatchInlineSnapshot(`
346
+ <p
347
+ style="color: blue; font-size: 4em;"
348
+ >
349
+ Hello
350
+ </p>
351
+ `);
352
+ });
353
+
354
+ it("does not pass transient props to HTML element", () => {
355
+ type CompProps = { $textColor: string };
356
+
357
+ const Comp = styled.div<CompProps>`
358
+ color: ${(props) => props.$textColor};
359
+ `;
360
+
361
+ const StyledComp = styled(Comp).attrs<Partial<CompProps>>(() => ({
362
+ $textColor: "red",
363
+ }))``;
364
+
365
+ expect(getSnapshot(<StyledComp />)).toMatchInlineSnapshot(`<div />`);
366
+ });
367
+
368
+ it.skip('should apply given "as" prop to the progressive type', () => {
369
+ const Comp = styled.div.attrs<{ as?: any }>({ as: "video" as const })``;
370
+
371
+ //@ts-expect-error
372
+ expect(getSnapshot(<Comp loop />)).toMatchInlineSnapshot();
373
+ });
374
+
375
+ // our own tests
376
+ it("should remap props", () => {
377
+ const Comp = styled.button.attrs<{ primary?: boolean; $submit?: boolean }>((p) => ({
378
+ type: p.$submit ? "submit" : "button",
379
+ $disabled: p.disabled,
380
+ }))<{ $disabled?: boolean }>``;
381
+
382
+ expect(getSnapshot(<Comp />)).toMatchInlineSnapshot(`
383
+ <button
384
+ type="button"
385
+ />
386
+ `);
387
+ expect(getSnapshot(<Comp disabled />)).toMatchInlineSnapshot(`
388
+ <button
389
+ disabled=""
390
+ type="button"
391
+ />
392
+ `);
393
+ expect(getSnapshot(<Comp $submit />)).toMatchInlineSnapshot(`
394
+ <button
395
+ type="submit"
396
+ />
397
+ `);
398
+ });
399
+
400
+ it("should have optional attrs props as component interface", () => {
401
+ const Comp = styled.h1.attrs<{ $primary?: boolean }>({
402
+ $primary: true,
403
+ })``;
404
+
405
+ expect(getSnapshot(<Comp />)).toMatchInlineSnapshot(`<h1 />`);
406
+ expect(getSnapshot(<Comp $primary />)).toMatchInlineSnapshot(`<h1 />`);
407
+ });
408
+
409
+ it("should have access to theme", () => {
410
+ const ThemePrinter = ({ theme, ...props }: { theme?: unknown }) => (
411
+ <pre {...props}>{JSON.stringify(theme)}</pre>
412
+ );
413
+ const Comp = styled(ThemePrinter).attrs<DataAttributes>((p) => ({
414
+ "data-color": (p.theme as { color: string }).color,
415
+ }))``;
416
+
417
+ expect(
418
+ getSnapshot(
419
+ <YakThemeProvider theme={{ color: "red" }}>
420
+ <Comp />
421
+ </YakThemeProvider>,
422
+ ),
423
+ ).toMatchInlineSnapshot(`
424
+ <pre
425
+ data-color="red"
426
+ />
427
+ `);
428
+ });
429
+
430
+ it("should pass theme if theme is overwritten", () => {
431
+ const ThemePrinter = ({ theme, ...props }: { theme?: unknown }) => (
432
+ <pre {...props}>{JSON.stringify(theme)}</pre>
433
+ );
434
+ const Comp = styled(ThemePrinter).attrs({
435
+ theme: { color: "blue" },
436
+ })``;
437
+
438
+ expect(
439
+ getSnapshot(
440
+ <YakThemeProvider theme={{ color: "red" }}>
441
+ <Comp />
442
+ </YakThemeProvider>,
443
+ ),
444
+ ).toMatchInlineSnapshot(`
445
+ <pre>
446
+ {"color":"blue"}
447
+ </pre>
448
+ `);
449
+ });
450
+
451
+ describe("attrs bug issue #163", () => {
452
+ it("should allow to delete a prop", () => {
453
+ const Comp = styled.h1.attrs<{ primary?: boolean }>({
454
+ primary: undefined,
455
+ })``;
456
+
457
+ expect(getSnapshot(<Comp primary />)).toMatchInlineSnapshot(`<h1 />`);
458
+ });
459
+
460
+ it("should allow to rename a prop", () => {
461
+ const Comp = styled.h1.attrs<{ primary?: boolean }>((p) => ({
462
+ ...p,
463
+ primary: undefined,
464
+ $primary: p.primary,
465
+ }))``;
466
+
467
+ expect(getSnapshot(<Comp primary />)).toMatchInlineSnapshot(`<h1 />`);
468
+ });
469
+
470
+ it("should allow to rename a prop in overwritten attrs", () => {
471
+ const Parent = styled.h1.attrs<{ count: number }>((p) => {
472
+ expect(p.count).toBe(1);
473
+ return {
474
+ count: ++p.count,
475
+ };
476
+ })``;
477
+ const Comp = styled(Parent).attrs<{ count: number }>((p) => {
478
+ expect(p.count).toBe(2);
479
+ return {
480
+ count: ++p.count,
481
+ };
482
+ })``;
483
+
484
+ expect(getSnapshot(<Comp count={1} />)).toMatchInlineSnapshot(`
485
+ <h1
486
+ count="3"
487
+ />
488
+ `);
489
+ });
490
+ });