├── .devcontainer └── devcontainer.json ├── .editorconfig ├── .gitattributes ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ ├── config.yml │ └── feature-request.yml ├── PULL_REQUEST_TEMPLATE.md ├── codecov.yml ├── workflows-disabled │ └── codeql.yml └── workflows │ ├── autofix.yml │ └── ci.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── automd.config.ts ├── build.config.ts ├── docs ├── .config │ ├── automd.config.ts │ └── docs.yaml ├── .docs │ └── public │ │ ├── icon.svg │ │ └── images │ │ ├── stormkit-deploy.png │ │ └── stormkit-new-app.png ├── 1.guide │ ├── 0.index.md │ ├── 00.migration.md │ ├── 1.utils.md │ ├── 10.tasks.md │ ├── 2.routing.md │ ├── 3.websocket.md │ ├── 4.storage.md │ ├── 5.database.md │ ├── 6.cache.md │ ├── 7.fetch.md │ ├── 8.assets.md │ ├── 9.plugins.md │ ├── 97.configuration.md │ ├── 98.typescript.md │ └── 99.nightly.md ├── 2.deploy │ ├── 0.index.md │ ├── 1.workers.md │ ├── 10.runtimes │ │ ├── 1.node.md │ │ ├── _dir.yml │ │ ├── _winterjs.md │ │ ├── bun.md │ │ └── deno.md │ ├── 2.custom-presets.md │ └── 20.providers │ │ ├── _dir.yml │ │ ├── alwaysdata.md │ │ ├── aws-amplify.md │ │ ├── aws.md │ │ ├── azure.md │ │ ├── cleavr.md │ │ ├── cloudflare.md │ │ ├── deno-deploy.md │ │ ├── digitalocean.md │ │ ├── edgio.md │ │ ├── firebase.md │ │ ├── flightcontrol.md │ │ ├── genezio.md │ │ ├── github-pages.md │ │ ├── gitlab-pages.md │ │ ├── heroku.md │ │ ├── iis.md │ │ ├── koyeb.md │ │ ├── netlify.md │ │ ├── platform-sh.md │ │ ├── render.md │ │ ├── stormkit.md │ │ ├── vercel.md │ │ ├── zeabur.md │ │ └── zerops.md ├── 3.config │ └── 0.index.md ├── bun.lockb └── package.json ├── eslint.config.mjs ├── examples ├── api-routes │ ├── api │ │ ├── hello.ts │ │ ├── hello │ │ │ └── [name].ts │ │ ├── test.get.ts │ │ └── test.post.ts │ ├── nitro.config.ts │ ├── package.json │ ├── routes │ │ └── [...].ts │ └── tsconfig.json ├── auto-imports │ ├── nitro.config.ts │ ├── package.json │ ├── routes │ │ └── index.ts │ ├── tsconfig.json │ └── utils │ │ └── hello.ts ├── cached-handler │ ├── nitro.config.ts │ ├── package.json │ ├── routes │ │ └── index.ts │ └── tsconfig.json ├── custom-error-handler │ ├── error.ts │ ├── nitro.config.ts │ ├── package.json │ ├── routes │ │ └── index.ts │ └── tsconfig.json ├── database │ ├── nitro.config.ts │ ├── package.json │ ├── routes │ │ └── index.ts │ ├── tasks │ │ └── db │ │ │ └── migrate.ts │ └── tsconfig.json ├── graceful-shutdown │ ├── nitro.config.ts │ ├── package.json │ ├── plugins │ │ └── db.ts │ ├── routes │ │ └── index.ts │ └── tsconfig.json ├── hello-world │ ├── .gitignore │ ├── README.md │ ├── nitro.config.ts │ ├── package.json │ ├── server │ │ └── routes │ │ │ └── index.ts │ └── tsconfig.json ├── middleware │ ├── middleware │ │ └── auth.ts │ ├── nitro.config.ts │ ├── package.json │ ├── routes │ │ └── index.ts │ └── tsconfig.json ├── nano-jsx │ ├── nitro.config.ts │ ├── package.json │ └── routes │ │ └── [...path].tsx ├── plugins │ ├── nitro.config.ts │ ├── package.json │ ├── plugins │ │ └── test.ts │ ├── routes │ │ └── index.ts │ └── tsconfig.json ├── renderer │ ├── api │ │ └── hello.ts │ ├── nitro.config.ts │ ├── package.json │ ├── renderer.ts │ └── tsconfig.json ├── tsconfig.json └── websocket │ ├── nitro.config.ts │ ├── package.json │ ├── routes │ ├── _ws.ts │ └── index.ts │ └── tsconfig.json ├── lib ├── config.d.mts ├── config.mjs ├── meta.d.mts ├── meta.mjs ├── runtime-meta.d.mts └── runtime-meta.mjs ├── package.json ├── playground ├── nitro.config.ts ├── public │ └── test.txt ├── server │ └── routes │ │ └── index.ts └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── renovate.json ├── scripts ├── bump-nightly.ts ├── gen-mirror.ts ├── gen-node-compat.ts ├── gen-presets.ts └── release-nightly.sh ├── src ├── build │ ├── assets.ts │ ├── build.ts │ ├── config.ts │ ├── plugins.ts │ ├── plugins │ │ ├── database.ts │ │ ├── error-handler.ts │ │ ├── esbuild.ts │ │ ├── externals.ts │ │ ├── handlers-meta.ts │ │ ├── handlers.ts │ │ ├── public-assets.ts │ │ ├── raw.ts │ │ ├── replace.ts │ │ ├── server-assets.ts │ │ ├── server-main.ts │ │ ├── sourcemap-min.ts │ │ ├── storage.ts │ │ └── virtual.ts │ ├── prepare.ts │ ├── rolldown │ │ ├── build.ts │ │ ├── config.ts │ │ ├── dev.ts │ │ └── prod.ts │ ├── rollup │ │ ├── build.ts │ │ ├── config.ts │ │ ├── dev.ts │ │ ├── error.ts │ │ └── prod.ts │ ├── snapshot.ts │ └── types.ts ├── cli │ ├── commands │ │ ├── build.ts │ │ ├── dev.ts │ │ ├── prepare.ts │ │ └── task │ │ │ ├── index.ts │ │ │ ├── list.ts │ │ │ └── run.ts │ ├── common.ts │ └── index.ts ├── config │ ├── defaults.ts │ ├── loader.ts │ ├── resolvers │ │ ├── assets.ts │ │ ├── builder.ts │ │ ├── compatibility.ts │ │ ├── database.ts │ │ ├── error.ts │ │ ├── export-conditions.ts │ │ ├── imports.ts │ │ ├── open-api.ts │ │ ├── paths.ts │ │ ├── route-rules.ts │ │ ├── runtime-config.ts │ │ ├── storage.ts │ │ ├── unenv.ts │ │ └── url.ts │ └── update.ts ├── dev │ ├── proxy.ts │ ├── server.ts │ ├── vfs.ts │ └── worker.ts ├── index.ts ├── module.ts ├── nitro.ts ├── prerender │ ├── prerender.ts │ └── utils.ts ├── presets │ ├── _all.gen.ts │ ├── _nitro │ │ ├── base-worker.ts │ │ ├── nitro-dev.ts │ │ ├── nitro-prerender.ts │ │ ├── preset.ts │ │ └── runtime │ │ │ ├── nitro-dev.ts │ │ │ ├── nitro-prerenderer.ts │ │ │ └── service-worker.ts │ ├── _resolve.ts │ ├── _static │ │ └── preset.ts │ ├── _types.gen.ts │ ├── _unenv │ │ ├── node-compat │ │ │ ├── cloudflare.ts │ │ │ ├── deno.ts │ │ │ └── netlify.ts │ │ ├── preset-deno.ts │ │ ├── preset-workerd.ts │ │ └── workerd │ │ │ ├── console.mjs │ │ │ ├── crypto.mjs │ │ │ ├── module.mjs │ │ │ ├── process.mjs │ │ │ ├── tls.mjs │ │ │ └── util.mjs │ ├── _utils │ │ ├── fs.ts │ │ └── preset.ts │ ├── alwaysdata │ │ └── preset.ts │ ├── aws-amplify │ │ ├── preset.ts │ │ ├── runtime │ │ │ └── aws-amplify.ts │ │ ├── types.ts │ │ └── utils.ts │ ├── aws-lambda │ │ ├── preset.ts │ │ ├── runtime │ │ │ ├── aws-lambda-streaming.ts │ │ │ └── aws-lambda.ts │ │ └── types.ts │ ├── azure │ │ ├── preset.ts │ │ ├── runtime │ │ │ └── azure-swa.ts │ │ ├── types.ts │ │ └── utils.ts │ ├── bun │ │ ├── preset.ts │ │ └── runtime │ │ │ └── bun.ts │ ├── cleavr │ │ └── preset.ts │ ├── cloudflare │ │ ├── preset.ts │ │ ├── runtime │ │ │ ├── _module-handler.ts │ │ │ ├── cloudflare-durable.ts │ │ │ ├── cloudflare-module.ts │ │ │ └── cloudflare-pages.ts │ │ ├── types.ts │ │ ├── utils.ts │ │ └── wrangler │ │ │ ├── _utils.ts │ │ │ ├── config.ts │ │ │ └── environment.ts │ ├── deno │ │ ├── preset.ts │ │ └── runtime │ │ │ ├── deno-deploy.ts │ │ │ └── deno-server.ts │ ├── digitalocean │ │ └── preset.ts │ ├── edgio │ │ └── preset.ts │ ├── firebase │ │ ├── preset.ts │ │ └── types.ts │ ├── flightcontrol │ │ └── preset.ts │ ├── genezio │ │ └── preset.ts │ ├── heroku │ │ └── preset.ts │ ├── iis │ │ ├── preset.ts │ │ └── utils.ts │ ├── index.mjs │ ├── index.ts │ ├── koyeb │ │ └── preset.ts │ ├── netlify │ │ ├── preset.ts │ │ ├── runtime │ │ │ ├── netlify-edge.ts │ │ │ └── netlify.ts │ │ ├── types.ts │ │ └── utils.ts │ ├── node │ │ ├── preset.ts │ │ └── runtime │ │ │ ├── cli.ts │ │ │ ├── node-cluster.ts │ │ │ ├── node-middleware.ts │ │ │ └── node-server.ts │ ├── platform.sh │ │ └── preset.ts │ ├── render.com │ │ └── preset.ts │ ├── stormkit │ │ ├── preset.ts │ │ └── runtime │ │ │ └── stormkit.ts │ ├── vercel │ │ ├── preset.ts │ │ ├── runtime │ │ │ └── vercel.ts │ │ ├── types.ts │ │ └── utils.ts │ ├── winterjs │ │ ├── preset.ts │ │ └── runtime │ │ │ └── winterjs.ts │ ├── zeabur │ │ ├── preset.ts │ │ └── runtime │ │ │ └── zeabur.ts │ └── zerops │ │ └── preset.ts ├── runtime │ ├── index.ts │ └── internal │ │ ├── app.ts │ │ ├── cache.ts │ │ ├── client.ts │ │ ├── config.ts │ │ ├── context.ts │ │ ├── database.ts │ │ ├── debug.ts │ │ ├── empty.ts │ │ ├── error │ │ ├── dev.ts │ │ ├── prod.ts │ │ └── utils.ts │ │ ├── index.ts │ │ ├── lib │ │ └── http-graceful-shutdown.ts │ │ ├── meta.ts │ │ ├── plugin.ts │ │ ├── renderer.ts │ │ ├── route-rules.ts │ │ ├── routes │ │ ├── openapi.ts │ │ ├── scalar.ts │ │ └── swagger.ts │ │ ├── shutdown.ts │ │ ├── static.ts │ │ ├── storage.ts │ │ ├── task.ts │ │ ├── utils.azure.ts │ │ ├── utils.env.ts │ │ ├── utils.lambda.ts │ │ └── utils.ts ├── scan.ts ├── task.ts ├── types │ ├── _utils.ts │ ├── config.ts │ ├── dev.ts │ ├── fetch │ │ ├── _match.ts │ │ ├── _serialize.ts │ │ ├── fetch.ts │ │ └── index.ts │ ├── global.ts │ ├── h3.ts │ ├── handler.ts │ ├── hooks.ts │ ├── index.ts │ ├── module.ts │ ├── nitro.ts │ ├── openapi-ts.ts │ ├── openapi.ts │ ├── prerender.ts │ ├── preset.ts │ ├── rollup.ts │ ├── route-rules.ts │ ├── runtime │ │ ├── asset.ts │ │ ├── cache.ts │ │ ├── index.ts │ │ ├── nitro.ts │ │ └── task.ts │ └── virtual │ │ ├── app-config.d.ts │ │ ├── database.d.ts │ │ ├── error-handler.d.ts │ │ ├── plugins.d.ts │ │ ├── polyfills.ts │ │ ├── public-assets.d.ts │ │ ├── server-assets.d.ts │ │ ├── server-handlers-meta.d.ts │ │ ├── server-handlers.d.ts │ │ ├── storage.d.ts │ │ └── tasks.ts └── utils │ ├── compress.ts │ ├── fs-tree.ts │ ├── fs.ts │ ├── nitro.ts │ ├── parallel.ts │ └── storage.ts ├── test ├── fixture │ ├── .env │ ├── .gitignore │ ├── api │ │ ├── _ignored.ts │ │ ├── cached.ts │ │ ├── db.ts │ │ ├── echo.ts │ │ ├── error.ts │ │ ├── errors.ts │ │ ├── headers.ts │ │ ├── hello.ts │ │ ├── hey │ │ │ └── index.get.ts │ │ ├── kebab.ts │ │ ├── meta │ │ │ └── test.ts │ │ ├── methods │ │ │ ├── default.post.ts │ │ │ ├── default.ts │ │ │ ├── foo.get.get.ts │ │ │ ├── get.ts │ │ │ ├── index.get.ts │ │ │ └── index.post.ts │ │ ├── param │ │ │ └── [test-id].ts │ │ ├── serialized │ │ │ ├── date.ts │ │ │ ├── error.ts │ │ │ ├── function.ts │ │ │ ├── map.ts │ │ │ ├── null.ts │ │ │ ├── set.ts │ │ │ ├── tuple.ts │ │ │ └── void.ts │ │ ├── storage │ │ │ ├── dev.dev.ts │ │ │ ├── item.get.ts │ │ │ └── item.put.ts │ │ ├── typed │ │ │ ├── catchall │ │ │ │ ├── [slug] │ │ │ │ │ └── [...another].ts │ │ │ │ └── some │ │ │ │ │ └── [...test].ts │ │ │ ├── todos │ │ │ │ ├── [...].ts │ │ │ │ └── [todoId] │ │ │ │ │ └── comments │ │ │ │ │ └── [...commentId].ts │ │ │ └── user │ │ │ │ ├── [userId] │ │ │ │ ├── [userExtends].ts │ │ │ │ ├── index.ts │ │ │ │ └── post │ │ │ │ │ ├── [postId].ts │ │ │ │ │ └── firstPost.ts │ │ │ │ └── john │ │ │ │ ├── [johnExtends].ts │ │ │ │ ├── index.ts │ │ │ │ └── post │ │ │ │ ├── [postId].ts │ │ │ │ └── coffee.ts │ │ ├── upload.post.ts │ │ └── wildcard │ │ │ └── [...param].ts │ ├── app.config.ts │ ├── assets │ │ ├── cat.jpg │ │ ├── test.json │ │ └── test.md │ ├── error.ts │ ├── files │ │ ├── index.html │ │ ├── sql.sql │ │ ├── sqlts.sql.ts │ │ └── test.txt │ ├── middleware │ │ └── _ignored.ts │ ├── nitro.config.ts │ ├── node_modules │ │ └── @fixture │ │ │ ├── nitro-dep-a │ │ │ ├── index.mjs │ │ │ ├── node_modules │ │ │ │ └── @fixture │ │ │ │ │ └── nitro-lib │ │ │ │ │ ├── index.mjs │ │ │ │ │ ├── node_modules │ │ │ │ │ └── @fixture │ │ │ │ │ │ └── nested-lib │ │ │ │ │ │ ├── index.mjs │ │ │ │ │ │ └── package.json │ │ │ │ │ └── package.json │ │ │ └── package.json │ │ │ ├── nitro-dep-b │ │ │ ├── index.mjs │ │ │ ├── node_modules │ │ │ │ └── @fixture │ │ │ │ │ └── nitro-lib │ │ │ │ │ ├── index.mjs │ │ │ │ │ ├── node_modules │ │ │ │ │ └── @fixture │ │ │ │ │ │ └── nested-lib │ │ │ │ │ │ ├── index.mjs │ │ │ │ │ │ └── package.json │ │ │ │ │ ├── package.json │ │ │ │ │ └── subpath.mjs │ │ │ └── package.json │ │ │ ├── nitro-lib │ │ │ ├── index.mjs │ │ │ ├── node_modules │ │ │ │ └── @fixture │ │ │ │ │ └── nested-lib │ │ │ │ │ ├── index.mjs │ │ │ │ │ └── package.json │ │ │ ├── package.json │ │ │ └── subpath.mjs │ │ │ └── nitro-utils │ │ │ ├── extra.mjs │ │ │ ├── extra2.mjs │ │ │ ├── index.mjs │ │ │ └── package.json │ ├── plugins │ │ ├── errors.ts │ │ └── vary.ts │ ├── public │ │ ├── _ignored.txt │ │ ├── _unignored.txt │ │ ├── build │ │ │ └── test.txt │ │ ├── cf-pages-exclude │ │ │ └── not-in-routes-json.txt │ │ ├── favicon.ico │ │ ├── foo.css │ │ └── foo.js │ ├── routes │ │ ├── (route-group) │ │ │ └── route-group.ts │ │ ├── 500.ts │ │ ├── assets │ │ │ ├── [id].ts │ │ │ ├── all.ts │ │ │ └── md.ts │ │ ├── config.ts │ │ ├── context.ts │ │ ├── env │ │ │ ├── index.dev.ts │ │ │ └── index.get.prod.ts │ │ ├── error-stack.ts │ │ ├── fetch.ts │ │ ├── file.ts │ │ ├── icon.png.ts │ │ ├── imports.ts │ │ ├── json-string.ts │ │ ├── jsx.tsx │ │ ├── modules.ts │ │ ├── node-compat.ts │ │ ├── prerender-custom.html.ts │ │ ├── prerender.ts │ │ ├── raw.ts │ │ ├── rules │ │ │ └── [...slug].ts │ │ ├── static-flags.ts │ │ ├── stream.ts │ │ ├── tasks │ │ │ └── [...name].ts │ │ ├── wait-until.ts │ │ └── wasm │ │ │ ├── dynamic-import.ts │ │ │ └── static-import.ts │ ├── server.config.ts │ ├── tasks │ │ ├── db │ │ │ └── migrate.ts │ │ └── test.ts │ ├── tsconfig.json │ ├── types.ts │ ├── utils │ │ ├── foo │ │ │ ├── bar │ │ │ │ └── test.ts │ │ │ └── test.ts │ │ └── test.ts │ └── wrangler.toml ├── presets │ ├── aws-lambda.test.ts │ ├── azure-swa.test.ts │ ├── bun.test.ts │ ├── cloudflare-module.test.ts │ ├── cloudflare-pages.test.ts │ ├── deno-server.test.ts │ ├── netlify.test.ts │ ├── nitro-dev.test.ts │ ├── node.test.ts │ ├── static.test.ts │ ├── vercel.test.ts │ └── winterjs.test.ts ├── scripts │ └── gen-fixture-types.ts ├── tests.ts └── unit │ ├── azure.utils.test.ts │ ├── externals.test.ts │ ├── runtime-config.test.ts │ └── utils.env.test.ts ├── tsconfig.json └── vitest.config.ts /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // https://code.visualstudio.com/docs/devcontainers/containers 2 | // https://containers.dev/implementors/json_reference/ 3 | { 4 | "name": "nitro-devcontainer", 5 | "forwardPorts": [3000], 6 | "image": "node:22", 7 | "features": {}, 8 | "customizations": { 9 | "vscode": { 10 | "settings": {}, 11 | "extensions": [ 12 | "ms-azuretools.vscode-docker", 13 | "dbaeumer.vscode-eslint", 14 | "github.vscode-github-actions", 15 | "esbenp.prettier-vscode" 16 | ] 17 | } 18 | }, 19 | "postStartCommand": "npm i -fg corepack && corepack enable && pnpm install && pnpm build --stub", 20 | "mounts": ["type=volume,target=${containerWorkspaceFolder}/node_modules"] 21 | } 22 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | trim_trailing_whitespace = true 7 | charset = utf-8 8 | 9 | [*.js] 10 | indent_style = space 11 | indent_size = 2 12 | 13 | [{package.json,*.yml,*.cjson}] 14 | indent_style = space 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.txt text eol=lf 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @pi0 -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: 📚 Nitro Documentation 4 | url: https://nitro.build/ 5 | about: Check the documentation for usage of Nuxt 3 6 | - name: 💬 Discussions 7 | url: https://github.com/nitrojs/nitro/discussions 8 | about: Use discussions if you have an idea for improvement and asking questions 9 | - name: 🔗 Nuxt related Issues 10 | url: https://github.com/nuxt/nuxt/issues/new/choose 11 | about: Please open an issue in nuxt/nuxt to discuss 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: "🚀 Feature request" 2 | description: Suggest a feature or improvement 3 | labels: ["pending triage"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thank you for taking the time to fill out this feature request! 9 | - type: textarea 10 | id: feature-description 11 | attributes: 12 | label: Describe the feature 13 | description: A clear and concise description of what you think would be a helpful addition, including the possible use cases and alternatives you have considered. If you have a working prototype or module that implements it, please include a link. 14 | placeholder: Feature description 15 | validations: 16 | required: true 17 | - type: checkboxes 18 | id: additional-info 19 | attributes: 20 | label: Additional information 21 | description: Additional information that helps us decide how to proceed. 22 | options: 23 | - label: Would you be willing to help implement this feature? 24 | -------------------------------------------------------------------------------- /.github/codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | threshold: 50% 6 | -------------------------------------------------------------------------------- /.github/workflows-disabled/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | branches: ["main"] 8 | 9 | jobs: 10 | analyze: 11 | name: Analyze 12 | runs-on: "ubuntu-latest" 13 | timeout-minutes: 360 14 | permissions: 15 | actions: read 16 | contents: read 17 | security-events: write 18 | 19 | strategy: 20 | fail-fast: false 21 | 22 | steps: 23 | - name: Checkout repository 24 | uses: actions/checkout@v4 25 | 26 | - name: Initialize CodeQL 27 | uses: github/codeql-action/init@v2 28 | with: 29 | languages: "javascript" 30 | 31 | - name: Perform CodeQL Analysis 32 | uses: github/codeql-action/analyze@v2 33 | with: 34 | category: "/language:javascript" 35 | -------------------------------------------------------------------------------- /.github/workflows/autofix.yml: -------------------------------------------------------------------------------- 1 | name: autofix.ci # needed to securely identify the workflow 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - v2 8 | - v3 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | autofix: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | - run: npm i -g --force corepack && corepack enable 19 | - uses: actions/setup-node@v4 20 | with: 21 | node-version: 22 22 | cache: "pnpm" 23 | - run: pnpm install 24 | - run: pnpm stub 25 | - run: pnpm gen-presets 26 | - name: Fix lint issues 27 | run: npm run lint:fix 28 | - uses: autofix-ci/action@ff86a557419858bb967097bfc916833f5647fa8c 29 | with: 30 | commit-message: "chore: apply automated updates" 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules 3 | jspm_packages 4 | 5 | # Nitro 6 | nitro.d.ts 7 | .nitro 8 | .data 9 | 10 | package-lock.json 11 | 12 | # Logs 13 | *.log 14 | 15 | # Temp directories 16 | .temp 17 | .tmp 18 | .cache 19 | 20 | # Generated dirs 21 | dist 22 | .nuxt 23 | .output 24 | .gen 25 | .tmp 26 | nuxt.d.ts 27 | 28 | # Junit reports 29 | reports 30 | 31 | # Coverage reports 32 | coverage 33 | *.lcov 34 | .nyc_output 35 | 36 | # VSCode 37 | .vscode 38 | 39 | # Intellij idea 40 | *.iml 41 | .idea 42 | 43 | # OSX 44 | .DS_Store 45 | .AppleDouble 46 | .LSOverride 47 | 48 | # Files that might appear in the root of a volume 49 | .DocumentRevisions-V100 50 | .fseventsd 51 | .Spotlight-V100 52 | .TemporaryItems 53 | .Trashes 54 | .VolumeIcon.icns 55 | .com.apple.timemachine.donotpresent 56 | 57 | # Directories potentially created on remote AFP share 58 | .AppleDB 59 | .AppleDesktop 60 | Network Trash Folder 61 | Temporary Items 62 | .apdisk 63 | 64 | .vercel_build_output 65 | .build-* 66 | .env 67 | .netlify 68 | .vercel 69 | .amplify-hosting 70 | staticwebapp.config.json 71 | .eslintcache 72 | playground/firebase.json 73 | .zeabur 74 | .apphosting 75 | 76 | test/fixture/functions 77 | .data 78 | 79 | .pnpm-store 80 | .wrangler 81 | 82 | # mirror pkg 83 | .mirror 84 | 85 | # Generated types 86 | *.d.ts 87 | !runtime-meta.d.ts 88 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | ignore-workspace-root-check=true 2 | shell-emulator=true 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | *.md 2 | **/*.gen.ts 3 | node_modules 4 | pnpm-lock.yaml 5 | **/.docs 6 | **/dist/** 7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5" 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Pooya Parsa and Nitro contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nitro 2 | 3 | 4 | 5 | [![npm version](https://img.shields.io/npm/v/nitro)](https://npmjs.com/package/nitro) 6 | [![npm downloads](https://img.shields.io/npm/dm/nitro)](https://npm.chart.dev/nitro) 7 | 8 | 9 | 10 | Create web servers that run anywhere! 📖 [**documentation**](https://nitro.build) 11 | 12 | > [!IMPORTANT] 13 | > You are on the **v3 beta branch**. Checkout the [v2](https://github.com/nitrojs/nitro/tree/v2) branch for current stable. 14 | 15 | Check [migration guide](./docs/1.guide/00.migration.md) for migrating from Nitro v2 to Nitro v3. 16 | 17 | ## Contribution 18 | 19 | Please check [Contribution guide](CONTRIBUTING.md). 20 | 21 | ## License 22 | 23 | 24 | 25 | Published under the [MIT](https://github.com/nitrojs/nitro/blob/main/LICENSE) license. 26 | Made by [@pi0](https://github.com/pi0) and [community](https://github.com/nitrojs/nitro/graphs/contributors) 💛 27 |

28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | --- 37 | 38 | _🤖 auto updated with [automd](https://automd.unjs.io)_ 39 | 40 | 41 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## ⚠️ Reporting a Vulnerability 4 | 5 | To report a vulnerability, please send an email to [security+nitro@unjs.io](mailto:security+nitro@unjs.io) or submit it for a bounty via [Huntr](https://huntr.dev/bounties/disclose/?target=https://github.com/nitrojs/nitro). 6 | 7 | All security vulnerabilities will be promptly verified and addressed. 8 | 9 | We recommend to regularly upgrade and publish with the latest versions of used packages and sub-dependencies by maintaining lock files (`yarn.lock`, `package-lock.json` and `pnpm-lock.yaml`) in order to ensure your application remains as secure as possible. 10 | -------------------------------------------------------------------------------- /automd.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | input: ["README.md", "docs/**/*.md"], 3 | generators: { 4 | compatDate: { 5 | async generate(ctx) { 6 | const { compatibilityChanges } = await import("./lib/meta.mjs"); 7 | 8 | const table = [ 9 | "| Compatibility date | Platform | Description |", 10 | "|------|----------|-------------|", 11 | ...compatibilityChanges.map( 12 | (change) => 13 | `| **≥ ${change.from}** | ${change.platform} | ${change.description} |` 14 | ), 15 | ]; 16 | 17 | return { 18 | contents: table.join("\n"), 19 | }; 20 | }, 21 | }, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /docs/.config/automd.config.ts: -------------------------------------------------------------------------------- 1 | export { default } from "../../automd.config"; 2 | -------------------------------------------------------------------------------- /docs/.docs/public/images/stormkit-deploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitrojs/nitro/8c2a2d8589a1c65b091f8bce1ca25d756f25bb11/docs/.docs/public/images/stormkit-deploy.png -------------------------------------------------------------------------------- /docs/.docs/public/images/stormkit-new-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitrojs/nitro/8c2a2d8589a1c65b091f8bce1ca25d756f25bb11/docs/.docs/public/images/stormkit-new-app.png -------------------------------------------------------------------------------- /docs/1.guide/98.typescript.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: tabler:brand-typescript 3 | --- 4 | 5 | # TypeScript 6 | 7 | > Nitro automatically generates the types for auto-imports and server routes :sparkles: 8 | 9 | ## `tsconfig.json` 10 | 11 | To leverage type hints within your project, create a `tsconfig.json` file that extends auto-generated types. 12 | 13 | ::code-group 14 | ```json [tsconfig.json (nitro)] 15 | { 16 | "extends": "./.nitro/types/tsconfig.json" 17 | } 18 | ``` 19 | 20 | ```json [server/tsconfig.json (nuxt)] 21 | { 22 | "extends": "../.nuxt/tsconfig.server.json" 23 | } 24 | ``` 25 | :: 26 | 27 | ::tip 28 | Starter templates have this file by default and usually you don't need to do anything. If this file does not exists, you can manually create it. 29 | :: 30 | 31 | ## Prepare types 32 | 33 | You can use `prepare` command to auto generate the types. 34 | This can be useful in a CI environment or as a `postinstall` command in your `package.json`. 35 | 36 | :pm-x{command="nitro prepare"} 37 | 38 | ::tip 39 | When using `nitro dev` command, types are also auto-generated! 40 | :: 41 | 42 | ::note 43 | For [Nuxt](https://nuxt.com) you should use `nuxi generate` 44 | :: 45 | 46 | -------------------------------------------------------------------------------- /docs/1.guide/99.nightly.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: ri:moon-fill 3 | --- 4 | 5 | # Nightly Channel 6 | 7 | > Nitro has a nightly release channel that automatically releases for every commit to `main` branch to try latest changes. 8 | 9 | You can opt-in to the nightly release channel by updating your `package.json`: 10 | 11 | ::code-group 12 | ```diff [Nitro] 13 | { 14 | "devDependencies": { 15 | -- "nitropack": "^2.0.0" 16 | ++ "nitropack": "npm:nitropack-nightly@latest" 17 | } 18 | } 19 | ``` 20 | ```diff [Nuxt] 21 | { 22 | "devDependencies": { 23 | -- "nuxt": "^3.0.0" 24 | ++ "nuxt": "npm:nuxt-nightly@latest" 25 | } 26 | } 27 | ``` 28 | :: 29 | 30 | ::note 31 | If you are using Nuxt, [use the Nuxt nightly channel](https://nuxt.com/docs/guide/going-further/nightly-release-channel#opting-in) as it already includes `nitropack-nightly`. 32 | :: 33 | 34 | Remove the lockfile (`package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, or `bun.lockb`) and reinstall the dependencies. 35 | -------------------------------------------------------------------------------- /docs/2.deploy/10.runtimes/_dir.yml: -------------------------------------------------------------------------------- 1 | icon: codicon:run-all 2 | -------------------------------------------------------------------------------- /docs/2.deploy/10.runtimes/_winterjs.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: game-icons:cold-heart 3 | --- 4 | 5 | # WinterJS 6 | 7 | **Preset:** `winterjs` 8 | 9 | You can easily build Nitro powered applications to run with [wasmerio/winterjs](https://github.com/wasmerio/winterjs) runtime. 10 | 11 | [WinterJS](https://github.com/wasmerio/winterjs) is a JavaScript Service Workers server written in Rust, that uses the SpiderMonkey runtime to execute JavaScript (the same runtime that Firefox uses) ([announcement](https://wasmer.io/posts/announcing-winterjs-service-workers)). 12 | 13 | 14 | ::warning 15 | 🚧 WinterJS runtime is unstable and under heavy development. Follow [nitrojs/nitro#1861](https://github.com/nitrojs/nitro/issues/1861) for status and information. 16 | :: 17 | 18 | 19 | In order to build for this runtime, use `NITRO_PRESET="winterjs"` environment variable: 20 | 21 | ```sh 22 | NITRO_PRESET="winterjs" npm run build 23 | ``` 24 | 25 | Make sure you have `wasmer` installed locally ([install wasmer](https://docs.wasmer.io/install)) 26 | 27 | Run locally: 28 | 29 | ```sh 30 | wasmer run wasmer/winterjs --forward-host-env --net --mapdir app:.output app/server/index.mjs 31 | ``` 32 | -------------------------------------------------------------------------------- /docs/2.deploy/10.runtimes/bun.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: simple-icons:bun 3 | --- 4 | 5 | # Bun 6 | 7 | > Run Nitro apps with Bun runtime. 8 | 9 | **Preset:** `bun` 10 | 11 | Nitro output is compatible with Bun runtime. While using default [Node.js](/deploy/runtimes/node) you can also run the output in bun, using `bun` preset has advantage of better optimizations. 12 | 13 | After building with bun preset using `bun` as preset, you can run server in production using: 14 | 15 | ```bash 16 | bun run ./.output/server/index.mjs 17 | ``` 18 | 19 | :read-more{to="https://bun.sh"} 20 | -------------------------------------------------------------------------------- /docs/2.deploy/10.runtimes/deno.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: simple-icons:deno 3 | --- 4 | 5 | # Deno 6 | 7 | > Run Nitro apps with [Deno](https://deno.com/) runtime. 8 | 9 | **Preset:** `deno_server` 10 | 11 | You can build your Nitro server using Node.js to run within [Deno Runtime](https://deno.com/runtime) in a custom server. 12 | 13 | ```bash 14 | # Build with the deno NITRO preset 15 | NITRO_PRESET=deno_server npm run build 16 | 17 | # Start production server 18 | deno run --unstable --allow-net --allow-read --allow-env .output/server/index.ts 19 | ``` 20 | 21 | ## Deno Deploy 22 | 23 | :read-more{to="/deploy/providers/deno-deploy"} 24 | -------------------------------------------------------------------------------- /docs/2.deploy/20.providers/_dir.yml: -------------------------------------------------------------------------------- 1 | icon: tdesign:cloud 2 | -------------------------------------------------------------------------------- /docs/2.deploy/20.providers/aws.md: -------------------------------------------------------------------------------- 1 | # AWS Lambda 2 | 3 | > Deploy Nitro apps to AWS Lambda. 4 | 5 | **Preset:** `aws_lambda` 6 | 7 | :read-more{title="AWS Lambda" to="https://aws.amazon.com/lambda/"} 8 | 9 | Nitro provides a built-in preset to generate output format compatible with [AWS Lambda](https://aws.amazon.com/lambda/). 10 | The output entrypoint in `.output/server/index.mjs` is compatible with [AWS Lambda format](https://docs.aws.amazon.com/lex/latest/dg/lambda-input-response-format.html). 11 | 12 | It can be used programmatically or as part of a deployment. 13 | 14 | ```ts 15 | import { handler } from './.output/server' 16 | 17 | // Use programmatically 18 | const { statusCode, headers, body } = handler({ rawPath: '/' }) 19 | ``` 20 | 21 | ## Inlining chunks 22 | 23 | Nitro output, by default uses dynamic chunks for lazy loading code only when needed. However this sometimes can not be ideal for performance. (See discussions in [nitrojs/nitro#650](https://github.com/nitrojs/nitro/pull/650)). You can enabling chunk inlining behavior using [`inlineDynamicImports`](/config#inlinedynamicimports) config. 24 | 25 | ::code-group 26 | 27 | ```ts [nitro.config.ts] 28 | export default defineNitroConfig({ 29 | inlineDynamicImports: true 30 | }); 31 | ``` 32 | 33 | ```ts [nuxt.config.ts] 34 | export default defineNuxtConfig({ 35 | nitro: { 36 | inlineDynamicImports: true 37 | } 38 | }) 39 | ``` 40 | 41 | :: 42 | 43 | 44 | ## Response streaming 45 | 46 | :read-more{title="Introducing AWS Lambda response streaming" to="https://aws.amazon.com/blogs/compute/introducing-aws-lambda-response-streaming/"} 47 | 48 | In order to enable response streaming, enable `awsLambda.streaming` flag: 49 | 50 | ```ts [nitro.config.ts] 51 | export default defineNitroConfig({ 52 | awsLambda: { 53 | streaming: true 54 | } 55 | }); 56 | ``` 57 | -------------------------------------------------------------------------------- /docs/2.deploy/20.providers/cleavr.md: -------------------------------------------------------------------------------- 1 | # Cleavr 2 | 3 | > Deploy Nitro apps to Cleavr. 4 | 5 | **Preset:** `cleavr` 6 | 7 | :read-more{title="cleavr.io" to="https://cleavr.io"} 8 | 9 | ::note 10 | Integration with this provider is possible with [zero configuration](/deploy/#zero-config-providers). 11 | :: 12 | 13 | ## Set up your web app 14 | 15 | In your project, set Nitro preset to `cleavr`. 16 | 17 | ```js 18 | export default { 19 | nitro: { 20 | preset: 'cleavr' 21 | } 22 | } 23 | ``` 24 | 25 | Push changes to your code repository. 26 | 27 | **In your Cleavr panel:** 28 | 29 | 1. Provision a new server 30 | 2. Add a website, selecting **Nuxt 3** as the app type 31 | 3. In web app > settings > Code Repo, point to your project's code repository 32 | 33 | You're now all set to deploy your project! 34 | -------------------------------------------------------------------------------- /docs/2.deploy/20.providers/digitalocean.md: -------------------------------------------------------------------------------- 1 | # DigitalOcean 2 | 3 | > Deploy Nitro apps to DigitalOcean. 4 | 5 | **Preset:** `digital_ocean` 6 | 7 | :read-more{title="Digital Ocean App Platform" to="https://docs.digitalocean.com/products/app-platform/"} 8 | 9 | ## Set up application 10 | 11 | 1. Create a new Digital Ocean app following the [guide](https://docs.digitalocean.com/products/app-platform/how-to/create-apps/). 12 | 13 | 1. Next, you'll need to configure environment variables. In your app settings, ensure the following app-level environment variables are set: 14 | 15 | ```bash 16 | NITRO_PRESET=digital_ocean 17 | ``` 18 | 19 | [More information](https://docs.digitalocean.com/products/app-platform/how-to/use-environment-variables/). 20 | 21 | 1. You will need to ensure you set an `engines.node` field in your app's `package.json` to ensure Digital Ocean uses a supported version of Node.js: 22 | 23 | ```json 24 | { 25 | "engines": { 26 | "node": "16.x" 27 | } 28 | } 29 | ``` 30 | 31 | [See more information](https://docs.digitalocean.com/products/app-platform/languages-frameworks/nodejs/#node-version). 32 | 33 | 1. You'll also need to add a run command so Digital Ocean knows what command to run after a build. You can do so by adding a start script to your `package.json`: 34 | 35 | ```json 36 | { 37 | "scripts": { 38 | "start": "node .output/server/index.mjs" 39 | } 40 | } 41 | ``` 42 | 43 | 1. Finally, you'll need to add this start script to your Digital Ocean app's run command. Go to `Components > Settings > Commands`, click "Edit", then add `npm run start` 44 | 45 | Your app should be live at a Digital Ocean generated URL and you can now follow [the rest of the Digital Ocean deployment guide](https://docs.digitalocean.com/products/app-platform/how-to/manage-deployments/). 46 | -------------------------------------------------------------------------------- /docs/2.deploy/20.providers/edgio.md: -------------------------------------------------------------------------------- 1 | # Edgio 2 | 3 | > Deploy Nitro apps to Edgio. 4 | 5 | **Preset:** `edgio` 6 | 7 | :read-more{title="edgio.io" to="https://edg.io/"} 8 | 9 | Edgio (formerly Layer0) extends the capabilities of a traditional CDN by not only hosting your static content, but also providing server-side rendering for progressive web applications as well as caching both your APIs and HTML at the network edge to provide your users with the fastest browsing experience. 10 | 11 | If this is your first time deploying to Edgio, the interactive CLI as part of the `deploy` command will prompt to authenticate using your browser. You may also [sign up](https://app.layer0.co/signup) prior to deployment. 12 | 13 | ## Install the Eedgio CLI 14 | 15 | ```bash 16 | npm i -g @edgio/cli 17 | ``` 18 | 19 | ## Testing production build locally with Edgio 20 | 21 | You can use Nitropack to test your app's development experience locally: 22 | 23 | ```bash 24 | NITRO_PRESET=edgio npx nitropack build 25 | ``` 26 | 27 | To simulate on local how your app would run in production with Edgio, run the following command: 28 | 29 | ```bash 30 | edgio build && edgio run --production 31 | ``` 32 | 33 | ## Deploying from your local machine 34 | 35 | Once you have tested your application locally, you may deploy using: 36 | 37 | ```bash 38 | edgio deploy 39 | ``` 40 | 41 | ## Deploying using CI/CD 42 | 43 | If you are deploying from a non-interactive environment, you will need to create an account on [Edgio Developer Console](https://app.layer0.co) first and setup a [deploy token](https://docs.edg.io/guides/basics/deployments#deploy-from-ci). Once the deploy token is created, save it as a secret to your environment. You can start the deploy by running: 44 | 45 | ```bash 46 | edgio deploy --token=XXX 47 | ``` 48 | -------------------------------------------------------------------------------- /docs/2.deploy/20.providers/firebase.md: -------------------------------------------------------------------------------- 1 | # Firebase 2 | 3 | > Deploy Nitro apps to Firebase. 4 | 5 | ::note 6 | You will need to be on the [**Blaze plan**](https://firebase.google.com/pricing) (Pay as you go) to get started. 7 | :: 8 | 9 | ## Firebase app hosting 10 | 11 | Preset: `firebase_app_hosting` 12 | 13 | :read-more{title="Firebase App Hosting" to="https://firebase.google.com/docs/app-hosting"} 14 | 15 | ::tip 16 | You can integrate with this provider using [zero configuration](/deploy/#zero-config-providers). 17 | :: 18 | 19 | ### Project setup 20 | 21 | 1. Go to the Firebase [console](https://console.firebase.google.com/) and set up a new project. 22 | 2. Select **Build > App Hosting** from the sidebar. 23 | - You may need to upgrade your billing plan at this step. 24 | 3. Click **Get Started**. 25 | - Choose a region. 26 | - Import a GitHub repository (you’ll need to link your GitHub account). 27 | - Configure deployment settings (project root directory and branch), and enable automatic rollouts. 28 | - Choose a unique ID for your backend. 29 | 4. Click Finish & Deploy to create your first rollout. 30 | 31 | When you deploy with Firebase App Hosting, the App Hosting preset will be run automatically at build time. 32 | -------------------------------------------------------------------------------- /docs/2.deploy/20.providers/gitlab-pages.md: -------------------------------------------------------------------------------- 1 | # GitLab Pages 2 | 3 | > Deploy Nitro apps to GitLab Pages. 4 | 5 | **Preset:** `gitlab_pages` 6 | 7 | :read-more{title="GitLab Pages" to="https://pages.github.com/"} 8 | 9 | ## Setup 10 | 11 | Follow the steps to [create a GitLab Pages site](https://docs.gitlab.com/ee/user/project/pages/#getting-started). 12 | 13 | ## Deployment 14 | 15 | 1. Here is an example GitLab Pages workflow to deploy your site to GitLab Pages: 16 | 17 | ```yaml [.gitlab-ci.yml] 18 | image: node:lts 19 | before_script: 20 | - npx nypm install 21 | pages: 22 | cache: 23 | paths: 24 | - node_modules/ 25 | variables: 26 | NITRO_PRESET: gitlab_pages 27 | script: 28 | - npm run build 29 | artifacts: 30 | paths: 31 | - .output/public 32 | publish: .output/public 33 | rules: 34 | # This ensures that only pushes to the default branch 35 | # will trigger a pages deploy 36 | - if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH 37 | ``` 38 | -------------------------------------------------------------------------------- /docs/2.deploy/20.providers/iis.md: -------------------------------------------------------------------------------- 1 | # IIS 2 | 3 | > Deploy Nitro apps to IIS. 4 | 5 | ## Using [IISnode](https://github.com/Azure/iisnode) 6 | 7 | **Preset:** `iis_node` 8 | 9 | 1. Install the latest LTS version of [Node.js](https://nodejs.org/en/) on your Windows Server. 10 | 2. Install [IISnode](https://github.com/azure/iisnode/releases) 11 | 3. Install [IIS `URLRewrite` Module](https://www.iis.net/downloads/microsoft/url-rewrite). 12 | 4. In IIS, add `.mjs` as a new mime type and set its content type to `application/javascript`. 13 | 5. Deploy the contents of your `.output` folder to your website in IIS. 14 | 15 | ## Using IIS handler 16 | 17 | **Preset:** `iis_handler` 18 | 19 | You can use IIS http handler directly. 20 | 21 | 1. Install the latest LTS version of [Node.js](https://nodejs.org/en/) on your Windows Server. 22 | 2. Install [IIS `HttpPlatformHandler` Module](https://www.iis.net/downloads/microsoft/httpplatformhandler) 23 | 3. Copy your `.output` directory into the Windows Server, and create a website on IIS pointing to that exact directory. 24 | 25 | ## IIS config options 26 | 27 | ::code-group 28 | 29 | ```ts [nitro.config.ts] 30 | export default defineNitroConfig({ 31 | // IIS options default 32 | iis: { 33 | // merges in a pre-existing web.config file to the nitro default file 34 | mergeConfig: true, 35 | // overrides the default nitro web.config file all together 36 | overrideConfig: false, 37 | }, 38 | }); 39 | ``` 40 | 41 | ```ts [nuxt.config.ts] 42 | export default defineNuxtConfig({ 43 | nitro: { 44 | // IIS options default 45 | iis: { 46 | // merges in a pre-existing web.config file to the nitro default file 47 | mergeConfig: true, 48 | // overrides the default nitro web.config file all together 49 | overrideConfig: false, 50 | }, 51 | }, 52 | }); 53 | ``` 54 | 55 | :: 56 | -------------------------------------------------------------------------------- /docs/2.deploy/20.providers/platform-sh.md: -------------------------------------------------------------------------------- 1 | # Platform.sh 2 | 3 | > Deploy Nitro apps to platform.sh 4 | 5 | **Preset:** `platform_sh` 6 | 7 | :read-more{to="https://platform.sh"} 8 | 9 | ## Setup 10 | 11 | First, create a new project on platform.sh and link it to the repository you want to auto-deploy with. 12 | 13 | Then in repository create `.platform.app.yaml` file: 14 | 15 | ```yaml [.platform.app.yaml] 16 | name: nitro-app 17 | type: 'nodejs:18' 18 | disk: 128 19 | web: 20 | commands: 21 | start: "node .output/server/index.mjs" 22 | build: 23 | flavor: none 24 | hooks: 25 | build: | 26 | corepack enable 27 | npx nypm install 28 | NITR_PRESET=platform_sh npm run build 29 | mounts: 30 | '.data': 31 | source: local 32 | source_path: .data 33 | ``` 34 | 35 | :read-more{title="Complete list of all available properties" to="https://docs.platform.sh/create-apps/app-reference.html"} 36 | 37 | :read-more{title="Complete list of all available properties" to="https://unjs.io/blog/2023-08-25-nitro-2.6#default-persistent-data-storage"} 38 | -------------------------------------------------------------------------------- /docs/2.deploy/20.providers/render.md: -------------------------------------------------------------------------------- 1 | # Render.com 2 | 3 | > Deploy Nitro apps to Render.com. 4 | 5 | **Preset:** `render_com` 6 | 7 | :read-more{title="render.com" to="https://render.com"} 8 | 9 | ## Set up application 10 | 11 | 1. [Create a new Web Service](https://dashboard.render.com/select-repo?type=web) and select the repository that contains your code. 12 | 2. Ensure the 'Node' environment is selected. 13 | 3. Update the start command to `node .output/server/index.mjs` 14 | 4. Click 'Advanced' and add an environment variable with `NITRO_PRESET` set to `render_com`. You may also need to add a `NODE_VERSION` environment variable set to `18` for the build to succeed ([docs](https://render.com/docs/node-version)). 15 | 5. Click 'Create Web Service'. 16 | 17 | ## Infrastructure as Code (IaC) 18 | 19 | 1. Create a file called `render.yaml` with following content at the root of your repository. 20 | 21 | > This file followed by [Infrastructure as Code](https://render.com/docs/infrastructure-as-code) on Render 22 | 23 | ```yaml 24 | services: 25 | - type: web 26 | name: 27 | env: node 28 | branch: main 29 | startCommand: node .output/server/index.mjs 30 | buildCommand: npx nypm install && npm run build 31 | envVars: 32 | - key: NITRO_PRESET 33 | value: render_com 34 | ``` 35 | 36 | 1. [Create a new Blueprint Instance](https://dashboard.render.com/select-repo?type=blueprint) and select the repository containing your `render.yaml` file. 37 | 38 | You should be good to go! 39 | -------------------------------------------------------------------------------- /docs/2.deploy/20.providers/stormkit.md: -------------------------------------------------------------------------------- 1 | # StormKit 2 | 3 | > Deploy Nitro apps to StormKit. 4 | 5 | **Preset:** `stormkit` 6 | 7 | :read-more{title="Stormkit" to="https://www.stormkit.io"} 8 | 9 | ::note 10 | Integration with [Stormkit](https://www.stormkit.io/) is possible with [zero configuration](/deploy#zero-config-providers). 11 | :: 12 | 13 | ## Setup 14 | 15 | Follow the steps to [create a new app](https://app.stormkit.io/apps/new) on Stormkit. 16 | 17 | ![Create a new app on Stormkit](/images/stormkit-new-app.png) 18 | 19 | ## Deployment 20 | 21 | By default, Stormkit will deploy your apps automatically when you push changes to your main branch. But to trigger a manual deploy (for example, you might do this for the very first deployment), you may click `Deploy now`. 22 | 23 | ![Trigger a manual deploy with Deploy Now](/images/stormkit-deploy.png) 24 | -------------------------------------------------------------------------------- /docs/2.deploy/20.providers/zeabur.md: -------------------------------------------------------------------------------- 1 | # Zeabur 2 | 3 | > Deploy Nitro apps to [Zeabur](https://zeabur.com). 4 | 5 | **Preset:** `zeabur` 6 | 7 | :read-more{title="Zeabur" to="https://zeabur.com"} 8 | 9 | ::note 10 | Integration with this provider is possible with [zero configuration](/deploy/#zero-config-providers). 11 | :: 12 | 13 | ## Deploy using git 14 | 15 | 1. Push your code to your git repository (Currently only GitHub supported). 16 | 2. [Import your project](https://zeabur.com/docs/get-started) into Zeabur. 17 | 3. Zeabur will detect that you are using Nitro and will enable the correct settings for your deployment. 18 | 4. Your application is deployed! 19 | -------------------------------------------------------------------------------- /docs/bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitrojs/nitro/8c2a2d8589a1c65b091f8bce1ca25d756f25bb11/docs/bun.lockb -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "undocs dev", 5 | "build": "undocs build" 6 | }, 7 | "devDependencies": { 8 | "undocs": "^0.2.30" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import unjs from "eslint-config-unjs"; 2 | 3 | export default unjs({ 4 | ignores: [ 5 | "**/.output", 6 | "**/.nitro", 7 | "**/.netlify", 8 | "**/.vercel", 9 | "**/.nuxt", 10 | "**/*.gen.*", 11 | "**/dist", 12 | ], 13 | rules: { 14 | "unicorn/no-null": 0, 15 | "no-undef": 0, 16 | "@typescript-eslint/no-unused-vars": 0, 17 | "unicorn/filename-case": 0, 18 | "unicorn/consistent-function-scoping": 0, 19 | "@typescript-eslint/no-empty-object-type": 0, 20 | "unicorn/no-empty-file": 0, 21 | "unicorn/prefer-ternary": 0, 22 | }, 23 | }); 24 | -------------------------------------------------------------------------------- /examples/api-routes/api/hello.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(() => "Nitro is amazing!"); 2 | -------------------------------------------------------------------------------- /examples/api-routes/api/hello/[name].ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler( 2 | (event) => `Hello ${event.context.params.name}!` 3 | ); 4 | -------------------------------------------------------------------------------- /examples/api-routes/api/test.get.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(() => "Test get handler"); 2 | -------------------------------------------------------------------------------- /examples/api-routes/api/test.post.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(async (event) => { 2 | const body = await readBody(event); 3 | return { 4 | message: "Test post handler", 5 | body, 6 | }; 7 | }); 8 | -------------------------------------------------------------------------------- /examples/api-routes/nitro.config.ts: -------------------------------------------------------------------------------- 1 | export default defineNitroConfig({ 2 | compatibilityDate: "2025-03-01", 3 | }); 4 | -------------------------------------------------------------------------------- /examples/api-routes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-api-routes", 3 | "private": true, 4 | "scripts": { 5 | "dev": "nitro dev", 6 | "build": "nitro build" 7 | }, 8 | "devDependencies": { 9 | "nitropack": "latest" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/api-routes/routes/[...].ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(() => { 2 | return ` 3 |

API Routes:

4 | 9 | `; 10 | }); 11 | -------------------------------------------------------------------------------- /examples/api-routes/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.nitro/types/tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /examples/auto-imports/nitro.config.ts: -------------------------------------------------------------------------------- 1 | export default defineNitroConfig({ 2 | compatibilityDate: "2025-03-01", 3 | }); 4 | -------------------------------------------------------------------------------- /examples/auto-imports/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-auto-imports", 3 | "private": true, 4 | "scripts": { 5 | "dev": "nitro dev", 6 | "build": "nitro build" 7 | }, 8 | "devDependencies": { 9 | "nitropack": "latest" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/auto-imports/routes/index.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(() => `

${makeGreeting("Nitro")}

`); 2 | -------------------------------------------------------------------------------- /examples/auto-imports/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.nitro/types/tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /examples/auto-imports/utils/hello.ts: -------------------------------------------------------------------------------- 1 | export function makeGreeting(name: string) { 2 | return `Hello, ${name}!`; 3 | } 4 | -------------------------------------------------------------------------------- /examples/cached-handler/nitro.config.ts: -------------------------------------------------------------------------------- 1 | export default defineNitroConfig({ 2 | compatibilityDate: "2025-03-01", 3 | }); 4 | -------------------------------------------------------------------------------- /examples/cached-handler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-cached-handler", 3 | "private": true, 4 | "scripts": { 5 | "dev": "nitro dev", 6 | "build": "nitro build" 7 | }, 8 | "devDependencies": { 9 | "nitropack": "latest" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/cached-handler/routes/index.ts: -------------------------------------------------------------------------------- 1 | export default defineCachedEventHandler( 2 | async () => { 3 | await new Promise((resolve) => setTimeout(resolve, 1000)); 4 | return `Response generated at ${new Date().toISOString()} (took 1 second)`; 5 | }, 6 | { 7 | shouldBypassCache: (e) => e.node.req.url.includes("preview"), 8 | } 9 | ); 10 | -------------------------------------------------------------------------------- /examples/cached-handler/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.nitro/types/tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /examples/custom-error-handler/error.ts: -------------------------------------------------------------------------------- 1 | import type { NitroErrorHandler } from "nitro"; 2 | 3 | const errorHandler: NitroErrorHandler = function (error, event) { 4 | event.res.end("[custom error handler] " + error.stack); 5 | }; 6 | 7 | export default errorHandler; 8 | -------------------------------------------------------------------------------- /examples/custom-error-handler/nitro.config.ts: -------------------------------------------------------------------------------- 1 | import errorHandler from "./error"; 2 | 3 | export default defineNitroConfig({ 4 | compatibilityDate: "2025-03-01", 5 | errorHandler: "~/error", 6 | devErrorHandler: errorHandler, 7 | }); 8 | -------------------------------------------------------------------------------- /examples/custom-error-handler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-custom-error-handler", 3 | "private": true, 4 | "scripts": { 5 | "dev": "nitro dev", 6 | "build": "nitro build" 7 | }, 8 | "devDependencies": { 9 | "nitropack": "latest" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/custom-error-handler/routes/index.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(() => { 2 | throw new Error("Example Error!"); 3 | }); 4 | -------------------------------------------------------------------------------- /examples/custom-error-handler/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.nitro/types/tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /examples/database/nitro.config.ts: -------------------------------------------------------------------------------- 1 | export default defineNitroConfig({ 2 | compatibilityDate: "2025-03-01", 3 | experimental: { 4 | database: true, 5 | tasks: true, 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /examples/database/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-database", 3 | "private": true, 4 | "scripts": { 5 | "dev": "nitro dev", 6 | "build": "nitro build" 7 | }, 8 | "devDependencies": { 9 | "nitropack": "latest" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/database/routes/index.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(async () => { 2 | const db = useDatabase(); 3 | 4 | // Create users table 5 | await db.sql`DROP TABLE IF EXISTS users`; 6 | await db.sql`CREATE TABLE IF NOT EXISTS users ("id" TEXT PRIMARY KEY, "firstName" TEXT, "lastName" TEXT, "email" TEXT)`; 7 | 8 | // Add a new user 9 | const userId = String(Math.round(Math.random() * 10_000)); 10 | await db.sql`INSERT INTO users VALUES (${userId}, 'John', 'Doe', '')`; 11 | 12 | // Query for users 13 | const { rows } = await db.sql`SELECT * FROM users WHERE id = ${userId}`; 14 | 15 | return { 16 | rows, 17 | }; 18 | }); 19 | -------------------------------------------------------------------------------- /examples/database/tasks/db/migrate.ts: -------------------------------------------------------------------------------- 1 | export default defineTask({ 2 | meta: { 3 | description: "Run database migrations", 4 | }, 5 | async run() { 6 | const db = useDatabase(); 7 | 8 | console.log("Running database migrations..."); 9 | 10 | // Create users table 11 | await db.sql`DROP TABLE IF EXISTS users`; 12 | await db.sql`CREATE TABLE IF NOT EXISTS users ("id" TEXT PRIMARY KEY, "firstName" TEXT, "lastName" TEXT, "email" TEXT)`; 13 | 14 | return { 15 | result: "Database migrations complete!", 16 | }; 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /examples/database/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.nitro/types/tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /examples/graceful-shutdown/nitro.config.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroConfig } from "nitro/config"; 2 | 3 | export default defineNitroConfig({ 4 | compatibilityDate: "2025-03-01", 5 | }); 6 | -------------------------------------------------------------------------------- /examples/graceful-shutdown/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-graceful-shutdown", 3 | "private": true, 4 | "scripts": { 5 | "dev": "nitro dev", 6 | "build": "nitro build" 7 | }, 8 | "devDependencies": { 9 | "nitropack": "latest" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/graceful-shutdown/plugins/db.ts: -------------------------------------------------------------------------------- 1 | export default defineNitroPlugin((nitroApp) => { 2 | nitroApp.hooks.hookOnce("close", async () => { 3 | console.log("Disconnecting database..."); 4 | 5 | // something you want to do, such like disconnect the database, or wait until the task is done 6 | await new Promise((resolve) => setTimeout(resolve, 500)); 7 | 8 | console.log("Database is disconnected!"); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /examples/graceful-shutdown/routes/index.ts: -------------------------------------------------------------------------------- 1 | import { eventHandler } from "h3"; 2 | 3 | export default eventHandler(async () => { 4 | console.log("Event handler is running..."); 5 | await new Promise((resolve) => setTimeout(resolve, 2000)); 6 | return { message: "Response took 2 seconds" }; 7 | }); 8 | -------------------------------------------------------------------------------- /examples/graceful-shutdown/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.nitro/types/tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /examples/hello-world/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .data 4 | .nitro 5 | .cache 6 | .output 7 | .env 8 | -------------------------------------------------------------------------------- /examples/hello-world/README.md: -------------------------------------------------------------------------------- 1 | # Nitro starter 2 | 3 | Look at the [nitro quick start](https://nitro.build/guide#quick-start) to learn more how to get started. 4 | -------------------------------------------------------------------------------- /examples/hello-world/nitro.config.ts: -------------------------------------------------------------------------------- 1 | // https://nitro.build/config 2 | export default defineNitroConfig({ 3 | compatibilityDate: "2025-03-01", 4 | srcDir: "server", 5 | }); 6 | -------------------------------------------------------------------------------- /examples/hello-world/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "scripts": { 4 | "build": "nitro build", 5 | "dev": "nitro dev", 6 | "preview": "node .output/server/index.mjs" 7 | }, 8 | "devDependencies": { 9 | "nitropack": "latest" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/hello-world/server/routes/index.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler((event) => { 2 | return "Start by editing server/routes/index.ts."; 3 | }); 4 | -------------------------------------------------------------------------------- /examples/hello-world/tsconfig.json: -------------------------------------------------------------------------------- 1 | // https://nitro.build/guide/typescript 2 | { 3 | "extends": "./.nitro/types/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /examples/middleware/middleware/auth.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler((event) => { 2 | event.context.auth = { name: "User " + Math.round(Math.random() * 100) }; 3 | }); 4 | -------------------------------------------------------------------------------- /examples/middleware/nitro.config.ts: -------------------------------------------------------------------------------- 1 | export default defineNitroConfig({ 2 | compatibilityDate: "2025-03-01", 3 | }); 4 | -------------------------------------------------------------------------------- /examples/middleware/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-middleware", 3 | "private": true, 4 | "scripts": { 5 | "dev": "nitro dev", 6 | "build": "nitro build" 7 | }, 8 | "devDependencies": { 9 | "nitropack": "latest" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/middleware/routes/index.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler((event) => ({ 2 | auth: event.context.auth, 3 | })); 4 | -------------------------------------------------------------------------------- /examples/middleware/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.nitro/types/tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /examples/nano-jsx/nitro.config.ts: -------------------------------------------------------------------------------- 1 | export default defineNitroConfig({ 2 | compatibilityDate: "2025-03-01", 3 | }); 4 | -------------------------------------------------------------------------------- /examples/nano-jsx/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-nano-jsx", 3 | "private": true, 4 | "scripts": { 5 | "dev": "nitro dev", 6 | "build": "nitro build" 7 | }, 8 | "devDependencies": { 9 | "nano-jsx": "^0.0.37", 10 | "nitropack": "latest" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/nano-jsx/routes/[...path].tsx: -------------------------------------------------------------------------------- 1 | import { defineEventHandler } from "h3"; 2 | import { h, renderSSR } from "nano-jsx"; 3 | 4 | export default defineEventHandler(() => { 5 | const html = renderSSR(() =>

Nitro + nano-jsx works!

); 6 | return html; 7 | }); 8 | -------------------------------------------------------------------------------- /examples/plugins/nitro.config.ts: -------------------------------------------------------------------------------- 1 | export default defineNitroConfig({ 2 | compatibilityDate: "2025-03-01", 3 | plugins: ["~/plugins/test"], 4 | }); 5 | -------------------------------------------------------------------------------- /examples/plugins/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-plugins", 3 | "private": true, 4 | "scripts": { 5 | "dev": "nitro dev", 6 | "build": "nitro build" 7 | }, 8 | "devDependencies": { 9 | "nitropack": "latest" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/plugins/plugins/test.ts: -------------------------------------------------------------------------------- 1 | export default defineNitroPlugin((_nitroApp) => { 2 | console.log("Nitro plugin!"); 3 | }); 4 | -------------------------------------------------------------------------------- /examples/plugins/routes/index.ts: -------------------------------------------------------------------------------- 1 | import { eventHandler } from "h3"; 2 | 3 | export default eventHandler(() => "

Hello Nitro!

"); 4 | -------------------------------------------------------------------------------- /examples/plugins/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.nitro/types/tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /examples/renderer/api/hello.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(() => "Nitro is amazing!"); 2 | -------------------------------------------------------------------------------- /examples/renderer/nitro.config.ts: -------------------------------------------------------------------------------- 1 | export default defineNitroConfig({ 2 | compatibilityDate: "2025-03-01", 3 | renderer: "~/renderer", 4 | }); 5 | -------------------------------------------------------------------------------- /examples/renderer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-renderer", 3 | "private": true, 4 | "scripts": { 5 | "dev": "nitro dev", 6 | "build": "nitro build" 7 | }, 8 | "devDependencies": { 9 | "nitropack": "latest" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/renderer/renderer.ts: -------------------------------------------------------------------------------- 1 | import { defineRenderHandler } from "nitro/runtime"; 2 | 3 | export default defineRenderHandler((_event) => { 4 | return { 5 | body: /* html */ ` 6 | 7 | 8 | Rendered Page 9 | 10 | 11 |

Rendered by Nitro!

12 | 13 | `, 14 | }; 15 | }); 16 | -------------------------------------------------------------------------------- /examples/renderer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.nitro/types/tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /examples/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "include": ["."] 4 | } 5 | -------------------------------------------------------------------------------- /examples/websocket/nitro.config.ts: -------------------------------------------------------------------------------- 1 | export default defineNitroConfig({ 2 | compatibilityDate: "2025-03-01", 3 | experimental: { 4 | websocket: true, 5 | }, 6 | }); 7 | -------------------------------------------------------------------------------- /examples/websocket/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-websocket", 3 | "private": true, 4 | "scripts": { 5 | "dev": "nitro dev", 6 | "build": "nitro build" 7 | }, 8 | "devDependencies": { 9 | "nitropack": "latest" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/websocket/routes/_ws.ts: -------------------------------------------------------------------------------- 1 | export default defineWebSocketHandler({ 2 | open(peer) { 3 | peer.send({ user: "server", message: `Welcome ${peer}!` }); 4 | peer.publish("chat", { user: "server", message: `${peer} joined!` }); 5 | peer.subscribe("chat"); 6 | }, 7 | message(peer, message) { 8 | if (message.text().includes("ping")) { 9 | peer.send({ user: "server", message: "pong" }); 10 | } else { 11 | const msg = { 12 | user: peer.toString(), 13 | message: message.toString(), 14 | }; 15 | peer.send(msg); // echo 16 | peer.publish("chat", msg); 17 | } 18 | }, 19 | close(peer) { 20 | peer.publish("chat", { user: "server", message: `${peer} left!` }); 21 | }, 22 | }); 23 | -------------------------------------------------------------------------------- /examples/websocket/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.nitro/types/tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /lib/config.d.mts: -------------------------------------------------------------------------------- 1 | import { NitroConfig } from "nitro/types"; 2 | 3 | export { NitroConfig } from "nitro/types"; 4 | 5 | declare function defineNitroConfig( 6 | config: Omit 7 | ): Omit; 8 | 9 | export { defineNitroConfig }; 10 | -------------------------------------------------------------------------------- /lib/config.mjs: -------------------------------------------------------------------------------- 1 | function defineNitroConfig(config) { 2 | return config; 3 | } 4 | 5 | export { defineNitroConfig }; 6 | -------------------------------------------------------------------------------- /lib/meta.d.mts: -------------------------------------------------------------------------------- 1 | import type { CompatibilityUpdate } from "compatx"; 2 | 3 | export const version: string; 4 | 5 | export const compatibilityChanges: CompatibilityUpdate[]; 6 | -------------------------------------------------------------------------------- /lib/meta.mjs: -------------------------------------------------------------------------------- 1 | import packageJson from "../package.json" with { type: "json" }; 2 | 3 | export const version = packageJson.version; 4 | 5 | export const compatibilityChanges = [ 6 | { 7 | from: "2024-05-07", 8 | platform: "netlify", 9 | description: "Netlify functions v2", 10 | }, 11 | { 12 | from: "2024-09-19", 13 | platform: "cloudflare", 14 | description: "Static assets support for cloudflare-module preset", 15 | }, 16 | { 17 | from: "2025-01-30", 18 | platform: "deno", 19 | description: "Deno v2 Node.js compatibility", 20 | }, 21 | ]; 22 | -------------------------------------------------------------------------------- /lib/runtime-meta.d.mts: -------------------------------------------------------------------------------- 1 | export declare const pkgDir: string; 2 | export declare const runtimeDir: string; 3 | export declare const subpaths: string[]; 4 | export declare const runtimeDependencies: string[]; 5 | -------------------------------------------------------------------------------- /lib/runtime-meta.mjs: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from "node:url"; 2 | 3 | export const pkgDir = fileURLToPath(new URL("..", import.meta.url)); 4 | 5 | export const runtimeDir = fileURLToPath( 6 | new URL("../dist/runtime/", import.meta.url) 7 | ); 8 | 9 | export const runtimeDependencies = [ 10 | "h3", 11 | "cookie-es", 12 | "defu", 13 | "destr", 14 | "hookable", 15 | "iron-webcrypto", 16 | "klona", 17 | "node-mock-http", 18 | "ofetch", 19 | "ohash", 20 | "pathe", 21 | "radix3", 22 | "scule", 23 | "ufo", 24 | "db0", 25 | "std-env", 26 | "uncrypto", 27 | "unctx", 28 | "unenv", 29 | "unstorage", 30 | "crossws", 31 | "croner", 32 | ]; 33 | -------------------------------------------------------------------------------- /playground/nitro.config.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroConfig } from "nitro/config"; 2 | 3 | export default defineNitroConfig({ 4 | compatibilityDate: "latest", 5 | srcDir: "server", 6 | }); 7 | -------------------------------------------------------------------------------- /playground/public/test.txt: -------------------------------------------------------------------------------- 1 | Test works! 2 | -------------------------------------------------------------------------------- /playground/server/routes/index.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(async (event) => { 2 | return {}; 3 | }); 4 | -------------------------------------------------------------------------------- /playground/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.nitro/types/tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "examples/**" 3 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["github>unjs/renovate-config"] 3 | } 4 | -------------------------------------------------------------------------------- /scripts/release-nightly.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Temporary forked from nuxt/nuxt 4 | 5 | set -xe 6 | 7 | # Restore all git changes 8 | git restore -s@ -SW -- . 9 | 10 | # Bump according to changelog 11 | pnpm changelogen --bump 12 | 13 | # Bump versions to nightly 14 | pnpm jiti ./scripts/bump-nightly 15 | 16 | # Build mirror 17 | pnpm gen-mirror 18 | 19 | # Resolve lockfile 20 | # pnpm install 21 | 22 | # Update token 23 | if [[ ! -z ${NODE_AUTH_TOKEN} ]] ; then 24 | echo "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}" >> ~/.npmrc 25 | echo "registry=https://registry.npmjs.org/" >> ~/.npmrc 26 | echo "always-auth=true" >> ~/.npmrc 27 | echo "npmAuthToken: ${NODE_AUTH_TOKEN}" >> ~/.npmrc.yml 28 | npm whoami 29 | fi 30 | 31 | # Release packages 32 | 33 | # nitro-nightly@latest => v3 34 | npm publish --access public --tolerate-republish --tag latest 35 | 36 | # nitropack-nightly@3x => v3-mirror 37 | cd .mirror 38 | npm publish --access public --tolerate-republish --tag 3x 39 | -------------------------------------------------------------------------------- /src/build/build.ts: -------------------------------------------------------------------------------- 1 | import type { Nitro } from "nitro/types"; 2 | 3 | export async function build(nitro: Nitro) { 4 | switch (nitro.options.builder) { 5 | case "rollup": { 6 | const { rollupBuild } = await import("./rollup/build"); 7 | return rollupBuild(nitro); 8 | } 9 | case "rolldown": { 10 | const { rolldownBuild } = await import("./rolldown/build"); 11 | return rolldownBuild(nitro); 12 | } 13 | default: { 14 | throw new Error(`Unknown builder: ${nitro.options.builder}`); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/build/plugins/database.ts: -------------------------------------------------------------------------------- 1 | import { connectors } from "db0"; 2 | import type { Nitro } from "nitro/types"; 3 | import { camelCase } from "scule"; 4 | import { virtual } from "./virtual"; 5 | 6 | export function database(nitro: Nitro) { 7 | if (!nitro.options.experimental.database) { 8 | return virtual( 9 | { 10 | "#nitro-internal-virtual/database": () => { 11 | return /* js */ `export const connectionConfigs = {};`; 12 | }, 13 | }, 14 | nitro.vfs 15 | ); 16 | } 17 | 18 | const dbConfigs = 19 | (nitro.options.dev && nitro.options.devDatabase) || nitro.options.database; 20 | 21 | const connectorsNames = [ 22 | ...new Set( 23 | Object.values(dbConfigs || {}).map((config) => config?.connector) 24 | ), 25 | ].filter(Boolean); 26 | 27 | for (const name of connectorsNames) { 28 | if (!connectors[name]) { 29 | throw new Error(`Database connector "${name}" is invalid.`); 30 | } 31 | } 32 | 33 | return virtual( 34 | { 35 | "#nitro-internal-virtual/database": () => { 36 | return ` 37 | ${connectorsNames 38 | .map( 39 | (name) => `import ${camelCase(name)}Connector from "${connectors[name]}";` 40 | ) 41 | .join("\n")} 42 | 43 | export const connectionConfigs = { 44 | ${Object.entries(dbConfigs || {}) 45 | .map( 46 | ([name, { connector, options }]) => 47 | `${name}: { 48 | connector: ${camelCase(connector)}Connector, 49 | options: ${JSON.stringify(options)} 50 | }` 51 | ) 52 | .join(",\n")} 53 | }; 54 | `; 55 | }, 56 | }, 57 | nitro.vfs 58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /src/build/plugins/error-handler.ts: -------------------------------------------------------------------------------- 1 | import type { Nitro } from "nitro/types"; 2 | import { virtual } from "./virtual"; 3 | import { runtimeDir } from "nitro/runtime/meta"; 4 | import { join } from "pathe"; 5 | 6 | export function errorHandler(nitro: Nitro) { 7 | return virtual( 8 | { 9 | "#nitro-internal-virtual/error-handler": () => { 10 | const errorHandlers = Array.isArray(nitro.options.errorHandler) 11 | ? nitro.options.errorHandler 12 | : [nitro.options.errorHandler]; 13 | 14 | const builtinHandler = join( 15 | runtimeDir, 16 | `internal/error/${nitro.options.dev ? "dev" : "prod"}` 17 | ); 18 | 19 | return /* js */ ` 20 | ${errorHandlers.map((h, i) => `import errorHandler$${i} from "${h}";`).join("\n")} 21 | 22 | const errorHandlers = [${errorHandlers.map((_, i) => `errorHandler$${i}`).join(", ")}]; 23 | 24 | import { defaultHandler } from "${builtinHandler}"; 25 | 26 | export default async function(error, event) { 27 | for (const handler of errorHandlers) { 28 | try { 29 | await handler(error, event, { defaultHandler }); 30 | if (event.handled) { 31 | return; // Response handled 32 | } 33 | } catch(error) { 34 | // Handler itself thrown, log and continue 35 | console.error(error); 36 | } 37 | } 38 | // H3 will handle fallback 39 | } 40 | `; 41 | }, 42 | }, 43 | nitro.vfs 44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /src/build/plugins/replace.ts: -------------------------------------------------------------------------------- 1 | import _replace from "@rollup/plugin-replace"; 2 | import type { RollupReplaceOptions } from "@rollup/plugin-replace"; 3 | import type { Plugin } from "rollup"; 4 | 5 | const NO_REPLACE_RE = /ROLLUP_NO_REPLACE/; 6 | 7 | export function replace(options: RollupReplaceOptions): Plugin { 8 | const _plugin = _replace(options); 9 | return { 10 | ..._plugin, 11 | // https://github.com/rollup/plugins/blob/master/packages/replace/src/index.js#L94 12 | renderChunk(code, chunk, options) { 13 | if (!NO_REPLACE_RE.test(code)) { 14 | // prettier-ignore 15 | // @ts-ignore 16 | return (_plugin.renderChunk as () => any).call(this, code, chunk, options ); 17 | } 18 | }, 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /src/build/plugins/server-main.ts: -------------------------------------------------------------------------------- 1 | import type { Nitro } from "nitro/types"; 2 | import type { Plugin } from "rollup"; 3 | 4 | export function serverMain(nitro: Nitro): Plugin { 5 | return { 6 | name: "nitro:server-main", 7 | renderChunk(code, chunk) { 8 | if (chunk.isEntry) { 9 | return { 10 | code: `globalThis.__nitro_main__ = import.meta.url; ${code}`, 11 | map: null, 12 | }; 13 | } 14 | }, 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /src/build/plugins/sourcemap-min.ts: -------------------------------------------------------------------------------- 1 | import type { ExistingRawSourceMap, Plugin } from "rollup"; 2 | 3 | export function sourcemapMininify() { 4 | return { 5 | name: "nitro:sourcemap-minify", 6 | generateBundle(_options, bundle) { 7 | for (const [key, asset] of Object.entries(bundle)) { 8 | // Only process sourcemaps 9 | if ( 10 | !key.endsWith(".map") || 11 | !("source" in asset) || 12 | typeof asset.source !== "string" 13 | ) { 14 | continue; 15 | } 16 | // Parse sourcemap 17 | const sourcemap: ExistingRawSourceMap = JSON.parse(asset.source); 18 | // Only process sourcemaps with node_module sources 19 | if ( 20 | !(sourcemap.sources || []).some((s) => s.includes("node_modules")) 21 | ) { 22 | continue; 23 | } 24 | // TODO: Try to treeshake mappings instead 25 | sourcemap.mappings = ""; 26 | asset.source = JSON.stringify(sourcemap); 27 | } 28 | }, 29 | } satisfies Plugin; 30 | } 31 | -------------------------------------------------------------------------------- /src/build/plugins/virtual.ts: -------------------------------------------------------------------------------- 1 | import type { RollupVirtualOptions, VirtualModule } from "nitro/types"; 2 | import { dirname, resolve } from "pathe"; 3 | import type { Plugin } from "rollup"; 4 | 5 | // Based on https://github.com/rollup/plugins/blob/master/packages/virtual/src/index.ts 6 | 7 | const PREFIX = "\0virtual:"; 8 | 9 | export function virtual( 10 | modules: RollupVirtualOptions, 11 | cache: Record = {} 12 | ): Plugin { 13 | const _modules = new Map(); 14 | 15 | for (const [id, mod] of Object.entries(modules)) { 16 | cache[id] = mod; 17 | _modules.set(id, mod); 18 | _modules.set(resolve(id), mod); 19 | } 20 | 21 | return { 22 | name: "virtual", 23 | 24 | resolveId(id, importer) { 25 | if (id in modules) { 26 | return PREFIX + id; 27 | } 28 | 29 | if (importer) { 30 | const importerNoPrefix = importer.startsWith(PREFIX) 31 | ? importer.slice(PREFIX.length) 32 | : importer; 33 | const resolved = resolve(dirname(importerNoPrefix), id); 34 | if (_modules.has(resolved)) { 35 | return PREFIX + resolved; 36 | } 37 | } 38 | 39 | return null; 40 | }, 41 | 42 | async load(id) { 43 | if (!id.startsWith(PREFIX)) { 44 | return null; 45 | } 46 | 47 | const idNoPrefix = id.slice(PREFIX.length); 48 | if (!_modules.has(idNoPrefix)) { 49 | return null; 50 | } 51 | 52 | let m = _modules.get(idNoPrefix); 53 | if (typeof m === "function") { 54 | m = await m(); 55 | } 56 | 57 | if (!m) { 58 | return null; 59 | } 60 | 61 | cache[id.replace(PREFIX, "")] = m; 62 | 63 | return { 64 | code: m as string, 65 | map: null, 66 | }; 67 | }, 68 | }; 69 | } 70 | -------------------------------------------------------------------------------- /src/build/prepare.ts: -------------------------------------------------------------------------------- 1 | import fsp from "node:fs/promises"; 2 | import type { Nitro } from "nitro/types"; 3 | 4 | export async function prepare(nitro: Nitro) { 5 | await prepareDir(nitro.options.output.dir); 6 | if (!nitro.options.noPublicDir) { 7 | await prepareDir(nitro.options.output.publicDir); 8 | } 9 | if (!nitro.options.static) { 10 | await prepareDir(nitro.options.output.serverDir); 11 | } 12 | } 13 | 14 | async function prepareDir(dir: string) { 15 | await fsp.rm(dir, { recursive: true, force: true }); 16 | await fsp.mkdir(dir, { recursive: true }); 17 | } 18 | -------------------------------------------------------------------------------- /src/build/rolldown/build.ts: -------------------------------------------------------------------------------- 1 | import { getRolldownConfig } from "./config"; 2 | import type { Nitro } from "nitro/types"; 3 | import { watchDev } from "./dev"; 4 | import { buildProduction } from "./prod"; 5 | 6 | export async function rolldownBuild(nitro: Nitro) { 7 | await nitro.hooks.callHook("build:before", nitro); 8 | const config = getRolldownConfig(nitro); 9 | await nitro.hooks.callHook("rollup:before", nitro, config as any); 10 | return nitro.options.dev 11 | ? watchDev(nitro, config) 12 | : buildProduction(nitro, config); 13 | } 14 | -------------------------------------------------------------------------------- /src/build/rollup/build.ts: -------------------------------------------------------------------------------- 1 | import { getRollupConfig } from "./config"; 2 | import type { Nitro } from "nitro/types"; 3 | import { watchDev } from "./dev"; 4 | import { buildProduction } from "./prod"; 5 | 6 | export async function rollupBuild(nitro: Nitro) { 7 | await nitro.hooks.callHook("build:before", nitro); 8 | const config = getRollupConfig(nitro); 9 | await nitro.hooks.callHook("rollup:before", nitro, config); 10 | return nitro.options.dev 11 | ? watchDev(nitro, config) 12 | : buildProduction(nitro, config); 13 | } 14 | -------------------------------------------------------------------------------- /src/build/rollup/error.ts: -------------------------------------------------------------------------------- 1 | import type esbuild from "esbuild"; 2 | import { isAbsolute, relative } from "pathe"; 3 | import type rollup from "rollup"; 4 | 5 | export function formatRollupError( 6 | _error: rollup.RollupError | esbuild.OnResolveResult 7 | ) { 8 | try { 9 | const logs: string[] = [_error.toString()]; 10 | const errors = (_error as any)?.errors || [_error as rollup.RollupError]; 11 | for (const error of errors) { 12 | const id = 13 | (error as any).path || error.id || (_error as rollup.RollupError).id; 14 | let path = isAbsolute(id) ? relative(process.cwd(), id) : id; 15 | const location = 16 | (error as rollup.RollupError).loc || 17 | (error as esbuild.PartialMessage).location; 18 | if (location) { 19 | path += `:${location.line}:${location.column}`; 20 | } 21 | const text = 22 | (error as esbuild.PartialMessage).text || 23 | (error as rollup.RollupError).frame; 24 | logs.push( 25 | `Rollup error while processing \`${path}\`` + text ? "\n\n" + text : "" 26 | ); 27 | } 28 | return logs.join("\n"); 29 | } catch { 30 | return _error?.toString(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/build/snapshot.ts: -------------------------------------------------------------------------------- 1 | import type { Nitro } from "nitro/types"; 2 | import { dirname, join, resolve } from "pathe"; 3 | import { mkdir, writeFile } from "node:fs/promises"; 4 | import { snapshotStorage } from "../utils/storage"; 5 | 6 | export async function snapshot(nitro: Nitro) { 7 | if ( 8 | nitro.options.bundledStorage.length === 0 || 9 | nitro.options.preset === "nitro-prerender" 10 | ) { 11 | return; 12 | } 13 | // TODO: Use virtual storage for server assets 14 | const storageDir = resolve(nitro.options.buildDir, "snapshot"); 15 | nitro.options.serverAssets.push({ 16 | baseName: "nitro:bundled", 17 | dir: storageDir, 18 | }); 19 | 20 | const data = await snapshotStorage(nitro); 21 | await Promise.all( 22 | Object.entries(data).map(async ([path, contents]) => { 23 | if (typeof contents !== "string") { 24 | contents = JSON.stringify(contents); 25 | } 26 | const fsPath = join(storageDir, path.replace(/:/g, "/")); 27 | await mkdir(dirname(fsPath), { recursive: true }); 28 | await writeFile(fsPath, contents, "utf8"); 29 | }) 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/cli/commands/build.ts: -------------------------------------------------------------------------------- 1 | import nodeCrypto from "node:crypto"; 2 | import { defineCommand } from "citty"; 3 | import type { DateString } from "compatx"; 4 | import { 5 | build, 6 | copyPublicAssets, 7 | createNitro, 8 | prepare, 9 | prerender, 10 | } from "nitro"; 11 | import { resolve } from "pathe"; 12 | import { commonArgs } from "../common"; 13 | 14 | // globalThis.crypto support for Node.js 18 15 | if (!globalThis.crypto) { 16 | globalThis.crypto = nodeCrypto as unknown as Crypto; 17 | } 18 | 19 | export default defineCommand({ 20 | meta: { 21 | name: "build", 22 | description: "Build nitro project for production", 23 | }, 24 | args: { 25 | ...commonArgs, 26 | minify: { 27 | type: "boolean", 28 | description: 29 | "Minify the output (overrides preset defaults you can also use `--no-minify` to disable).", 30 | }, 31 | preset: { 32 | type: "string", 33 | description: 34 | "The build preset to use (you can also use `NITRO_PRESET` environment variable).", 35 | }, 36 | compatibilityDate: { 37 | type: "string", 38 | description: 39 | "The date to use for preset compatibility (you can also use `NITRO_COMPATIBILITY_DATE` environment variable).", 40 | }, 41 | }, 42 | async run({ args }) { 43 | const rootDir = resolve((args.dir || args._dir || ".") as string); 44 | const nitro = await createNitro( 45 | { 46 | rootDir, 47 | dev: false, 48 | minify: args.minify, 49 | preset: args.preset, 50 | }, 51 | { 52 | compatibilityDate: args.compatibilityDate as DateString, 53 | } 54 | ); 55 | await prepare(nitro); 56 | await copyPublicAssets(nitro); 57 | await prerender(nitro); 58 | await build(nitro); 59 | await nitro.close(); 60 | }, 61 | }); 62 | -------------------------------------------------------------------------------- /src/cli/commands/prepare.ts: -------------------------------------------------------------------------------- 1 | import { defineCommand } from "citty"; 2 | import { createNitro, writeTypes } from "nitro"; 3 | import { resolve } from "pathe"; 4 | import { commonArgs } from "../common"; 5 | 6 | export default defineCommand({ 7 | meta: { 8 | name: "prepare", 9 | description: "Generate types for the project", 10 | }, 11 | args: { 12 | ...commonArgs, 13 | }, 14 | async run({ args }) { 15 | const rootDir = resolve((args.dir || args._dir || ".") as string); 16 | const nitro = await createNitro({ rootDir }); 17 | await writeTypes(nitro); 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /src/cli/commands/task/index.ts: -------------------------------------------------------------------------------- 1 | import { defineCommand } from "citty"; 2 | 3 | export default defineCommand({ 4 | meta: { 5 | name: "task", 6 | description: "Operate in nitro tasks (experimental)", 7 | }, 8 | subCommands: { 9 | list: () => import("./list").then((r) => r.default), 10 | run: () => import("./run").then((r) => r.default), 11 | }, 12 | }); 13 | -------------------------------------------------------------------------------- /src/cli/commands/task/list.ts: -------------------------------------------------------------------------------- 1 | import { defineCommand } from "citty"; 2 | import { consola } from "consola"; 3 | import { listTasks, loadOptions } from "nitro"; 4 | import { resolve } from "pathe"; 5 | 6 | export default defineCommand({ 7 | meta: { 8 | name: "run", 9 | description: "List available tasks (experimental)", 10 | }, 11 | args: { 12 | dir: { 13 | type: "string", 14 | description: "project root directory", 15 | }, 16 | }, 17 | async run({ args }) { 18 | const cwd = resolve((args.dir || args.cwd || ".") as string); 19 | const options = await loadOptions({ rootDir: cwd }).catch(() => undefined); 20 | 21 | const tasks = await listTasks({ 22 | cwd, 23 | buildDir: options?.buildDir || ".nitro", 24 | }); 25 | for (const [name, task] of Object.entries(tasks)) { 26 | consola.log( 27 | ` - \`${name}\`${ 28 | task.meta?.description ? ` - ${task.meta.description}` : "" 29 | }` 30 | ); 31 | } 32 | }, 33 | }); 34 | -------------------------------------------------------------------------------- /src/cli/commands/task/run.ts: -------------------------------------------------------------------------------- 1 | import { defineCommand } from "citty"; 2 | import { consola } from "consola"; 3 | import destr from "destr"; 4 | import { createNitro, loadOptions, runTask } from "nitro"; 5 | import { resolve } from "pathe"; 6 | 7 | export default defineCommand({ 8 | meta: { 9 | name: "run", 10 | description: 11 | "Run a runtime task in the currently running dev server (experimental)", 12 | }, 13 | args: { 14 | name: { 15 | type: "positional", 16 | description: "task name", 17 | required: true, 18 | }, 19 | dir: { 20 | type: "string", 21 | description: "project root directory", 22 | }, 23 | payload: { 24 | type: "string", 25 | description: "payload json to pass to the task", 26 | }, 27 | }, 28 | async run({ args }) { 29 | const cwd = resolve((args.dir || args.cwd || ".") as string); 30 | const options = await loadOptions({ rootDir: cwd }).catch(() => undefined); 31 | 32 | consola.info(`Running task \`${args.name}\`...`); 33 | let payload: any = destr(args.payload || "{}"); 34 | if (typeof payload !== "object") { 35 | consola.error( 36 | `Invalid payload: \`${args.payload}\` (it should be a valid JSON object)` 37 | ); 38 | payload = undefined; 39 | } 40 | try { 41 | const { result } = await runTask( 42 | { 43 | name: args.name, 44 | context: {}, 45 | payload, 46 | }, 47 | { 48 | cwd, 49 | buildDir: options?.buildDir || ".nitro", 50 | } 51 | ); 52 | consola.success("Result:", result); 53 | } catch (error) { 54 | consola.error(`Failed to run task \`${args.name}\`: ${error}`); 55 | process.exit(1); // eslint-disable-line unicorn/no-process-exit 56 | } 57 | }, 58 | }); 59 | -------------------------------------------------------------------------------- /src/cli/common.ts: -------------------------------------------------------------------------------- 1 | import type { ArgsDef } from "citty"; 2 | 3 | export const commonArgs = { 4 | dir: { 5 | type: "string", 6 | description: "project root directory", 7 | }, 8 | _dir: { 9 | type: "positional", 10 | default: ".", 11 | description: "project root directory (prefer using `--dir`)", 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /src/cli/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { defineCommand, runMain } from "citty"; 3 | import { version as nitroVersion } from "nitro/meta"; 4 | 5 | const main = defineCommand({ 6 | meta: { 7 | name: "nitro", 8 | description: "Nitro CLI", 9 | version: nitroVersion, 10 | }, 11 | subCommands: { 12 | dev: () => import("./commands/dev").then((r) => r.default), 13 | build: () => import("./commands/build").then((r) => r.default), 14 | prepare: () => import("./commands/prepare").then((r) => r.default), 15 | task: () => import("./commands/task").then((r) => r.default), 16 | }, 17 | }); 18 | 19 | runMain(main); 20 | -------------------------------------------------------------------------------- /src/config/resolvers/builder.ts: -------------------------------------------------------------------------------- 1 | import type { NitroOptions } from "nitro/types"; 2 | 3 | export async function resolveBuilder(options: NitroOptions) { 4 | if (!options.builder) { 5 | options.builder = (process.env.NITRO_BUILDER as any) || "rollup"; 6 | } 7 | 8 | if (options.builder === "rolldown") { 9 | try { 10 | await import("rolldown"); 11 | } catch { 12 | throw new Error( 13 | `Builder "rolldown" is not available. Make sure to install "rolldown" package.` 14 | ); 15 | } 16 | } 17 | 18 | if (!["rollup", "rolldown"].includes(options.builder!)) { 19 | throw new Error(`Builder "${options.builder}" is not supported.`); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/config/resolvers/compatibility.ts: -------------------------------------------------------------------------------- 1 | import type { DateString } from "compatx"; 2 | import type { NitroOptions } from "nitro/types"; 3 | import { formatDate, resolveCompatibilityDatesFromEnv } from "compatx"; 4 | import _consola from "consola"; 5 | import { colors } from "consola/utils"; 6 | 7 | import { relative } from "pathe"; 8 | import { isTest } from "std-env"; 9 | 10 | // Nitro v2.9.6 release 11 | export const fallbackCompatibilityDate = "2024-04-03" as DateString; 12 | 13 | let _fallbackInfoShown = false; 14 | 15 | export async function resolveCompatibilityOptions(options: NitroOptions) { 16 | // Normalize and expand compatibility date from environment variables 17 | options.compatibilityDate = resolveCompatibilityDatesFromEnv( 18 | options.compatibilityDate 19 | ); 20 | 21 | // If no compatibility date is specified, prompt or notify the user to set it 22 | if (!options.compatibilityDate.default) { 23 | const consola = _consola.withTag("nitro"); 24 | if ( 25 | !_fallbackInfoShown && 26 | !isTest && 27 | options.preset !== "nitro-prerender" 28 | ) { 29 | consola.warn( 30 | [ 31 | /* WARN */ `Please add \`compatibilityDate: '${formatDate("latest")}'\` to the config file. Using \`${fallbackCompatibilityDate}\` as fallback.`, 32 | ` More info: ${colors.underline("https://nitro.build/deploy#compatibility-date")}`, 33 | ].join("\n") 34 | ); 35 | _fallbackInfoShown = true; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/config/resolvers/database.ts: -------------------------------------------------------------------------------- 1 | import type { NitroOptions } from "nitro/types"; 2 | 3 | export async function resolveDatabaseOptions(options: NitroOptions) { 4 | if (options.experimental.database && options.imports) { 5 | options.imports.presets.push({ 6 | from: "nitro/runtime/internal/database", 7 | imports: ["useDatabase"], 8 | }); 9 | if (options.dev && !options.database && !options.devDatabase) { 10 | options.devDatabase = { 11 | default: { 12 | connector: "sqlite", 13 | options: { 14 | cwd: options.rootDir, 15 | }, 16 | }, 17 | }; 18 | } else if (options.node && !options.database) { 19 | options.database = { 20 | default: { 21 | connector: "sqlite", 22 | options: {}, 23 | }, 24 | }; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/config/resolvers/error.ts: -------------------------------------------------------------------------------- 1 | import { runtimeDir } from "nitro/runtime/meta"; 2 | import type { NitroOptions } from "nitro/types"; 3 | import { join } from "pathe"; 4 | 5 | export async function resolveErrorOptions(options: NitroOptions) { 6 | if (!options.errorHandler) { 7 | options.errorHandler = []; 8 | } else if (!Array.isArray(options.errorHandler)) { 9 | options.errorHandler = [options.errorHandler]; 10 | } 11 | 12 | // Always add the default error handler as the last one 13 | options.errorHandler.push( 14 | join(runtimeDir, `internal/error/${options.dev ? "dev" : "prod"}`) 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/config/resolvers/export-conditions.ts: -------------------------------------------------------------------------------- 1 | import type { NitroOptions } from "nitro/types"; 2 | 3 | export async function resolveExportConditionsOptions(options: NitroOptions) { 4 | options.exportConditions = _resolveExportConditions( 5 | options.exportConditions || [], 6 | { dev: options.dev, node: options.node, wasm: options.experimental.wasm } 7 | ); 8 | } 9 | 10 | function _resolveExportConditions( 11 | conditions: string[], 12 | opts: { dev: boolean; node: boolean; wasm?: boolean } 13 | ) { 14 | const resolvedConditions: string[] = []; 15 | 16 | // 1. Add dev or production 17 | resolvedConditions.push(opts.dev ? "development" : "production"); 18 | 19 | // 2. Add user specified conditions 20 | resolvedConditions.push(...conditions); 21 | 22 | // 3. Add runtime conditions (node or web) 23 | if (opts.node) { 24 | resolvedConditions.push("node"); 25 | } else { 26 | // https://runtime-keys.proposal.wintercg.org/ 27 | resolvedConditions.push( 28 | "wintercg", 29 | "worker", 30 | "web", 31 | "browser", 32 | "workerd", 33 | "edge-light", 34 | "netlify", 35 | "edge-routine", 36 | "deno" 37 | ); 38 | } 39 | 40 | // 4. Add unwasm conditions 41 | if (opts.wasm) { 42 | resolvedConditions.push("wasm", "unwasm"); 43 | } 44 | 45 | // 5. Add default conditions 46 | resolvedConditions.push("import", "default"); 47 | 48 | // Dedup with preserving order 49 | return resolvedConditions.filter( 50 | (c, i) => resolvedConditions.indexOf(c) === i 51 | ); 52 | } 53 | -------------------------------------------------------------------------------- /src/config/resolvers/storage.ts: -------------------------------------------------------------------------------- 1 | import type { NitroOptions } from "nitro/types"; 2 | import { resolve } from "pathe"; 3 | 4 | export async function resolveStorageOptions(options: NitroOptions) { 5 | // Build-only storage 6 | const fsMounts = { 7 | root: resolve(options.rootDir), 8 | src: resolve(options.srcDir), 9 | build: resolve(options.buildDir), 10 | cache: resolve(options.buildDir, "cache"), 11 | } as const; 12 | for (const p in fsMounts) { 13 | options.devStorage[p] = options.devStorage[p] || { 14 | driver: "fs", 15 | readOnly: p === "root" || p === "src", 16 | base: fsMounts[p as keyof typeof fsMounts], 17 | }; 18 | } 19 | 20 | // Runtime storage 21 | if ( 22 | options.dev && 23 | options.storage.data === undefined && 24 | options.devStorage.data === undefined 25 | ) { 26 | options.devStorage.data = { 27 | driver: "fs", 28 | base: resolve(options.rootDir, ".data/kv"), 29 | }; 30 | } else if (options.node && options.storage.data === undefined) { 31 | options.storage.data = { 32 | driver: "fsLite", 33 | base: "./.data/kv", 34 | }; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/config/resolvers/unenv.ts: -------------------------------------------------------------------------------- 1 | import type { NitroOptions } from "nitro/types"; 2 | import type { Preset } from "unenv"; 3 | 4 | export const common: Preset = { 5 | meta: { 6 | name: "nitro-common", 7 | url: import.meta.url, 8 | }, 9 | alias: { 10 | "node-mock-http/_polyfill/events": "node:events", 11 | "node-mock-http/_polyfill/buffer": "node:buffer", 12 | "buffer/": "node:buffer", 13 | "buffer/index": "node:buffer", 14 | "buffer/index.js": "node:buffer", 15 | "string_decoder/": "node:string_decoder", 16 | "process/": "node:process", 17 | }, 18 | }; 19 | 20 | export const nodeless: Preset = { 21 | meta: { 22 | name: "nitro-nodeless", 23 | url: import.meta.url, 24 | }, 25 | inject: { 26 | global: "unenv/polyfill/globalthis", 27 | process: "node:process", 28 | Buffer: ["node:buffer", "Buffer"], 29 | clearImmediate: ["node:timers", "clearImmediate"], 30 | setImmediate: ["node:timers", "setImmediate"], 31 | performance: "unenv/polyfill/performance", 32 | PerformanceObserver: ["node:perf_hooks", "PerformanceObserver"], 33 | BroadcastChannel: ["node:worker_threads", "BroadcastChannel"], 34 | }, 35 | polyfill: [ 36 | "unenv/polyfill/globalthis-global", 37 | "unenv/polyfill/process", 38 | "unenv/polyfill/buffer", 39 | "unenv/polyfill/timers", 40 | ], 41 | }; 42 | 43 | export async function resolveUnenv(options: NitroOptions) { 44 | options.unenv ??= []; 45 | if (!Array.isArray(options.unenv)) { 46 | options.unenv = [options.unenv]; 47 | } 48 | options.unenv = options.unenv.filter(Boolean); 49 | if (!options.node) { 50 | options.unenv.unshift(nodeless); 51 | } 52 | options.unenv.unshift(common); 53 | } 54 | -------------------------------------------------------------------------------- /src/config/resolvers/url.ts: -------------------------------------------------------------------------------- 1 | import type { NitroOptions } from "nitro/types"; 2 | import { withLeadingSlash, withTrailingSlash } from "ufo"; 3 | 4 | export async function resolveURLOptions(options: NitroOptions) { 5 | options.baseURL = withLeadingSlash(withTrailingSlash(options.baseURL)); 6 | } 7 | -------------------------------------------------------------------------------- /src/config/update.ts: -------------------------------------------------------------------------------- 1 | import consola from "consola"; 2 | import type { Nitro, NitroDynamicConfig } from "nitro/types"; 3 | import { normalizeRouteRules } from "./resolvers/route-rules"; 4 | import { normalizeRuntimeConfig } from "./resolvers/runtime-config"; 5 | 6 | export async function updateNitroConfig( 7 | nitro: Nitro, 8 | config: NitroDynamicConfig 9 | ) { 10 | nitro.options.routeRules = normalizeRouteRules( 11 | config.routeRules ? config : nitro.options 12 | ); 13 | nitro.options.runtimeConfig = normalizeRuntimeConfig( 14 | config.runtimeConfig ? config : nitro.options 15 | ); 16 | await nitro.hooks.callHook("rollup:reload"); 17 | consola.success("Nitro config hot reloaded!"); 18 | } 19 | -------------------------------------------------------------------------------- /src/dev/proxy.ts: -------------------------------------------------------------------------------- 1 | import type { TLSSocket } from "node:tls"; 2 | import type { ProxyServerOptions, ProxyServer } from "httpxy"; 3 | import { createError, type H3Event } from "h3"; 4 | 5 | import { createProxyServer } from "httpxy"; 6 | 7 | export type HTTPProxy = { 8 | proxy: ProxyServer; 9 | handleEvent: (event: H3Event, opts?: ProxyServerOptions) => Promise; 10 | }; 11 | 12 | export function createHTTPProxy(defaults: ProxyServerOptions = {}): HTTPProxy { 13 | const proxy = createProxyServer(defaults); 14 | 15 | proxy.on("proxyReq", (proxyReq, req) => { 16 | if (!proxyReq.hasHeader("x-forwarded-for")) { 17 | const address = req.socket.remoteAddress; 18 | if (address) { 19 | proxyReq.appendHeader("x-forwarded-for", address); 20 | } 21 | } 22 | if (!proxyReq.hasHeader("x-forwarded-port")) { 23 | const localPort = req?.socket?.localPort; 24 | if (localPort) { 25 | proxyReq.setHeader("x-forwarded-port", req.socket.localPort); 26 | } 27 | } 28 | if (!proxyReq.hasHeader("x-forwarded-Proto")) { 29 | const encrypted = (req?.connection as TLSSocket)?.encrypted; 30 | proxyReq.setHeader("x-forwarded-proto", encrypted ? "https" : "http"); 31 | } 32 | }); 33 | 34 | const handleEvent = async (event: H3Event, opts: ProxyServerOptions = {}) => { 35 | try { 36 | event._handled = true; 37 | await proxy.web(event.node.req, event.node.res, opts); 38 | } catch (error: any) { 39 | try { 40 | event.node.res.setHeader("refresh", "3"); 41 | } catch { 42 | // Ignore 43 | } 44 | throw createError({ 45 | statusCode: 503, 46 | message: "Dev server is unavailable.", 47 | cause: error, 48 | }); 49 | } 50 | }; 51 | 52 | return { 53 | proxy, 54 | handleEvent, 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // Core 2 | export { createNitro } from "./nitro"; 3 | 4 | // Prerender 5 | export { prerender } from "./prerender/prerender"; 6 | 7 | // Dev server 8 | export { createDevServer } from "./dev/server"; 9 | 10 | // Config loader 11 | export { loadOptions } from "./config/loader"; 12 | 13 | // Tasks API 14 | export { runTask, listTasks } from "./task"; 15 | 16 | // Build 17 | export { build } from "./build/build"; 18 | export { copyPublicAssets } from "./build/assets"; 19 | export { prepare } from "./build/prepare"; 20 | export { writeTypes } from "./build/types"; 21 | -------------------------------------------------------------------------------- /src/module.ts: -------------------------------------------------------------------------------- 1 | import { createJiti } from "jiti"; 2 | import type { Nitro, NitroModule, NitroModuleInput } from "nitro/types"; 3 | 4 | export async function installModules(nitro: Nitro) { 5 | const _modules = [...(nitro.options.modules || [])]; 6 | const modules = await Promise.all( 7 | _modules.map((mod) => _resolveNitroModule(mod, nitro.options)) 8 | ); 9 | const _installedURLs = new Set(); 10 | for (const mod of modules) { 11 | if (mod._url) { 12 | if (_installedURLs.has(mod._url)) { 13 | continue; 14 | } 15 | _installedURLs.add(mod._url); 16 | } 17 | await mod.setup(nitro); 18 | } 19 | } 20 | 21 | async function _resolveNitroModule( 22 | mod: NitroModuleInput, 23 | nitroOptions: Nitro["options"] 24 | ): Promise { 25 | let _url: string | undefined; 26 | 27 | if (typeof mod === "string") { 28 | // @ts-ignore 29 | globalThis.defineNitroModule = 30 | // @ts-ignore 31 | globalThis.defineNitroModule || ((mod) => mod); 32 | 33 | const jiti = createJiti(nitroOptions.rootDir, { 34 | alias: nitroOptions.alias, 35 | }); 36 | const _modPath = jiti.esmResolve(mod); 37 | _url = _modPath; 38 | mod = (await jiti.import(_modPath, { default: true })) as NitroModule; 39 | } 40 | 41 | if (typeof mod === "function") { 42 | mod = { setup: mod }; 43 | } 44 | 45 | if (!mod.setup) { 46 | // TODO: Warn? 47 | mod.setup = () => {}; 48 | } 49 | 50 | return { 51 | _url, 52 | ...mod, 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /src/presets/_all.gen.ts: -------------------------------------------------------------------------------- 1 | // Auto-generated using gen-presets script 2 | 3 | import _nitro from "./_nitro/preset"; 4 | import _static from "./_static/preset"; 5 | import _alwaysdata from "./alwaysdata/preset"; 6 | import _awsAmplify from "./aws-amplify/preset"; 7 | import _awsLambda from "./aws-lambda/preset"; 8 | import _azure from "./azure/preset"; 9 | import _bun from "./bun/preset"; 10 | import _cleavr from "./cleavr/preset"; 11 | import _cloudflare from "./cloudflare/preset"; 12 | import _deno from "./deno/preset"; 13 | import _digitalocean from "./digitalocean/preset"; 14 | import _edgio from "./edgio/preset"; 15 | import _firebase from "./firebase/preset"; 16 | import _flightcontrol from "./flightcontrol/preset"; 17 | import _genezio from "./genezio/preset"; 18 | import _heroku from "./heroku/preset"; 19 | import _iis from "./iis/preset"; 20 | import _koyeb from "./koyeb/preset"; 21 | import _netlify from "./netlify/preset"; 22 | import _node from "./node/preset"; 23 | import _platformSh from "./platform.sh/preset"; 24 | import _renderCom from "./render.com/preset"; 25 | import _stormkit from "./stormkit/preset"; 26 | import _vercel from "./vercel/preset"; 27 | import _winterjs from "./winterjs/preset"; 28 | import _zeabur from "./zeabur/preset"; 29 | import _zerops from "./zerops/preset"; 30 | 31 | export default [ 32 | ..._nitro, 33 | ..._static, 34 | ..._alwaysdata, 35 | ..._awsAmplify, 36 | ..._awsLambda, 37 | ..._azure, 38 | ..._bun, 39 | ..._cleavr, 40 | ..._cloudflare, 41 | ..._deno, 42 | ..._digitalocean, 43 | ..._edgio, 44 | ..._firebase, 45 | ..._flightcontrol, 46 | ..._genezio, 47 | ..._heroku, 48 | ..._iis, 49 | ..._koyeb, 50 | ..._netlify, 51 | ..._node, 52 | ..._platformSh, 53 | ..._renderCom, 54 | ..._stormkit, 55 | ..._vercel, 56 | ..._winterjs, 57 | ..._zeabur, 58 | ..._zerops, 59 | ] as const; 60 | -------------------------------------------------------------------------------- /src/presets/_nitro/base-worker.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroPreset } from "../_utils/preset"; 2 | 3 | const baseWorker = defineNitroPreset( 4 | { 5 | entry: null as any, // Abstract 6 | node: false, 7 | minify: true, 8 | noExternals: true, 9 | rollupConfig: { 10 | output: { 11 | format: "iife", 12 | generatedCode: { 13 | symbols: true, 14 | }, 15 | }, 16 | }, 17 | inlineDynamicImports: true, // iffe does not support code-splitting 18 | }, 19 | { 20 | name: "base-worker" as const, 21 | url: import.meta.url, 22 | } 23 | ); 24 | 25 | export default [baseWorker] as const; 26 | -------------------------------------------------------------------------------- /src/presets/_nitro/nitro-dev.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroPreset } from "../_utils/preset"; 2 | 3 | const nitroDev = defineNitroPreset( 4 | { 5 | entry: "./runtime/nitro-dev", 6 | output: { 7 | serverDir: "{{ buildDir }}/dev", 8 | }, 9 | externals: { trace: false }, 10 | inlineDynamicImports: true, // externals plugin limitation 11 | sourceMap: true, 12 | }, 13 | { 14 | name: "nitro-dev" as const, 15 | url: import.meta.url, 16 | } 17 | ); 18 | 19 | export default [nitroDev] as const; 20 | -------------------------------------------------------------------------------- /src/presets/_nitro/nitro-prerender.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroPreset } from "../_utils/preset"; 2 | 3 | const nitroPrerender = defineNitroPreset( 4 | { 5 | entry: "./runtime/nitro-prerenderer", 6 | serveStatic: true, 7 | output: { 8 | serverDir: "{{ buildDir }}/prerender", 9 | }, 10 | externals: { trace: false }, 11 | }, 12 | { 13 | name: "nitro-prerender" as const, 14 | url: import.meta.url, 15 | } 16 | ); 17 | 18 | export default [nitroPrerender] as const; 19 | -------------------------------------------------------------------------------- /src/presets/_nitro/preset.ts: -------------------------------------------------------------------------------- 1 | import worker from "./base-worker"; 2 | import dev from "./nitro-dev"; 3 | import prerender from "./nitro-prerender"; 4 | 5 | export default [...worker, ...dev, ...prerender] as const; 6 | -------------------------------------------------------------------------------- /src/presets/_nitro/runtime/nitro-prerenderer.ts: -------------------------------------------------------------------------------- 1 | import "#nitro-internal-pollyfills"; 2 | import consola from "consola"; 3 | import { getRequestHeader, getRequestURL, H3Error, isEvent } from "h3"; 4 | import { useNitroApp } from "nitro/runtime"; 5 | import { trapUnhandledNodeErrors } from "nitro/runtime/internal"; 6 | 7 | const nitroApp = useNitroApp(); 8 | 9 | nitroApp.hooks.hook("error", (error, context) => { 10 | if ( 11 | isEvent(context.event) && 12 | !(error as H3Error).unhandled && 13 | (error as H3Error).statusCode >= 500 && 14 | getRequestHeader(context.event, "x-nitro-prerender") 15 | ) { 16 | const url = getRequestURL(context.event).href; 17 | consola.error( 18 | `[prerender error]`, 19 | `[${context.event.method}]`, 20 | `[${url}]`, 21 | error 22 | ); 23 | } 24 | }); 25 | 26 | export const localFetch = nitroApp.localFetch; 27 | export const closePrerenderer = () => nitroApp.hooks.callHook("close"); 28 | 29 | // Trap unhandled errors 30 | trapUnhandledNodeErrors(); 31 | -------------------------------------------------------------------------------- /src/presets/_nitro/runtime/service-worker.ts: -------------------------------------------------------------------------------- 1 | import "#nitro-internal-pollyfills"; 2 | import { useNitroApp } from "nitro/runtime"; 3 | import { isPublicAssetURL } from "#nitro-internal-virtual/public-assets"; 4 | 5 | const nitroApp = useNitroApp(); 6 | 7 | // @ts-expect-error 8 | addEventListener("fetch", (event: FetchEvent) => { 9 | const url = new URL(event.request.url); 10 | if (isPublicAssetURL(url.pathname) || url.pathname.includes("/_server/")) { 11 | return; 12 | } 13 | 14 | event.respondWith(handleEvent(url, event)); 15 | }); 16 | 17 | async function handleEvent(url: URL, event: FetchEvent) { 18 | let body; 19 | if (event.request.body) { 20 | body = await event.request.arrayBuffer(); 21 | } 22 | 23 | return nitroApp.localFetch(url.pathname + url.search, { 24 | host: url.hostname, 25 | protocol: url.protocol, 26 | headers: event.request.headers, 27 | method: event.request.method, 28 | redirect: event.request.redirect, 29 | body, 30 | }); 31 | } 32 | 33 | declare const self: ServiceWorkerGlobalScope; 34 | 35 | self.addEventListener("install", () => { 36 | self.skipWaiting(); 37 | }); 38 | 39 | self.addEventListener("activate", (event) => { 40 | event.waitUntil(self.clients.claim()); 41 | }); 42 | -------------------------------------------------------------------------------- /src/presets/_static/preset.ts: -------------------------------------------------------------------------------- 1 | import fsp from "node:fs/promises"; 2 | import { defineNitroPreset } from "../_utils/preset"; 3 | import { join } from "pathe"; 4 | 5 | const _static = defineNitroPreset( 6 | { 7 | static: true, 8 | output: { 9 | dir: "{{ rootDir }}/.output", 10 | publicDir: "{{ output.dir }}/public", 11 | }, 12 | prerender: { 13 | crawlLinks: true, 14 | }, 15 | commands: { 16 | preview: "npx serve ./public", 17 | }, 18 | }, 19 | { 20 | name: "static" as const, 21 | static: true, 22 | url: import.meta.url, 23 | } 24 | ); 25 | 26 | const githubPages = defineNitroPreset( 27 | { 28 | extends: "static", 29 | commands: { 30 | deploy: "npx gh-pages --dotfiles -d ./public", 31 | }, 32 | prerender: { 33 | routes: [ 34 | "/", 35 | // https://docs.github.com/en/pages/getting-started-with-github-pages/creating-a-custom-404-page-for-your-github-pages-site 36 | "/404.html", 37 | ], 38 | }, 39 | hooks: { 40 | async compiled(nitro) { 41 | await fsp.writeFile( 42 | join(nitro.options.output.publicDir, ".nojekyll"), 43 | "" 44 | ); 45 | }, 46 | }, 47 | }, 48 | { 49 | name: "github-pages" as const, 50 | static: true, 51 | url: import.meta.url, 52 | } 53 | ); 54 | 55 | const gitlabPages = defineNitroPreset( 56 | { 57 | extends: "static", 58 | prerender: { 59 | routes: [ 60 | "/", 61 | // https://docs.gitlab.com/ee/user/project/pages/introduction.html#custom-error-codes-pages 62 | "/404.html", 63 | ], 64 | }, 65 | }, 66 | { 67 | name: "gitlab-pages" as const, 68 | static: true, 69 | url: import.meta.url, 70 | } 71 | ); 72 | 73 | export default [_static, githubPages, gitlabPages] as const; 74 | -------------------------------------------------------------------------------- /src/presets/_unenv/preset-deno.ts: -------------------------------------------------------------------------------- 1 | import type { Preset } from "unenv"; 2 | import { builtnNodeModules } from "./node-compat/deno"; 3 | 4 | // https://platform-node-compat.deno.dev/ 5 | // https://platform-node-compat.netlify.app/ 6 | 7 | export const unenvDenoPreset: Preset = { 8 | meta: { 9 | name: "nitro-deno", 10 | url: import.meta.url, 11 | }, 12 | external: builtnNodeModules.map((m) => `node:${m}`), 13 | alias: { 14 | // (native) 15 | ...Object.fromEntries( 16 | [...builtnNodeModules, "sys"].flatMap((m) => [ 17 | [m, `node:${m}`], 18 | [`node:${m}`, `node:${m}`], 19 | ]) 20 | ), 21 | }, 22 | inject: { 23 | performance: false, 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /src/presets/_unenv/workerd/console.mjs: -------------------------------------------------------------------------------- 1 | import workerdConsole from "#workerd/node:console"; 2 | 3 | import { 4 | Console, 5 | _ignoreErrors, 6 | _stderr, 7 | _stderrErrorHandler, 8 | _stdout, 9 | _stdoutErrorHandler, 10 | _times, 11 | } from "unenv/node/console"; 12 | 13 | export { 14 | Console, 15 | _ignoreErrors, 16 | _stderr, 17 | _stderrErrorHandler, 18 | _stdout, 19 | _stdoutErrorHandler, 20 | _times, 21 | } from "unenv/node/console"; 22 | 23 | export const { 24 | assert, 25 | clear, 26 | context, 27 | count, 28 | countReset, 29 | createTask, 30 | debug, 31 | dir, 32 | dirxml, 33 | error, 34 | group, 35 | groupCollapsed, 36 | groupEnd, 37 | info, 38 | log, 39 | profile, 40 | profileEnd, 41 | table, 42 | time, 43 | timeEnd, 44 | timeLog, 45 | timeStamp, 46 | trace, 47 | warn, 48 | } = workerdConsole; 49 | 50 | const consolePolyfill = { 51 | Console, 52 | _ignoreErrors, 53 | _stderr, 54 | _stderrErrorHandler, 55 | _stdout, 56 | _stdoutErrorHandler, 57 | _times, 58 | }; 59 | 60 | const consoleModule = /*@__PURE__*/ new Proxy(workerdConsole, { 61 | get(target, prop) { 62 | if (Reflect.has(target, prop)) { 63 | return Reflect.get(target, prop); 64 | } 65 | return Reflect.get(consolePolyfill, prop); 66 | }, 67 | }); 68 | 69 | export default consoleModule; 70 | -------------------------------------------------------------------------------- /src/presets/_unenv/workerd/tls.mjs: -------------------------------------------------------------------------------- 1 | // https://github.com/cloudflare/workerd/blob/main/src/node/tls.ts 2 | 3 | import workerdTLS from "#workerd/node:tls"; 4 | 5 | import { 6 | CLIENT_RENEG_LIMIT, 7 | CLIENT_RENEG_WINDOW, 8 | DEFAULT_CIPHERS, 9 | DEFAULT_ECDH_CURVE, 10 | DEFAULT_MAX_VERSION, 11 | DEFAULT_MIN_VERSION, 12 | Server, 13 | createSecurePair, 14 | createServer, 15 | getCiphers, 16 | rootCertificates, 17 | } from "unenv/node/tls"; 18 | 19 | export { 20 | CLIENT_RENEG_LIMIT, 21 | CLIENT_RENEG_WINDOW, 22 | DEFAULT_CIPHERS, 23 | DEFAULT_ECDH_CURVE, 24 | DEFAULT_MAX_VERSION, 25 | DEFAULT_MIN_VERSION, 26 | Server, 27 | createSecurePair, 28 | createServer, 29 | getCiphers, 30 | rootCertificates, 31 | } from "unenv/node/tls"; 32 | 33 | export const { 34 | TLSSocket, 35 | connect, 36 | SecureContext, 37 | checkServerIdentity, 38 | convertALPNProtocols, 39 | createSecureContext, 40 | } = workerdTLS; 41 | 42 | export default { 43 | // native 44 | TLSSocket, 45 | connect, 46 | // polyfill 47 | CLIENT_RENEG_LIMIT, 48 | CLIENT_RENEG_WINDOW, 49 | DEFAULT_CIPHERS, 50 | DEFAULT_ECDH_CURVE, 51 | DEFAULT_MAX_VERSION, 52 | DEFAULT_MIN_VERSION, 53 | SecureContext, 54 | Server, 55 | checkServerIdentity, 56 | convertALPNProtocols, 57 | createSecureContext, 58 | createSecurePair, 59 | createServer, 60 | getCiphers, 61 | rootCertificates, 62 | }; 63 | -------------------------------------------------------------------------------- /src/presets/_utils/fs.ts: -------------------------------------------------------------------------------- 1 | import fsp from "node:fs/promises"; 2 | import { relative, dirname } from "pathe"; 3 | import consola from "consola"; 4 | import { colors } from "consola/utils"; 5 | 6 | export function prettyPath(p: string, highlight = true) { 7 | p = relative(process.cwd(), p); 8 | return highlight ? colors.cyan(p) : p; 9 | } 10 | 11 | export async function writeFile( 12 | file: string, 13 | contents: Buffer | string, 14 | log = false 15 | ) { 16 | await fsp.mkdir(dirname(file), { recursive: true }); 17 | await fsp.writeFile( 18 | file, 19 | contents, 20 | typeof contents === "string" ? "utf8" : undefined 21 | ); 22 | if (log) { 23 | consola.info("Generated", prettyPath(file)); 24 | } 25 | } 26 | 27 | export async function isDirectory(path: string) { 28 | try { 29 | return (await fsp.stat(path)).isDirectory(); 30 | } catch { 31 | return false; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/presets/_utils/preset.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from "node:url"; 2 | import type { NitroPreset, NitroPresetMeta } from "nitro/types"; 3 | 4 | export function defineNitroPreset< 5 | P extends NitroPreset, 6 | M extends NitroPresetMeta, 7 | >(preset: P, meta?: M): P & { _meta: NitroPresetMeta } { 8 | if ( 9 | meta?.url && 10 | typeof preset !== "function" && 11 | preset.entry && 12 | preset.entry.startsWith(".") 13 | ) { 14 | preset.entry = fileURLToPath(new URL(preset.entry, meta.url)); 15 | } 16 | return { ...preset, _meta: meta } as P & { _meta: M }; 17 | } 18 | -------------------------------------------------------------------------------- /src/presets/alwaysdata/preset.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroPreset } from "../_utils/preset"; 2 | 3 | const alwaysdata = defineNitroPreset( 4 | { 5 | extends: "node-server", 6 | serveStatic: true, 7 | commands: { 8 | deploy: 9 | "rsync -rRt --info=progress2 ./ [account]@ssh-[account].alwaysdata.net:www/my-app", 10 | }, 11 | }, 12 | { 13 | name: "alwaysdata" as const, 14 | url: import.meta.url, 15 | } 16 | ); 17 | 18 | export default [alwaysdata] as const; 19 | -------------------------------------------------------------------------------- /src/presets/aws-amplify/preset.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroPreset } from "../_utils/preset"; 2 | import { writeAmplifyFiles } from "./utils"; 3 | 4 | export type { AWSAmplifyOptions as PresetOptions } from "./types"; 5 | 6 | const awsAmplify = defineNitroPreset( 7 | { 8 | entry: "./runtime/aws-amplify", 9 | serveStatic: true, 10 | output: { 11 | dir: "{{ rootDir }}/.amplify-hosting", 12 | serverDir: "{{ output.dir }}/compute/default", 13 | publicDir: "{{ output.dir }}/static{{ baseURL }}", 14 | }, 15 | commands: { 16 | preview: "node ./compute/default/server.js", 17 | }, 18 | hooks: { 19 | async compiled(nitro) { 20 | await writeAmplifyFiles(nitro); 21 | }, 22 | }, 23 | }, 24 | { 25 | name: "aws-amplify" as const, 26 | stdName: "aws_amplify", 27 | url: import.meta.url, 28 | } 29 | ); 30 | 31 | export default [awsAmplify] as const; 32 | -------------------------------------------------------------------------------- /src/presets/aws-amplify/runtime/aws-amplify.ts: -------------------------------------------------------------------------------- 1 | import "#nitro-internal-pollyfills"; 2 | import { useNitroApp } from "nitro/runtime"; 3 | 4 | import { Server } from "node:http"; 5 | import { toNodeListener } from "h3"; 6 | 7 | const nitroApp = useNitroApp(); 8 | 9 | const server = new Server(toNodeListener(nitroApp.h3App)); 10 | 11 | // @ts-ignore 12 | server.listen(3000, (err) => { 13 | if (err) { 14 | console.error(err); 15 | } else { 16 | console.log(`Listening on http://localhost:3000 (AWS Amplify Hosting)`); 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /src/presets/aws-lambda/preset.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroPreset } from "../_utils/preset"; 2 | export type { AwsLambdaOptions as PresetOptions } from "./types"; 3 | 4 | const awsLambda = defineNitroPreset( 5 | { 6 | entry: "./runtime/aws-lambda", 7 | awsLambda: { 8 | streaming: false, 9 | }, 10 | hooks: { 11 | "rollup:before": (nitro, rollupConfig) => { 12 | if (nitro.options.awsLambda.streaming) { 13 | (rollupConfig.input as string) += "-streaming"; 14 | } 15 | }, 16 | }, 17 | }, 18 | { 19 | name: "aws-lambda" as const, 20 | url: import.meta.url, 21 | } 22 | ); 23 | 24 | export default [awsLambda] as const; 25 | -------------------------------------------------------------------------------- /src/presets/aws-lambda/types.ts: -------------------------------------------------------------------------------- 1 | export interface AwsLambdaOptions { 2 | streaming?: boolean; 3 | } 4 | -------------------------------------------------------------------------------- /src/presets/azure/preset.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroPreset } from "../_utils/preset"; 2 | import type { Nitro } from "nitro/types"; 3 | import { writeSWARoutes } from "./utils"; 4 | 5 | export type { AzureOptions as PresetOptions } from "./types"; 6 | 7 | const azureSWA = defineNitroPreset( 8 | { 9 | entry: "./runtime/azure-swa", 10 | output: { 11 | serverDir: "{{ output.dir }}/server/functions", 12 | publicDir: "{{ output.dir }}/public/{{ baseURL }}", 13 | }, 14 | commands: { 15 | preview: 16 | "npx @azure/static-web-apps-cli start ./public --api-location ./server", 17 | }, 18 | hooks: { 19 | async compiled(ctx: Nitro) { 20 | await writeSWARoutes(ctx); 21 | }, 22 | }, 23 | }, 24 | { 25 | name: "azure-swa" as const, 26 | stdName: "azure_static", 27 | url: import.meta.url, 28 | } 29 | ); 30 | 31 | export default [azureSWA] as const; 32 | -------------------------------------------------------------------------------- /src/presets/azure/runtime/azure-swa.ts: -------------------------------------------------------------------------------- 1 | import "#nitro-internal-pollyfills"; 2 | import { useNitroApp } from "nitro/runtime"; 3 | import { 4 | getAzureParsedCookiesFromHeaders, 5 | normalizeLambdaOutgoingHeaders, 6 | } from "nitro/runtime/internal"; 7 | 8 | import type { HttpRequest, HttpResponse } from "@azure/functions"; 9 | import { parseURL } from "ufo"; 10 | 11 | const nitroApp = useNitroApp(); 12 | 13 | export async function handle(context: { res: HttpResponse }, req: HttpRequest) { 14 | let url: string; 15 | if (req.headers["x-ms-original-url"]) { 16 | // This URL has been proxied as there was no static file matching it. 17 | const parsedURL = parseURL(req.headers["x-ms-original-url"]); 18 | url = parsedURL.pathname + parsedURL.search; 19 | } else { 20 | // Because Azure SWA handles /api/* calls differently they 21 | // never hit the proxy and we have to reconstitute the URL. 22 | url = "/api/" + (req.params.url || ""); 23 | } 24 | 25 | const { body, status, headers } = await nitroApp.localCall({ 26 | url, 27 | headers: req.headers, 28 | method: req.method || undefined, 29 | // https://github.com/Azure/azure-functions-host/issues/293 30 | body: req.rawBody, 31 | }); 32 | 33 | // (v3 - current) https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-node?tabs=typescript%2Cwindows%2Cazure-cli&pivots=nodejs-model-v3#http-response 34 | // (v4) https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-node?tabs=typescript%2Cwindows%2Cazure-cli&pivots=nodejs-model-v4#http-response 35 | context.res = { 36 | status, 37 | cookies: getAzureParsedCookiesFromHeaders(headers), 38 | headers: normalizeLambdaOutgoingHeaders(headers, true), 39 | body, 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /src/presets/azure/types.ts: -------------------------------------------------------------------------------- 1 | export interface AzureOptions { 2 | config?: { 3 | platform?: { 4 | apiRuntime?: string; 5 | [key: string]: unknown; 6 | }; 7 | navigationFallback?: { 8 | rewrite?: string; 9 | [key: string]: unknown; 10 | }; 11 | [key: string]: unknown; 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /src/presets/bun/preset.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroPreset } from "../_utils/preset"; 2 | 3 | const bun = defineNitroPreset( 4 | { 5 | entry: "./runtime/bun", 6 | serveStatic: true, 7 | // https://bun.sh/docs/runtime/modules#resolution 8 | exportConditions: ["bun", "node", "import", "default"], 9 | commands: { 10 | preview: "bun run ./server/index.mjs", 11 | }, 12 | }, 13 | { 14 | name: "bun" as const, 15 | url: import.meta.url, 16 | } 17 | ); 18 | 19 | export default [bun] as const; 20 | -------------------------------------------------------------------------------- /src/presets/bun/runtime/bun.ts: -------------------------------------------------------------------------------- 1 | import "#nitro-internal-pollyfills"; 2 | import { useNitroApp } from "nitro/runtime"; 3 | import { startScheduleRunner } from "nitro/runtime/internal"; 4 | 5 | import wsAdapter from "crossws/adapters/bun"; 6 | 7 | const nitroApp = useNitroApp(); 8 | 9 | const ws = import.meta._websocket 10 | ? wsAdapter(nitroApp.h3App.websocket) 11 | : undefined; 12 | 13 | // @ts-expect-error 14 | const server = Bun.serve({ 15 | port: process.env.NITRO_PORT || process.env.PORT || 3000, 16 | websocket: import.meta._websocket ? ws!.websocket : (undefined as any), 17 | async fetch(req: Request, server: any) { 18 | // https://crossws.unjs.io/adapters/bun 19 | if (import.meta._websocket && req.headers.get("upgrade") === "websocket") { 20 | return ws!.handleUpgrade(req, server); 21 | } 22 | 23 | const url = new URL(req.url); 24 | 25 | let body; 26 | if (req.body) { 27 | body = await req.arrayBuffer(); 28 | } 29 | 30 | return nitroApp.localFetch(url.pathname + url.search, { 31 | host: url.hostname, 32 | protocol: url.protocol, 33 | headers: req.headers, 34 | method: req.method, 35 | redirect: req.redirect, 36 | body, 37 | }); 38 | }, 39 | }); 40 | 41 | console.log(`Listening on http://localhost:${server.port}...`); 42 | 43 | // Scheduled tasks 44 | if (import.meta._tasks) { 45 | startScheduleRunner(); 46 | } 47 | -------------------------------------------------------------------------------- /src/presets/cleavr/preset.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroPreset } from "../_utils/preset"; 2 | 3 | const cleavr = defineNitroPreset( 4 | { 5 | extends: "node-server", 6 | serveStatic: true, 7 | }, 8 | { 9 | name: "cleavr" as const, 10 | stdName: "cleavr", 11 | url: import.meta.url, 12 | } 13 | ); 14 | 15 | export default [cleavr] as const; 16 | -------------------------------------------------------------------------------- /src/presets/cloudflare/runtime/cloudflare-module.ts: -------------------------------------------------------------------------------- 1 | import "#nitro-internal-pollyfills"; 2 | import type { fetch } from "@cloudflare/workers-types"; 3 | import wsAdapter from "crossws/adapters/cloudflare"; 4 | import { useNitroApp } from "nitro/runtime"; 5 | import { isPublicAssetURL } from "#nitro-internal-virtual/public-assets"; 6 | import { createHandler } from "./_module-handler"; 7 | 8 | const nitroApp = useNitroApp(); 9 | 10 | const ws = import.meta._websocket 11 | ? wsAdapter(nitroApp.h3App.websocket) 12 | : undefined; 13 | 14 | interface Env { 15 | ASSETS?: { fetch: typeof fetch }; 16 | } 17 | 18 | export default createHandler({ 19 | fetch(request, env, context, url) { 20 | // Static assets fallback (optional binding) 21 | if (env.ASSETS && isPublicAssetURL(url.pathname)) { 22 | return env.ASSETS.fetch(request); 23 | } 24 | 25 | // Websocket upgrade 26 | // https://crossws.unjs.io/adapters/cloudflare 27 | if ( 28 | import.meta._websocket && 29 | request.headers.get("upgrade") === "websocket" 30 | ) { 31 | return ws!.handleUpgrade(request as any, env, context); 32 | } 33 | }, 34 | }); 35 | -------------------------------------------------------------------------------- /src/presets/cloudflare/wrangler/_utils.ts: -------------------------------------------------------------------------------- 1 | // Extracted from @types/yargs 2 | type PascalCase = string extends S 3 | ? string 4 | : S extends `${infer T}-${infer U}` 5 | ? `${Capitalize}${PascalCase}` 6 | : Capitalize; 7 | 8 | type CamelCase = string extends S 9 | ? string 10 | : S extends `${infer T}-${infer U}` 11 | ? `${T}${PascalCase}` 12 | : S; 13 | 14 | export type CamelCaseKey = K extends string 15 | ? Exclude, ""> 16 | : K; 17 | -------------------------------------------------------------------------------- /src/presets/deno/runtime/deno-deploy.ts: -------------------------------------------------------------------------------- 1 | import "#nitro-internal-pollyfills"; 2 | import { useNitroApp } from "nitro/runtime"; 3 | 4 | import type { Deno as _Deno } from "@deno/types"; 5 | import wsAdapter from "crossws/adapters/deno"; 6 | 7 | const nitroApp = useNitroApp(); 8 | 9 | const ws = import.meta._websocket 10 | ? wsAdapter(nitroApp.h3App.websocket) 11 | : undefined; 12 | 13 | Deno.serve((request: Request, info: _Deno.ServeHandlerInfo) => { 14 | // https://crossws.unjs.io/adapters/deno 15 | if ( 16 | import.meta._websocket && 17 | request.headers.get("upgrade") === "websocket" 18 | ) { 19 | return ws!.handleUpgrade(request, info); 20 | } 21 | return handleRequest(request, info); 22 | }); 23 | 24 | async function handleRequest(request: Request, info: _Deno.ServeHandlerInfo) { 25 | const url = new URL(request.url); 26 | 27 | const headers = new Headers(request.headers); 28 | 29 | // Add client IP address to headers 30 | // (rightmost is most trustable) 31 | headers.append("x-forwarded-for", info.remoteAddr.hostname); 32 | 33 | // There is currently no way to know if the request was made over HTTP or HTTPS 34 | // Deno deploy force redirects to HTTPS so we assume HTTPS by default 35 | if (!headers.has("x-forwarded-proto")) { 36 | headers.set("x-forwarded-proto", "https"); 37 | } 38 | 39 | // https://deno.land/api?s=Body 40 | let body; 41 | if (request.body) { 42 | body = await request.arrayBuffer(); 43 | } 44 | 45 | return nitroApp.localFetch(url.pathname + url.search, { 46 | host: url.hostname, 47 | protocol: url.protocol, 48 | headers, 49 | method: request.method, 50 | redirect: request.redirect, 51 | body, 52 | }); 53 | } 54 | -------------------------------------------------------------------------------- /src/presets/digitalocean/preset.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroPreset } from "../_utils/preset"; 2 | 3 | const digitalOcean = defineNitroPreset( 4 | { 5 | extends: "node-server", 6 | serveStatic: true, 7 | }, 8 | { 9 | name: "digital-ocean" as const, 10 | url: import.meta.url, 11 | } 12 | ); 13 | 14 | export default [digitalOcean] as const; 15 | -------------------------------------------------------------------------------- /src/presets/flightcontrol/preset.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroPreset } from "../_utils/preset"; 2 | 3 | const flightControl = defineNitroPreset( 4 | { 5 | extends: "node-server", 6 | serveStatic: true, 7 | }, 8 | { 9 | name: "flight-control" as const, 10 | url: import.meta.url, 11 | } 12 | ); 13 | 14 | export default [flightControl] as const; 15 | -------------------------------------------------------------------------------- /src/presets/genezio/preset.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroPreset } from "../_utils/preset"; 2 | 3 | const genezio = defineNitroPreset( 4 | { 5 | extends: "aws_lambda", 6 | }, 7 | { 8 | name: "genezio" as const, 9 | url: import.meta.url, 10 | } 11 | ); 12 | export default [genezio] as const; 13 | -------------------------------------------------------------------------------- /src/presets/heroku/preset.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroPreset } from "../_utils/preset"; 2 | 3 | const heroku = defineNitroPreset( 4 | { 5 | extends: "node-server", 6 | serveStatic: true, 7 | }, 8 | { 9 | name: "heroku" as const, 10 | url: import.meta.url, 11 | } 12 | ); 13 | 14 | export default [heroku] as const; 15 | -------------------------------------------------------------------------------- /src/presets/iis/preset.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroPreset } from "../_utils/preset"; 2 | import type { Nitro } from "nitro/types"; 3 | import { writeIISFiles, writeIISNodeFiles } from "./utils"; 4 | 5 | const iisHandler = defineNitroPreset( 6 | { 7 | extends: "node-server", 8 | serveStatic: true, 9 | hooks: { 10 | async compiled(nitro: Nitro) { 11 | await writeIISFiles(nitro); 12 | }, 13 | }, 14 | }, 15 | { 16 | name: "iis-handler" as const, 17 | url: import.meta.url, 18 | } 19 | ); 20 | 21 | const iisNode = defineNitroPreset( 22 | { 23 | extends: "node-server", 24 | serveStatic: true, 25 | hooks: { 26 | async compiled(nitro: Nitro) { 27 | await writeIISNodeFiles(nitro); 28 | }, 29 | }, 30 | }, 31 | { 32 | name: "iis-node" as const, 33 | url: import.meta.url, 34 | } 35 | ); 36 | 37 | export default [iisHandler, iisNode] as const; 38 | -------------------------------------------------------------------------------- /src/presets/index.mjs: -------------------------------------------------------------------------------- 1 | export { resolvePreset } from "./_resolve"; 2 | -------------------------------------------------------------------------------- /src/presets/index.ts: -------------------------------------------------------------------------------- 1 | export { resolvePreset } from "./_resolve"; 2 | 3 | export type { PresetOptions, PresetName, PresetNameInput } from "./_types.gen"; 4 | -------------------------------------------------------------------------------- /src/presets/koyeb/preset.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroPreset } from "../_utils/preset"; 2 | 3 | const koyeb = defineNitroPreset( 4 | { 5 | extends: "node-server", 6 | serveStatic: true, 7 | }, 8 | { 9 | name: "koyeb" as const, 10 | url: import.meta.url, 11 | } 12 | ); 13 | 14 | export default [koyeb] as const; 15 | -------------------------------------------------------------------------------- /src/presets/netlify/runtime/netlify-edge.ts: -------------------------------------------------------------------------------- 1 | import "#nitro-internal-pollyfills"; 2 | import { useNitroApp } from "nitro/runtime"; 3 | import { isPublicAssetURL } from "#nitro-internal-virtual/public-assets"; 4 | import type { Context } from "@netlify/edge-functions"; 5 | 6 | const nitroApp = useNitroApp(); 7 | 8 | // https://docs.netlify.com/edge-functions/api/ 9 | export default async function netlifyEdge(request: Request, _context: Context) { 10 | const url = new URL(request.url); 11 | 12 | if (isPublicAssetURL(url.pathname)) { 13 | return; 14 | } 15 | 16 | if (!request.headers.has("x-forwarded-proto") && url.protocol === "https:") { 17 | request.headers.set("x-forwarded-proto", "https"); 18 | } 19 | 20 | let body; 21 | if (request.body) { 22 | body = await request.arrayBuffer(); 23 | } 24 | 25 | return nitroApp.localFetch(url.pathname + url.search, { 26 | host: url.hostname, 27 | protocol: url.protocol, 28 | headers: request.headers, 29 | method: request.method, 30 | redirect: request.redirect, 31 | body, 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /src/presets/netlify/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Netlify options 3 | */ 4 | export interface NetlifyOptions { 5 | images?: { 6 | /** 7 | * Permitted remote image sources. Array of regex strings. 8 | * @see https://docs.netlify.com/image-cdn/overview/#remote-path 9 | */ 10 | remote_images?: string[]; 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /src/presets/node/preset.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroPreset } from "../_utils/preset"; 2 | import { normalize } from "pathe"; 3 | import { resolveModulePath } from "exsolve"; 4 | 5 | const nodeServer = defineNitroPreset( 6 | { 7 | entry: "./runtime/node-server", 8 | serveStatic: true, 9 | commands: { 10 | preview: "node ./server/index.mjs", 11 | }, 12 | }, 13 | { 14 | name: "node-server" as const, 15 | url: import.meta.url, 16 | } 17 | ); 18 | 19 | const nodeMiddleware = defineNitroPreset( 20 | { 21 | entry: "./runtime/node-middleware", 22 | }, 23 | { 24 | name: "node-middleware" as const, 25 | url: import.meta.url, 26 | } 27 | ); 28 | 29 | const nodeCluster = defineNitroPreset( 30 | { 31 | extends: "node-server", 32 | serveStatic: true, 33 | entry: "./runtime/node-cluster", 34 | hooks: { 35 | "rollup:before"(_nitro, rollupConfig) { 36 | const manualChunks = rollupConfig.output?.manualChunks; 37 | if (manualChunks && typeof manualChunks === "function") { 38 | const serverEntry = resolveModulePath("./runtime/node-server", { 39 | from: import.meta.url, 40 | extensions: [".mjs", ".ts"], 41 | }); 42 | rollupConfig.output.manualChunks = (id, meta) => { 43 | if (id.includes("node-server") && normalize(id) === serverEntry) { 44 | return "nitro/node-worker"; 45 | } 46 | return manualChunks(id, meta); 47 | }; 48 | } 49 | }, 50 | }, 51 | }, 52 | { 53 | name: "node-cluster" as const, 54 | url: import.meta.url, 55 | } 56 | ); 57 | 58 | export default [nodeServer, nodeCluster, nodeMiddleware] as const; 59 | -------------------------------------------------------------------------------- /src/presets/node/runtime/cli.ts: -------------------------------------------------------------------------------- 1 | import "#nitro-internal-pollyfills"; 2 | import { useNitroApp } from "nitro/runtime"; 3 | import { normalize } from "pathe"; 4 | 5 | const nitroApp = useNitroApp(); 6 | 7 | async function cli() { 8 | const url = process.argv[2] || "/"; 9 | const debug = (label: string, ...args: any[]) => 10 | console.debug(`> ${label}:`, ...args); 11 | const r = await nitroApp.localCall({ url }); 12 | 13 | debug("URL", url); 14 | debug("StatusCode", r.status); 15 | debug("StatusMessage", r.statusText); 16 | // @ts-ignore 17 | for (const header of Object.entries(r.headers)) { 18 | debug(header[0], header[1]); 19 | } 20 | console.log("\n", r.body?.toString()); 21 | } 22 | 23 | if (process.argv.some((arg) => import.meta.url.includes(normalize(arg)))) { 24 | // eslint-disable-next-line unicorn/prefer-top-level-await 25 | cli().catch((error) => { 26 | console.error(error); 27 | // eslint-disable-next-line unicorn/no-process-exit 28 | process.exit(1); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /src/presets/node/runtime/node-middleware.ts: -------------------------------------------------------------------------------- 1 | import "#nitro-internal-pollyfills"; 2 | import { toNodeListener } from "h3"; 3 | import { useNitroApp } from "nitro/runtime"; 4 | import { 5 | startScheduleRunner, 6 | trapUnhandledNodeErrors, 7 | } from "nitro/runtime/internal"; 8 | 9 | const nitroApp = useNitroApp(); 10 | 11 | export const middleware = toNodeListener(nitroApp.h3App); 12 | 13 | /** @experimental */ 14 | export const websocket = import.meta._websocket 15 | ? nitroApp.h3App.websocket 16 | : undefined; 17 | 18 | // Trap unhandled errors 19 | trapUnhandledNodeErrors(); 20 | 21 | // Scheduled tasks 22 | if (import.meta._tasks) { 23 | startScheduleRunner(); 24 | } 25 | -------------------------------------------------------------------------------- /src/presets/platform.sh/preset.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroPreset } from "../_utils/preset"; 2 | 3 | const platformSh = defineNitroPreset( 4 | { 5 | extends: "node-server", 6 | serveStatic: true, 7 | }, 8 | { 9 | name: "platform-sh" as const, 10 | url: import.meta.url, 11 | } 12 | ); 13 | 14 | export default [platformSh] as const; 15 | -------------------------------------------------------------------------------- /src/presets/render.com/preset.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroPreset } from "../_utils/preset"; 2 | 3 | const renderCom = defineNitroPreset( 4 | { 5 | extends: "node-server", 6 | serveStatic: true, 7 | }, 8 | { 9 | name: "render-com" as const, 10 | url: import.meta.url, 11 | } 12 | ); 13 | 14 | export default [renderCom] as const; 15 | -------------------------------------------------------------------------------- /src/presets/stormkit/preset.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroPreset } from "../_utils/preset"; 2 | 3 | const stormkit = defineNitroPreset( 4 | { 5 | entry: "./runtime/stormkit", 6 | output: { 7 | dir: "{{ rootDir }}/.stormkit", 8 | publicDir: "{{ rootDir }}/.stormkit/public/{{ baseURL }}", 9 | }, 10 | }, 11 | { 12 | name: "stormkit" as const, 13 | stdName: "stormkit", 14 | url: import.meta.url, 15 | } 16 | ); 17 | 18 | export default [stormkit] as const; 19 | -------------------------------------------------------------------------------- /src/presets/stormkit/runtime/stormkit.ts: -------------------------------------------------------------------------------- 1 | import "#nitro-internal-pollyfills"; 2 | import { useNitroApp } from "nitro/runtime"; 3 | import { normalizeLambdaOutgoingBody } from "nitro/runtime/internal"; 4 | 5 | import type { Handler } from "aws-lambda"; 6 | 7 | type StormkitEvent = { 8 | url: string; // e.g. /my/path, /my/path?with=query 9 | path: string; 10 | method: string; 11 | body?: string; 12 | query?: Record>; 13 | headers?: Record; 14 | rawHeaders?: Array; 15 | }; 16 | 17 | type StormkitResponse = { 18 | headers?: Record; 19 | body?: string; 20 | buffer?: string; 21 | statusCode: number; 22 | errorMessage?: string; 23 | errorStack?: string; 24 | }; 25 | 26 | const nitroApp = useNitroApp(); 27 | 28 | export const handler: Handler = 29 | async function (event, context) { 30 | const response = await nitroApp.localCall({ 31 | event, 32 | url: event.url, 33 | context, 34 | headers: event.headers, 35 | method: event.method || "GET", 36 | query: event.query, 37 | body: event.body, 38 | }); 39 | 40 | const awsBody = await normalizeLambdaOutgoingBody( 41 | response.body, 42 | response.headers 43 | ); 44 | 45 | return { 46 | statusCode: response.status, 47 | headers: normalizeOutgoingHeaders(response.headers), 48 | [awsBody.type === "text" ? "body" : "buffer"]: awsBody.body, 49 | }; 50 | }; 51 | 52 | function normalizeOutgoingHeaders( 53 | headers: Record 54 | ): Record { 55 | return Object.fromEntries( 56 | Object.entries(headers).map(([k, v]) => [ 57 | k, 58 | Array.isArray(v) ? v.join(",") : String(v), 59 | ]) 60 | ); 61 | } 62 | -------------------------------------------------------------------------------- /src/presets/vercel/preset.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroPreset } from "../_utils/preset"; 2 | import type { Nitro } from "nitro/types"; 3 | import { 4 | deprecateSWR, 5 | generateFunctionFiles, 6 | generateStaticFiles, 7 | } from "./utils"; 8 | 9 | export type { VercelOptions as PresetOptions } from "./types"; 10 | 11 | // https://vercel.com/docs/build-output-api/v3 12 | 13 | const vercel = defineNitroPreset( 14 | { 15 | entry: "./runtime/vercel", 16 | output: { 17 | dir: "{{ rootDir }}/.vercel/output", 18 | serverDir: "{{ output.dir }}/functions/__nitro.func", 19 | publicDir: "{{ output.dir }}/static/{{ baseURL }}", 20 | }, 21 | commands: { 22 | deploy: "", 23 | preview: "", 24 | }, 25 | hooks: { 26 | "rollup:before": (nitro: Nitro) => { 27 | deprecateSWR(nitro); 28 | }, 29 | async compiled(nitro: Nitro) { 30 | await generateFunctionFiles(nitro); 31 | }, 32 | }, 33 | }, 34 | { 35 | name: "vercel" as const, 36 | stdName: "vercel", 37 | url: import.meta.url, 38 | } 39 | ); 40 | 41 | const vercelStatic = defineNitroPreset( 42 | { 43 | extends: "static", 44 | output: { 45 | dir: "{{ rootDir }}/.vercel/output", 46 | publicDir: "{{ output.dir }}/static/{{ baseURL }}", 47 | }, 48 | commands: { 49 | preview: "npx serve ./static", 50 | }, 51 | hooks: { 52 | "rollup:before": (nitro: Nitro) => { 53 | deprecateSWR(nitro); 54 | }, 55 | async compiled(nitro: Nitro) { 56 | await generateStaticFiles(nitro); 57 | }, 58 | }, 59 | }, 60 | { 61 | name: "vercel-static" as const, 62 | stdName: "vercel", 63 | static: true, 64 | url: import.meta.url, 65 | } 66 | ); 67 | 68 | export default [vercel, vercelStatic] as const; 69 | -------------------------------------------------------------------------------- /src/presets/vercel/runtime/vercel.ts: -------------------------------------------------------------------------------- 1 | import "#nitro-internal-pollyfills"; 2 | import { useNitroApp } from "nitro/runtime"; 3 | 4 | import { type NodeListener, toNodeListener } from "h3"; 5 | import { parseQuery } from "ufo"; 6 | 7 | const nitroApp = useNitroApp(); 8 | 9 | const handler = toNodeListener(nitroApp.h3App); 10 | 11 | const listener: NodeListener = function (req, res) { 12 | const query = req.headers["x-now-route-matches"] as string; 13 | if (query) { 14 | const { url } = parseQuery(query); 15 | if (url) { 16 | req.url = url as string; 17 | } 18 | } 19 | return handler(req, res); 20 | }; 21 | 22 | export default listener; 23 | -------------------------------------------------------------------------------- /src/presets/winterjs/preset.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroPreset } from "../_utils/preset"; 2 | 3 | const winterjs = defineNitroPreset( 4 | { 5 | extends: "base-worker", 6 | entry: "./runtime/winterjs", 7 | minify: false, 8 | serveStatic: "inline", 9 | wasm: { 10 | lazy: true, 11 | }, 12 | commands: { 13 | preview: 14 | "wasmer run wasmer/winterjs --forward-host-env --net --mapdir app:./ app/server/index.mjs", 15 | }, 16 | }, 17 | { 18 | name: "winterjs" as const, 19 | url: import.meta.url, 20 | } 21 | ); 22 | 23 | export default [winterjs] as const; 24 | -------------------------------------------------------------------------------- /src/presets/zeabur/runtime/zeabur.ts: -------------------------------------------------------------------------------- 1 | import "#nitro-internal-pollyfills"; 2 | import { type NodeListener, toNodeListener } from "h3"; 3 | import { useNitroApp } from "nitro/runtime"; 4 | 5 | const handler = toNodeListener(useNitroApp().h3App); 6 | 7 | const listener: NodeListener = function (req, res) { 8 | return handler(req, res); 9 | }; 10 | 11 | export default listener; 12 | -------------------------------------------------------------------------------- /src/presets/zerops/preset.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroPreset } from "../_utils/preset"; 2 | 3 | const zerops = defineNitroPreset( 4 | { 5 | extends: "node-server", 6 | serveStatic: true, 7 | }, 8 | { 9 | name: "zerops" as const, 10 | url: import.meta.url, 11 | } 12 | ); 13 | 14 | const zeropsStatic = defineNitroPreset( 15 | { 16 | extends: "static", 17 | output: { 18 | dir: "{{ rootDir }}/.zerops/output", 19 | publicDir: "{{ output.dir }}/static", 20 | }, 21 | }, 22 | { 23 | name: "zerops-static" as const, 24 | url: import.meta.url, 25 | static: true, 26 | } 27 | ); 28 | 29 | export default [zerops, zeropsStatic] as const; 30 | -------------------------------------------------------------------------------- /src/runtime/index.ts: -------------------------------------------------------------------------------- 1 | // Public API (also exposed as auto-imports defined in core/imports.ts) 2 | 3 | // App 4 | export { useNitroApp } from "./internal/app"; 5 | 6 | // Config 7 | export { useRuntimeConfig } from "./internal/config"; 8 | 9 | // Storage 10 | export { useStorage } from "./internal/storage"; 11 | 12 | // Type (only) helpers 13 | export { defineNitroPlugin } from "./internal/plugin"; 14 | export { defineRouteMeta } from "./internal/meta"; 15 | export { defineNitroErrorHandler } from "./internal/error/utils"; 16 | 17 | // Renderer 18 | export { defineRenderHandler } from "./internal/renderer"; 19 | 20 | // Route rules 21 | export { getRouteRules } from "./internal/route-rules"; 22 | 23 | // Context 24 | export { useEvent } from "./internal/context"; 25 | 26 | // Tasks 27 | export { defineTask, runTask } from "./internal/task"; 28 | 29 | // Database 30 | export { useDatabase } from "./internal/database"; 31 | 32 | // Cache 33 | export { 34 | defineCachedFunction, 35 | defineCachedEventHandler, 36 | cachedFunction, 37 | cachedEventHandler, 38 | } from "./internal/cache"; 39 | -------------------------------------------------------------------------------- /src/runtime/internal/client.ts: -------------------------------------------------------------------------------- 1 | import type { $Fetch, NitroFetchRequest } from "nitro/types"; 2 | // Client polyfill 3 | import { $fetch } from "ofetch"; 4 | 5 | if (!globalThis.$fetch) { 6 | globalThis.$fetch = $fetch as $Fetch; 7 | } 8 | -------------------------------------------------------------------------------- /src/runtime/internal/context.ts: -------------------------------------------------------------------------------- 1 | import { AsyncLocalStorage } from "node:async_hooks"; 2 | import { type H3Event, createError } from "h3"; 3 | import type { NitroAsyncContext } from "nitro/types"; 4 | import { getContext } from "unctx"; 5 | 6 | export const nitroAsyncContext = getContext("nitro-app", { 7 | asyncContext: import.meta._asyncContext, 8 | AsyncLocalStorage: import.meta._asyncContext ? AsyncLocalStorage : undefined, 9 | }); 10 | 11 | /** 12 | * 13 | * Access to the current Nitro request event. 14 | * 15 | * @experimental 16 | * - Requires `experimental.asyncContext: true` config to work. 17 | * - Works in Node.js and limited runtimes only 18 | * 19 | */ 20 | export function useEvent(): H3Event { 21 | try { 22 | return nitroAsyncContext.use().event; 23 | } catch { 24 | const hint = import.meta._asyncContext 25 | ? "Note: This is an experimental feature and might be broken on non-Node.js environments." 26 | : "Enable the experimental flag using `experimental.asyncContext: true`."; 27 | throw createError({ 28 | message: `Nitro request context is not available. ${hint}`, 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/runtime/internal/database.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from "db0"; 2 | import { createDatabase } from "db0"; 3 | import { connectionConfigs } from "#nitro-internal-virtual/database"; 4 | 5 | const instances: Record = Object.create(null); 6 | 7 | export function useDatabase(name = "default"): Database { 8 | if (instances[name]) { 9 | return instances[name]; 10 | } 11 | if (!connectionConfigs[name]) { 12 | throw new Error(`Database connection "${name}" not configured.`); 13 | } 14 | return (instances[name] = createDatabase( 15 | connectionConfigs[name].connector(connectionConfigs[name].options || {}) 16 | )); 17 | } 18 | -------------------------------------------------------------------------------- /src/runtime/internal/debug.ts: -------------------------------------------------------------------------------- 1 | import { createDebugger } from "hookable"; 2 | import { defineNitroPlugin } from "./plugin"; 3 | 4 | export default defineNitroPlugin((nitro) => { 5 | createDebugger(nitro.hooks, { tag: "nitro-runtime" }); 6 | }); 7 | -------------------------------------------------------------------------------- /src/runtime/internal/empty.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitrojs/nitro/8c2a2d8589a1c65b091f8bce1ca25d756f25bb11/src/runtime/internal/empty.ts -------------------------------------------------------------------------------- /src/runtime/internal/error/utils.ts: -------------------------------------------------------------------------------- 1 | import type { NitroErrorHandler } from "nitro/types"; 2 | 3 | export function defineNitroErrorHandler( 4 | handler: NitroErrorHandler 5 | ): NitroErrorHandler { 6 | return handler; 7 | } 8 | 9 | export type InternalHandlerResponse = { 10 | status: number; 11 | statusText: string; 12 | headers: Record; 13 | body: string | Record; 14 | }; 15 | -------------------------------------------------------------------------------- /src/runtime/internal/index.ts: -------------------------------------------------------------------------------- 1 | // Limited INTERNAL exports used by the presets runtime 2 | // Please don't use these in your project code! 3 | 4 | export { 5 | trapUnhandledNodeErrors, 6 | normalizeCookieHeader, 7 | requestHasBody, 8 | joinHeaders, 9 | toBuffer, 10 | } from "./utils"; 11 | 12 | export { 13 | normalizeLambdaIncomingHeaders, 14 | normalizeLambdaOutgoingHeaders, 15 | normalizeLambdaOutgoingBody, 16 | } from "./utils.lambda"; 17 | 18 | export { startScheduleRunner, runCronTasks } from "./task"; 19 | export { getAzureParsedCookiesFromHeaders } from "./utils.azure"; 20 | export { getGracefulShutdownConfig, setupGracefulShutdown } from "./shutdown"; 21 | export { getRouteRulesForPath } from "./route-rules"; 22 | -------------------------------------------------------------------------------- /src/runtime/internal/meta.ts: -------------------------------------------------------------------------------- 1 | import type { NitroRouteMeta } from "nitro/types"; 2 | 3 | export function defineRouteMeta(meta: NitroRouteMeta) { 4 | return meta; 5 | } 6 | -------------------------------------------------------------------------------- /src/runtime/internal/plugin.ts: -------------------------------------------------------------------------------- 1 | import type { NitroAppPlugin } from "nitro/types"; 2 | 3 | export function defineNitroPlugin(def: NitroAppPlugin) { 4 | return def; 5 | } 6 | 7 | export const nitroPlugin = defineNitroPlugin; 8 | -------------------------------------------------------------------------------- /src/runtime/internal/routes/swagger.ts: -------------------------------------------------------------------------------- 1 | import type { ReferenceConfiguration } from "@scalar/api-reference"; 2 | import { eventHandler } from "h3"; 3 | import { useRuntimeConfig } from "../config"; 4 | 5 | // https://github.com/swagger-api/swagger-ui 6 | 7 | export default eventHandler((event) => { 8 | const runtimeConfig = useRuntimeConfig(event); 9 | const title = runtimeConfig.nitro.openAPI?.meta?.title || "API Reference"; 10 | const description = runtimeConfig.nitro.openAPI?.meta?.description || ""; 11 | const openAPIEndpoint = 12 | runtimeConfig.nitro.openAPI?.route || "./_openapi.json"; 13 | 14 | const CDN_BASE = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@^5"; 15 | return /* html */ ` 16 | 17 | 18 | 19 | 20 | 21 | ${title} 22 | 23 | 24 | 25 |
26 | 27 | 31 | 44 | 45 | `; 46 | }); 47 | -------------------------------------------------------------------------------- /src/runtime/internal/shutdown.ts: -------------------------------------------------------------------------------- 1 | import type { Server as HttpServer } from "node:http"; 2 | import type { NitroApp } from "nitro/types"; 3 | import gracefulShutdown from "./lib/http-graceful-shutdown"; 4 | 5 | export function getGracefulShutdownConfig() { 6 | return { 7 | disabled: !!process.env.NITRO_SHUTDOWN_DISABLED, 8 | signals: (process.env.NITRO_SHUTDOWN_SIGNALS || "SIGTERM SIGINT") 9 | .split(" ") 10 | .map((s) => s.trim()), 11 | timeout: 12 | Number.parseInt(process.env.NITRO_SHUTDOWN_TIMEOUT || "", 10) || 30_000, 13 | forceExit: !process.env.NITRO_SHUTDOWN_NO_FORCE_EXIT, 14 | }; 15 | } 16 | 17 | export function setupGracefulShutdown( 18 | listener: HttpServer, 19 | nitroApp: NitroApp 20 | ) { 21 | const shutdownConfig = getGracefulShutdownConfig(); 22 | if (shutdownConfig.disabled) { 23 | return; 24 | } 25 | // https://github.com/sebhildebrandt/http-graceful-shutdown 26 | gracefulShutdown(listener, { 27 | signals: shutdownConfig.signals.join(" "), 28 | timeout: shutdownConfig.timeout, 29 | forceExit: shutdownConfig.forceExit, 30 | onShutdown: async () => { 31 | await new Promise((resolve) => { 32 | const timeout = setTimeout(() => { 33 | console.warn("Graceful shutdown timeout, force exiting..."); 34 | resolve(); 35 | }, shutdownConfig.timeout); 36 | nitroApp.hooks 37 | .callHook("close") 38 | .catch((error) => { 39 | console.error(error); 40 | }) 41 | .finally(() => { 42 | clearTimeout(timeout); 43 | resolve(); 44 | }); 45 | }); 46 | }, 47 | }); 48 | } 49 | -------------------------------------------------------------------------------- /src/runtime/internal/storage.ts: -------------------------------------------------------------------------------- 1 | import type { Storage, StorageValue } from "unstorage"; 2 | import { prefixStorage } from "unstorage"; 3 | import { storage } from "#nitro-internal-virtual/storage"; 4 | 5 | export function useStorage( 6 | base = "" 7 | ): Storage { 8 | return (base 9 | ? prefixStorage(storage, base) 10 | : storage) as unknown as Storage; 11 | } 12 | -------------------------------------------------------------------------------- /src/types/_utils.ts: -------------------------------------------------------------------------------- 1 | export type Enumerate< 2 | N extends number, 3 | Acc extends number[] = [], 4 | > = Acc["length"] extends N 5 | ? Acc[number] 6 | : Enumerate; 7 | 8 | export type IntRange = Exclude< 9 | Enumerate, 10 | Enumerate 11 | >; 12 | 13 | export type ExcludeFunctions> = Pick< 14 | G, 15 | // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type 16 | { [P in keyof G]: NonNullable extends Function ? never : P }[keyof G] 17 | >; 18 | 19 | export type DeepPartial = T extends (...args: never) => any 20 | ? T 21 | : T extends Record 22 | ? { [P in keyof T]?: DeepPartial } 23 | : T; 24 | 25 | export type KebabCase< 26 | T extends string, 27 | A extends string = "", 28 | > = T extends `${infer F}${infer R}` 29 | ? KebabCase ? "" : "-"}${Lowercase}`> 30 | : A; 31 | -------------------------------------------------------------------------------- /src/types/dev.ts: -------------------------------------------------------------------------------- 1 | import type { IncomingMessage, OutgoingMessage } from "node:http"; 2 | import type { Duplex } from "node:stream"; 3 | import type { Worker } from "node:worker_threads"; 4 | import type { FSWatcher } from "chokidar"; 5 | import type { App } from "h3"; 6 | import type { ListenOptions, Listener } from "listhen"; 7 | 8 | export interface DevServerOptions { 9 | port: number; 10 | hostname: string; 11 | watch: string[]; 12 | } 13 | 14 | export interface NitroWorker { 15 | worker: Worker | null; 16 | address: { host: string; port: number; socketPath?: string }; 17 | } 18 | 19 | export interface NitroDevServer { 20 | reload: () => void; 21 | listen: ( 22 | port: ListenOptions["port"], 23 | opts?: Partial 24 | ) => Promise; 25 | app: App; 26 | close: () => Promise; 27 | watcher?: FSWatcher; 28 | upgrade: ( 29 | req: IncomingMessage, 30 | socket: OutgoingMessage | Duplex, 31 | head: Buffer 32 | ) => void; 33 | } 34 | -------------------------------------------------------------------------------- /src/types/fetch/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./_match"; 2 | export * from "./_serialize"; 3 | export * from "./fetch"; 4 | -------------------------------------------------------------------------------- /src/types/global.ts: -------------------------------------------------------------------------------- 1 | import type { NitroConfig, NitroOptions } from "./config"; 2 | import type { NitroModule } from "./module"; 3 | 4 | export interface NitroStaticBuildFlags { 5 | _asyncContext?: boolean; 6 | _websocket?: boolean; 7 | _tasks?: boolean; 8 | dev?: boolean; 9 | client?: boolean; 10 | nitro?: boolean; 11 | baseURL?: string; 12 | prerender?: boolean; 13 | preset?: NitroOptions["preset"]; 14 | server?: boolean; 15 | versions?: { 16 | nitro?: string; 17 | }; 18 | } 19 | 20 | declare global { 21 | // eslint-disable-next-line @typescript-eslint/no-namespace 22 | namespace NodeJS { 23 | interface Process extends NitroStaticBuildFlags {} 24 | } 25 | 26 | interface ImportMeta extends NitroStaticBuildFlags {} 27 | } 28 | 29 | declare global { 30 | const defineNitroConfig: ( 31 | config: Omit 32 | ) => Omit; 33 | const defineNitroModule: (definition: NitroModule) => NitroModule; 34 | } 35 | 36 | export type {}; 37 | -------------------------------------------------------------------------------- /src/types/h3.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | CacheOptions, 3 | CaptureError, 4 | CapturedErrorContext, 5 | } from "./runtime"; 6 | import type { Base$Fetch, NitroFetchRequest } from "./fetch/fetch"; 7 | 8 | export type H3EventFetch = ( 9 | request: NitroFetchRequest, 10 | init?: RequestInit 11 | ) => Promise; 12 | 13 | export type H3Event$Fetch = Base$Fetch; 14 | 15 | declare module "h3" { 16 | interface H3Event { 17 | /** @experimental Calls fetch with same context and request headers */ 18 | fetch: H3EventFetch; 19 | /** @experimental Calls fetch with same context and request headers */ 20 | $fetch: H3Event$Fetch; 21 | waitUntil: (promise: Promise) => void; 22 | /** @experimental */ 23 | captureError: CaptureError; 24 | } 25 | interface H3Context { 26 | nitro: { 27 | _waitUntilPromises?: Promise[]; 28 | /** @experimental */ 29 | errors: { error?: Error; context: CapturedErrorContext }[]; 30 | }; 31 | 32 | cache: { 33 | options: CacheOptions; 34 | }; 35 | } 36 | } 37 | 38 | export type {}; 39 | -------------------------------------------------------------------------------- /src/types/hooks.ts: -------------------------------------------------------------------------------- 1 | import type { NitroConfig } from "./config"; 2 | import type { Nitro, NitroTypes } from "./nitro"; 3 | import type { PrerenderRoute } from "./prerender"; 4 | import type { RollupConfig } from "./rollup"; 5 | 6 | type HookResult = void | Promise; 7 | 8 | export interface NitroHooks { 9 | "types:extend": (types: NitroTypes) => HookResult; 10 | "build:before": (nitro: Nitro) => HookResult; 11 | "rollup:before": (nitro: Nitro, config: RollupConfig) => HookResult; 12 | compiled: (nitro: Nitro) => HookResult; 13 | "dev:reload": () => HookResult; 14 | "dev:start": () => HookResult; 15 | "dev:error": (cause?: unknown) => HookResult; 16 | "rollup:reload": () => HookResult; 17 | restart: () => HookResult; 18 | close: () => HookResult; 19 | // Prerender 20 | "prerender:routes": (routes: Set) => HookResult; 21 | "prerender:config": (config: NitroConfig) => HookResult; 22 | "prerender:init": (prerenderer: Nitro) => HookResult; 23 | "prerender:generate": (route: PrerenderRoute, nitro: Nitro) => HookResult; 24 | "prerender:route": (route: PrerenderRoute) => HookResult; 25 | "prerender:done": (result: { 26 | prerenderedRoutes: PrerenderRoute[]; 27 | failedRoutes: PrerenderRoute[]; 28 | }) => HookResult; 29 | } 30 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./fetch"; 2 | export * from "./runtime"; 3 | export * from "./config"; 4 | export * from "./dev"; 5 | export * from "./global"; 6 | export * from "./h3"; 7 | export * from "./handler"; 8 | export * from "./hooks"; 9 | export * from "./module"; 10 | export * from "./nitro"; 11 | export * from "./prerender"; 12 | export * from "./preset"; 13 | export * from "./rollup"; 14 | export * from "./route-rules"; 15 | -------------------------------------------------------------------------------- /src/types/module.ts: -------------------------------------------------------------------------------- 1 | import type { Nitro } from "./nitro"; 2 | 3 | export type NitroModuleInput = string | NitroModule | NitroModule["setup"]; 4 | 5 | export interface NitroModule { 6 | name?: string; 7 | setup: (this: void, nitro: Nitro) => void | Promise; 8 | } 9 | -------------------------------------------------------------------------------- /src/types/openapi.ts: -------------------------------------------------------------------------------- 1 | import type { ApiReferenceConfiguration as ScalarConfig } from "@scalar/api-reference"; 2 | 3 | /** 4 | * Nitro OpenAPI configuration 5 | */ 6 | export interface NitroOpenAPIConfig { 7 | /** 8 | * OpenAPI meta information 9 | */ 10 | meta?: { 11 | title?: string; 12 | description?: string; 13 | version?: string; 14 | }; 15 | 16 | /** 17 | * OpenAPI json route 18 | * 19 | * Default is `/_openapi.json` 20 | */ 21 | route?: string; 22 | 23 | /** 24 | * Enable OpenAPI generation for production builds 25 | */ 26 | production?: false | "runtime" | "prerender"; 27 | 28 | /** 29 | * UI configurations 30 | */ 31 | ui?: { 32 | /** 33 | * Scalar UI configuration 34 | */ 35 | scalar?: 36 | | false 37 | | (Partial & { 38 | /** 39 | * Scalar UI route 40 | * 41 | * Default is `/_scalar` 42 | */ 43 | route?: string; 44 | }); 45 | /** 46 | * Swagger UI configuration 47 | */ 48 | swagger?: 49 | | false 50 | | { 51 | /** 52 | * Swagger UI route 53 | * 54 | * Default is `/_swagger` 55 | */ 56 | route?: string; 57 | }; 58 | }; 59 | } 60 | -------------------------------------------------------------------------------- /src/types/prerender.ts: -------------------------------------------------------------------------------- 1 | export interface PrerenderRoute { 2 | route: string; 3 | contents?: string; 4 | data?: ArrayBuffer; 5 | fileName?: string; 6 | error?: Error & { statusCode: number; statusMessage: string }; 7 | generateTimeMS?: number; 8 | skip?: boolean; 9 | contentType?: string; 10 | } 11 | 12 | /** @deprecated Internal type will be removed in future versions */ 13 | export type PrerenderGenerateRoute = PrerenderRoute; 14 | -------------------------------------------------------------------------------- /src/types/preset.ts: -------------------------------------------------------------------------------- 1 | import type { DateString } from "compatx"; 2 | import type { ProviderName } from "std-env"; 3 | import type { NitroConfig } from "./config"; 4 | 5 | export type NitroPreset = NitroConfig | (() => NitroConfig); 6 | 7 | export interface NitroPresetMeta { 8 | url: string; 9 | name: string; 10 | stdName?: ProviderName; 11 | aliases?: string[]; 12 | static?: boolean; 13 | compatibilityDate?: DateString; 14 | } 15 | -------------------------------------------------------------------------------- /src/types/runtime/asset.ts: -------------------------------------------------------------------------------- 1 | export interface PublicAsset { 2 | type: string; 3 | etag: string; 4 | mtime: string; 5 | path: string; 6 | size: number; 7 | encoding?: string; 8 | data?: string; 9 | } 10 | 11 | export interface AssetMeta { 12 | type?: string; 13 | etag?: string; 14 | mtime?: string; 15 | } 16 | -------------------------------------------------------------------------------- /src/types/runtime/cache.ts: -------------------------------------------------------------------------------- 1 | import type { H3Event } from "h3"; 2 | 3 | export interface CacheEntry { 4 | value?: T; 5 | expires?: number; 6 | mtime?: number; 7 | integrity?: string; 8 | } 9 | 10 | export interface CacheOptions { 11 | name?: string; 12 | getKey?: (...args: ArgsT) => string | Promise; 13 | transform?: (entry: CacheEntry, ...args: ArgsT) => any; 14 | validate?: (entry: CacheEntry, ...args: ArgsT) => boolean; 15 | shouldInvalidateCache?: (...args: ArgsT) => boolean | Promise; 16 | shouldBypassCache?: (...args: ArgsT) => boolean | Promise; 17 | group?: string; 18 | integrity?: any; 19 | /** 20 | * Number of seconds to cache the response. Defaults to 1. 21 | */ 22 | maxAge?: number; 23 | swr?: boolean; 24 | staleMaxAge?: number; 25 | base?: string; 26 | } 27 | 28 | export interface ResponseCacheEntry { 29 | body: T | undefined; 30 | code: number; 31 | headers: Record; 32 | } 33 | 34 | export interface CachedEventHandlerOptions 35 | extends Omit< 36 | CacheOptions, [H3Event]>, 37 | "transform" | "validate" 38 | > { 39 | headersOnly?: boolean; 40 | varies?: string[] | readonly string[]; 41 | } 42 | -------------------------------------------------------------------------------- /src/types/runtime/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./asset"; 2 | export * from "./cache"; 3 | export * from "./nitro"; 4 | export * from "./task"; 5 | -------------------------------------------------------------------------------- /src/types/runtime/task.ts: -------------------------------------------------------------------------------- 1 | type MaybePromise = T | Promise; 2 | 3 | /** @experimental */ 4 | export interface TaskContext {} 5 | 6 | /** @experimental */ 7 | export interface TaskPayload { 8 | [key: string]: unknown; 9 | } 10 | 11 | /** @experimental */ 12 | export interface TaskMeta { 13 | name?: string; 14 | description?: string; 15 | } 16 | 17 | /** @experimental */ 18 | export interface TaskEvent { 19 | name: string; 20 | payload: TaskPayload; 21 | context: TaskContext; 22 | } 23 | 24 | /** @experimental */ 25 | export interface TaskResult { 26 | result?: RT; 27 | } 28 | 29 | /** @experimental */ 30 | export interface Task { 31 | meta?: TaskMeta; 32 | run(event: TaskEvent): MaybePromise<{ result?: RT }>; 33 | } 34 | 35 | /** @experimental */ 36 | export interface TaskRunnerOptions { 37 | cwd?: string; 38 | buildDir?: string; 39 | } 40 | -------------------------------------------------------------------------------- /src/types/virtual/app-config.d.ts: -------------------------------------------------------------------------------- 1 | import type { AppConfig } from "nitro"; 2 | 3 | export const appConfig: AppConfig; 4 | -------------------------------------------------------------------------------- /src/types/virtual/database.d.ts: -------------------------------------------------------------------------------- 1 | import type { Connector } from "db0"; 2 | 3 | export declare const connectionConfigs: { 4 | [name: string]: { 5 | connector: (options: any) => Connector; 6 | options: any; 7 | }; 8 | }; 9 | -------------------------------------------------------------------------------- /src/types/virtual/error-handler.d.ts: -------------------------------------------------------------------------------- 1 | import type { NitroErrorHandler } from "nitro/types"; 2 | 3 | type EParams = Parameters; 4 | type EReturn = ReturnType; 5 | 6 | const errorHandler: (error: EParams[0], event: EParams[1]) => EReturn; 7 | 8 | export default errorHandler; 9 | -------------------------------------------------------------------------------- /src/types/virtual/plugins.d.ts: -------------------------------------------------------------------------------- 1 | import type { NitroAppPlugin } from "../plugin"; 2 | 3 | export const plugins: NitroAppPlugin[] = []; 4 | -------------------------------------------------------------------------------- /src/types/virtual/polyfills.ts: -------------------------------------------------------------------------------- 1 | export default {}; 2 | -------------------------------------------------------------------------------- /src/types/virtual/public-assets.d.ts: -------------------------------------------------------------------------------- 1 | export const publicAssetBases: string[]; 2 | export const isPublicAssetURL: (id: string) => boolean; 3 | export const getPublicAssetMeta: (id: string) => { maxAge?: number }; 4 | export const readAsset: (id: string) => Promise; 5 | export const getAsset: (id: string) => PublicAsset; 6 | -------------------------------------------------------------------------------- /src/types/virtual/server-assets.d.ts: -------------------------------------------------------------------------------- 1 | export function readAsset(id: string): Promise; 2 | export function statAsset(id: string): Promise; 3 | export function getKeys(): Promise; 4 | -------------------------------------------------------------------------------- /src/types/virtual/server-handlers-meta.d.ts: -------------------------------------------------------------------------------- 1 | import type { OperationObject } from "../openapi-ts"; 2 | import { NitroRouteMeta } from "nitro/types"; 3 | 4 | export const handlersMeta: { 5 | route?: string; 6 | method?: string; 7 | meta?: NitroRouteMeta; 8 | }[]; 9 | -------------------------------------------------------------------------------- /src/types/virtual/server-handlers.d.ts: -------------------------------------------------------------------------------- 1 | import type { H3EventHandler, LazyEventHandler, RouterMethod } from "h3"; 2 | 3 | type HandlerDefinition = { 4 | route: string; 5 | lazy?: boolean; 6 | middleware?: boolean; 7 | handler: H3EventHandler; 8 | method?: RouterMethod; 9 | } & { 10 | lazy: true; 11 | handler: LazyEventHandler; 12 | }; 13 | 14 | export const handlers: HandlerDefinition[]; 15 | -------------------------------------------------------------------------------- /src/types/virtual/storage.d.ts: -------------------------------------------------------------------------------- 1 | import type { Storage } from "unstorage"; 2 | 3 | export declare const storage: Storage; 4 | -------------------------------------------------------------------------------- /src/types/virtual/tasks.ts: -------------------------------------------------------------------------------- 1 | import type { Task, TaskMeta } from "nitro/types"; 2 | 3 | export const tasks: Record< 4 | string, 5 | { resolve?: () => Promise; meta: TaskMeta } 6 | > = {}; 7 | 8 | export const scheduledTasks: false | { cron: string; tasks: string[] }[] = []; 9 | -------------------------------------------------------------------------------- /src/utils/nitro.ts: -------------------------------------------------------------------------------- 1 | import type { Nitro } from "nitro/types"; 2 | import { upperFirst } from "scule"; 3 | 4 | export function nitroServerName(nitro: Nitro) { 5 | return nitro.options.framework.name === "nitro" 6 | ? "Nitro Server" 7 | : `${upperFirst(nitro.options.framework.name as string)} Nitro server`; 8 | } 9 | -------------------------------------------------------------------------------- /src/utils/parallel.ts: -------------------------------------------------------------------------------- 1 | export async function runParallel( 2 | inputs: Set, 3 | cb: (input: T) => unknown | Promise, 4 | opts: { concurrency: number; interval?: number } 5 | ) { 6 | const tasks = new Set>(); 7 | 8 | function queueNext(): undefined | Promise { 9 | const route = inputs.values().next().value; 10 | if (!route) { 11 | return; 12 | } 13 | 14 | inputs.delete(route); 15 | const task = ( 16 | opts.interval 17 | ? new Promise((resolve) => setTimeout(resolve, opts.interval)) 18 | : Promise.resolve() 19 | ) 20 | .then(() => cb(route)) 21 | .catch((error) => { 22 | console.error(error); 23 | }); 24 | 25 | tasks.add(task); 26 | return task.then(() => { 27 | tasks.delete(task); 28 | if (inputs.size > 0) { 29 | return refillQueue(); 30 | } 31 | }); 32 | } 33 | 34 | function refillQueue(): Promise { 35 | const workers = Math.min(opts.concurrency - tasks.size, inputs.size); 36 | return Promise.all(Array.from({ length: workers }, () => queueNext())); 37 | } 38 | 39 | await refillQueue(); 40 | } 41 | -------------------------------------------------------------------------------- /src/utils/storage.ts: -------------------------------------------------------------------------------- 1 | import type { Nitro } from "nitro/types"; 2 | import { klona } from "klona"; 3 | import { createStorage as _createStorage, builtinDrivers } from "unstorage"; 4 | 5 | export async function createStorage(nitro: Nitro) { 6 | const storage = _createStorage(); 7 | 8 | // https://github.com/unjs/unstorage/issues/566 9 | const mounts = klona({ 10 | ...nitro.options.storage, 11 | ...nitro.options.devStorage, 12 | }); 13 | 14 | for (const [path, opts] of Object.entries(mounts)) { 15 | if (opts.driver) { 16 | const driver = await import( 17 | builtinDrivers[opts.driver as keyof typeof builtinDrivers] || 18 | opts.driver 19 | ).then((r) => r.default || r); 20 | storage.mount(path, driver(opts)); 21 | } else { 22 | nitro.logger.warn(`No \`driver\` set for storage mount point "${path}".`); 23 | } 24 | } 25 | 26 | return storage; 27 | } 28 | 29 | export async function snapshotStorage(nitro: Nitro) { 30 | const data: Record = {}; 31 | 32 | const allKeys = [ 33 | ...new Set( 34 | await Promise.all( 35 | nitro.options.bundledStorage.map((base) => nitro.storage.getKeys(base)) 36 | ).then((r) => r.flat()) 37 | ), 38 | ]; 39 | 40 | await Promise.all( 41 | allKeys.map(async (key) => { 42 | data[key] = await nitro.storage.getItem(key); 43 | }) 44 | ); 45 | 46 | return data; 47 | } 48 | -------------------------------------------------------------------------------- /test/fixture/.env: -------------------------------------------------------------------------------- 1 | APP_DOMAIN=test.com 2 | NITRO_DYNAMIC=from-env 3 | -------------------------------------------------------------------------------- /test/fixture/.gitignore: -------------------------------------------------------------------------------- 1 | !node_modules 2 | !.env 3 | -------------------------------------------------------------------------------- /test/fixture/api/_ignored.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler((event) => { 2 | throw createError("This file should be ignored!"); 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixture/api/cached.ts: -------------------------------------------------------------------------------- 1 | export default defineCachedEventHandler( 2 | (event) => { 3 | return { 4 | timestamp: Date.now(), 5 | eventContextCache: event.context.cache, 6 | }; 7 | }, 8 | { swr: true, maxAge: 60 } 9 | ); 10 | -------------------------------------------------------------------------------- /test/fixture/api/db.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(async () => { 2 | const db = useDatabase(); 3 | 4 | // Create users table 5 | await db.sql`DROP TABLE IF EXISTS users`; 6 | await db.sql`CREATE TABLE IF NOT EXISTS users ("id" TEXT PRIMARY KEY, "firstName" TEXT, "lastName" TEXT, "email" TEXT)`; 7 | 8 | // Add a new user 9 | const userId = "1001"; 10 | await db.sql`INSERT INTO users VALUES (${userId}, 'John', 'Doe', '')`; 11 | 12 | // Query for users 13 | const { rows } = await db.sql`SELECT * FROM users WHERE id = ${userId}`; 14 | 15 | return { 16 | rows, 17 | }; 18 | }); 19 | -------------------------------------------------------------------------------- /test/fixture/api/echo.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler((event) => { 2 | return { 3 | url: event.path, 4 | method: event.method, 5 | headers: Object.fromEntries(event.headers.entries()), 6 | }; 7 | }); 8 | -------------------------------------------------------------------------------- /test/fixture/api/error.ts: -------------------------------------------------------------------------------- 1 | import { createError } from "h3"; 2 | export default eventHandler(() => { 3 | throw createError({ 4 | statusCode: 503, 5 | statusMessage: "Service Unavailable", 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /test/fixture/api/errors.ts: -------------------------------------------------------------------------------- 1 | import { allErrors } from "../plugins/errors"; 2 | 3 | export default eventHandler((event) => { 4 | return { 5 | allErrors: allErrors.map((entry) => ({ 6 | message: entry.error.message, 7 | })), 8 | }; 9 | }); 10 | -------------------------------------------------------------------------------- /test/fixture/api/headers.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler((event) => { 2 | setHeader(event, "x-foo", "bar"); 3 | setHeader(event, "x-array", ["foo", "bar"]); 4 | 5 | setHeader(event, "Set-Cookie", "foo=bar, bar=baz"); 6 | setCookie(event, "test", "value"); 7 | setCookie(event, "test2", "value"); 8 | 9 | return "headers sent"; 10 | }); 11 | -------------------------------------------------------------------------------- /test/fixture/api/hello.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(() => ({ message: "Hello API" })); 2 | -------------------------------------------------------------------------------- /test/fixture/api/hey/index.get.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(() => "Hey API"); 2 | -------------------------------------------------------------------------------- /test/fixture/api/kebab.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(() => kebabCase("HelloWorld")); 2 | -------------------------------------------------------------------------------- /test/fixture/api/meta/test.ts: -------------------------------------------------------------------------------- 1 | defineRouteMeta({ 2 | openAPI: { 3 | tags: ["test"], 4 | description: "Test route description", 5 | parameters: [{ in: "query", name: "test", required: true }], 6 | responses: { 7 | 200: { 8 | description: "result", 9 | content: { 10 | "application/json": { schema: { $ref: "#/components/schemas/Test" } }, 11 | }, 12 | }, 13 | }, 14 | $global: { 15 | components: { 16 | schemas: { 17 | Test: { 18 | type: "object", 19 | properties: { 20 | status: { 21 | type: "string", 22 | enum: ["OK", "ERROR"], 23 | }, 24 | }, 25 | }, 26 | }, 27 | }, 28 | }, 29 | }, 30 | }); 31 | 32 | export default defineEventHandler(() => { 33 | return { 34 | status: "OK", 35 | }; 36 | }); 37 | -------------------------------------------------------------------------------- /test/fixture/api/methods/default.post.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler<"Default override">(() => "Default override"); 2 | -------------------------------------------------------------------------------- /test/fixture/api/methods/default.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler<"Default route">(() => "Default route"); 2 | -------------------------------------------------------------------------------- /test/fixture/api/methods/foo.get.get.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(() => "foo.get"); 2 | -------------------------------------------------------------------------------- /test/fixture/api/methods/get.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(() => "get"); 2 | -------------------------------------------------------------------------------- /test/fixture/api/methods/index.get.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler<"Index get">(() => "Index get"); 2 | -------------------------------------------------------------------------------- /test/fixture/api/methods/index.post.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler<"Index post">(() => "Index post"); 2 | -------------------------------------------------------------------------------- /test/fixture/api/param/[test-id].ts: -------------------------------------------------------------------------------- 1 | export default eventHandler((event) => { 2 | setHeader(event, "Content-Type", "text/plain; charset=utf-16"); 3 | return event.context.params!["test-id"]; 4 | }); 5 | -------------------------------------------------------------------------------- /test/fixture/api/serialized/date.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(() => ({ createdAt: new Date() })); 2 | -------------------------------------------------------------------------------- /test/fixture/api/serialized/error.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(() => { 2 | return createError({ 3 | statusCode: 400, 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /test/fixture/api/serialized/function.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(() => { 2 | return { foo: () => "foo" }; 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixture/api/serialized/map.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(() => { 2 | return { foo: new Map([["key", 2]]) }; 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixture/api/serialized/null.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(() => { 2 | return null; 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixture/api/serialized/set.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(() => { 2 | return { foo: new Set(["item"]) }; 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixture/api/serialized/tuple.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(() => { 2 | return ["foo", new Date()] as [string, Date]; 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixture/api/serialized/void.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(() => {}); 2 | -------------------------------------------------------------------------------- /test/fixture/api/storage/dev.dev.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(async (event) => { 2 | const storage = useStorage(); 3 | return { 4 | keys: await storage.getKeys("/src/public"), 5 | }; 6 | }); 7 | -------------------------------------------------------------------------------- /test/fixture/api/storage/item.get.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(async (event) => { 2 | const { base = "", key = "" } = getQuery(event) as Record; 3 | const storage = useStorage(`test:${base}`); 4 | 5 | if (!key || key.endsWith(":")) { 6 | return await storage.getKeys(); 7 | } 8 | 9 | const value = await storage.getItem(key); 10 | return value; 11 | }); 12 | -------------------------------------------------------------------------------- /test/fixture/api/storage/item.put.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(async (event) => { 2 | const { base = "", key = "" } = getQuery(event) as Record; 3 | const storage = useStorage(`test:${base}`); 4 | const value = await readBody(event); 5 | await storage.setItem(key, value); 6 | return value; 7 | }); 8 | -------------------------------------------------------------------------------- /test/fixture/api/typed/catchall/[slug]/[...another].ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(() => ({ 2 | internalApiKey: "/api/typed/catchall/:slug/**:another" as const, 3 | })); 4 | -------------------------------------------------------------------------------- /test/fixture/api/typed/catchall/some/[...test].ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(() => ({ 2 | internalApiKey: "/api/typed/catchall/some/**:test" as const, 3 | })); 4 | -------------------------------------------------------------------------------- /test/fixture/api/typed/todos/[...].ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(() => ({ 2 | internalApiKey: "/api/typed/todos/**" as const, 3 | })); 4 | -------------------------------------------------------------------------------- /test/fixture/api/typed/todos/[todoId]/comments/[...commentId].ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(() => ({ 2 | internalApiKey: "/api/typed/todos/:todoId/comments/**:commentId" as const, 3 | })); 4 | -------------------------------------------------------------------------------- /test/fixture/api/typed/user/[userId]/[userExtends].ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(() => ({ 2 | internalApiKey: "/api/typed/user/:userId/:userExtends" as const, 3 | })); 4 | -------------------------------------------------------------------------------- /test/fixture/api/typed/user/[userId]/index.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(() => ({ 2 | internalApiKey: "/api/typed/user/:userId" as const, 3 | })); 4 | -------------------------------------------------------------------------------- /test/fixture/api/typed/user/[userId]/post/[postId].ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(() => ({ 2 | internalApiKey: "/api/typed/user/:userId/post/:postId" as const, 3 | })); 4 | -------------------------------------------------------------------------------- /test/fixture/api/typed/user/[userId]/post/firstPost.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(() => ({ 2 | internalApiKey: "/api/typed/user/:userId/post/firstPost" as const, 3 | })); 4 | -------------------------------------------------------------------------------- /test/fixture/api/typed/user/john/[johnExtends].ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(() => ({ 2 | internalApiKey: "/api/typed/user/john/:johnExtends" as const, 3 | })); 4 | -------------------------------------------------------------------------------- /test/fixture/api/typed/user/john/index.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(() => ({ 2 | internalApiKey: "/api/typed/user/john" as const, 3 | })); 4 | -------------------------------------------------------------------------------- /test/fixture/api/typed/user/john/post/[postId].ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(() => ({ 2 | internalApiKey: "/api/typed/user/john/post/:postId" as const, 3 | })); 4 | -------------------------------------------------------------------------------- /test/fixture/api/typed/user/john/post/coffee.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(() => ({ 2 | internalApiKey: "/api/typed/user/john/post/coffee" as const, 3 | })); 4 | -------------------------------------------------------------------------------- /test/fixture/api/upload.post.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler((event) => { 2 | return "uploaded!"; 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixture/api/wildcard/[...param].ts: -------------------------------------------------------------------------------- 1 | export default eventHandler((event) => { 2 | return event.context.params!.param as string; 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixture/app.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | "app-config": true, 3 | }; 4 | -------------------------------------------------------------------------------- /test/fixture/assets/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitrojs/nitro/8c2a2d8589a1c65b091f8bce1ca25d756f25bb11/test/fixture/assets/cat.jpg -------------------------------------------------------------------------------- /test/fixture/assets/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "bar" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixture/assets/test.md: -------------------------------------------------------------------------------- 1 | # Hello world 2 | 3 | Use `process.env.NODE_ENV` to ... 4 | -------------------------------------------------------------------------------- /test/fixture/error.ts: -------------------------------------------------------------------------------- 1 | import { defineNitroErrorHandler } from "nitro/runtime"; 2 | import { send } from "h3"; 3 | export default defineNitroErrorHandler( 4 | async (error, event, { defaultHandler }) => { 5 | if (event.path.includes("?json")) { 6 | const response = await defaultHandler(error, event, { json: true }); 7 | return send(event, JSON.stringify({ json: response.body }, null, 2)); 8 | } 9 | } 10 | ); 11 | -------------------------------------------------------------------------------- /test/fixture/files/index.html: -------------------------------------------------------------------------------- 1 |

nitro is amazing!

2 | -------------------------------------------------------------------------------- /test/fixture/files/sql.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -------------------------------------------------------------------------------- /test/fixture/files/sqlts.sql.ts: -------------------------------------------------------------------------------- 1 | export default "--\n"; 2 | -------------------------------------------------------------------------------- /test/fixture/files/test.txt: -------------------------------------------------------------------------------- 1 | this is an asset from a text file from nitro 2 | -------------------------------------------------------------------------------- /test/fixture/middleware/_ignored.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler((event) => { 2 | throw createError("This file should be ignored!"); 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixture/node_modules/@fixture/nitro-dep-a/index.mjs: -------------------------------------------------------------------------------- 1 | export { default } from '@fixture/nitro-lib' 2 | -------------------------------------------------------------------------------- /test/fixture/node_modules/@fixture/nitro-dep-a/node_modules/@fixture/nitro-lib/index.mjs: -------------------------------------------------------------------------------- 1 | import nestedLib from '@fixture/nested-lib' 2 | 3 | export default '@fixture/nitro-lib@1.0.0+' + nestedLib 4 | -------------------------------------------------------------------------------- /test/fixture/node_modules/@fixture/nitro-dep-a/node_modules/@fixture/nitro-lib/node_modules/@fixture/nested-lib/index.mjs: -------------------------------------------------------------------------------- 1 | export default '@fixture/nested-lib@1.0.0' 2 | -------------------------------------------------------------------------------- /test/fixture/node_modules/@fixture/nitro-dep-a/node_modules/@fixture/nitro-lib/node_modules/@fixture/nested-lib/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fixture/nested-lib", 3 | "version": "1.0.0", 4 | "exports": "./index.mjs" 5 | } 6 | -------------------------------------------------------------------------------- /test/fixture/node_modules/@fixture/nitro-dep-a/node_modules/@fixture/nitro-lib/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nitro-lib", 3 | "version": "1.0.0", 4 | "exports": "./index.mjs", 5 | "dependencies": { 6 | "nested-lib": "1.0.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/fixture/node_modules/@fixture/nitro-dep-a/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fixture/nitro-dep-a", 3 | "version": "1.0.0", 4 | "exports": "./index.mjs", 5 | "dependencies": { 6 | "@fixture/nitro-lib": "1.0.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/fixture/node_modules/@fixture/nitro-dep-b/index.mjs: -------------------------------------------------------------------------------- 1 | export { default } from '@fixture/nitro-lib' 2 | -------------------------------------------------------------------------------- /test/fixture/node_modules/@fixture/nitro-dep-b/node_modules/@fixture/nitro-lib/index.mjs: -------------------------------------------------------------------------------- 1 | import nestedLib from '@fixture/nested-lib' 2 | 3 | export default '@fixture/nitro-lib@2.0.1+' + nestedLib 4 | -------------------------------------------------------------------------------- /test/fixture/node_modules/@fixture/nitro-dep-b/node_modules/@fixture/nitro-lib/node_modules/@fixture/nested-lib/index.mjs: -------------------------------------------------------------------------------- 1 | export default '@fixture/nested-lib@2.0.1' 2 | -------------------------------------------------------------------------------- /test/fixture/node_modules/@fixture/nitro-dep-b/node_modules/@fixture/nitro-lib/node_modules/@fixture/nested-lib/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fixture/nested-lib", 3 | "version": "2.0.1", 4 | "exports": "./index.mjs" 5 | } 6 | -------------------------------------------------------------------------------- /test/fixture/node_modules/@fixture/nitro-dep-b/node_modules/@fixture/nitro-lib/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nitro-lib", 3 | "version": "2.0.1", 4 | "exports": { 5 | ".": "./index.mjs", 6 | "./subpath": "./subpath.mjs" 7 | }, 8 | "dependencies": { 9 | "nested-lib": "2.0.1" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/fixture/node_modules/@fixture/nitro-dep-b/node_modules/@fixture/nitro-lib/subpath.mjs: -------------------------------------------------------------------------------- 1 | export default '2.0.1'; 2 | -------------------------------------------------------------------------------- /test/fixture/node_modules/@fixture/nitro-dep-b/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fixture/nitro-dep-b", 3 | "version": "2.0.1", 4 | "exports": "./index.mjs", 5 | "dependencies": { 6 | "@fixture/nitro-lib": "2.0.1" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/fixture/node_modules/@fixture/nitro-lib/index.mjs: -------------------------------------------------------------------------------- 1 | import nestedLib from '@fixture/nested-lib' 2 | 3 | export default '@fixture/nitro-lib@2.0.0+' + nestedLib 4 | -------------------------------------------------------------------------------- /test/fixture/node_modules/@fixture/nitro-lib/node_modules/@fixture/nested-lib/index.mjs: -------------------------------------------------------------------------------- 1 | export default '@fixture/nested-lib@2.0.0' 2 | -------------------------------------------------------------------------------- /test/fixture/node_modules/@fixture/nitro-lib/node_modules/@fixture/nested-lib/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fixture/nested-lib", 3 | "version": "2.0.0", 4 | "exports": "./index.mjs" 5 | } 6 | -------------------------------------------------------------------------------- /test/fixture/node_modules/@fixture/nitro-lib/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fixture/nitro-lib-aliased-from-another-name", 3 | "version": "2.0.0", 4 | "exports": { 5 | ".": "./index.mjs", 6 | "./subpath": "./subpath.mjs" 7 | }, 8 | "dependencies": { 9 | "@fixture/nested-lib": "2.0.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/fixture/node_modules/@fixture/nitro-lib/subpath.mjs: -------------------------------------------------------------------------------- 1 | export default '@fixture/nitro-lib@2.0.0'; 2 | -------------------------------------------------------------------------------- /test/fixture/node_modules/@fixture/nitro-utils/extra.mjs: -------------------------------------------------------------------------------- 1 | export default '@fixture/nitro-utils/extra' 2 | -------------------------------------------------------------------------------- /test/fixture/node_modules/@fixture/nitro-utils/extra2.mjs: -------------------------------------------------------------------------------- 1 | export default '@fixture/nitro-utils/extra2' 2 | -------------------------------------------------------------------------------- /test/fixture/node_modules/@fixture/nitro-utils/index.mjs: -------------------------------------------------------------------------------- 1 | export default '@fixture/nitro-utils' 2 | -------------------------------------------------------------------------------- /test/fixture/node_modules/@fixture/nitro-utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fixture/nitro-utils", 3 | "private": true, 4 | "type": "module", 5 | "exports": { 6 | ".": "./index.mjs", 7 | "./extra": "./extra.mjs", 8 | "./extra2": "./extra2.mjs" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/fixture/plugins/errors.ts: -------------------------------------------------------------------------------- 1 | export const allErrors: { error: Error; context: any }[] = []; 2 | 3 | export default defineNitroPlugin((app) => { 4 | app.hooks.hook("error", (error, context) => { 5 | allErrors.push({ error, context }); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /test/fixture/plugins/vary.ts: -------------------------------------------------------------------------------- 1 | export default defineNitroPlugin((app) => { 2 | app.hooks.hook("request", (event) => { 3 | if (event.path.endsWith(".css")) { 4 | setResponseHeader(event, "Vary", "Origin"); 5 | } 6 | if (event.path.endsWith(".js")) { 7 | setResponseHeader(event, "Vary", ["Origin"]); 8 | } 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /test/fixture/public/_ignored.txt: -------------------------------------------------------------------------------- 1 | This file should be ignored! 2 | -------------------------------------------------------------------------------- /test/fixture/public/_unignored.txt: -------------------------------------------------------------------------------- 1 | This file should not be ignored! 2 | -------------------------------------------------------------------------------- /test/fixture/public/build/test.txt: -------------------------------------------------------------------------------- 1 | Works! 2 | -------------------------------------------------------------------------------- /test/fixture/public/cf-pages-exclude/not-in-routes-json.txt: -------------------------------------------------------------------------------- 1 | This file shouldn't be under "exclude" in the _routes.json outputted by Cloudflare Pages builds. 2 | It's covered by the "/cf-pages-exclude/*" wildcard. 3 | -------------------------------------------------------------------------------- /test/fixture/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitrojs/nitro/8c2a2d8589a1c65b091f8bce1ca25d756f25bb11/test/fixture/public/favicon.ico -------------------------------------------------------------------------------- /test/fixture/public/foo.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitrojs/nitro/8c2a2d8589a1c65b091f8bce1ca25d756f25bb11/test/fixture/public/foo.css -------------------------------------------------------------------------------- /test/fixture/public/foo.js: -------------------------------------------------------------------------------- 1 | const hello = "world"; 2 | -------------------------------------------------------------------------------- /test/fixture/routes/(route-group)/route-group.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler((event) => { 2 | return "Hi from inside group"; 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixture/routes/500.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler((event) => { 2 | throw createError({ statusCode: 500, statusMessage: "Test Error" }); 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixture/routes/assets/[id].ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(async (event) => { 2 | const serverAssets = useStorage("assets/server"); 3 | 4 | const id = event.context.params.id; 5 | 6 | if (!(await serverAssets.hasItem(id))) { 7 | throw createError({ message: `Asset ${id} not found`, statusCode: 404 }); 8 | } 9 | 10 | const meta = (await serverAssets.getMeta( 11 | event.context.params.id 12 | )) as unknown as { type: string; etag: string; mtime: string }; 13 | 14 | if (meta.type) { 15 | setResponseHeader(event, "content-type", meta.type); 16 | } 17 | 18 | if (meta.etag) { 19 | setResponseHeader(event, "etag", meta.etag); 20 | } 21 | 22 | if (meta.mtime) { 23 | setResponseHeader(event, "last-modified", meta.mtime); 24 | } 25 | 26 | return serverAssets.getItemRaw(event.context.params.id); 27 | }); 28 | -------------------------------------------------------------------------------- /test/fixture/routes/assets/all.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(async (event) => { 2 | const serverAssets = useStorage("assets/server"); 3 | 4 | const keys = await serverAssets.getKeys(); 5 | const items = await Promise.all( 6 | keys.map(async (key) => { 7 | return { 8 | key, 9 | meta: await serverAssets.getMeta(key), 10 | data: await serverAssets.getItem(key).then((r) => 11 | // prettier-ignore 12 | typeof r === "string" ? r.slice(0, 32) : (isPureObject(r) ? r : ``) 13 | ), 14 | }; 15 | }) 16 | ); 17 | 18 | return items; 19 | }); 20 | 21 | function isPureObject(value) { 22 | return ( 23 | value !== null && typeof value === "object" && value.constructor === Object 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /test/fixture/routes/assets/md.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(async (event) => { 2 | const md = await import("../../assets/test.md" as string).then( 3 | (r) => r.default 4 | ); 5 | return md; 6 | }); 7 | -------------------------------------------------------------------------------- /test/fixture/routes/config.ts: -------------------------------------------------------------------------------- 1 | const sharedRuntimeConfig = useRuntimeConfig(); 2 | 3 | export default eventHandler((event) => { 4 | const runtimeConfig = useRuntimeConfig(event); 5 | 6 | return { 7 | runtimeConfig, 8 | sharedRuntimeConfig, 9 | }; 10 | }); 11 | -------------------------------------------------------------------------------- /test/fixture/routes/context.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(async () => { 2 | await Promise.resolve(setTimeout(() => {}, 10)); 3 | return await useTest(); 4 | }); 5 | 6 | function useTest() { 7 | return { 8 | context: { 9 | path: useEvent().path, 10 | }, 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /test/fixture/routes/env/index.dev.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(() => "dev env"); 2 | -------------------------------------------------------------------------------- /test/fixture/routes/env/index.get.prod.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(() => "prod env"); 2 | -------------------------------------------------------------------------------- /test/fixture/routes/error-stack.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(async (event) => { 2 | return { 3 | stack: new Error("testing error").stack.replace(/\\/g, "/"), 4 | }; 5 | }); 6 | -------------------------------------------------------------------------------- /test/fixture/routes/fetch.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(async (event) => { 2 | const nitroApp = useNitroApp(); 3 | return { 4 | $fetch: await fetch("/api/hey").then((r) => r.text()), 5 | "event.fetch": await event.fetch("/api/hey").then((r) => r.text()), 6 | "event.$fetch": await event.$fetch("/api/hey"), 7 | "nitroApp.localFetch": await nitroApp 8 | .localFetch("/api/hey") 9 | .then((r) => r.text()), 10 | "nitroApp.localCall": await nitroApp 11 | .localCall({ url: "/api/hey" }) 12 | .then((r) => r.body), 13 | }; 14 | }); 15 | -------------------------------------------------------------------------------- /test/fixture/routes/file.ts: -------------------------------------------------------------------------------- 1 | import { useStorage } from "nitro/runtime"; 2 | 3 | export default defineEventHandler(async (event) => { 4 | const query = getQuery(event); 5 | const filename = query?.filename || "index.html"; 6 | const serverAsset = await useStorage().getItem(`assets/files/${filename}`); 7 | return serverAsset; 8 | }); 9 | -------------------------------------------------------------------------------- /test/fixture/routes/icon.png.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler((event) => { 2 | setHeader(event, "Content-Type", "image/png"); 3 | return Buffer.from(_base64ToArray(_getLogoBase64())); 4 | }); 5 | 6 | function _getLogoBase64() { 7 | return "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAT9JREFUOE+t081KAlEUwPH/nRkn0pmgZWQgFLTpBYqQVkFUmgTRNnqKkFrZop7Blm4KQq02rVqYr1AILaKPZYIzGY46ExdRNMWP6u4ul/O759x7jggli0fgxQGd0ZYD4liEkh+VXwQ3r3Ik4DV3igBVQNXtnYomQB65rQjoALbndE7DAfZvbe5eq11KJmKSeqxwXnBaZ13ASThAyfHYTJd4sztTGQrYWxjjueQybSjsXFs4bcbQwO6NTSZqkn+vcZgvt9IdGthIW8xPqlxGTOK5MumnRs0jATIgNquTWPYTy1oUivXRAYkklvwsTmlEsxapNWPwL8hHlCU0l67CxbrJi+0yYyqkHgZ8409AQkFD4WprAsMniN9/9u+DXoBEVoI+zlYNDnJ9AE0Bn4Cveu9WHtcabV5r643/GKa/jfM3OT68lZwxK8oAAAAASUVORK5CYII="; 8 | } 9 | 10 | function _base64ToArray(base64: string) { 11 | const str = atob(base64); 12 | const bytes = new Uint8Array(str.length); 13 | for (let i = 0; i < str.length; i++) { 14 | // eslint-disable-next-line unicorn/prefer-code-point 15 | bytes[i] = str.charCodeAt(i); 16 | } 17 | return bytes; 18 | } 19 | -------------------------------------------------------------------------------- /test/fixture/routes/imports.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(() => { 2 | return { 3 | testUtil: testUtil(), 4 | testNestedUtil: testFooUtil() + testBarUtil(), 5 | }; 6 | }); 7 | -------------------------------------------------------------------------------- /test/fixture/routes/json-string.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(() => { 2 | return '{"foo":"bar"}'; 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixture/routes/jsx.tsx: -------------------------------------------------------------------------------- 1 | const h = (tag: string, props: any, ...children: any[]) => { 2 | return `<${tag} ${Object.keys(props || {}) 3 | .map((key) => `${key}="${props[key]}"`) 4 | .join(" ") 5 | .trim()}>${children.join("")}`; 6 | }; 7 | 8 | export default eventHandler(() => { 9 | return

Hello JSX!

; 10 | }); 11 | -------------------------------------------------------------------------------- /test/fixture/routes/modules.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import depA from "@fixture/nitro-dep-a"; 3 | // @ts-ignore 4 | import depB from "@fixture/nitro-dep-b"; 5 | // @ts-ignore 6 | import depLib from "@fixture/nitro-lib"; 7 | // @ts-ignore 8 | import subpathLib from "@fixture/nitro-lib/subpath"; 9 | // @ts-ignore 10 | import extraUtils from "@fixture/nitro-utils/extra"; 11 | // @ts-ignore 12 | import extraUtilsAbsolute from "#fixture-nitro-utils-extra-absolute"; 13 | 14 | export default defineEventHandler(() => { 15 | return { 16 | depA, // expected to all be 1.0.0 17 | depB, // expected to all be 2.0.1 18 | depLib, // expected to all be 2.0.0 19 | subpathLib, // expected to 2.0.0 20 | extraUtils, 21 | extraUtilsAbsolute, 22 | }; 23 | }); 24 | -------------------------------------------------------------------------------- /test/fixture/routes/prerender-custom.html.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler((event) => { 2 | const links = ["/api/hello", "/api/param/foo.json", "/api/param/foo.css"]; 3 | 4 | return ` 5 | 6 | 7 | 8 | Prerendered routes test 9 | 10 | 11 |

Prerendered routes test #2:

12 |
    13 | ${links.map((link) => `
  • ${link}
  • `).join("\n")} 14 |
15 | 16 | `; 17 | }); 18 | -------------------------------------------------------------------------------- /test/fixture/routes/raw.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import sql from "../files/sql.sql"; 3 | 4 | // https://github.com/nitrojs/nitro/issues/2836 5 | import sqlts from "../files/sqlts.sql"; 6 | 7 | export default defineEventHandler(async () => { 8 | return { 9 | sql: sql.trim(), 10 | sqlts: sqlts.trim(), 11 | }; 12 | }); 13 | -------------------------------------------------------------------------------- /test/fixture/routes/rules/[...slug].ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler((event) => event.path); 2 | -------------------------------------------------------------------------------- /test/fixture/routes/static-flags.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(async (event) => { 2 | return { 3 | dev: [process.dev, import.meta.dev], 4 | preset: [process.preset, import.meta.preset], 5 | prerender: [process.prerender, import.meta.prerender], 6 | server: [process.server, import.meta.server], 7 | client: [process.client, import.meta.client], 8 | nitro: [process.nitro, import.meta.nitro], 9 | "versions.nitro": [process.versions.nitro, import.meta.versions.nitro], 10 | "versions?.nitro": [process.versions?.nitro, import.meta.versions?.nitro], 11 | }; 12 | }); 13 | -------------------------------------------------------------------------------- /test/fixture/routes/stream.ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(() => { 2 | const encoder = new TextEncoder(); 3 | const stream = new ReadableStream({ 4 | start(controller) { 5 | controller.enqueue(encoder.encode("nitro")); 6 | controller.enqueue(encoder.encode("is")); 7 | controller.enqueue(encoder.encode("awesome")); 8 | controller.close(); 9 | }, 10 | }); 11 | return stream; 12 | }); 13 | -------------------------------------------------------------------------------- /test/fixture/routes/tasks/[...name].ts: -------------------------------------------------------------------------------- 1 | export default eventHandler(async (event) => { 2 | const name = getRouterParam(event, "name"); 3 | const payload = { ...getQuery(event) }; 4 | const { result } = await runTask(name, { payload }); 5 | return { 6 | name, 7 | payload, 8 | result, 9 | }; 10 | }); 11 | -------------------------------------------------------------------------------- /test/fixture/routes/wait-until.ts: -------------------------------------------------------------------------------- 1 | const timeTakingOperation = async () => { 2 | // console.log("wait-until.ts: timeTakingOperation() start"); 3 | await new Promise((resolve) => setTimeout(resolve, 100)); 4 | // console.log("wait-until.ts: timeTakingOperation() done"); 5 | }; 6 | 7 | export default eventHandler((event) => { 8 | event.waitUntil(timeTakingOperation()); 9 | 10 | return "done"; 11 | }); 12 | -------------------------------------------------------------------------------- /test/fixture/routes/wasm/dynamic-import.ts: -------------------------------------------------------------------------------- 1 | export default defineLazyEventHandler(async () => { 2 | // @ts-ignore 3 | const { sum } = await import("unwasm/examples/sum.wasm").then((r) => 4 | r.default() 5 | ); 6 | return eventHandler(() => { 7 | return `2+3=${sum(2, 3)}`; 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /test/fixture/routes/wasm/static-import.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import init, { sum } from "unwasm/examples/sum.wasm"; 3 | 4 | export default defineLazyEventHandler(async () => { 5 | await init(); 6 | return eventHandler(() => { 7 | return `2+3=${sum(2, 3)}`; 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /test/fixture/server.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | "server-config": true, 3 | }; 4 | -------------------------------------------------------------------------------- /test/fixture/tasks/db/migrate.ts: -------------------------------------------------------------------------------- 1 | export default defineTask({ 2 | meta: { 3 | description: "Run database migrations", 4 | }, 5 | run() { 6 | console.log("Running DB migration task..."); 7 | return { result: "Success" }; 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /test/fixture/tasks/test.ts: -------------------------------------------------------------------------------- 1 | export default defineTask({ 2 | meta: { 3 | description: "task to debug", 4 | }, 5 | async run(taskEvent) { 6 | console.log("test task", taskEvent); 7 | if (taskEvent.payload.wait) { 8 | await new Promise((resolve) => 9 | setTimeout(resolve, Number(taskEvent.payload.wait)) 10 | ); 11 | } 12 | if (taskEvent.payload.error) { 13 | throw new Error("test error"); 14 | } 15 | return { result: taskEvent }; 16 | }, 17 | }); 18 | -------------------------------------------------------------------------------- /test/fixture/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.nitro/types/tsconfig.json", 3 | "compilerOptions": { 4 | "noErrorTruncation": true, 5 | "allowSyntheticDefaultImports": true, 6 | "skipLibCheck": true, 7 | "baseUrl": ".", 8 | "strict": false, 9 | "types": ["@cloudflare/workers-types"], 10 | "paths": { 11 | "nitro/cli": ["../../src/cli"], 12 | "nitro": ["../../src/index"], 13 | "nitro/runtime": ["../../src/runtime"], 14 | "nitro/runtime/internal": ["./src/runtime/internal"], 15 | "nitro/runtime/*": ["../../src/runtime/*"], 16 | "nitro/presets": ["../../src/presets"], 17 | "nitro/presets/*": ["../../src/presets/*"], 18 | "nitro/rollup": ["../../src/rollup"], 19 | "nitro/types": ["../../src/types"], 20 | "nitro/meta": ["../../lib/meta.mjs"], 21 | "nitro/config": ["../../lib/config.mjs"], 22 | "nitro/runtime/meta": ["../../lib/runtime-meta.mjs"], 23 | "#nitro-internal-virtual/*": ["../../src/types/virtual/*"], 24 | "#internal/nitro": ["../../src/runtime/_compat"], 25 | "#internal/nitro/*": ["../../src/runtime/_compat/*"], 26 | "#internal/types/openapi-ts": ["../../src/types/openapi-ts"] 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/fixture/utils/foo/bar/test.ts: -------------------------------------------------------------------------------- 1 | export const testBarUtil = () => 12_345; 2 | -------------------------------------------------------------------------------- /test/fixture/utils/foo/test.ts: -------------------------------------------------------------------------------- 1 | export const testFooUtil = () => 1234; 2 | -------------------------------------------------------------------------------- /test/fixture/utils/test.ts: -------------------------------------------------------------------------------- 1 | export const testUtil = () => 123; 2 | -------------------------------------------------------------------------------- /test/fixture/wrangler.toml: -------------------------------------------------------------------------------- 1 | compatibility_flags = [ "nodejs_compat" ] 2 | -------------------------------------------------------------------------------- /test/presets/bun.test.ts: -------------------------------------------------------------------------------- 1 | import { execa, execaCommandSync } from "execa"; 2 | import { getRandomPort, waitForPort } from "get-port-please"; 3 | import { resolve } from "pathe"; 4 | import { describe } from "vitest"; 5 | import { setupTest, testNitro } from "../tests"; 6 | 7 | const hasBun = 8 | execaCommandSync("bun --version", { stdio: "ignore", reject: false }) 9 | .exitCode === 0; 10 | 11 | describe.runIf(hasBun)("nitro:preset:bun", async () => { 12 | const ctx = await setupTest("bun"); 13 | testNitro(ctx, async () => { 14 | const port = await getRandomPort(); 15 | process.env.PORT = String(port); 16 | const p = execa("bun", [resolve(ctx.outDir, "server/index.mjs")], { 17 | stdio: "inherit", 18 | }); 19 | ctx.server = { 20 | url: `http://127.0.0.1:${port}`, 21 | close: () => { 22 | // p.kill() 23 | }, 24 | } as any; 25 | await waitForPort(port); 26 | return async ({ url, ...opts }) => { 27 | const res = await ctx.fetch(url, opts); 28 | return res; 29 | }; 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /test/presets/cloudflare-module.test.ts: -------------------------------------------------------------------------------- 1 | import { Miniflare } from "miniflare"; 2 | import { resolve } from "pathe"; 3 | import { Response as _Response } from "undici"; 4 | import { describe } from "vitest"; 5 | 6 | import { setupTest, testNitro } from "../tests"; 7 | 8 | describe("nitro:preset:cloudflare-module", async () => { 9 | const ctx = await setupTest("cloudflare-module"); 10 | 11 | testNitro(ctx, () => { 12 | const mf = new Miniflare({ 13 | modules: true, 14 | scriptPath: resolve(ctx.outDir, "server/index.mjs"), 15 | modulesRules: [{ type: "CompiledWasm", include: ["**/*.wasm"] }], 16 | assets: { 17 | directory: resolve(ctx.outDir, "public"), 18 | routerConfig: { has_user_worker: true }, 19 | assetConfig: { 20 | // https://developers.cloudflare.com/workers/static-assets/routing/#routing-configuration 21 | html_handling: "auto-trailing-slash" /* default */, 22 | not_found_handling: "none" /* default */, 23 | }, 24 | }, 25 | compatibilityFlags: [ 26 | "streams_enable_constructors", 27 | "nodejs_compat", 28 | "no_nodejs_compat_v2", 29 | ], 30 | bindings: { ...ctx.env }, 31 | }); 32 | 33 | return async ({ url, headers, method, body }) => { 34 | const res = await mf.dispatchFetch("http://localhost" + url, { 35 | headers: headers || {}, 36 | method: method || "GET", 37 | redirect: "manual", 38 | body, 39 | }); 40 | return res as unknown as Response; 41 | }; 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /test/presets/deno-server.test.ts: -------------------------------------------------------------------------------- 1 | import { execa, execaCommandSync } from "execa"; 2 | import { getRandomPort, waitForPort } from "get-port-please"; 3 | import { describe } from "vitest"; 4 | import { setupTest, testNitro } from "../tests"; 5 | 6 | const hasDeno = 7 | execaCommandSync("deno --version", { stdio: "ignore", reject: false }) 8 | .exitCode === 0; 9 | 10 | describe.runIf(hasDeno)("nitro:preset:deno-server", async () => { 11 | const ctx = await setupTest("deno-server"); 12 | testNitro(ctx, async () => { 13 | const port = await getRandomPort(); 14 | const p = execa("deno", ["task", "start"], { 15 | cwd: ctx.outDir, 16 | // stdio: "inherit", 17 | stdio: "ignore", 18 | env: { 19 | NITRO_PORT: String(port), 20 | NITRO_HOST: "127.0.0.1", 21 | }, 22 | }); 23 | ctx.server = { 24 | url: `http://127.0.0.1:${port}`, 25 | close: () => { 26 | // p.kill() 27 | }, 28 | } as any; 29 | await waitForPort(port, { delay: 1000, retries: 20, host: "127.0.0.1" }); 30 | return async ({ url, ...opts }) => { 31 | const res = await ctx.fetch(url, opts); 32 | return res; 33 | }; 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /test/presets/node.test.ts: -------------------------------------------------------------------------------- 1 | import { existsSync } from "node:fs"; 2 | import { resolve } from "pathe"; 3 | import { isWindows } from "std-env"; 4 | import { describe, expect, it } from "vitest"; 5 | import { setupTest, startServer, testNitro } from "../tests"; 6 | 7 | describe("nitro:preset:node-middleware", async () => { 8 | const ctx = await setupTest("node-middleware"); 9 | 10 | testNitro(ctx, async () => { 11 | const entryPath = resolve(ctx.outDir, "server/index.mjs"); 12 | const { middleware } = await import(entryPath); 13 | 14 | await startServer(ctx, middleware); 15 | 16 | return async ({ url, ...opts }) => { 17 | const res = await ctx.fetch(url, opts); 18 | return res; 19 | }; 20 | }); 21 | 22 | it("should handle nested cached route rules", async () => { 23 | const cached = await ctx.fetch("/rules/_/noncached/cached"); 24 | expect(cached.headers.get("etag")).toBeDefined(); 25 | 26 | const noncached = await ctx.fetch("/rules/_/noncached/noncached"); 27 | expect(noncached.headers.get("etag")).toBeNull(); 28 | 29 | const cached2 = await ctx.fetch("/rules/_/cached/cached"); 30 | expect(cached2.headers.get("etag")).toBeDefined(); 31 | 32 | const noncached2 = await ctx.fetch("/rules/_/cached/noncached"); 33 | expect(noncached2.headers.get("etag")).toBeNull(); 34 | }); 35 | 36 | it.skipIf(isWindows)("should not bundle externals", () => { 37 | const serverNodeModules = resolve(ctx.outDir, "server/node_modules"); 38 | expect( 39 | existsSync(resolve(serverNodeModules, "@fixture/nitro-utils/extra.mjs")) 40 | ).toBe(true); 41 | expect( 42 | existsSync(resolve(serverNodeModules, "@fixture/nitro-utils/extra2.mjs")) 43 | ).toBe(true); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/presets/static.test.ts: -------------------------------------------------------------------------------- 1 | import fsp from "node:fs/promises"; 2 | import { resolve } from "pathe"; 3 | import { describe, expect, it } from "vitest"; 4 | import { setupTest } from "../tests"; 5 | 6 | describe("nitro:preset:static", async () => { 7 | const ctx = await setupTest("static"); 8 | it("should not generate a server folder", async () => { 9 | const contents = await fsp.readdir(resolve(ctx.outDir)); 10 | expect(contents).toMatchInlineSnapshot(` 11 | [ 12 | "nitro.json", 13 | "public", 14 | ] 15 | `); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /test/presets/winterjs.test.ts: -------------------------------------------------------------------------------- 1 | import { execa, execaCommandSync } from "execa"; 2 | import { getRandomPort, waitForPort } from "get-port-please"; 3 | import { describe } from "vitest"; 4 | import { setupTest, testNitro } from "../tests"; 5 | 6 | const hasWasmer = false; 7 | // execaCommandSync("wasmer --version", { stdio: "ignore", reject: false }) 8 | // .exitCode === 0; 9 | 10 | describe.runIf(hasWasmer)("nitro:preset:winterjs", async () => { 11 | const ctx = await setupTest("winterjs"); 12 | testNitro(ctx, async () => { 13 | const port = await getRandomPort(); 14 | const p = execa( 15 | "wasmer", 16 | [ 17 | "run", 18 | "wasmer/winterjs", 19 | "--forward-host-env", 20 | "--net", 21 | "--mapdir", 22 | "app:" + ctx.outDir, 23 | "app/server/index.mjs", 24 | ], 25 | { 26 | stdio: "inherit", 27 | env: { 28 | // ...ctx.env, 29 | RUST_BACKTRACE: "full", 30 | LISTEN_IP: "127.0.0.1", 31 | PORT: String(port), 32 | }, 33 | } 34 | ); 35 | ctx.server = { 36 | url: `http://127.0.0.1:${port}`, 37 | close: () => p.kill(), 38 | } as any; 39 | await waitForPort(port, { delay: 1000, retries: 20, host: "127.0.0.1" }); 40 | return async ({ url, ...opts }) => { 41 | const res = await ctx.fetch(url, opts); 42 | return res; 43 | }; 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/scripts/gen-fixture-types.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from "mlly"; 2 | import { createNitro, writeTypes } from "nitro"; 3 | import { resolve } from "pathe"; 4 | import { scanHandlers } from "../../src/scan"; 5 | 6 | const prepare = async () => { 7 | const fixtureDir = fileURLToPath(new URL("../fixture", import.meta.url).href); 8 | 9 | const nitro = await createNitro({ 10 | rootDir: fixtureDir, 11 | serveStatic: true, 12 | output: { dir: resolve(fixtureDir, ".output", "types") }, 13 | }); 14 | 15 | await scanHandlers(nitro); 16 | await writeTypes(nitro); 17 | }; 18 | 19 | prepare(); 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": false, 4 | "allowSyntheticDefaultImports": true, 5 | "skipLibCheck": true, 6 | "target": "es2022", 7 | "allowJs": true, 8 | "resolveJsonModule": true, 9 | "moduleDetection": "force", 10 | "isolatedModules": true, 11 | "verbatimModuleSyntax": true, 12 | "strict": true, 13 | // "noUncheckedIndexedAccess": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noImplicitOverride": true, 16 | "module": "preserve", 17 | "noEmit": true, 18 | "jsx": "preserve", 19 | "jsxFactory": "h", 20 | "jsxFragmentFactory": "Fragment", 21 | "lib": ["es2022", "webworker", "dom.iterable"], 22 | "types": ["node", "@cloudflare/workers-types"], 23 | "paths": { 24 | "nitro": ["./src/index"], 25 | "nitro/runtime": ["./src/runtime"], 26 | "nitro/runtime/internal": ["./src/runtime/internal"], 27 | "nitro/presets": ["./src/presets"], 28 | "nitro/types": ["./src/types"], 29 | "nitro/config": ["./lib/config.mjs"], 30 | "nitro/meta": ["./lib/meta.mjs"], 31 | "nitro/runtime/meta": ["./lib/runtime-meta.mjs"], 32 | "#nitro-internal-virtual/*": ["./src/types/virtual/*"], 33 | "#internal/nitro": ["./src/runtime"], 34 | "#internal/nitro/*": ["./src/runtime/*"], 35 | "#internal/types/openapi-ts": ["./src/types/openapi-ts"] 36 | } 37 | }, 38 | "include": ["src", "test", "scripts/gen-mirror.ts"], 39 | "exclude": ["dist", "lib", "examples", "playground", "test/fixture"] 40 | } 41 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | testTimeout: 30_000, 6 | coverage: { 7 | reporter: ["text", "clover", "json"], 8 | include: ["src/**/*.ts", "!src/types/**/*.ts"], 9 | }, 10 | include: ["test/**/*.test.ts"], 11 | }, 12 | }); 13 | --------------------------------------------------------------------------------