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 4 commits into
base: main
Choose a base branch
from
Draft

feat(web): add web generator #285

wants to merge 4 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

Codecov Report

Attention: Patch coverage is 60.60831% with 531 lines in your changes missing coverage. Please review.

Project coverage is 70.69%. Comparing base (9e3ba2f) to head (4274e9e).
Report is 2 commits behind head on main.

✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...nerators/jsx-ast/utils/createSignatureElements.mjs 27.39% 167 Missing ⚠️
src/generators/web/index.mjs 42.40% 72 Missing ⚠️
src/generators/jsx-ast/utils/buildContent.mjs 46.61% 63 Missing ⚠️
src/generators/web/build/generate.mjs 23.61% 55 Missing ⚠️
src/generators/web/build/bundle.mjs 34.17% 52 Missing ⚠️
src/generators/jsx-ast/index.mjs 26.00% 37 Missing ⚠️
src/generators/jsx-ast/utils/buildBarProps.mjs 42.00% 28 Missing and 1 partial ⚠️
src/generators/orama-db/index.mjs 54.34% 21 Missing ⚠️
src/utils/queries/index.mjs 71.73% 10 Missing and 3 partials ⚠️
src/generators/web/build/plugins.mjs 89.15% 9 Missing ⚠️
... and 7 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #285      +/-   ##
==========================================
- Coverage   71.52%   70.69%   -0.84%     
==========================================
  Files         117      129      +12     
  Lines        9719    10756    +1037     
  Branches      590      647      +57     
==========================================
+ Hits         6952     7604     +652     
- Misses       2764     3145     +381     
- Partials        3        7       +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@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

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):

  • PostCSS / Tailwind removed1
  • ESBuild replaced with Rolldown12 (Speed comparison: https://x.com/youyuxi/status/1869608132386922720)
  • Externalize Server-side dependencies1
  • Hast compiled directly to Babel OR Babel replaced with ESTree alternative3
    • Fix: Hast -> String -> Transforms -> Compile (Rather than Hast -> ESTree -> Babel -> Transforms -> String -> Compile)
  • OOM Crashes on less powerful machines resolved4

Footnotes

  1. Depends on the precompilation of @node-core/ui-components. 2 3

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

  3. Currently, we MDX -> ESTree -> Babel transforms -> Text. I would like to either MDX -> ESTree -> Transforms -> Text OR MDX -> ESTree -> Text -> String Transforms. Although, perhaps MDX -> String -> Transforms will be speedy?

  4. My A2179 crashes (OOM) when generating doc/api/*.md, but maybe it's just me? Our perf improvements should resolve this.

title: 'string',
description: 'string',
path: 'string',
};
Copy link

Choose a reason for hiding this comment

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

Hi!
This schema is the one used to display the results. You can add other properties that Orama will search for. This will help add metadata like tags and similar to all documents.

export const SCHEMA = {
  title: 'string',
  description: 'string',
  path: 'string',
  tags: 'string[]', // or 'string'
};

@ovflowd WDYT about this enhancement?

NB: You can also play with the "boost" parameter. From my experience, boosting the title and similar fields is helpful because people commonly want to see first the title-matching result and the rest later.

Copy link
Member

Choose a reason for hiding this comment

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

cc @avivkeller; Thank you, @allevo!

Copy link
Member Author

Choose a reason for hiding this comment

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

Sounds good to me, but we'd need to add tags / whatnot to the upstream docs, or hardcode them here. I feel like, while more work, upstreaming the tags aligns more with #332.

Copy link
Member

Choose a reason for hiding this comment

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

Let's at least configure the boost parameter. We could add these "tags" as part of the YAML schema (for the time being)

Copy link
Member Author

Choose a reason for hiding this comment

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

How should we define what to "boost"? A hardcoded list?

@avivkeller
Copy link
Member Author

Screenshot 2025-07-03 at 16 46 25 Everything shown in this image is PostCSS / Tailwind. That's roughly ~90s (on my machine). Once @Harkunwar finishes that (thx btw), we should be able to get this to run at a fairly decent speed.

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
7 participants