Skip to content

feat(jsx): add jsx generator #273

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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open

feat(jsx): add jsx generator #273

wants to merge 5 commits into from

Conversation

avivkeller
Copy link
Member

@avivkeller avivkeller commented May 17, 2025

This PR introduces a jsx generator that transforms Markdown AST and metadata entries into a JSX (recma) AST (with a root of Program), which can then be compiled by the react-web generator from #7.

The JSX generator produces AST that assumes the following components have been implemented:

  • CodeBox: Implementation matches nodejs.org CodeBox
  • CodeTabs: Implementation matches nodejs.org CodeTabs
  • Blockquote: Import from @node-core/ui-components/Common/Blockquote
  • AlertBox: Import from @node-core/ui-components/Common/AlertBox
  • CircularIcon: Accepts:
    • symbol: Icon emblem (e.g., "C" or "E")
    • color: Icon color (e.g., "red" or "#FFFFF")
    Design
  • ChangeHistory: Similar to @node-core/ui-components/Common/Select, accepts:
    • changes: Array of { versions: string[], label: string, url?: string } (where versions[] contains semver strings like v1.2.3)
    Design
  • NavBar: Wrapper of @node-core/ui-components/Containers/NavBar, accepts no props
  • Footer: Wrapper of @node-core/ui-components/Containers/Footer, accepts no props
  • Article: Import from @node-core/ui-components/Containers/Article
  • SideBar: Similar to @node-core/ui-components/Container/SideBar, accepts:
    • versions: Array of versions (e.g., v20.0.0) where the document exists
    • currentVersion: Current documentation version (e.g., v20.1.0)
    • currentPage: Current page (e.g., async_context.html) for highlighting in the sidebar
    • docPages: Array of [{ title: string, headings: [title, hash][], doc: string }] pairs (e.g., { title: "Globals", headers: [["someGlobal", "#some-global"]], doc: "globals.html"})
    Design
  • MetaBar: Wrapper of @node-core/ui-components/Containers/MetaBar, accepts:
    • headings: Array of { depth: number, value: string } for table of contents
    • addedIn: Version when doc/module was added (e.g., v0.10.0)
    • readingTime: String like "9 min read"
    • viewAs: Array of ["Type", "URL"] pairs (currently only supports ["JSON", "globals.json"])
    • editThisPage: GitHub URL for editing the page

As a reminder, the Figma design is:
image

Copy link
Member

@AugustinMauroy AugustinMauroy left a comment

Choose a reason for hiding this comment

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

Interesting basic, don't miss to add some test

@avivkeller
Copy link
Member Author

I'm working on skipping the MDX and going to straight to JSX, so I'll add lots of tests when that's started

@avivkeller avivkeller changed the title [WIP] mdx generator [WIP] jsx generator May 18, 2025
@avivkeller avivkeller force-pushed the wip/mdx-generator branch 2 times, most recently from 60922b4 to d2bd5f4 Compare May 18, 2025 23:42
@avivkeller
Copy link
Member Author

avivkeller commented May 19, 2025

This uses pnpm for now, see #236 for reference. If we do adopt pnpm, it would be a seperate PR, this is just for development.

@avivkeller avivkeller linked an issue May 20, 2025 that may be closed by this pull request
@avivkeller avivkeller changed the title [WIP] jsx generator feat(jsx): add jsx generator May 21, 2025
@avivkeller avivkeller marked this pull request as ready for review May 22, 2025 00:24
@avivkeller avivkeller force-pushed the wip/mdx-generator branch from 8020c31 to 60e76e5 Compare May 22, 2025 00:24
@avivkeller avivkeller requested a review from a team as a code owner May 22, 2025 00:24
@@ -15,7 +15,8 @@
"test:watch": "node --test --watch",
"prepare": "husky",
"run": "node bin/cli.mjs",
"watch": "node --watch bin/cli.mjs"
"watch": "node --watch bin/cli.mjs",
"postinstall": "git-deps install"
Copy link
Member

Choose a reason for hiding this comment

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

If this is the intended behavior and the solution, we should move this dependency to node.js organization, before merging this pull-request. It will run on every postinstall, and it's huge security concern.

Copy link
Member Author

Choose a reason for hiding this comment

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

We are still looking for solutions, see #236

Copy link
Member

Choose a reason for hiding this comment

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

Agreed with @anonrig although I'd say this is a draft, but we def... need to find a perma solution.

Copy link
Member

Choose a reason for hiding this comment

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

We should at the very least move this dep to @pkgjs. @avivkeller feel free to open an issue on /admin to transfer this + ownership.

Copy link
Member Author

Choose a reason for hiding this comment

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

Shouldn't we first decide whether this is a perma solution? I'd argue that we shouldn't maintain a package that we aren't going to use-but I'm happy to transfer it to the org regardless

Copy link
Member

Choose a reason for hiding this comment

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

Sure, but this PR cannot land till we decide that, or at least cannot land whilst git-deps is a dependency here 😅

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure, but this PR cannot land till we decide that

Per the vote in #236, it looks like nodejs/nodejs.org#7776 will resolve that, and once they are published, I'll switch it. git-deps allows me to test this PR until then.

@avivkeller avivkeller force-pushed the wip/mdx-generator branch from ef09a04 to a002cc3 Compare May 22, 2025 21:14
*
* @param {string} name - The name of the JSX element
* @param {{
* inline?: boolean,
Copy link
Member

Choose a reason for hiding this comment

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

Lines 11 to 13 should be indented

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

Right, but I believe extra level, makes it easier to read:

/**
 * Creates an MDX JSX element with support for complex attribute values.
 *
 * @param {string} name - The name of the JSX element
 * @param {{
 *   inline?: boolean,
 *   children?: string | import('unist').Node[],
 *   [key: string]: any
 * }} [options={}] - Options including type, children, and JSX attributes
 * @returns {import('unist').Node} The created MDX JSX element node
 */

Copy link
Member Author

Choose a reason for hiding this comment

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

  1. The lint rule is enforced
  2. We don't indent anywhere else, i.e. in createMetadata or legacy-html

This can be changed, but it breaks our conventions

* @param {ApiDocMetadataEntry} head - Main API metadata entry
* @param {Array<ApiDocMetadataEntry>} entries - All API metadata entries
*/
export function buildMetaBarProps(head, entries) {
Copy link
Member

Choose a reason for hiding this comment

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

Maybe explain a bit more why we need to append all this text

Copy link
Member Author

Choose a reason for hiding this comment

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

Like what, does // Extract text content for reading time calculation not sum it up? readingTime expects text, not AST.

addedIn: head.introduced_in || head.added_in || '',
readingTime: readingTime(textContent).text,
viewAs: [['JSON', `${head.api}.json`]],
editThisPage: `${DOC_API_BLOB_EDIT_BASE_URL}${head.api}.md`,
Copy link
Member

Choose a reason for hiding this comment

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

Don't we have a metadata property that is exactly this already?

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think so, we use the same method of building it in legacy-html.

Copy link
Member

Choose a reason for hiding this comment

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

Might make sense to then make this as a property on the metadata itself

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think it's needed. It's really just {constant}{metadata.api}.md, so it can very easily be built, however, if you insist, maybe that can be a follow up.

Copy link
Member

@ovflowd ovflowd left a comment

Choose a reason for hiding this comment

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

Aren't we using arrow functions across the codebase, or are we only using regular functions?

*/
const createContentStructure = (entries, sideBarProps, metaBarProps) => {
return createTree('root', [
createJSXElement('NavBar'),
Copy link
Member

@ovflowd ovflowd May 24, 2025

Choose a reason for hiding this comment

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

Since all these are React Components, would make sense to reference these as constants such as:

import { NavBar } from '@node-core/ui....'

....

createJSXElement(NavBar.displayName), // or something similar

This ensures that we use their real names; And then wherever we expose them (be it next.mdx.mjs and next.client.mdx.mjs) we also use the same constants.

This ensures that this won't randomly break if we chan the declared Components within MDX or somewhere else. Apply this to all components you're invoking within this new generator.

Copy link
Member Author

Choose a reason for hiding this comment

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

Most of these components haven't been implemented yet, can this be done when they are implemented?

// TODO(@avivkeller): Our parsers shouldn't return raw nodes
// When they mistake "<Type>" for an HTML node, rather than
// the string type that it is.
if (node.type === 'raw') {
Copy link
Member

Choose a reason for hiding this comment

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

I'd rather have a switch statement here :P

Copy link
Member

Choose a reason for hiding this comment

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

Or simply have text within TYPE_TRANSFORMS

Copy link
Member Author

Choose a reason for hiding this comment

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

I'd rather have a switch statement here :P

There's only one clause, so I think we should leave it as is.

Or simply have text within TYPE_TRANSFORMS

The transforms we are apply are for .tagName, not .type

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add mdx-react generator
5 participants