├── .borp.yaml ├── .gitattributes ├── .github ├── dependabot.yml ├── stale.yml └── workflows │ └── ci.yml ├── .gitignore ├── .npmrc ├── LICENSE ├── README.md ├── eslint.config.js ├── index.js ├── lib ├── find-plugins.js └── runtime.js ├── package.json ├── scripts ├── unit-typescript-esbuild.js ├── unit-typescript-esm.js ├── unit-typescript-native-type-stripping.js ├── unit-typescript-swc-node-register.js ├── unit-typescript-tsimp.js ├── unit-typescript-tsm.js └── unit-typescript-tsx.js ├── test ├── commonjs │ ├── autohooks-basic.js │ ├── autohooks-cascade.js │ ├── autohooks-disabled.js │ ├── autohooks-overwrite.js │ ├── autohooks │ │ ├── basic.js │ │ ├── cascade.js │ │ ├── disabled.js │ │ ├── overwrite.js │ │ └── routes │ │ │ ├── .autohooks.js │ │ │ ├── child │ │ │ ├── .autohooks.js │ │ │ ├── grandchild │ │ │ │ ├── .autohooks.js │ │ │ │ └── routes.js │ │ │ └── routes.js │ │ │ ├── routes.js │ │ │ └── sibling │ │ │ └── routes.js │ ├── babel-node.js │ ├── babel-node │ │ └── routes │ │ │ ├── foo │ │ │ └── index.ts │ │ │ └── root.ts │ ├── basic.js │ ├── basic │ │ ├── app.js │ │ ├── defaultPrefix │ │ │ ├── defaultPrefix.js │ │ │ ├── noDefaultPrefix.js │ │ │ ├── overridePrefix.js │ │ │ └── prefixed.js │ │ ├── encapsulate │ │ │ ├── plugin1.js │ │ │ └── plugin2.js │ │ ├── foo │ │ │ ├── a │ │ │ │ └── b │ │ │ │ │ └── c │ │ │ │ │ └── d.js │ │ │ ├── autoroute │ │ │ │ ├── get.js │ │ │ │ └── list.js │ │ │ ├── autowrap │ │ │ │ ├── get.js │ │ │ │ └── list.js │ │ │ ├── bar │ │ │ │ ├── index.js │ │ │ │ └── is-not-loaded.js │ │ │ ├── configPrefix.js │ │ │ ├── configPrefixCallback.js │ │ │ ├── decorator.js │ │ │ ├── exports-default.js │ │ │ ├── ignored.test.js │ │ │ ├── ignored │ │ │ │ └── index.js │ │ │ ├── manualprefix │ │ │ │ ├── get.js │ │ │ │ ├── index.js │ │ │ │ └── list.js │ │ │ ├── options.js │ │ │ ├── prefixed.js │ │ │ ├── skipAutoload.js │ │ │ ├── something.js │ │ │ └── something.sh │ │ ├── index-pattern │ │ │ ├── custom.js │ │ │ └── index.js │ │ ├── index │ │ │ ├── bar │ │ │ │ └── index.js │ │ │ ├── ignored.js │ │ │ └── index.js │ │ ├── one │ │ │ ├── index.js │ │ │ └── two │ │ │ │ └── index.js │ │ ├── rewrite-route-prefix │ │ │ ├── index.js │ │ │ └── two │ │ │ │ ├── index.js │ │ │ │ └── three │ │ │ │ ├── empty │ │ │ │ └── two │ │ │ │ │ └── index.js │ │ │ │ └── index.js │ │ ├── routeParams │ │ │ ├── foo │ │ │ │ ├── _id1 │ │ │ │ │ ├── bar │ │ │ │ │ │ └── _id2 │ │ │ │ │ │ │ └── index.js │ │ │ │ │ └── index.js │ │ │ │ └── index.js │ │ │ └── index.js │ │ └── skip │ │ │ └── noJS │ │ │ └── .gitignore │ ├── cyclic-dependency │ │ ├── app.js │ │ └── lib │ │ │ ├── a.js │ │ │ └── b.js │ ├── cyclic.js │ ├── deep.js │ ├── deep │ │ ├── app.js │ │ └── routes │ │ │ └── level-1 │ │ │ └── level-2 │ │ │ └── routes.js │ ├── dependency.js │ ├── dependency │ │ ├── app.js │ │ └── plugins │ │ │ ├── deep │ │ │ └── plugin-d.js │ │ │ ├── plugin-a.js │ │ │ ├── plugin-b.js │ │ │ ├── plugin-c.js │ │ │ ├── plugin-e.js │ │ │ ├── plugin-f.js │ │ │ └── plugin-g.js │ ├── error.js │ ├── graph-dependency.js │ ├── graph-dependency │ │ ├── app.js │ │ └── lib │ │ │ ├── a.js │ │ │ ├── b.js │ │ │ ├── c.js │ │ │ └── d.js │ ├── index-error │ │ ├── app.js │ │ └── package │ │ │ ├── answer.js │ │ │ └── package.json │ ├── index-package.js │ ├── index-package │ │ ├── app.js │ │ ├── foo │ │ │ └── bar.js │ │ ├── index.js │ │ └── package.json │ ├── module-error │ │ ├── app.js │ │ └── lib │ │ │ └── a.mjs │ ├── non-plugin.js │ ├── non-plugin │ │ ├── app.js │ │ └── lib │ │ │ ├── a-plugin.js │ │ │ └── non-plugin.js │ ├── options.js │ ├── options │ │ ├── app.js │ │ ├── lib-plugin.js │ │ ├── plugins-2 │ │ │ └── plugin-x.js │ │ ├── plugins-3 │ │ │ └── plugin-y.js │ │ └── plugins │ │ │ ├── plugin-a.js │ │ │ ├── plugin-b.js │ │ │ ├── plugin-c.js │ │ │ ├── plugin-d.js │ │ │ └── plugin-e.js │ ├── package.json │ ├── route-parameters-basic.js │ ├── route-parameters-disabled.js │ ├── route-parameters │ │ ├── basic.js │ │ ├── disabled.js │ │ └── routes │ │ │ ├── __country-__language │ │ │ └── actions.js │ │ │ └── users │ │ │ ├── _id │ │ │ └── actions.js │ │ │ └── index.js │ ├── syntax-error │ │ ├── app.js │ │ └── lib │ │ │ └── a.js │ ├── ts-error │ │ ├── app.js │ │ └── lib │ │ │ └── a.ts │ └── ts-node │ │ └── routes │ │ ├── bar │ │ └── index.ts │ │ ├── foo │ │ ├── baz │ │ │ └── index.ts │ │ └── index.ts │ │ └── root.ts ├── issues │ ├── 369 │ │ ├── invalid-autohooks │ │ │ └── .autohooks.js │ │ ├── invalid-index-type │ │ │ └── index.ts │ │ ├── non-SyntaxError │ │ │ └── .autohooks.js │ │ ├── routes │ │ │ ├── .autohooks.js │ │ │ ├── child │ │ │ │ ├── .autohooks.js │ │ │ │ └── routes.js │ │ │ ├── promisified │ │ │ │ ├── .autohooks.js │ │ │ │ └── routes.js │ │ │ └── routes.js │ │ └── test.js │ ├── 374 │ │ ├── routes │ │ │ ├── entity │ │ │ │ ├── .autohooks.js │ │ │ │ ├── _entity │ │ │ │ │ └── index.js │ │ │ │ └── index.js │ │ │ └── index.js │ │ └── test.js │ ├── 376 │ │ ├── routes │ │ │ ├── entity │ │ │ │ ├── .autohooks.js │ │ │ │ └── index.js │ │ │ └── index.js │ │ └── test.js │ ├── 388 │ │ ├── routes │ │ │ └── foo.tsx │ │ └── test.js │ ├── 453 │ │ ├── routes │ │ │ ├── autohooks.mjs │ │ │ ├── first │ │ │ │ ├── autohooks.mjs │ │ │ │ ├── first.route.mjs │ │ │ │ └── fourth │ │ │ │ │ ├── autohooks.mjs │ │ │ │ │ └── fourth.route.mjs │ │ │ ├── global.route.mjs │ │ │ └── second │ │ │ │ ├── autohooks.mjs │ │ │ │ └── second.route.mjs │ │ └── test.js │ └── 462 │ │ ├── routes │ │ ├── api.mjs │ │ ├── empty.cjs │ │ ├── empty.js │ │ ├── empty.mjs │ │ ├── hello.mjs │ │ └── object.mjs │ │ └── test.js ├── module │ ├── autohooks.js │ ├── autohooks │ │ ├── basic.mjs │ │ ├── cascade.mjs │ │ ├── disabled.mjs │ │ ├── overwrite.mjs │ │ └── routes │ │ │ ├── .autohooks.mjs │ │ │ ├── child │ │ │ ├── .autohooks.mjs │ │ │ ├── grandchild │ │ │ │ ├── .autohooks.mjs │ │ │ │ └── routes.mjs │ │ │ └── routes.mjs │ │ │ ├── routes.mjs │ │ │ └── sibling │ │ │ └── routes.mjs │ ├── basic.js │ ├── basic │ │ ├── app.js │ │ ├── defaultPrefix │ │ │ ├── defaultPrefix.js │ │ │ ├── noDefaultPrefix.js │ │ │ ├── overridePrefix.js │ │ │ └── prefixed.js │ │ ├── foo │ │ │ ├── autoroute │ │ │ │ ├── get.js │ │ │ │ └── list.js │ │ │ ├── autowrap │ │ │ │ ├── get.js │ │ │ │ └── list.js │ │ │ ├── bar │ │ │ │ └── index.js │ │ │ ├── commonjs.cjs │ │ │ ├── configPrefix.js │ │ │ ├── configPrefixCallback.js │ │ │ ├── ignored.test.js │ │ │ ├── ignored │ │ │ │ └── index.js │ │ │ ├── manualprefix │ │ │ │ ├── get.js │ │ │ │ ├── index.js │ │ │ │ └── list.js │ │ │ ├── module.mjs │ │ │ ├── options.js │ │ │ ├── prefixed.js │ │ │ ├── skipAutoload.js │ │ │ ├── something.js │ │ │ └── something.sh │ │ ├── nested │ │ │ └── shallow │ │ │ │ ├── deep │ │ │ │ ├── deeper │ │ │ │ │ └── index.js │ │ │ │ └── index.js │ │ │ │ └── index.js │ │ ├── one │ │ │ ├── index.js │ │ │ └── two │ │ │ │ └── index.js │ │ ├── skip │ │ │ └── noJS │ │ │ │ └── .gitignore │ │ └── ten │ │ │ ├── index.js │ │ │ └── nine │ │ │ └── index.js │ ├── dependency.js │ ├── dependency │ │ ├── app.js │ │ └── plugins │ │ │ ├── plugin-a.js │ │ │ ├── plugin-b.js │ │ │ ├── plugin-c.js │ │ │ ├── plugin-d.js │ │ │ ├── plugin-e.js │ │ │ ├── plugin-f.js │ │ │ └── plugin-g.js │ ├── esm-import.js │ ├── esm-import │ │ ├── app-default.js │ │ ├── app-named.js │ │ ├── app-star-default.js │ │ ├── app-star-named.js │ │ └── plugins │ │ │ ├── default │ │ │ └── index.js │ │ │ ├── named │ │ │ └── index.js │ │ │ ├── star-default │ │ │ └── index.js │ │ │ └── star-named │ │ │ └── index.js │ ├── index-package.js │ ├── index-package │ │ ├── app.js │ │ ├── foo │ │ │ └── bar.js │ │ ├── index.js │ │ └── package.json │ ├── options.js │ ├── options │ │ ├── app.js │ │ ├── lib-plugin.js │ │ ├── plugins-2 │ │ │ └── plugin-x.js │ │ ├── plugins-3 │ │ │ └── plugin-y.js │ │ └── plugins │ │ │ ├── plugin-a.js │ │ │ ├── plugin-b.js │ │ │ ├── plugin-c.js │ │ │ ├── plugin-d.js │ │ │ └── plugin-e.js │ ├── package.json │ ├── route-parameters.js │ └── route-parameters │ │ ├── basic.mjs │ │ ├── disabled.mjs │ │ └── routes │ │ ├── index.mjs │ │ ├── pages │ │ ├── _id │ │ │ └── actions.mjs │ │ └── routes.mjs │ │ └── users │ │ └── _id │ │ └── actions.mjs ├── typescript-common │ ├── basic │ │ ├── app.ts │ │ └── foo │ │ │ ├── javascript.js │ │ │ ├── shouldBeIgnored.d.ts │ │ │ └── typescript.ts │ └── index.ts ├── typescript-esm │ ├── app │ │ └── index.ts │ ├── forceESM.ts │ └── package.json ├── typescript-jest │ ├── babel-node │ │ └── index.test.ts │ ├── basic │ │ ├── app.ts │ │ ├── basic.test.ts │ │ └── foo │ │ │ └── typescript.ts │ └── integration │ │ ├── instance.ts │ │ └── integration.test.ts ├── typescript │ ├── basic.ts │ └── basic │ │ ├── app.ts │ │ └── foo │ │ ├── javascript.js │ │ ├── shouldBeIgnored.d.ts │ │ └── typescript.ts └── vitest │ ├── basic.test.ts │ └── filter.test.ts ├── tsconfig.tsimp.json ├── types ├── index.d.ts └── index.test-d.ts └── vitest.config.ts /.borp.yaml: -------------------------------------------------------------------------------- 1 | files: 2 | - 'test/issues/*/test.js' 3 | - 'test/commonjs/*.js' 4 | - 'test/module/*.js' 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set default behavior to automatically convert line endings 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | open-pull-requests-limit: 10 8 | 9 | - package-ecosystem: "npm" 10 | directory: "/" 11 | schedule: 12 | interval: "monthly" 13 | open-pull-requests-limit: 10 14 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 15 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - "discussion" 8 | - "feature request" 9 | - "bug" 10 | - "help wanted" 11 | - "plugin suggestion" 12 | - "good first issue" 13 | # Label to use when marking an issue as stale 14 | staleLabel: stale 15 | # Comment to post when marking an issue as stale. Set to `false` to disable 16 | markComment: > 17 | This issue has been automatically marked as stale because it has not had 18 | recent activity. It will be closed if no further activity occurs. Thank you 19 | for your contributions. 20 | # Comment to post when closing a stale issue. Set to `false` to disable 21 | closeComment: false 22 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - next 8 | - 'v*' 9 | paths-ignore: 10 | - 'docs/**' 11 | - '*.md' 12 | pull_request: 13 | paths-ignore: 14 | - 'docs/**' 15 | - '*.md' 16 | 17 | # This allows a subsequently queued workflow run to interrupt previous runs 18 | concurrency: 19 | group: "${{ github.workflow }}-${{ github.event.pull_request.head.label || github.head_ref || github.ref }}" 20 | cancel-in-progress: true 21 | 22 | permissions: 23 | contents: read 24 | 25 | jobs: 26 | test: 27 | permissions: 28 | contents: write 29 | pull-requests: write 30 | uses: fastify/workflows/.github/workflows/plugins-ci.yml@v5 31 | with: 32 | license-check: true 33 | lint: true 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | .env 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # vuepress v2.x temp and cache directory 104 | .temp 105 | .cache 106 | 107 | # Docusaurus cache and generated files 108 | .docusaurus 109 | 110 | # Serverless directories 111 | .serverless/ 112 | 113 | # FuseBox cache 114 | .fusebox/ 115 | 116 | # DynamoDB Local files 117 | .dynamodb/ 118 | 119 | # TernJS port file 120 | .tern-port 121 | 122 | # Stores VSCode versions used for testing VSCode extensions 123 | .vscode-test 124 | 125 | # yarn v2 126 | .yarn/cache 127 | .yarn/unplugged 128 | .yarn/build-state.yml 129 | .yarn/install-state.gz 130 | .pnp.* 131 | 132 | # Vim swap files 133 | *.swp 134 | 135 | # macOS files 136 | .DS_Store 137 | 138 | # Clinic 139 | .clinic 140 | 141 | # lock files 142 | bun.lockb 143 | package-lock.json 144 | pnpm-lock.yaml 145 | yarn.lock 146 | 147 | # editor files 148 | .vscode 149 | .idea 150 | 151 | #tap files 152 | .tap/ 153 | 154 | # tsimp cache 155 | .tsimp/ 156 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | ignore-scripts=true 2 | package-lock=false 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-present The Fastify team 4 | 5 | The Fastify team members are listed at https://github.com/fastify/fastify#team. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = require('neostandard')({ 4 | ignores: [ 5 | ...require('neostandard').resolveIgnoresFromGitignore(), 6 | 'test/commonjs/syntax-error/lib/a.js', 7 | 'test/issues/369/invalid-autohooks', 8 | 'test/issues/369/non-SyntaxError' 9 | ], 10 | ts: true 11 | }) 12 | -------------------------------------------------------------------------------- /lib/find-plugins.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { readdir } = require('node:fs/promises') 4 | const { relative, join } = require('node:path') 5 | const runtime = require('./runtime') 6 | 7 | async function findPlugins (dir, options) { 8 | const { opts, prefix } = options 9 | 10 | const pluginTree = { 11 | [dir]: { hooks: [], plugins: [] } 12 | } 13 | 14 | await buildTree(pluginTree, dir, { prefix, opts, depth: 0, hooks: [] }) 15 | 16 | return pluginTree 17 | } 18 | 19 | async function buildTree (pluginTree, dir, { prefix, opts, depth, hooks }) { 20 | // check to see if hooks or plugins have been added to this prefix, initialize if not 21 | if (!pluginTree[dir]) { 22 | pluginTree[dir] = { hooks: [], plugins: [] } 23 | } 24 | 25 | const dirEntries = await readdir(dir, { withFileTypes: true }) 26 | 27 | const currentDirHooks = findCurrentDirHooks(pluginTree, { dir, dirEntries, hooks, opts }) 28 | 29 | const { indexDirEntry, hasNoDirectory } = processIndexDirEntryIfExists(pluginTree, { dirEntries, opts, dir, prefix }) 30 | if (hasNoDirectory) { 31 | return 32 | } 33 | 34 | // Contains package.json but no index.js file? 35 | const packageDirEntry = dirEntries.find((dirEntry) => dirEntry.name === 'package.json') 36 | if (packageDirEntry && !indexDirEntry) { 37 | throw new Error(`@fastify/autoload cannot import plugin at '${dir}'. To fix this error rename the main entry file to 'index.js' (or .cjs, .mjs, .ts).`) 38 | } 39 | 40 | // Otherwise treat each script file as a plugin 41 | await processDirContents(pluginTree, { dirEntries, opts, indexDirEntry, prefix, dir, depth, currentDirHooks }) 42 | } 43 | 44 | function findCurrentDirHooks (pluginTree, { dir, dirEntries, hooks, opts }) { 45 | if (!opts.autoHooks) return [] 46 | 47 | let currentDirHooks = [] 48 | // Hooks were passed in, create new array specific to this plugin item 49 | for (const hook of hooks) { 50 | currentDirHooks.push(hook) 51 | } 52 | 53 | // Contains autohooks file? 54 | const autoHooks = dirEntries.find((dirEntry) => opts.autoHooksPattern.test(dirEntry.name)) 55 | if (autoHooks) { 56 | const file = join(dir, autoHooks.name) 57 | const { type } = getScriptType(file, opts.packageType) 58 | 59 | // Overwrite current hooks? 60 | if (opts.overwriteHooks && currentDirHooks.length > 0) { 61 | currentDirHooks = [] 62 | } 63 | 64 | // Add hook to current chain 65 | currentDirHooks.push({ file, type }) 66 | } 67 | 68 | pluginTree[dir].hooks = currentDirHooks 69 | 70 | return currentDirHooks 71 | } 72 | 73 | function processIndexDirEntryIfExists (pluginTree, { opts, dirEntries, dir, prefix }) { 74 | // Contains index file? 75 | const indexDirEntry = dirEntries.find((dirEntry) => opts.indexPattern.test(dirEntry.name)) 76 | if (!indexDirEntry) return { indexDirEntry } 77 | 78 | const file = join(dir, indexDirEntry.name) 79 | const { language, type } = getScriptType(file, opts.packageType) 80 | handleTypeScriptSupport(file, language, true) 81 | accumulatePlugin({ dir, file, type, opts, pluginTree, prefix }) 82 | 83 | const hasNoDirectory = dirEntries.every((dirEntry) => !dirEntry.isDirectory()) 84 | 85 | return { indexDirEntry, hasNoDirectory } 86 | } 87 | 88 | async function processDirContents (pluginTree, { dirEntries, opts, indexDirEntry, prefix, dir, depth, currentDirHooks }) { 89 | for (const dirEntry of dirEntries) { 90 | if (opts.ignorePattern && RegExp(opts.ignorePattern).test(dirEntry.name)) { 91 | continue 92 | } 93 | 94 | const atMaxDepth = Number.isFinite(opts.maxDepth) && opts.maxDepth <= depth 95 | const file = join(dir, dirEntry.name) 96 | if (dirEntry.isDirectory() && !atMaxDepth) { 97 | await processDirectory(pluginTree, { prefix, opts, dirEntry, dir, file, depth, currentDirHooks }) 98 | } else if (indexDirEntry) { 99 | // An index.js file is present in the directory so we ignore the others modules (but not the subdirectories) 100 | } else if (dirEntry.isFile() && opts.scriptPattern.test(dirEntry.name)) { 101 | processFile(pluginTree, { dir, file, opts, dirEntry, pluginTree, prefix }) 102 | } 103 | } 104 | } 105 | 106 | async function processDirectory (pluginTree, { prefix, opts, dirEntry, dir, file, depth, currentDirHooks }) { 107 | let prefixBreadCrumb = (prefix ? `${prefix}/` : '/') 108 | if (opts.dirNameRoutePrefix === true) { 109 | prefixBreadCrumb += dirEntry.name 110 | } else if (typeof opts.dirNameRoutePrefix === 'function') { 111 | const prefixReplacer = opts.dirNameRoutePrefix(dir, dirEntry.name) 112 | if (prefixReplacer) { 113 | prefixBreadCrumb += prefixReplacer 114 | } 115 | } 116 | 117 | // Pass hooks forward to next level 118 | const hooks = opts.autoHooks && opts.cascadeHooks ? currentDirHooks : [] 119 | await buildTree(pluginTree, file, { opts, prefix: prefixBreadCrumb, depth: depth + 1, hooks }) 120 | } 121 | 122 | function processFile (pluginTree, { dir, file, opts, dirEntry, prefix }) { 123 | const { language, type } = getScriptType(file, opts.packageType) 124 | handleTypeScriptSupport(file, language) 125 | 126 | // Don't place hook in plugin queue 127 | if (!opts.autoHooksPattern.test(dirEntry.name)) { 128 | accumulatePlugin({ dir, file, type, opts, pluginTree, prefix }) 129 | } 130 | } 131 | 132 | function accumulatePlugin ({ dir, file, type, opts, pluginTree, prefix }) { 133 | // Replace backward slash to forward slash for consistent behavior between windows and posix. 134 | const filePath = '/' + relative(opts.dir, file).replace(/\\/gu, '/') 135 | if (opts.matchFilter && !filterPath(filePath, opts.matchFilter)) { 136 | return 137 | } 138 | 139 | if (opts.ignoreFilter && filterPath(filePath, opts.ignoreFilter)) { 140 | return 141 | } 142 | 143 | pluginTree[dir].plugins.push({ file, type, prefix }) 144 | } 145 | 146 | function handleTypeScriptSupport (file, language, isHook = false) { 147 | if ( 148 | language === 'typescript' && 149 | /* c8 ignore start - This cannot be reached from Node 23 native type-stripping */ 150 | !runtime.supportTypeScript 151 | ) { 152 | throw new Error( 153 | `@fastify/autoload cannot import ${isHook ? 'hooks ' : ''}plugin at '${file}'. To fix this error compile TypeScript to JavaScript or use 'ts-node' to run your app.` 154 | ) 155 | } 156 | /* c8 ignore end */ 157 | } 158 | 159 | function filterPath (path, filter) { 160 | if (typeof filter === 'string') { 161 | return path.includes(filter) 162 | } 163 | 164 | if (filter instanceof RegExp) { 165 | return filter.test(path) 166 | } 167 | 168 | return filter(path) 169 | } 170 | 171 | const typescriptPattern = /\.(?:ts|mts|cts)$/iu 172 | function getScriptType (fname, packageType) { 173 | return { 174 | language: typescriptPattern.test(fname) ? 'typescript' : 'javascript', 175 | type: determineModuleType(fname, packageType) 176 | } 177 | } 178 | 179 | const modulePattern = /\.(?:mjs|mts)$/iu 180 | const commonjsPattern = /\.(?:cjs|cts)$/iu 181 | function determineModuleType (fname, defaultType) { 182 | if (modulePattern.test(fname)) { 183 | return 'module' 184 | } 185 | 186 | if (commonjsPattern.test(fname)) { 187 | return 'commonjs' 188 | } 189 | 190 | return defaultType || 'commonjs' 191 | } 192 | 193 | module.exports = findPlugins 194 | -------------------------------------------------------------------------------- /lib/runtime.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // runtime cache 4 | const cache = {} 5 | 6 | let processArgv 7 | function checkProcessArgv (moduleName) { 8 | /* c8 ignore start */ 9 | // nullish needed for non Node.js runtime 10 | processArgv ??= (process.execArgv ?? []).concat(process.argv ?? []) 11 | /* c8 ignore stop */ 12 | return processArgv.some((arg) => arg.indexOf(moduleName) >= 0) 13 | } 14 | 15 | let preloadModules 16 | function checkPreloadModules (moduleName) { 17 | /* c8 ignore start */ 18 | // nullish needed for non Node.js runtime 19 | preloadModules ??= (process._preload_modules ?? []) 20 | /* c8 ignore stop */ 21 | return preloadModules.includes(moduleName) 22 | } 23 | 24 | let preloadModulesString 25 | function checkPreloadModulesString (moduleName) { 26 | preloadModulesString ??= preloadModules.toString() 27 | return preloadModulesString.includes(moduleName) 28 | } 29 | 30 | function checkEnvVariable (name, value) { 31 | return value 32 | ? process.env[name] === value 33 | : process.env[name] !== undefined 34 | } 35 | 36 | const runtime = {} 37 | // use Object.defineProperties to provide lazy load 38 | Object.defineProperties(runtime, { 39 | tsNode: { 40 | get () { 41 | cache.tsNode ??= ( 42 | // --require tsnode/register 43 | (Symbol.for('ts-node.register.instance') in process) || 44 | // --loader ts-node/esm 45 | checkProcessArgv('ts-node/esm') || 46 | // ts-node-dev 47 | !!process.env.TS_NODE_DEV 48 | ) 49 | return cache.tsNode 50 | } 51 | }, 52 | babelNode: { 53 | get () { 54 | cache.babelNode ??= checkProcessArgv('babel-node') 55 | return cache.babelNode 56 | } 57 | }, 58 | vitest: { 59 | get () { 60 | cache.vitest ??= ( 61 | checkEnvVariable('VITEST', 'true') || 62 | checkEnvVariable('VITEST_WORKER_ID') 63 | ) 64 | return cache.vitest 65 | } 66 | }, 67 | jest: { 68 | get () { 69 | cache.jest ??= checkEnvVariable('JEST_WORKER_ID') 70 | return cache.jest 71 | } 72 | }, 73 | swc: { 74 | get () { 75 | cache.swc ??= ( 76 | checkPreloadModules('@swc/register') || 77 | checkPreloadModules('@swc-node/register') || 78 | checkProcessArgv('.bin/swc-node') 79 | ) 80 | return cache.swc 81 | } 82 | }, 83 | tsm: { 84 | get () { 85 | cache.tsm ??= checkPreloadModules('tsm') 86 | return cache.tsm 87 | } 88 | }, 89 | esbuild: { 90 | get () { 91 | cache.esbuild ??= checkPreloadModules('esbuild-register') 92 | return cache.esbuild 93 | } 94 | }, 95 | tsx: { 96 | get () { 97 | cache.tsx ??= checkPreloadModulesString('tsx') 98 | return cache.tsx 99 | } 100 | }, 101 | tsimp: { 102 | get () { 103 | cache.tsimp ??= checkProcessArgv('tsimp/import') 104 | return cache.tsimp 105 | } 106 | }, 107 | supportTypeScript: { 108 | get () { 109 | cache.supportTypeScript ??= ( 110 | checkEnvVariable('FASTIFY_AUTOLOAD_TYPESCRIPT') || 111 | runtime.tsNode || 112 | runtime.vitest || 113 | runtime.babelNode || 114 | runtime.jest || 115 | runtime.swc || 116 | runtime.tsm || 117 | runtime.tsx || 118 | runtime.esbuild || 119 | runtime.tsimp || 120 | runtime.supportNativeTypeScript 121 | ) 122 | return cache.supportTypeScript 123 | } 124 | }, 125 | supportNativeTypeScript: { 126 | get () { 127 | cache.supportNativeTypeScript ??= 128 | /* c8 ignore start - The right-hand part of the branch is unreachable on v20 for the moment */ 129 | process.features.typescript !== undefined && process.features.typescript !== false 130 | /* c8 ignore end */ 131 | return cache.supportNativeTypeScript 132 | } 133 | }, 134 | forceESM: { 135 | get () { 136 | cache.forceESM ??= ( 137 | checkProcessArgv('ts-node/esm') || 138 | runtime.vitest || 139 | false 140 | ) 141 | return cache.forceESM 142 | } 143 | }, 144 | }) 145 | 146 | module.exports = runtime 147 | module.exports.runtime = runtime 148 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fastify/autoload", 3 | "version": "6.3.1", 4 | "description": "Require all plugins in a directory", 5 | "main": "index.js", 6 | "type": "commonjs", 7 | "types": "types/index.d.ts", 8 | "scripts": { 9 | "lint": "eslint", 10 | "lint:fix": "eslint --fix", 11 | "test": "npm run typescript && npm run typescript:native && npm run typescript:jest && npm run typescript:swc-node-register && npm run typescript:tsm && npm run typescript:tsx && npm run typescript:vitest && npm run typescript:esbuild && npm run unit", 12 | "typescript": "tsd", 13 | "typescript:jest": "jest", 14 | "typescript:esm": "node scripts/unit-typescript-esm.js", 15 | "typescript:swc-node-register": "node scripts/unit-typescript-swc-node-register.js", 16 | "typescript:tsm": "node scripts/unit-typescript-tsm.js", 17 | "typescript:tsx": "node scripts/unit-typescript-tsx.js", 18 | "typescript:tsimp": "node scripts/unit-typescript-tsimp.js", 19 | "typescript:esbuild": "node scripts/unit-typescript-esbuild.js", 20 | "typescript:native": "node scripts/unit-typescript-native-type-stripping.js", 21 | "typescript:vitest": "vitest run", 22 | "typescript:vitest:dev": "vitest", 23 | "unit": "borp -C --check-coverage --lines 100 --reporter=@jsumners/line-reporter" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "git+https://github.com/fastify/fastify-autoload.git" 28 | }, 29 | "keywords": [ 30 | "fastify", 31 | "require", 32 | "folder", 33 | "directory", 34 | "plugin", 35 | "plugins", 36 | "automatically", 37 | "load", 38 | "auto" 39 | ], 40 | "author": "Matteo Collina ", 41 | "contributors": [ 42 | { 43 | "name": "Tomas Della Vedova", 44 | "url": "http://delved.org" 45 | }, 46 | { 47 | "name": "Manuel Spigolon", 48 | "email": "behemoth89@gmail.com" 49 | }, 50 | { 51 | "name": "Aras Abbasi", 52 | "email": "aras.abbasi@gmail.com" 53 | }, 54 | { 55 | "name": "Frazer Smith", 56 | "email": "frazer.dev@icloud.com", 57 | "url": "https://github.com/fdawgs" 58 | } 59 | ], 60 | "license": "MIT", 61 | "bugs": { 62 | "url": "https://github.com/fastify/fastify-autoload/issues" 63 | }, 64 | "homepage": "https://github.com/fastify/fastify-autoload#readme", 65 | "funding": [ 66 | { 67 | "type": "github", 68 | "url": "https://github.com/sponsors/fastify" 69 | }, 70 | { 71 | "type": "opencollective", 72 | "url": "https://opencollective.com/fastify" 73 | } 74 | ], 75 | "devDependencies": { 76 | "@fastify/url-data": "^6.0.0", 77 | "@jsumners/line-reporter": "^1.0.1", 78 | "@swc-node/register": "^1.9.1", 79 | "@swc/core": "^1.5.25", 80 | "@types/jest": "^30.0.0", 81 | "@types/node": "^24.0.8", 82 | "borp": "^0.21.0", 83 | "esbuild": "^0.25.0", 84 | "esbuild-register": "^3.5.0", 85 | "eslint": "^9.17.0", 86 | "fastify": "^5.0.0", 87 | "fastify-plugin": "^5.0.0", 88 | "jest": "^30.0.3", 89 | "neostandard": "^0.12.0", 90 | "ts-jest": "^29.1.4", 91 | "ts-node": "^10.9.2", 92 | "ts-node-dev": "^2.0.0", 93 | "tsd": "^0.33.0", 94 | "tsimp": "^2.0.11", 95 | "tsm": "^2.3.0", 96 | "tsx": "^4.15.7", 97 | "typescript": "5.5", 98 | "vite": "^7.0.0", 99 | "vitest": "^4.0.6" 100 | }, 101 | "jest": { 102 | "preset": "ts-jest", 103 | "testEnvironment": "node", 104 | "rootDir": "./test/typescript-jest", 105 | "testMatch": [ 106 | "**/*.test.ts" 107 | ], 108 | "transform": { 109 | "^.+\\.(ts|tsx)$": "ts-jest" 110 | } 111 | }, 112 | "publishConfig": { 113 | "access": "public" 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /scripts/unit-typescript-esbuild.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { exec } = require('node:child_process') 4 | 5 | const args = [ 6 | 'node', 7 | '--require=esbuild-register', 8 | 'test/typescript/basic.ts' 9 | ] 10 | const child = exec(args.join(' '), { 11 | shell: true 12 | }) 13 | 14 | child.stdout.pipe(process.stdout) 15 | child.stderr.pipe(process.stderr) 16 | child.once('close', process.exit) 17 | -------------------------------------------------------------------------------- /scripts/unit-typescript-esm.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { exec } = require('node:child_process') 4 | 5 | const args = [ 6 | 'node', 7 | '--loader=ts-node/esm', 8 | '--experimental-specifier-resolution=node', 9 | '--test', 10 | 'test/typescript-esm/*.ts' 11 | ] 12 | 13 | const child = exec(args.join(' '), { 14 | shell: true, 15 | env: { 16 | ...process.env, 17 | TS_NODE_COMPILER_OPTIONS: JSON.stringify({ 18 | module: 'ESNext', 19 | target: 'ES2020', 20 | allowJs: false, 21 | moduleResolution: 'node', 22 | esModuleInterop: true 23 | }) 24 | } 25 | }) 26 | 27 | child.stdout.pipe(process.stdout) 28 | child.stderr.pipe(process.stderr) 29 | child.once('close', process.exit) 30 | -------------------------------------------------------------------------------- /scripts/unit-typescript-native-type-stripping.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { exec } = require('node:child_process') 4 | const runtime = require('../lib/runtime') 5 | 6 | if (runtime.supportNativeTypeScript) { 7 | common() 8 | } 9 | 10 | function common () { 11 | const args = ['node', 'test/typescript-common/index.ts'] 12 | const child = exec(args.join(' '), { 13 | shell: true, 14 | }) 15 | child.stdout.pipe(process.stdout) 16 | child.stderr.pipe(process.stderr) 17 | child.once('close', esm) 18 | } 19 | 20 | function esm () { 21 | const args = ['node', 'test/typescript-esm/forceESM.ts'] 22 | 23 | const child = exec(args.join(' '), { 24 | shell: true, 25 | }) 26 | 27 | child.stdout.pipe(process.stdout) 28 | child.stderr.pipe(process.stderr) 29 | child.once('close', process.exit) 30 | } 31 | -------------------------------------------------------------------------------- /scripts/unit-typescript-swc-node-register.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { exec } = require('node:child_process') 4 | 5 | const args = [ 6 | 'node', 7 | '--require=@swc-node/register', 8 | 'test/typescript/basic.ts' 9 | ] 10 | 11 | const child = exec(args.join(' '), { 12 | shell: true 13 | }) 14 | 15 | child.stdout.pipe(process.stdout) 16 | child.stderr.pipe(process.stderr) 17 | child.once('close', process.exit) 18 | -------------------------------------------------------------------------------- /scripts/unit-typescript-tsimp.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { exec } = require('node:child_process') 4 | 5 | const args = [ 6 | 'TSIMP_PROJECT=tsconfig.tsimp.json', 7 | 'node', 8 | '--import=tsimp/import', 9 | 'test/typescript/basic.ts' 10 | ] 11 | 12 | const child = exec(args.join(' '), { 13 | shell: true 14 | }) 15 | 16 | child.stdout.pipe(process.stdout) 17 | child.stderr.pipe(process.stderr) 18 | child.once('close', process.exit) 19 | -------------------------------------------------------------------------------- /scripts/unit-typescript-tsm.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { exec } = require('node:child_process') 4 | 5 | const args = [ 6 | 'node', 7 | '--require=tsm', 8 | '--test', 9 | 'test/typescript/basic.ts' 10 | ] 11 | 12 | const child = exec(args.join(' '), { 13 | shell: true 14 | }) 15 | 16 | child.stdout.pipe(process.stdout) 17 | child.stderr.pipe(process.stderr) 18 | child.once('close', process.exit) 19 | -------------------------------------------------------------------------------- /scripts/unit-typescript-tsx.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { exec } = require('node:child_process') 4 | 5 | const nodeVersion = Number(process.version.split('.')[0].slice(1)) 6 | 7 | const args = [ 8 | 'npx', 9 | nodeVersion >= 18 ? '--node-options=--import=tsx' : '', 10 | 'tsnd', 11 | 'test/typescript/basic.ts' 12 | ] 13 | 14 | const child = exec(args.join(' '), { 15 | shell: true 16 | }) 17 | 18 | child.stdout.pipe(process.stdout) 19 | child.stderr.pipe(process.stderr) 20 | child.once('close', process.exit) 21 | -------------------------------------------------------------------------------- /test/commonjs/autohooks-basic.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { after, before, describe, it } = require('node:test') 4 | const assert = require('node:assert') 5 | const Fastify = require('fastify') 6 | 7 | describe('Node test suite for autohooks-basic', function () { 8 | const app = Fastify() 9 | before(async function () { 10 | app.register(require('./autohooks/basic')) 11 | app.decorateRequest('hooked', '') 12 | await app.ready() 13 | }) 14 | 15 | after(async function () { 16 | await app.close() 17 | }) 18 | 19 | it('should respond correctly to /', async function () { 20 | const res = await app.inject({ url: '/' }) 21 | 22 | assert.strictEqual(res.statusCode, 200) 23 | assert.deepStrictEqual(JSON.parse(res.payload), { hooked: ['root'] }) 24 | }) 25 | 26 | it('should respond correctly to /child', async function () { 27 | const res = await app.inject({ url: '/child' }) 28 | 29 | assert.strictEqual(res.statusCode, 200) 30 | assert.deepStrictEqual(JSON.parse(res.payload), { hooked: ['child'] }) 31 | }) 32 | 33 | it('should respond correctly to /child/grandchild', async function () { 34 | const res = await app.inject({ url: '/child/grandchild' }) 35 | 36 | assert.strictEqual(res.statusCode, 200) 37 | assert.deepStrictEqual(JSON.parse(res.payload), { hooked: ['grandchild'] }) 38 | }) 39 | 40 | it('should respond correctly to /sibling', async function () { 41 | const res = await app.inject({ url: '/sibling' }) 42 | 43 | assert.strictEqual(res.statusCode, 200) 44 | assert.deepStrictEqual(JSON.parse(res.payload), { hooked: '' }) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /test/commonjs/autohooks-cascade.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { after, before, describe, it } = require('node:test') 4 | const assert = require('node:assert') 5 | const Fastify = require('fastify') 6 | 7 | describe('Node test suite for autohooks-cascade', function () { 8 | const app = Fastify() 9 | before(async function () { 10 | app.register(require('./autohooks/cascade')) 11 | app.decorateRequest('hooked', '') 12 | await app.ready() 13 | }) 14 | 15 | after(async function () { 16 | await app.close() 17 | }) 18 | 19 | it('should respond correctly to /', async function () { 20 | const res = await app.inject({ url: '/' }) 21 | 22 | assert.strictEqual(res.statusCode, 200) 23 | assert.deepStrictEqual(JSON.parse(res.payload), { hooked: ['root'] }) 24 | }) 25 | 26 | it('should respond correctly to /child', async function () { 27 | const res = await app.inject({ url: '/child' }) 28 | 29 | assert.strictEqual(res.statusCode, 200) 30 | assert.deepStrictEqual(JSON.parse(res.payload), { hooked: ['root', 'child'] }) 31 | }) 32 | 33 | it('should respond correctly to /child/grandchild', async function () { 34 | const res = await app.inject({ url: '/child/grandchild' }) 35 | 36 | assert.strictEqual(res.statusCode, 200) 37 | assert.deepStrictEqual(JSON.parse(res.payload), { hooked: ['root', 'child', 'grandchild'] }) 38 | }) 39 | 40 | it('should respond correctly to /sibling', async function () { 41 | const res = await app.inject({ url: '/sibling' }) 42 | 43 | assert.strictEqual(res.statusCode, 200) 44 | assert.deepStrictEqual(JSON.parse(res.payload), { hooked: ['root'] }) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /test/commonjs/autohooks-disabled.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { after, before, describe, it } = require('node:test') 4 | const assert = require('node:assert') 5 | const Fastify = require('fastify') 6 | 7 | describe('Node test suite for autohooks-disabled', function () { 8 | const app = Fastify() 9 | before(async function () { 10 | app.register(require('./autohooks/disabled')) 11 | app.decorateRequest('hooked', 'disabled') 12 | await app.ready() 13 | }) 14 | 15 | after(async function () { 16 | await app.close() 17 | }) 18 | 19 | it('should respond correctly to /', async function () { 20 | const res = await app.inject({ url: '/' }) 21 | 22 | assert.strictEqual(res.statusCode, 200) 23 | assert.deepStrictEqual(JSON.parse(res.payload), { hooked: 'disabled' }) 24 | }) 25 | 26 | it('should respond correctly to /child', async function () { 27 | const res = await app.inject({ url: '/child' }) 28 | 29 | assert.strictEqual(res.statusCode, 200) 30 | assert.deepStrictEqual(JSON.parse(res.payload), { hooked: 'disabled' }) 31 | }) 32 | 33 | it('should respond correctly to /child/grandchild', async function () { 34 | const res = await app.inject({ url: '/child/grandchild' }) 35 | 36 | assert.strictEqual(res.statusCode, 200) 37 | assert.deepStrictEqual(JSON.parse(res.payload), { hooked: 'disabled' }) 38 | }) 39 | 40 | it('should respond correctly to /sibling', async function () { 41 | const res = await app.inject({ url: '/sibling' }) 42 | 43 | assert.strictEqual(res.statusCode, 200) 44 | assert.deepStrictEqual(JSON.parse(res.payload), { hooked: 'disabled' }) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /test/commonjs/autohooks-overwrite.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { after, before, describe, it } = require('node:test') 4 | const assert = require('node:assert') 5 | const Fastify = require('fastify') 6 | 7 | describe('Node test suite for autohooks-overwrite', function () { 8 | const app = Fastify() 9 | before(async function () { 10 | app.register(require('./autohooks/overwrite')) 11 | app.decorateRequest('hooked', '') 12 | await app.ready() 13 | }) 14 | 15 | after(async function () { 16 | await app.close() 17 | }) 18 | 19 | it('should respond correctly to /', async function () { 20 | const res = await app.inject({ url: '/' }) 21 | 22 | assert.strictEqual(res.statusCode, 200) 23 | assert.deepStrictEqual(JSON.parse(res.payload), { hooked: ['root'] }) 24 | }) 25 | 26 | it('should respond correctly to /child', async function () { 27 | const res = await app.inject({ url: '/child' }) 28 | 29 | assert.strictEqual(res.statusCode, 200) 30 | assert.deepStrictEqual(JSON.parse(res.payload), { hooked: ['child'] }) 31 | }) 32 | 33 | it('should respond correctly to /child/grandchild', async function () { 34 | const res = await app.inject({ url: '/child/grandchild' }) 35 | 36 | assert.strictEqual(res.statusCode, 200) 37 | assert.deepStrictEqual(JSON.parse(res.payload), { hooked: ['grandchild'] }) 38 | }) 39 | 40 | it('should respond correctly to /sibling', async function () { 41 | const res = await app.inject({ url: '/sibling' }) 42 | 43 | assert.strictEqual(res.statusCode, 200) 44 | assert.deepStrictEqual(JSON.parse(res.payload), { hooked: ['root'] }) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /test/commonjs/autohooks/basic.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('node:path') 4 | const autoLoad = require('../../../') 5 | 6 | module.exports = function (fastify, opts, next) { 7 | fastify.register(autoLoad, { 8 | dir: path.join(__dirname, 'routes'), 9 | autoHooks: true 10 | }) 11 | 12 | next() 13 | } 14 | -------------------------------------------------------------------------------- /test/commonjs/autohooks/cascade.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('node:path') 4 | const autoLoad = require('../../../') 5 | 6 | module.exports = function (fastify, opts, next) { 7 | fastify.register(autoLoad, { 8 | dir: path.join(__dirname, 'routes'), 9 | autoHooks: true, 10 | cascadeHooks: true 11 | }) 12 | 13 | next() 14 | } 15 | -------------------------------------------------------------------------------- /test/commonjs/autohooks/disabled.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('node:path') 4 | const autoLoad = require('../../../') 5 | 6 | module.exports = function (fastify, opts, next) { 7 | fastify.register(autoLoad, { 8 | dir: path.join(__dirname, 'routes'), 9 | autoHooks: false // disabling specifically for testing clarity, default state is disabled 10 | }) 11 | 12 | next() 13 | } 14 | -------------------------------------------------------------------------------- /test/commonjs/autohooks/overwrite.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('node:path') 4 | const autoLoad = require('../../../') 5 | 6 | module.exports = function (fastify, opts, next) { 7 | fastify.register(autoLoad, { 8 | dir: path.join(__dirname, 'routes'), 9 | autoHooks: true, 10 | cascadeHooks: true, 11 | overwriteHooks: true 12 | }) 13 | 14 | next() 15 | } 16 | -------------------------------------------------------------------------------- /test/commonjs/autohooks/routes/.autohooks.js: -------------------------------------------------------------------------------- 1 | module.exports = async function (app, opts) { 2 | app.addHook('onRequest', async (req, reply) => { 3 | req.hooked = req.hooked || [] 4 | req.hooked.push('root') 5 | }) 6 | } 7 | -------------------------------------------------------------------------------- /test/commonjs/autohooks/routes/child/.autohooks.js: -------------------------------------------------------------------------------- 1 | module.exports = async function (app, opts) { 2 | app.addHook('onRequest', async (req, reply) => { 3 | req.hooked = req.hooked || [] 4 | req.hooked.push('child') 5 | }) 6 | } 7 | -------------------------------------------------------------------------------- /test/commonjs/autohooks/routes/child/grandchild/.autohooks.js: -------------------------------------------------------------------------------- 1 | module.exports = async function (app, opts) { 2 | app.addHook('onRequest', async (req, reply) => { 3 | req.hooked = req.hooked || [] 4 | req.hooked.push('grandchild') 5 | }) 6 | } 7 | -------------------------------------------------------------------------------- /test/commonjs/autohooks/routes/child/grandchild/routes.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async function (app, opts) { 4 | app.get('/', async function (req, reply) { 5 | reply.status(200).send({ hooked: req.hooked }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/commonjs/autohooks/routes/child/routes.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async function (app, opts) { 4 | app.get('/', async function (req, reply) { 5 | reply.status(200).send({ hooked: req.hooked }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/commonjs/autohooks/routes/routes.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async function (app, opts) { 4 | app.get('/', async function (req, reply) { 5 | reply.status(200).send({ hooked: req.hooked }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/commonjs/autohooks/routes/sibling/routes.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async function (app, opts) { 4 | app.get('/', async function (req, reply) { 5 | reply.status(200).send({ hooked: req.hooked }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/commonjs/babel-node.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { after, before, describe, it } = require('node:test') 4 | const assert = require('node:assert') 5 | const Fastify = require('fastify') 6 | const AutoLoad = require('../../') 7 | const { join } = require('node:path') 8 | 9 | describe('Node test suite for babel-node', function () { 10 | const app = Fastify() 11 | 12 | before(async function () { 13 | app.register(AutoLoad, { 14 | dir: join(__dirname, 'babel-node/routes') 15 | }) 16 | await app.ready() 17 | }) 18 | 19 | after(async function () { 20 | await app.close() 21 | }) 22 | 23 | it('should respond correctly to /', async function () { 24 | const res = await app.inject({ 25 | url: '/' 26 | }) 27 | 28 | assert.strictEqual(res.statusCode, 200) 29 | assert.deepStrictEqual(JSON.parse(res.payload), { hello: 'world' }) 30 | }) 31 | 32 | it('should respond correctly to /foo', async function () { 33 | const res = await app.inject({ 34 | url: '/foo' 35 | }) 36 | 37 | assert.strictEqual(res.statusCode, 200) 38 | assert.deepStrictEqual(JSON.parse(res.payload), { foo: 'bar' }) 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /test/commonjs/babel-node/routes/foo/index.ts: -------------------------------------------------------------------------------- 1 | module.exports = async (fastify) => { 2 | fastify.get('/', function () { 3 | return { foo: 'bar' } 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/commonjs/babel-node/routes/root.ts: -------------------------------------------------------------------------------- 1 | module.exports = async (fastify) => { 2 | fastify.get('/', function () { 3 | return { hello: 'world' } 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/commonjs/basic.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { after, before, describe, it } = require('node:test') 4 | const assert = require('node:assert') 5 | const Fastify = require('fastify') 6 | 7 | describe('Node test suite for basic', function () { 8 | const app = Fastify() 9 | 10 | before(async function () { 11 | app.register(require('./basic/app')) 12 | await app.ready() 13 | }) 14 | 15 | after(async function () { 16 | await app.close() 17 | }) 18 | 19 | it('should set app.foo correctly', function () { 20 | assert.strictEqual(app.foo, 'bar') 21 | }) 22 | 23 | it('should respond correctly to /something', async function () { 24 | const res = await app.inject({ url: '/something' }) 25 | 26 | assert.strictEqual(res.statusCode, 200) 27 | assert.deepStrictEqual(JSON.parse(res.payload), { something: 'else' }) 28 | }) 29 | 30 | it('should respond correctly to /autoroute/items/1', async function () { 31 | const res = await app.inject({ url: '/autoroute/items/1' }) 32 | 33 | assert.strictEqual(res.statusCode, 200) 34 | assert.deepStrictEqual(JSON.parse(res.payload), { answer: 42 }) 35 | }) 36 | 37 | it('should respond correctly to /autoroute/items', async function () { 38 | const res = await app.inject({ url: '/autoroute/items' }) 39 | 40 | assert.strictEqual(res.statusCode, 200) 41 | assert.deepStrictEqual(JSON.parse(res.payload), [{ answer: 42 }, { answer: 41 }]) 42 | }) 43 | 44 | it('should respond correctly to /autowrap/1', async function () { 45 | const res = await app.inject({ url: '/autowrap/1' }) 46 | 47 | assert.strictEqual(res.statusCode, 200) 48 | assert.deepStrictEqual(JSON.parse(res.payload), { answer: 42 }) 49 | }) 50 | 51 | it('should respond correctly to /autowrap', async function () { 52 | const res = await app.inject({ url: '/autowrap' }) 53 | 54 | assert.strictEqual(res.statusCode, 200) 55 | assert.deepStrictEqual(JSON.parse(res.payload), [{ answer: 42 }, { answer: 41 }]) 56 | }) 57 | 58 | it('should respond correctly to /semiautomatic/items/1', async function () { 59 | const res = await app.inject({ url: '/semiautomatic/items/1' }) 60 | 61 | assert.strictEqual(res.statusCode, 200) 62 | assert.deepStrictEqual(JSON.parse(res.payload), { answer: 42 }) 63 | }) 64 | 65 | it('should respond correctly to /semiautomatic/items', async function () { 66 | const res = await app.inject({ url: '/semiautomatic/items' }) 67 | 68 | assert.strictEqual(res.statusCode, 200) 69 | assert.deepStrictEqual(JSON.parse(res.payload), [{ answer: 42 }, { answer: 41 }]) 70 | }) 71 | 72 | it('should respond correctly to /bar', async function () { 73 | const res = await app.inject({ url: '/bar' }) 74 | 75 | assert.strictEqual(res.statusCode, 200) 76 | assert.deepStrictEqual(JSON.parse(res.payload), { foo: 'bar' }) 77 | }) 78 | 79 | it('should respond correctly to /prefixed', async function () { 80 | const res = await app.inject({ url: '/prefixed' }) 81 | 82 | assert.strictEqual(res.statusCode, 200) 83 | assert.deepStrictEqual(JSON.parse(res.payload), { something: 'else' }) 84 | }) 85 | 86 | it('should respond correctly to /default', async function () { 87 | const res = await app.inject({ url: '/default' }) 88 | 89 | assert.strictEqual(res.statusCode, 200) 90 | assert.deepStrictEqual(JSON.parse(res.payload), { exports: 'default' }) 91 | }) 92 | 93 | it('should respond correctly to /skip', async function () { 94 | const res = await app.inject({ url: '/skip' }) 95 | 96 | assert.strictEqual(res.statusCode, 404) 97 | }) 98 | 99 | it('should respond correctly to /options', async function () { 100 | const res = await app.inject({ url: '/options' }) 101 | 102 | assert.strictEqual(res.statusCode, 200) 103 | assert.deepStrictEqual(JSON.parse(res.payload), { foo: 'bar' }) 104 | }) 105 | 106 | it('should respond correctly to /defaultPrefix', async function () { 107 | const res = await app.inject({ url: '/defaultPrefix' }) 108 | 109 | assert.strictEqual(res.statusCode, 200) 110 | assert.deepStrictEqual(JSON.parse(res.payload), { index: true }) 111 | }) 112 | 113 | it('should respond correctly to /defaultPrefix/prefixed', async function () { 114 | const res = await app.inject({ url: '/defaultPrefix/prefixed' }) 115 | 116 | assert.strictEqual(res.statusCode, 200) 117 | assert.deepStrictEqual(JSON.parse(res.payload), { prefixed: true }) 118 | }) 119 | 120 | it('should respond correctly to /defaultPrefix/overriddenPrefix', async function () { 121 | const res = await app.inject({ url: '/defaultPrefix/overriddenPrefix' }) 122 | 123 | assert.strictEqual(res.statusCode, 404) 124 | }) 125 | 126 | it('should respond correctly to /overriddenPrefix', async function () { 127 | const res = await app.inject({ url: '/overriddenPrefix' }) 128 | 129 | assert.strictEqual(res.statusCode, 200) 130 | assert.deepStrictEqual(JSON.parse(res.payload), { overide: 'prefix' }) 131 | }) 132 | 133 | it('should respond correctly to /noPrefix', async function () { 134 | const res = await app.inject({ url: '/noPrefix' }) 135 | 136 | assert.strictEqual(res.statusCode, 200) 137 | assert.deepStrictEqual(JSON.parse(res.payload), { no: 'prefix' }) 138 | }) 139 | 140 | it('should respond correctly to /a/b/c', async function () { 141 | const res = await app.inject({ url: '/a/b/c' }) 142 | 143 | assert.strictEqual(res.statusCode, 200) 144 | assert.strictEqual(res.payload.toString(), 'd') 145 | }) 146 | 147 | it('should respond correctly to /custom-index/', async function () { 148 | const res = await app.inject({ url: '/custom-index/' }) 149 | 150 | assert.strictEqual(res.statusCode, 200) 151 | assert.deepStrictEqual(JSON.parse(res.payload), { custom: true }) 152 | }) 153 | 154 | it('should respond correctly to /index/', async function () { 155 | const res = await app.inject({ url: '/index/' }) 156 | 157 | assert.strictEqual(res.statusCode, 200) 158 | assert.deepStrictEqual(JSON.parse(res.payload), { works: true }) 159 | }) 160 | 161 | it('should respond correctly to /index/bar/', async function () { 162 | const res = await app.inject({ url: '/index/bar/' }) 163 | 164 | assert.strictEqual(res.statusCode, 200) 165 | assert.deepStrictEqual(JSON.parse(res.payload), { works: true }) 166 | }) 167 | 168 | describe('rewrite-route-prefix variants', function () { 169 | const urls = [ 170 | '/rewrite-route-prefix', 171 | '/rewrite-route-prefix/', 172 | '/rewrite-route-prefix/tre', 173 | '/rewrite-route-prefix/tre/', 174 | '/rewrite-route-prefix/tre/empty/' 175 | ] 176 | 177 | for (const url of urls) { 178 | it(`should respond correctly to ${url}`, async function () { 179 | const res = await app.inject({ url }) 180 | 181 | assert.strictEqual(res.statusCode, 200) 182 | assert.deepStrictEqual(JSON.parse(res.payload), { works: true }) 183 | }) 184 | } 185 | }) 186 | 187 | it('should respond correctly to /index/ignored', async function () { 188 | const res = await app.inject({ url: '/index/ignored' }) 189 | 190 | assert.strictEqual(res.statusCode, 404) 191 | }) 192 | 193 | it('should respond correctly to /one/', async function () { 194 | const res = await app.inject({ url: '/one/' }) 195 | 196 | assert.strictEqual(res.statusCode, 200) 197 | assert.deepStrictEqual(JSON.parse(res.payload), { works: true }) 198 | }) 199 | 200 | it('should respond correctly to /one/two/three', async function () { 201 | const res = await app.inject({ url: '/one/two/three' }) 202 | 203 | assert.strictEqual(res.statusCode, 200) 204 | assert.deepStrictEqual(JSON.parse(res.payload), { works: true }) 205 | }) 206 | 207 | it('should respond correctly to /routeParams', async function () { 208 | const res = await app.inject({ url: '/routeParams' }) 209 | 210 | assert.strictEqual(res.statusCode, 200) 211 | assert.deepStrictEqual(JSON.parse(res.payload), { works: true }) 212 | }) 213 | 214 | it('should respond correctly to /routeParams/foo', async function () { 215 | const res = await app.inject({ url: '/routeParams/foo' }) 216 | 217 | assert.strictEqual(res.statusCode, 200) 218 | assert.deepStrictEqual(JSON.parse(res.payload), { works: true }) 219 | }) 220 | 221 | it('should respond correctly to /routeParams/foo/1', async function () { 222 | const res = await app.inject({ url: '/routeParams/foo/1' }) 223 | 224 | assert.strictEqual(res.statusCode, 200) 225 | assert.deepStrictEqual(JSON.parse(res.payload), { works: true, id1: '1' }) 226 | }) 227 | 228 | it('should respond correctly to /routeParams/foo/abc/bar/2', async function () { 229 | const res = await app.inject({ url: '/routeParams/foo/abc/bar/2' }) 230 | 231 | assert.strictEqual(res.statusCode, 200) 232 | assert.deepStrictEqual(JSON.parse(res.payload), { works: true, id1: 'abc', id2: '2' }) 233 | }) 234 | 235 | it('should respond correctly to /encapsulate', async function () { 236 | const res = await app.inject({ url: '/encapsulate' }) 237 | 238 | assert.strictEqual(res.statusCode, 200) 239 | assert.deepStrictEqual(JSON.parse(res.payload), { works: true }) 240 | }) 241 | 242 | it('should respond correctly to /configPrefix', async function () { 243 | const res = await app.inject({ url: '/configPrefix' }) 244 | 245 | assert.strictEqual(res.statusCode, 200) 246 | assert.deepStrictEqual(JSON.parse(res.payload), { configPrefix: true }) 247 | }) 248 | 249 | it('should respond correctly to /configPrefixCallback', async function () { 250 | const res = await app.inject({ url: '/configPrefixCallback' }) 251 | 252 | assert.strictEqual(res.statusCode, 200) 253 | assert.deepStrictEqual(JSON.parse(res.payload), { configPrefixCallback: true }) 254 | }) 255 | }) 256 | -------------------------------------------------------------------------------- /test/commonjs/basic/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fs = require('node:fs') 4 | const path = require('node:path') 5 | const autoLoad = require('../../../') 6 | 7 | module.exports = function (fastify, opts, next) { 8 | fastify.register(autoLoad, { 9 | dir: path.join(__dirname, 'foo'), 10 | options: { foo: 'bar' }, 11 | ignorePattern: /^ignored/ 12 | }) 13 | 14 | fastify.register(autoLoad, { 15 | dir: path.join(__dirname, 'index-pattern'), 16 | options: { prefix: '/custom-index' }, 17 | indexPattern: /.custom\.js$/, 18 | ignorePattern: /^index/ 19 | }) 20 | 21 | fastify.register(autoLoad, { 22 | dir: path.join(__dirname, 'rewrite-route-prefix'), 23 | options: { prefix: '/rewrite-route-prefix' }, 24 | dirNameRoutePrefix: function rewrite (folderParent, folderName) { 25 | switch (folderName) { 26 | case 'two': // called twice 27 | return false 28 | case 'three': 29 | return 'tre' 30 | case 'empty': 31 | return folderName 32 | default: 33 | throw new Error('unexpected folderName: ' + folderName) 34 | } 35 | } 36 | }) 37 | 38 | fastify.register(autoLoad, { 39 | dir: path.join(__dirname, 'defaultPrefix'), 40 | options: { prefix: '/defaultPrefix' } 41 | }) 42 | 43 | fastify.register(autoLoad, { 44 | dir: path.join(__dirname, 'one'), 45 | options: { prefix: 'one/' } 46 | }) 47 | 48 | fastify.register(autoLoad, { 49 | dir: path.join(__dirname, 'index'), 50 | options: { prefix: 'index/' } 51 | }) 52 | 53 | fastify.register(autoLoad, { 54 | dir: path.join(__dirname, 'routeParams'), 55 | options: { prefix: 'routeParams/' }, 56 | routeParams: true 57 | }) 58 | 59 | fastify.register(autoLoad, { 60 | dir: path.join(__dirname, 'encapsulate'), 61 | encapsulate: false 62 | }) 63 | 64 | const skipDir = path.join(__dirname, 'skip') 65 | fs.mkdir(path.join(skipDir, 'empty'), () => { 66 | fastify.register(autoLoad, { 67 | dir: skipDir 68 | }) 69 | 70 | next() 71 | }) 72 | } 73 | 74 | module.exports[Symbol.for('skip-override')] = true 75 | -------------------------------------------------------------------------------- /test/commonjs/basic/defaultPrefix/defaultPrefix.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (f, opts, next) { 4 | f.get('/', (request, reply) => { 5 | reply.send({ index: true }) 6 | }) 7 | 8 | next() 9 | } 10 | -------------------------------------------------------------------------------- /test/commonjs/basic/defaultPrefix/noDefaultPrefix.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (f, opts, next) { 4 | f.get('/noPrefix', (request, reply) => { 5 | reply.send({ no: 'prefix' }) 6 | }) 7 | 8 | next() 9 | } 10 | 11 | module.exports.prefixOverride = '' 12 | -------------------------------------------------------------------------------- /test/commonjs/basic/defaultPrefix/overridePrefix.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (f, opts, next) { 4 | f.get('/', (request, reply) => { 5 | reply.send({ overide: 'prefix' }) 6 | }) 7 | 8 | next() 9 | } 10 | 11 | module.exports.autoPrefix = '/notUsed' 12 | 13 | module.exports.prefixOverride = '/overriddenPrefix' 14 | -------------------------------------------------------------------------------- /test/commonjs/basic/defaultPrefix/prefixed.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (f, opts, next) { 4 | f.get('/', (request, reply) => { 5 | reply.send({ prefixed: true }) 6 | }) 7 | 8 | next() 9 | } 10 | 11 | module.exports.autoPrefix = '/prefixed' 12 | -------------------------------------------------------------------------------- /test/commonjs/basic/encapsulate/plugin1.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = (fastify, opts, done) => { 4 | fastify.decorateRequest('sharedVar', '') 5 | 6 | fastify.addHook('onRequest', (request, reply, done) => { 7 | fastify.sharedVar = true 8 | done() 9 | }) 10 | 11 | done() 12 | } 13 | -------------------------------------------------------------------------------- /test/commonjs/basic/encapsulate/plugin2.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async (fastify, opts) => { 4 | fastify.get('/encapsulate', { 5 | handler: async (request, reply) => { 6 | reply.status(200).send({ works: fastify.sharedVar }) 7 | }, 8 | schema: { 9 | response: { 10 | 200: { 11 | type: 'object', 12 | properties: { 13 | works: { type: 'boolean' } 14 | } 15 | } 16 | } 17 | } 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /test/commonjs/basic/foo/a/b/c/d.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (app, opts, next) { 4 | app.get('/', (req, reply) => { reply.send('d') }) 5 | next() 6 | } 7 | -------------------------------------------------------------------------------- /test/commonjs/basic/foo/autoroute/get.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (f, opts, next) { 4 | f.get('/items/:id', (request, reply) => { 5 | reply.send({ answer: 42 }) 6 | }) 7 | 8 | next() 9 | } 10 | -------------------------------------------------------------------------------- /test/commonjs/basic/foo/autoroute/list.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (f, opts, next) { 4 | f.get('/items', (request, reply) => { 5 | reply.send([{ answer: 42 }, { answer: 41 }]) 6 | }) 7 | 8 | next() 9 | } 10 | -------------------------------------------------------------------------------- /test/commonjs/basic/foo/autowrap/get.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | method: 'GET', 5 | url: '/:id', 6 | handler: (request, reply) => { 7 | reply.send({ answer: 42 }) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/commonjs/basic/foo/autowrap/list.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | method: 'GET', 5 | url: '/', 6 | handler: (request, reply) => { 7 | reply.send([{ answer: 42 }, { answer: 41 }]) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/commonjs/basic/foo/bar/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (f, opts, next) { 4 | f.get('/', (request, reply) => { 5 | reply.send({ foo: 'bar' }) 6 | }) 7 | 8 | next() 9 | } 10 | -------------------------------------------------------------------------------- /test/commonjs/basic/foo/bar/is-not-loaded.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (app, opts, next) { 4 | next(new Error('should not be loaded')) 5 | } 6 | -------------------------------------------------------------------------------- /test/commonjs/basic/foo/configPrefix.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (f, opts, next) { 4 | f.get('/', (request, reply) => { 5 | reply.send({ configPrefix: true }) 6 | }) 7 | 8 | next() 9 | } 10 | 11 | module.exports.autoConfig = { 12 | prefix: '/configPrefix' 13 | } 14 | -------------------------------------------------------------------------------- /test/commonjs/basic/foo/configPrefixCallback.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (f, opts, next) { 4 | f.get('/', (request, reply) => { 5 | reply.send({ configPrefixCallback: true }) 6 | }) 7 | 8 | next() 9 | } 10 | 11 | const options = () => ({}) 12 | options.prefix = '/configPrefixCallback' 13 | 14 | module.exports.autoConfig = options 15 | -------------------------------------------------------------------------------- /test/commonjs/basic/foo/decorator.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (f, opts, next) { 4 | f.decorate('foo', 'bar') 5 | 6 | next() 7 | } 8 | 9 | module.exports[Symbol.for('skip-override')] = true 10 | -------------------------------------------------------------------------------- /test/commonjs/basic/foo/exports-default.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports.default = function (f, opts, next) { 4 | f.get('/', (request, reply) => { 5 | reply.send({ exports: 'default' }) 6 | }) 7 | 8 | next() 9 | } 10 | 11 | module.exports.autoPrefix = '/default' 12 | -------------------------------------------------------------------------------- /test/commonjs/basic/foo/ignored.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.exit(1) 4 | -------------------------------------------------------------------------------- /test/commonjs/basic/foo/ignored/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.exit(1) 4 | -------------------------------------------------------------------------------- /test/commonjs/basic/foo/manualprefix/get.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (f, opts, next) { 4 | f.get('/items/:id', (request, reply) => { 5 | reply.send({ answer: 42 }) 6 | }) 7 | 8 | next() 9 | } 10 | -------------------------------------------------------------------------------- /test/commonjs/basic/foo/manualprefix/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (fastify, opts, next) { 4 | fastify.register(require('./list')) 5 | fastify.register(require('./get')) 6 | 7 | next() 8 | } 9 | 10 | module.exports.autoPrefix = '/semiautomatic' 11 | -------------------------------------------------------------------------------- /test/commonjs/basic/foo/manualprefix/list.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (f, opts, next) { 4 | f.get('/items', (request, reply) => { 5 | reply.send([{ answer: 42 }, { answer: 41 }]) 6 | }) 7 | 8 | next() 9 | } 10 | -------------------------------------------------------------------------------- /test/commonjs/basic/foo/options.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (f, opts, next) { 4 | f.get('/options', (request, reply) => { 5 | reply.send(opts) 6 | }) 7 | 8 | next() 9 | } 10 | -------------------------------------------------------------------------------- /test/commonjs/basic/foo/prefixed.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (f, opts, next) { 4 | f.get('/', (request, reply) => { 5 | reply.send({ something: 'else' }) 6 | }) 7 | 8 | next() 9 | } 10 | 11 | module.exports.autoPrefix = '/prefixed' 12 | -------------------------------------------------------------------------------- /test/commonjs/basic/foo/skipAutoload.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (f, opts, next) { 4 | f.get('/skip', (request, reply) => { 5 | reply.send('skip') 6 | }) 7 | 8 | next() 9 | } 10 | 11 | module.exports.autoload = false 12 | -------------------------------------------------------------------------------- /test/commonjs/basic/foo/something.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (f, opts, next) { 4 | f.get('/something', (request, reply) => { 5 | reply.send({ something: 'else' }) 6 | }) 7 | 8 | next() 9 | } 10 | -------------------------------------------------------------------------------- /test/commonjs/basic/foo/something.sh: -------------------------------------------------------------------------------- 1 | echo "hello world" 2 | -------------------------------------------------------------------------------- /test/commonjs/basic/index-pattern/custom.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async (server, opts) => { 4 | server.get('/', async (request, reply) => { 5 | reply.status(200).send({ custom: true }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/commonjs/basic/index-pattern/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async (server, opts) => { 4 | throw new Error('this file should not be loaded') 5 | } 6 | -------------------------------------------------------------------------------- /test/commonjs/basic/index/bar/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async (server, opts) => { 4 | server.get('/', async (request, reply) => { 5 | reply.status(200).send({ works: true }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/commonjs/basic/index/ignored.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // This module should be ignored because an index.js file is present in the same directory 4 | 5 | module.exports = async (server, opts) => { 6 | server.get('/ignored', async (request, reply) => { 7 | reply.status(200).send({ works: true }) 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /test/commonjs/basic/index/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async (server, opts) => { 4 | server.get('/', async (request, reply) => { 5 | reply.status(200).send({ works: true }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/commonjs/basic/one/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async (server, opts) => { 4 | server.get('/', async (req, reply) => { 5 | reply.send({ works: true }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/commonjs/basic/one/two/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async (server, opts) => { 4 | server.get('/three', async (req, reply) => { 5 | reply.send({ works: true }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/commonjs/basic/rewrite-route-prefix/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async (server, opts) => { 4 | server.get('/', async (req, reply) => { 5 | reply.send({ works: true }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/commonjs/basic/rewrite-route-prefix/two/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async (server, opts) => { 4 | server.get('/two', async (req, reply) => { 5 | reply.send({ works: true }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/commonjs/basic/rewrite-route-prefix/two/three/empty/two/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async (server, opts) => { 4 | server.get('/', async (req, reply) => { 5 | reply.send({ works: true }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/commonjs/basic/rewrite-route-prefix/two/three/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async (server, opts) => { 4 | server.get('/', async (req, reply) => { 5 | reply.send({ works: true }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/commonjs/basic/routeParams/foo/_id1/bar/_id2/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async (server, opts) => { 4 | server.get('/', async (request, reply) => { 5 | const params = request.params 6 | reply.status(200).send({ works: true, ...params }) 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /test/commonjs/basic/routeParams/foo/_id1/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async (server, opts) => { 4 | server.get('/', async (request, reply) => { 5 | const params = request.params 6 | reply.status(200).send({ works: true, ...params }) 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /test/commonjs/basic/routeParams/foo/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async (server, opts) => { 4 | server.get('/', async (request, reply) => { 5 | reply.status(200).send({ works: true }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/commonjs/basic/routeParams/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async (server, opts) => { 4 | server.get('/', async (request, reply) => { 5 | reply.status(200).send({ works: true }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/commonjs/basic/skip/noJS/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | *.js 3 | -------------------------------------------------------------------------------- /test/commonjs/cyclic-dependency/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('node:path') 4 | const AutoLoad = require('../../../') 5 | 6 | module.exports = function (fastify, opts, next) { 7 | fastify.register(AutoLoad, { 8 | dir: path.join(__dirname, 'lib') 9 | }) 10 | 11 | next() 12 | } 13 | -------------------------------------------------------------------------------- /test/commonjs/cyclic-dependency/lib/a.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fp = require('fastify-plugin') 4 | 5 | module.exports = fp(function (fastify, opts, next) { 6 | fastify.get('/a', function (_request, reply) { 7 | reply.send(opts) 8 | }) 9 | next() 10 | }, { 11 | dependencies: ['plugin-b'], 12 | name: 'plugin-a' 13 | }) 14 | -------------------------------------------------------------------------------- /test/commonjs/cyclic-dependency/lib/b.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fp = require('fastify-plugin') 4 | 5 | module.exports = fp(function (fastify, opts, next) { 6 | fastify.get('/b', function (_request, reply) { 7 | reply.send(opts) 8 | }) 9 | next() 10 | }, { 11 | dependencies: ['plugin-a'], 12 | name: 'plugin-b' 13 | }) 14 | -------------------------------------------------------------------------------- /test/commonjs/cyclic.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { after, before, describe, it } = require('node:test') 4 | const assert = require('node:assert') 5 | const Fastify = require('fastify') 6 | 7 | describe('Node test suite for babel-node', function () { 8 | const app = Fastify() 9 | 10 | before(async function () { 11 | app.register(require('./cyclic-dependency/app')) 12 | }) 13 | 14 | after(async function () { 15 | await app.close() 16 | }) 17 | 18 | it('should return cyclic dependency error', async function () { 19 | await assert.rejects(app.ready(), { message: 'Cyclic dependency' }) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /test/commonjs/deep.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { after, before, describe, it } = require('node:test') 4 | const assert = require('node:assert') 5 | const Fastify = require('fastify') 6 | 7 | describe('Node test suite for deep routes', function () { 8 | const app = Fastify() 9 | 10 | before(async function () { 11 | app.register(require('./deep/app')) 12 | await app.ready() 13 | }) 14 | 15 | after(async function () { 16 | await app.close() 17 | }) 18 | 19 | it('should respond correctly to /with-dirs/level-1/level-2/deep-route', async function () { 20 | const res = await app.inject({ 21 | url: '/with-dirs/level-1/level-2/deep-route' 22 | }) 23 | 24 | assert.strictEqual(res.statusCode, 200) 25 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'deep-route' }) 26 | }) 27 | 28 | it('should respond correctly to /without-dirs/deep-route', async function () { 29 | const res = await app.inject({ 30 | url: '/without-dirs/deep-route' 31 | }) 32 | 33 | assert.strictEqual(res.statusCode, 200) 34 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'deep-route' }) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /test/commonjs/deep/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('node:path') 4 | const autoLoad = require('../../../') 5 | 6 | module.exports = function (fastify, opts, next) { 7 | fastify.register(autoLoad, { 8 | dir: path.join(__dirname, 'routes'), 9 | options: { 10 | prefix: '/with-dirs' 11 | } 12 | }) 13 | 14 | fastify.register(autoLoad, { 15 | dir: path.join(__dirname, 'routes'), 16 | dirNameRoutePrefix: false, 17 | options: { 18 | prefix: '/without-dirs' 19 | } 20 | }) 21 | 22 | next() 23 | } 24 | -------------------------------------------------------------------------------- /test/commonjs/deep/routes/level-1/level-2/routes.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function deepPlugin (f, opts, next) { 4 | f.get('/deep-route', (request, reply) => { 5 | reply.send({ data: 'deep-route' }) 6 | }) 7 | 8 | next() 9 | } 10 | 11 | module.exports = deepPlugin 12 | -------------------------------------------------------------------------------- /test/commonjs/dependency.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { after, before, describe, it } = require('node:test') 4 | const assert = require('node:assert') 5 | const Fastify = require('fastify') 6 | 7 | describe('Node test suite for dependency', function () { 8 | const app = Fastify() 9 | 10 | before(async function () { 11 | app.register(require('./dependency/app')) 12 | await app.ready() 13 | }) 14 | 15 | after(async function () { 16 | await app.close() 17 | }) 18 | 19 | it('should respond correctly to /plugin-a', async function () { 20 | const res = await app.inject({ url: '/plugin-a' }) 21 | 22 | assert.strictEqual(res.statusCode, 200) 23 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'plugin-a' }) 24 | }) 25 | 26 | it('should respond correctly to /plugin-b', async function () { 27 | const res = await app.inject({ url: '/plugin-b' }) 28 | 29 | assert.strictEqual(res.statusCode, 200) 30 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'plugin-b' }) 31 | }) 32 | 33 | it('should respond correctly to /plugin-c', async function () { 34 | const res = await app.inject({ url: '/plugin-c' }) 35 | 36 | assert.strictEqual(res.statusCode, 200) 37 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'plugin-c' }) 38 | }) 39 | 40 | it('should respond correctly to /plugin-d', async function () { 41 | const res = await app.inject({ url: '/plugin-d' }) 42 | 43 | assert.strictEqual(res.statusCode, 200) 44 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'plugin-d' }) 45 | }) 46 | 47 | it('should respond correctly to /plugin-e', async function () { 48 | const res = await app.inject({ url: '/plugin-e' }) 49 | 50 | assert.strictEqual(res.statusCode, 200) 51 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'plugin-e' }) 52 | }) 53 | 54 | it('should respond correctly to /plugin-f', async function () { 55 | const res = await app.inject({ url: '/plugin-f' }) 56 | 57 | assert.strictEqual(res.statusCode, 200) 58 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'plugin-f' }) 59 | }) 60 | 61 | it('should respond correctly to /plugin-g', async function () { 62 | const res = await app.inject({ url: '/plugin-g' }) 63 | 64 | assert.strictEqual(res.statusCode, 200) 65 | assert.deepStrictEqual(JSON.parse(res.payload).path, '/plugin-g') 66 | }) 67 | }) 68 | -------------------------------------------------------------------------------- /test/commonjs/dependency/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('node:path') 4 | const fastifyUrlData = require('@fastify/url-data') 5 | 6 | const AutoLoad = require('../../../') 7 | 8 | module.exports = function (fastify, opts, next) { 9 | fastify.register(fastifyUrlData) 10 | 11 | fastify.register(AutoLoad, { 12 | dir: path.join(__dirname, 'plugins') 13 | }) 14 | 15 | next() 16 | } 17 | -------------------------------------------------------------------------------- /test/commonjs/dependency/plugins/deep/plugin-d.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fp = require('fastify-plugin') 4 | 5 | function plugin (f, opts, next) { 6 | if (!f.pluginE || !f.pluginF) { 7 | return next() 8 | } 9 | 10 | f.get('/plugin-d', (request, reply) => { 11 | reply.send({ data: 'plugin-d' }) 12 | }) 13 | 14 | f.decorate('pluginD', true) 15 | 16 | next() 17 | } 18 | 19 | module.exports = fp(plugin, { 20 | name: 'plugin-d', 21 | dependencies: ['plugin-e'] 22 | }) 23 | -------------------------------------------------------------------------------- /test/commonjs/dependency/plugins/plugin-a.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fp = require('fastify-plugin') 4 | 5 | function plugin (f, opts, next) { 6 | if (!f.pluginD || !f.pluginE || !f.pluginF) { 7 | return next() 8 | } 9 | 10 | f.get('/plugin-a', (request, reply) => { 11 | reply.send({ data: 'plugin-a' }) 12 | }) 13 | 14 | f.decorate('pluginA', true) 15 | 16 | next() 17 | } 18 | 19 | module.exports = fp(plugin, { 20 | name: 'plugin-a', 21 | dependencies: ['plugin-d'] 22 | }) 23 | -------------------------------------------------------------------------------- /test/commonjs/dependency/plugins/plugin-b.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fp = require('fastify-plugin') 4 | 5 | function plugin (f, opts, next) { 6 | if (!f.pluginE || !f.pluginF) { 7 | return next() 8 | } 9 | 10 | f.get('/plugin-b', (request, reply) => { 11 | reply.send({ data: 'plugin-b' }) 12 | }) 13 | 14 | f.decorate('pluginB', true) 15 | 16 | next() 17 | } 18 | 19 | module.exports = fp(plugin, { 20 | name: 'plugin-b', 21 | dependencies: ['plugin-e'] 22 | }) 23 | -------------------------------------------------------------------------------- /test/commonjs/dependency/plugins/plugin-c.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fp = require('fastify-plugin') 4 | 5 | function plugin (f, opts, next) { 6 | if (!f.pluginE || !f.pluginF) { 7 | return next() 8 | } 9 | 10 | f.get('/plugin-c', (request, reply) => { 11 | reply.send({ data: 'plugin-c' }) 12 | }) 13 | 14 | f.decorate('pluginC', true) 15 | 16 | next() 17 | } 18 | 19 | module.exports = fp(plugin, { 20 | name: 'plugin-c', 21 | dependencies: ['plugin-e'] 22 | }) 23 | -------------------------------------------------------------------------------- /test/commonjs/dependency/plugins/plugin-e.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fp = require('fastify-plugin') 4 | 5 | function plugin (f, opts, next) { 6 | if (!f.pluginF) { 7 | return next() 8 | } 9 | 10 | f.get('/plugin-e', (request, reply) => { 11 | reply.send({ data: 'plugin-e' }) 12 | }) 13 | 14 | f.decorate('pluginE', true) 15 | 16 | next() 17 | } 18 | 19 | module.exports = fp(plugin, { 20 | name: 'plugin-e', 21 | dependencies: ['plugin-f'] 22 | }) 23 | -------------------------------------------------------------------------------- /test/commonjs/dependency/plugins/plugin-f.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fp = require('fastify-plugin') 4 | 5 | function plugin (f, opts, next) { 6 | f.get('/plugin-f', (request, reply) => { 7 | reply.send({ data: 'plugin-f' }) 8 | }) 9 | 10 | f.decorate('pluginF', true) 11 | 12 | next() 13 | } 14 | 15 | module.exports = fp(plugin, { 16 | name: 'plugin-f' 17 | }) 18 | -------------------------------------------------------------------------------- /test/commonjs/dependency/plugins/plugin-g.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fp = require('fastify-plugin') 4 | 5 | function plugin (f, opts, next) { 6 | f.get('/plugin-g', (request, reply) => { 7 | const data = request.urlData() 8 | 9 | reply.send(data) 10 | }) 11 | 12 | next() 13 | } 14 | 15 | module.exports = fp(plugin, { 16 | name: 'plugin-g', 17 | dependencies: ['@fastify/url-data'] 18 | }) 19 | -------------------------------------------------------------------------------- /test/commonjs/error.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { after, before, describe, it } = require('node:test') 4 | const assert = require('node:assert') 5 | const Fastify = require('fastify') 6 | 7 | const runtime = require('../../lib/runtime') 8 | 9 | describe('independent module support - unexpected token', function () { 10 | const app = Fastify() 11 | 12 | before(async function () { 13 | app.register(require('./syntax-error/app')) 14 | }) 15 | 16 | after(async function () { 17 | await app.close() 18 | }) 19 | 20 | it('should return unexpected token error', async function () { 21 | await assert.rejects(app.ready(), SyntaxError, /unexpected token/i) 22 | }) 23 | }) 24 | 25 | describe('independent module support - cannot import plugin index', function () { 26 | const app = Fastify() 27 | 28 | before(async function () { 29 | app.register(require('./index-error/app')) 30 | }) 31 | 32 | after(async function () { 33 | await app.close() 34 | }) 35 | 36 | it('should return cannot import plugin index error', async function () { 37 | await assert.rejects(app.ready(), Error, /cannot import plugin.*index/i) 38 | }) 39 | }) 40 | 41 | describe('independent module support - cannot import plugin typescript', function () { 42 | const app = Fastify() 43 | 44 | before(async function () { 45 | app.register(require('./ts-error/app')) 46 | }) 47 | 48 | after(async function () { 49 | await app.close() 50 | }) 51 | 52 | it('should return cannot import plugin typescript error', async function () { 53 | if (runtime.supportNativeTypeScript) { 54 | assert.doesNotThrow(() => app.ready()) 55 | } else { 56 | await assert.rejects(app.ready(), Error, /cannot import plugin.*typescript/i) 57 | } 58 | }) 59 | }) 60 | 61 | describe('independent module support - cyclic dependency error', function () { 62 | const app = Fastify() 63 | 64 | before(async function () { 65 | app.register(require('./cyclic-dependency/app')) 66 | }) 67 | 68 | after(async function () { 69 | await app.close() 70 | }) 71 | 72 | it('should return cannot import plugin typescript error', async function () { 73 | await assert.rejects(app.ready(), new Error('Cyclic dependency')) 74 | }) 75 | }) 76 | -------------------------------------------------------------------------------- /test/commonjs/graph-dependency.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { after, before, describe, it } = require('node:test') 4 | const assert = require('node:assert') 5 | const Fastify = require('fastify') 6 | 7 | describe('Node test suite for graph dependency', function () { 8 | const app = Fastify() 9 | 10 | before(async function () { 11 | app.register(require('./graph-dependency/app')) 12 | await app.ready() 13 | }) 14 | 15 | after(async function () { 16 | await app.close() 17 | }) 18 | 19 | it('should respond correctly to /a', async function () { 20 | const res = await app.inject({ url: '/a' }) 21 | 22 | assert.strictEqual(res.statusCode, 200) 23 | }) 24 | 25 | it('should respond correctly to /b', async function () { 26 | const res = await app.inject({ url: '/b' }) 27 | 28 | assert.strictEqual(res.statusCode, 200) 29 | }) 30 | 31 | it('should respond correctly to /c', async function () { 32 | const res = await app.inject({ url: '/c' }) 33 | 34 | assert.strictEqual(res.statusCode, 200) 35 | }) 36 | 37 | it('should respond correctly to /d', async function () { 38 | const res = await app.inject({ url: '/d' }) 39 | 40 | assert.strictEqual(res.statusCode, 200) 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /test/commonjs/graph-dependency/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('node:path') 4 | const AutoLoad = require('../../../') 5 | 6 | module.exports = function (fastify, opts, next) { 7 | fastify.register(AutoLoad, { 8 | dir: path.join(__dirname, 'lib') 9 | }) 10 | 11 | next() 12 | } 13 | -------------------------------------------------------------------------------- /test/commonjs/graph-dependency/lib/a.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fp = require('fastify-plugin') 4 | 5 | module.exports = fp(function (fastify, opts, next) { 6 | fastify.get('/a', function (_request, reply) { 7 | reply.send(opts) 8 | }) 9 | next() 10 | }, { 11 | dependencies: ['plugin-b', 'plugin-c'], 12 | name: 'plugin-a' 13 | }) 14 | -------------------------------------------------------------------------------- /test/commonjs/graph-dependency/lib/b.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fp = require('fastify-plugin') 4 | 5 | module.exports = fp(function (fastify, opts, next) { 6 | fastify.get('/b', function (_request, reply) { 7 | reply.send(opts) 8 | }) 9 | next() 10 | }, { 11 | dependencies: ['plugin-d'], 12 | name: 'plugin-b' 13 | }) 14 | -------------------------------------------------------------------------------- /test/commonjs/graph-dependency/lib/c.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fp = require('fastify-plugin') 4 | 5 | module.exports = fp(function (fastify, opts, next) { 6 | fastify.get('/c', function (_request, reply) { 7 | reply.send(opts) 8 | }) 9 | next() 10 | }, { 11 | dependencies: ['plugin-d'], 12 | name: 'plugin-c' 13 | }) 14 | -------------------------------------------------------------------------------- /test/commonjs/graph-dependency/lib/d.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fp = require('fastify-plugin') 4 | 5 | module.exports = fp(function (fastify, opts, next) { 6 | fastify.get('/d', function (_request, reply) { 7 | reply.send(opts) 8 | }) 9 | next() 10 | }, { 11 | name: 'plugin-d' 12 | }) 13 | -------------------------------------------------------------------------------- /test/commonjs/index-error/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('node:path') 4 | const AutoLoad = require('../../../') 5 | 6 | module.exports = function (fastify, opts, next) { 7 | fastify.register(AutoLoad, { 8 | dir: path.join(__dirname, 'package') 9 | }) 10 | 11 | next() 12 | } 13 | -------------------------------------------------------------------------------- /test/commonjs/index-error/package/answer.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (f, opts, next) { 4 | f.get('/package', (request, reply) => { 5 | reply.send({ answer: 42 }) 6 | }) 7 | 8 | next() 9 | } 10 | -------------------------------------------------------------------------------- /test/commonjs/index-error/package/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "answer" 3 | } 4 | -------------------------------------------------------------------------------- /test/commonjs/index-package.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { after, before, describe, it } = require('node:test') 4 | const assert = require('node:assert') 5 | const Fastify = require('fastify') 6 | 7 | describe('Node test suite for index package', function () { 8 | const app = Fastify() 9 | 10 | before(async function () { 11 | app.register(require('./index-package/app')) 12 | await app.ready() 13 | }) 14 | 15 | after(async function () { 16 | await app.close() 17 | }) 18 | 19 | it('should respond correctly to /foo/bar', async function () { 20 | const res = await app.inject({ url: '/foo/bar' }) 21 | 22 | assert.strictEqual(res.statusCode, 200) 23 | assert.deepStrictEqual(res.json(), { success: true }) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /test/commonjs/index-package/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fastifyAutoload = require('../../../') 4 | 5 | module.exports = function (fastify, opts, next) { 6 | fastify.register(fastifyAutoload, { 7 | dir: __dirname 8 | }) 9 | 10 | next() 11 | } 12 | 13 | module.exports.autoload = false 14 | -------------------------------------------------------------------------------- /test/commonjs/index-package/foo/bar.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (f, opts, next) { 4 | f.get('/bar', (request, reply) => { 5 | reply.send({ success: true }) 6 | }) 7 | 8 | next() 9 | } 10 | -------------------------------------------------------------------------------- /test/commonjs/index-package/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | if (require.main === module) { 4 | // side effects 5 | } 6 | 7 | exports.autoload = false 8 | -------------------------------------------------------------------------------- /test/commonjs/index-package/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "index-package" 3 | } 4 | -------------------------------------------------------------------------------- /test/commonjs/module-error/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('node:path') 4 | const AutoLoad = require('../../../') 5 | 6 | module.exports = function (fastify, opts, next) { 7 | fastify.register(AutoLoad, { 8 | dir: path.join(__dirname, 'lib') 9 | }) 10 | 11 | next() 12 | } 13 | -------------------------------------------------------------------------------- /test/commonjs/module-error/lib/a.mjs: -------------------------------------------------------------------------------- 1 | export default function (f, opts, next) { 2 | f.get('/module', (request, reply) => { 3 | reply.send(opts) 4 | }) 5 | 6 | next() 7 | } 8 | -------------------------------------------------------------------------------- /test/commonjs/non-plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // This test tests that automatic loading will skip modules that do not 4 | // export a function, i.e. not a fastify plugin. 5 | 6 | const { after, before, describe, it } = require('node:test') 7 | const assert = require('node:assert') 8 | const Fastify = require('fastify') 9 | 10 | describe('Node test suite for non-plugin', function () { 11 | const app = Fastify() 12 | 13 | before(async function () { 14 | app.register(require('./non-plugin/app')) 15 | await app.ready() 16 | }) 17 | 18 | after(async function () { 19 | await app.close() 20 | }) 21 | 22 | it('should respond correctly to /foo', async function () { 23 | const res = await app.inject({ url: '/foo' }) 24 | 25 | assert.strictEqual(res.statusCode, 200) 26 | assert.strictEqual(res.payload, 'foo') 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /test/commonjs/non-plugin/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('node:path') 4 | const AutoLoad = require('../../../') 5 | 6 | module.exports = function (fastify, opts, next) { 7 | fastify.register(AutoLoad, { 8 | dir: path.join(__dirname, 'lib') 9 | }) 10 | 11 | next() 12 | } 13 | -------------------------------------------------------------------------------- /test/commonjs/non-plugin/lib/a-plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async function (server) { 4 | server.get('/foo', function (req, reply) { 5 | reply.send('foo') 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/commonjs/non-plugin/lib/non-plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | foo: 'foo' 5 | } 6 | -------------------------------------------------------------------------------- /test/commonjs/options.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { after, before, describe, it } = require('node:test') 4 | const assert = require('node:assert') 5 | const Fastify = require('fastify') 6 | 7 | describe('Node test suite for options', function () { 8 | const app = Fastify() 9 | 10 | before(async function () { 11 | app.decorate('root', 'root') 12 | app.register(require('./options/app')) 13 | await app.ready() 14 | }) 15 | 16 | after(async function () { 17 | await app.close() 18 | }) 19 | 20 | it('should respond correctly to /plugin-a with global option override', async function () { 21 | const res = await app.inject({ url: '/plugin-a' }) 22 | 23 | assert.strictEqual(res.statusCode, 200) 24 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'test-1' }) 25 | }) 26 | 27 | it('should respond correctly to /plugin-b', async function () { 28 | const res = await app.inject({ url: '/plugin-b' }) 29 | 30 | assert.strictEqual(res.statusCode, 200) 31 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'override' }) 32 | }) 33 | 34 | it('should respond correctly to /plugin-default', async function () { 35 | const res = await app.inject({ url: '/plugin-default' }) 36 | 37 | assert.strictEqual(res.statusCode, 200) 38 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'default' }) 39 | }) 40 | 41 | it('should respond correctly to /plugin-c', async function () { 42 | const res = await app.inject({ url: '/plugin-c' }) 43 | 44 | assert.strictEqual(res.statusCode, 200) 45 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'c' }) 46 | }) 47 | 48 | it('should respond correctly to /plugin-d', async function () { 49 | const res = await app.inject({ url: '/plugin-d' }) 50 | 51 | assert.strictEqual(res.statusCode, 200) 52 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'test-3' }) 53 | }) 54 | 55 | it('should respond correctly to /plugin-e', async function () { 56 | const res = await app.inject({ url: '/plugin-e' }) 57 | 58 | assert.strictEqual(res.statusCode, 200) 59 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'test-4-root' }) 60 | }) 61 | 62 | it('should respond correctly to /plugin-y', async function () { 63 | const res = await app.inject({ url: '/plugin-y' }) 64 | 65 | assert.strictEqual(res.statusCode, 200) 66 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'y' }) 67 | }) 68 | }) 69 | -------------------------------------------------------------------------------- /test/commonjs/options/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('node:path') 4 | const fastifyUrlData = require('@fastify/url-data') 5 | 6 | const AutoLoad = require('../../../') 7 | 8 | module.exports = function (fastify, opts, next) { 9 | fastify.register(fastifyUrlData) 10 | 11 | fastify.register(AutoLoad, { 12 | dir: path.join(__dirname, 'plugins'), 13 | options: { 14 | b: 'override' 15 | } 16 | }) 17 | 18 | fastify.register(AutoLoad, { 19 | dir: path.join(__dirname, 'plugins-2') 20 | }) 21 | 22 | fastify.register(AutoLoad, { 23 | dir: path.join(__dirname, 'plugins-3') 24 | }) 25 | 26 | next() 27 | } 28 | -------------------------------------------------------------------------------- /test/commonjs/options/lib-plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fp = require('fastify-plugin') 4 | 5 | function plugin (f, opts, next) { 6 | const { name = 'default' } = opts 7 | f.get('/plugin-' + name, (request, reply) => { 8 | reply.send({ data: name }) 9 | }) 10 | 11 | next() 12 | } 13 | 14 | module.exports = fp(plugin, { name: 'lib-plugin' }) 15 | -------------------------------------------------------------------------------- /test/commonjs/options/plugins-2/plugin-x.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const plugin = require('../lib-plugin') 4 | 5 | module.exports = plugin 6 | -------------------------------------------------------------------------------- /test/commonjs/options/plugins-3/plugin-y.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const plugin = require('../lib-plugin') 4 | 5 | plugin.autoConfig = { name: 'y' } 6 | 7 | module.exports = plugin 8 | -------------------------------------------------------------------------------- /test/commonjs/options/plugins/plugin-a.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fp = require('fastify-plugin') 4 | 5 | function plugin (f, opts, next) { 6 | f.get('/plugin-a', (request, reply) => { 7 | reply.send({ data: opts.a }) 8 | }) 9 | 10 | next() 11 | } 12 | 13 | plugin.autoConfig = { a: 'test-1' } 14 | 15 | module.exports = fp(plugin, { name: 'plugin-a' }) 16 | -------------------------------------------------------------------------------- /test/commonjs/options/plugins/plugin-b.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fp = require('fastify-plugin') 4 | 5 | function plugin (f, opts, next) { 6 | f.get('/plugin-b', (request, reply) => { 7 | reply.send({ data: opts.b }) 8 | }) 9 | 10 | next() 11 | } 12 | 13 | plugin.autoConfig = { b: 'test-2' } 14 | 15 | module.exports = fp(plugin, { name: 'plugin-b' }) 16 | -------------------------------------------------------------------------------- /test/commonjs/options/plugins/plugin-c.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const plugin = require('../lib-plugin') 4 | 5 | plugin.autoConfig = { name: 'c' } 6 | 7 | module.exports = plugin 8 | -------------------------------------------------------------------------------- /test/commonjs/options/plugins/plugin-d.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fp = require('fastify-plugin') 4 | 5 | function plugin (f, opts, next) { 6 | f.get('/plugin-d', (request, reply) => { 7 | reply.send({ data: opts.d }) 8 | }) 9 | 10 | next() 11 | } 12 | 13 | plugin.autoConfig = { d: 'test-3' } 14 | 15 | exports.default = fp(plugin, { name: 'plugin-d' }) 16 | -------------------------------------------------------------------------------- /test/commonjs/options/plugins/plugin-e.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fp = require('fastify-plugin') 4 | 5 | function plugin (f, opts, next) { 6 | f.get('/plugin-e', (request, reply) => { 7 | reply.send({ data: opts.e }) 8 | }) 9 | 10 | next() 11 | } 12 | 13 | plugin.autoConfig = (fastify) => { 14 | return { e: 'test-4-' + fastify.root } 15 | } 16 | 17 | exports.default = fp(plugin, { name: 'plugin-e' }) 18 | -------------------------------------------------------------------------------- /test/commonjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "commonjs" 3 | } -------------------------------------------------------------------------------- /test/commonjs/route-parameters-basic.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { after, before, describe, it } = require('node:test') 4 | const assert = require('node:assert') 5 | const Fastify = require('fastify') 6 | 7 | describe('Node test suite for route parameters basic', function () { 8 | const app = Fastify() 9 | 10 | before(async function () { 11 | app.register(require('./route-parameters/basic')) 12 | await app.ready() 13 | }) 14 | 15 | after(async function () { 16 | await app.close() 17 | }) 18 | 19 | it('should respond correctly to /users', async function () { 20 | const res = await app.inject({ url: '/users' }) 21 | 22 | assert.strictEqual(res.statusCode, 200) 23 | assert.deepStrictEqual(JSON.parse(res.payload), { 24 | users: [{ id: 7, username: 'example' }], 25 | }) 26 | }) 27 | 28 | it('should respond correctly to /users/7', async function () { 29 | const res = await app.inject({ url: '/users/7' }) 30 | 31 | assert.strictEqual(res.statusCode, 200) 32 | assert.deepStrictEqual(JSON.parse(res.payload), { 33 | user: { id: '7', username: 'example' }, 34 | }) 35 | }) 36 | 37 | it('should respond correctly to /users/_id', async function () { 38 | const res = await app.inject({ url: '/users/_id' }) 39 | 40 | assert.strictEqual(res.statusCode, 200) 41 | assert.deepStrictEqual(JSON.parse(res.payload), { 42 | user: { id: '_id', username: 'example' }, 43 | }) 44 | }) 45 | 46 | it('should respond correctly to /be-nl', async function () { 47 | const res = await app.inject({ url: '/be-nl' }) 48 | 49 | assert.strictEqual(res.statusCode, 200) 50 | assert.deepStrictEqual(JSON.parse(res.payload), { 51 | country: 'be', 52 | language: 'nl', 53 | }) 54 | }) 55 | }) 56 | -------------------------------------------------------------------------------- /test/commonjs/route-parameters-disabled.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { after, before, describe, it } = require('node:test') 4 | const assert = require('node:assert') 5 | const Fastify = require('fastify') 6 | 7 | describe('Node test suite for route parameters disabled', function () { 8 | const app = Fastify() 9 | 10 | before(async function () { 11 | app.register(require('./route-parameters/disabled')) 12 | await app.ready() 13 | }) 14 | 15 | after(async function () { 16 | await app.close() 17 | }) 18 | 19 | it('should respond correctly to /users', async function () { 20 | const res = await app.inject({ url: '/users' }) 21 | 22 | assert.strictEqual(res.statusCode, 200) 23 | assert.deepStrictEqual(JSON.parse(res.payload), { users: [{ id: 7, username: 'example' }] }) 24 | }) 25 | 26 | it('should respond with 404 to /users/7', async function () { 27 | const res = await app.inject({ url: '/users/7' }) 28 | 29 | assert.strictEqual(res.statusCode, 404) 30 | }) 31 | 32 | it('should respond correctly to /users/_id', async function () { 33 | const res = await app.inject({ url: '/users/_id' }) 34 | 35 | assert.strictEqual(res.statusCode, 200) 36 | assert.deepStrictEqual(JSON.parse(res.payload), { user: { id: 'null', username: 'example' } }) 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /test/commonjs/route-parameters/basic.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('node:path') 4 | const autoLoad = require('../../../') 5 | 6 | module.exports = function (fastify, opts, next) { 7 | fastify.register(autoLoad, { 8 | dir: path.join(__dirname, 'routes'), 9 | routeParams: true 10 | }) 11 | 12 | next() 13 | } 14 | -------------------------------------------------------------------------------- /test/commonjs/route-parameters/disabled.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('node:path') 4 | const autoLoad = require('../../../') 5 | 6 | module.exports = function (fastify, opts, next) { 7 | fastify.register(autoLoad, { 8 | dir: path.join(__dirname, 'routes'), 9 | routeParams: false 10 | }) 11 | 12 | next() 13 | } 14 | -------------------------------------------------------------------------------- /test/commonjs/route-parameters/routes/__country-__language/actions.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async function (app, opts) { 4 | app.get('/', async function (req, reply) { 5 | reply.status(200).send(req.params) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/commonjs/route-parameters/routes/users/_id/actions.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async function (app, opts) { 4 | app.get('/', async function (req, reply) { 5 | reply.status(200).send({ user: { id: req.params.id || 'null', username: 'example' } }) // explicit null reply to preserve response body 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/commonjs/route-parameters/routes/users/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async function (app, opts) { 4 | app.get('/', async function (req, reply) { 5 | reply.status(200).send({ users: [{ id: 7, username: 'example' }] }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/commonjs/syntax-error/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('node:path') 4 | const AutoLoad = require('../../../') 5 | 6 | module.exports = function (fastify, opts, next) { 7 | fastify.register(AutoLoad, { 8 | dir: path.join(__dirname, 'lib') 9 | }) 10 | 11 | next() 12 | } 13 | -------------------------------------------------------------------------------- /test/commonjs/syntax-error/lib/a.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (app, opts, next) { 4 | // this has a syntax error on purpose 5 | [ 6 | } 7 | -------------------------------------------------------------------------------- /test/commonjs/ts-error/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('node:path') 4 | const AutoLoad = require('../../../') 5 | 6 | module.exports = function (fastify, opts, next) { 7 | fastify.register(AutoLoad, { 8 | dir: path.join(__dirname, 'lib') 9 | }) 10 | 11 | next() 12 | } 13 | -------------------------------------------------------------------------------- /test/commonjs/ts-error/lib/a.ts: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (f, opts, next) { 4 | f.get('/something', (request, reply) => { 5 | reply.send({ something: 'else' }) 6 | }) 7 | 8 | next() 9 | } 10 | -------------------------------------------------------------------------------- /test/commonjs/ts-node/routes/bar/index.ts: -------------------------------------------------------------------------------- 1 | module.exports.default = async (fastify: any) => { 2 | fastify.get('/', function () { 3 | return { bar: 'bar' } 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/commonjs/ts-node/routes/foo/baz/index.ts: -------------------------------------------------------------------------------- 1 | module.exports.default = async (fastify: any) => { 2 | fastify.get('/customPath', function () { 3 | return { baz: 'baz' } 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/commonjs/ts-node/routes/foo/index.ts: -------------------------------------------------------------------------------- 1 | module.exports.default = async (fastify: any) => { 2 | fastify.get('/', function () { 3 | return { foo: 'foo' } 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/commonjs/ts-node/routes/root.ts: -------------------------------------------------------------------------------- 1 | module.exports.default = async (fastify: any) => { 2 | fastify.get('/', function () { 3 | return { hello: 'world' } 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/issues/369/invalid-autohooks/.autohooks.js: -------------------------------------------------------------------------------- 1 | @ 2 | -------------------------------------------------------------------------------- /test/issues/369/invalid-index-type/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fastify/fastify-autoload/a6e39220798cc34307ad496b8566940b7f5e9036/test/issues/369/invalid-index-type/index.ts -------------------------------------------------------------------------------- /test/issues/369/non-SyntaxError/.autohooks.js: -------------------------------------------------------------------------------- 1 | x 2 | -------------------------------------------------------------------------------- /test/issues/369/routes/.autohooks.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async function (app) { 4 | app.addHook('onRequest', async (req) => { 5 | req.hooked = req.hooked || [] 6 | req.hooked.push('root') 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /test/issues/369/routes/child/.autohooks.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async function (app) { 4 | app.addHook('onRequest', async (req) => { 5 | req.hooked = req.hooked || [] 6 | req.hooked.push('child') 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /test/issues/369/routes/child/routes.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async function (app) { 4 | app.get('/', async function (req, reply) { 5 | reply.status(200).send({ hooked: req.hooked }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/issues/369/routes/promisified/.autohooks.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = new Promise((resolve) => { 4 | resolve(async function (app) { 5 | app.addHook('onRequest', async (req) => { 6 | req.hooked = req.hooked || [] 7 | req.hooked.push('promisified') 8 | }) 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /test/issues/369/routes/promisified/routes.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async function (app) { 4 | app.get('/', async function (req, reply) { 5 | reply.status(200).send({ hooked: req.hooked }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/issues/369/routes/routes.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async function (app) { 4 | app.get('/', async function (req, reply) { 5 | reply.status(200).send({ hooked: req.hooked }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/issues/369/test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { describe, it } = require('node:test') 4 | const assert = require('node:assert') 5 | const Fastify = require('fastify') 6 | const path = require('node:path') 7 | const autoload = require('../../..') 8 | const runtime = require('../../../lib/runtime') 9 | 10 | describe('Issue 369 tests', async function () { 11 | it('Should throw an error when trying to load invalid hooks', async function () { 12 | const app = Fastify() 13 | app.register(autoload, { 14 | dir: path.join(__dirname, 'invalid-autohooks'), 15 | autoHooks: true, 16 | }) 17 | 18 | await assert.rejects(app.ready(), /Invalid or unexpected token/) 19 | }) 20 | 21 | it('Should throw an error when trying to import hooks plugin using index.ts if typescriptSupport is not enabled', async function () { 22 | const app = Fastify() 23 | app.register(autoload, { 24 | dir: path.join(__dirname, 'invalid-index-type'), 25 | autoHooks: true, 26 | }) 27 | if (runtime.supportNativeTypeScript) { 28 | assert.doesNotThrow(() => app.ready()) 29 | } else { 30 | await assert.rejects( 31 | app.ready(), 32 | new Error( 33 | `@fastify/autoload cannot import hooks plugin at '${path.join( 34 | __dirname, 35 | 'invalid-index-type/index.ts' 36 | )}'. To fix this error compile TypeScript to JavaScript or use 'ts-node' to run your app.` 37 | ) 38 | ) 39 | } 40 | }) 41 | 42 | it("Should not accumulate plugin if doesn't comply to matchFilter", async function () { 43 | const app = Fastify() 44 | app.register(autoload, { 45 | dir: path.join(__dirname, 'routes'), 46 | }) 47 | 48 | await app.ready() 49 | const res = await app.inject({ url: '/' }) 50 | assert.strictEqual(res.statusCode, 200) 51 | 52 | const app2 = Fastify() 53 | app2.register(autoload, { 54 | dir: path.join(__dirname, 'routes'), 55 | matchFilter: /invalid/, 56 | }) 57 | 58 | await app2.ready() 59 | const res2 = await app2.inject({ url: '/' }) 60 | assert.strictEqual(res2.statusCode, 404) 61 | }) 62 | 63 | it('Should be able to filter paths using a string', async function () { 64 | const app = Fastify() 65 | app.register(autoload, { 66 | dir: path.join(__dirname, 'routes'), 67 | matchFilter: 'routes.js', 68 | }) 69 | 70 | await app.ready() 71 | const res = await app.inject({ url: '/' }) 72 | assert.strictEqual(res.statusCode, 200) 73 | 74 | const app2 = Fastify() 75 | app2.register(autoload, { 76 | dir: path.join(__dirname, 'routes'), 77 | matchFilter: 'invalid-path', 78 | }) 79 | 80 | await app2.ready() 81 | const res2 = await app2.inject({ url: '/' }) 82 | assert.strictEqual(res2.statusCode, 404) 83 | }) 84 | 85 | it('Should be able to filter paths using a function', async function () { 86 | const app = Fastify() 87 | app.register(autoload, { 88 | dir: path.join(__dirname, 'routes'), 89 | matchFilter: (path) => path.includes('routes.js'), 90 | }) 91 | 92 | await app.ready() 93 | const res = await app.inject({ url: '/' }) 94 | assert.strictEqual(res.statusCode, 200) 95 | 96 | const app2 = Fastify() 97 | app2.register(autoload, { 98 | dir: path.join(__dirname, 'routes'), 99 | matchFilter: (path) => path.includes('invalid-path'), 100 | }) 101 | 102 | await app2.ready() 103 | const res2 = await app2.inject({ url: '/' }) 104 | assert.strictEqual(res2.statusCode, 404) 105 | }) 106 | 107 | it('Should not accumulate plugin if ignoreFilter is matched', async function () { 108 | const app = Fastify() 109 | app.register(autoload, { 110 | dir: path.join(__dirname, 'routes'), 111 | ignoreFilter: /\/not-exists.js/, 112 | }) 113 | 114 | await app.ready() 115 | const res = await app.inject({ url: '/' }) 116 | assert.strictEqual(res.statusCode, 200) 117 | 118 | const app2 = Fastify() 119 | app2.register(autoload, { 120 | dir: path.join(__dirname, 'routes'), 121 | ignoreFilter: /\/routes.js/, 122 | autoHooks: true, 123 | }) 124 | 125 | await app2.ready() 126 | const res2 = await app2.inject({ url: '/' }) 127 | assert.strictEqual(res2.statusCode, 404) 128 | }) 129 | 130 | it('Should not set skip-override if hook plugin is not a function or async function', async function () { 131 | const app = Fastify() 132 | app.register(autoload, { 133 | dir: path.join(__dirname, 'routes'), 134 | autoHooks: true, 135 | cascadeHooks: true, 136 | }) 137 | 138 | app.decorateRequest('hooked', '') 139 | await app.ready() 140 | 141 | const res = await app.inject({ url: '/child' }) 142 | assert.strictEqual(res.statusCode, 200) 143 | assert.deepStrictEqual(JSON.parse(res.payload), { 144 | hooked: ['root', 'child'], 145 | }) 146 | 147 | const res2 = await app.inject({ url: '/promisified' }) 148 | assert.strictEqual(res2.statusCode, 200) 149 | assert.deepStrictEqual(JSON.parse(res2.payload), { hooked: ['root'] }) 150 | }) 151 | 152 | it('Should not enrich non-SyntaxError', async function () { 153 | const app = Fastify() 154 | app.register(autoload, { 155 | dir: path.join(__dirname, 'non-SyntaxError'), 156 | autoHooks: true, 157 | }) 158 | 159 | await assert.rejects(app.ready(), new ReferenceError('x is not defined')) 160 | }) 161 | }) 162 | -------------------------------------------------------------------------------- /test/issues/374/routes/entity/.autohooks.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async function (app) { 4 | app.addHook('onRequest', async (req) => { 5 | req.hooked = req.hooked || [] 6 | req.hooked.push('root') 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /test/issues/374/routes/entity/_entity/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async function (app) { 4 | app.get('/', async function (req, reply) { 5 | reply.status(200).send({ path: req.url, hooked: req.hooked, params: req.params }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/issues/374/routes/entity/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async function (app) { 4 | app.get('/', async function (req, reply) { 5 | reply.status(200).send({ path: req.url, hooked: req.hooked }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/issues/374/routes/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async function (app) { 4 | app.get('/', async function (req, reply) { 5 | reply.status(200).send({ path: req.url }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/issues/374/test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { after, before, describe, it } = require('node:test') 4 | const assert = require('node:assert') 5 | const path = require('node:path') 6 | const Fastify = require('fastify') 7 | const autoLoad = require('../../../') 8 | 9 | describe('Issue 374 tests', function () { 10 | const app = Fastify() 11 | 12 | before(async function () { 13 | app.register(autoLoad, { 14 | dir: path.join(__dirname, 'routes'), 15 | autoHooks: true, 16 | cascadeHooks: true, 17 | routeParams: true, 18 | }) 19 | await app.ready() 20 | }) 21 | 22 | after(async function () { 23 | await app.close() 24 | }) 25 | 26 | it('should respond correctly to /', async function () { 27 | const res = await app.inject({ url: '/' }) 28 | 29 | assert.strictEqual(res.statusCode, 200) 30 | assert.deepStrictEqual(res.json(), { path: '/' }) 31 | }) 32 | 33 | it('should respond correctly to /entity', async function () { 34 | const res = await app.inject({ url: '/entity' }) 35 | 36 | assert.strictEqual(res.statusCode, 200) 37 | assert.deepStrictEqual(res.json(), { path: '/entity', hooked: ['root'] }) 38 | }) 39 | 40 | it('should respond correctly to /entity/1', async function () { 41 | const res = await app.inject({ url: '/entity/1' }) 42 | 43 | assert.strictEqual(res.statusCode, 200) 44 | assert.deepStrictEqual(res.json(), { 45 | path: '/entity/1', 46 | hooked: ['root'], 47 | params: { entity: '1' }, 48 | }) 49 | }) 50 | }) 51 | -------------------------------------------------------------------------------- /test/issues/376/routes/entity/.autohooks.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async function (app) { 4 | app.addHook('onRequest', async (req) => { 5 | req.hooked = req.hooked || [] 6 | req.hooked.push('root') 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /test/issues/376/routes/entity/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async function (app) { 4 | app.get('/', async function (req, reply) { 5 | reply.status(200).send({ path: req.url, hooked: req.hooked }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/issues/376/routes/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async function (app) { 4 | app.get('/', async function (req, reply) { 5 | reply.status(200).send({ path: req.url }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/issues/376/test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { after, before, describe, it } = require('node:test') 4 | const assert = require('node:assert') 5 | const path = require('node:path') 6 | const Fastify = require('fastify') 7 | const autoLoad = require('../../../') 8 | 9 | describe('Issue 376 tests', function () { 10 | const app = Fastify() 11 | 12 | before(async function () { 13 | app.register(autoLoad, { 14 | dir: path.join(__dirname, 'routes'), 15 | autoHooks: true, 16 | options: { prefix: '/api' }, 17 | }) 18 | 19 | app.register(autoLoad, { 20 | dir: path.join(__dirname, 'routes'), 21 | autoHooks: true, 22 | options: { prefix: '/prefix/' }, 23 | }) 24 | await app.ready() 25 | }) 26 | 27 | after(async function () { 28 | await app.close() 29 | }) 30 | 31 | it('should respond correctly to /api', async function () { 32 | const res = await app.inject({ url: '/api' }) 33 | 34 | assert.strictEqual(res.statusCode, 200) 35 | assert.deepStrictEqual(res.json(), { path: '/api' }) 36 | }) 37 | 38 | it('should respond correctly to /api/entity', async function () { 39 | const res = await app.inject({ url: '/api/entity' }) 40 | 41 | assert.strictEqual(res.statusCode, 200) 42 | assert.deepStrictEqual(res.json(), { 43 | path: '/api/entity', 44 | hooked: ['root'], 45 | }) 46 | }) 47 | 48 | it('should respond correctly to /prefix', async function () { 49 | const res = await app.inject({ url: '/prefix' }) 50 | 51 | assert.strictEqual(res.statusCode, 200) 52 | assert.deepStrictEqual(res.json(), { path: '/prefix' }) 53 | }) 54 | 55 | it('should respond correctly to /prefix/entity', async function () { 56 | const res = await app.inject({ url: '/prefix/entity' }) 57 | 58 | assert.strictEqual(res.statusCode, 200) 59 | assert.deepStrictEqual(res.json(), { 60 | path: '/prefix/entity', 61 | hooked: ['root'], 62 | }) 63 | }) 64 | }) 65 | -------------------------------------------------------------------------------- /test/issues/388/routes/foo.tsx: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async function (app) { 4 | app.get('/', async function (_req, reply) { 5 | reply.status(200).send({ tsx: 'ok' }) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /test/issues/388/test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { afterEach, beforeEach, describe, it } = require('node:test') 4 | const assert = require('node:assert') 5 | const path = require('node:path') 6 | const Fastify = require('fastify') 7 | const autoLoad = require('../../../') 8 | 9 | describe('Issue 388 tests', function () { 10 | let app 11 | 12 | beforeEach(async function () { 13 | app = Fastify() 14 | }) 15 | 16 | afterEach(async function () { 17 | await app.close() 18 | }) 19 | 20 | it('should respond 404 to /', async function () { 21 | app = Fastify() 22 | app.register(autoLoad, { 23 | dir: path.join(__dirname, 'routes'), 24 | }) 25 | await app.ready() 26 | 27 | const res = await app.inject({ url: '/' }) 28 | 29 | assert.strictEqual(res.statusCode, 404) 30 | }) 31 | 32 | it('should respond 200 to / when script pattern is loaded', async function () { 33 | app = Fastify() 34 | app.register(autoLoad, { 35 | dir: path.join(__dirname, 'routes'), 36 | scriptPattern: /(js|ts|tsx)$/, 37 | }) 38 | await app.ready() 39 | 40 | const res = await app.inject({ url: '/' }) 41 | 42 | assert.strictEqual(res.statusCode, 200) 43 | assert.deepStrictEqual(res.json(), { tsx: 'ok' }) 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /test/issues/453/routes/autohooks.mjs: -------------------------------------------------------------------------------- 1 | const pluginRegister = async (fastify) => { 2 | fastify.addHook('onRequest', async (request) => { 3 | request.hooksUsed.push('global') 4 | }) 5 | } 6 | 7 | export default pluginRegister 8 | -------------------------------------------------------------------------------- /test/issues/453/routes/first/autohooks.mjs: -------------------------------------------------------------------------------- 1 | const pluginRegister = async (fastify) => { 2 | fastify.addHook('onRequest', async (request) => { 3 | request.hooksUsed.push('first') 4 | }) 5 | } 6 | 7 | export default pluginRegister 8 | -------------------------------------------------------------------------------- /test/issues/453/routes/first/first.route.mjs: -------------------------------------------------------------------------------- 1 | const routes = async (fastify) => { 2 | fastify.get('/first', async (request) => { 3 | return { hooksUsed: request.hooksUsed } 4 | }) 5 | } 6 | 7 | export default routes 8 | -------------------------------------------------------------------------------- /test/issues/453/routes/first/fourth/autohooks.mjs: -------------------------------------------------------------------------------- 1 | const pluginRegister = async (fastify) => { 2 | fastify.addHook('onRequest', async (request) => { 3 | request.hooksUsed.push('fourth') 4 | }) 5 | } 6 | 7 | export default pluginRegister 8 | -------------------------------------------------------------------------------- /test/issues/453/routes/first/fourth/fourth.route.mjs: -------------------------------------------------------------------------------- 1 | const routes = async (fastify) => { 2 | fastify.get('/fourth', async (request) => { 3 | return { hooksUsed: request.hooksUsed } 4 | }) 5 | } 6 | 7 | export default routes 8 | -------------------------------------------------------------------------------- /test/issues/453/routes/global.route.mjs: -------------------------------------------------------------------------------- 1 | const routes = async (fastify) => { 2 | fastify.get('/global', async (request) => { 3 | return { hooksUsed: request.hooksUsed } 4 | }) 5 | } 6 | 7 | export default routes 8 | -------------------------------------------------------------------------------- /test/issues/453/routes/second/autohooks.mjs: -------------------------------------------------------------------------------- 1 | // This folder is not used directly by the test, but in the previous implementation, the last hook used would affect the outcome. 2 | // This folder ensures that the test fails if the fix is reverted or modified incorrectly. 3 | const pluginRegister = async (fastify) => { 4 | fastify.addHook('onRequest', async (request) => { 5 | request.hooksUsed.push('second') 6 | }) 7 | } 8 | 9 | export default pluginRegister 10 | -------------------------------------------------------------------------------- /test/issues/453/routes/second/second.route.mjs: -------------------------------------------------------------------------------- 1 | // This folder is not used directly by the test, but in the previous implementation, the last hook used would affect the outcome. 2 | // This folder ensures that the test fails if the fix is reverted or modified incorrectly. 3 | const routes = async (fastify) => { 4 | fastify.get('/second', async (request) => { 5 | return { hooksUsed: request.hooksUsed } 6 | }) 7 | } 8 | 9 | export default routes 10 | -------------------------------------------------------------------------------- /test/issues/453/test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { beforeEach, afterEach, describe, it } = require('node:test') 4 | const assert = require('node:assert') 5 | const path = require('node:path') 6 | const Fastify = require('fastify') 7 | const autoLoad = require('../../../') 8 | 9 | const startApp = async (autoloadConfig) => { 10 | const app = Fastify() 11 | app.addHook('onRequest', async (request) => { 12 | request.hooksUsed = [] 13 | }) 14 | app.register(autoLoad, { 15 | dir: path.join(__dirname, 'routes'), 16 | autoHooks: true, 17 | ...autoloadConfig, 18 | }) 19 | app.decorateRequest('hooksUsed') 20 | await app.ready() 21 | return app 22 | } 23 | 24 | describe('Issue 453 tests', function () { 25 | describe('cascadeHooks === false', () => { 26 | describe('dirNameRoutePrefix === true not interfere with auto hooks', () => { 27 | let app 28 | 29 | beforeEach(async function () { 30 | app = await startApp({ 31 | cascadeHooks: false, 32 | dirNameRoutePrefix: true 33 | }) 34 | }) 35 | 36 | afterEach(async function () { 37 | await app.close() 38 | }) 39 | 40 | it('should only use global hook in global route', async function () { 41 | const res = await app.inject({ url: '/global' }) 42 | assert.strictEqual(res.statusCode, 200) 43 | assert.deepStrictEqual(JSON.parse(res.body), { hooksUsed: ['global'] }) 44 | }) 45 | 46 | it('should only use child hook in child route', async function () { 47 | const res = await app.inject({ url: '/first/first' }) 48 | assert.strictEqual(res.statusCode, 200) 49 | assert.deepStrictEqual(JSON.parse(res.body), { hooksUsed: ['first'] }) 50 | }) 51 | 52 | it('should only use grandchild hook in grandchild route', async function () { 53 | const res = await app.inject({ url: '/first/fourth/fourth' }) 54 | assert.strictEqual(res.statusCode, 200) 55 | assert.deepStrictEqual(JSON.parse(res.body), { hooksUsed: ['fourth'] }) 56 | }) 57 | }) 58 | describe('dirNameRoutePrefix === false not interfere with auto hooks', () => { 59 | let app 60 | 61 | beforeEach(async function () { 62 | app = await startApp({ 63 | cascadeHooks: false, 64 | dirNameRoutePrefix: false 65 | }) 66 | }) 67 | 68 | afterEach(async function () { 69 | await app.close() 70 | }) 71 | 72 | it('should only use global hook in global route', async function () { 73 | const res = await app.inject({ url: '/global' }) 74 | assert.strictEqual(res.statusCode, 200) 75 | assert.deepStrictEqual(JSON.parse(res.body), { hooksUsed: ['global'] }) 76 | }) 77 | 78 | it('should only use child hook in child route', async function () { 79 | const res = await app.inject({ url: '/first' }) 80 | assert.strictEqual(res.statusCode, 200) 81 | assert.deepStrictEqual(JSON.parse(res.body), { hooksUsed: ['first'] }) 82 | }) 83 | 84 | it('should only use grandchild hook in grandchild route', async function () { 85 | const res = await app.inject({ url: '/fourth' }) 86 | assert.strictEqual(res.statusCode, 200) 87 | assert.deepStrictEqual(JSON.parse(res.body), { hooksUsed: ['fourth'] }) 88 | }) 89 | }) 90 | describe('dirNameRoutePrefix === () => false not interfere with auto hooks', () => { 91 | let app 92 | 93 | beforeEach(async function () { 94 | app = await startApp({ 95 | cascadeHooks: false, 96 | dirNameRoutePrefix: () => { 97 | return false 98 | } 99 | }) 100 | }) 101 | 102 | afterEach(async function () { 103 | await app.close() 104 | }) 105 | 106 | it('should only use global hook in global route', async function () { 107 | const res = await app.inject({ url: '/global' }) 108 | assert.strictEqual(res.statusCode, 200) 109 | assert.deepStrictEqual(JSON.parse(res.body), { hooksUsed: ['global'] }) 110 | }) 111 | 112 | it('should only use child hook in child route', async function () { 113 | const res = await app.inject({ url: '/first' }) 114 | assert.strictEqual(res.statusCode, 200) 115 | assert.deepStrictEqual(JSON.parse(res.body), { hooksUsed: ['first'] }) 116 | }) 117 | 118 | it('should only use grandchild hook in grandchild route', async function () { 119 | const res = await app.inject({ url: '/fourth' }) 120 | assert.strictEqual(res.statusCode, 200) 121 | assert.deepStrictEqual(JSON.parse(res.body), { hooksUsed: ['fourth'] }) 122 | }) 123 | }) 124 | }) 125 | 126 | describe('cascadeHooks === true', () => { 127 | describe('dirNameRoutePrefix === true not interfere with auto hooks', () => { 128 | let app 129 | 130 | beforeEach(async function () { 131 | app = await startApp({ 132 | cascadeHooks: true, 133 | dirNameRoutePrefix: true 134 | }) 135 | }) 136 | 137 | afterEach(async function () { 138 | await app.close() 139 | }) 140 | 141 | it('should only use global hook in global route', async function () { 142 | const res = await app.inject({ url: '/global' }) 143 | assert.strictEqual(res.statusCode, 200) 144 | assert.deepStrictEqual(JSON.parse(res.body), { hooksUsed: ['global'] }) 145 | }) 146 | 147 | it('should use hooks till child in child route', async function () { 148 | const res = await app.inject({ url: '/first/first' }) 149 | assert.strictEqual(res.statusCode, 200) 150 | assert.deepStrictEqual(JSON.parse(res.body), { hooksUsed: ['global', 'first'] }) 151 | }) 152 | 153 | it('should use hooks till grandchild in grandchild route', async function () { 154 | const res = await app.inject({ url: '/first/fourth/fourth' }) 155 | assert.strictEqual(res.statusCode, 200) 156 | assert.deepStrictEqual(JSON.parse(res.body), { hooksUsed: ['global', 'first', 'fourth'] }) 157 | }) 158 | }) 159 | describe('dirNameRoutePrefix === false not interfere with auto hooks', () => { 160 | let app 161 | 162 | beforeEach(async function () { 163 | app = await startApp({ 164 | cascadeHooks: true, 165 | dirNameRoutePrefix: false 166 | }) 167 | }) 168 | 169 | afterEach(async function () { 170 | await app.close() 171 | }) 172 | 173 | it('should only use global hook in global route', async function () { 174 | const res = await app.inject({ url: '/global' }) 175 | assert.strictEqual(res.statusCode, 200) 176 | assert.deepStrictEqual(JSON.parse(res.body), { hooksUsed: ['global'] }) 177 | }) 178 | 179 | it('should use hooks till child in child route', async function () { 180 | const res = await app.inject({ url: '/first' }) 181 | assert.strictEqual(res.statusCode, 200) 182 | assert.deepStrictEqual(JSON.parse(res.body), { hooksUsed: ['global', 'first'] }) 183 | }) 184 | 185 | it('should use hooks till grandchild in grandchild route', async function () { 186 | const res = await app.inject({ url: '/fourth' }) 187 | assert.strictEqual(res.statusCode, 200) 188 | assert.deepStrictEqual(JSON.parse(res.body), { hooksUsed: ['global', 'first', 'fourth'] }) 189 | }) 190 | }) 191 | describe('dirNameRoutePrefix === () => false not interfere with auto hooks', () => { 192 | let app 193 | 194 | beforeEach(async function () { 195 | app = await startApp({ 196 | cascadeHooks: true, 197 | dirNameRoutePrefix: () => { 198 | return false 199 | } 200 | }) 201 | }) 202 | 203 | afterEach(async function () { 204 | await app.close() 205 | }) 206 | 207 | it('should only use global hook in global route', async function () { 208 | const res = await app.inject({ url: '/global' }) 209 | assert.strictEqual(res.statusCode, 200) 210 | assert.deepStrictEqual(JSON.parse(res.body), { hooksUsed: ['global'] }) 211 | }) 212 | 213 | it('should use hooks till child in child route', async function () { 214 | const res = await app.inject({ url: '/first' }) 215 | assert.strictEqual(res.statusCode, 200) 216 | assert.deepStrictEqual(JSON.parse(res.body), { hooksUsed: ['global', 'first'] }) 217 | }) 218 | 219 | it('should use hooks till grandchild in grandchild route', async function () { 220 | const res = await app.inject({ url: '/fourth' }) 221 | assert.strictEqual(res.statusCode, 200) 222 | assert.deepStrictEqual(JSON.parse(res.body), { hooksUsed: ['global', 'first', 'fourth'] }) 223 | }) 224 | }) 225 | }) 226 | }) 227 | -------------------------------------------------------------------------------- /test/issues/462/routes/api.mjs: -------------------------------------------------------------------------------- 1 | export default async function (app) { 2 | app.get('/api', async function (req, reply) { 3 | reply.status(200).send({ path: req.url }) 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/issues/462/routes/empty.cjs: -------------------------------------------------------------------------------- 1 | // cjs empty file 2 | -------------------------------------------------------------------------------- /test/issues/462/routes/empty.js: -------------------------------------------------------------------------------- 1 | // empty file 2 | -------------------------------------------------------------------------------- /test/issues/462/routes/empty.mjs: -------------------------------------------------------------------------------- 1 | // mjs empty file 2 | -------------------------------------------------------------------------------- /test/issues/462/routes/hello.mjs: -------------------------------------------------------------------------------- 1 | // file with something but not a plugin export 2 | export const hello = () => { 3 | // do something 4 | } 5 | -------------------------------------------------------------------------------- /test/issues/462/routes/object.mjs: -------------------------------------------------------------------------------- 1 | // export default an empty object 2 | export default {} 3 | -------------------------------------------------------------------------------- /test/issues/462/test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { after, before, describe, it, mock } = require('node:test') 4 | const path = require('node:path') 5 | const Fastify = require('fastify') 6 | const autoLoad = require('../../../') 7 | const assert = require('node:assert') 8 | 9 | describe('Issue 462: ignore invalid and/or empty .js and .mjs files when using esm', function () { 10 | const app = Fastify() 11 | 12 | before(async function () { 13 | app.register(autoLoad, { 14 | dir: path.join(__dirname, 'routes'), 15 | forceESM: true, 16 | }) 17 | mock.method(app.log, 'debug') 18 | await app.ready() 19 | }) 20 | 21 | after(async function () { 22 | await app.close() 23 | }) 24 | 25 | it('should respond correctly to /api', async function () { 26 | const res = await app.inject({ url: '/api' }) 27 | 28 | assert.strictEqual(res.statusCode, 200) 29 | assert.deepStrictEqual(res.json(), { path: '/api' }) 30 | 31 | // check debug call when importing empty/invalid esm files 32 | // 5 debug log including: empty.cjs, empty.js, empty.mjs, hello.mjs, object.mjs 33 | // api.mjs pass the check so it will not trigger the debug log 34 | // increasing this number if there are more empty/invalid esm files you want to test 35 | assert.strictEqual(app.log.debug.mock.callCount(), 5) 36 | }) 37 | }) 38 | -------------------------------------------------------------------------------- /test/module/autohooks.js: -------------------------------------------------------------------------------- 1 | import { after, before, describe, it } from 'node:test' 2 | import assert from 'node:assert' 3 | import fastify from 'fastify' 4 | import autoHooksBasic from './autohooks/basic.mjs' 5 | import autoHooksCascade from './autohooks/cascade.mjs' 6 | import autoHooksOverwrite from './autohooks/overwrite.mjs' 7 | import autoHooksDisabled from './autohooks/disabled.mjs' 8 | 9 | describe('autohooks: Default behaviour', function () { 10 | const app = fastify() 11 | 12 | before(async function () { 13 | app.register(autoHooksBasic) 14 | app.decorateRequest('hooked', '') 15 | await app.ready() 16 | }) 17 | 18 | after(async function () { 19 | await app.close() 20 | }) 21 | 22 | it('should respond correctly to /', async function () { 23 | const res = await app.inject('/') 24 | assert.strictEqual(res.statusCode, 200) 25 | assert.deepStrictEqual(res.json(), { hooked: ['root'] }) 26 | }) 27 | 28 | it('should respond correctly to /child', async function () { 29 | const res = await app.inject('/child') 30 | assert.strictEqual(res.statusCode, 200) 31 | assert.deepStrictEqual(res.json(), { hooked: ['child'] }) 32 | }) 33 | 34 | it('should respond correctly to /child/grandchild', async function () { 35 | const res = await app.inject('/child/grandchild') 36 | assert.strictEqual(res.statusCode, 200) 37 | assert.deepStrictEqual(res.json(), { hooked: ['grandchild'] }) 38 | }) 39 | 40 | it('should respond correctly to /sibling', async function () { 41 | const res = await app.inject('/sibling') 42 | assert.strictEqual(res.statusCode, 200) 43 | assert.deepStrictEqual(res.json(), { hooked: '' }) 44 | }) 45 | }) 46 | 47 | describe('autohooks: Cascade behaviour', function () { 48 | const app = fastify() 49 | 50 | before(async function () { 51 | app.register(autoHooksCascade) 52 | app.decorateRequest('hooked', '') 53 | await app.ready() 54 | }) 55 | 56 | after(async function () { 57 | await app.close() 58 | }) 59 | 60 | it('should respond correctly to /', async function () { 61 | const res = await app.inject('/') 62 | assert.strictEqual(res.statusCode, 200) 63 | assert.deepStrictEqual(res.json(), { hooked: ['root'] }) 64 | }) 65 | 66 | it('should respond correctly to /child', async function () { 67 | const res = await app.inject('/child') 68 | assert.strictEqual(res.statusCode, 200) 69 | assert.deepStrictEqual(res.json(), { hooked: ['root', 'child'] }) 70 | }) 71 | 72 | it('should respond correctly to /child/grandchild', async function () { 73 | const res = await app.inject('/child/grandchild') 74 | assert.strictEqual(res.statusCode, 200) 75 | assert.deepStrictEqual(res.json(), { 76 | hooked: ['root', 'child', 'grandchild'], 77 | }) 78 | }) 79 | 80 | it('should respond correctly to /sibling', async function () { 81 | const res = await app.inject('/sibling') 82 | assert.strictEqual(res.statusCode, 200) 83 | assert.deepStrictEqual(res.json(), { hooked: ['root'] }) 84 | }) 85 | }) 86 | 87 | describe('autohooks: Overwrite cascade behaviour', function () { 88 | const app = fastify() 89 | 90 | before(async function () { 91 | app.register(autoHooksOverwrite) 92 | app.decorateRequest('hooked', '') 93 | await app.ready() 94 | }) 95 | 96 | after(async function () { 97 | await app.close() 98 | }) 99 | 100 | it('should respond correctly to /', async function () { 101 | const res = await app.inject('/') 102 | assert.strictEqual(res.statusCode, 200) 103 | assert.deepStrictEqual(res.json(), { hooked: ['root'] }) 104 | }) 105 | 106 | it('should respond correctly to /child', async function () { 107 | const res = await app.inject('/child') 108 | assert.strictEqual(res.statusCode, 200) 109 | assert.deepStrictEqual(res.json(), { hooked: ['child'] }) 110 | }) 111 | 112 | it('should respond correctly to /child/grandchild', async function () { 113 | const res = await app.inject('/child/grandchild') 114 | assert.strictEqual(res.statusCode, 200) 115 | assert.deepStrictEqual(res.json(), { hooked: ['grandchild'] }) 116 | }) 117 | 118 | it('should respond correctly to /sibling', async function () { 119 | const res = await app.inject('/sibling') 120 | assert.strictEqual(res.statusCode, 200) 121 | assert.deepStrictEqual(res.json(), { hooked: ['root'] }) 122 | }) 123 | }) 124 | 125 | describe('autohooks: Disabled behaviour', function () { 126 | const app = fastify() 127 | 128 | before(async function () { 129 | app.register(autoHooksDisabled) 130 | app.decorateRequest('hooked', 'disabled') 131 | await app.ready() 132 | }) 133 | 134 | after(async function () { 135 | await app.close() 136 | }) 137 | 138 | it('should respond correctly to /', async function () { 139 | const res = await app.inject('/') 140 | assert.strictEqual(res.statusCode, 200) 141 | assert.deepStrictEqual(res.json(), { hooked: 'disabled' }) 142 | }) 143 | 144 | it('should respond correctly to /child', async function () { 145 | const res = await app.inject('/child') 146 | assert.strictEqual(res.statusCode, 200) 147 | assert.deepStrictEqual(res.json(), { hooked: 'disabled' }) 148 | }) 149 | 150 | it('should respond correctly to /child/grandchild', async function () { 151 | const res = await app.inject('/child/grandchild') 152 | assert.strictEqual(res.statusCode, 200) 153 | assert.deepStrictEqual(res.json(), { hooked: 'disabled' }) 154 | }) 155 | 156 | it('should respond correctly to /sibling', async function () { 157 | const res = await app.inject('/sibling') 158 | assert.strictEqual(res.statusCode, 200) 159 | assert.deepStrictEqual(res.json(), { hooked: 'disabled' }) 160 | }) 161 | }) 162 | -------------------------------------------------------------------------------- /test/module/autohooks/basic.mjs: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | import { fileURLToPath } from 'node:url' 3 | 4 | import autoload from '../../../index.js' 5 | 6 | const { dirname } = path 7 | const __filename = fileURLToPath(import.meta.url) 8 | const __dirname = dirname(__filename) 9 | 10 | export default async function (fastify, opts) { 11 | fastify.register(autoload, { 12 | dir: path.join(__dirname, 'routes'), 13 | autoHooks: true 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /test/module/autohooks/cascade.mjs: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | import { fileURLToPath } from 'node:url' 3 | 4 | import autoload from '../../../index.js' 5 | 6 | const { dirname } = path 7 | const __filename = fileURLToPath(import.meta.url) 8 | const __dirname = dirname(__filename) 9 | 10 | export default async function (fastify, opts) { 11 | fastify.register(autoload, { 12 | dir: path.join(__dirname, 'routes'), 13 | autoHooks: true, 14 | cascadeHooks: true 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /test/module/autohooks/disabled.mjs: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | import { fileURLToPath } from 'node:url' 3 | 4 | import autoload from '../../../index.js' 5 | 6 | const { dirname } = path 7 | const __filename = fileURLToPath(import.meta.url) 8 | const __dirname = dirname(__filename) 9 | 10 | export default async function (fastify, opts) { 11 | fastify.register(autoload, { 12 | dir: path.join(__dirname, 'routes'), 13 | autoHooks: false // disabling specifically for testing clarity, default state is disabled 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /test/module/autohooks/overwrite.mjs: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | import { fileURLToPath } from 'node:url' 3 | 4 | import autoload from '../../../index.js' 5 | 6 | const { dirname } = path 7 | const __filename = fileURLToPath(import.meta.url) 8 | const __dirname = dirname(__filename) 9 | 10 | export default async function (fastify, opts) { 11 | fastify.register(autoload, { 12 | dir: path.join(__dirname, 'routes'), 13 | autoHooks: true, 14 | cascadeHooks: true, 15 | overwriteHooks: true 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /test/module/autohooks/routes/.autohooks.mjs: -------------------------------------------------------------------------------- 1 | export default async function (app, opts) { 2 | app.addHook('onRequest', async (req, reply) => { 3 | req.hooked = req.hooked || [] 4 | req.hooked.push('root') 5 | }) 6 | } 7 | -------------------------------------------------------------------------------- /test/module/autohooks/routes/child/.autohooks.mjs: -------------------------------------------------------------------------------- 1 | export default async function (app, opts) { 2 | app.addHook('onRequest', async (req, reply) => { 3 | req.hooked = req.hooked || [] 4 | req.hooked.push('child') 5 | }) 6 | } 7 | -------------------------------------------------------------------------------- /test/module/autohooks/routes/child/grandchild/.autohooks.mjs: -------------------------------------------------------------------------------- 1 | export default async function (app, opts) { 2 | app.addHook('onRequest', async (req, reply) => { 3 | req.hooked = req.hooked || [] 4 | req.hooked.push('grandchild') 5 | }) 6 | } 7 | -------------------------------------------------------------------------------- /test/module/autohooks/routes/child/grandchild/routes.mjs: -------------------------------------------------------------------------------- 1 | export default async function (app, opts) { 2 | app.get('/', async function (req, reply) { 3 | return { hooked: req.hooked } 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/module/autohooks/routes/child/routes.mjs: -------------------------------------------------------------------------------- 1 | export default async function (app, opts) { 2 | app.get('/', async function (req, reply) { 3 | return { hooked: req.hooked } 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/module/autohooks/routes/routes.mjs: -------------------------------------------------------------------------------- 1 | export default async function (app, opts) { 2 | app.get('/', async function (req, reply) { 3 | return { hooked: req.hooked } 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/module/autohooks/routes/sibling/routes.mjs: -------------------------------------------------------------------------------- 1 | export default async function (app, opts) { 2 | app.get('/', async function (req, reply) { 3 | return { hooked: req.hooked } 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/module/basic.js: -------------------------------------------------------------------------------- 1 | import { after, before, describe, it } from 'node:test' 2 | import assert from 'node:assert' 3 | import fastify from 'fastify' 4 | import basicApp from './basic/app.js' 5 | 6 | describe('basic tests', function () { 7 | const app = fastify() 8 | 9 | before(async function () { 10 | app.register(basicApp) 11 | await app.ready() 12 | }) 13 | 14 | after(async function () { 15 | await app.close() 16 | }) 17 | 18 | it('should respond correctly to /something', async function () { 19 | const res = await app.inject({ url: '/something' }) 20 | assert.strictEqual(res.statusCode, 200) 21 | assert.deepStrictEqual(JSON.parse(res.payload), { something: 'else' }) 22 | }) 23 | 24 | it('should respond correctly to /autoroute/items/1', async function () { 25 | const res = await app.inject({ url: '/autoroute/items/1' }) 26 | assert.strictEqual(res.statusCode, 200) 27 | assert.deepStrictEqual(JSON.parse(res.payload), { answer: 42 }) 28 | }) 29 | 30 | it('should respond correctly to /autoroute/items', async function () { 31 | const res = await app.inject({ url: '/autoroute/items' }) 32 | assert.strictEqual(res.statusCode, 200) 33 | assert.deepStrictEqual(JSON.parse(res.payload), [ 34 | { answer: 42 }, 35 | { answer: 41 }, 36 | ]) 37 | }) 38 | 39 | it('should respond correctly to /autowrap/1', async function () { 40 | const res = await app.inject({ url: '/autowrap/1' }) 41 | assert.strictEqual(res.statusCode, 200) 42 | assert.deepStrictEqual(JSON.parse(res.payload), { answer: 42 }) 43 | }) 44 | 45 | it('should respond correctly to /autowrap', async function () { 46 | const res = await app.inject({ url: '/autowrap' }) 47 | assert.strictEqual(res.statusCode, 200) 48 | assert.deepStrictEqual(JSON.parse(res.payload), [ 49 | { answer: 42 }, 50 | { answer: 41 }, 51 | ]) 52 | }) 53 | 54 | it('should respond correctly to /semiautomatic/items/1', async function () { 55 | const res = await app.inject({ url: '/semiautomatic/items/1' }) 56 | assert.strictEqual(res.statusCode, 200) 57 | assert.deepStrictEqual(JSON.parse(res.payload), { answer: 42 }) 58 | }) 59 | 60 | it('should respond correctly to /semiautomatic/items', async function () { 61 | const res = await app.inject({ url: '/semiautomatic/items' }) 62 | assert.strictEqual(res.statusCode, 200) 63 | assert.deepStrictEqual(JSON.parse(res.payload), [ 64 | { answer: 42 }, 65 | { answer: 41 }, 66 | ]) 67 | }) 68 | 69 | it('should respond correctly to /bar', async function () { 70 | const res = await app.inject({ url: '/bar' }) 71 | assert.strictEqual(res.statusCode, 200) 72 | assert.deepStrictEqual(JSON.parse(res.payload), { foo: 'bar' }) 73 | }) 74 | 75 | it('should respond correctly to /prefixed', async function () { 76 | const res = await app.inject({ url: '/prefixed' }) 77 | assert.strictEqual(res.statusCode, 200) 78 | assert.deepStrictEqual(JSON.parse(res.payload), { something: 'else' }) 79 | }) 80 | 81 | it('should respond correctly to /skip', async function () { 82 | const res = await app.inject({ url: '/skip' }) 83 | assert.strictEqual(res.statusCode, 404) 84 | }) 85 | 86 | it('should respond correctly to /options', async function () { 87 | const res = await app.inject({ url: '/options' }) 88 | assert.strictEqual(res.statusCode, 200) 89 | assert.deepStrictEqual(JSON.parse(res.payload), { foo: 'bar' }) 90 | }) 91 | 92 | it('should respond correctly to /commonjs', async function () { 93 | const res = await app.inject({ url: '/commonjs' }) 94 | assert.strictEqual(res.statusCode, 200) 95 | assert.deepStrictEqual(JSON.parse(res.payload), { foo: 'bar' }) 96 | }) 97 | 98 | it('should respond correctly to /module', async function () { 99 | const res = await app.inject({ url: '/module' }) 100 | assert.strictEqual(res.statusCode, 200) 101 | assert.deepStrictEqual(JSON.parse(res.payload), { foo: 'bar' }) 102 | }) 103 | 104 | it('should respond correctly to /defaultPrefix', async function () { 105 | const res = await app.inject({ url: '/defaultPrefix' }) 106 | assert.strictEqual(res.statusCode, 200) 107 | assert.deepStrictEqual(JSON.parse(res.payload), { index: true }) 108 | }) 109 | 110 | it('should respond correctly to /defaultPrefix/prefixed', async function () { 111 | const res = await app.inject({ url: '/defaultPrefix/prefixed' }) 112 | assert.strictEqual(res.statusCode, 200) 113 | assert.deepStrictEqual(JSON.parse(res.payload), { prefixed: true }) 114 | }) 115 | 116 | it('should respond correctly to /defaultPrefix/overriddenPrefix', async function () { 117 | const res = await app.inject({ url: '/defaultPrefix/overriddenPrefix' }) 118 | assert.strictEqual(res.statusCode, 404) 119 | }) 120 | 121 | it('should respond correctly to /overriddenPrefix', async function () { 122 | const res = await app.inject({ url: '/overriddenPrefix' }) 123 | assert.strictEqual(res.statusCode, 200) 124 | assert.deepStrictEqual(JSON.parse(res.payload), { overide: 'prefix' }) 125 | }) 126 | 127 | it('should respond correctly to /noPrefix', async function () { 128 | const res = await app.inject({ url: '/noPrefix' }) 129 | assert.strictEqual(res.statusCode, 200) 130 | assert.deepStrictEqual(JSON.parse(res.payload), { no: 'prefix' }) 131 | }) 132 | 133 | it('should respond correctly to /one/', async function () { 134 | const res = await app.inject({ url: '/one/' }) 135 | assert.strictEqual(res.statusCode, 200) 136 | assert.deepStrictEqual(JSON.parse(res.payload), { works: true }) 137 | }) 138 | 139 | it('should respond correctly to /one/two/three', async function () { 140 | const res = await app.inject({ url: '/one/two/three' }) 141 | assert.strictEqual(res.statusCode, 200) 142 | assert.deepStrictEqual(JSON.parse(res.payload), { works: true }) 143 | }) 144 | 145 | it('should respond correctly to /ten/', async function () { 146 | const res = await app.inject({ url: '/ten/' }) 147 | assert.strictEqual(res.statusCode, 200) 148 | assert.deepStrictEqual(JSON.parse(res.payload), { works: true }) 149 | }) 150 | 151 | it('should respond correctly to /ten/eight', async function () { 152 | const res = await app.inject({ url: '/ten/eight' }) 153 | assert.strictEqual(res.statusCode, 200) 154 | assert.deepStrictEqual(JSON.parse(res.payload), { works: true }) 155 | }) 156 | 157 | it('should respond correctly to /nested/shallow', async function () { 158 | const res = await app.inject({ url: '/nested/shallow' }) 159 | assert.strictEqual(res.statusCode, 200) 160 | assert.deepStrictEqual(JSON.parse(res.payload), { works: true }) 161 | }) 162 | 163 | it('should respond correctly to /nested/shallow/deep', async function () { 164 | const res = await app.inject({ url: '/nested/shallow/deep' }) 165 | assert.strictEqual(res.statusCode, 200) 166 | assert.deepStrictEqual(JSON.parse(res.payload), { works: true }) 167 | }) 168 | 169 | it('should respond correctly to /nested/shallow/deep/deeper', async function () { 170 | const res = await app.inject({ url: '/nested/shallow/deep/deeper' }) 171 | assert.strictEqual(res.statusCode, 404) 172 | assert.deepStrictEqual(JSON.parse(res.payload), { 173 | message: 'Route GET:/nested/shallow/deep/deeper not found', 174 | error: 'Not Found', 175 | statusCode: 404, 176 | }) 177 | }) 178 | 179 | it('should respond correctly to /configPrefix', async function () { 180 | const res = await app.inject({ url: '/configPrefix' }) 181 | assert.strictEqual(res.statusCode, 200) 182 | assert.deepStrictEqual(JSON.parse(res.payload), { configPrefix: true }) 183 | }) 184 | 185 | it('should respond correctly to /configPrefixCallback', async function () { 186 | const res = await app.inject({ url: '/configPrefixCallback' }) 187 | assert.strictEqual(res.statusCode, 200) 188 | assert.deepStrictEqual(JSON.parse(res.payload), { 189 | configPrefixCallback: true, 190 | }) 191 | }) 192 | }) 193 | -------------------------------------------------------------------------------- /test/module/basic/app.js: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs' 2 | import path, { dirname } from 'node:path' 3 | import autoLoad from '../../../index.js' 4 | import { fileURLToPath } from 'node:url' 5 | 6 | const __filename = fileURLToPath(import.meta.url) 7 | const __dirname = dirname(__filename) 8 | 9 | export default function (fastify, opts, next) { 10 | fastify.register(autoLoad, { 11 | dir: path.join(__dirname, 'foo'), 12 | options: { foo: 'bar' }, 13 | ignorePattern: /^ignored/ 14 | }) 15 | 16 | fastify.register(autoLoad, { 17 | dir: path.join(__dirname, 'defaultPrefix'), 18 | options: { prefix: '/defaultPrefix' } 19 | }) 20 | 21 | fastify.register(autoLoad, { 22 | dir: path.join(__dirname, 'one'), 23 | options: { prefix: 'one/' } 24 | }) 25 | 26 | fastify.register(autoLoad, { 27 | dir: path.join(__dirname, 'ten'), 28 | dirNameRoutePrefix: false, 29 | options: { prefix: 'ten/' } 30 | }) 31 | 32 | fastify.register(autoLoad, { 33 | dir: path.join(__dirname, 'nested'), 34 | maxDepth: 2, 35 | options: { prefix: 'nested/' } 36 | }) 37 | 38 | const skipDir = path.join(__dirname, 'skip') 39 | fs.mkdir(path.join(skipDir, 'empty'), () => { 40 | fastify.register(autoLoad, { 41 | dir: skipDir 42 | }) 43 | 44 | next() 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /test/module/basic/defaultPrefix/defaultPrefix.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | export default function (f, opts, next) { 4 | f.get('/', (request, reply) => { 5 | reply.send({ index: true }) 6 | }) 7 | 8 | next() 9 | } 10 | -------------------------------------------------------------------------------- /test/module/basic/defaultPrefix/noDefaultPrefix.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | export default function (f, opts, next) { 4 | f.get('/noPrefix', (request, reply) => { 5 | reply.send({ no: 'prefix' }) 6 | }) 7 | 8 | next() 9 | } 10 | 11 | export const prefixOverride = '' 12 | -------------------------------------------------------------------------------- /test/module/basic/defaultPrefix/overridePrefix.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | export default function (f, opts, next) { 4 | f.get('/', (request, reply) => { 5 | reply.send({ overide: 'prefix' }) 6 | }) 7 | 8 | next() 9 | } 10 | 11 | export const autoPrefix = '/notUsed' 12 | 13 | export const prefixOverride = '/overriddenPrefix' 14 | -------------------------------------------------------------------------------- /test/module/basic/defaultPrefix/prefixed.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | export default function (f, opts, next) { 4 | f.get('/', (request, reply) => { 5 | reply.send({ prefixed: true }) 6 | }) 7 | 8 | next() 9 | } 10 | 11 | export const autoPrefix = '/prefixed' 12 | -------------------------------------------------------------------------------- /test/module/basic/foo/autoroute/get.js: -------------------------------------------------------------------------------- 1 | export default function (f, opts, next) { 2 | f.get('/items/:id', (request, reply) => { 3 | reply.send({ answer: 42 }) 4 | }) 5 | 6 | next() 7 | } 8 | -------------------------------------------------------------------------------- /test/module/basic/foo/autoroute/list.js: -------------------------------------------------------------------------------- 1 | export default function (f, opts, next) { 2 | f.get('/items', (request, reply) => { 3 | reply.send([{ answer: 42 }, { answer: 41 }]) 4 | }) 5 | 6 | next() 7 | } 8 | -------------------------------------------------------------------------------- /test/module/basic/foo/autowrap/get.js: -------------------------------------------------------------------------------- 1 | export default { 2 | method: 'GET', 3 | url: '/:id', 4 | handler: (request, reply) => { 5 | reply.send({ answer: 42 }) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/module/basic/foo/autowrap/list.js: -------------------------------------------------------------------------------- 1 | export default { 2 | method: 'GET', 3 | url: '/', 4 | handler: (request, reply) => { 5 | reply.send([{ answer: 42 }, { answer: 41 }]) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/module/basic/foo/bar/index.js: -------------------------------------------------------------------------------- 1 | export default function (f, opts, next) { 2 | f.get('/', (request, reply) => { 3 | reply.send({ foo: 'bar' }) 4 | }) 5 | 6 | next() 7 | } 8 | -------------------------------------------------------------------------------- /test/module/basic/foo/commonjs.cjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (f, opts, next) { 4 | f.get('/commonjs', (request, reply) => { 5 | reply.send(opts) 6 | }) 7 | 8 | next() 9 | } 10 | -------------------------------------------------------------------------------- /test/module/basic/foo/configPrefix.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | export default function (f, opts, next) { 4 | f.get('/', (request, reply) => { 5 | reply.send({ configPrefix: true }) 6 | }) 7 | 8 | next() 9 | } 10 | 11 | export const autoConfig = { 12 | prefix: '/configPrefix' 13 | } 14 | -------------------------------------------------------------------------------- /test/module/basic/foo/configPrefixCallback.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | export default function (f, opts, next) { 4 | f.get('/', (request, reply) => { 5 | reply.send({ configPrefixCallback: true }) 6 | }) 7 | 8 | next() 9 | } 10 | 11 | export const autoConfig = () => ({}) 12 | autoConfig.prefix = '/configPrefixCallback' 13 | -------------------------------------------------------------------------------- /test/module/basic/foo/ignored.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.exit(1) 4 | -------------------------------------------------------------------------------- /test/module/basic/foo/ignored/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.exit(1) 4 | -------------------------------------------------------------------------------- /test/module/basic/foo/manualprefix/get.js: -------------------------------------------------------------------------------- 1 | export default function (f, opts, next) { 2 | f.get('/items/:id', (request, reply) => { 3 | reply.send({ answer: 42 }) 4 | }) 5 | 6 | next() 7 | } 8 | -------------------------------------------------------------------------------- /test/module/basic/foo/manualprefix/index.js: -------------------------------------------------------------------------------- 1 | import list from './list.js' 2 | import get from './get.js' 3 | 4 | export default function (fastify, opts, next) { 5 | fastify.register(list) 6 | fastify.register(get) 7 | 8 | next() 9 | } 10 | 11 | export const autoPrefix = '/semiautomatic' 12 | -------------------------------------------------------------------------------- /test/module/basic/foo/manualprefix/list.js: -------------------------------------------------------------------------------- 1 | export default function (f, opts, next) { 2 | f.get('/items', (request, reply) => { 3 | reply.send([{ answer: 42 }, { answer: 41 }]) 4 | }) 5 | 6 | next() 7 | } 8 | -------------------------------------------------------------------------------- /test/module/basic/foo/module.mjs: -------------------------------------------------------------------------------- 1 | export default function (f, opts, next) { 2 | f.get('/module', (request, reply) => { 3 | reply.send(opts) 4 | }) 5 | 6 | next() 7 | } 8 | -------------------------------------------------------------------------------- /test/module/basic/foo/options.js: -------------------------------------------------------------------------------- 1 | export default function (f, opts, next) { 2 | f.get('/options', (request, reply) => { 3 | reply.send(opts) 4 | }) 5 | 6 | next() 7 | } 8 | -------------------------------------------------------------------------------- /test/module/basic/foo/prefixed.js: -------------------------------------------------------------------------------- 1 | export default function (f, opts, next) { 2 | f.get('/', (request, reply) => { 3 | reply.send({ something: 'else' }) 4 | }) 5 | 6 | next() 7 | } 8 | 9 | export const autoPrefix = '/prefixed' 10 | -------------------------------------------------------------------------------- /test/module/basic/foo/skipAutoload.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | export default function (f, opts, next) { 4 | f.get('/skip', (request, reply) => { 5 | reply.send('skip') 6 | }) 7 | 8 | next() 9 | } 10 | 11 | export const autoload = false 12 | -------------------------------------------------------------------------------- /test/module/basic/foo/something.js: -------------------------------------------------------------------------------- 1 | export default function (f, opts, next) { 2 | f.get('/something', (request, reply) => { 3 | reply.send({ something: 'else' }) 4 | }) 5 | 6 | next() 7 | } 8 | -------------------------------------------------------------------------------- /test/module/basic/foo/something.sh: -------------------------------------------------------------------------------- 1 | echo "hello world" 2 | -------------------------------------------------------------------------------- /test/module/basic/nested/shallow/deep/deeper/index.js: -------------------------------------------------------------------------------- 1 | export default async (server, opts) => { 2 | server.get('/', async (req, reply) => { 3 | reply.send({ works: true }) 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/module/basic/nested/shallow/deep/index.js: -------------------------------------------------------------------------------- 1 | export default async (server, opts) => { 2 | server.get('/', async (req, reply) => { 3 | reply.send({ works: true }) 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/module/basic/nested/shallow/index.js: -------------------------------------------------------------------------------- 1 | export default async (server, opts) => { 2 | server.get('/', async (req, reply) => { 3 | reply.send({ works: true }) 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/module/basic/one/index.js: -------------------------------------------------------------------------------- 1 | export default async (server, opts) => { 2 | server.get('/', async (req, reply) => { 3 | reply.send({ works: true }) 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/module/basic/one/two/index.js: -------------------------------------------------------------------------------- 1 | export default async (server, opts) => { 2 | server.get('/three', async (req, reply) => { 3 | reply.send({ works: true }) 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/module/basic/skip/noJS/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | *.js 3 | -------------------------------------------------------------------------------- /test/module/basic/ten/index.js: -------------------------------------------------------------------------------- 1 | export default async (server, opts) => { 2 | server.get('/', async (req, reply) => { 3 | reply.send({ works: true }) 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/module/basic/ten/nine/index.js: -------------------------------------------------------------------------------- 1 | export default async (server, opts) => { 2 | server.get('/eight', async (req, reply) => { 3 | reply.send({ works: true }) 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/module/dependency.js: -------------------------------------------------------------------------------- 1 | import { after, before, describe, it } from 'node:test' 2 | import assert from 'node:assert' 3 | import fastify from 'fastify' 4 | import dependencyApp from './dependency/app.js' 5 | 6 | describe('dependency tests', function () { 7 | const app = fastify() 8 | 9 | before(async function () { 10 | app.register(dependencyApp) 11 | await app.ready() 12 | }) 13 | 14 | after(async function () { 15 | await app.close() 16 | }) 17 | 18 | it('should respond correctly to /plugin-a', async function () { 19 | const res = await app.inject({ url: '/plugin-a' }) 20 | assert.strictEqual(res.statusCode, 200) 21 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'plugin-a' }) 22 | }) 23 | 24 | it('should respond correctly to /plugin-b', async function () { 25 | const res = await app.inject({ url: '/plugin-b' }) 26 | assert.strictEqual(res.statusCode, 200) 27 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'plugin-b' }) 28 | }) 29 | 30 | it('should respond correctly to /plugin-c', async function () { 31 | const res = await app.inject({ url: '/plugin-c' }) 32 | assert.strictEqual(res.statusCode, 200) 33 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'plugin-c' }) 34 | }) 35 | 36 | it('should respond correctly to /plugin-d', async function () { 37 | const res = await app.inject({ url: '/plugin-d' }) 38 | assert.strictEqual(res.statusCode, 200) 39 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'plugin-d' }) 40 | }) 41 | 42 | it('should respond correctly to /plugin-e', async function () { 43 | const res = await app.inject({ url: '/plugin-e' }) 44 | assert.strictEqual(res.statusCode, 200) 45 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'plugin-e' }) 46 | }) 47 | 48 | it('should respond correctly to /plugin-f', async function () { 49 | const res = await app.inject({ url: '/plugin-f' }) 50 | assert.strictEqual(res.statusCode, 200) 51 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'plugin-f' }) 52 | }) 53 | 54 | it('should respond correctly to /plugin-g', async function () { 55 | const res = await app.inject({ url: '/plugin-g' }) 56 | assert.strictEqual(res.statusCode, 200) 57 | assert.deepStrictEqual(JSON.parse(res.payload).path, '/plugin-g') 58 | }) 59 | }) 60 | -------------------------------------------------------------------------------- /test/module/dependency/app.js: -------------------------------------------------------------------------------- 1 | import path, { dirname } from 'node:path' 2 | import fastifyUrlData from '@fastify/url-data' 3 | import autoLoad from '../../../index.js' 4 | import { fileURLToPath } from 'node:url' 5 | 6 | const __filename = fileURLToPath(import.meta.url) 7 | const __dirname = dirname(__filename) 8 | 9 | export default function (fastify, opts, next) { 10 | fastify.register(fastifyUrlData) 11 | 12 | fastify.register(autoLoad, { 13 | dir: path.join(__dirname, 'plugins') 14 | }) 15 | 16 | next() 17 | } 18 | -------------------------------------------------------------------------------- /test/module/dependency/plugins/plugin-a.js: -------------------------------------------------------------------------------- 1 | import fp from 'fastify-plugin' 2 | 3 | function plugin (f, opts, next) { 4 | if (!f.pluginD || !f.pluginE || !f.pluginF) { 5 | return next() 6 | } 7 | 8 | f.get('/plugin-a', (request, reply) => { 9 | reply.send({ data: 'plugin-a' }) 10 | }) 11 | 12 | f.decorate('pluginA', true) 13 | 14 | next() 15 | } 16 | 17 | export default fp(plugin, { 18 | name: 'plugin-a', 19 | dependencies: ['plugin-d'] 20 | }) 21 | -------------------------------------------------------------------------------- /test/module/dependency/plugins/plugin-b.js: -------------------------------------------------------------------------------- 1 | import fp from 'fastify-plugin' 2 | 3 | function plugin (f, opts, next) { 4 | if (!f.pluginE || !f.pluginF) { 5 | return next() 6 | } 7 | 8 | f.get('/plugin-b', (request, reply) => { 9 | reply.send({ data: 'plugin-b' }) 10 | }) 11 | 12 | f.decorate('pluginB', true) 13 | 14 | next() 15 | } 16 | 17 | export default fp(plugin, { 18 | name: 'plugin-b', 19 | dependencies: ['plugin-e'] 20 | }) 21 | -------------------------------------------------------------------------------- /test/module/dependency/plugins/plugin-c.js: -------------------------------------------------------------------------------- 1 | import fp from 'fastify-plugin' 2 | 3 | function plugin (f, opts, next) { 4 | if (!f.pluginE || !f.pluginF) { 5 | return next() 6 | } 7 | 8 | f.get('/plugin-c', (request, reply) => { 9 | reply.send({ data: 'plugin-c' }) 10 | }) 11 | 12 | f.decorate('pluginC', true) 13 | 14 | next() 15 | } 16 | 17 | export default fp(plugin, { 18 | name: 'plugin-c', 19 | dependencies: ['plugin-e'] 20 | }) 21 | -------------------------------------------------------------------------------- /test/module/dependency/plugins/plugin-d.js: -------------------------------------------------------------------------------- 1 | import fp from 'fastify-plugin' 2 | 3 | function plugin (f, opts, next) { 4 | if (!f.pluginE || !f.pluginF) { 5 | return next() 6 | } 7 | 8 | f.get('/plugin-d', (request, reply) => { 9 | reply.send({ data: 'plugin-d' }) 10 | }) 11 | 12 | f.decorate('pluginD', true) 13 | 14 | next() 15 | } 16 | 17 | export default fp(plugin, { 18 | name: 'plugin-d', 19 | dependencies: ['plugin-e'] 20 | }) 21 | -------------------------------------------------------------------------------- /test/module/dependency/plugins/plugin-e.js: -------------------------------------------------------------------------------- 1 | import fp from 'fastify-plugin' 2 | 3 | function plugin (f, opts, next) { 4 | if (!f.pluginF) { 5 | return next() 6 | } 7 | 8 | f.get('/plugin-e', (request, reply) => { 9 | reply.send({ data: 'plugin-e' }) 10 | }) 11 | 12 | f.decorate('pluginE', true) 13 | 14 | next() 15 | } 16 | 17 | export default fp(plugin, { 18 | name: 'plugin-e', 19 | dependencies: ['plugin-f'] 20 | }) 21 | -------------------------------------------------------------------------------- /test/module/dependency/plugins/plugin-f.js: -------------------------------------------------------------------------------- 1 | import fp from 'fastify-plugin' 2 | 3 | function plugin (f, opts, next) { 4 | f.get('/plugin-f', (request, reply) => { 5 | reply.send({ data: 'plugin-f' }) 6 | }) 7 | 8 | f.decorate('pluginF', true) 9 | 10 | next() 11 | } 12 | 13 | export default fp(plugin, { 14 | name: 'plugin-f' 15 | }) 16 | -------------------------------------------------------------------------------- /test/module/dependency/plugins/plugin-g.js: -------------------------------------------------------------------------------- 1 | import fp from 'fastify-plugin' 2 | 3 | function plugin (f, opts, next) { 4 | f.get('/plugin-g', (request, reply) => { 5 | const data = request.urlData() 6 | 7 | reply.send(data) 8 | }) 9 | 10 | next() 11 | } 12 | 13 | export default fp(plugin, { 14 | name: 'plugin-g', 15 | dependencies: ['@fastify/url-data'] 16 | }) 17 | -------------------------------------------------------------------------------- /test/module/esm-import.js: -------------------------------------------------------------------------------- 1 | import { afterEach, describe, it } from 'node:test' 2 | import assert from 'node:assert' 3 | import fastify from 'fastify' 4 | import esmImportAppDefault from './esm-import/app-default.js' 5 | import esmImportAppNamed from './esm-import/app-named.js' 6 | import esmImportAppStarDefault from './esm-import/app-star-default.js' 7 | import esmImportAppStarNamed from './esm-import/app-star-named.js' 8 | 9 | describe('ESM import tests', async function () { 10 | let app 11 | afterEach(async () => { 12 | await app.close() 13 | }) 14 | 15 | it('default', async function () { 16 | app = fastify() 17 | app.register(esmImportAppDefault) 18 | await app.ready() 19 | 20 | const res = await app.inject('/default') 21 | assert.strictEqual(res.statusCode, 200) 22 | assert.deepStrictEqual(res.json(), { script: 'default' }) 23 | await app.close() 24 | }) 25 | 26 | it('named', async function () { 27 | app = fastify() 28 | app.register(esmImportAppNamed) 29 | await app.ready() 30 | 31 | const res = await app.inject('/named') 32 | assert.strictEqual(res.statusCode, 200) 33 | assert.deepStrictEqual(res.json(), { script: 'named' }) 34 | await app.close() 35 | }) 36 | 37 | it('star default', async function () { 38 | app = fastify() 39 | app.register(esmImportAppStarDefault) 40 | await app.ready() 41 | 42 | const res = await app.inject('/star-default') 43 | assert.strictEqual(res.statusCode, 200) 44 | assert.deepStrictEqual(res.json(), { script: 'star-default' }) 45 | await app.close() 46 | }) 47 | 48 | it('star named', async function () { 49 | app = fastify() 50 | app.register(esmImportAppStarNamed) 51 | await app.ready() 52 | 53 | const res = await app.inject('/star-named') 54 | assert.strictEqual(res.statusCode, 200) 55 | assert.deepStrictEqual(res.json(), { script: 'star-named' }) 56 | }) 57 | }) 58 | -------------------------------------------------------------------------------- /test/module/esm-import/app-default.js: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | import { fileURLToPath } from 'node:url' 3 | 4 | import fastifyAutoloadDefault from '../../../index.js' 5 | 6 | const { dirname } = path 7 | const __filename = fileURLToPath(import.meta.url) 8 | const __dirname = dirname(__filename) 9 | 10 | export default async function (fastify, opts) { 11 | fastify.register(fastifyAutoloadDefault, { 12 | dir: path.join(__dirname, 'plugins', 'default'), 13 | options: { foo: 'bar' } 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /test/module/esm-import/app-named.js: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | import { fileURLToPath } from 'node:url' 3 | 4 | import { fastifyAutoload as fastifyAutoloadNamed } from '../../../index.js' 5 | 6 | const { dirname } = path 7 | const __filename = fileURLToPath(import.meta.url) 8 | const __dirname = dirname(__filename) 9 | 10 | export default async function (fastify, opts) { 11 | fastify.register(fastifyAutoloadNamed, { 12 | dir: path.join(__dirname, 'plugins', 'named'), 13 | options: { foo: 'bar' } 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /test/module/esm-import/app-star-default.js: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | import { fileURLToPath } from 'node:url' 3 | 4 | import * as fastifyAutoloadStar from '../../../index.js' 5 | 6 | const { dirname } = path 7 | const __filename = fileURLToPath(import.meta.url) 8 | const __dirname = dirname(__filename) 9 | 10 | export default async function (fastify, opts) { 11 | fastify.register(fastifyAutoloadStar.default, { 12 | dir: path.join(__dirname, 'plugins', 'star-default'), 13 | options: { foo: 'bar' } 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /test/module/esm-import/app-star-named.js: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | import { fileURLToPath } from 'node:url' 3 | 4 | import * as fastifyAutoloadStar from '../../../index.js' 5 | 6 | const { dirname } = path 7 | const __filename = fileURLToPath(import.meta.url) 8 | const __dirname = dirname(__filename) 9 | 10 | export default async function (fastify, opts) { 11 | fastify.register(fastifyAutoloadStar.fastifyAutoload, { 12 | dir: path.join(__dirname, 'plugins', 'star-named'), 13 | options: { foo: 'bar' } 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /test/module/esm-import/plugins/default/index.js: -------------------------------------------------------------------------------- 1 | export default async function (fastify, opts) { 2 | fastify.get('/default', async (request, reply) => { 3 | return { script: 'default' } 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/module/esm-import/plugins/named/index.js: -------------------------------------------------------------------------------- 1 | export default async function (fastify, opts) { 2 | fastify.get('/named', async (request, reply) => { 3 | return { script: 'named' } 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/module/esm-import/plugins/star-default/index.js: -------------------------------------------------------------------------------- 1 | export default async function (fastify, opts) { 2 | fastify.get('/star-default', async (request, reply) => { 3 | return { script: 'star-default' } 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/module/esm-import/plugins/star-named/index.js: -------------------------------------------------------------------------------- 1 | export default async function (fastify, opts) { 2 | fastify.get('/star-named', async (request, reply) => { 3 | return { script: 'star-named' } 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/module/index-package.js: -------------------------------------------------------------------------------- 1 | import { after, before, describe, it } from 'node:test' 2 | import assert from 'node:assert' 3 | import fastify from 'fastify' 4 | import indexPackage from './index-package/app.js' 5 | 6 | describe('index package', function () { 7 | const app = fastify() 8 | 9 | before(async function () { 10 | app.register(indexPackage) 11 | await app.ready() 12 | }) 13 | 14 | after(async function () { 15 | await app.close() 16 | }) 17 | 18 | it('should handle foo/bar correctly', async function () { 19 | const res = await app.inject('/foo/bar') 20 | 21 | assert.strictEqual(res.statusCode, 200) 22 | assert.deepStrictEqual(res.json(), { success: true }) 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /test/module/index-package/app.js: -------------------------------------------------------------------------------- 1 | import url from 'node:url' 2 | import path from 'node:path' 3 | import fastifyAutoload from '../../../index.js' 4 | 5 | export default function (fastify, opts, next) { 6 | fastify.register(fastifyAutoload, { 7 | dir: path.dirname(url.fileURLToPath(import.meta.url)) 8 | }) 9 | 10 | next() 11 | } 12 | 13 | export const autoload = false 14 | -------------------------------------------------------------------------------- /test/module/index-package/foo/bar.js: -------------------------------------------------------------------------------- 1 | export default function (f, opts, next) { 2 | f.get('/bar', (request, reply) => { 3 | reply.send({ success: true }) 4 | }) 5 | 6 | next() 7 | } 8 | -------------------------------------------------------------------------------- /test/module/index-package/index.js: -------------------------------------------------------------------------------- 1 | import url from 'node:url' 2 | import process from 'node:process' 3 | 4 | if (url.fileURLToPath(import.meta.url) === process.argv[1]) { 5 | // side effects 6 | } 7 | 8 | export const autoload = false 9 | -------------------------------------------------------------------------------- /test/module/index-package/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "index-package", 3 | "type": "module" 4 | } 5 | -------------------------------------------------------------------------------- /test/module/options.js: -------------------------------------------------------------------------------- 1 | import { after, before, describe, it } from 'node:test' 2 | import assert from 'node:assert' 3 | import fastify from 'fastify' 4 | import optionsApp from './options/app.js' 5 | 6 | describe('options tests', function () { 7 | const app = fastify() 8 | 9 | before(async function () { 10 | app.decorate('root', 'root') 11 | app.register(optionsApp) 12 | await app.ready() 13 | }) 14 | 15 | after(async function () { 16 | await app.close() 17 | }) 18 | 19 | it('should respond correctly to /plugin-a', async function () { 20 | const res = await app.inject({ url: '/plugin-a' }) 21 | assert.strictEqual(res.statusCode, 200) 22 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'test-1' }) 23 | }) 24 | 25 | it('should respond correctly to /plugin-b', async function () { 26 | const res = await app.inject({ url: '/plugin-b' }) 27 | assert.strictEqual(res.statusCode, 200) 28 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'override' }) 29 | }) 30 | 31 | it('should respond correctly to /plugin-default', async function () { 32 | const res = await app.inject({ url: '/plugin-default' }) 33 | assert.strictEqual(res.statusCode, 200) 34 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'default' }) 35 | }) 36 | 37 | it('should respond correctly to /plugin-c', async function () { 38 | const res = await app.inject({ url: '/plugin-c' }) 39 | assert.strictEqual(res.statusCode, 200) 40 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'c' }) 41 | }) 42 | 43 | it('should respond correctly to /plugin-d', async function () { 44 | const res = await app.inject({ url: '/plugin-d' }) 45 | assert.strictEqual(res.statusCode, 200) 46 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'test-3' }) 47 | }) 48 | 49 | it('should respond correctly to /plugin-e', async function () { 50 | const res = await app.inject({ url: '/plugin-e' }) 51 | assert.strictEqual(res.statusCode, 200) 52 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'test-4-root' }) 53 | }) 54 | 55 | it('should respond correctly to /plugin-y', async function () { 56 | const res = await app.inject({ url: '/plugin-y' }) 57 | assert.strictEqual(res.statusCode, 200) 58 | assert.deepStrictEqual(JSON.parse(res.payload), { data: 'y' }) 59 | }) 60 | }) 61 | -------------------------------------------------------------------------------- /test/module/options/app.js: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | import fastifyUrlData from '@fastify/url-data' 3 | import { fileURLToPath } from 'node:url' 4 | import autoLoad from '../../../index.js' 5 | 6 | const { dirname } = path 7 | const __filename = fileURLToPath(import.meta.url) 8 | const __dirname = dirname(__filename) 9 | 10 | export default function (fastify, opts, next) { 11 | fastify.register(fastifyUrlData) 12 | 13 | fastify.register(autoLoad, { 14 | dir: path.join(__dirname, 'plugins'), 15 | options: { 16 | b: 'override' 17 | } 18 | }) 19 | 20 | fastify.register(autoLoad, { 21 | dir: path.join(__dirname, 'plugins-2') 22 | }) 23 | 24 | fastify.register(autoLoad, { 25 | dir: path.join(__dirname, 'plugins-3') 26 | }) 27 | 28 | next() 29 | } 30 | -------------------------------------------------------------------------------- /test/module/options/lib-plugin.js: -------------------------------------------------------------------------------- 1 | import fp from 'fastify-plugin' 2 | 3 | function plugin (f, opts, next) { 4 | const { name = 'default' } = opts 5 | f.get('/plugin-' + name, (request, reply) => { 6 | reply.send({ data: name }) 7 | }) 8 | 9 | next() 10 | } 11 | 12 | export default fp(plugin, { name: 'lib-plugin' }) 13 | -------------------------------------------------------------------------------- /test/module/options/plugins-2/plugin-x.js: -------------------------------------------------------------------------------- 1 | import plugin from '../lib-plugin.js' 2 | 3 | export default plugin 4 | -------------------------------------------------------------------------------- /test/module/options/plugins-3/plugin-y.js: -------------------------------------------------------------------------------- 1 | import plugin from '../lib-plugin.js' 2 | 3 | export default plugin 4 | export const autoConfig = { name: 'y' } 5 | -------------------------------------------------------------------------------- /test/module/options/plugins/plugin-a.js: -------------------------------------------------------------------------------- 1 | import fp from 'fastify-plugin' 2 | 3 | function plugin (f, opts, next) { 4 | f.get('/plugin-a', (request, reply) => { 5 | reply.send({ data: opts.a }) 6 | }) 7 | 8 | next() 9 | } 10 | 11 | export default fp(plugin, { name: 'plugin-a' }) 12 | export const autoConfig = { a: 'test-1' } 13 | -------------------------------------------------------------------------------- /test/module/options/plugins/plugin-b.js: -------------------------------------------------------------------------------- 1 | import fp from 'fastify-plugin' 2 | 3 | function plugin (f, opts, next) { 4 | f.get('/plugin-b', (request, reply) => { 5 | reply.send({ data: opts.b }) 6 | }) 7 | 8 | next() 9 | } 10 | 11 | export default fp(plugin, { name: 'plugin-b' }) 12 | export const autoConfig = { b: 'test-2' } 13 | -------------------------------------------------------------------------------- /test/module/options/plugins/plugin-c.js: -------------------------------------------------------------------------------- 1 | import plugin from '../lib-plugin.js' 2 | 3 | export default plugin 4 | export const autoConfig = { name: 'c' } 5 | -------------------------------------------------------------------------------- /test/module/options/plugins/plugin-d.js: -------------------------------------------------------------------------------- 1 | import fp from 'fastify-plugin' 2 | 3 | function plugin (f, opts, next) { 4 | f.get('/plugin-d', (request, reply) => { 5 | reply.send({ data: opts.d }) 6 | }) 7 | 8 | next() 9 | } 10 | 11 | export default fp(plugin, { name: 'plugin-d' }) 12 | export const autoConfig = { d: 'test-3' } 13 | -------------------------------------------------------------------------------- /test/module/options/plugins/plugin-e.js: -------------------------------------------------------------------------------- 1 | import fp from 'fastify-plugin' 2 | 3 | function plugin (f, opts, next) { 4 | f.get('/plugin-e', (request, reply) => { 5 | reply.send({ data: opts.e }) 6 | }) 7 | 8 | next() 9 | } 10 | 11 | export default fp(plugin, { name: 'plugin-e' }) 12 | export const autoConfig = (fastify) => { 13 | return { e: 'test-4-' + fastify.root } 14 | } 15 | -------------------------------------------------------------------------------- /test/module/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module" 3 | } -------------------------------------------------------------------------------- /test/module/route-parameters.js: -------------------------------------------------------------------------------- 1 | import { after, before, describe, it } from 'node:test' 2 | import assert from 'node:assert' 3 | import fastify from 'fastify' 4 | import routeParametersBasic from './route-parameters/basic.mjs' 5 | import routeParametersDisabled from './route-parameters/disabled.mjs' 6 | 7 | describe('routeParams: Default behaviour', function () { 8 | const app = fastify() 9 | 10 | before(async function () { 11 | app.register(routeParametersBasic) 12 | await app.ready() 13 | }) 14 | 15 | after(async function () { 16 | await app.close() 17 | }) 18 | 19 | it('should handle root route correctly', async function () { 20 | const res = await app.inject('/') 21 | assert.strictEqual(res.statusCode, 200) 22 | assert.deepStrictEqual(res.json(), { route: '/' }) 23 | }) 24 | 25 | it('should handle /pages route correctly', async function () { 26 | const res = await app.inject('/pages') 27 | assert.strictEqual(res.statusCode, 200) 28 | assert.deepStrictEqual(res.json(), { route: '/pages' }) 29 | }) 30 | 31 | it('should handle /pages/archived route correctly', async function () { 32 | const res = await app.inject('/pages/archived') 33 | assert.strictEqual(res.statusCode, 200) 34 | assert.deepStrictEqual(res.json(), { route: '/pages/archived' }) 35 | }) 36 | 37 | it('should handle /pages/:id route correctly', async function () { 38 | const res = await app.inject('/pages/test_id') 39 | assert.strictEqual(res.statusCode, 200) 40 | assert.deepStrictEqual(res.json(), { route: '/pages/:id/', id: 'test_id' }) 41 | }) 42 | 43 | it('should handle /pages/:id/edit route correctly', async function () { 44 | const res = await app.inject('/pages/test_id/edit') 45 | assert.strictEqual(res.statusCode, 200) 46 | assert.deepStrictEqual(res.json(), { 47 | route: '/pages/:id/edit', 48 | id: 'test_id', 49 | }) 50 | }) 51 | 52 | it('should handle /users/:id/details route correctly', async function () { 53 | const res = await app.inject('/users/test_id/details') 54 | assert.strictEqual(res.statusCode, 200) 55 | assert.deepStrictEqual(res.json(), { 56 | route: '/users/:id/details', 57 | id: 'test_id', 58 | }) 59 | }) 60 | }) 61 | 62 | describe('routeParams: off', function () { 63 | const app = fastify() 64 | 65 | before(async function () { 66 | app.register(routeParametersDisabled) 67 | await app.ready() 68 | }) 69 | 70 | after(async function () { 71 | await app.close() 72 | }) 73 | 74 | it('should handle root route correctly', async function () { 75 | const res = await app.inject('/') 76 | assert.strictEqual(res.statusCode, 200) 77 | assert.deepStrictEqual(res.json(), { route: '/' }) 78 | }) 79 | 80 | it('should handle /pages route correctly', async function () { 81 | const res = await app.inject('/pages') 82 | assert.strictEqual(res.statusCode, 200) 83 | assert.deepStrictEqual(res.json(), { route: '/pages' }) 84 | }) 85 | 86 | it('should handle /pages/archived route correctly', async function () { 87 | const res = await app.inject('/pages/archived') 88 | assert.strictEqual(res.statusCode, 200) 89 | assert.deepStrictEqual(res.json(), { route: '/pages/archived' }) 90 | }) 91 | 92 | it('should handle /pages/test_id route correctly', async function () { 93 | const res = await app.inject('/pages/test_id') 94 | assert.strictEqual(res.statusCode, 404) 95 | }) 96 | 97 | it('should handle /pages/test_id/edit route correctly', async function () { 98 | const res = await app.inject('/pages/test_id/edit') 99 | assert.strictEqual(res.statusCode, 404) 100 | }) 101 | 102 | it('should handle /pages/_id route correctly', async function () { 103 | const res = await app.inject('/pages/_id') 104 | assert.strictEqual(res.statusCode, 200) 105 | assert.deepStrictEqual(res.json(), { route: '/pages/:id/' }) 106 | }) 107 | 108 | it('should handle /pages/_id/edit route correctly', async function () { 109 | const res = await app.inject('/pages/_id/edit') 110 | assert.strictEqual(res.statusCode, 200) 111 | assert.deepStrictEqual(res.json(), { route: '/pages/:id/edit' }) 112 | }) 113 | 114 | it('should handle /users/test_id/details route correctly', async function () { 115 | const res = await app.inject('/users/test_id/details') 116 | assert.strictEqual(res.statusCode, 404) 117 | }) 118 | 119 | it('should handle /users/_id/details route correctly', async function () { 120 | const res = await app.inject('/users/_id/details') 121 | assert.strictEqual(res.statusCode, 200) 122 | assert.deepStrictEqual(res.json(), { route: '/users/:id/details' }) 123 | }) 124 | }) 125 | -------------------------------------------------------------------------------- /test/module/route-parameters/basic.mjs: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | import { fileURLToPath } from 'node:url' 3 | 4 | import autoload from '../../../index.js' 5 | 6 | const { dirname } = path 7 | const __filename = fileURLToPath(import.meta.url) 8 | const __dirname = dirname(__filename) 9 | 10 | export default async function (fastify, opts) { 11 | fastify.register(autoload, { 12 | dir: path.join(__dirname, 'routes'), 13 | routeParams: true 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /test/module/route-parameters/disabled.mjs: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | import { fileURLToPath } from 'node:url' 3 | 4 | import autoload from '../../../index.js' 5 | 6 | const { dirname } = path 7 | const __filename = fileURLToPath(import.meta.url) 8 | const __dirname = dirname(__filename) 9 | 10 | export default async function (fastify, opts) { 11 | fastify.register(autoload, { 12 | dir: path.join(__dirname, 'routes'), 13 | routeParams: false 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /test/module/route-parameters/routes/index.mjs: -------------------------------------------------------------------------------- 1 | export default async function (app, opts) { 2 | app.get('/', async function (req, reply) { 3 | return { route: '/' } 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/module/route-parameters/routes/pages/_id/actions.mjs: -------------------------------------------------------------------------------- 1 | export default async function (app, opts) { 2 | app.get('/', async function (req, reply) { 3 | return { route: '/pages/:id/', id: req.params.id } 4 | }) 5 | 6 | app.get('/edit', async function (req, reply) { 7 | return { route: '/pages/:id/edit', id: req.params.id } 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /test/module/route-parameters/routes/pages/routes.mjs: -------------------------------------------------------------------------------- 1 | export default async function (app, opts) { 2 | app.get('/', async function (req, reply) { 3 | return { route: '/pages' } 4 | }) 5 | 6 | app.get('/archived', async function (req, reply) { 7 | return { route: '/pages/archived', id: req.params.id } // should be null 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /test/module/route-parameters/routes/users/_id/actions.mjs: -------------------------------------------------------------------------------- 1 | export default async function (app, opts) { 2 | app.get('/details', async function (req, reply) { 3 | return { route: '/users/:id/details', id: req.params.id } 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /test/typescript-common/basic/app.ts: -------------------------------------------------------------------------------- 1 | import type { FastifyInstance } from 'fastify' 2 | 3 | const { join } = require('node:path') 4 | const fastifyAutoLoad = require('../../../index.js') 5 | 6 | module.exports = function (fastify: FastifyInstance, _opts: any, next: any) { 7 | fastify.register(fastifyAutoLoad, { 8 | dir: join(__dirname, 'foo'), 9 | }) 10 | 11 | next() 12 | } 13 | -------------------------------------------------------------------------------- /test/typescript-common/basic/foo/javascript.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (fastify, _opts, next) { 4 | fastify.get('/javascript', (_request, reply) => { 5 | reply.send({ script: 'java' }) 6 | }) 7 | 8 | next() 9 | } 10 | -------------------------------------------------------------------------------- /test/typescript-common/basic/foo/shouldBeIgnored.d.ts: -------------------------------------------------------------------------------- 1 | // See https://github.com/fastify/fastify-autoload/issues/65 2 | declare const definitionFilesAreIncluded: never 3 | export default definitionFilesAreIncluded 4 | -------------------------------------------------------------------------------- /test/typescript-common/basic/foo/typescript.ts: -------------------------------------------------------------------------------- 1 | module.exports = function (fastify: any, _opts, next) { 2 | fastify.get('/typescript', (_request, reply) => { 3 | reply.send({ script: 'type' }) 4 | }) 5 | 6 | next() 7 | } 8 | -------------------------------------------------------------------------------- /test/typescript-common/index.ts: -------------------------------------------------------------------------------- 1 | 'use script' 2 | 3 | const { test: t } = require('node:test') 4 | const fastify = require('fastify') 5 | 6 | const basicApp = require('./basic/app.ts') 7 | 8 | t.plan(5) 9 | 10 | const app = fastify() 11 | 12 | app.register(basicApp) 13 | 14 | app.ready(async function (err) { 15 | t.error(err) 16 | 17 | await app 18 | .inject({ 19 | url: '/javascript', 20 | }) 21 | .then(function (res: any) { 22 | t.equal(res.statusCode, 200) 23 | t.same(JSON.parse(res.payload), { script: 'java' }) 24 | }) 25 | .catch((err) => { 26 | t.error(err) 27 | }) 28 | 29 | await app 30 | .inject({ 31 | url: '/typescript', 32 | }) 33 | .then(function (res: any) { 34 | t.equal(res.statusCode, 200) 35 | t.same(JSON.parse(res.payload), { script: 'type' }) 36 | }) 37 | .catch((err) => { 38 | t.error(err) 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /test/typescript-esm/app/index.ts: -------------------------------------------------------------------------------- 1 | import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify' 2 | 3 | export default function (fastify: FastifyInstance, _: object, next: (err?: Error) => void): void { 4 | fastify.get('/installed', (_: FastifyRequest, reply: FastifyReply): void => { 5 | reply.send({ result: 'ok' }) 6 | }) 7 | 8 | next() 9 | } 10 | -------------------------------------------------------------------------------- /test/typescript-esm/forceESM.ts: -------------------------------------------------------------------------------- 1 | import test, { describe, before, after } from 'node:test' 2 | import assert from 'node:assert' 3 | import fastify from 'fastify' 4 | 5 | import { dirname, resolve } from 'node:path' 6 | import { fileURLToPath } from 'node:url' 7 | import fastifyAutoLoad from '../../index.js' 8 | 9 | const __dirname = dirname(fileURLToPath(import.meta.url)) 10 | 11 | describe('typescript/basic test suite', function () { 12 | const app = fastify() 13 | before(async function () { 14 | app.register(fastifyAutoLoad, { dir: resolve(__dirname, 'app'), forceESM: true }) 15 | await app.ready() 16 | }) 17 | 18 | after(async function () { 19 | await app.close() 20 | }) 21 | 22 | test('should load routes and respond correctly', async function () { 23 | const res = await app.inject({ url: '/installed' }) 24 | assert.strictEqual(res.statusCode, 200) 25 | assert.deepStrictEqual(JSON.parse(res.payload), { result: 'ok' }) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /test/typescript-esm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module" 3 | } -------------------------------------------------------------------------------- /test/typescript-jest/babel-node/index.test.ts: -------------------------------------------------------------------------------- 1 | import fastify from 'fastify' 2 | import { join } from 'node:path' 3 | import AutoLoad from '../../../' 4 | 5 | describe('load typescript using babel-node', () => { 6 | const app = fastify() 7 | 8 | beforeAll(done => { 9 | app.register(AutoLoad, { 10 | dir: join(__dirname, '../../commonjs/babel-node/routes') 11 | }) 12 | app.ready(done) 13 | }) 14 | 15 | it('tests the root route', async function () { 16 | const response = await app.inject({ 17 | method: 'GET', 18 | url: '/' 19 | }) 20 | expect(response.statusCode).toEqual(200) 21 | expect(JSON.parse(response.payload)).toEqual({ hello: 'world' }) 22 | }) 23 | it('tests /foo route', async function () { 24 | const response = await app.inject({ 25 | method: 'GET', 26 | url: '/foo' 27 | }) 28 | expect(response.statusCode).toBe(200) 29 | expect(JSON.parse(response.payload)).toEqual({ foo: 'bar' }) 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /test/typescript-jest/basic/app.ts: -------------------------------------------------------------------------------- 1 | import { FastifyPluginCallback } from 'fastify' 2 | import { join } from 'node:path' 3 | const fastifyAutoLoad = require('../../../index') 4 | 5 | const app: FastifyPluginCallback = function (fastify, _opts, next): void { 6 | fastify.register(fastifyAutoLoad, { 7 | dir: join(__dirname, 'foo'), 8 | }) 9 | 10 | next() 11 | } 12 | 13 | export default app 14 | -------------------------------------------------------------------------------- /test/typescript-jest/basic/basic.test.ts: -------------------------------------------------------------------------------- 1 | import fastify from 'fastify' 2 | import basicApp from './app' 3 | 4 | const app = fastify() 5 | 6 | app.register(basicApp) 7 | 8 | describe('load typescript plugin in jest environment', () => { 9 | beforeAll(done => { 10 | app.ready(done) 11 | }) 12 | 13 | it('should response code 200 OK', async () => { 14 | const { payload, statusCode } = await app.inject({ 15 | url: '/typescript' 16 | }) 17 | expect(statusCode).toBe(200) 18 | expect(JSON.parse(payload)).toMatchObject({ script: 'type' }) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /test/typescript-jest/basic/foo/typescript.ts: -------------------------------------------------------------------------------- 1 | import { FastifyPluginCallback } from 'fastify' 2 | 3 | const plugin: FastifyPluginCallback = function (fastify, _opts, next): void { 4 | fastify.get('/typescript', (_request, reply): void => { 5 | reply.send({ script: 'type' }) 6 | }) 7 | 8 | next() 9 | } 10 | 11 | export default plugin 12 | -------------------------------------------------------------------------------- /test/typescript-jest/integration/instance.ts: -------------------------------------------------------------------------------- 1 | import fastify from 'fastify' 2 | import basicApp from '../../typescript/basic/app' 3 | 4 | const app = fastify() 5 | app.register(basicApp) 6 | 7 | app.listen({ 8 | port: Math.floor(Math.random() * 3000 + 3000) 9 | }, function (err) { 10 | if (err) process.stderr.write('failed') 11 | process.stdout.write('success') 12 | app.close() 13 | }) 14 | -------------------------------------------------------------------------------- /test/typescript-jest/integration/integration.test.ts: -------------------------------------------------------------------------------- 1 | import { exec } from 'node:child_process' 2 | 3 | describe('integration test', function () { 4 | test.concurrent.each(['ts-node', 'ts-node-dev'])( 5 | 'integration with %s', 6 | async function (instance) { 7 | await new Promise(function (resolve) { 8 | const child = exec(`${instance} "${process.cwd()}/test/typescript-jest/integration/instance.ts"`) 9 | let stderr = '' 10 | child.stderr?.on('data', function (b) { 11 | stderr = stderr + b.toString() 12 | }) 13 | let stdout = '' 14 | child.stdout?.on('data', function (b) { 15 | stdout = stdout + b.toString() 16 | }) 17 | child.once('close', function () { 18 | expect(stderr.includes('failed')).toStrictEqual(false) 19 | expect(stdout.includes('success')).toStrictEqual(true) 20 | resolve('') 21 | }) 22 | }) 23 | }, 24 | 30000 25 | ) 26 | }) 27 | -------------------------------------------------------------------------------- /test/typescript/basic.ts: -------------------------------------------------------------------------------- 1 | import test, { describe, before, after } from 'node:test' 2 | import assert from 'node:assert' 3 | import fastify from 'fastify' 4 | 5 | import basicApp from './basic/app' 6 | 7 | describe('typescript/basic test suite', function () { 8 | const app = fastify() 9 | before(async function () { 10 | app.register(basicApp) 11 | await app.ready() 12 | }) 13 | 14 | after(async function () { 15 | await app.close() 16 | }) 17 | 18 | test('should respond correctly to /javascript', async function () { 19 | const res = await app.inject({ url: '/javascript' }) 20 | assert.strictEqual(res.statusCode, 200) 21 | assert.deepStrictEqual(JSON.parse(res.payload), { script: 'java' }) 22 | }) 23 | 24 | test('should respond correctly to /typescript', async function () { 25 | const res = await app.inject({ url: '/typescript' }) 26 | assert.strictEqual(res.statusCode, 200) 27 | assert.deepStrictEqual(JSON.parse(res.payload), { script: 'type' }) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /test/typescript/basic/app.ts: -------------------------------------------------------------------------------- 1 | import { FastifyPluginCallback } from 'fastify' 2 | import { join } from 'node:path' 3 | import fastifyAutoLoad from '../../../' 4 | 5 | const app: FastifyPluginCallback = function (fastify, _opts, next): void { 6 | fastify.register(fastifyAutoLoad, { 7 | dir: join(__dirname, 'foo'), 8 | }) 9 | 10 | next() 11 | } 12 | 13 | export default app 14 | -------------------------------------------------------------------------------- /test/typescript/basic/foo/javascript.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (fastify, _opts, next) { 4 | fastify.get('/javascript', (_request, reply) => { 5 | reply.send({ script: 'java' }) 6 | }) 7 | 8 | next() 9 | } 10 | -------------------------------------------------------------------------------- /test/typescript/basic/foo/shouldBeIgnored.d.ts: -------------------------------------------------------------------------------- 1 | // See https://github.com/fastify/fastify-autoload/issues/65 2 | declare const definitionFilesAreIncluded: never 3 | export default definitionFilesAreIncluded 4 | -------------------------------------------------------------------------------- /test/typescript/basic/foo/typescript.ts: -------------------------------------------------------------------------------- 1 | import { FastifyPluginCallback } from 'fastify' 2 | 3 | const plugin: FastifyPluginCallback = function (fastify, _opts, next): void { 4 | fastify.get('/typescript', (_request, reply): void => { 5 | reply.send({ script: 'type' }) 6 | }) 7 | 8 | next() 9 | } 10 | 11 | export default plugin 12 | -------------------------------------------------------------------------------- /test/vitest/basic.test.ts: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import { describe, test, expect } from 'vitest' 4 | import Fastify from 'fastify' 5 | import AutoLoad from '../../index' 6 | import { join } from 'node:path' 7 | 8 | describe.concurrent('Vitest test suite', function () { 9 | const app = Fastify() 10 | app.register(AutoLoad, { 11 | dir: join(__dirname, '../commonjs/ts-node/routes') 12 | }) 13 | 14 | test('Test the root route', async function () { 15 | const response = await app.inject({ 16 | method: 'GET', 17 | url: '/' 18 | }) 19 | expect(response.statusCode).toEqual(200) 20 | expect(JSON.parse(response.payload)).toEqual({ hello: 'world' }) 21 | }) 22 | 23 | test('Test /foo route', async function () { 24 | const response = await app.inject({ 25 | method: 'GET', 26 | url: '/foo' 27 | }) 28 | expect(response.statusCode).toBe(200) 29 | expect(JSON.parse(response.payload)).toEqual({ foo: 'foo' }) 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /test/vitest/filter.test.ts: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import { describe, test, expect } from 'vitest' 4 | import Fastify from 'fastify' 5 | import AutoLoad from '../../index' 6 | import { join } from 'node:path' 7 | 8 | describe.concurrent('Vitest ignore filters test suite', function () { 9 | const app = Fastify() 10 | app.register(AutoLoad, { 11 | dir: join(__dirname, '../commonjs/ts-node/routes'), 12 | ignoreFilter: 'foo' 13 | }) 14 | 15 | test('Test the root route', async function () { 16 | const response = await app.inject({ 17 | method: 'GET', 18 | url: '/' 19 | }) 20 | expect(response.statusCode).toEqual(200) 21 | }) 22 | 23 | test('Test /foo route', async function () { 24 | const response = await app.inject({ 25 | method: 'GET', 26 | url: '/foo' 27 | }) 28 | expect(response.statusCode).toBe(404) 29 | }) 30 | 31 | test('Test /bar route', async function () { 32 | const response = await app.inject({ 33 | method: 'GET', 34 | url: '/bar' 35 | }) 36 | expect(response.statusCode).toBe(200) 37 | }) 38 | 39 | test('Test /baz route', async function () { 40 | const response = await app.inject({ 41 | method: 'GET', 42 | url: '/foo/baz' 43 | }) 44 | expect(response.statusCode).toBe(404) 45 | }) 46 | }) 47 | 48 | describe.concurrent('Vitest match filters test suite', function () { 49 | const app = Fastify() 50 | app.register(AutoLoad, { 51 | dir: join(__dirname, '../commonjs/ts-node/routes'), 52 | matchFilter: (path) => path.startsWith('/foo') 53 | }) 54 | 55 | test('Test the root route', async function () { 56 | const response = await app.inject({ 57 | method: 'GET', 58 | url: '/' 59 | }) 60 | expect(response.statusCode).toEqual(404) 61 | }) 62 | 63 | test('Test /foo route', async function () { 64 | const response = await app.inject({ 65 | method: 'GET', 66 | url: '/foo' 67 | }) 68 | expect(response.statusCode).toBe(200) 69 | }) 70 | 71 | test('Test /bar route', async function () { 72 | const response = await app.inject({ 73 | method: 'GET', 74 | url: '/bar' 75 | }) 76 | expect(response.statusCode).toBe(404) 77 | }) 78 | 79 | test('Test /baz route', async function () { 80 | const response = await app.inject({ 81 | method: 'GET', 82 | url: '/foo/baz/customPath' 83 | }) 84 | expect(response.statusCode).toBe(200) 85 | }) 86 | }) 87 | 88 | describe.concurrent('Vitest match filters without prefix test suite', function () { 89 | const app = Fastify() 90 | app.register(AutoLoad, { 91 | dir: join(__dirname, '../commonjs/ts-node/routes'), 92 | dirNameRoutePrefix: false, 93 | matchFilter: (path) => path.startsWith('/foo/baz') 94 | }) 95 | 96 | test('Test /baz route', async function () { 97 | const response = await app.inject({ 98 | method: 'GET', 99 | url: '/customPath' 100 | }) 101 | expect(response.statusCode).toBe(200) 102 | }) 103 | }) 104 | -------------------------------------------------------------------------------- /tsconfig.tsimp.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noEmit": true, 4 | "strict": true 5 | }, 6 | "include": ["test/typescript/**/*.ts"] 7 | } 8 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | import { FastifyPluginCallback, FastifyPluginOptions } from 'fastify' 2 | 3 | type FastifyAutoloadPlugin = FastifyPluginCallback> 4 | 5 | declare namespace fastifyAutoload { 6 | type RewritePrefix = (folderParent: string, folderName: string) => string | boolean 7 | type Filter = string | RegExp | ((path: string) => boolean) 8 | 9 | export interface AutoloadPluginOptions { 10 | dir: string 11 | dirNameRoutePrefix?: boolean | RewritePrefix 12 | ignoreFilter?: Filter 13 | matchFilter?: Filter 14 | ignorePattern?: RegExp 15 | scriptPattern?: RegExp 16 | indexPattern?: RegExp 17 | options?: FastifyPluginOptions 18 | maxDepth?: number 19 | forceESM?: boolean 20 | encapsulate?: boolean 21 | autoHooks?: boolean 22 | autoHooksPattern?: RegExp 23 | cascadeHooks?: boolean 24 | overwriteHooks?: boolean 25 | routeParams?: boolean 26 | } 27 | 28 | export const fastifyAutoload: FastifyAutoloadPlugin 29 | export { fastifyAutoload as default } 30 | } 31 | 32 | declare function fastifyAutoload ( 33 | ...params: Parameters 34 | ): ReturnType 35 | 36 | export = fastifyAutoload 37 | -------------------------------------------------------------------------------- /types/index.test-d.ts: -------------------------------------------------------------------------------- 1 | import fastify, { FastifyInstance, FastifyPluginCallback } from 'fastify' 2 | import { expectType } from 'tsd' 3 | import * as fastifyAutoloadStar from '..' 4 | import fastifyAutoloadDefault, { AutoloadPluginOptions, fastifyAutoload as fastifyAutoloadNamed } from '..' 5 | 6 | import fastifyAutoloadCjsImport = require('..') 7 | const fastifyAutoloadCjs = require('..') 8 | 9 | const app: FastifyInstance = fastify() 10 | app.register(fastifyAutoloadNamed, { dir: 'test' }) 11 | app.register(fastifyAutoloadDefault, { dir: 'test' }) 12 | app.register(fastifyAutoloadCjs, { dir: 'test' }) 13 | app.register(fastifyAutoloadCjsImport.default, { dir: 'test' }) 14 | app.register(fastifyAutoloadCjsImport.fastifyAutoload, { dir: 'test' }) 15 | app.register(fastifyAutoloadStar.default, { dir: 'test' }) 16 | app.register(fastifyAutoloadStar.fastifyAutoload, { dir: 'test' }) 17 | 18 | expectType>(fastifyAutoloadNamed) 19 | expectType>(fastifyAutoloadDefault) 20 | expectType>(fastifyAutoloadCjsImport.default) 21 | expectType>(fastifyAutoloadCjsImport.fastifyAutoload) 22 | expectType>(fastifyAutoloadStar.default) 23 | expectType>(fastifyAutoloadStar.fastifyAutoload) 24 | expectType(fastifyAutoloadCjs) 25 | 26 | const opt1: AutoloadPluginOptions = { 27 | dir: 'test' 28 | } 29 | const opt2: AutoloadPluginOptions = { 30 | dir: 'test', 31 | ignorePattern: /skip/ 32 | } 33 | const opt3: AutoloadPluginOptions = { 34 | dir: 'test', 35 | scriptPattern: /js/, 36 | indexPattern: /index/, 37 | } 38 | const opt4: AutoloadPluginOptions = { 39 | dir: 'test', 40 | options: { 41 | prefix: 'test' 42 | } 43 | } 44 | const opt5: AutoloadPluginOptions = { 45 | dir: 'test', 46 | maxDepth: 1, 47 | } 48 | const opt6: AutoloadPluginOptions = { 49 | dir: 'test', 50 | routeParams: true, 51 | } 52 | const opt7: AutoloadPluginOptions = { 53 | dir: 'test', 54 | forceESM: true, 55 | autoHooks: true, 56 | autoHooksPattern: /^[_.]?auto_?hooks(?:\.ts|\.js|\.cjs|\.mjs)$/i, 57 | cascadeHooks: true, 58 | overwriteHooks: true, 59 | } 60 | const opt8: AutoloadPluginOptions = { 61 | dir: 'test', 62 | encapsulate: false, 63 | } 64 | const opt9: AutoloadPluginOptions = { 65 | dir: 'test', 66 | ignoreFilter: /test/, 67 | matchFilter: /handler/ 68 | } 69 | const opt10: AutoloadPluginOptions = { 70 | dir: 'test', 71 | ignoreFilter: 'test', 72 | matchFilter: 'handler' 73 | } 74 | const opt11: AutoloadPluginOptions = { 75 | dir: 'test', 76 | ignoreFilter: (path) => path.endsWith('.spec.ts'), 77 | matchFilter: (path) => path.split('/').at(-2) === 'handlers' 78 | } 79 | app.register(fastifyAutoloadDefault, opt1) 80 | app.register(fastifyAutoloadDefault, opt2) 81 | app.register(fastifyAutoloadDefault, opt3) 82 | app.register(fastifyAutoloadDefault, opt4) 83 | app.register(fastifyAutoloadDefault, opt5) 84 | app.register(fastifyAutoloadDefault, opt6) 85 | app.register(fastifyAutoloadDefault, opt7) 86 | app.register(fastifyAutoloadDefault, opt8) 87 | app.register(fastifyAutoloadDefault, opt9) 88 | app.register(fastifyAutoloadDefault, opt10) 89 | app.register(fastifyAutoloadDefault, opt11) 90 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config' 2 | 3 | export default defineConfig({ 4 | test: { 5 | include: ['test/vitest/**/*.test.ts'] 6 | }, 7 | }) 8 | --------------------------------------------------------------------------------