Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #617 +/- ##
==========================================
- Coverage 78.62% 78.62% -0.01%
==========================================
Files 128 128
Lines 12461 12470 +9
Branches 902 902
==========================================
+ Hits 9798 9804 +6
- Misses 2658 2661 +3
Partials 5 5 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
| File | Base | Head | Diff |
|---|---|---|---|
orama-db.json |
8.03 MB | 8.03 MB | +2.00 B (+0.00%) |
web Generator
| File | Base | Head | Diff |
|---|---|---|---|
webcrypto.js |
464.93 KB | 428.44 KB | -36.49 KB (-7.85%) |
webcrypto.html |
528.45 KB | 500.52 KB | -27.93 KB (-5.29%) |
crypto.js |
1.24 MB | 1.23 MB | -6.32 KB (-0.50%) |
crypto.html |
987.57 KB | 982.80 KB | -4.77 KB (-0.48%) |
styles.css |
128.14 KB | 129.88 KB | +1.73 KB (+1.35%) |
addons.js |
299.72 KB | 299.75 KB | +29.00 B (+0.01%) |
debugger.js |
31.09 KB | 31.12 KB | +29.00 B (+0.09%) |
report.js |
188.53 KB | 188.56 KB | +29.00 B (+0.02%) |
typescript.js |
25.81 KB | 25.83 KB | +29.00 B (+0.11%) |
cli.js |
325.51 KB | 325.54 KB | +24.00 B (+0.01%) |
deprecations.js |
313.52 KB | 313.55 KB | +24.00 B (+0.01%) |
environment_variables.js |
15.13 KB | 15.15 KB | +24.00 B (+0.15%) |
errors.js |
413.89 KB | 413.92 KB | +24.00 B (+0.01%) |
esm.js |
139.78 KB | 139.80 KB | +24.00 B (+0.02%) |
globals.js |
141.34 KB | 141.36 KB | +24.00 B (+0.02%) |
module.js |
351.50 KB | 351.53 KB | +24.00 B (+0.01%) |
modules.js |
158.24 KB | 158.26 KB | +24.00 B (+0.01%) |
n-api.js |
721.30 KB | 721.32 KB | +24.00 B (+0.00%) |
packages.js |
126.22 KB | 126.25 KB | +24.00 B (+0.02%) |
permissions.js |
28.46 KB | 28.48 KB | +24.00 B (+0.08%) |
webstreams.js |
320.81 KB | 320.84 KB | +24.00 B (+0.01%) |
assert.js |
467.59 KB | 467.61 KB | +19.00 B (+0.00%) |
async_context.js |
194.17 KB | 194.19 KB | +19.00 B (+0.01%) |
async_hooks.js |
201.30 KB | 201.32 KB | +19.00 B (+0.01%) |
buffer.js |
1.13 MB | 1.13 MB | +19.00 B (+0.00%) |
child_process.js |
489.11 KB | 489.13 KB | +19.00 B (+0.00%) |
cluster.js |
206.89 KB | 206.90 KB | +19.00 B (+0.01%) |
console.js |
114.73 KB | 114.75 KB | +19.00 B (+0.02%) |
dgram.js |
185.12 KB | 185.14 KB | +19.00 B (+0.01%) |
diagnostics_channel.js |
252.87 KB | 252.89 KB | +19.00 B (+0.01%) |
dns.js |
292.44 KB | 292.46 KB | +19.00 B (+0.01%) |
domain.js |
93.08 KB | 93.10 KB | +19.00 B (+0.02%) |
embedding.js |
37.28 KB | 37.30 KB | +19.00 B (+0.05%) |
events.js |
575.74 KB | 575.76 KB | +19.00 B (+0.00%) |
fs.js |
1.33 MB | 1.33 MB | +19.00 B (+0.00%) |
http.js |
742.92 KB | 742.94 KB | +19.00 B (+0.00%) |
http2.js |
861.83 KB | 861.85 KB | +19.00 B (+0.00%) |
https.js |
166.44 KB | 166.46 KB | +19.00 B (+0.01%) |
inspector.js |
132.83 KB | 132.85 KB | +19.00 B (+0.01%) |
intl.js |
37.06 KB | 37.08 KB | +19.00 B (+0.05%) |
net.js |
321.67 KB | 321.69 KB | +19.00 B (+0.01%) |
os.js |
116.34 KB | 116.36 KB | +19.00 B (+0.02%) |
path.js |
105.32 KB | 105.33 KB | +19.00 B (+0.02%) |
perf_hooks.js |
406.03 KB | 406.04 KB | +19.00 B (+0.00%) |
process.js |
747.57 KB | 747.58 KB | +19.00 B (+0.00%) |
punycode.js |
30.23 KB | 30.25 KB | +19.00 B (+0.06%) |
querystring.js |
33.02 KB | 33.04 KB | +19.00 B (+0.06%) |
quic.js |
220.25 KB | 220.27 KB | +19.00 B (+0.01%) |
readline.js |
238.88 KB | 238.89 KB | +19.00 B (+0.01%) |
repl.js |
215.40 KB | 215.42 KB | +19.00 B (+0.01%) |
single-executable-applications.js |
83.41 KB | 83.43 KB | +19.00 B (+0.02%) |
sqlite.js |
241.73 KB | 241.75 KB | +19.00 B (+0.01%) |
stream.js |
909.92 KB | 909.94 KB | +19.00 B (+0.00%) |
string_decoder.js |
32.32 KB | 32.34 KB | +19.00 B (+0.06%) |
synopsis.js |
15.70 KB | 15.72 KB | +19.00 B (+0.12%) |
test.js |
885.03 KB | 885.05 KB | +19.00 B (+0.00%) |
timers.js |
107.82 KB | 107.84 KB | +19.00 B (+0.02%) |
tls.js |
349.38 KB | 349.40 KB | +19.00 B (+0.01%) |
tracing.js |
78.84 KB | 78.86 KB | +19.00 B (+0.02%) |
tty.js |
54.21 KB | 54.23 KB | +19.00 B (+0.03%) |
url.js |
354.43 KB | 354.45 KB | +19.00 B (+0.01%) |
util.js |
797.10 KB | 797.12 KB | +19.00 B (+0.00%) |
v8.js |
355.28 KB | 355.30 KB | +19.00 B (+0.01%) |
vm.js |
380.39 KB | 380.41 KB | +19.00 B (+0.00%) |
wasi.js |
44.80 KB | 44.82 KB | +19.00 B (+0.04%) |
worker_threads.js |
420.88 KB | 420.90 KB | +19.00 B (+0.00%) |
zlib.js |
334.44 KB | 334.46 KB | +19.00 B (+0.01%) |
documentation.js |
9.72 KB | 9.74 KB | +14.00 B (+0.14%) |
index.js |
9.51 KB | 9.52 KB | +14.00 B (+0.14%) |
There was a problem hiding this comment.
Pull request overview
This PR implements an announcement banner component that fetches configuration from a remote JSON endpoint to display global and version-specific banners on the API documentation pages. The banners support date-based activation/deactivation and can include links to relevant resources.
Changes:
- Added
AnnouncementBannercomponent that asynchronously fetches and displays banners from a remote config - Implemented
isBannerActiveutility function to filter banners based on date ranges - Added
versionMajorandremoteConfigfields to static data passed from server to client
Reviewed changes
Copilot reviewed 7 out of 9 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
src/generators/web/index.mjs |
Adds remote config URL to web generator default configuration |
src/generators/web/types.d.ts |
Adds remoteConfig type definition for the web generator |
src/generators/web/utils/data.mjs |
Extracts version major and remote config URL from server config to client static data |
src/generators/web/constants.mjs |
Registers AnnouncementBanner component in JSX imports map |
src/generators/jsx-ast/utils/buildContent.mjs |
Adds AnnouncementBanner to document layout above navigation bar |
src/generators/web/ui/utils/banner.mjs |
Implements date range checking logic for banner activation |
src/generators/web/ui/utils/__tests__/banner.test.mjs |
Comprehensive tests for banner date range logic |
src/generators/web/ui/components/AnnouncementBanner/types.d.ts |
Type definitions for banner entries and remote config structure |
src/generators/web/ui/components/AnnouncementBanner/index.jsx |
Main component that fetches config and renders active banners |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| '#config/Logo': '@node-core/ui-components/Common/NodejsLogo', | ||
| }, | ||
| remoteConfig: | ||
| 'https://gist.githubusercontent.com/araujogui/8ea72ffaf574f58fca1482e764e8b5c8/raw/16af51e4efbf37da7b6aff9b7e5dd967d955aacf/api-docs.config.json', |
There was a problem hiding this comment.
The hardcoded Gist URL includes a specific commit hash in the raw URL. This means the configuration is pinned to a specific version and won't automatically update when the Gist is modified. If the intent is to fetch the latest version dynamically, the URL should use the format without the commit hash (e.g., ending with /raw/api-docs.config.json). If the commit hash is intentional for stability, consider documenting why this specific version is pinned and how it should be updated.
| 'https://gist.githubusercontent.com/araujogui/8ea72ffaf574f58fca1482e764e8b5c8/raw/16af51e4efbf37da7b6aff9b7e5dd967d955aacf/api-docs.config.json', | |
| 'https://gist.githubusercontent.com/araujogui/8ea72ffaf574f58fca1482e764e8b5c8/raw/api-docs.config.json', |
| const versionBanner = config[`v${versionMajor}`]?.banner; | ||
| if (versionBanner && isBannerActive(versionBanner)) { | ||
| active.push(versionBanner); |
There was a problem hiding this comment.
The versionMajor is being used without a null check when constructing the version key. If config.version?.major is null (when version is not available), the template literal will create a key "vnull" which doesn't match the expected format "v24" from the issue description. Consider early returning or handling the null case explicitly before attempting to fetch version-specific banners.
| const versionBanner = config[`v${versionMajor}`]?.banner; | |
| if (versionBanner && isBannerActive(versionBanner)) { | |
| active.push(versionBanner); | |
| if (versionMajor != null) { | |
| const versionBanner = config[`v${versionMajor}`]?.banner; | |
| if (versionBanner && isBannerActive(versionBanner)) { | |
| active.push(versionBanner); | |
| } |
| remoteConfig: | ||
| 'https://gist.githubusercontent.com/araujogui/8ea72ffaf574f58fca1482e764e8b5c8/raw/16af51e4efbf37da7b6aff9b7e5dd967d955aacf/api-docs.config.json', |
There was a problem hiding this comment.
According to issue #327, the configuration should reside "on this repository or nodejs/node" as a static api-docs.config.json file, but this implementation uses a GitHub Gist URL instead. Consider whether this temporary Gist URL is intentional for testing/development, or if the final implementation should point to a file hosted in the official repositories. If this is a development URL, it should be documented or replaced before merging.
| <div> | ||
| {banners.map(banner => ( | ||
| <Banner key={banner.link} type={banner.type}> | ||
| {banner.link ? <a href={banner.link}>{banner.text}</a> : banner.text} | ||
| {banner.link && <ArrowUpRightIcon />} | ||
| </Banner> | ||
| ))} | ||
| </div> |
There was a problem hiding this comment.
The wrapper div element lacks semantic meaning. Consider using a more semantic HTML element like <aside> or adding appropriate ARIA attributes to indicate this is an announcements region. For example, <div role="region" aria-label="Announcements"> would improve accessibility by clearly identifying the purpose of this section to screen readers.
| <div> | |
| {banners.map(banner => ( | |
| <Banner key={banner.link} type={banner.type}> | |
| {banner.link ? <a href={banner.link}>{banner.text}</a> : banner.text} | |
| {banner.link && <ArrowUpRightIcon />} | |
| </Banner> | |
| ))} | |
| </div> | |
| <aside aria-label="Announcements"> | |
| {banners.map(banner => ( | |
| <Banner key={banner.link} type={banner.type}> | |
| {banner.link ? <a href={banner.link}>{banner.text}</a> : banner.text} | |
| {banner.link && <ArrowUpRightIcon />} | |
| </Banner> | |
| ))} | |
| </aside> |
| <div> | ||
| {banners.map(banner => ( | ||
| <Banner key={banner.link} type={banner.type}> | ||
| {banner.link ? <a href={banner.link}>{banner.text}</a> : banner.text} |
There was a problem hiding this comment.
External URLs from the remote config are used directly in href attributes without validation. While React/Preact provides some protection against javascript: protocol URLs, there's still a potential security risk if the remote config source is compromised. Consider validating that banner.link uses an approved protocol (https://) or implementing an allowlist of acceptable domains to ensure only trusted URLs are rendered. This is especially important since the config is fetched from an external source.
| {banners.map(banner => ( | ||
| <Banner key={banner.link} type={banner.type}> |
There was a problem hiding this comment.
The key used for banners without links will be undefined, which can cause React reconciliation issues. When there are multiple banners without links, they will all have the same key (undefined), leading to incorrect updates and potential bugs in the banner display. Consider using the banner text or index as a fallback key, or use a combination of fields to ensure uniqueness. For example: key={banner.link || banner.text} or key={banner.link || ${banner.text}-${index}}.
| {banners.map(banner => ( | |
| <Banner key={banner.link} type={banner.type}> | |
| {banners.map((banner, index) => ( | |
| <Banner key={banner.link || `${banner.text}-${index}`} type={banner.type}> |
| {banner.link ? <a href={banner.link}>{banner.text}</a> : banner.text} | ||
| {banner.link && <ArrowUpRightIcon />} |
There was a problem hiding this comment.
The ArrowUpRightIcon typically indicates an external link that opens in a new tab, but the anchor tag is missing target="_blank" and rel="noopener noreferrer" attributes. If the intention is for these links to open in a new tab (as the icon suggests), add target="_blank". For security and accessibility, also include rel="noopener noreferrer" to prevent the linked page from accessing window.opener, and consider adding an aria-label to indicate the link opens externally.
| {banner.link ? <a href={banner.link}>{banner.text}</a> : banner.text} | |
| {banner.link && <ArrowUpRightIcon />} | |
| {banner.link ? ( | |
| <a | |
| href={banner.link} | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| aria-label={`${banner.text} (opens in a new tab)`} | |
| > | |
| {banner.text} | |
| </a> | |
| ) : ( | |
| banner.text | |
| )} | |
| {banner.link && <ArrowUpRightIcon aria-hidden="true" />} |
| remoteConfig: | ||
| 'https://gist.githubusercontent.com/araujogui/8ea72ffaf574f58fca1482e764e8b5c8/raw/16af51e4efbf37da7b6aff9b7e5dd967d955aacf/api-docs.config.json', |
There was a problem hiding this comment.
We don't have a banner config defined anywhere, so, in the meantime, can we have it read from nodejs.org's site.json (or whatever file stores the main sites' banner)
| templatePath: string; | ||
| title: string; | ||
| imports: Record<string, string>; | ||
| remoteConfig: string | null; |
There was a problem hiding this comment.
| remoteConfig: string | null; | |
| remoteConfig: string; |
| {banners.map(banner => ( | ||
| <Banner key={banner.link} type={banner.type}> | ||
| {banner.link ? <a href={banner.link}>{banner.text}</a> : banner.text} | ||
| {banner.link && <ArrowUpRightIcon />} |
There was a problem hiding this comment.
nit: this can go within the above line
| versionMajor: config.version?.major ?? null, | ||
| remoteConfig: config.remoteConfig ?? null, |
There was a problem hiding this comment.
The idea of this data is for components that can't accept the data as properties. Can we instead pass these values as properties to the component, which produces a smaller bundle?
Description
Create announcements banner component
Validation
Related Issues
Fixes #327
Check List
node --run testand all tests passed.node --run format&node --run lint.