-
Notifications
You must be signed in to change notification settings - Fork 70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow ax()
to handle non-string classnames when collected.
#1696
Conversation
The example being passing `<Component xcss={cx(…)} />` or `<Component xcss={[…]} />` results in `ax` capturing things like this: ```tsx [ '_1bah1yb4', undefined, ['_16jlkb7n', undefined], AtomicGroups { values: Map(2) { '_1bsb' => '_1bsb1osq', '_16jl' => '_16jlkb7n' } } ] ``` I don't know if it's the best way to handle this, but internally we've just been invoking `toString()` via a wrapper, and we're trying to get to native Compiled. Makes sense (and works) to me!
🦋 Changeset detectedLatest commit: 7835fd8 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
ax()
to handle non-string classnames when collected.ax()
to handle non-string classnames when collected.
✅ Deploy Preview for compiled-css-in-js canceled.
|
export default function ax(classNames: (string | undefined | null | false)[]): string | undefined { | ||
if (classNames.length <= 1 && (!classNames[0] || classNames[0].indexOf(' ') === -1)) { | ||
export default function ax( | ||
classNames: (string | undefined | null | false | AtomicGroups)[] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the introduction of cx
, I believe this is the proper type as this does happen (or cx
should change in a way I don't know how).
Realistically this could also be infinitely recursive, I don't think anything flattens here…but I won't solve a problem that doesn't really exist.
@@ -41,7 +48,16 @@ export default function ax(classNames: (string | undefined | null | false)[]): s | |||
continue; | |||
} | |||
|
|||
const groups = cls.split(' '); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if:
const groups = (cls + '').split(' ');
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah actually, doing const groups = String(cls).split(' ');
does work actually, just feels like it doesn't tell you what it's doing is all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd do whatever results in better performance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, not sure about the array syntax <Component xcss={[style, style]} />
, but whatever, that fails types anyways.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doing arbitrary online benchmarks which mean little, protecting via a typeof like:
if (typeof cls === 'string') {
return cls.split(' ');
} else {
return String(cls).split(' ');
}
is faster by ~4% than just blindly calling String(c1)
first (and if we add Array.isArray()
in there it's much faster). I gather String()
doesn't handle this (but could/should), it's an implementation detail that may not be consistent.
Worst-case, I'd expect it to be 1:1, I don't see how v8 could do anything super special here—basically the negative impact is just from doing typeof
checks, which are pretty minimal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sweet ok. I think we can lean on type assumptions and safely use the typeof check tbh.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could use help actually figuring out how to test this change as it's apparent the scenario doesn't work, or I'm doing something wrong. I'm not sure how our <Button xcss={cx(…)} />
tests are passing (I figured this wasn't tested, but it is? I expected those to error…)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least I tested this in ax.test.ts
now.
} else if (Array.isArray(cls)) { | ||
groups = cls; // NOTE: Not really a valid scenario, so we don't deeply recurse or handle this properly | ||
} else { | ||
// NOTE: Could throw an error here if this isn't our `AtomicGroups` to help understand that this is collecting invalid styles. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found doing a cls instanceof AtomicGroups
wasn't consistently working, might as well try to blanket support, though…it does mean previous things that would throw 'cls.split is not a function'
now more quietly fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that's fine. We can lean on the typesystem for everything else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
somewhat unrelated question, but do you guys use ac
and AtomicGroups
for anything? i thought that was part of the class name compression work that we've now shelved (probably safe to just yeet)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the root example comes from using cx()
which uses ac()
. Frankly, I do not know anything here, I'm just hacking it together and I think I understand what's going on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we used ac
based on @liamqma's guidance. If you think it's not useful we can switch it out for whatever.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, we've parked classname compression.
I suggest switching it out for the old good ax
. 🙂
No need forax()
to handle AtomicGroup. less runtime code 🙂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops i was going to write a big comment but it got deleted 😢
but yeah depends on whether you want to support nesting like cx([cx('_aaaa_a'), '_aaaa_b'])
... if not, i guess there's no value that an ac
-based approach would provide
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(and maybe delete the ac
/class name compression stuff altogether too, so we don't have unused functions in runtime? but that's probably more suited for a follow-up PR)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense to me, is it literally as simple as swapping out ac
for ax
? It kinda looks like it locally as I think I've got all my tests passing 🙈
-return ac(actualStyles);
+return ax(actualStyles);
If that's a drop-in, I can pop that out tomorrow, but I won't make the whole removal (bit over my head).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this works: #1697
ax()
to handle non-string classnames when collected.ax()
to handle non-string classnames when collected.
if (typeof cls === 'string') { | ||
groups = cls.split(' '); | ||
} else if (Array.isArray(cls)) { | ||
groups = cls; // NOTE: Not really a valid scenario, so we don't deeply recurse or handle this properly |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is an area in the codebase i haven't looked at in a while, so i'm not too familiar with the runtime stuff 😅
from the comment and the tests, i take it only APIs outside of Compiled (and never Compiled APIs) would ever create a situation where Array.isArray(cls)
is true right? (e.g. className = [['_abcd1234', '_efgh1234'], '_ijkl1234 mnop1234', ...]
)
i don't have a preference on whether to keep the else if (Array.isArray(cls)) { ... }
branch in or not, just aiming to understand the PR fully
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to drop the scenario, right now let's say if you do like this (passing props.xcss
around):
const Component = (props) => (
<div className={xcss} css={{ margin: 0 }} />
);
export default (props) => <Component xcss={[{ margin: '12px' }, props.xcss]} />;
You will get a type error because our xcss
implementation requires xcss={cx({ margin: '12px' }, props.xcss)}
and arrays won't work, but in theory I could see people copy + pasting css={[…]}
array into xcss={[…]}
and ignoring it and it throwing an error (because I kind of did that 🤣).
I don't mind much either way to be honest, it does have a type error.
I would prefer to either throw new Error(…)
in here or keep it, because if it does get passed, doing String(array).split(' ');
is quite unperformant (like 50–100% reduction in executions per minute if we attempt to stringify arrays, I wouldn't want that hitting production).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that makes sense, thank you for the additional context
Closing as I think #1697 is just a better approach. |
I want to allow our
ax()
collection to better handle arbitrary values. The example being passing using our pass-through styles withcssMap
like<Component xcss={cx(…)} />
or<Component xcss={[…]} />
results inax
capturing things like this:I don't know if it's the best way to handle this, but internally we've just been invoking
toString()
via a wrapper, and we're trying to get to native Compiled. Makes sense (and works) to me!PR checklist
I have...