Skip to content
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

EmitResolver cannot handle JsxOpeningLikeElement and JsxOpeningFragment that didn't originate from the parse tree #35686

Open
itsdouges opened this issue Dec 15, 2019 · 3 comments
Labels
Bug A bug in TypeScript Domain: Transforms Relates to the public transform API Help Wanted You can do this Rescheduled This issue was previously scheduled to an earlier milestone
Milestone

Comments

@itsdouges
Copy link

itsdouges commented Dec 15, 2019

TypeScript Version: 3.7.x-dev.201xxxxx

Search Terms:

Debug Failure. False expression., typescript transformer, jsx element

Code

  • clone https://github.com/madou/untitled-css-in-js-project
  • run git checkout b229dc749e4614bb8d9194c8de340a82f10c8f8a
  • run yarn
  • run yarn test
  • notice one test fails should not blow up when transforming with const
  • notice a similar test but with var instead of const passes

the node transformation is done here - if i return the original jsx element node then the test passes. but then that defeats the purpose of the transformer.. 😛

the code is essentially transforming

const Component = () => <div css={{ fontSize: '20px' }}>hello world</div>;

to

const Component = () => (
<>
  <style>{'.a { font-size: 20px; } '}</style>
  <div className="a">hello world</div>
</>
);

Expected behavior:

it works no error thrown

Actual behavior:

"Debug Failure. False expression." error thrown. also tried with nightly typescript version - same error.

Related Issues:

#24380

😄 would love to get this figured out! hoping it's just something i've done wrong. make from this twitter thread https://twitter.com/orta/status/1206139333448732672

@DanielRosenwasser DanielRosenwasser added Bug A bug in TypeScript Domain: Transforms Relates to the public transform API Help Wanted You can do this Good First Issue Well scoped, documented and has the green light labels Dec 16, 2019
@DanielRosenwasser DanielRosenwasser added this to the TypeScript 3.8.1 milestone Dec 16, 2019
@itsdouges
Copy link
Author

hi is @rbuckton going to be working on this? if not is there any pointers you can give me to fix this? 😄

@ajafff
Copy link
Contributor

ajafff commented Dec 30, 2019

TL;DR:

- ts.createJsxOpeningElement(ts.createIdentifier('style'), [], ts.createJsxAttributes([])), node,
+ ts.setOriginalNode(ts.createJsxOpeningElement(ts.createIdentifier('style'), [], ts.createJsxAttributes([])), node),

- ts.createJsxOpeningFragment(),
+ ts.setOriginalNode(ts.createJsxOpeningFragment(), node),

The issue title should rather be: "EmitResolver cannot handle JsxOpeningLikeElement and JsxOpeningFragment that didn't originate from the parse tree"


Explanation:

I looked into this. The es2015 transform tries to generate a unique name for block-scoped bindings if it has the same name as another binding in the same function scope. If there is any block scoped binding in the file, it tries to look up every Identifier to detect potential collisions. Since var is not block-scoped, this code path is not taken in your other test.
Finding out whether there's another binding uses resolveName which relies on Node#parent to walk up the AST. This is a really bad idea in transforms as transformed nodes don't have a parent. resolveNameHelper contains an assertion that expects the function to only exit at a SourceFile as that's typically the only Node without parent.
To prevent everything from blowing up, there's a check in getReferencedDeclarationWithCollidingName to return early if the current Identifier doesn't come from the parse tree (i.e. the original node is not synthesized) and therefore has no parent.

Unfortunately there's the JSX transform transforming your JSX Elements to JSX-factory calls. This code (createReactNamespace in factory.ts) does some trickery to make it look like real nodes from the parse tree. It unsets the Synthesized flag and sets the parent property to the original node of the current JsxOpeningLikeElement | JsxOpeningFragment.
But your JsxOpeningLikeElements and JsxOpeningFragments don't have an original node. Therefore parent is set to undefined which trips up resolveNameHelper as explained above.

@itsdouges
Copy link
Author

itsdouges commented Dec 30, 2019

hi @ajafff really appreciate you looking into this - thanks!

with this new knowledge for replacing nodes - how would you recommend replacing a node in a typescript transformer the proper way? (let's say I want to change a function declaration to an arrow function) is setOriginalNode needed to do that every time?

i've been writing up this handbook https://github.com/madou/typescript-transformer-handbook - would love to have all pro tips and how to's etc in it. if you have time to give it a proof read would be great 😄


fix applied, works great thanks mate

atlassian-labs/compiled@8e30b7c

@itsdouges itsdouges changed the title Transforming JSX Element node that returns a new node causes "Debug Failure. False expression." error EmitResolver cannot handle JsxOpeningLikeElement and JsxOpeningFragment that didn't originate from the parse tree Dec 30, 2019
@itsdouges itsdouges reopened this Dec 31, 2019
@RyanCavanaugh RyanCavanaugh added the Rescheduled This issue was previously scheduled to an earlier milestone label Aug 31, 2020
@andrewbranch andrewbranch removed this from the TypeScript 4.4.1 (RC) milestone Aug 31, 2021
@andrewbranch andrewbranch added this to the TypeScript 4.5.0 milestone Aug 31, 2021
@RyanCavanaugh RyanCavanaugh removed the Good First Issue Well scoped, documented and has the green light label Feb 4, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Domain: Transforms Relates to the public transform API Help Wanted You can do this Rescheduled This issue was previously scheduled to an earlier milestone
Projects
None yet
Development

No branches or pull requests

6 participants