Skip to content

Commit c8bb2b7

Browse files
committed
Feat: styled - Support styled.div like shorthand API
1 parent 4a185b4 commit c8bb2b7

File tree

5 files changed

+294
-45
lines changed

5 files changed

+294
-45
lines changed

.changeset/stupid-hairs-behave.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@mincho-js/react": minor
3+
---
4+
5+
**styled**
6+
- Add `styled.div` like shorthand API

examples/react-babel/src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { styled } from "@mincho-js/react";
22

3-
const BaseComponent = styled("div", {
3+
const BaseComponent = styled.div({
44
base: {
55
fontWeight: "bold",
66
},

packages/babel/src/styled.ts

Lines changed: 87 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { types as t, PluginObj } from "@babel/core";
2-
import type { PluginState, ProgramScope } from "@/types.js";
3-
import { registerImportMethod } from "@/utils.js";
1+
import { types as t } from "@babel/core";
2+
import type { NodePath, PluginObj } from "@babel/core";
3+
import type { PluginState, ProgramScope } from "./types.js";
4+
import { registerImportMethod } from "./utils.js";
45

56
/**
67
* The plugin for transforming styled components
@@ -25,27 +26,15 @@ export function styledComponentPlugin(): PluginObj<PluginState> {
2526

2627
path.traverse({
2728
CallExpression(callPath) {
28-
const callee = callPath.get("callee");
29+
const normalizedArguments = normalizeStyledCall(callPath);
2930

30-
const isReactAdapter = callee.referencesImport(
31-
"@mincho-js/react",
32-
"styled"
33-
);
34-
35-
if (!isReactAdapter) {
31+
if (!normalizedArguments) {
3632
return;
3733
}
3834

39-
const runtimeImport = "@mincho-js/react/runtime";
40-
41-
const args = callPath.node.arguments;
42-
43-
// Validate arguments
44-
if (args.length < 2) {
45-
return;
46-
}
35+
const { tag, styles, rest } = normalizedArguments;
4736

48-
const [tag, styles, ...restArgs] = args;
37+
const runtimeImport = "@mincho-js/react/runtime";
4938

5039
const styledIdentifier = registerImportMethod(
5140
callPath,
@@ -79,7 +68,7 @@ export function styledComponentPlugin(): PluginObj<PluginState> {
7968
const callExpression = t.callExpression(styledIdentifier, [
8069
t.cloneNode(tag),
8170
recipeCallExpression,
82-
...restArgs
71+
...rest.map((argument) => t.cloneNode(argument))
8372
]);
8473

8574
// Add @__PURE__ annotation for tree-shaking
@@ -101,3 +90,81 @@ export function styledComponentPlugin(): PluginObj<PluginState> {
10190
}
10291
};
10392
}
93+
94+
function normalizeStyledCall(callPath: NodePath<t.CallExpression>): {
95+
tag: t.Expression;
96+
styles: t.Expression;
97+
rest: Array<t.Expression | t.SpreadElement>;
98+
} | null {
99+
const callee = callPath.get("callee");
100+
const args = callPath.node.arguments;
101+
102+
const toValidRestArguments = (
103+
input: typeof args
104+
): Array<t.Expression | t.SpreadElement> =>
105+
input.filter(
106+
(arg): arg is t.Expression | t.SpreadElement =>
107+
t.isExpression(arg) || t.isSpreadElement(arg)
108+
);
109+
110+
if (callee.isIdentifier()) {
111+
if (!callee.referencesImport("@mincho-js/react", "styled")) {
112+
return null;
113+
}
114+
115+
const [tag, styles, ...restArgs] = args;
116+
117+
if (!tag || !styles || !t.isExpression(tag) || !t.isExpression(styles)) {
118+
return null;
119+
}
120+
121+
return {
122+
tag,
123+
styles,
124+
rest: toValidRestArguments(restArgs)
125+
};
126+
}
127+
128+
if (!callee.isMemberExpression()) {
129+
return null;
130+
}
131+
132+
const object = callee.get("object");
133+
134+
if (
135+
!object.isIdentifier() ||
136+
!object.referencesImport("@mincho-js/react", "styled")
137+
) {
138+
return null;
139+
}
140+
141+
const [styles, ...restArgs] = args;
142+
143+
if (!styles || !t.isExpression(styles)) {
144+
return null;
145+
}
146+
147+
const property = callee.get("property");
148+
149+
let memberTag: t.Expression | null = null;
150+
151+
if (callee.node.computed) {
152+
if (t.isExpression(property.node)) {
153+
memberTag = property.node;
154+
}
155+
} else if (property.isIdentifier()) {
156+
memberTag = t.stringLiteral(property.node.name);
157+
} else if (property.isStringLiteral()) {
158+
memberTag = t.stringLiteral(property.node.value);
159+
}
160+
161+
if (!memberTag) {
162+
return null;
163+
}
164+
165+
return {
166+
tag: memberTag,
167+
styles,
168+
rest: toValidRestArguments(restArgs)
169+
};
170+
}

packages/react/src/index.ts

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {
1+
import type {
22
ComplexPropDefinitions,
33
ConditionalVariants,
44
PatternOptions,
@@ -7,9 +7,10 @@ import {
77
VariantGroups,
88
VariantObjectSelection,
99
PropDefinitionOutput,
10-
ResolveComplex
10+
ResolveComplex,
11+
CSSRule
1112
} from "@mincho-js/css";
12-
import {
13+
import type {
1314
ComponentProps,
1415
ComponentType,
1516
ElementType,
@@ -19,12 +20,12 @@ import {
1920
RefAttributes
2021
} from "react";
2122

22-
export { $$styled } from "./runtime.js";
23+
import { tags, SupportedElements } from "./tags.js";
2324

24-
type KeyofIntrinsicElements = keyof JSX.IntrinsicElements;
25+
export { $$styled } from "./runtime.js";
2526

2627
// == Main =====================================================================
27-
export function styled<
28+
function styledImpl<
2829
Props,
2930
Component extends ForwardRefExoticComponent<Props>,
3031
RulesVariants extends VariantGroups | undefined = undefined,
@@ -41,7 +42,7 @@ export function styled<
4142
RulesProps
4243
>;
4344

44-
export function styled<
45+
function styledImpl<
4546
Props,
4647
Component extends ComponentType<Props>,
4748
RulesVariants extends VariantGroups | undefined = undefined,
@@ -58,7 +59,7 @@ export function styled<
5859
RulesProps
5960
>;
6061

61-
export function styled<
62+
function styledImpl<
6263
Props extends object,
6364
Component extends ElementType,
6465
RulesVariants extends VariantGroups | undefined = undefined,
@@ -75,8 +76,8 @@ export function styled<
7576
RulesProps
7677
>;
7778

78-
export function styled<
79-
Component extends KeyofIntrinsicElements,
79+
function styledImpl<
80+
Component extends SupportedElements,
8081
RulesVariants extends VariantGroups | undefined = undefined,
8182
RulesToggleVariants extends VariantDefinitions | undefined = undefined,
8283
RulesProps extends ComplexPropDefinitions<PropTarget> | undefined = undefined
@@ -91,11 +92,11 @@ export function styled<
9192
RulesProps
9293
>;
9394

94-
export function styled<
95+
function styledImpl<
9596
Component extends
9697
| ForwardRefExoticComponent<unknown>
9798
| ComponentType<unknown>
98-
| KeyofIntrinsicElements
99+
| SupportedElements
99100
| ComponentWithAs<object, ElementType>,
100101
RulesVariants extends VariantGroups | undefined = undefined,
101102
RulesToggleVariants extends VariantDefinitions | undefined = undefined,
@@ -115,23 +116,50 @@ export function styled<
115116
);
116117
}
117118

118-
type IntrinsicProps<TComponent> = TComponent extends KeyofIntrinsicElements
119+
type TaggedStyled = {
120+
[Tag in SupportedElements]: <
121+
RulesVariants extends VariantGroups | undefined = undefined,
122+
RulesToggleVariants extends VariantDefinitions | undefined = undefined,
123+
RulesProps extends
124+
| ComplexPropDefinitions<PropTarget>
125+
| undefined = undefined
126+
>(
127+
options: PatternOptions<RulesVariants, RulesToggleVariants, RulesProps>
128+
) => StyledComponent<
129+
IntrinsicProps<Tag>,
130+
Tag,
131+
RulesVariants,
132+
RulesToggleVariants,
133+
RulesProps
134+
>;
135+
};
136+
137+
/** NOTE: Make simple type for avoid too complex error
138+
* error TS2590: Expression produces a union type that is too complex to represent.
139+
*/
140+
const taggedStyled: Record<string, unknown> = {};
141+
tags.forEach((tag) => {
142+
taggedStyled[tag] = (options: CSSRule) => styledImpl(tag, options);
143+
});
144+
145+
export const styled = Object.assign(styledImpl, taggedStyled as TaggedStyled);
146+
147+
type IntrinsicProps<TComponent> = TComponent extends SupportedElements
119148
? JSX.IntrinsicElements[TComponent]
120149
: never;
121150

122-
type InferStyledComponentProps<Component> =
123-
Component extends KeyofIntrinsicElements
124-
? IntrinsicProps<Component>
125-
: Component extends ComponentType<infer ComponentTypeProps>
126-
? ComponentTypeProps
127-
: Component extends ForwardRefExoticComponent<infer ForwardRefProps>
128-
? ForwardRefProps
129-
: Component extends ComponentWithAs<infer WithAsProps, ElementType>
130-
? WithAsProps
131-
: never;
151+
type InferStyledComponentProps<Component> = Component extends SupportedElements
152+
? IntrinsicProps<Component>
153+
: Component extends ComponentType<infer ComponentTypeProps>
154+
? ComponentTypeProps
155+
: Component extends ForwardRefExoticComponent<infer ForwardRefProps>
156+
? ForwardRefProps
157+
: Component extends ComponentWithAs<infer WithAsProps, ElementType>
158+
? WithAsProps
159+
: never;
132160

133161
type InferStyledComponentElement<Component> =
134-
Component extends KeyofIntrinsicElements
162+
Component extends SupportedElements
135163
? Component
136164
: Component extends ElementType
137165
? Component

0 commit comments

Comments
 (0)