@yak/solid 0.0.0 → 0.2.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 +37 -24
- package/dist/context/baseContext.d.ts +0 -1
- package/dist/context/index.d.ts +0 -1
- package/dist/context/index.js +2 -1
- package/dist/context/index.js.map +1 -1
- package/dist/index.d.ts +15 -40
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +42 -55
- package/dist/internal.js +433 -107
- package/dist/internal.js.map +1 -1
- package/dist/isolated-source-eval/index.js +3 -1
- package/dist/isolated-source-eval/index.js.map +1 -1
- package/dist/isolated-source-eval/worker.js.map +1 -1
- package/dist/loaders/vite-plugin.d.ts +2 -7
- package/dist/loaders/vite-plugin.js +12 -8
- package/dist/loaders/vite-plugin.js.map +1 -1
- package/loaders/vite-plugin.ts +2 -2
- package/package.json +24 -24
- package/runtime/__tests__/typeTest.tsx +10 -1
- package/runtime/atoms.ts +5 -3
- package/runtime/cssLiteral.ts +59 -57
- package/runtime/cssProp.ts +0 -2
- package/runtime/internals/mergeClasses.ts +1 -0
- package/runtime/internals/mergeCssProp.ts +68 -40
- package/runtime/mocks/cssLiteral.ts +2 -4
- package/runtime/mocks/styled.ts +6 -2
- package/runtime/publicStyledApi.ts +19 -7
- package/runtime/styled.ts +806 -306
- package/runtime/__tests__/styled.test.tsx +0 -270
package/runtime/cssLiteral.ts
CHANGED
|
@@ -1,22 +1,33 @@
|
|
|
1
1
|
import type { YakTheme } from "./index.ts";
|
|
2
2
|
import type { Accessor } from "solid-js";
|
|
3
|
-
import { ClassCollector,
|
|
3
|
+
import { ClassCollector, CompiledStyleProcessor, StyleObject } from "./publicStyledApi.js";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
// Symbol.for, not Symbol(): the package ships two bundles (the public entry
|
|
6
|
+
// and the internal one the compiler imports) and both contain this module. a
|
|
7
|
+
// styled component created through one must be recognized by the other
|
|
8
|
+
export const yakComponentSymbol = Symbol.for("@yak/solid:component");
|
|
6
9
|
|
|
7
10
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* the class names of one render, kept as one space-separated string in the
|
|
12
|
+
* order the style block adds them. add() is the hot path: an append, and a
|
|
13
|
+
* duplicate check only once a second name arrives. has() and delete() exist
|
|
14
|
+
* for runtime processors that take a name back out, such as an atom that
|
|
15
|
+
* reverts a class. a Set would need a split and a join per render for the
|
|
16
|
+
* same string
|
|
13
17
|
*/
|
|
14
18
|
export class Classes implements ClassCollector {
|
|
15
19
|
value: string;
|
|
20
|
+
/**
|
|
21
|
+
* false once a name came from the author, such as an atom. compiler names
|
|
22
|
+
* are safe to print as they are; serializeElement in styled.ts escapes the
|
|
23
|
+
* class once this is false
|
|
24
|
+
*/
|
|
25
|
+
generated = true;
|
|
16
26
|
constructor(initial?: string) {
|
|
17
27
|
this.value = initial || "";
|
|
18
28
|
}
|
|
19
|
-
add(name: string) {
|
|
29
|
+
add(name: string, generated = true) {
|
|
30
|
+
if (!generated) this.generated = false;
|
|
20
31
|
if (!this.value) {
|
|
21
32
|
this.value = name;
|
|
22
33
|
} else if (!this.has(name)) {
|
|
@@ -61,9 +72,9 @@ type CSSStyles<TProps = {}> = {
|
|
|
61
72
|
style: { [key: string]: string | ((props: TProps) => string) };
|
|
62
73
|
};
|
|
63
74
|
|
|
64
|
-
type CSSFunction = <TProps
|
|
75
|
+
export type CSSFunction = <TProps>(
|
|
65
76
|
styles: TemplateStringsArray,
|
|
66
|
-
...values: CSSInterpolation<TProps & { theme: Accessor<YakTheme> }>[]
|
|
77
|
+
...values: CSSInterpolation<NoInfer<TProps> & { theme: Accessor<YakTheme> }>[]
|
|
67
78
|
) => ComponentStyles<TProps>;
|
|
68
79
|
|
|
69
80
|
export type NestedRuntimeStyleProcessor = (
|
|
@@ -76,59 +87,52 @@ export type NestedRuntimeStyleProcessor = (
|
|
|
76
87
|
style?: StyleObject;
|
|
77
88
|
}
|
|
78
89
|
| void
|
|
90
|
+
| false
|
|
91
|
+
| null
|
|
92
|
+
| string
|
|
93
|
+
| number
|
|
79
94
|
| NestedRuntimeStyleProcessor;
|
|
80
95
|
|
|
81
96
|
/**
|
|
82
|
-
*
|
|
97
|
+
* the runtime behind css``. the compiler rewrites every css`` and styled``
|
|
98
|
+
* call, so what arrives here is never the template the author wrote but its
|
|
99
|
+
* compiled form: class names, callbacks and css-variable maps (examples in
|
|
100
|
+
* the loop below). the public typings describe the call before compilation,
|
|
101
|
+
* which is why this function is internal: mocks/cssLiteral.ts exports the
|
|
102
|
+
* css the author sees, cast to the public type.
|
|
83
103
|
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* During development the user of @yak/solid wants to work with the
|
|
88
|
-
* typings BEFORE compilation.
|
|
89
|
-
*
|
|
90
|
-
* Therefore this is only an internal function only and it must be cast to any
|
|
91
|
-
* before exported to the user.
|
|
92
|
-
*
|
|
93
|
-
* The internal functioning of css`` is to return a single callback function that runs all functions
|
|
94
|
-
* (or creates new ones if needed) that are passed as arguments. These functions receive the props, classes, and style object as arguments
|
|
95
|
-
* and operate directly on the classes and style objects.
|
|
104
|
+
* it returns one processor, (props, classes, style) => void, that runs every
|
|
105
|
+
* compiled piece: class names go into the collector, css values into the
|
|
106
|
+
* style object
|
|
96
107
|
*/
|
|
97
|
-
export function css<TProps>(
|
|
98
|
-
styles: TemplateStringsArray,
|
|
99
|
-
...values: CSSInterpolation<NoInfer<TProps> & { theme: Accessor<YakTheme> }>[]
|
|
100
|
-
): ComponentStyles<TProps>;
|
|
101
|
-
export function css<TProps>(...args: Array<any>): RuntimeStyleProcessor<TProps> {
|
|
102
|
-
// Normally this could be an array of strings passed, but as we transpile the usage of css`` ourselves, we control the arguments
|
|
103
|
-
// and ensure that only the first argument is a string (class name of the non-dynamic styles)
|
|
108
|
+
export function css<TProps>(...args: Array<any>): CompiledStyleProcessor<TProps> {
|
|
104
109
|
let staticClass: string | undefined;
|
|
105
110
|
const dynamicCssFunctions: NestedRuntimeStyleProcessor[] = [];
|
|
106
|
-
for (const arg of args as Array<string |
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
// compiled -> css("yak31e4")
|
|
111
|
+
for (const arg of args as Array<string | NestedRuntimeStyleProcessor | CSSStyles<any>>) {
|
|
112
|
+
// static css became a css-module class name at build time:
|
|
113
|
+
// css`color: red;` -> css("yak31e4")
|
|
110
114
|
if (typeof arg === "string") {
|
|
111
115
|
staticClass = arg;
|
|
112
116
|
}
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
//
|
|
117
|
+
// conditional css stays a callback that returns another compiled css():
|
|
118
|
+
// css`${props => props.active && css`color: red;`}`
|
|
119
|
+
// -> css(props => props.active && css("yak31e4"))
|
|
116
120
|
else if (typeof arg === "function") {
|
|
117
|
-
dynamicCssFunctions.push(arg
|
|
121
|
+
dynamicCssFunctions.push(arg);
|
|
118
122
|
}
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
//
|
|
123
|
+
// a css value became a variable the callback fills at render time:
|
|
124
|
+
// css`transform: translate(${props => props.x});`
|
|
125
|
+
// -> css("yak31e4", { style: { "--yakVarX": props => props.x } })
|
|
122
126
|
else if (typeof arg === "object" && "style" in arg) {
|
|
123
127
|
dynamicCssFunctions.push((props, _, style) => {
|
|
124
128
|
for (const key in arg.style) {
|
|
125
129
|
const value = arg.style[key];
|
|
126
130
|
if (typeof value === "function") {
|
|
127
131
|
style[key as keyof StyleObject] = String(
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
-
//
|
|
132
|
+
// a callback may return another callback before it yields the
|
|
133
|
+
// value, such as a theme-dependent one:
|
|
134
|
+
// const color = (props) => props.theme().mode === "dark" ? "black" : "white";
|
|
135
|
+
// css`border-color: ${color};`
|
|
132
136
|
recursivePropExecution(props, value),
|
|
133
137
|
) as never;
|
|
134
138
|
} else {
|
|
@@ -139,10 +143,8 @@ export function css<TProps>(...args: Array<any>): RuntimeStyleProcessor<TProps>
|
|
|
139
143
|
}
|
|
140
144
|
}
|
|
141
145
|
|
|
142
|
-
//
|
|
143
|
-
//
|
|
144
|
-
// `$dynamic: false` lets the styled runtime skip theme lookup and
|
|
145
|
-
// style-object allocation entirely for static components
|
|
146
|
+
// no dynamic parts, the common case: $dynamic false lets styled() skip the
|
|
147
|
+
// theme lookup and the style object for the component
|
|
146
148
|
if (dynamicCssFunctions.length === 0) {
|
|
147
149
|
return Object.assign(
|
|
148
150
|
(_: unknown, classes: ClassCollector) => {
|
|
@@ -150,8 +152,8 @@ export function css<TProps>(...args: Array<any>): RuntimeStyleProcessor<TProps>
|
|
|
150
152
|
classes.add(staticClass);
|
|
151
153
|
}
|
|
152
154
|
},
|
|
153
|
-
{ $dynamic: false },
|
|
154
|
-
)
|
|
155
|
+
{ $dynamic: false as const },
|
|
156
|
+
) satisfies CompiledStyleProcessor<TProps>;
|
|
155
157
|
}
|
|
156
158
|
|
|
157
159
|
return Object.assign(
|
|
@@ -160,15 +162,15 @@ export function css<TProps>(...args: Array<any>): RuntimeStyleProcessor<TProps>
|
|
|
160
162
|
classes.add(staticClass);
|
|
161
163
|
}
|
|
162
164
|
for (let i = 0; i < dynamicCssFunctions.length; i++) {
|
|
163
|
-
|
|
165
|
+
runProcessor(props, dynamicCssFunctions[i], classes, allStyles);
|
|
164
166
|
}
|
|
165
167
|
},
|
|
166
|
-
{ $dynamic: true },
|
|
167
|
-
)
|
|
168
|
+
{ $dynamic: true as const },
|
|
169
|
+
) satisfies CompiledStyleProcessor<TProps>;
|
|
168
170
|
}
|
|
169
171
|
|
|
170
|
-
|
|
171
|
-
const
|
|
172
|
+
/** run one processor and fold what it returns into the collector and the style object */
|
|
173
|
+
const runProcessor = (
|
|
172
174
|
props: unknown,
|
|
173
175
|
fn: NestedRuntimeStyleProcessor,
|
|
174
176
|
classes: ClassCollector,
|
|
@@ -186,7 +188,7 @@ const unwrapProps = (
|
|
|
186
188
|
}
|
|
187
189
|
if ("style" in result && result.style) {
|
|
188
190
|
for (const key in result.style) {
|
|
189
|
-
//
|
|
191
|
+
// both objects use StyleObject; typescript loses the key/value relation in this loop
|
|
190
192
|
style[key as keyof StyleObject] = result.style[key as keyof StyleObject] as any;
|
|
191
193
|
}
|
|
192
194
|
}
|
package/runtime/cssProp.ts
CHANGED
|
@@ -27,6 +27,7 @@ export const normalizeClass = (value: unknown): string => {
|
|
|
27
27
|
* ```tsx
|
|
28
28
|
* <div class={__yak_mergeClassNames("yX", active() && "active")} />
|
|
29
29
|
* ```
|
|
30
|
+
* combineProps in styled.ts uses it for two attrs layers' classes as well.
|
|
30
31
|
*/
|
|
31
32
|
export const mergeClasses = (yakClass: string, userClass: unknown): string | undefined => {
|
|
32
33
|
const user = normalizeClass(userClass);
|
|
@@ -1,54 +1,82 @@
|
|
|
1
|
+
import { isServer } from "@solidjs/web";
|
|
1
2
|
import { Classes } from "../cssLiteral.js";
|
|
2
|
-
import { RuntimeStyleProcessor } from "../publicStyledApi.js";
|
|
3
|
+
import { RuntimeStyleProcessor, StyleObject } from "../publicStyledApi.js";
|
|
4
|
+
import { normalizeClass } from "./mergeClasses.js";
|
|
5
|
+
|
|
6
|
+
type Source = Record<PropertyKey, unknown>;
|
|
7
|
+
|
|
8
|
+
/** the last source that carries the key */
|
|
9
|
+
const lastWith = (sources: Source[], key: string): Source | undefined => {
|
|
10
|
+
for (let index = sources.length - 1; index >= 0; index--) {
|
|
11
|
+
if (key in sources[index]) return sources[index];
|
|
12
|
+
}
|
|
13
|
+
return undefined;
|
|
14
|
+
};
|
|
3
15
|
|
|
4
16
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
17
|
+
* Merges the relevant props of a native element with a css prop. The
|
|
18
|
+
* compiler adds it for the `css` prop:
|
|
19
|
+
* ```tsx
|
|
20
|
+
* <button class="a" {...props} style={s} css={css`color: green;`} />
|
|
21
|
+
* ```
|
|
22
|
+
* compiles to
|
|
8
23
|
* ```tsx
|
|
9
|
-
* <
|
|
10
|
-
*
|
|
11
|
-
* css={css`
|
|
12
|
-
* color: green;
|
|
13
|
-
* `}
|
|
14
|
-
* {...{ style: { padding: "30px" }}}
|
|
15
|
-
* />
|
|
24
|
+
* <button {...__yak_mergeCssProp(css("yak1"), { class: "a" }, props, { style: s })} />
|
|
25
|
+
* ```
|
|
16
26
|
*/
|
|
17
27
|
export const mergeCssProp = (
|
|
18
|
-
relevantProps: {
|
|
19
|
-
class?: string;
|
|
20
|
-
style?: Record<string, string>;
|
|
21
|
-
} & Record<string, unknown>,
|
|
22
28
|
cssProp: RuntimeStyleProcessor<unknown> | false | null | undefined,
|
|
29
|
+
...sources: (Source | null | undefined)[]
|
|
23
30
|
) => {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
const present = sources.filter((source): source is Source => source != null);
|
|
32
|
+
const out: Source = {};
|
|
33
|
+
for (const source of present) {
|
|
34
|
+
for (const key of Reflect.ownKeys(source)) {
|
|
35
|
+
// contributed below, from the last source that carries them
|
|
36
|
+
if (key === "class" || key === "style") continue;
|
|
37
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(source, key)!;
|
|
38
|
+
if (descriptor.get || descriptor.set || !descriptor.enumerable) {
|
|
39
|
+
Object.defineProperty(out, key, descriptor);
|
|
40
|
+
} else {
|
|
41
|
+
out[key] = descriptor.value;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
32
44
|
}
|
|
45
|
+
// `in` finds the owner without invoking its getter
|
|
46
|
+
const classOwner = lastWith(present, "class");
|
|
47
|
+
const styleOwner = lastWith(present, "style");
|
|
33
48
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
49
|
+
const mergedClass = (): string | undefined => {
|
|
50
|
+
const classes = new Classes(normalizeClass(classOwner?.class));
|
|
51
|
+
// a falsy css prop applies no styles, e.g. `css={on && css`...`}` with `on` false
|
|
52
|
+
if (cssProp) cssProp({}, classes, {} as StyleObject);
|
|
53
|
+
return classes.value || undefined;
|
|
54
|
+
};
|
|
55
|
+
const mergedStyle = (): StyleObject | undefined => {
|
|
56
|
+
const base = styleOwner?.style as StyleObject | undefined;
|
|
57
|
+
const style: StyleObject = base ? { ...base } : {};
|
|
58
|
+
if (cssProp) cssProp({}, new Classes(), style);
|
|
59
|
+
for (const _ in style) return style;
|
|
60
|
+
return undefined;
|
|
61
|
+
};
|
|
41
62
|
|
|
42
|
-
if (
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
63
|
+
if (isServer) {
|
|
64
|
+
// The server reads once, so the values are resolved here; a class or
|
|
65
|
+
// style read takes no hydration id. A key left undefined would make
|
|
66
|
+
// `ssrElement` write `class=""`, so only carried values are set.
|
|
67
|
+
const className = mergedClass();
|
|
68
|
+
if (className !== undefined) out.class = className;
|
|
69
|
+
const style = mergedStyle();
|
|
70
|
+
if (style !== undefined) out.style = style;
|
|
71
|
+
return out;
|
|
46
72
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
73
|
+
// on the client the spread reads these inside its effect, so a signal read
|
|
74
|
+
// by a source or by the css prop updates the attribute in place
|
|
75
|
+
if (cssProp || classOwner) {
|
|
76
|
+
Object.defineProperty(out, "class", { get: mergedClass, enumerable: true, configurable: true });
|
|
51
77
|
}
|
|
52
|
-
|
|
53
|
-
|
|
78
|
+
if (cssProp || styleOwner) {
|
|
79
|
+
Object.defineProperty(out, "style", { get: mergedStyle, enumerable: true, configurable: true });
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
54
82
|
};
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
export type { ComponentStyles, CSSInterpolation } from "../cssLiteral.js";
|
|
1
|
+
import type { CSSFunction, NestedRuntimeStyleProcessor } from "../cssLiteral.js";
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
4
|
* Allows to use CSS styles in a styled or css block
|
|
@@ -14,7 +12,7 @@ export type { ComponentStyles, CSSInterpolation } from "../cssLiteral.js";
|
|
|
14
12
|
* `;
|
|
15
13
|
* ```
|
|
16
14
|
*/
|
|
17
|
-
export const css:
|
|
15
|
+
export const css: CSSFunction = (styles: TemplateStringsArray, ...args: unknown[]) => {
|
|
18
16
|
// When called in yak files as a template tag (without SWC transformation),
|
|
19
17
|
// return { __yak: rawCss } so the cross-file resolver can
|
|
20
18
|
// extract the mixin value from evaluated .yak files.
|
package/runtime/mocks/styled.ts
CHANGED
|
@@ -2,7 +2,11 @@ import type { JSX } from "@solidjs/web";
|
|
|
2
2
|
import { styled as StyledFactory } from "../styled.js";
|
|
3
3
|
|
|
4
4
|
export const styled = /* @__PURE__ */ new Proxy(StyledFactory, {
|
|
5
|
-
get(target,
|
|
6
|
-
|
|
5
|
+
get(target, key) {
|
|
6
|
+
// only a tag name becomes a factory. symbols, the function's own
|
|
7
|
+
// properties and `then` pass through, so a promise that resolves the
|
|
8
|
+
// factory does not take it for a thenable
|
|
9
|
+
if (typeof key !== "string" || key === "then" || key in target) return Reflect.get(target, key);
|
|
10
|
+
return target(key as keyof JSX.IntrinsicElements);
|
|
7
11
|
},
|
|
8
12
|
}) as typeof StyledFactory;
|
|
@@ -43,7 +43,7 @@ export interface StyledFn {
|
|
|
43
43
|
*/
|
|
44
44
|
export interface YakComponent<T> extends AnyComponent<T> {
|
|
45
45
|
// This is intentionally typed to hide the internal implementation details.
|
|
46
|
-
[yakComponentSymbol]: [unknown, unknown, unknown
|
|
46
|
+
[yakComponentSymbol]: readonly [unknown, unknown, unknown];
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/**
|
|
@@ -143,14 +143,14 @@ export type FastOmit<T extends object, U extends string | number | symbol> = {
|
|
|
143
143
|
};
|
|
144
144
|
|
|
145
145
|
/**
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
* a real Set<string> also satisfies this interface.
|
|
146
|
+
* Collects the class names of one render. The runtime keeps them in one
|
|
147
|
+
* space-separated string (Classes in cssLiteral.ts, the only implementation):
|
|
148
|
+
* add() appends, has() and delete() let a runtime processor take a name back
|
|
149
|
+
* out. A Set would need a split and a join on every render for the same string.
|
|
151
150
|
*/
|
|
152
151
|
export type ClassCollector = {
|
|
153
|
-
|
|
152
|
+
/** generated is false for an author string such as an atom; the server writer escapes those */
|
|
153
|
+
add(name: string, generated?: boolean): void;
|
|
154
154
|
has(name: string): boolean;
|
|
155
155
|
delete(name: string): void;
|
|
156
156
|
};
|
|
@@ -168,6 +168,18 @@ export type RuntimeStyleProcessor<T> = ((
|
|
|
168
168
|
style: StyleObject,
|
|
169
169
|
) => void) & { $dynamic?: boolean };
|
|
170
170
|
|
|
171
|
+
/** A class-only processor ignores props and needs no style object. */
|
|
172
|
+
export type StaticStyleProcessor = ((
|
|
173
|
+
props: unknown,
|
|
174
|
+
classes: ClassCollector,
|
|
175
|
+
style?: StyleObject,
|
|
176
|
+
) => void) & { $dynamic: false };
|
|
177
|
+
|
|
178
|
+
/** css() marks its output so styled() can select the static render path. */
|
|
179
|
+
export type CompiledStyleProcessor<T> =
|
|
180
|
+
| StaticStyleProcessor
|
|
181
|
+
| (RuntimeStyleProcessor<T> & { $dynamic: true });
|
|
182
|
+
|
|
171
183
|
/**
|
|
172
184
|
* Utility type to keep the generic API of a component while still being able to use it in a selector
|
|
173
185
|
*/
|