Repository navigation
feat: require NestJS 12, reflect-metadata 0.2.2 and Vitest 5 - #80
Merged
Merged
Conversation
Move every NestJS peer to ^12.0.0, reflect-metadata to ^0.2.2 and vitest to 5.x. reflect-metadata 0.2.0 overflows the stack or loses decorator metadata when a second copy is loaded; 0.2.1 fixed that with a shared registry. Accept the fastify versions @nestjs/platform-fastify 12 pins (5.12.1 to 5.12.5), require @nestjs/throttler 6.7.1, the first release to accept Nest 12, and drop @nestjs/swagger, which nothing has imported since the zod-native OpenAPI generator replaced it. vite becomes a dev dependency, as Vitest 5 only declares it as a peer. Nest 12 loads its optional packages with literal import() calls, which esbuild resolves at build time, so `libnest build` now also leaves out @nestjs/platform-express, @fastify/multipart and the .js-suffixed @nestjs/websockets/socket-module.js. A new build test starts the bundled example app, proving Nest runs without them. Nest 12 no longer applies a parent's @optional() flags to a subclass that declares its own constructor, so LoggingService redeclares @optional() on its options and stays resolvable without LOGGING_MODULE_OPTIONS_TOKEN. The e2e testing helper now runs the app on the Fastify adapter, as production does, instead of Nest's default Express adapter, which needed @nestjs/platform-express installed and was only present here through @nestjs/swagger. Tests no longer rely on mock calls recorded in beforeAll, which Vitest 5 clears before each test now that clearMocks defaults to true. BREAKING CHANGE: requires @nestjs/common, @nestjs/core, @nestjs/platform-fastify and @nestjs/testing ^12.0.0, reflect-metadata ^0.2.2, fastify ^5.12.1 and vitest 5.x. The e2e helper from @douglasneuroinformatics/libnest/testing now runs the app on the Fastify adapter instead of Express. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Vitest 5, now a required peer, supports Node ^22.12.0 || ^24.0.0 || >=26.0.0, so the engines range no longer claims the 22.x releases before 22.12. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #80 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 64 64
Lines 838 836 -2
Branches 147 147
=========================================
- Hits 838 836 -2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
🎉 This PR is included in version 10.0.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This prepares libnest 10.0.0, which requires NestJS 12 and Vitest 5. It unblocks Open Data Capture's move to both. The commit carries a
BREAKING CHANGE:footer, so semantic-release will cut a major. I checked this locally with@semantic-release/commit-analyzerand theconventionalcommitspreset: the result ismajor.What changed
Peer and dependency ranges
@nestjs/common,core,platform-fastify,testing(peer)^11^12.0.0reflect-metadata(peer)~0.1.13^0.2.2isProviderFor, or silently loses metadata, when a second copy is loaded. 0.2.1 fixed this with a shared registry.vitest(peer)4.x5.xfastify(peer)^5.6.1^5.12.1@nestjs/platform-fastifypins fastify exactly: 5.12.1 (12.0.0–12.0.1), 5.12.4 (12.0.2–12.0.3), 5.12.5 (12.0.4–12.1.2).^5.12.1accepts every one of those, and the lockfile resolves a single fastify 5.12.5.@nestjs/throttler^6.5.0^6.7.1@nestjs/swagger^11.2.5@nestjs/platform-expresshere (see below).vitest,@vitest/coverage-v8(dev)^4.0.17^5.0.3vite(dev, new)^7.3.6viteonly as a required peer. Without it, pnpm reused a stale vite 6.3.3, which does not satisfy^6.4.0. This matches ODC's catalog.engines.node22.x || 24.x^22.12.0 || 24.x^22.12.0on the 22 lineThe lockfile has exactly one
reflect-metadata(0.2.2), one each of@nestjs/common,core,platform-fastifyandtesting(12.1.2), and onefastify(5.12.5).libnest build: externals for Nest 12's lazy importsNest 12 ships as ESM. It loads its optional packages with literal
import()calls such astryLoadPackage('@nestjs/platform-express', () => import('@nestjs/platform-express')). esbuild resolves these at build time, and the build fails when a package is absent. The externals list moves to an exportedOPTIONAL_EXTERNALSand gains three entries:@nestjs/platform-express.NestFactory.createnow always preloads it.@fastify/multipart. This is a new lazy import in platform-fastify 12.@nestjs/websockets/socket-module.js. Nest now imports the.jssubpath, so the old@nestjs/websockets/socket-moduleentry no longer matched.@nestjs/microservicesstill covers@nestjs/microservices/microservices-module.js, because esbuild treats a bare package name as a prefix.A new build test bundles the example app, checks that every optional specifier is left as a runtime
import(), then imports the bundle and starts the app withcreateApplicationInstance()andinit(). This proves Nest runs without those packages installed.LoggingServiceredeclares@Optional()Nest 12 no longer applies a parent's
@Optional()flags to a subclass that declares its own constructor.LoggingServiceextendsJSONLogger, so under Nest 11 its options parameter was optional by inheritance. It now redeclares@Optional()on that parameter, so it stays resolvable withoutLOGGING_MODULE_OPTIONS_TOKEN. A new test fails without it.INQUIRERalways resolves under Nest 12, so it needs no flag.AppModuleandJSONLoggerthemselves are not subclasses, so they are unaffected.The
e2etesting helper runs on Fastifye2e()from@douglasneuroinformatics/libnest/testingcalledcreateNestApplication()with no adapter, so it ran on Nest's default Express adapter, which needs@nestjs/platform-express. It now passesnew FastifyAdapter(appContainer.fastifyOptions)and waits for Fastify'sready(), so e2e tests run on the adapter production uses. Thecurrent-userandapp.factorytests were moved off Express the same way.Vitest 5
clearMocksnow defaults totrue, which cleared calls recorded inbeforeAll. Theapp.factoryCryptoService tests now assert on the provided instance. Theprisma.extensionstest snapshots thedefineExtensioncalls insidebeforeAll.testing/plugin),MockFactory/MockedInstance, the Prisma mocks andsrc/testing/setup.tsneeded no changes. They use no removed entry points (vitest/coverage,vitest/reporters, …), no non-top-levelvi.mock/vi.hoisted, and no worker IDs.clearMocksleaves mock implementations intact./* v8 ignore if */inbuild.tsis still needed under Vitest 5. Removing it drops branch coverage to 99.66%, so it stays, with its comment updated.Checked, no change needed
PipeTransform.ArgumentMetadatais now generic and gainsschema.ValidationPipe,ValidObjectIdPipeandParseSchemaPipetype-check unchanged and their tests pass.Migration notes for consumers
@nestjs/common,core,platform-fastifyandtestingto^12,reflect-metadatato^0.2.2,vitestand@vitest/*to 5.x, andfastifyto the version your@nestjs/platform-fastifypins. Installviteexplicitly if your package manager does not auto-install peers.reflect-metadata0.2.2 across the whole install. A leftover 0.1.x copy alongside 0.2.x can still drop decorator metadata.e2e()now runs your app on Fastify. Express-only middleware, or tests that relied on Express semantics, may need updating. Its body limit is Fastify's 1 MiB default unlessfastifyOptionssets one. Production keeps libnest's 50 MiB default.libnest buildno longer bundles@nestjs/platform-express,@fastify/multipartor@nestjs/websockets/socket-module.js, even when they are installed. If your own code imports any of them, the bundle will import it fromnode_modulesat runtime.clearMocks: true). Assertions on calls made inbeforeAllmust move into the test, or you can setclearMocks: false.Verification
Run from the repo root on the committed tree:
pnpm install --frozen-lockfile: lockfile up to datepnpm prisma:generate && pnpm lint: passpnpm test:coverage: 50 files and 230 tests pass, with 100% statements, branches, functions and linespnpm build: passpnpm example:build && pnpm example:start: the bundled example serves/docs/spec.json(200) and/docs(200, HTML).POST /catsreturns 201,GET /catsreturns the created cat, and an invalid body returns 400.pnpm example:dev --no-watch(libnest devon Nest 12's ESM packages):POST /catsreturns 201 and/docs/spec.jsonreturns 200.Against Open Data Capture
An npm-packed tarball of this branch was installed into a scratch worktree of Open Data Capture, with Nest 12.1.1,
reflect-metadata0.2.2 and Vitest 5.0.2 (12.1.2 and 5.0.3 were still inside ODC's 7-day release-age window):reflect-metadata(0.2.2), a single version of each Nest core package and a singlefastify. The production bundle contains onereflect-metadataregistry.pnpm lint: all 34 tasks pass.apitest. That coversMockFactory, the vitest plugin and the SWC transform. ODC needs three Vitest 5 changes of its own, none involving libnest: its project configs inherit the rootprojects, two tests assert on calls cleared byclearMocks, and V8 now counts??branches.libnest buildof the API, run withoutnode_modules, as in ODC's Docker image: the app boots and every route maps./spec.jsonreturns 200 with 39 paths, and a bad body returns 400. Setup, login, and group and user CRUD work, and a missing token returns 401.libnest dev: 276 of 277 pass. The one failure is a subject-graph test that then passed 5 out of 5 times on repeat.Note for reviewers
The commit header is
feat:rather thanfeat!:. The repo'scommit-msghook rejects!because commitlint 19.8 resolvesconventional-changelog-conventionalcommits9.3.1 and getsparserOpts: undefined, then falls back to a parser without!support. This is a pre-existing tooling issue. semantic-release still reads theBREAKING CHANGE:footer as a major.🤖 Generated with Claude Code