Skip to content

Commit de69ff9

Browse files
committed
feat(#1046): add automatic middleware
1 parent 8fa0d17 commit de69ff9

File tree

3 files changed

+171
-2
lines changed

3 files changed

+171
-2
lines changed

src/build/autoAddMiddleware.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Minimal interface to facilitate unit-testing
2+
export interface NuxtPage {
3+
meta?: Record<string, unknown>
4+
children?: NuxtPage[]
5+
}
6+
7+
/**
8+
* Adds `middleware: ['sidebase-auth']` to pages which use `definePageMeta({ auth: true })` or `definePageMeta({ auth: {} })`.
9+
* @see https://nuxt.com/docs/4.x/guide/directory-structure/app/middleware#setting-middleware-at-build-time
10+
*/
11+
export function autoAddMiddleware(pages: NuxtPage[], middlewareName: string) {
12+
for (const page of pages) {
13+
if (page.meta !== undefined && (page.meta.auth === true || typeof page.meta.auth === 'object')) {
14+
const previousMiddleware: unknown = page.meta.middleware
15+
let normalizedMiddleware: unknown[]
16+
17+
if (previousMiddleware === undefined) {
18+
normalizedMiddleware = []
19+
} else if (Array.isArray(previousMiddleware)) {
20+
normalizedMiddleware = previousMiddleware
21+
} else {
22+
normalizedMiddleware = [previousMiddleware]
23+
}
24+
25+
if (!normalizedMiddleware.includes(middlewareName)) {
26+
normalizedMiddleware.push(middlewareName)
27+
page.meta.middleware = normalizedMiddleware
28+
}
29+
}
30+
31+
if (page.children) {
32+
autoAddMiddleware(page.children, middlewareName)
33+
}
34+
}
35+
}

src/module.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import type {
2222
RefreshHandler,
2323
SupportedAuthProviders
2424
} from './runtime/types'
25+
import { autoAddMiddleware } from './build/autoAddMiddleware'
2526

2627
const topLevelDefaults = {
2728
isEnabled: true,
@@ -99,6 +100,7 @@ const defaultsByBackend: {
99100
}
100101

101102
const PACKAGE_NAME = 'sidebase-auth'
103+
const MIDDLEWARE_NAME = PACKAGE_NAME
102104

103105
export default defineNuxtModule<ModuleOptions>({
104106
meta: {
@@ -218,7 +220,6 @@ export default defineNuxtModule<ModuleOptions>({
218220
})
219221

220222
// 5.2. Create refresh handler
221-
// const generatedRefreshHandlerPath = resolve('./runtime/refreshHandler.ts')
222223
const generatedRefreshHandlerPath = addTemplate({
223224
filename: './refreshHandler.ts',
224225
async getContents() {
@@ -241,10 +242,15 @@ export default defineNuxtModule<ModuleOptions>({
241242

242243
// 6. Register middleware for autocomplete in definePageMeta
243244
addRouteMiddleware({
244-
name: 'sidebase-auth',
245+
name: MIDDLEWARE_NAME,
245246
path: resolve('./runtime/middleware/sidebase-auth')
246247
})
247248

249+
// 6.5. Automatically add the middleware when `definePageMeta({ auth: true })` usage is detected
250+
if (!options.globalAppMiddleware || !options.globalAppMiddleware.isEnabled) {
251+
nuxt.hook('pages:extend', (pages) => autoAddMiddleware(pages, MIDDLEWARE_NAME))
252+
}
253+
248254
// 7. Add plugin for initial load
249255
addPlugin(resolve('./runtime/plugin'))
250256

tests/autoAddMiddleware.spec.ts

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import { describe, it, expect } from 'vitest'
2+
import { autoAddMiddleware, type NuxtPage } from '../src/build/autoAddMiddleware'
3+
4+
const MIDDLEWARE_NAME = 'sidebase-auth'
5+
6+
describe('setMiddleware', () => {
7+
it('adds middleware if meta.auth is true', () => {
8+
testMiddleware({ meta: { auth: true } }, [MIDDLEWARE_NAME])
9+
})
10+
11+
it('adds middleware if meta.auth is object', () => {
12+
testMiddleware({ meta: { auth: {} } }, [MIDDLEWARE_NAME])
13+
testMiddleware({ meta: { auth: { navigateUnauthenticatedTo: '/' } } }, [MIDDLEWARE_NAME])
14+
})
15+
16+
it('ignores pages without meta.auth', () => {
17+
testMiddleware({}, undefined)
18+
testMiddleware({ meta: {} }, undefined)
19+
testMiddleware({ meta: { foo: 'bar' } }, undefined)
20+
testMiddleware({ meta: { auth2: 'foo' } }, undefined)
21+
testMiddleware({ meta: { middleware: 'foo' } }, 'foo')
22+
testMiddleware({ meta: { middleware: ['foo'] } }, ['foo'])
23+
const middlewareFunction = () => {}
24+
testMiddleware({ meta: { middleware: middlewareFunction } }, middlewareFunction)
25+
})
26+
27+
it('does not add when meta.auth is false', () => {
28+
testMiddleware({ meta: { auth: false } }, undefined)
29+
})
30+
31+
it('does not add when middleware is already present', () => {
32+
testMiddleware(
33+
{ meta: { auth: true, middleware: MIDDLEWARE_NAME } },
34+
MIDDLEWARE_NAME
35+
)
36+
testMiddleware(
37+
{ meta: { auth: true, middleware: [MIDDLEWARE_NAME] } },
38+
[MIDDLEWARE_NAME]
39+
)
40+
testMiddleware(
41+
{ meta: { auth: true, middleware: ['foo', MIDDLEWARE_NAME, 'bar'] } },
42+
['foo', MIDDLEWARE_NAME, 'bar']
43+
)
44+
testMiddleware(
45+
{ meta: { middleware: MIDDLEWARE_NAME } },
46+
MIDDLEWARE_NAME
47+
)
48+
testMiddleware(
49+
{ meta: { middleware: [MIDDLEWARE_NAME] } },
50+
[MIDDLEWARE_NAME]
51+
)
52+
testMiddleware(
53+
{ meta: { middleware: ['foo', MIDDLEWARE_NAME, 'bar'] } },
54+
['foo', MIDDLEWARE_NAME, 'bar']
55+
)
56+
})
57+
58+
it('adds to an existing array', () => {
59+
testMiddleware(
60+
{ meta: { auth: true, middleware: [] } },
61+
[MIDDLEWARE_NAME]
62+
)
63+
testMiddleware(
64+
{ meta: { auth: true, middleware: ['foo'] } },
65+
['foo', MIDDLEWARE_NAME]
66+
)
67+
})
68+
69+
it('wraps string middleware into array', () => {
70+
testMiddleware(
71+
{ meta: { auth: true, middleware: 'foo' } },
72+
['foo', MIDDLEWARE_NAME]
73+
)
74+
})
75+
76+
it('overrides undefined', () => {
77+
testMiddleware(
78+
{ meta: { auth: true, middleware: undefined } },
79+
[MIDDLEWARE_NAME]
80+
)
81+
})
82+
83+
it('wraps other middleware options into array', () => {
84+
const functionMiddleware = () => {}
85+
testMiddleware(
86+
{ meta: { auth: true, middleware: functionMiddleware } },
87+
[functionMiddleware, MIDDLEWARE_NAME]
88+
)
89+
})
90+
91+
it('handles multiple pages', () => {
92+
const pages: NuxtPage[] = [
93+
{ meta: { auth: true } },
94+
{ meta: { auth: true, middleware: 'foo' } }
95+
]
96+
97+
autoAddMiddleware(pages, MIDDLEWARE_NAME)
98+
99+
expect(pages[0].meta?.middleware).toEqual([MIDDLEWARE_NAME])
100+
expect(pages[1].meta?.middleware).toEqual(['foo', MIDDLEWARE_NAME])
101+
})
102+
103+
it('handles nested children', () => {
104+
const pages: NuxtPage[] = [
105+
{
106+
meta: {},
107+
children: [
108+
{ meta: { auth: true } }
109+
]
110+
}
111+
]
112+
113+
autoAddMiddleware(pages, MIDDLEWARE_NAME)
114+
115+
expect(pages[0].meta?.middleware).toBeUndefined()
116+
expect(pages[0].children?.[0].meta?.middleware).toEqual([MIDDLEWARE_NAME])
117+
})
118+
})
119+
120+
/**
121+
* Helper: test a single-page scenario
122+
*/
123+
function testMiddleware(page: NuxtPage, expected: unknown) {
124+
const pages: NuxtPage[] = [page]
125+
autoAddMiddleware(pages, MIDDLEWARE_NAME)
126+
expect(pages[0].meta?.middleware).toEqual(expected)
127+
}
128+

0 commit comments

Comments
 (0)