Skip to content

fix(npm): improves root handling and resolve to local font files when possible#367

Open
florian-lefebvre wants to merge 4 commits into
mainfrom
fix/npm-provider-misc
Open

fix(npm): improves root handling and resolve to local font files when possible#367
florian-lefebvre wants to merge 4 commits into
mainfrom
fix/npm-provider-misc

Conversation

@florian-lefebvre
Copy link
Copy Markdown
Collaborator

@florian-lefebvre florian-lefebvre commented Apr 6, 2026

Reported at withastro/astro#16218.

While digging the reproduction, I uncovered a few bugs:

  1. The key was missing .json, making it harder to debug
  2. Defaulting root to '.' causes a bunch of issues when manipulating paths. I updated it to process.cwd() which does the same thing. I updated tests to set it back to '.' to make it easier to mock
  3. When resolving local fonts, URLs would be proxied to the CDN which defeats the purpose of using local files. Now, we compute paths to local font files

Summary by CodeRabbit

  • Bug Fixes

    • npm provider now resolves the root directory to the actual project working directory by default and normalizes trailing slashes, improving local package resolution.
  • Improvements

    • Local font URL resolution now produces consistent filesystem-style paths for local packages.
    • TypeScript configuration extended to include Node.js types.
  • Tests

    • Updated tests to reflect explicit root handling and normalized path behavior.

Review Change Stack

Comment thread src/providers/npm.ts
const npmFetch = $fetch.create({ baseURL: cdn })
const readFile = providerOptions.readFile
const root = providerOptions.root || '.'
const root = stripTrailingSlash(providerOptions.root || await import('node:process').then(m => m.cwd()))
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

An eslint rules requires us to use the module instead of the global. I decided to use a dynamic import to use it as a last resort, eg. in browser envs (although I doubt this is the primary usecase)

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 6, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.66%. Comparing base (91f59d2) to head (04f6ad7).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #367      +/-   ##
==========================================
+ Coverage   98.37%   98.66%   +0.29%     
==========================================
  Files          12       12              
  Lines         676      674       -2     
  Branches      176      175       -1     
==========================================
  Hits          665      665              
+ Misses         11        9       -2     

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 6, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1cd49216-cac2-4ba5-a259-d436bce969bb

📥 Commits

Reviewing files that changed from the base of the PR and between 1c2b327 and eec091b.

📒 Files selected for processing (1)
  • tsconfig.json
✅ Files skipped from review due to trivial changes (1)
  • tsconfig.json

📝 Walkthrough

Walkthrough

Updates TypeScript configuration to include Node.js types; README and tests adjust npm provider root documentation and usage; npm provider code normalizes root, resolves runtime default via process.cwd(), refactors URL transforms, and updates cache key format.

Changes

Documentation

Layer / File(s) Summary
README update
README.md
Updated default documentation for providers.npm root from '.' to process.cwd().

Google Provider

Layer / File(s) Summary
Lint comment cleanup
src/providers/google.ts
Removed two inline ESLint suppression comments (e18e/prefer-array-to-sorted) without changing runtime behavior.

NPM Provider Implementation

Layer / File(s) Summary
Provider runtime and URL handling
src/providers/npm.ts
Factory made async; root default now resolves via process.cwd() and is normalized by stripping trailing slashes; URL transformation consolidated into transformUrls() and applied to local/CDN resolution; resolved-font cache key now includes .json extension.

NPM Provider Tests

Layer / File(s) Summary
Tests updated for explicit root and path expectations
test/providers/npm.test.ts
Tests now pass root: '.' for local-resolution scenarios, expect filesystem-style /node_modules/... paths, and verify normalized root behavior in custom-root tests.

TypeScript Configuration

Layer / File(s) Summary
Node types added
tsconfig.json
Added "types": ["node"] to compiler options to include Node.js ambient type declarations.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

Suggested reviewers

  • danielroe

Poem

🐰 I hop through cwd where paths are found,
Trailing slashes fall to the ground,
Async waits and URLs bend,
Cache keys end with .json, my friend,
Node types beside me, safe and sound.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: improving root handling and enabling local font file resolution in the npm provider.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/npm-provider-misc

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
src/providers/npm.ts (2)

316-316: Cache key format change will orphan existing entries.

Adding .json to the cache key is a breaking change for cached data. Existing cache entries without the .json suffix will become unreachable and remain orphaned until they expire (one week). This isn't a functional issue—just be aware that users may see cache misses after upgrading.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/providers/npm.ts` at line 316, The cache key was changed to include a
".json" suffix in the const key =
`npm:${pkgName}/${cssFile}-${hash(options)}.json`, which will orphan existing
entries; revert to the previous key format or implement backward-compatibility
in the cache read path by attempting the old key (e.g., const oldKey =
`npm:${pkgName}/${cssFile}-${hash(options)}`) when fetching and, if found,
either migrate it to the new key or use it directly, and ensure any cache write
still uses the chosen canonical key so subsequent reads are consistent (update
the code around the key variable usage to try oldKey before new key and migrate
if necessary).

132-137: Consider handling backslashes for Windows compatibility.

The stripTrailingSlash() function only handles forward slashes. On Windows, paths might use backslashes. Consider also stripping trailing backslashes.

♻️ Optional fix for Windows compatibility
 function stripTrailingSlash(str: string): string {
-  if (str.endsWith('/')) {
-    return str.slice(0, -1)
+  if (str.endsWith('/') || str.endsWith('\\')) {
+    return str.slice(0, -1)
   }
   return str
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/providers/npm.ts` around lines 132 - 137, stripTrailingSlash currently
only trims a trailing forward slash; update the function (stripTrailingSlash) to
also handle Windows backslashes by trimming any trailing forward or backslash
characters (e.g., treat both '/' and '\' as trailing separators) so paths like
"C:\path\" are normalized; implement by removing trailing slash/backslash
characters (use a single logic that strips both types) and return the cleaned
string from stripTrailingSlash.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/providers/npm.ts`:
- Line 316: The cache key was changed to include a ".json" suffix in the const
key = `npm:${pkgName}/${cssFile}-${hash(options)}.json`, which will orphan
existing entries; revert to the previous key format or implement
backward-compatibility in the cache read path by attempting the old key (e.g.,
const oldKey = `npm:${pkgName}/${cssFile}-${hash(options)}`) when fetching and,
if found, either migrate it to the new key or use it directly, and ensure any
cache write still uses the chosen canonical key so subsequent reads are
consistent (update the code around the key variable usage to try oldKey before
new key and migrate if necessary).
- Around line 132-137: stripTrailingSlash currently only trims a trailing
forward slash; update the function (stripTrailingSlash) to also handle Windows
backslashes by trimming any trailing forward or backslash characters (e.g.,
treat both '/' and '\' as trailing separators) so paths like "C:\path\" are
normalized; implement by removing trailing slash/backslash characters (use a
single logic that strips both types) and return the cleaned string from
stripTrailingSlash.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 35712c94-45eb-4bf1-9a4e-d1490dd94853

📥 Commits

Reviewing files that changed from the base of the PR and between 62bb93e and 1c2b327.

📒 Files selected for processing (5)
  • README.md
  • src/providers/google.ts
  • src/providers/npm.ts
  • test/providers/npm.test.ts
  • tsconfig.json

@florian-lefebvre
Copy link
Copy Markdown
Collaborator Author

@danielroe when you have time, I'd like your review on this!

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.

1 participant