feat: add DocFX API reference site, published alongside the demo - #453
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #453 +/- ##
=======================================
Coverage 87.69% 87.69%
=======================================
Files 43 43
Lines 3893 3893
Branches 993 993
=======================================
Hits 3414 3414
Misses 277 277
Partials 202 202 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
be5c931 to
af266ce
Compare
c3429eb to
fec4f41
Compare
f12724d to
c46df59
Compare
Generated from the XML doc comments both packable projects already emit, and published into the same GitHub Pages deployment as the demo under /docs/ - one deployment, one domain, no separate hosting. Build it with docfx/build.sh, never docfx directly. The script copies docs/*.md into the gitignored docfx/articles/ (docfx's TOC resolver needs conceptual markdown inside the project tree) and rewrites their repo-relative links to GitHub permalinks, since the published site ships only rendered articles. It also restores and builds PhoneNumbers.Extensions first: docfx loads both csprojs through MSBuild, which needs a restored project.assets.json under this repo's Central Package Management. Keeping that in the script rather than in each caller means the preview workflow, the deploy workflow and a local run all start from the same state, so a green preview really does predict a green deploy. filterConfig.yml excludes compiler-generated members and anything marked [EditorBrowsable(EditorBrowsableState.Never)], so members the library has deliberately hidden from IntelliSense do not reappear as ordinary API docs. DocFX's git integration is off entirely (disableGitFeatures, _disableContribution). build.sh copies articles in from docs/, so no _gitContribute path could ever produce a working "Edit this page" link for them - the file it would point at was never committed there. It also sidesteps libgit2, which is unverified on this repo's arm64 runners. The docs build in deploy-demo.yml is deliberately NOT continue-on-error: a Pages deployment replaces the whole site, so carrying on past a docs failure would publish a wwwroot with no docs/ in it and take /docs down to a 404 rather than leaving it stale, while README and the demo sidebar both link there. Failing the job means the previous deployment keeps serving, demo and docs together. docs_preview.yml builds the site on every relevant PR and uploads it as a downloadable artifact, so a reviewer can see the rendered result without merging or publishing. assets/brand/ holds the logomark and favicon at the repo root rather than inside either site, so everything that needs the mark shares one copy.
Ports the demo's design tokens onto DocFX's modern template - warm-neutral
surfaces, orange accent, Space Grotesk/Manrope/JetBrains Mono, light and
dark - so the two sites read as one product rather than two tools bolted
together. The rail is rebuilt from DocFX's horizontal navbar into the
demo's persistent dark sidebar.
Two cascade traps worth knowing about, since both made earlier attempts
silently inert:
- DocFX's bundled highlight.js theme styles the .hljs class directly. A
single class selector outranks any number of chained type selectors, so
"pre code, .article pre" never beat it and every --code-bg/--code-text
here was dead. These rules match .hljs itself.
- That same bundled theme paints the structural token classes
(subst/function/params/formula) at its own #dcdcdc, a cool grey against
this theme's warm --code-text, so plain identifiers inside a dark code
block came out a different temperature from plain text everywhere else.
Syntax highlighting ("Ember") keeps every token on the warm half of the
wheel, but deliberately spread along a ladder - wine, red, orange, gold,
olive - rather than packed into a narrow orange band. Crowded into ~40
degrees of hue the nearest pair sits about 14 dE apart, which at code size
stops reading as highlighting and starts reading as orange text. Every
colour clears 4.5:1 against its own code background and every pair is at
least 20 dE from the others.
The install command carries a copy-to-clipboard button and wraps the way a
shell does. The block is not a flex row: that makes the command its own
box starting after the "$", so a wrapped line hangs under the "d" of
dotnet instead of returning to the prompt column. The button is floated a
hair under one line-height rather than inset with padding-right, so it
costs width only on the line it sits on and a continuation runs on
underneath it; its tap target lives on a centred pseudo-element, because
sizing the button itself would make the float several lines tall and
shorten all of them.
The cards' grid minimum is 260px, not 230: auto-fit packs as many columns
as the minimum allows, so too small a minimum kept three cramped columns
in the article's ~860px content width - and the install command paid for
it, wrapping to four lines. It was counter-intuitively worse at full width
than in a narrow window, which drops to one roomy column.
main.js is imported by the modern template as an ES module. The copy
handler is delegated from the document rather than bound per button,
because the template renders page content client side and per-node
handlers would be lost on the next navigation. A denied clipboard
(insecure origin, permissions policy) leaves the button idle rather than
claiming a copy that did not happen.
Adds an "API Docs" entry to the demo's Resources section. Nav.BaseUri
already carries the GitHub Pages repo-name PathBase, so "{BaseUri}docs/"
resolves correctly from any client-side route.
The glyph is a component rather than inline markup, per the demo's
SVG-icons-as-components rule; IconTests renders every IconBase subclass,
so it is covered without a new test.
…eference The port made a lot of Java's package-private classes public, so the generated reference published metadata plumbing and prefix-map internals as first-class API alongside PhoneNumberUtil and ShortNumberInfo. Which types those are was worked out by walking the public signature graph outward from the entry points a consumer actually starts from (PhoneNumberUtil, ShortNumberInfo, AsYouTypeFormatter, PhoneNumberMatcher, the three mappers, the two exceptions, LocaleData, and everything in Extensions). Twelve public types are unreachable from any of them, named in no consumer-facing doc, and handed out by nothing: AreaCodeMap, AreaCodeMapStorageStrategy, DefaultMapStorage, FlyweightMapStorage, BuildMetadataFromBin, BuildMetadataFromXml, BuildPrefixMapFromBin, CountryCodeToRegionCodeMap, MappingFileProvider, MetadataFilter, MetadataManager, PhoneMetadataCollection (+ its Builder) They get [EditorBrowsable(EditorBrowsableState.Never)], matching what RegexCache, PhoneRegex and IMetadataLoader already do - but without [Obsolete], since unlike those three these are all still used internally and TreatWarningsAsErrors would break the build. docfx's existing attribute filter picks them up with no filterConfig change, taking the reference from 43 documented types to 34 with no dangling cross-references left behind. Package validation confirms this is not an API-compat break. Deliberately left visible: - NumberFormat, PhoneMetadata and PhoneNumberDesc are reachable (FormatByPattern takes a List<NumberFormat>; the metadata accessors hand out the other two), so they stay documented. - BuildMetadataFromXml is hidden but still public and callable. The supported route for custom metadata is PhoneNumberUtil.CreateInstance( Stream), which takes a plain Stream and drives the parser internally, so no consumer needs to name it. - LeniencyExtensions is an extension method on the documented Leniency enum - the C# stand-in for Java's Leniency.verify - so hiding it would leave Leniency looking like it has no verify at all. Reachability analysis structurally cannot see extension methods, so this one is a judgement call rather than a measurement. AGENTS.md said CountryCodeToRegionCodeMap.cs is generated and must not be hand-edited. Nothing regenerates it: its own header still reads "todo make this file automatically generated", and lib/github-actions-metadata-update.sh explicitly treats a change to it as hand-written content. Corrected, since this commit edits it.
86d84f7 to
a824a61
Compare
| "resource": [ | ||
| { | ||
| "//": "Shared with the Blazor demo, which publishes the same three files into its own wwwroot; kept at the repo root so the two sites cannot drift. Mapped to images/ so _appLogoPath and _appFaviconPath below stay site-relative.", | ||
| "src": "../assets/brand", |
There was a problem hiding this comment.
not sure about this but i think the demo doesn't references these assets/brand files at all
There was a problem hiding this comment.
Good catch — you're right, and the comment was just wrong. The demo has no favicon link at all and draws its rail logo from Components/Icons/, so docfx was the only thing reading those files.
Moved them to docfx/brand/, corrected the comment, and dropped the assets/brand/** path filters from deploy-demo.yml and docs_preview.yml since docfx/** now covers them. No more repo-root assets/ directory.
|
|
||
| C# port of Google's [libphonenumber](https://github.com/google/libphonenumber) — parse, format, validate, and geolocate international phone numbers. | ||
|
|
||
| <a class="docs-btn" href="xref:PhoneNumbers"> |
There was a problem hiding this comment.
Thanks — confirmed the cause: docfx replaces an <a> whose href is an xref: with its own generated anchor, which drops the docs-btn class and the arrow SVG.
Took your fix with one tweak: pointed it at api/PhoneNumbers.yml rather than api/PhoneNumbers.html. Both render the button correctly, but the literal .html trips an InvalidFileLink warning since docfx validates file links against source files — the .yml is the real source and docfx rewrites it to .html on build. Clean build is back to 0 warnings.
…enders as a button Two review comments from @wmundev: The `assets/brand/` comment claimed the SVGs were shared with the Blazor demo. They are not — the demo has no favicon link and draws its rail logo from `Components/Icons/`, so docfx was the only consumer of a repo-root directory presented as shared. Move them to `docfx/brand/`, correct the comment, and drop the now-redundant `assets/brand/**` path filters from both workflows, which `docfx/**` already covers. The hero link rendered as plain text rather than a button: docfx replaces an `<a>` whose href is an `xref:` with its own anchor, dropping the class and the arrow SVG. Point it at the generated `api/PhoneNumbers.yml` instead — docfx validates that link and rewrites it to `.html`, so the authored markup survives. The literal `.html` also works but trips an InvalidFileLink warning; the rationale lives in the frontmatter, which is stripped from the published page. Verified with a clean `./docfx/build.sh`: 0 warnings, the button keeps its class and SVG, and the three brand SVGs still publish to `_site/images/`.
#453 moved assets/brand/ to docfx/brand/ on the grounds that nothing outside docfx read it. That was true of main at the time, but this branch is what makes the demo read it — a Content item in PhoneNumbers.Demo.csproj links the same three SVGs into wwwroot/, and index.html swaps logo.svg/logo-dark.svg from the theme toggle. Merging the two as they stood produced two byte-identical copies and no single source of truth, with no merge conflict to flag it. Consolidate back onto assets/brand/ as both PRs originally intended: drop docfx/brand/, point docfx.json's resource src back at ../assets/brand, and restore the assets/brand/** path filters to deploy-demo.yml and docs_preview.yml so a change to the marks still rebuilds both sites. Correct the docfx.json and AGENTS.md comments to describe the arrangement that now actually exists. Verified: ./docfx/build.sh is clean (0 warnings) with the three SVGs in _site/images/ and the hero button intact, and a Release publish of the demo with PathBase=/libphonenumber-csharp/ lands all three in its wwwroot/.

Summary
A generated API reference site for both packable projects, built with DocFX from the XML doc comments they already emit, and themed with the demo's own design tokens so the two sites read as one product rather than two tools bolted together.
/docs/— one deployment, one domain, no separate hosting.docs/api-differences-from-java.mdbecomes a page rather than a link buried in the README.docfx/build.shcopies it in and rewrites its repo-relative links to GitHub permalinks, since the published site ships only rendered articles.filterConfig.ymldrops compiler-generated and[EditorBrowsable(Never)]members. Twelve public-but-internal types — metadata plumbing and prefix-map internals, unreachable from any consumer entry point — are now marked[EditorBrowsable(Never)]too, taking the reference from 43 documented types to 34. Package validation confirms that is not an API-compat break.Build it with
./docfx/build.sh, neverdocfxdirectly: the script restores what docfx's MSBuild pass needs and copies the articles in, so a local run, the preview workflow and the deploy all start from the same state.One deliberate coupling worth a reviewer's attention: the docs build in
deploy-demo.ymlis notcontinue-on-error. A Pages deployment replaces the whole site, so carrying on past a docs failure would publish awwwrootwith nodocs/in it and take/docsdown to a 404 rather than leaving it stale. Failing the job keeps the previous deployment serving, demo and docs together — at the cost of a docfx regression also blocking demo-only deploys.Reviewing it
docs_preview.ymluploads the rendered site as an artifact on every relevant PR — no merge or Pages publish needed. Downloadapi-docs-previewfrom the run summary, unzip, and serve it with any static server (python3 -m http.serverfrom inside the folder). Openingindex.htmloverfile://won't work; the site fetches its nav and search data over HTTP.Test plan
deploy-demo.ymlsucceeds and/docs/is live and themed