-
Notifications
You must be signed in to change notification settings - Fork 364
[babel-plugin] Support nested objects in defineConsts
#1303
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
base: main
Are you sure you want to change the base?
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as resolved.
This comment was marked as resolved.
| const outerMost = getOuterMostMemberExpression(path); | ||
|
|
||
| // to handle nested member expressions, we wait until we are at the outer most member expression | ||
| // and then we can extract the full path and evaluate it via the object proxy | ||
| if (outerMost === path) { | ||
| const pathInfo = getFullMemberPath(path); | ||
|
|
||
| if (pathInfo != null && pathInfo.parts.length > 0) { | ||
| const baseObject = evaluateCached(pathInfo.baseObject, state); | ||
| if (!state.confident) { | ||
| return; | ||
| } | ||
|
|
||
| if (baseObject[PROXY_MARKER]) { | ||
| return baseObject[`__nested__${pathInfo.parts.join('.')}`]; | ||
| } | ||
| } | ||
| } |
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.
A previous iteration of this PR made a change directly to the proxy to "build up" nested accesses overtime, and then had this AST traversal extract out the value once we were at the terminal/outermost member access. This worked well for any code which accesses the proxy via this traversal, but broke for code that accessed it in other ways, specifically variables here:
stylex/packages/@stylexjs/babel-plugin/src/visitors/stylex-create-theme.js
Lines 154 to 162 in d8053a2
| if ( | |
| typeof variables.__varGroupHash__ !== 'string' || | |
| variables.__varGroupHash__ === '' | |
| ) { | |
| throw callExpressionPath.buildCodeFrameError( | |
| 'Can only override variables theme created with defineVars().', | |
| SyntaxError, | |
| ); | |
| } |
And themeVars[key] here
| const nameHash = themeVars[key].slice(6, -1); |
A simple way to fix this would've been to special case these accesses or to make them aware of the proxy. Neither of these seem like great solutions as they would leak the implementation details of this evaluate loop throughout the codebase.
My solution here is to have Babel to traverse the AST as normal, but once we are at the terminal/outermost access of a member expression, extract and join the full key path as a dotted string and pass that to the proxy. This keeps the proxy behaving as normal but means that dotted accesses work. I can add some benchmarks in another PR if there are concerns about build performance here.
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 added a quick benchmark in #1312 and updated the benchmark in this PR to use nested keys as well. This change seems to be insignificant on the "complex create" benchmark.
| throw new Error( | ||
| `Conflicting constant paths detected: "${fullPath}". This can happen when you have both nested properties (e.g., {a: {b: 'value'}}) and a literal dotted key (e.g., {'a.b': 'value'}) that resolve to the same path.`, | ||
| ); |
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 couldn't come up with a great way to get around conflicting keys like this and it feels like a pattern to avoid anyways.
5d0beb1 to
cb0520d
Compare
|
Is there anything that would be helpful to get this change moved along? This would be super valuable for us internally. |
|
I'm still going through the code changes, but I want to lay out my thinking here before leaving any code comments: Why I'm generally against thisMy primary opposition to this API change is that So, while, yes, it is possible to extend What I think we should do insteadOne API that we have been discussing for a few weeks is something called In many ways, Trade-offsI would argue that Since Another advantage of So with all of that said, I would like to understand if a If you have a strong argument for why it would be more helpful to extend |
|
I have a few thoughts on this.
If we want to try out this nested |
|
Thanks both for your thoughts! We chatted a bit elsewhere, but just to summarize:
It sounds like the best path forward will be to:
For now, I'll close this PR out. Thanks again! Footnotes
|
57e041a to
5ce36df
Compare
What changed / motivation ?
Our design system uses three-tiered tokens with component specific tokens like
$designSystem-component-button-primary-background-defaultand$designSystem-component-button-primary-background-hovered. This structure lends itself naturally to a nested theme object:We use
defineConststo implement this theme object (defineVars doesn't work for us, certain product requirements mean that we need the values of our theme object to be CSS variable references that are defined in an external file).Currently there is no good way to implement a structure like this with
defineConsts. Long camel case keys is an option, but has awkward Intellisense/auto-complete support. Splitting up the definitions into many top-leveldefineConstscalls (buttonColours,buttonBorderRadius, etc.) is the closest we can get, but is annoying in a few ways:(# of components) * (# of properties))This PR adds support for arbitrarily nested objects in
defineConsts. That is, the following is now valid StyleX:Additional Context
Fixture tests were added. I made a small change to the
defineConstsdocumentation to suggest that the objects can be nested. See comments for some notes on the code.Pre-flight checklist
Contribution Guidelines