Skip to content

feat(build)!: ship an explicit JPMS module and vendor the country code enum - #474

Merged
dargmuesli merged 5 commits into
betafrom
feat/build/jpms
Sep 2, 2026
Merged

dargmuesli merged 5 commits into
betafrom
feat/build/jpms

Conversation

@dargmuesli

Copy link
Copy Markdown
Member

The jar becomes an explicit JPMS module. Until now it shipped as an automatic module named by the Automatic-Module-Name manifest entry, which gives consumers a stable name but nothing else: no exports, no declared dependencies, and no way for jlink to treat the library as a real module. module-info.java now declares all 38 packages as exports, marks Gson and both HttpComponents modules requires transitive because their types appear in this library's own public signatures, and opens the five model packages Gson populates reflectively. The module name is unchanged, so an existing requires se.michaelthelin.spotify; keeps working, and nothing changes for code on the classpath.

That forced the com.neovisionaries:nv-i18n dependency out. CountryCode is part of the public API, so a module would have to requires transitive it, but nv-i18n has neither a module descriptor nor an Automatic-Module-Name, so its name is derived from its file name. Publishing a descriptor that depends on a derived name breaks every consumer if the artifact is ever renamed or later gains a real one, and it keeps consumers from producing a clean jlink image. The maintained fork proposed in #467 has the same gap, so switching forks would not have helped.

The library only ever needed the alpha-2 string from it: 35 market(CountryCode) parameters, two country parameters, one CountryCode[] field, and one LanguageCode lookup inside an assert. se.michaelthelin.spotify.enums.CountryCode is generated from Locale.getISOCountries() and keeps the constant names, so callers change one import and nothing else. It carries XK on top of the JDK's list: checked against Spotify's own 186-market response and every available_markets array in the fixtures, that is the only code Spotify uses that the JDK omits. The one LanguageCode use was an assertion, and is now a Locale.getISOLanguages() check.

ModelObjectGson registers an adapter for the new enum alongside the others, which also makes it case-insensitive and nulls an unknown code rather than the object around it. Nothing covered CountryCode[] before, so there is a test for it; removing the registration fails it.

An explicit module also makes javadoc emit a module-scoped tree, moving every class page under a se.michaelthelin.spotify/ path segment. Since GitHub Pages has no server-side redirects, a root 404.html maps the old paths onto the new ones, which is one file rather than a stub per page. It takes effect when beta merges to main, at which point both the move and the redirect land together.

Verified against a real consumer rather than only in isolation: a downstream JPMS application builds and jlinks against this, and the resulting runtime image lists se.michaelthelin.spotify as a named module instead of folding it into a merged one.

Two things worth knowing that this PR does not change. Automatic-Module-Name stays in the manifest even though the descriptor supersedes it, because it costs one line and still covers tooling that reads the manifest but not module-info. And mvn package compiles examples/ into the module while -P release does not, so CI validates a module that is not quite the one that gets published; the published artifact is correct, but a module-info.java referencing an example package would pass CI and fail at release.

BREAKING CHANGE: market and country parameters take se.michaelthelin.spotify.enums.CountryCode instead of com.neovisionaries.i18n.CountryCode. The constant names are identical, so this is an import change.

nv-i18n has no module descriptor and no Automatic-Module-Name, so its module name is derived from its file name and cannot be required safely from an explicit module.
The jar now carries a real module-info instead of relying on the Automatic-Module-Name fallback, so consumers get exports, transitive requires and the reflective access Gson needs declared rather than inferred.
An explicit module makes javadoc emit a module-scoped tree, which moves every class page. GitHub Pages has no server-side redirects, so a root 404.html maps the old paths onto the new ones.
@codecov

codecov Bot commented Sep 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.84170% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 31.65%. Comparing base (f46781a) to head (749af37).

Files with missing lines Patch % Lines
...va/se/michaelthelin/spotify/enums/CountryCode.java 99.22% 1 Missing and 1 partial ⚠️
...ts/data/playlists/GetFeaturedPlaylistsRequest.java 0.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               beta     #474      +/-   ##
============================================
+ Coverage     29.66%   31.65%   +1.98%     
- Complexity      679      683       +4     
============================================
  Files           323      324       +1     
  Lines          8851     9109     +258     
  Branches        776      777       +1     
============================================
+ Hits           2626     2883     +257     
  Misses         5669     5669              
- Partials        556      557       +1     

☔ View full report in Codecov by Harness.
📢 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.

@dargmuesli
dargmuesli merged commit 384e973 into beta Sep 2, 2026
11 checks passed
@dargmuesli
dargmuesli deleted the feat/build/jpms branch September 2, 2026 00:44
@dargmuesli dargmuesli mentioned this pull request Sep 2, 2026
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