From 46640939559986ada9673b0a62cbd1346eed33c3 Mon Sep 17 00:00:00 2001 From: sergiochan Date: Wed, 11 Mar 2026 01:01:55 -0700 Subject: [PATCH 1/4] fix: add opt-in appendAutoPrefix for directory prefixes --- README.md | 15 +++++ index.js | 17 +++--- test/issues/205/routes/children/new-routes.js | 7 +++ test/issues/205/test.js | 60 +++++++++++++++++++ types/index.d.ts | 1 + 5 files changed, 93 insertions(+), 7 deletions(-) create mode 100644 test/issues/205/routes/children/new-routes.js create mode 100644 test/issues/205/test.js diff --git a/README.md b/README.md index de501881..02f85ce8 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,17 @@ fastify.register(autoLoad, { }) ``` +### `appendAutoPrefix` (optional) - Default: `false` + +When `true`, `plugin.autoPrefix` is appended to the directory-derived prefix (from `dirNameRoutePrefix`) instead of replacing it. + +```js +fastify.register(autoLoad, { + dir: path.join(__dirname, 'routes'), + appendAutoPrefix: true +}) +``` + ### `matchFilter` (optional) Filter matching any path that should be loaded. Can be a RegExp, a string, or a function returning a boolean. @@ -225,6 +236,8 @@ Any option specified here will override `plugin.autoConfig` options specified in When setting both `options.prefix` and `plugin.autoPrefix` they will be concatenated. +By default, `plugin.autoPrefix` replaces directory-derived prefixes. Set `appendAutoPrefix: true` to keep directory prefixes and append `plugin.autoPrefix` after them. + ```js // index.js fastify.register(autoLoad, { @@ -409,6 +422,8 @@ autoConfig.prefix = '/hello' Set routing prefix for plugin. +`plugin.autoPrefix` overrides directory-derived prefixes by default. Use the global `appendAutoPrefix` option to append `plugin.autoPrefix` to directory prefixes instead. + ```js module.exports = function (fastify, opts, next) { fastify.get('/', (request, reply) => { diff --git a/index.js b/index.js index ffaa2cbe..1d130676 100644 --- a/index.js +++ b/index.js @@ -11,7 +11,8 @@ const defaults = { indexPattern: /^index(?:\.ts|\.js|\.cjs|\.mjs|\.cts|\.mts)$/iu, autoHooksPattern: /^[_.]?auto_?hooks(?:\.ts|\.js|\.cjs|\.mjs|\.cts|\.mts)$/iu, dirNameRoutePrefix: true, - encapsulate: true + encapsulate: true, + appendAutoPrefix: false } const fastifyAutoload = async function autoload (fastify, options) { @@ -101,7 +102,7 @@ async function loadPlugin ({ file, type, directoryPrefix, options, log }) { plugin.autoConfig = undefined } - handlePrefixConfig({ plugin, pluginOptions, content, directoryPrefix }) + handlePrefixConfig({ plugin, pluginOptions, content, directoryPrefix, appendAutoPrefix: options.appendAutoPrefix }) return { plugin, @@ -258,16 +259,18 @@ function loadPluginOptions (content, overrideConfig) { return { ...pluginConfig, ...overrideConfig } } -function handlePrefixConfig ({ plugin, pluginOptions, content, directoryPrefix }) { +function handlePrefixConfig ({ plugin, pluginOptions, content, directoryPrefix, appendAutoPrefix }) { if (pluginOptions.prefix?.endsWith('/')) { pluginOptions.prefix = pluginOptions.prefix.slice(0, -1) } + const autoPrefix = plugin.autoPrefix ?? content.autoPrefix + let prefix - if (plugin.autoPrefix !== undefined) { - prefix = plugin.autoPrefix - } else if (content.autoPrefix !== undefined) { - prefix = content.autoPrefix + if (appendAutoPrefix && autoPrefix !== undefined && directoryPrefix) { + prefix = `${directoryPrefix}/${autoPrefix}` + } else if (autoPrefix !== undefined) { + prefix = autoPrefix } else { prefix = directoryPrefix } diff --git a/test/issues/205/routes/children/new-routes.js b/test/issues/205/routes/children/new-routes.js new file mode 100644 index 00000000..9a9d6750 --- /dev/null +++ b/test/issues/205/routes/children/new-routes.js @@ -0,0 +1,7 @@ +'use strict' + +module.exports = async function (app) { + app.get('/entity', async () => ({ ok: true })) +} + +module.exports.autoPrefix = '/batch' diff --git a/test/issues/205/test.js b/test/issues/205/test.js new file mode 100644 index 00000000..1c298251 --- /dev/null +++ b/test/issues/205/test.js @@ -0,0 +1,60 @@ +'use strict' + +const { after, before, describe, it } = require('node:test') +const assert = require('node:assert') +const path = require('node:path') +const Fastify = require('fastify') +const autoLoad = require('../../../') + +describe('Issue 205: append autoPrefix to directory prefixes without breaking defaults', function () { + describe('default behavior', function () { + const app = Fastify() + + before(async function () { + app.register(autoLoad, { + dir: path.join(__dirname, 'routes'), + options: { prefix: '/hooked-plugin' } + }) + await app.ready() + }) + + after(async function () { + await app.close() + }) + + it('keeps autoPrefix overriding directory prefixes by default', async function () { + const overridden = await app.inject({ method: 'GET', url: '/hooked-plugin/batch/entity' }) + assert.strictEqual(overridden.statusCode, 200) + assert.deepStrictEqual(overridden.json(), { ok: true }) + + const appended = await app.inject({ method: 'GET', url: '/hooked-plugin/children/batch/entity' }) + assert.strictEqual(appended.statusCode, 404) + }) + }) + + describe('appendAutoPrefix: true', function () { + const app = Fastify() + + before(async function () { + app.register(autoLoad, { + dir: path.join(__dirname, 'routes'), + options: { prefix: '/hooked-plugin' }, + appendAutoPrefix: true + }) + await app.ready() + }) + + after(async function () { + await app.close() + }) + + it('concatenates directory prefixes before plugin autoPrefix', async function () { + const appended = await app.inject({ method: 'GET', url: '/hooked-plugin/children/batch/entity' }) + assert.strictEqual(appended.statusCode, 200) + assert.deepStrictEqual(appended.json(), { ok: true }) + + const overridden = await app.inject({ method: 'GET', url: '/hooked-plugin/batch/entity' }) + assert.strictEqual(overridden.statusCode, 404) + }) + }) +}) diff --git a/types/index.d.ts b/types/index.d.ts index 3107dfae..cac1c6ba 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -9,6 +9,7 @@ declare namespace fastifyAutoload { export interface AutoloadPluginOptions { dir: string dirNameRoutePrefix?: boolean | RewritePrefix + appendAutoPrefix?: boolean ignoreFilter?: Filter matchFilter?: Filter ignorePattern?: RegExp From c5cf8993acaa6b475b00c9d452b161c703938579 Mon Sep 17 00:00:00 2001 From: sergiochan Date: Wed, 11 Mar 2026 13:01:59 -0700 Subject: [PATCH 2/4] docs: clarify appendAutoPrefix behavior with route examples --- README.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 02f85ce8..2f4a0e82 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,30 @@ fastify.register(autoLoad, { ### `appendAutoPrefix` (optional) - Default: `false` -When `true`, `plugin.autoPrefix` is appended to the directory-derived prefix (from `dirNameRoutePrefix`) instead of replacing it. +Controls how `autoPrefix` from a plugin file is combined with the directory-derived prefix. + +- `false` (default): `autoPrefix` replaces the directory-derived prefix for that plugin. +- `true`: the directory-derived prefix is kept, and `autoPrefix` is appended after it. + +Example directory: + +```text +routes/ + children/ + new-routes.js // exports autoPrefix = '/batch' +``` + +With default behavior (`appendAutoPrefix: false`), the route is loaded as: + +```text +/your-parent-prefix/batch/entity +``` + +With `appendAutoPrefix: true`, the route is loaded as: + +```text +/your-parent-prefix/children/batch/entity +``` ```js fastify.register(autoLoad, { From 4dba83c6febc18c3fca34fb2ac5e1e35a0be15fd Mon Sep 17 00:00:00 2001 From: Yuheng Date: Thu, 16 Jul 2026 10:58:33 -0700 Subject: [PATCH 3/4] test: simplify appendAutoPrefix cases Signed-off-by: Yuheng --- test/issues/205/test.js | 48 +++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/test/issues/205/test.js b/test/issues/205/test.js index 1c298251..bfe82717 100644 --- a/test/issues/205/test.js +++ b/test/issues/205/test.js @@ -1,60 +1,52 @@ 'use strict' -const { after, before, describe, it } = require('node:test') +const { describe, it } = require('node:test') const assert = require('node:assert') const path = require('node:path') const Fastify = require('fastify') const autoLoad = require('../../../') describe('Issue 205: append autoPrefix to directory prefixes without breaking defaults', function () { - describe('default behavior', function () { + it('keeps autoPrefix overriding directory prefixes by default', async function () { const app = Fastify() - before(async function () { - app.register(autoLoad, { - dir: path.join(__dirname, 'routes'), - options: { prefix: '/hooked-plugin' } - }) - await app.ready() + app.register(autoLoad, { + dir: path.join(__dirname, 'routes'), + options: { prefix: '/hooked-plugin' } }) + await app.ready() - after(async function () { - await app.close() - }) - - it('keeps autoPrefix overriding directory prefixes by default', async function () { + try { const overridden = await app.inject({ method: 'GET', url: '/hooked-plugin/batch/entity' }) assert.strictEqual(overridden.statusCode, 200) assert.deepStrictEqual(overridden.json(), { ok: true }) const appended = await app.inject({ method: 'GET', url: '/hooked-plugin/children/batch/entity' }) assert.strictEqual(appended.statusCode, 404) - }) + } finally { + await app.close() + } }) - describe('appendAutoPrefix: true', function () { + it('concatenates directory prefixes before plugin autoPrefix when enabled', async function () { const app = Fastify() - before(async function () { - app.register(autoLoad, { - dir: path.join(__dirname, 'routes'), - options: { prefix: '/hooked-plugin' }, - appendAutoPrefix: true - }) - await app.ready() + app.register(autoLoad, { + dir: path.join(__dirname, 'routes'), + options: { prefix: '/hooked-plugin' }, + appendAutoPrefix: true }) + await app.ready() - after(async function () { - await app.close() - }) - - it('concatenates directory prefixes before plugin autoPrefix', async function () { + try { const appended = await app.inject({ method: 'GET', url: '/hooked-plugin/children/batch/entity' }) assert.strictEqual(appended.statusCode, 200) assert.deepStrictEqual(appended.json(), { ok: true }) const overridden = await app.inject({ method: 'GET', url: '/hooked-plugin/batch/entity' }) assert.strictEqual(overridden.statusCode, 404) - }) + } finally { + await app.close() + } }) }) From 0653c09a5f2b788225e5d193fd9b67854a869cb0 Mon Sep 17 00:00:00 2001 From: Yuheng Date: Thu, 16 Jul 2026 12:37:51 -0700 Subject: [PATCH 4/4] test: use test cleanup hooks Signed-off-by: Yuheng --- test/issues/205/test.js | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/test/issues/205/test.js b/test/issues/205/test.js index bfe82717..7138d800 100644 --- a/test/issues/205/test.js +++ b/test/issues/205/test.js @@ -7,8 +7,9 @@ const Fastify = require('fastify') const autoLoad = require('../../../') describe('Issue 205: append autoPrefix to directory prefixes without breaking defaults', function () { - it('keeps autoPrefix overriding directory prefixes by default', async function () { + it('should keep autoPrefix overriding directory prefixes by default', async function (t) { const app = Fastify() + t.after(() => app.close()) app.register(autoLoad, { dir: path.join(__dirname, 'routes'), @@ -16,20 +17,17 @@ describe('Issue 205: append autoPrefix to directory prefixes without breaking de }) await app.ready() - try { - const overridden = await app.inject({ method: 'GET', url: '/hooked-plugin/batch/entity' }) - assert.strictEqual(overridden.statusCode, 200) - assert.deepStrictEqual(overridden.json(), { ok: true }) + const overridden = await app.inject({ method: 'GET', url: '/hooked-plugin/batch/entity' }) + assert.strictEqual(overridden.statusCode, 200) + assert.deepStrictEqual(overridden.json(), { ok: true }) - const appended = await app.inject({ method: 'GET', url: '/hooked-plugin/children/batch/entity' }) - assert.strictEqual(appended.statusCode, 404) - } finally { - await app.close() - } + const appended = await app.inject({ method: 'GET', url: '/hooked-plugin/children/batch/entity' }) + assert.strictEqual(appended.statusCode, 404) }) - it('concatenates directory prefixes before plugin autoPrefix when enabled', async function () { + it('should concatenate directory prefixes before plugin autoPrefix when appendAutoPrefix is true', async function (t) { const app = Fastify() + t.after(() => app.close()) app.register(autoLoad, { dir: path.join(__dirname, 'routes'), @@ -38,15 +36,11 @@ describe('Issue 205: append autoPrefix to directory prefixes without breaking de }) await app.ready() - try { - const appended = await app.inject({ method: 'GET', url: '/hooked-plugin/children/batch/entity' }) - assert.strictEqual(appended.statusCode, 200) - assert.deepStrictEqual(appended.json(), { ok: true }) + const appended = await app.inject({ method: 'GET', url: '/hooked-plugin/children/batch/entity' }) + assert.strictEqual(appended.statusCode, 200) + assert.deepStrictEqual(appended.json(), { ok: true }) - const overridden = await app.inject({ method: 'GET', url: '/hooked-plugin/batch/entity' }) - assert.strictEqual(overridden.statusCode, 404) - } finally { - await app.close() - } + const overridden = await app.inject({ method: 'GET', url: '/hooked-plugin/batch/entity' }) + assert.strictEqual(overridden.statusCode, 404) }) })