Skip to content

Add Nuxt module for frictionless spa setup#160

Merged
garronej merged 8 commits intokeycloakify:mainfrom
DPHonys:nuxt_module
Mar 1, 2026
Merged

Add Nuxt module for frictionless spa setup#160
garronej merged 8 commits intokeycloakify:mainfrom
DPHonys:nuxt_module

Conversation

@DPHonys
Copy link
Copy Markdown
Contributor

@DPHonys DPHonys commented Feb 25, 2026

Description

This PR adds a dedicated Nuxt module for oidc-spa, building on top of the existing Vite plugin support.

Problem

While the Vite plugin already handles Nuxt 3 and 4 projects, users still had to manually wire up oidc-spa through addVitePlugin in their project modules - an unusual pattern for a Nuxt ecosystem where modules are the standard integration point. There was no idiomatic, first-class way to configure oidc-spa in nuxt.config.ts.

Solution

Added a new oidc-spa/nuxt-spa export that provides a proper Nuxt module built with @nuxt/kit:

  • Uses defineNuxtModule with config key oidcSpa, so configuration lives in nuxt.config.ts under { oidcSpa: { ... } }
  • Enforces ssr: false at module setup time and throws a clear error if SSR is not disabled
  • Registers the Vite plugin client-side only via addVitePlugin(..., { client: true, server: false })
  • Exported as ESM-only (oidc-spa/nuxt-spa) and excluded from the CJS build, as Nuxt modules are ESM-native

Testing

Tested with Nuxt 4 in SPA mode (ssr: false) and verified that oidcEarlyInit() is called correctly via the Vite plugin and that the module configuration flows through properly from nuxt.config.ts.

Usage

// nuxt.config.ts
export default defineNuxtConfig({
  ssr: false,
  modules: ["oidc-spa/nuxt-spa"],
  oidcSpa: {
    // plugin options here
  }
});

Summary by CodeRabbit

  • New Features

    • Added a Nuxt 3 SPA module for client-side use that enforces non-SSR deployments and registers a client-only plugin.
  • Dependencies

    • Package exports updated to expose the new SPA entry.
    • Dev and peer dependency metadata updated to include Nuxt tooling entries and mark them optional for consumers.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 25, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a81b3fc and 0b5c94f.

📒 Files selected for processing (1)
  • src/nuxt-spa/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/nuxt-spa/index.ts

📝 Walkthrough

Walkthrough

Adds a Nuxt 3 SPA module that registers a client-only oidc-spa Vite plugin, exposes the module via a new ./nuxt-spa package export, and updates build/dev dependency config to support and exclude the SPA entry from the CJS build. (47 words)

Changes

Cohort / File(s) Summary
Package Configuration
package.json
Added root export ./nuxt-spa mapping types, module, import, and default to dist/esm/nuxt-spa/index.*; added @nuxt/kit and @nuxt/schema to devDependencies, peerDependencies, and peerDependenciesMeta (optional).
Build Configuration
scripts/build.ts
Added nuxt-spa to the cjs target exclude list so the nuxt-spa entry is not compiled into the CommonJS output.
Nuxt Module
src/nuxt-spa/index.ts
New Nuxt 3 module (oidc-spa, configKey oidcSpa) that enforces SSR disabled and registers the client-only Vite plugin via addVitePlugin, forwarding resolved options to oidcSpa.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Dev as Developer/Nuxt config
participant Nuxt as Nuxt runtime (module setup)
participant Vite as Vite
participant Plugin as oidcSpa Vite plugin

Dev->>Nuxt: enable module (configKey: oidcSpa)
Nuxt->>Nuxt: check ssr setting
alt ssr enabled
    Nuxt->>Dev: throw error (SSR must be disabled)
else ssr disabled
    Nuxt->>Vite: addVitePlugin(client: true, server: false, plugin: oidcSpa(options))
    Vite->>Plugin: initialize plugin for client build
    Plugin->>Vite: hook into client build/runtime

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I hopped a new module through the dell,
Client-side auth where single pages dwell,
Vite wears the plugin, SSR stays away,
Exports point to ESM to light the way,
Tiny paws dancing — a tidy dev day!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add Nuxt module for frictionless spa setup' accurately describes the main change - adding a new Nuxt module export for SPA configuration, which is the primary objective of this PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
package.json (1)

49-87: ⚠️ Potential issue | 🔴 Critical

Move @nuxt/kit from optional peerDependencies to dependencies.

The src/nuxt-spa/index.ts module imports @nuxt/kit directly at runtime (defineNuxtModule, addVitePlugin), making it a required dependency, not optional. Per Nuxt's official Kit guide, @nuxt/kit must be in dependencies, not peerDependencies. Placing it only as an optional peer will cause Nuxt consumers (especially pnpm users) to encounter "Cannot find module" errors unless they manually add @nuxt/kit to their own dependencies—defeating the purpose of module encapsulation.

Move @nuxt/kit (and @nuxt/schema) from peerDependencies into dependencies. Remove the optional metadata entries for both. Also ensure versions match or exceed the consumer's nuxt version (currently ^4.3.1 is appropriate).

Also applies to: 100-101

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

In `@package.json` around lines 49 - 87, The package.json currently lists
`@nuxt/kit` (and `@nuxt/schema`) as optional peerDependencies but
src/nuxt-spa/index.ts imports defineNuxtModule and addVitePlugin at runtime, so
move "@nuxt/kit" and "@nuxt/schema" from "peerDependencies" into "dependencies"
(using versions at or above the project's nuxt range, e.g. ^4.3.1), remove their
entries from "peerDependenciesMeta", and delete them from the peerDependencies
block; keep other peers unchanged so consumers no longer get "Cannot find
module" errors when importing src/nuxt-spa/index.ts.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@package.json`:
- Around line 49-87: The package.json currently lists `@nuxt/kit` (and
`@nuxt/schema`) as optional peerDependencies but src/nuxt-spa/index.ts imports
defineNuxtModule and addVitePlugin at runtime, so move "@nuxt/kit" and
"@nuxt/schema" from "peerDependencies" into "dependencies" (using versions at or
above the project's nuxt range, e.g. ^4.3.1), remove their entries from
"peerDependenciesMeta", and delete them from the peerDependencies block; keep
other peers unchanged so consumers no longer get "Cannot find module" errors
when importing src/nuxt-spa/index.ts.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 491297f and 10aa968.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (3)
  • package.json
  • scripts/build.ts
  • src/nuxt-spa/index.ts

@DPHonys DPHonys changed the title Nuxt module Add Nuxt module for frictionless spa setup Feb 25, 2026
Copy link
Copy Markdown
Collaborator

@garronej garronej left a comment

Choose a reason for hiding this comment

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

Thanks a lot! LGTM beside the listing of the peer deps

Comment thread package.json
Comment on lines +96 to +97
"@nuxt/kit": "^4.3.1",
"@nuxt/schema": "^4.3.1",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Don't we want that as optional peer dependency?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I moved them away from peerDependencies based on a (mis)reading of the Nuxt docs + the CodeRabbit review. After digging in a bit more, it’s clear these should be peerDependencies. Sorry about that. Hopefully now correctly done.

Copy link
Copy Markdown
Collaborator

@garronej garronej Feb 26, 2026

Choose a reason for hiding this comment

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

Actually, it doesn’t really matter.

If users are working with Nuxt, the dependency will already be available in their project.
We declare it because some package managers perform advanced checks and will emit warnings if a library imports a dependency that isn’t listed as either a dependency or a peerDependency. So this is mostly ceremonial.

Looking at your PR, it seems that only @nuxt/kit is explicitly imported.
Therefore, it should be the only peer dependency we declare.

@nuxt/schema can remain in devDependencies for local development purposes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed @nuxt/schema from the peer dependencies, but kept @nuxt/kit and @nuxt/schema at the same version in package.json, since Nuxt strongly recommends this for authoring Nuxt modules.

Copy link
Copy Markdown

@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)
package.json (1)

53-54: Consider constraining the peer dependency version range for @nuxt/kit / @nuxt/schema.

The wildcard "*" allows any version, including Nuxt 3-era @nuxt/kit (e.g. 3.x). The module was authored and tested against Nuxt 4; binding to ">=4.0.0" would prevent subtle breakage if a consumer happens to have a Nuxt 3 workspace installed alongside.

♻️ Suggested version constraint
-        "@nuxt/kit": "*",
-        "@nuxt/schema": "*",
+        "@nuxt/kit": ">=4.0.0",
+        "@nuxt/schema": ">=4.0.0",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` around lines 53 - 54, Change the peerDependency entries that
currently use "*" for `@nuxt/kit` and `@nuxt/schema` to a constrained Nuxt 4 range
(e.g. ">=4.0.0") so the package will not be satisfied by Nuxt 3 artifacts;
update the package.json peerDependencies for the symbols "@nuxt/kit" and
"@nuxt/schema" to use the new version range (or a stricter range like ">=4.0.0
<5.0.0") and run a quick install/test to confirm no regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@package.json`:
- Around line 53-54: Change the peerDependency entries that currently use "*"
for `@nuxt/kit` and `@nuxt/schema` to a constrained Nuxt 4 range (e.g. ">=4.0.0") so
the package will not be satisfied by Nuxt 3 artifacts; update the package.json
peerDependencies for the symbols "@nuxt/kit" and "@nuxt/schema" to use the new
version range (or a stricter range like ">=4.0.0 <5.0.0") and run a quick
install/test to confirm no regressions.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 58673d6 and 36be8fa.

📒 Files selected for processing (1)
  • package.json

Comment thread package.json
Comment on lines +96 to +97
"@nuxt/kit": "^4.3.1",
"@nuxt/schema": "^4.3.1",
Copy link
Copy Markdown
Collaborator

@garronej garronej Feb 26, 2026

Choose a reason for hiding this comment

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

Actually, it doesn’t really matter.

If users are working with Nuxt, the dependency will already be available in their project.
We declare it because some package managers perform advanced checks and will emit warnings if a library imports a dependency that isn’t listed as either a dependency or a peerDependency. So this is mostly ceremonial.

Looking at your PR, it seems that only @nuxt/kit is explicitly imported.
Therefore, it should be the only peer dependency we declare.

@nuxt/schema can remain in devDependencies for local development purposes.

Comment thread src/nuxt-spa/index.ts Outdated
import { defineNuxtModule, addVitePlugin } from "@nuxt/kit";
import { oidcSpa, type OidcSpaVitePluginParams } from "../vite-plugin";

export default defineNuxtModule<OidcSpaVitePluginParams>().with({
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Do we want an export default here?

In the future we might want to export the actual adapter for vue from this module.
In general I prefer named exports. But if you tell me that having a named export for this will break expectations in a major way, I trust your jugement.

Copy link
Copy Markdown
Contributor Author

@DPHonys DPHonys Feb 26, 2026

Choose a reason for hiding this comment

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

Nuxt expects the module entrypoint (the file resolved from modules: ["…"] in nuxt.config.ts) to expose the module as a default export. That’s what the Nuxt docs/examples use (export default defineNuxtModule(...)), and it’s what makes this work:

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ["oidc-spa/nuxt-spa"], // Nuxt loads the entry and uses its default export
});

If we switch the entry file to only named exports, the string-based resolution is likely to break / become nonstandard (users would have to import the module manually).

We can still satisfy the preference for named exports by exporting both if wanted:

export const oidcSpaNuxtModule = defineNuxtModule(...);
export default oidcSpaNuxtModule;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Exporting both now.

@DPHonys DPHonys mentioned this pull request Feb 26, 2026
@garronej
Copy link
Copy Markdown
Collaborator

garronej commented Mar 1, 2026

Hey @DPHonys,

So sorry for the delayed review. Merging now, looks solid!

@garronej garronej merged commit ef70120 into keycloakify:main Mar 1, 2026
6 checks passed
@garronej
Copy link
Copy Markdown
Collaborator

garronej commented Mar 1, 2026

I triggered the release of a new version: 10.1.0

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.

2 participants