Skip to content

feat: assets copying#753

Open
moshams272 wants to merge 4 commits intonodejs:mainfrom
moshams272:feat/ast-asset-copy
Open

feat: assets copying#753
moshams272 wants to merge 4 commits intonodejs:mainfrom
moshams272:feat/ast-asset-copy

Conversation

@moshams272
Copy link
Copy Markdown
Contributor

Description

Local images referenced in Markdown files were not resolved correctly or copied to the final build, this PR will solve this by copying the images in the output/assets/.

I solved this from root from the jsx-ast phase as they are markdown AST because we can iterate on them easier than JSX elements.

Steps:

  • Updated src/generators/ast/generate.mjs to include the absolute fullPath of each Markdown file.

  • Updated src/generators/metadata/utils/parse.mjs to include the fullPath into the metadata entries.

  • Implemented extractAssetsFromAST in src/generators/jsx-ast/utils/buildContent.mjs, that identifies local images, rewrites their URLs to a centralized /assets/ directory, and collects the source paths.

  • Implemented copyProjectAssets to the web generator src/generators/web/generate.mjs to aggregate all unique assets and copy them to the output directory.

Validation

  • Added tests in src/generators/jsx-ast/utils/__tests__/buildContent.test.mjs to verify that the assetsMap is populated with absolute paths.

  • Tested against Node.js API documentation; local images (like logo.png) are now correctly copied to the out/assets folder and displayed in the web generated.

image Screenshot from 2026-04-05 20-21-43

Related Issues

#748

Check List

  • I have read the Contributing Guidelines and made commit messages that follow the guideline.
  • I have run node --run test and all tests passed.
  • I have check code formatting with node --run format & node --run lint.
  • I've covered new added functionality with unit tests if necessary.

@moshams272 moshams272 requested a review from a team as a code owner April 5, 2026 19:10
@cursor
Copy link
Copy Markdown

cursor bot commented Apr 5, 2026

PR Summary

Medium Risk
Adds new path-based asset rewriting and filesystem copy behavior during generation; mistakes could break image URLs or cause missing/incorrectly overwritten assets in the output.

Overview
Adds support for local markdown image assets in generated web docs by threading each markdown file’s absolute fullPath through AST/metadata generation, rewriting local image URLs to /assets/<filename> during buildContent, and returning an assetsMap alongside the JSX AST.

The web generator now aggregates these asset maps and copies the referenced files into output/assets/ during build, with a new unit test asserting local URL resolution and asset-map population.

Reviewed by Cursor Bugbot for commit 536ed59. Bugbot is set up for automated code reviews on this repo. Configure here.

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 5, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
api-docs-tooling Ready Ready Preview Apr 5, 2026 7:11pm

Request Review

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 5, 2026

Codecov Report

❌ Patch coverage is 63.33333% with 44 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.42%. Comparing base (2680e84) to head (536ed59).

Files with missing lines Patch % Lines
src/generators/web/generate.mjs 0.00% 37 Missing ⚠️
src/generators/jsx-ast/utils/buildContent.mjs 88.13% 7 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #753      +/-   ##
==========================================
+ Coverage   77.08%   77.42%   +0.33%     
==========================================
  Files         153      153              
  Lines       13749    13865     +116     
  Branches     1111     1122      +11     
==========================================
+ Hits        10599    10735     +136     
+ Misses       3146     3126      -20     
  Partials        4        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.

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 4 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 536ed59. Configure here.

}

return assetsMap;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Asset filename collisions silently overwrite different images

Medium Severity

extractAssetsFromAST uses basename(originalUrl) as the destination filename, so two different images with the same name from different directories (e.g., ./diagrams/arch.png in fs.md and ./diagrams/arch.png in net.md) both map to arch.png in /assets/. One copy silently overwrites the other, and both markdown files end up referencing the wrong image. The AST URLs are also both rewritten to /assets/arch.png, making the collision unrecoverable.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 536ed59. Configure here.


for (const [source, dest] of allAssets.entries()) {
try {
await cp(source, dest, { force: true });
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Use copyFile with COPYFILE_FICLONE for files

Low Severity

cp is used to copy individual asset files, but the project convention requires copyFile from node:fs/promises with the constants.COPYFILE_FICLONE flag for single-file copies. copyFile with COPYFILE_FICLONE attempts a zero-copy clone and gracefully falls back, which is more efficient for single files than cp.

Fix in Cursor Fix in Web

Triggered by learned rule: Use copyFile with COPYFILE_FICLONE instead of manual readFile+writeFile

Reviewed by Cursor Bugbot for commit 536ed59. Configure here.

return false;
} catch {
return true;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Use URL.parse() instead of new URL()

Low Severity

isLocalAsset uses new URL(url) inside a try/catch to detect local paths. The project convention prefers URL.parse(), which returns null for invalid URLs instead of throwing. This can be simplified to return URL.parse(url) === null.

Fix in Cursor Fix in Web

Triggered by learned rule: Prefer URL.parse() and destructuring over new URL().property

Reviewed by Cursor Bugbot for commit 536ed59. Configure here.

assetsMap.set(sourcePath, fileName);

// Rewrite AST URL
imageNode.url = `/assets/${fileName}`;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hardcoded 'assets' path duplicated across two generators

Low Severity

The string 'assets' is hardcoded as an output directory name in both buildContent.mjs (URL rewrite to `/assets/${fileName}`) and generate.mjs (filesystem path via join(outputDir, 'assets', name)). Since this is a cross-generator constant controlling output path structure, it belongs as a named constant. Changing it in one generator without the other would silently break image resolution.

Additional Locations (1)
Fix in Cursor Fix in Web

Triggered by learned rule: Regex patterns and external URLs belong in constants files

Reviewed by Cursor Bugbot for commit 536ed59. Configure here.

@avivkeller
Copy link
Copy Markdown
Member

avivkeller commented Apr 5, 2026

Why can't the user specify "all my assets are in /xyz/, copy that", this seems like a lot of work and data that needs to now be processed

@moshams272
Copy link
Copy Markdown
Contributor Author

moshams272 commented Apr 5, 2026

Why can't the user specify "all my assets are in /xyz/, copy that", this seems like a lot of work and data that needs to now be processed

If you mean as staticDir in the config file, I think when a project uses doc-kit he cann't iterate to put them in one folder!

what do you think?

@avivkeller
Copy link
Copy Markdown
Member

I think when a project uses doc-kit he cann't iterate to put them in one folder!

Say pathsToCopy: [{ '/path/to/my/assets': '/path/to/my/output' }, '/path/to/my/other/asset'] -> Output /path/to/my/assets at /path/to/my/output and /path/to/my/other/asset at asset.

wdyt @nodejs/web-infra I want opinions

@moshams272
Copy link
Copy Markdown
Contributor Author

I'll hold off on fixing the bot's comments, Waiting for the final decision.

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.

2 participants