Skip to content

feat: getAvailableFontProperties()#398

Open
florian-lefebvre wants to merge 13 commits into
mainfrom
feat/available-font-properties
Open

feat: getAvailableFontProperties()#398
florian-lefebvre wants to merge 13 commits into
mainfrom
feat/available-font-properties

Conversation

@florian-lefebvre
Copy link
Copy Markdown
Collaborator

@florian-lefebvre florian-lefebvre commented May 20, 2026

Adds a new getAvailableFontProperties() API to the unifont instance and providers. Currently when you pass options to resolveFont() you have no way to know if what you're providing can actually be used by the provider. This new API allows such thing now. Examples:

  • A user asks for weight 100, but only 300-700 are available. Now a warning can be logged
  • A CLI asks the user which weights to pick for the selected family. Instead of suggesting a generic list, show exactly what's supported

Tests have been added for all. Note that I didn't implement it for 2 providers:

  • Adobe: not able to test properly, it is generally speaking less featured than other providers
  • NPM: not all supported packages implement metadata, in particular Cal Sans. Omitted for now

Readme is updated

Summary by CodeRabbit

  • New Features

    • Added getAvailableFontProperties() to query available formats, styles, weights and subsets for a font family; results include the provider source. Also exported the corresponding result type.
  • Documentation

    • README updated with usage, return semantics (may be undefined), optional providers argument, and throwOnError behavior; guidance for custom providers added.
  • Tests

    • Added tests for provider results, unknown families, error handling, and throwOnError.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

📝 Walkthrough

Walkthrough

This PR adds a new getAvailableFontProperties() method to the Unifont API, enabling callers to query available font properties (formats, styles, weights, subsets) from a provider stack. The method is implemented in the core orchestration layer and supported by all font providers with appropriate metadata introspection. Fontsource includes caching of variable-font axes. Comprehensive documentation and test coverage are included.

Changes

Add getAvailableFontProperties capability

Layer / File(s) Summary
Type contracts and exports
src/types.ts, src/index.ts
GetAvailableFontPropertiesResult type defined as a partial of ResolveFontOptions without options field. InitializedProvider interface extended with optional getAvailableFontProperties(family: string) method. Type exported for public use.
Core Unifont orchestration
src/unifont.ts
Unifont interface adds getAvailableFontProperties(fontFamily, providers?) method. createUnifont implementation loops through configured providers in order, calls each provider's optional method if present, returns first successful result, handles errors consistently with throwOnError option, and wires the API into the returned unifont object.
Simple provider implementations
src/providers/bunny.ts, src/providers/fontshare.ts, src/providers/google.ts, src/providers/googleicons.ts
Each provider implements getAvailableFontProperties by extracting available formats, styles, weights, and subsets from metadata. Returns undefined for unknown font families. Type updates align internal style types to FontStyles[].
Fontsource provider with variable-font caching
src/providers/fontsource.ts
Implements getAvailableFontProperties with support for variable-font weight ranges. New getVariableAxes async helper caches variable-font axis metadata in ctx.storage, falling back to Fontsource API fetch. Variable fonts augment weight list with min/max wght axis bounds.
Documentation and examples
README.md
"Getting started" example demonstrates getAvailableFontProperties('Poppins'). throwOnError option docs updated to include failures from the method. New API section documents return type, usage example, and provider filtering. Custom provider guide documents optional getAvailableFontProperties implementation pattern with example.
Core API test suite
test/index.test.ts
New test suite for unifont.getAvailableFontProperties covers no-provider behavior, successful aggregation, error logging on provider failure, and error throwing when throwOnError: true.
Provider-specific tests
test/providers/bunny.test.ts, test/providers/fontshare.test.ts, test/providers/fontsource.test.ts, test/providers/google.test.ts, test/providers/googleicons.test.ts
Each provider gets a test case validating getAvailableFontProperties returns expected properties snapshot for known fonts and undefined for unknown families.

Sequence Diagram

sequenceDiagram
  participant Client
  participant Unifont
  participant Provider1
  participant Provider2
  Client->>Unifont: getAvailableFontProperties(fontFamily)
  Unifont->>Provider1: getAvailableFontProperties(fontFamily)
  alt Provider1 has method
    Provider1-->>Unifont: properties or undefined
    alt Result found
      Unifont-->>Client: properties + provider name
    else Result empty
      Unifont->>Provider2: getAvailableFontProperties(fontFamily)
      Provider2-->>Unifont: properties or undefined
      Unifont-->>Client: properties + provider name or undefined
    end
  else Provider1 no method or throws
    Unifont->>Provider2: getAvailableFontProperties(fontFamily)
    Provider2-->>Unifont: properties or undefined
    Unifont-->>Client: properties + provider name or undefined
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • unjs/unifont#393: Both PRs modify src/providers/fontsource.ts around variable-font axis metadata—main PR adds getAvailableFontProperties and the caching helper, while the related PR uses axis data for CDN variant selection.

Suggested reviewers

  • danielroe

Poem

🐰 I hopped through providers, sniffed formats and weights,
From Bunny to Google, I peeked at their crates.
Variable axes cached, styles neatly found,
Query a family — its properties bound.
Hooray! A rabbit-approved font-properties round.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 'feat: getAvailableFontProperties()' directly matches the main change: adding a new getAvailableFontProperties() API across unifont and all providers with documentation and tests.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/available-font-properties

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.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 20, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.51%. Comparing base (91f59d2) to head (9ee09ed).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #398      +/-   ##
==========================================
+ Coverage   98.37%   98.51%   +0.14%     
==========================================
  Files          12       12              
  Lines         676      741      +65     
  Branches      176      193      +17     
==========================================
+ Hits          665      730      +65     
  Misses         11       11              

☔ 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.

@florian-lefebvre florian-lefebvre marked this pull request as ready for review May 20, 2026 11:49
@florian-lefebvre florian-lefebvre changed the title feat: core feat: getAvailableFontProperties() May 20, 2026
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.

Actionable comments posted: 4

🧹 Nitpick comments (1)
README.md (1)

659-659: 💤 Low value

Consider documenting the type signature consistently with other methods.

The type signature uses providers?: T[number]['_name'][], while resolveFont() (line 484) and listFonts() (line 624) document providers?: T[]. While T[number]['_name'][] is more precise (it's explicitly the array of provider name strings), this inconsistency might confuse users comparing the API methods.

The Providers subsection correctly specifies Type: string[], so the usage is clear, but consider aligning the method signature format with existing methods for consistency.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` at line 659, Update the documented Type signature so it matches
the format used by resolveFont() and listFonts(): replace "providers?:
T[number]['_name'][]" with "providers?: T[]" in the Type line for the
GetAvailableFontProperties method (the line currently showing "(fontFamily:
string, providers?: T[number]['_name'][]) =>
Promise<GetAvailableFontPropertiesResult & { provider?: T[number]['_name'] }")
to keep method docs consistent with the Providers subsection and the other
method signatures.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@README.md`:
- Line 689: Rename the misleading variable to reflect that
getAvailableFontProperties() returns properties rather than names: replace
occurrences of availableFonts with a clearer identifier like
availableFontProperties (or fontProperties) in the example using
getAvailableFontProperties('Roboto', ['google']) so the variable name aligns
with the method purpose and avoids confusion.

In `@src/providers/fontsource.ts`:
- Around line 95-111: In getAvailableFontProperties, a rejection from
getVariableAxes when font.variable is true will abort the whole call; wrap the
await getVariableAxes(ctx, font) in a try/catch so failures degrade gracefully:
attempt the call only when font.variable, and on error swallow or log it and
continue returning the static properties (formats, styles, subsets, weights)
without variable wght range; only push the `${variableAxes.axes.wght.min}
${variableAxes.axes.wght.max}` string if variableAxes was successfully retrieved
and variableAxes.axes.wght exists.

In `@src/providers/googleicons.ts`:
- Around line 87-97: getAvailableFontProperties currently returns weights
['100','400'] for non-Icons families which contradicts the Material Symbols
usage that fetches wght over 100..700; update getAvailableFontProperties (and
keep reference to googleIcons and the fontFamily param) so that non-Icons
families return the full weight range
['100','200','300','400','500','600','700'] while preserving Icons families as
['400'] to remain consistent with wght handling and downstream
validation/suggestions.

In `@src/unifont.ts`:
- Line 23: The getAvailableFontProperties implementation incorrectly treats an
empty object returned from a provider as a successful hit (because {} is truthy)
and also fails to attach the provider name to the success result; update the
provider loop inside getAvailableFontProperties to explicitly detect an
empty/invalid result (e.g., check for required keys like variants, subsets,
metrics or Object.keys(result).length === 0) and continue to the next provider
when empty, and when a non-empty result is found, return the result augmented
with provider: currentProviderName so the return type { provider?: ... } is
satisfied.

---

Nitpick comments:
In `@README.md`:
- Line 659: Update the documented Type signature so it matches the format used
by resolveFont() and listFonts(): replace "providers?: T[number]['_name'][]"
with "providers?: T[]" in the Type line for the GetAvailableFontProperties
method (the line currently showing "(fontFamily: string, providers?:
T[number]['_name'][]) => Promise<GetAvailableFontPropertiesResult & { provider?:
T[number]['_name'] }") to keep method docs consistent with the Providers
subsection and the other method signatures.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ae09ba22-65a9-41af-8544-3b27385789e2

📥 Commits

Reviewing files that changed from the base of the PR and between 91f59d2 and fa8f4d0.

📒 Files selected for processing (15)
  • README.md
  • src/index.ts
  • src/providers/bunny.ts
  • src/providers/fontshare.ts
  • src/providers/fontsource.ts
  • src/providers/google.ts
  • src/providers/googleicons.ts
  • src/types.ts
  • src/unifont.ts
  • test/index.test.ts
  • test/providers/bunny.test.ts
  • test/providers/fontshare.test.ts
  • test/providers/fontsource.test.ts
  • test/providers/google.test.ts
  • test/providers/googleicons.test.ts

Comment thread README.md Outdated
Comment thread src/providers/fontsource.ts
Comment thread src/providers/googleicons.ts
Comment thread src/unifont.ts Outdated
florian-lefebvre and others added 3 commits May 20, 2026 16:56
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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 (1)
src/providers/bunny.ts (1)

73-73: ⚡ Quick win

Make subsets ordering deterministic before returning it.

Line 73 currently returns subset keys in upstream metadata order, which can drift and create unstable API output/snapshots. Sorting here makes responses deterministic for consumers.

Proposed patch
-        subsets: Object.keys(font.variants),
+        subsets: Object.keys(font.variants).sort(),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/providers/bunny.ts` at line 73, The returned subsets array is currently
using Object.keys(font.variants) which preserves upstream order and can be
unstable; change the assignment for subsets to a deterministic sorted list by
calling .sort() (preferably .sort((a,b)=>a.localeCompare(b))) on the
Object.keys(...) result so the response/snapshots are stable; update the
expression that sets subsets in the code that references font.variants
accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/providers/bunny.ts`:
- Line 73: The returned subsets array is currently using
Object.keys(font.variants) which preserves upstream order and can be unstable;
change the assignment for subsets to a deterministic sorted list by calling
.sort() (preferably .sort((a,b)=>a.localeCompare(b))) on the Object.keys(...)
result so the response/snapshots are stable; update the expression that sets
subsets in the code that references font.variants accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3d179af2-43ee-4364-93d2-19d05c665bc4

📥 Commits

Reviewing files that changed from the base of the PR and between 94af979 and 9ee09ed.

📒 Files selected for processing (2)
  • src/providers/bunny.ts
  • test/providers/bunny.test.ts

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