Skip to content

feat(web): add web generator #285

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

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft

feat(web): add web generator #285

wants to merge 2 commits into from

Conversation

avivkeller
Copy link
Member

@avivkeller avivkeller commented May 28, 2025

Fixes #7.

This PR adds the web generator.

Tasks / Issues

P1 – Must Complete Before Merge

  • Add more items (anyone can do this as they review1)

P2 – Must complete before migration

P3 – Can Be Done in a Follow-up

  • Remove mustache dependency
  • Add more items (anyone can do this as they review1)

Get a preview

node bin/cli.mjs generate -t orama-db -i "/node/doc/api/*.md" -o "./out" # If you want search functionality
node bin/cli.mjs generate -t web -i "/node/doc/api/*.md" -o "./out" --index "/node/doc/api/index.md" # Specifying `--index` is optional, but recommended
npx serve out # You can serve the output, or just open one of the files in your browser. Serving is required for using search.

Footnotes

  1. Add things as they appear, or leave review comments. 2 3

@codecov-commenter
Copy link

codecov-commenter commented May 28, 2025

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
171 2 169 0
View the top 2 failed test(s) by shortest run time
test::should process entries and include JSX wrapper elements
Stack Traces | 0.00654s run time
Error [ERR_TEST_FAILURE]: Missing "NavBar". Found: 
    at new Promise (<anonymous>)
    at Array.map (<anonymous>) {
  code: 'ERR_TEST_FAILURE',
  failureType: 'testCodeFailure',
  cause: AssertionError [ERR_ASSERTION]: Missing "NavBar". Found: 
      at file:.../utils/__tests__/buildContent.test.mjs:31:7
      at Array.forEach (<anonymous>)
      at TestContext.<anonymous> (file:.../utils/__tests__/buildContent.test.mjs:30:19)
      at Test.runInAsyncScope (node:async_hooks:214:14)
      at Test.run (node:internal/test_runner/test:1047:25)
      at Test.start (node:internal/test_runner/test:944:17)
      at node:internal/test_runner/test:1440:71
      at node:internal/per_context/primordials:483:82
      at new Promise (<anonymous>)
      at new SafePromise (node:internal/per_context/primordials:451:29) {
    generatedMessage: false,
    code: 'ERR_ASSERTION',
    actual: false,
    expected: true,
    operator: '=='
  }
}
test::should return HTML and CSS
Stack Traces | 0.0346s run time
Error [ERR_TEST_FAILURE]: result.html.startsWith is not a function
    at async Promise.all (index 0) {
  code: 'ERR_TEST_FAILURE',
  failureType: 'testCodeFailure',
  cause: TypeError [Error]: result.html.startsWith is not a function
      at TestContext.<anonymous> (file:.../web/__tests__/index.test.mjs:43:27)
      at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
      at async Test.run (node:internal/test_runner/test:1054:7)
      at async Promise.all (index 0)
      at async Suite.run (node:internal/test_runner/test:1442:7)
      at async Test.processPendingSubtests (node:internal/test_runner/test:744:7)
}

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@avivkeller avivkeller marked this pull request as ready for review May 28, 2025 22:13
@avivkeller avivkeller requested a review from a team as a code owner May 28, 2025 22:13
@avivkeller avivkeller marked this pull request as draft May 29, 2025 16:36
const language = matches?.groups?.language ?? '';
const [copyText, setCopyText] = useState('Copy to clipboard');

const handleCopy = async text => {
Copy link
Member

Choose a reason for hiding this comment

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

Why not including useCopyToClipord in ui lib ?

Copy link
Member Author

Choose a reason for hiding this comment

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

IIRC I didn't include it since it didn't fall under the "UI Component" category, it's a hook, however, if you disagree, I won't block.

"estree-util-value-to-estree": "^3.4.0",
"estree-util-visit": "^2.0.0",
"github-slugger": "^2.0.0",
"glob": "^11.0.2",
"hast-util-to-string": "^3.0.1",
"hastscript": "^9.0.1",
"html-minifier-terser": "^7.2.0",
"mustache": "^4.2.0",
Copy link
Member

Choose a reason for hiding this comment

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

Hmm, why?

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 tried to use __<VARIABLE>__ like in the other generators, however, the JavaScript I was trying to add to the HTML broke the structure of the file. Using a library like Mustache properly escapes anything injected.

Copy link
Member

@AugustinMauroy AugustinMauroy May 29, 2025

Choose a reason for hiding this comment

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

export const TEMPLATE = ({...props}) => dedent`
<html>
 ${props.foo}
</html>
`; 

this can work

Copy link
Member Author

@avivkeller avivkeller May 30, 2025

Choose a reason for hiding this comment

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

Sorry if I wasn’t clear, the compiled JavaScript broke the HTML structure because it contained characters that need to be escaped to be in an HTML script.

For example, in your scenario, if props.foo contains a </html>, it’ll escape the sandbox.

Mustache performs all the needed escaping.

Copy link
Member

Choose a reason for hiding this comment

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

I don't like that we're using yet another layer of whatever freakery here. Mustache is great, but it feels like you're throing a nuke at an ant's sized problem. Not to mention adding yet another engine here will make things even more complex and costly-performance wise. Let's try to avoid throwing npm packages to any problem we encounter, shall we?

@avivkeller
Copy link
Member Author

avivkeller commented May 30, 2025

🎉 The code now dehydrates to the client so it can render without JavaScript!

@AugustinMauroy
Copy link
Member

🎉 The code now dehydrates to the client so it run without JavaScript!

Wow 😵‍💫 and what about codetab

@avivkeller
Copy link
Member Author

avivkeller commented May 30, 2025

It rehydrates and runs with JS, but if you don't have JS, you can still view the docs. I used React's SSRing

@avivkeller
Copy link
Member Author

@AugustinMauroy and I got search to finally work 🎉
image

@avivkeller avivkeller force-pushed the feat/web/gen branch 2 times, most recently from 024bbea to dbfe55d Compare June 3, 2025 21:40
@avivkeller
Copy link
Member Author

@nodejs/nodejs-website @nodejs/web-infra ChangeHistory and SideBar aren't implemented yet, so this is still a draft, but it's ready for you to take a look, so feel free to review :-)

@avivkeller avivkeller linked an issue Jun 6, 2025 that may be closed by this pull request
@avivkeller
Copy link
Member Author

Can we add syntax highlighting on these entries.

Fixed.

Can we update the table so that the <code> tags are not in bold
Is it just me or is the font size changing for code tags within the table? And yes, feels like the styling is a bit broken not gonna lie.

The <code /> tags use the same styling everywhere, it's inherited from @node-core/ui-components/styles/markdown.css.

I also feel the heading sections don't need to be a <code> tag anymore as they're just a method name.

Fixed.

Class Constructors should it be "new ClassName" or just "ClassName" Constructor?

I've changed it to ${name} Constructor

We should document what C, M, E (and the different versions of "C") mean somewhere. Either by a hover tooltip or "about this documentation"
Otherwise let's rename the Pills to be like "Class", "Nethod", "Event", "Class Constructor"

If we want to add them to "About this documentation", that's a nodejs/node issue. If we want to add a tooltip, we can PR nodejs/nodejs.org's DataTag. I'm also fine with the pill.

Ah and some stuff seems to not be using the new table yet

Fixed.

I feel we need to add "tags" labels or something; as it upsets me so much "write file" and none ofn the fs: write file methods are even showing up on these results.

I've updated the orama-db generator to also index the first paragraph of a given header.

It'd be great if we could also have a navbar 🤔 but maybe not (and not a blocker can be a folow-up)

We could add a static (backup) navbar, and then also use #327 to load any potential changes?

Select shows major + minor; But options show major with .x; Shouldn't select options label show the full version?

That's an issue with the releases array: It only includes majors.

@ovflowd
Copy link
Member

ovflowd commented Jun 29, 2025

The <code /> tags use the same styling everywhere, it's inherited from @node-core/ui-components/styles/markdown.css.

Yes, but that's not the <code> styling, these elements are written with <bold><code>ffff</code></bold> we could sanitize something or change the font for small code blocks, or the antialising because it looks quite weird and contrasting the table/theme

@ovflowd
Copy link
Member

ovflowd commented Jun 29, 2025

I've updated the orama-db generator to also index the first paragraph of a given header.

Orama also supports tags and labels, hence we could for fs.writeFileSync add a label of write file sync (direct human counterpart) or something; Trying to be ingenius here to increase the likelihood of a match based on what matters versus textual match.

cc @micheleriva any idea here?

@ovflowd
Copy link
Member

ovflowd commented Jun 29, 2025

We could add a static (backup) navbar, and then also use #327 to load any potential changes?

We can keep it empty for now. Would be cool to have at least one entry "Go back to the Website" (i18n)

BTW, have we integrated i18n on the new web gen?

@ovflowd
Copy link
Member

ovflowd commented Jun 29, 2025

@avivkeller latest commit build failing:

image

@avivkeller
Copy link
Member Author

avivkeller commented Jun 29, 2025

On my machine, performance tanked, I need to investigate.

@ovflowd
Copy link
Member

ovflowd commented Jun 29, 2025

Noticed the same.

@ovflowd
Copy link
Member

ovflowd commented Jun 29, 2025

image

I wonder if for these sort of properties (such as an object), we can do something like

image

(Just a rough idea) but allows better understanding that these are pieces of options?


Also I noticed what's wrong, the text on the table is 14px but the <code> is 16px when it should also be 14px!

image

image

@avivkeller
Copy link
Member Author

avivkeller commented Jun 29, 2025

I actually think the perf regression is a bug in tailwind. Maybe the patch they just released contained a bug.

@avivkeller
Copy link
Member Author

avivkeller commented Jun 29, 2025

Indeed. Downgrading to @tailwind/[email protected] resolves the perf regression

@avivkeller
Copy link
Member Author

avivkeller commented Jun 29, 2025

Yes, but that's not the <code> styling, these elements are written with ffff we could sanitize something or change the font for small code blocks, or the antialising because it looks quite weird and contrasting the table/theme

I've adjusted the code tags to inherit their size.

Orama also supports tags and labels

We could add a "tags" in the YAML metadata in nodejs/node?

I wonder if for these sort of properties (such as an object), we can do something like

Done.

BTW, have we integrated i18n on the new web gen?

#280

@ovflowd
Copy link
Member

ovflowd commented Jun 29, 2025

We could add a "tags" in the YAML metadata in nodejs/node?

too much upstream work. -1

@ovflowd
Copy link
Member

ovflowd commented Jun 29, 2025

Is it still supposed to take this much?:

node bin/cli.mjs generate -t web -i "../node/doc/api/*.md" -o "./out" --index  106.07s user 34.23s system 293% cpu 47.827 total

@ovflowd
Copy link
Member

ovflowd commented Jun 29, 2025

I feel we need to fix styles upstream on the Table component?

image

(missing borders, and maybe making the paddings be equally correct top/bottom)

@ovflowd
Copy link
Member

ovflowd commented Jun 29, 2025

Also, OOC: Do we need line numbers on these that are from the definitions?

image

@ovflowd
Copy link
Member

ovflowd commented Jun 29, 2025

image

Also missing Description column for top level items?

I also think when there's no description can we either write N/A or just -

@avivkeller
Copy link
Member Author

Is it still supposed to take this much?:

106.07s isn't great, we have several improvements planned, including:

  • Using a faster minifier
  • Precompiling ui-components (So no custom ESBuild resolver, PostCSS, or Tailwind)

I feel we need to fix styles upstream on the Table component?
(missing borders, and maybe making the paddings be equally correct top/bottom)

nodejs/nodejs.org#7911

Also, OOC: Do we need line numbers on these that are from the definitions?

No, I can update the CSS to remove them.

Also missing Description column for top level items?

It's not a bug, if there aren't any descriptions on any of the entries, the column is omitted. Ditto for name and type. However, perhaps we should give the subtable headings if it has different columns than the parent table.

@avivkeller
Copy link
Member Author

avivkeller commented Jun 30, 2025

We are also Promise.all-ing the generation, which, while faster, can be very intensive for the host machine, so I want make that use a for loop, like the legacy generator.

It prevents memory crashes from happening, but it exponentially slower, so I really need to improve performance.

(We can also p-limit the Promise.all, what what would the limit be? --threads is for the amount of workers, should it also dictate how many promises the worker can spawn?)

@ovflowd
Copy link
Member

ovflowd commented Jun 30, 2025

It's not a bug, if there aren't any descriptions on any of the entries, the column is omitted. Ditto for name and type. However, perhaps we should give the subtable headings if it has different columns than the parent table.

I think we should always render all columns .. But yeah, I also think suitable headings make sense. At the very least suitable headings and fixed paddings

@ovflowd
Copy link
Member

ovflowd commented Jun 30, 2025

We are also Promise.all-ing the generation, which, while faster, can be very intensive for the host machine, so I want make that use a for loop, like the legacy generator.

It prevents memory crashes from happening, but it exponentially slower, so I really need to improve performance.

(We can also p-limit the Promise.all, what what would the limit be? --threads is for the amount of workers, should it also dictate how many promises the worker can spawn?)

Let's think of that if all other performance improvements aren't enough

@avivkeller
Copy link
Member Author

I think we should always render all columns .. But yeah, I also think suitable headings make sense. At the very least suitable headings and fixed paddings

I feel like there are cases where that's not beneficial, like
Screenshot 2025-06-29 at 21 09 28

Let's think of that if all other performance improvements aren't enough

That (being using Promise.all) is what we are using right now... with the 100s+ execution time 😬

@ovflowd
Copy link
Member

ovflowd commented Jun 30, 2025

I think we should always render all columns .. But yeah, I also think suitable headings make sense. At the very least suitable headings and fixed paddings

I feel like there are cases where that's not beneficial, like Screenshot 2025-06-29 at 21 09 28

Let's think of that if all other performance improvements aren't enough

That (being using Promise.all) is what we are using right now... with the 100s+ execution time 😬

I feel that we should always have the same columns, to make it consistent. Otherwise it might be confusing. Simply set the values to said empty values as N/A or - I'm 100% inclined into consistency here (please 🙇)

@ovflowd
Copy link
Member

ovflowd commented Jun 30, 2025

I think we should always render all columns .. But yeah, I also think suitable headings make sense. At the very least suitable headings and fixed paddings

I feel like there are cases where that's not beneficial, like Screenshot 2025-06-29 at 21 09 28

Let's think of that if all other performance improvements aren't enough

That (being using Promise.all) is what we are using right now... with the 100s+ execution time 😬

I feel that we should always have the same columns, to make it consistent. Otherwise it might be confusing. Simply set the values to said empty values as N/A or - I'm 100% inclined into consistency here (please 🙇)

Even more as just this Type , doesn't tell anything to the users. Is this a parameter? Return type? Argument of the method? Also where's the snippet on that example?

@avivkeller
Copy link
Member Author

avivkeller commented Jun 30, 2025

Even more as just this Type, doesn't tell anything to the users. Is this a parameter? Return type? Argument of the method? Also where's the snippet on that example?

That example was generated from the following markdown:

#### `dir.path`

<!-- YAML
added: v12.12.0
-->

* {string}

The read-only path of this directory as was provided to [`fs.opendir()`][],
[`fs.opendirSync()`][], or [`fsPromises.opendir()`][].

Maybe it's just me, but it feels weird to have the table below when only Type is specified:

Property Type Description
- <string> -

@avivkeller avivkeller marked this pull request as draft July 1, 2025 00:34
@avivkeller
Copy link
Member Author

avivkeller commented Jul 1, 2025

Moving to draft until the following performance improvements are completed (dynamic list, feel free to add more items):

Footnotes

  1. Ideally, we'd use a pluginless implementation due to the removal of PostCSS, making everything even faster

  2. Currently, we MDX -> ESTree -> Babel transforms -> Text. I would like to either MDX -> ESTree -> Transforms -> Text OR MDX -> ESTree -> Text -> String Transforms.

  3. My A2179 crashes (OOM) when generating doc/api/*.md, but maybe it's just me?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blocked enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add react-web generator Write React Components
6 participants