Skip to content

Support JSX fragments with jsxFragmentFactory compiler option and @jsxFrag pragma - #38720

Merged
Wesley Wigham (weswigham) merged 1 commit into
microsoft:masterfrom
nojvek:nojvek-jsx-fragment-factory
Jun 18, 2020
Merged

Support JSX fragments with jsxFragmentFactory compiler option and @jsxFrag pragma#38720
Wesley Wigham (weswigham) merged 1 commit into
microsoft:masterfrom
nojvek:nojvek-jsx-fragment-factory

Conversation

@nojvek

@nojvek Noj Vek (nojvek) commented May 21, 2020

Copy link
Copy Markdown
Contributor

Reviving: #35392 (which was closed due to inactivity)

Checklist

Problem:

Currently <><Foo /></> only works with "jsx": "react". Using an inline pragma for jsxFactory /** @jsx dom */ or config defined "jsxFactory": "h" throws an error that JSX fragment is not supported when using --jsxFactory

The issue has been open for almost 3 years now.

Proposal Fix:

Very much inspired from babel to keep ecosystem consistent.

https://babeljs.io/docs/en/babel-plugin-transform-react-jsx

  1. jsxFragmentFactory compiler option.

As suggested and reviewed by Wesley Wigham (@weswigham) in previous PR.

Babel plugin-transform-react-jsx supports following options

{
  "plugins": [
    ["@babel/plugin-transform-react-jsx", {
      "pragma": "Preact.h", // default pragma is React.createElement
      "pragmaFrag": "Preact.Fragment", // default is React.Fragment
      "throwIfNamespace": false // defaults to true
    }]
  ]
}

Typescript already supports jsxFactory compiler option, this PR adds another optional jsxFragmentFactory compiler option for similar developer UX.

{
  "compilerOptions": {
    "target": "esnext", 
    "module": "commonjs",
    "jsx": "react",
    "jsxFactory": "h",
    "jsxFragmentFactory": "Fragment"
  }
}
  1. support for @jsxFrag pragma. This code would work in both typescript and babel without changes. TS will need jsx: react for emit though.
/** @jsx Preact.h */
/** @jsxFrag Preact.Fragment */

import Preact from 'preact';

var descriptions = items.map(item => (
  <>
    <dt>{item.name}</dt>
    <dd>{item.value}</dd>
  </>
));

@nojvek
Noj Vek (nojvek) force-pushed the nojvek-jsx-fragment-factory branch from 4010afc to 975e7b8 Compare May 22, 2020 00:38
@nojvek

Copy link
Copy Markdown
Contributor Author

Wesley Wigham (@weswigham) - would appreciate a review when you get a chance.

Thanks!

@nojvek

Copy link
Copy Markdown
Contributor Author

Wesley Wigham (@weswigham) I added a type elision test like you mentioned. Did a bit of debugging but still have little idea how to make the emmiter emit jsxFrag import so we don't end up with this, where snabbdom doesn't have a corresponding require

//// [mix-n-match.js]
"use strict";
exports.__esModule = true;
/* @jsx h */
/* @jsxFrag Frag */
var preact_1 = require("./preact");
preact_1.h(snabbdom_1.Frag, null,
    preact_1.h("span", null));

@weswigham

Copy link
Copy Markdown
Member

You'll need to mark it as referenced when a fragment that uses it is check'ed in the checker - checkJsxOpeningLikeElementOrOpeningFragment does this. It needs to swap the name it resolves and marks when it's looking at a fragment.

@nojvek
Noj Vek (nojvek) force-pushed the nojvek-jsx-fragment-factory branch from 30a8f17 to df5a194 Compare May 22, 2020 15:48
@nojvek

Copy link
Copy Markdown
Contributor Author

Sweet! I have the type elision scenario passing as a test.

Wesley Wigham (@weswigham), ready for a review 👀 when you are.

@nojvek

Copy link
Copy Markdown
Contributor Author

TypeScript Bot (@typescript-bot) pack this

@nojvek

Copy link
Copy Markdown
Contributor Author

I guess TypeScript Bot (@typescript-bot) only listens to typescript team 🤷‍♂️

@weswigham

Copy link
Copy Markdown
Member

TypeScript Bot (@typescript-bot) pack this :P

@typescript-bot

TypeScript Bot (typescript-bot) commented May 23, 2020

Copy link
Copy Markdown
Contributor

Heya Wesley Wigham (@weswigham), I've started to run the tarball bundle task on this PR at df5a194. You can monitor the build here.

@nojvek

Copy link
Copy Markdown
Contributor Author

Please tell me that when a TS team member types @typescript-bot sudo make me a 🥪, something really cool happens. If not, you need to seriously evaluate priorities in the roadmap. Ha!

@typescript-bot

TypeScript Bot (typescript-bot) commented May 23, 2020

Copy link
Copy Markdown
Contributor

Hey Wesley Wigham (@weswigham), I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json like so:

{
    "devDependencies": {
        "typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/74777/artifacts?artifactName=tgz&fileId=4E617F10A295CC8B5DD8371C8F44C745356B0A4C8893BB6AD7964773498EA80F02&fileName=/typescript-4.0.0-insiders.20200523.tgz"
    }
}

and then running npm install.


There is also a playground for this build.

@nojvek

Copy link
Copy Markdown
Contributor Author

Wesley Wigham (@weswigham) do you have any ETA when you'll review this or whether it's likely to land in next TS version ?

Comment thread src/compiler/checker.ts Outdated

@weswigham Wesley Wigham (weswigham) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this looks pretty good now (small note above). I'd like to get another pair of eyes, just to be prudent, and confirm this is OK to merge with Daniel Rosenwasser (@DanielRosenwasser).

@nojvek

Copy link
Copy Markdown
Contributor Author

Kind ping Daniel Rosenwasser (@DanielRosenwasser) for 👀

@nojvek

Copy link
Copy Markdown
Contributor Author

Kind ping again Daniel Rosenwasser (@DanielRosenwasser) for review 👀 ^

@nojvek

Copy link
Copy Markdown
Contributor Author

Another kind ping Daniel Rosenwasser (@DanielRosenwasser) ^

Wesley Wigham (@weswigham) is there anyone else in the TS team that can review this for merge? I have a feeling that this could be a PR that sits for months and eventually ends up being closed. This is my 3rd try trying to make a PR for jsxFragments over the last 2 years.

Please please please don't leave this hanging in limbo.

@DanielRosenwasser

Copy link
Copy Markdown
Member

Hey, I checked in with a couple of other people on the team. I'm going to raise it for the design meeting agenda tomorrow, but if there's nothing else controversial I think we can get it in by next week for the release.

@nojvek

Copy link
Copy Markdown
Contributor Author

You are the best Daniel Rosenwasser (@DanielRosenwasser). Thank you 🙏

@weswigham

Copy link
Copy Markdown
Member

Andrew Branch (@andrewbranch) you wanna give this a second review and help get this merged?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved, with grammar note. Thanks Noj Vek (@nojvek)!

"code": 17016
},
"JSX fragment is not supported when using an inline JSX factory pragma": {
"An @jsxFrag pragma is required when using an @jsx pragma with JSX fragments.": {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully this doesn’t turn into the GIF pronunciation debate, but I don’t pronounce the @ character so I think these articles should be “a,” not “an” 🙃

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty glad we moved away from "working @microsoft" to "working at @microsoft"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... I pronounce the @. 😆

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When Wesley Wigham (@weswigham) first mentioned this as PR comment, I didn't fully agree with him, but when I read it over many times I was internally conflicted which way is the right way. In the end I chose to appease Wesley Wigham (@weswigham) 🤷

Comment thread src/compiler/checker.ts
const jsxFactoryNamespace = getJsxNamespace(node);
const jsxFactoryLocation = isNodeOpeningLikeElement ? (<JsxOpeningLikeElement>node).tagName : node;

// allow null as jsxFragmentFactory

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious, what’s the utility of this? I couldn’t find anything about using null in the babel transform docs.

@weswigham Wesley Wigham (weswigham) Jun 15, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the idea is that some libraries may use a null tag name to indicate a fragment, rather than some specific fragment object. Mithril actually uses '[' as the fragment sentinel nowadays, so we should actually consider supporting arbitrary simple expressions here...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

snabbdom-jsx-lite uses null. https://github.com/nojvek/snabbdom-jsx-lite/blob/master/tsconfig.json#L8

Mostly because if

is compiled to h('div', null). null meaning no attrs,

<> compiles to h(null, null) meaning it's no named tag i.e Fragment. This avoids an extra function call for evaluation of fragment function, which in most cases convert the children to an array.

@weswigham

Copy link
Copy Markdown
Member

Noj Vek (@nojvek) would you be able to do a quick resync with master?

@nojvek
Noj Vek (nojvek) force-pushed the nojvek-jsx-fragment-factory branch from 6a7a8c7 to 2d7372a Compare June 16, 2020 02:08
@nojvek
Noj Vek (nojvek) force-pushed the nojvek-jsx-fragment-factory branch from 2d7372a to b950086 Compare June 18, 2020 02:07
@nojvek

Copy link
Copy Markdown
Contributor Author

FYI Wesley Wigham (@weswigham), merged with latest master and ensured tests pass locally.

@weswigham
Wesley Wigham (weswigham) merged commit f697d26 into microsoft:master Jun 18, 2020
@nojvek
Noj Vek (nojvek) deleted the nojvek-jsx-fragment-factory branch June 18, 2020 14:42
@microsoft Microsoft (microsoft) locked as resolved and limited conversation to collaborators Oct 21, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support JSX-Fragments with custom jsxFactory

5 participants