From c03efd5065251db301c992724b104d003353425d Mon Sep 17 00:00:00 2001 From: Ryan Turnquist Date: Thu, 16 Jan 2025 14:41:47 -0800 Subject: [PATCH] Http verbs (#105) * fix: support HEAD, PATCH and OPTIONS http methods * feat: use GET without body as default HEAD handling * chore: remove console.log and update lint config * fix: update CI to use node 20,22 and windows * fix: collapse default HEAD entry in routes log --- .changeset/short-lions-run.md | 5 + .github/workflows/ci.yml | 6 +- packages/run/.mocharc.json => .mocharc.json | 3 +- eslint.config.mjs | 1 + package.json | 4 +- packages/run/package.json | 6 +- .../all-http-verbs/.marko-run/routes.d.ts | 42 ++++++++ .../__snapshots__/dev.expected.md | 24 +++++ .../__snapshots__/preview.expected.md | 24 +++++ .../all-http-verbs/src/routes/+handler.ts | 14 +++ .../all-http-verbs/src/routes/+page.marko | 1 + .../fixtures/all-http-verbs/test.config.ts | 26 +++++ .../fixtures/all-http-verbs/tsconfig.json | 4 + .../.marko-run/routes.d.ts | 31 ++++++ .../__snapshots__/dev.expected.md | 9 ++ .../__snapshots__/preview.expected.md | 9 ++ .../src/routes/+page.marko | 1 + .../default-get-as-head/test.config.ts | 14 +++ .../default-get-as-head/tsconfig.json | 4 + packages/run/src/adapter/dev-server.ts | 13 ++- packages/run/src/runtime/internal.ts | 17 ++- packages/run/src/runtime/types.ts | 2 +- .../basic-500.expected.router.js | 8 +- .../basic-500.expected.routes.md | 8 +- .../__snapshots__/basic.expected.router.js | 14 ++- .../__snapshots__/basic.expected.routes.md | 16 ++- .../build-routes.expected.router.js | 43 ++++++-- .../build-routes.expected.routes.md | 83 ++++++++++----- .../build-routes.expected.routetypes.d.ts | 2 +- .../fixtures/build-routes/routes.txt | 2 +- .../flat-routes.expected.router.js | 81 ++++++++++++-- .../flat-routes.expected.routes.md | 100 ++++++++++++++---- .../flat-routes.expected.routetypes.d.ts | 12 +-- .../__tests__/fixtures/flat-routes/routes.txt | 2 +- .../multiple-sources.expected.router.js | 14 ++- .../multiple-sources.expected.routes.md | 16 ++- .../nested-dynamic.expected.router.js | 47 +++++++- .../nested-dynamic.expected.routes.md | 8 +- .../optional-dynamic.expected.router.js | 30 +++++- .../optional-dynamic.expected.routes.md | 32 ++++-- .../optional-static.expected.router.js | 55 ++++++++-- .../optional-static.expected.routes.md | 64 ++++++++--- .../optional-types.expected.router.js | 62 ++++++++++- .../optional-types.expected.routes.md | 32 ++++-- .../param-optional-rest.expected.router.js | 21 +++- .../param-optional-rest.expected.routes.md | 16 ++- .../uri-encoded.expected.router.js | 19 +++- .../uri-encoded.expected.routes.md | 8 +- .../__tests__/{main.test.cts => main.test.ts} | 7 +- packages/run/src/vite/codegen/index.ts | 94 +++++++++------- packages/run/src/vite/constants.ts | 11 +- packages/run/src/vite/plugin.ts | 1 - packages/run/src/vite/routes/builder.ts | 2 +- packages/run/src/vite/utils/log.ts | 40 ++++--- packages/run/src/vite/utils/route.ts | 30 ++++-- 55 files changed, 1008 insertions(+), 232 deletions(-) create mode 100644 .changeset/short-lions-run.md rename packages/run/.mocharc.json => .mocharc.json (52%) create mode 100644 packages/run/src/__tests__/fixtures/all-http-verbs/.marko-run/routes.d.ts create mode 100644 packages/run/src/__tests__/fixtures/all-http-verbs/__snapshots__/dev.expected.md create mode 100644 packages/run/src/__tests__/fixtures/all-http-verbs/__snapshots__/preview.expected.md create mode 100644 packages/run/src/__tests__/fixtures/all-http-verbs/src/routes/+handler.ts create mode 100644 packages/run/src/__tests__/fixtures/all-http-verbs/src/routes/+page.marko create mode 100644 packages/run/src/__tests__/fixtures/all-http-verbs/test.config.ts create mode 100644 packages/run/src/__tests__/fixtures/all-http-verbs/tsconfig.json create mode 100644 packages/run/src/__tests__/fixtures/default-get-as-head/.marko-run/routes.d.ts create mode 100644 packages/run/src/__tests__/fixtures/default-get-as-head/__snapshots__/dev.expected.md create mode 100644 packages/run/src/__tests__/fixtures/default-get-as-head/__snapshots__/preview.expected.md create mode 100644 packages/run/src/__tests__/fixtures/default-get-as-head/src/routes/+page.marko create mode 100644 packages/run/src/__tests__/fixtures/default-get-as-head/test.config.ts create mode 100644 packages/run/src/__tests__/fixtures/default-get-as-head/tsconfig.json rename packages/run/src/vite/__tests__/{main.test.cts => main.test.ts} (96%) diff --git a/.changeset/short-lions-run.md b/.changeset/short-lions-run.md new file mode 100644 index 0000000..fd65d1a --- /dev/null +++ b/.changeset/short-lions-run.md @@ -0,0 +1,5 @@ +--- +"@marko/run": patch +--- + +Support PATCH, OPTIONS and HEAD http methods diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cb47f80..03317ac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,13 +28,13 @@ jobs: - name: Lint Code run: npm run @ci:lint test: - runs-on: ubuntu-latest - name: "test: node@${{ matrix.node }}" + runs-on: ${{ matrix.os }} + name: "test: node@${{ matrix.node }} (${{ matrix.os }})" strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest] - node: [18, 20] + node: [20, 22] steps: - name: Checkout code uses: actions/checkout@v4 diff --git a/packages/run/.mocharc.json b/.mocharc.json similarity index 52% rename from packages/run/.mocharc.json rename to .mocharc.json index 6ecac1c..9c43f0e 100644 --- a/packages/run/.mocharc.json +++ b/.mocharc.json @@ -2,6 +2,7 @@ "exit": true, "timeout": 20000, "extension": ["js", "ts", "marko"], - "watchFiles": ["src/**/*.ts", "src/**/*.marko"], + "spec": ["packages/**/src/**/*.test.@(js|ts)"], + "watchFiles": ["packages/**/src/**/*.@(ts|marko)"], "require": ["tsx", "mocha-snap"] } diff --git a/eslint.config.mjs b/eslint.config.mjs index 1909d81..74c9c62 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -13,6 +13,7 @@ export default tseslint.config( "**/.app", "**/.cache", "**/.marko-run", + "**/.netlify", "**/__snapshots__", "**/*.marko.js", "**/*actual*", diff --git a/package.json b/package.json index 8df77fe..a3c5659 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,8 @@ "lint": "eslint --format unix . && prettier . --check --with-node-modules --log-level=warn", "prepare": "husky", "report": "open ./coverage/lcov-report/index.html", - "test": "npm run test -w packages -w packages/adapters --if-present", - "test:update": "UPDATE_EXPECTATIONS=1 mocha --update" + "test": "cross-env NODE_ENV=test NODE_OPTIONS='$NODE_OPTIONS --import tsx' mocha", + "test:update": "cross-env UPDATE_EXPECTATIONS=1 npm test -- --update" }, "devDependencies": { "@changesets/cli": "^2.27.11", diff --git a/packages/run/package.json b/packages/run/package.json index ad391bc..629d6be 100644 --- a/packages/run/package.json +++ b/packages/run/package.json @@ -29,11 +29,7 @@ "dist" ], "scripts": { - "build": "rm -rf ./dist && tsc -b && tsx scripts/build.ts", - "test": "cross-env NODE_ENV=test NODE_OPTIONS='$NODE_OPTIONS --import tsx' mocha \"./src/**/__tests__/*.test.?(c)ts\"", - "test:inspect": "npm test -- --inspect", - "test:update": "npm test -- --update", - "test:watch": "npm test -- --watch" + "build": "rm -rf ./dist && tsc -b && tsx scripts/build.ts" }, "dependencies": { "@marko/run-explorer": "^0.1.2", diff --git a/packages/run/src/__tests__/fixtures/all-http-verbs/.marko-run/routes.d.ts b/packages/run/src/__tests__/fixtures/all-http-verbs/.marko-run/routes.d.ts new file mode 100644 index 0000000..4ab2776 --- /dev/null +++ b/packages/run/src/__tests__/fixtures/all-http-verbs/.marko-run/routes.d.ts @@ -0,0 +1,42 @@ +/* + WARNING: This file is automatically generated and any changes made to it will be overwritten without warning. + Do NOT manually edit this file or your changes will be lost. +*/ + +import { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform } from "@marko/run/namespace"; +import type * as Run from "@marko/run"; + + +declare module "@marko/run" { + interface AppData extends Run.DefineApp<{ + routes: { + "/": Routes["/"]; + } + }> {} +} + +declare module "../src/routes/+handler" { + namespace MarkoRun { + export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform }; + export type Route = Run.Routes["/"]; + export type Context = Run.MultiRouteContext; + export type Handler = Run.HandlerLike; + /** @deprecated use `((context, next) => { ... }) satisfies MarkoRun.Handler` instead */ + export const route: Run.HandlerTypeFn; + } +} + +declare module "../src/routes/+page.marko" { + namespace MarkoRun { + export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform }; + export type Route = Run.Routes["/"]; + export type Context = Run.MultiRouteContext & Marko.Global; + export type Handler = Run.HandlerLike; + /** @deprecated use `((context, next) => { ... }) satisfies MarkoRun.Handler` instead */ + export const route: Run.HandlerTypeFn; + } +} + +type Routes = { + "/": { verb: "get" | "post"; }; +} diff --git a/packages/run/src/__tests__/fixtures/all-http-verbs/__snapshots__/dev.expected.md b/packages/run/src/__tests__/fixtures/all-http-verbs/__snapshots__/dev.expected.md new file mode 100644 index 0000000..339d0bf --- /dev/null +++ b/packages/run/src/__tests__/fixtures/all-http-verbs/__snapshots__/dev.expected.md @@ -0,0 +1,24 @@ +# Loading + +```html +page: GET / +``` + +# Step 0 +()=>assertNoBody("HEAD") + +# Step 1 +()=>assertBody("POST") + +# Step 2 +()=>assertBody("PUT") + +# Step 3 +()=>assertBody("DELETE") + +# Step 4 +()=>assertBody("PATCH") + +# Step 5 +()=>assertBody("OPTIONS") + diff --git a/packages/run/src/__tests__/fixtures/all-http-verbs/__snapshots__/preview.expected.md b/packages/run/src/__tests__/fixtures/all-http-verbs/__snapshots__/preview.expected.md new file mode 100644 index 0000000..339d0bf --- /dev/null +++ b/packages/run/src/__tests__/fixtures/all-http-verbs/__snapshots__/preview.expected.md @@ -0,0 +1,24 @@ +# Loading + +```html +page: GET / +``` + +# Step 0 +()=>assertNoBody("HEAD") + +# Step 1 +()=>assertBody("POST") + +# Step 2 +()=>assertBody("PUT") + +# Step 3 +()=>assertBody("DELETE") + +# Step 4 +()=>assertBody("PATCH") + +# Step 5 +()=>assertBody("OPTIONS") + diff --git a/packages/run/src/__tests__/fixtures/all-http-verbs/src/routes/+handler.ts b/packages/run/src/__tests__/fixtures/all-http-verbs/src/routes/+handler.ts new file mode 100644 index 0000000..69df025 --- /dev/null +++ b/packages/run/src/__tests__/fixtures/all-http-verbs/src/routes/+handler.ts @@ -0,0 +1,14 @@ +const handler: MarkoRun.Handler = (context) => { + return new Response( + `handler: ${context.request.method} ${context.url.pathname}`, + { headers: { "content-type": "text/plain" } }, + ); +}; + +export { handler as POST } +export { handler as PUT } +export { handler as DELETE } +export { handler as PATCH } +export { handler as HEAD } +export { handler as OPTIONS } + diff --git a/packages/run/src/__tests__/fixtures/all-http-verbs/src/routes/+page.marko b/packages/run/src/__tests__/fixtures/all-http-verbs/src/routes/+page.marko new file mode 100644 index 0000000..0d172be --- /dev/null +++ b/packages/run/src/__tests__/fixtures/all-http-verbs/src/routes/+page.marko @@ -0,0 +1 @@ +-- page: GET ${$global.url.pathname} \ No newline at end of file diff --git a/packages/run/src/__tests__/fixtures/all-http-verbs/test.config.ts b/packages/run/src/__tests__/fixtures/all-http-verbs/test.config.ts new file mode 100644 index 0000000..5f1dd6a --- /dev/null +++ b/packages/run/src/__tests__/fixtures/all-http-verbs/test.config.ts @@ -0,0 +1,26 @@ +import assert from "assert"; + +export const steps = [ + () => assertNoBody('HEAD'), + () => assertBody('POST'), + () => assertBody('PUT'), + () => assertBody('DELETE'), + () => assertBody('PATCH'), + () => assertBody('OPTIONS') +] + +async function assertBody(method: string) { + const url = new URL(page.url()); + const response = await page.request.fetch(url.href, { method }); + assert.equal(response.ok(), true, `Response for ${method} is not ok`); + const body = await response.text(); + assert.equal(body, `handler: ${method} ${url.pathname}`, `Response for ${method} has an unexpected body: "${body}"`); +} + +async function assertNoBody(method: string) { + const url = new URL(page.url()); + const response = await page.request.fetch(url.href, { method }); + assert.equal(response.ok(), true, `Response for ${method} is not ok`); + const body = await response.text(); + assert.equal(body, '', `Response for ${method} has a non-empty body: "${body}"`); +} \ No newline at end of file diff --git a/packages/run/src/__tests__/fixtures/all-http-verbs/tsconfig.json b/packages/run/src/__tests__/fixtures/all-http-verbs/tsconfig.json new file mode 100644 index 0000000..03ff88a --- /dev/null +++ b/packages/run/src/__tests__/fixtures/all-http-verbs/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig-base.json", + "include": ["src/**/*", ".marko-run/*"], +} \ No newline at end of file diff --git a/packages/run/src/__tests__/fixtures/default-get-as-head/.marko-run/routes.d.ts b/packages/run/src/__tests__/fixtures/default-get-as-head/.marko-run/routes.d.ts new file mode 100644 index 0000000..e9767bc --- /dev/null +++ b/packages/run/src/__tests__/fixtures/default-get-as-head/.marko-run/routes.d.ts @@ -0,0 +1,31 @@ +/* + WARNING: This file is automatically generated and any changes made to it will be overwritten without warning. + Do NOT manually edit this file or your changes will be lost. +*/ + +import { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform } from "@marko/run/namespace"; +import type * as Run from "@marko/run"; + + +declare module "@marko/run" { + interface AppData extends Run.DefineApp<{ + routes: { + "/": Routes["/"]; + } + }> {} +} + +declare module "../src/routes/+page.marko" { + namespace MarkoRun { + export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform }; + export type Route = Run.Routes["/"]; + export type Context = Run.MultiRouteContext & Marko.Global; + export type Handler = Run.HandlerLike; + /** @deprecated use `((context, next) => { ... }) satisfies MarkoRun.Handler` instead */ + export const route: Run.HandlerTypeFn; + } +} + +type Routes = { + "/": { verb: "get"; }; +} diff --git a/packages/run/src/__tests__/fixtures/default-get-as-head/__snapshots__/dev.expected.md b/packages/run/src/__tests__/fixtures/default-get-as-head/__snapshots__/dev.expected.md new file mode 100644 index 0000000..62ffaac --- /dev/null +++ b/packages/run/src/__tests__/fixtures/default-get-as-head/__snapshots__/dev.expected.md @@ -0,0 +1,9 @@ +# Loading + +```html +page: GET / +``` + +# Step 0 +()=>requestHead() + diff --git a/packages/run/src/__tests__/fixtures/default-get-as-head/__snapshots__/preview.expected.md b/packages/run/src/__tests__/fixtures/default-get-as-head/__snapshots__/preview.expected.md new file mode 100644 index 0000000..62ffaac --- /dev/null +++ b/packages/run/src/__tests__/fixtures/default-get-as-head/__snapshots__/preview.expected.md @@ -0,0 +1,9 @@ +# Loading + +```html +page: GET / +``` + +# Step 0 +()=>requestHead() + diff --git a/packages/run/src/__tests__/fixtures/default-get-as-head/src/routes/+page.marko b/packages/run/src/__tests__/fixtures/default-get-as-head/src/routes/+page.marko new file mode 100644 index 0000000..0d172be --- /dev/null +++ b/packages/run/src/__tests__/fixtures/default-get-as-head/src/routes/+page.marko @@ -0,0 +1 @@ +-- page: GET ${$global.url.pathname} \ No newline at end of file diff --git a/packages/run/src/__tests__/fixtures/default-get-as-head/test.config.ts b/packages/run/src/__tests__/fixtures/default-get-as-head/test.config.ts new file mode 100644 index 0000000..38fe31d --- /dev/null +++ b/packages/run/src/__tests__/fixtures/default-get-as-head/test.config.ts @@ -0,0 +1,14 @@ +import assert from "assert"; + +export const steps = [ + () => requestHead(), +] + +async function requestHead() { + const url = new URL(page.url()); + const response = await page.request.fetch(url.href, { method: 'HEAD' }); + const headers = response.headers(); + assert.equal(response.ok(), true); + assert.match(headers['content-type'], /text\/html/); + assert.equal(await response.body(), ''); +} \ No newline at end of file diff --git a/packages/run/src/__tests__/fixtures/default-get-as-head/tsconfig.json b/packages/run/src/__tests__/fixtures/default-get-as-head/tsconfig.json new file mode 100644 index 0000000..03ff88a --- /dev/null +++ b/packages/run/src/__tests__/fixtures/default-get-as-head/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig-base.json", + "include": ["src/**/*", ".marko-run/*"], +} \ No newline at end of file diff --git a/packages/run/src/adapter/dev-server.ts b/packages/run/src/adapter/dev-server.ts index 4acc8b5..c3c7fe1 100644 --- a/packages/run/src/adapter/dev-server.ts +++ b/packages/run/src/adapter/dev-server.ts @@ -36,11 +36,20 @@ declare global { export async function createViteDevServer( config?: InlineConfig, ): Promise { - const devServer = await createServer({ + const finalConfig = { ...config, appType: "custom", server: { ...config?.server, middlewareMode: true }, - }); + } satisfies InlineConfig; + + const { cors } = finalConfig.server; + if (cors === undefined) { + finalConfig.server.cors = { preflightContinue: true }; + } else if (typeof cors === "object") { + cors.preflightContinue ??= true; + } + + const devServer = await createServer(finalConfig); getDevGlobal().addDevServer(devServer); diff --git a/packages/run/src/runtime/internal.ts b/packages/run/src/runtime/internal.ts index 45ab938..c9694ea 100644 --- a/packages/run/src/runtime/internal.ts +++ b/packages/run/src/runtime/internal.ts @@ -1,5 +1,6 @@ import type { AnyRoute, + Awaitable, Context, InputObject, MultiRouteContext, @@ -49,7 +50,7 @@ export function createContext( route: route.path, serializedGlobals, } - : { + : ({ request, url, platform, @@ -57,7 +58,7 @@ export function createContext( params: {}, route: "", serializedGlobals, - }; + } as unknown as Context); let input: InputObject | undefined; return [ @@ -161,6 +162,18 @@ export function normalize( return passthrough; } +export function stripResponseBodySync(response: Response): Response { + return response.body ? new Response(null, response) : response; +} + +export function stripResponseBody( + response: Awaitable, +): Awaitable { + return "then" in response + ? response.then(stripResponseBodySync) + : stripResponseBodySync(response); +} + export function passthrough() {} export function noContent() { diff --git a/packages/run/src/runtime/types.ts b/packages/run/src/runtime/types.ts index f47bb72..5bae493 100644 --- a/packages/run/src/runtime/types.ts +++ b/packages/run/src/runtime/types.ts @@ -1,4 +1,4 @@ -type Awaitable = Promise | T; +export type Awaitable = Promise | T; type OneOrMany = T | T[]; type NoParams = {}; type AllKeys = T extends T ? keyof T : never; diff --git a/packages/run/src/vite/__tests__/fixtures/basic-500/__snapshots__/basic-500.expected.router.js b/packages/run/src/vite/__tests__/fixtures/basic-500/__snapshots__/basic-500.expected.router.js index adde75b..aa6b71a 100644 --- a/packages/run/src/vite/__tests__/fixtures/basic-500/__snapshots__/basic-500.expected.router.js +++ b/packages/run/src/vite/__tests__/fixtures/basic-500/__snapshots__/basic-500.expected.router.js @@ -1,6 +1,6 @@ // @marko/run/router import { NotHandled, NotMatched, createContext } from 'virtual:marko-run/runtime/internal'; -import { get1 } from 'virtual:marko-run/__marko-run__route.js'; +import { get1, head1 } from 'virtual:marko-run/__marko-run__route.js'; import page500 from './.marko/route.500.marko?marko-server-entry'; const page500ResponseInit = { @@ -23,6 +23,12 @@ export function match(method, pathname) { if (len === 1) return { handler: get1, params: {}, meta: {}, path: '/' }; // / return null; } + case 'HEAD': + case 'head': { + const len = pathname.length; + if (len === 1) return { handler: head1, params: {}, meta: {}, path: '/' }; // / + return null; + } } return null; } diff --git a/packages/run/src/vite/__tests__/fixtures/basic-500/__snapshots__/basic-500.expected.routes.md b/packages/run/src/vite/__tests__/fixtures/basic-500/__snapshots__/basic-500.expected.routes.md index ce4bc87..6c8d829 100644 --- a/packages/run/src/vite/__tests__/fixtures/basic-500/__snapshots__/basic-500.expected.routes.md +++ b/packages/run/src/vite/__tests__/fixtures/basic-500/__snapshots__/basic-500.expected.routes.md @@ -16,12 +16,16 @@ import Page from '../src/routes/+page.marko'; ### Handler ```js // virtual:marko-run/__marko-run__route.js -import { pageResponse } from 'virtual:marko-run/runtime/internal'; +import { pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import page from './.marko/route.marko?marko-server-entry'; -export async function get1(context, buildInput) { +export function get1(context, buildInput) { return pageResponse(page, buildInput()); } + +export function head1(context, buildInput) { + return stripResponseBody(get1(context, buildInput)); +} ``` diff --git a/packages/run/src/vite/__tests__/fixtures/basic/__snapshots__/basic.expected.router.js b/packages/run/src/vite/__tests__/fixtures/basic/__snapshots__/basic.expected.router.js index 1e7b8ad..bcc8d98 100644 --- a/packages/run/src/vite/__tests__/fixtures/basic/__snapshots__/basic.expected.router.js +++ b/packages/run/src/vite/__tests__/fixtures/basic/__snapshots__/basic.expected.router.js @@ -1,7 +1,7 @@ // @marko/run/router import { NotHandled, NotMatched, createContext } from 'virtual:marko-run/runtime/internal'; -import { get1 } from 'virtual:marko-run/__marko-run__route.js'; -import { get2 } from 'virtual:marko-run/__marko-run__route.fOoBaR.js'; +import { get1, head1 } from 'virtual:marko-run/__marko-run__route.js'; +import { get2, head2 } from 'virtual:marko-run/__marko-run__route.fOoBaR.js'; globalThis.__marko_run__ = { match, fetch, invoke }; @@ -22,6 +22,16 @@ export function match(method, pathname) { } return null; } + case 'HEAD': + case 'head': { + const len = pathname.length; + if (len === 1) return { handler: head1, params: {}, meta: {}, path: '/' }; // / + const i1 = pathname.indexOf('/', 1) + 1; + if (!i1 || i1 === len) { + if (pathname.slice(1, i1 ? -1 : len) === 'fOoBaR') return { handler: head2, params: {}, meta: {}, path: '/fOoBaR' }; // /fOoBaR + } + return null; + } } return null; } diff --git a/packages/run/src/vite/__tests__/fixtures/basic/__snapshots__/basic.expected.routes.md b/packages/run/src/vite/__tests__/fixtures/basic/__snapshots__/basic.expected.routes.md index 3a431a7..7c6226e 100644 --- a/packages/run/src/vite/__tests__/fixtures/basic/__snapshots__/basic.expected.routes.md +++ b/packages/run/src/vite/__tests__/fixtures/basic/__snapshots__/basic.expected.routes.md @@ -6,12 +6,16 @@ ### Handler ```js // virtual:marko-run/__marko-run__route.js -import { pageResponse } from 'virtual:marko-run/runtime/internal'; +import { pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import page from './src/routes/+page.marko?marko-server-entry'; -export async function get1(context, buildInput) { +export function get1(context, buildInput) { return pageResponse(page, buildInput()); } + +export function head1(context, buildInput) { + return stripResponseBody(get1(context, buildInput)); +} ``` --- ## Route `/fOoBaR` @@ -20,10 +24,14 @@ export async function get1(context, buildInput) { ### Handler ```js // virtual:marko-run/__marko-run__route.fOoBaR.js -import { pageResponse } from 'virtual:marko-run/runtime/internal'; +import { pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import page from './src/routes/fOoBaR/+page.marko?marko-server-entry'; -export async function get2(context, buildInput) { +export function get2(context, buildInput) { return pageResponse(page, buildInput()); } + +export function head2(context, buildInput) { + return stripResponseBody(get2(context, buildInput)); +} ``` diff --git a/packages/run/src/vite/__tests__/fixtures/build-routes/__snapshots__/build-routes.expected.router.js b/packages/run/src/vite/__tests__/fixtures/build-routes/__snapshots__/build-routes.expected.router.js index d933878..ddc1277 100644 --- a/packages/run/src/vite/__tests__/fixtures/build-routes/__snapshots__/build-routes.expected.router.js +++ b/packages/run/src/vite/__tests__/fixtures/build-routes/__snapshots__/build-routes.expected.router.js @@ -1,12 +1,12 @@ // @marko/run/router import { NotHandled, NotMatched, createContext } from 'virtual:marko-run/runtime/internal'; -import { get1 } from 'virtual:marko-run/__marko-run__route._protected._home.js'; -import { get2, post2, meta2 } from 'virtual:marko-run/__marko-run__route._protected._home.new.js'; -import { get3, put3, post3, delete3 } from 'virtual:marko-run/__marko-run__route._protected._home.notes.$id.js'; -import { put4, post4, delete4, meta4 } from 'virtual:marko-run/__marko-run__route._protected._home.notes.$id.comments.js'; -import { get5 } from 'virtual:marko-run/__marko-run__route.callback.oauth2.js'; -import { get6 } from 'virtual:marko-run/__marko-run__route.my.js'; -import { get7 } from 'virtual:marko-run/__marko-run__route.$$match.js'; +import { get1, head1 } from 'virtual:marko-run/__marko-run__route._protected._home.js'; +import { get2, head2, post2, meta2 } from 'virtual:marko-run/__marko-run__route._protected._home.new.js'; +import { get3, head3, post3, put3, delete3 } from 'virtual:marko-run/__marko-run__route._protected._home.notes.$id.js'; +import { post4, put4, delete4, meta4 } from 'virtual:marko-run/__marko-run__route._protected._home.notes.$id.comments.js'; +import { get5, head5 } from 'virtual:marko-run/__marko-run__route.callback.oauth2.js'; +import { get6, head6 } from 'virtual:marko-run/__marko-run__route.my.js'; +import { get7, head7 } from 'virtual:marko-run/__marko-run__route.$$match.js'; import page404 from './.marko/route.404.marko?marko-server-entry'; import page500 from './.marko/route.500.marko?marko-server-entry'; @@ -58,6 +58,35 @@ export function match(method, pathname) { } return { handler: get7, params: { match: pathname.slice(1) }, meta: {}, path: '/:match*' }; // /$$match } + case 'HEAD': + case 'head': { + const len = pathname.length; + if (len === 1) return { handler: head1, params: {}, meta: {}, path: '/' }; // / + const i1 = pathname.indexOf('/', 1) + 1; + if (!i1 || i1 === len) { + switch (pathname.slice(1, i1 ? -1 : len)) { + case 'new': return { handler: head2, params: {}, meta: meta2, path: '/new' }; // /new + case 'my': return { handler: head6, params: {}, meta: {}, path: '/my' }; // /my + } + } else { + switch (pathname.slice(1, i1 - 1)) { + case 'notes': { + const i2 = pathname.indexOf('/', 7) + 1; + if (!i2 || i2 === len) { + const s2 = decodeURIComponent(pathname.slice(7, i2 ? -1 : len)); + if (s2) return { handler: head3, params: { id: s2 }, meta: {}, path: '/notes/:id' }; // /notes/$id + } + } break; + case 'callback': { + const i2 = pathname.indexOf('/', 10) + 1; + if (!i2 || i2 === len) { + if (pathname.slice(10, i2 ? -1 : len) === 'oauth2') return { handler: head5, params: {}, meta: {}, path: '/callback/oauth2' }; // /callback/oauth2 + } + } break; + } + } + return { handler: head7, params: { match: pathname.slice(1) }, meta: {}, path: '/:match*' }; // /$$match + } case 'POST': case 'post': { const len = pathname.length; diff --git a/packages/run/src/vite/__tests__/fixtures/build-routes/__snapshots__/build-routes.expected.routes.md b/packages/run/src/vite/__tests__/fixtures/build-routes/__snapshots__/build-routes.expected.routes.md index f46c2a5..ef08787 100644 --- a/packages/run/src/vite/__tests__/fixtures/build-routes/__snapshots__/build-routes.expected.routes.md +++ b/packages/run/src/vite/__tests__/fixtures/build-routes/__snapshots__/build-routes.expected.routes.md @@ -35,16 +35,20 @@ import Page from '../../../src/routes/_protected/_home/+page.marko'; ### Handler ```js // virtual:marko-run/__marko-run__route._protected._home.js -import { call, pageResponse } from 'virtual:marko-run/runtime/internal'; +import { call, pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import { mware4, mware5, mware7 } from 'virtual:marko-run/__marko-run__middleware.js'; import page from './.marko/_protected/_home/route.marko?marko-server-entry'; -export async function get1(context, buildInput) { +export function get1(context, buildInput) { const __page = () => pageResponse(page, buildInput()); const __mware7 = () => call(mware7, __page, context); const __mware5 = () => call(mware5, __mware7, context); return call(mware4, __mware5, context); } + +export function head1(context, buildInput) { + return stripResponseBody(get1(context, buildInput)); +} ``` --- ## Route `/_protected/_home/new` @@ -66,7 +70,7 @@ import Page from '../../../../src/routes/_protected/_home/new/+page.marko'; ### Handler ```js // virtual:marko-run/__marko-run__route._protected._home.new.js -import { normalize, call, noContent, pageResponse } from 'virtual:marko-run/runtime/internal'; +import { normalize, call, noContent, pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import { mware4, mware5, mware7 } from 'virtual:marko-run/__marko-run__middleware.js'; import { POST } from './src/routes/_protected/_home/new/+handler.post.ts'; import page from './.marko/_protected/_home/new/route.marko?marko-server-entry'; @@ -74,14 +78,18 @@ export { default as meta2 } from './src/routes/_protected/_home/new/+meta.json'; const postHandler = normalize(POST); -export async function get2(context, buildInput) { +export function get2(context, buildInput) { const __page = () => pageResponse(page, buildInput()); const __mware7 = () => call(mware7, __page, context); const __mware5 = () => call(mware5, __mware7, context); return call(mware4, __mware5, context); } -export async function post2(context, buildInput) { +export function head2(context, buildInput) { + return stripResponseBody(get2(context, buildInput)); +} + +export function post2(context) { const __postHandler = () => call(postHandler, noContent, context); const __mware7 = () => call(mware7, __postHandler, context); const __mware5 = () => call(mware5, __mware7, context); @@ -108,7 +116,7 @@ import Page from '../../../../../src/routes/_protected/_home/notes/$id/+page.mar ### Handler ```js // virtual:marko-run/__marko-run__route._protected._home.notes.$id.js -import { normalize, call, noContent, pageResponse } from 'virtual:marko-run/runtime/internal'; +import { normalize, call, noContent, pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import { mware4, mware5, mware7, mware13 } from 'virtual:marko-run/__marko-run__middleware.js'; import { PUT, POST, DELETE } from './src/routes/_protected/_home/notes/$id/+handler.put_post_delete.ts'; import page from './.marko/_protected/_home/notes/$id/route.marko?marko-server-entry'; @@ -117,7 +125,7 @@ const putHandler = normalize(PUT); const postHandler = normalize(POST); const deleteHandler = normalize(DELETE); -export async function get3(context, buildInput) { +export function get3(context, buildInput) { const __page = () => pageResponse(page, buildInput()); const __mware13 = () => call(mware13, __page, context); const __mware7 = () => call(mware7, __mware13, context); @@ -125,23 +133,27 @@ export async function get3(context, buildInput) { return call(mware4, __mware5, context); } -export async function put3(context, buildInput) { - const __putHandler = () => call(putHandler, noContent, context); - const __mware13 = () => call(mware13, __putHandler, context); +export function head3(context, buildInput) { + return stripResponseBody(get3(context, buildInput)); +} + +export function post3(context) { + const __postHandler = () => call(postHandler, noContent, context); + const __mware13 = () => call(mware13, __postHandler, context); const __mware7 = () => call(mware7, __mware13, context); const __mware5 = () => call(mware5, __mware7, context); return call(mware4, __mware5, context); } -export async function post3(context, buildInput) { - const __postHandler = () => call(postHandler, noContent, context); - const __mware13 = () => call(mware13, __postHandler, context); +export function put3(context) { + const __putHandler = () => call(putHandler, noContent, context); + const __mware13 = () => call(mware13, __putHandler, context); const __mware7 = () => call(mware7, __mware13, context); const __mware5 = () => call(mware5, __mware7, context); return call(mware4, __mware5, context); } -export async function delete3(context, buildInput) { +export function delete3(context) { const __deleteHandler = () => call(deleteHandler, noContent, context); const __mware13 = () => call(mware13, __deleteHandler, context); const __mware7 = () => call(mware7, __mware13, context); @@ -165,23 +177,23 @@ const putHandler = normalize(PUT); const postHandler = normalize(POST); const deleteHandler = normalize(DELETE); -export async function put4(context) { - const __putHandler = () => call(putHandler, noContent, context); - const __mware13 = () => call(mware13, __putHandler, context); +export function post4(context) { + const __postHandler = () => call(postHandler, noContent, context); + const __mware13 = () => call(mware13, __postHandler, context); const __mware7 = () => call(mware7, __mware13, context); const __mware5 = () => call(mware5, __mware7, context); return call(mware4, __mware5, context); } -export async function post4(context) { - const __postHandler = () => call(postHandler, noContent, context); - const __mware13 = () => call(mware13, __postHandler, context); +export function put4(context) { + const __putHandler = () => call(putHandler, noContent, context); + const __mware13 = () => call(mware13, __putHandler, context); const __mware7 = () => call(mware7, __mware13, context); const __mware5 = () => call(mware5, __mware7, context); return call(mware4, __mware5, context); } -export async function delete4(context) { +export function delete4(context) { const __deleteHandler = () => call(deleteHandler, noContent, context); const __mware13 = () => call(mware13, __deleteHandler, context); const __mware7 = () => call(mware7, __mware13, context); @@ -196,16 +208,20 @@ export async function delete4(context) { ### Handler ```js // virtual:marko-run/__marko-run__route.callback.oauth2.js -import { normalize, call, noContent } from 'virtual:marko-run/runtime/internal'; +import { normalize, call, noContent, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import { mware4 } from 'virtual:marko-run/__marko-run__middleware.js'; import { GET } from './src/routes/callback/oauth2/+handler.get.ts'; const getHandler = normalize(GET); -export async function get5(context) { +export function get5(context) { const __getHandler = () => call(getHandler, noContent, context); return call(mware4, __getHandler, context); } + +export function head5(context) { + return stripResponseBody(get5(context)); +} ``` --- ## Route `/my` @@ -224,18 +240,25 @@ import Page from '../../src/routes/my/+page.marko'; ### Handler ```js // virtual:marko-run/__marko-run__route.my.js -import { normalize, call, pageResponse } from 'virtual:marko-run/runtime/internal'; +import { normalize, call, pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import { mware4 } from 'virtual:marko-run/__marko-run__middleware.js'; -import { GET } from './src/routes/my/+handler.get.ts'; +import { GET, HEAD } from './src/routes/my/+handler.get_head.ts'; import page from './.marko/my/route.marko?marko-server-entry'; const getHandler = normalize(GET); +const headHandler = normalize(HEAD); -export async function get6(context, buildInput) { +export function get6(context, buildInput) { const __page = () => pageResponse(page, buildInput()); const __getHandler = () => call(getHandler, __page, context); return call(mware4, __getHandler, context); } + +export function head6(context, buildInput) { + const __page = () => pageResponse(page, buildInput()); + const __headHandler = () => call(headHandler, __page, context); + return stripResponseBody(call(mware4, __headHandler, context)); +} ``` --- ## Route `/$$match` @@ -244,16 +267,20 @@ export async function get6(context, buildInput) { ### Handler ```js // virtual:marko-run/__marko-run__route.$$match.js -import { normalize, call, noContent } from 'virtual:marko-run/runtime/internal'; +import { normalize, call, noContent, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import { mware4 } from 'virtual:marko-run/__marko-run__middleware.js'; import { GET } from './src/routes/$$match/+handler.get.ts'; const getHandler = normalize(GET); -export async function get7(context) { +export function get7(context) { const __getHandler = () => call(getHandler, noContent, context); return call(mware4, __getHandler, context); } + +export function head7(context) { + return stripResponseBody(get7(context)); +} ``` diff --git a/packages/run/src/vite/__tests__/fixtures/build-routes/__snapshots__/build-routes.expected.routetypes.d.ts b/packages/run/src/vite/__tests__/fixtures/build-routes/__snapshots__/build-routes.expected.routetypes.d.ts index da4770c..bb00cab 100644 --- a/packages/run/src/vite/__tests__/fixtures/build-routes/__snapshots__/build-routes.expected.routetypes.d.ts +++ b/packages/run/src/vite/__tests__/fixtures/build-routes/__snapshots__/build-routes.expected.routetypes.d.ts @@ -65,7 +65,7 @@ declare module "./callback/oauth2/+handler.get" { } } -declare module "./my/+handler.get" { +declare module "./my/+handler.get_head" { namespace MarkoRun { export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform }; export type Route = Run.Routes["/my"]; diff --git a/packages/run/src/vite/__tests__/fixtures/build-routes/routes.txt b/packages/run/src/vite/__tests__/fixtures/build-routes/routes.txt index 54720f5..49bcabc 100644 --- a/packages/run/src/vite/__tests__/fixtures/build-routes/routes.txt +++ b/packages/run/src/vite/__tests__/fixtures/build-routes/routes.txt @@ -20,7 +20,7 @@ /oauth2 +handler.get.ts /my - +handler.get.ts + +handler.get_head.ts +page.marko /$$match +handler.get.ts diff --git a/packages/run/src/vite/__tests__/fixtures/flat-routes/__snapshots__/flat-routes.expected.router.js b/packages/run/src/vite/__tests__/fixtures/flat-routes/__snapshots__/flat-routes.expected.router.js index bbd3b5c..3d64e82 100644 --- a/packages/run/src/vite/__tests__/fixtures/flat-routes/__snapshots__/flat-routes.expected.router.js +++ b/packages/run/src/vite/__tests__/fixtures/flat-routes/__snapshots__/flat-routes.expected.router.js @@ -1,13 +1,13 @@ // @marko/run/router import { NotHandled, NotMatched, createContext } from 'virtual:marko-run/runtime/internal'; -import { get1 } from 'virtual:marko-run/__marko-run__route.js'; -import { get2 } from 'virtual:marko-run/__marko-run__route.foo.js'; -import { get3 } from 'virtual:marko-run/__marko-run__route.$id.js'; -import { get4 } from 'virtual:marko-run/__marko-run__route.$$rest.js'; -import { get5 } from 'virtual:marko-run/__marko-run__route.a.c.js'; -import { get6 } from 'virtual:marko-run/__marko-run__route.a.d.js'; -import { get7 } from 'virtual:marko-run/__marko-run__route.b.c.js'; -import { get8 } from 'virtual:marko-run/__marko-run__route.b.d.js'; +import { get1, head1 } from 'virtual:marko-run/__marko-run__route.js'; +import { get2, head2, post2 } from 'virtual:marko-run/__marko-run__route.foo.js'; +import { get3, head3 } from 'virtual:marko-run/__marko-run__route.$id.js'; +import { get4, head4 } from 'virtual:marko-run/__marko-run__route.$$rest.js'; +import { get5, head5, post5 } from 'virtual:marko-run/__marko-run__route.a.c.js'; +import { get6, head6, post6 } from 'virtual:marko-run/__marko-run__route.a.d.js'; +import { get7, head7, post7 } from 'virtual:marko-run/__marko-run__route.b.c.js'; +import { get8, head8, post8 } from 'virtual:marko-run/__marko-run__route.b.d.js'; globalThis.__marko_run__ = { match, fetch, invoke }; @@ -51,6 +51,71 @@ export function match(method, pathname) { } return { handler: get4, params: { rest: pathname.slice(1) }, meta: {}, path: '/:rest*' }; // /$$rest } + case 'HEAD': + case 'head': { + const len = pathname.length; + if (len === 1) return { handler: head1, params: {}, meta: {}, path: '/' }; // / + const i1 = pathname.indexOf('/', 1) + 1; + if (!i1 || i1 === len) { + const s1 = decodeURIComponent(pathname.slice(1, i1 ? -1 : len)); + if (s1 === 'foo') return { handler: head2, params: {}, meta: {}, path: '/foo' }; // /foo + if (s1) return { handler: head3, params: { id: s1 }, meta: {}, path: '/:id' }; // /$id + } else { + switch (pathname.slice(1, i1 - 1)) { + case 'a': { + const i2 = pathname.indexOf('/', 3) + 1; + if (!i2 || i2 === len) { + switch (pathname.slice(3, i2 ? -1 : len)) { + case 'c': return { handler: head5, params: {}, meta: {}, path: '/a/c' }; // /a/c + case 'd': return { handler: head6, params: {}, meta: {}, path: '/a/d' }; // /a/d + } + } + } break; + case 'b': { + const i2 = pathname.indexOf('/', 3) + 1; + if (!i2 || i2 === len) { + switch (pathname.slice(3, i2 ? -1 : len)) { + case 'c': return { handler: head7, params: {}, meta: {}, path: '/b/c' }; // /b/c + case 'd': return { handler: head8, params: {}, meta: {}, path: '/b/d' }; // /b/d + } + } + } break; + } + } + return { handler: head4, params: { rest: pathname.slice(1) }, meta: {}, path: '/:rest*' }; // /$$rest + } + case 'POST': + case 'post': { + const len = pathname.length; + if (len > 1) { + const i1 = pathname.indexOf('/', 1) + 1; + if (!i1 || i1 === len) { + if (pathname.slice(1, i1 ? -1 : len) === 'foo') return { handler: post2, params: {}, meta: {}, path: '/foo' }; // /foo + } else { + switch (pathname.slice(1, i1 - 1)) { + case 'a': { + const i2 = pathname.indexOf('/', 3) + 1; + if (!i2 || i2 === len) { + switch (pathname.slice(3, i2 ? -1 : len)) { + case 'c': return { handler: post5, params: {}, meta: {}, path: '/a/c' }; // /a/c + case 'd': return { handler: post6, params: {}, meta: {}, path: '/a/d' }; // /a/d + } + } + } break; + case 'b': { + const i2 = pathname.indexOf('/', 3) + 1; + if (!i2 || i2 === len) { + switch (pathname.slice(3, i2 ? -1 : len)) { + case 'c': return { handler: post7, params: {}, meta: {}, path: '/b/c' }; // /b/c + case 'd': return { handler: post8, params: {}, meta: {}, path: '/b/d' }; // /b/d + } + } + } break; + } + } + } + return null; + } } return null; } diff --git a/packages/run/src/vite/__tests__/fixtures/flat-routes/__snapshots__/flat-routes.expected.routes.md b/packages/run/src/vite/__tests__/fixtures/flat-routes/__snapshots__/flat-routes.expected.routes.md index ff4871c..963fbe7 100644 --- a/packages/run/src/vite/__tests__/fixtures/flat-routes/__snapshots__/flat-routes.expected.routes.md +++ b/packages/run/src/vite/__tests__/fixtures/flat-routes/__snapshots__/flat-routes.expected.routes.md @@ -16,12 +16,16 @@ export const mware3 = normalize(middleware3); ### Handler ```js // virtual:marko-run/__marko-run__route.js -import { pageResponse } from 'virtual:marko-run/runtime/internal'; +import { pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import page from './src/routes/foo,$id,$$rest,+page.marko?marko-server-entry'; -export async function get1(context, buildInput) { +export function get1(context, buildInput) { return pageResponse(page, buildInput()); } + +export function head1(context, buildInput) { + return stripResponseBody(get1(context, buildInput)); +} ``` --- ## Route `/foo` @@ -30,16 +34,25 @@ export async function get1(context, buildInput) { ### Handler ```js // virtual:marko-run/__marko-run__route.foo.js -import { normalize, call, pageResponse } from 'virtual:marko-run/runtime/internal'; -import { GET } from './src/routes/foo,(a,b).(c,d)+handler.get.marko'; +import { normalize, call, noContent, pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; +import { GET, POST } from './src/routes/foo,(a,b).(c,d)+handler.get_post.marko'; import page from './src/routes/foo,$id,$$rest,+page.marko?marko-server-entry'; const getHandler = normalize(GET); +const postHandler = normalize(POST); -export async function get2(context, buildInput) { +export function get2(context, buildInput) { const __page = () => pageResponse(page, buildInput()); return call(getHandler, __page, context); } + +export function head2(context, buildInput) { + return stripResponseBody(get2(context, buildInput)); +} + +export function post2(context) { + return call(postHandler, noContent, context); +} ``` --- ## Route `/$id` @@ -48,14 +61,18 @@ export async function get2(context, buildInput) { ### Handler ```js // virtual:marko-run/__marko-run__route.$id.js -import { call, pageResponse } from 'virtual:marko-run/runtime/internal'; +import { call, pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import { mware3 } from 'virtual:marko-run/__marko-run__middleware.js'; import page from './src/routes/foo,$id,$$rest,+page.marko?marko-server-entry'; -export async function get3(context, buildInput) { +export function get3(context, buildInput) { const __page = () => pageResponse(page, buildInput()); return call(mware3, __page, context); } + +export function head3(context, buildInput) { + return stripResponseBody(get3(context, buildInput)); +} ``` --- ## Route `/$$rest` @@ -64,12 +81,16 @@ export async function get3(context, buildInput) { ### Handler ```js // virtual:marko-run/__marko-run__route.$$rest.js -import { pageResponse } from 'virtual:marko-run/runtime/internal'; +import { pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import page from './src/routes/foo,$id,$$rest,+page.marko?marko-server-entry'; -export async function get4(context, buildInput) { +export function get4(context, buildInput) { return pageResponse(page, buildInput()); } + +export function head4(context, buildInput) { + return stripResponseBody(get4(context, buildInput)); +} ``` --- ## Route `/a/c` @@ -78,14 +99,23 @@ export async function get4(context, buildInput) { ### Handler ```js // virtual:marko-run/__marko-run__route.a.c.js -import { normalize, call, noContent } from 'virtual:marko-run/runtime/internal'; -import { GET } from './src/routes/foo,(a,b).(c,d)+handler.get.marko'; +import { normalize, call, noContent, stripResponseBody } from 'virtual:marko-run/runtime/internal'; +import { GET, POST } from './src/routes/foo,(a,b).(c,d)+handler.get_post.marko'; const getHandler = normalize(GET); +const postHandler = normalize(POST); -export async function get5(context) { +export function get5(context) { return call(getHandler, noContent, context); } + +export function head5(context) { + return stripResponseBody(get5(context)); +} + +export function post5(context) { + return call(postHandler, noContent, context); +} ``` --- ## Route `/a/d` @@ -94,16 +124,26 @@ export async function get5(context) { ### Handler ```js // virtual:marko-run/__marko-run__route.a.d.js -import { normalize, call, noContent } from 'virtual:marko-run/runtime/internal'; +import { normalize, call, noContent, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import { mware3 } from 'virtual:marko-run/__marko-run__middleware.js'; -import { GET } from './src/routes/foo,(a,b).(c,d)+handler.get.marko'; +import { GET, POST } from './src/routes/foo,(a,b).(c,d)+handler.get_post.marko'; const getHandler = normalize(GET); +const postHandler = normalize(POST); -export async function get6(context) { +export function get6(context) { const __getHandler = () => call(getHandler, noContent, context); return call(mware3, __getHandler, context); } + +export function head6(context) { + return stripResponseBody(get6(context)); +} + +export function post6(context) { + const __postHandler = () => call(postHandler, noContent, context); + return call(mware3, __postHandler, context); +} ``` --- ## Route `/b/c` @@ -112,14 +152,23 @@ export async function get6(context) { ### Handler ```js // virtual:marko-run/__marko-run__route.b.c.js -import { normalize, call, noContent } from 'virtual:marko-run/runtime/internal'; -import { GET } from './src/routes/foo,(a,b).(c,d)+handler.get.marko'; +import { normalize, call, noContent, stripResponseBody } from 'virtual:marko-run/runtime/internal'; +import { GET, POST } from './src/routes/foo,(a,b).(c,d)+handler.get_post.marko'; const getHandler = normalize(GET); +const postHandler = normalize(POST); -export async function get7(context) { +export function get7(context) { return call(getHandler, noContent, context); } + +export function head7(context) { + return stripResponseBody(get7(context)); +} + +export function post7(context) { + return call(postHandler, noContent, context); +} ``` --- ## Route `/b/d` @@ -128,12 +177,21 @@ export async function get7(context) { ### Handler ```js // virtual:marko-run/__marko-run__route.b.d.js -import { normalize, call, noContent } from 'virtual:marko-run/runtime/internal'; -import { GET } from './src/routes/foo,(a,b).(c,d)+handler.get.marko'; +import { normalize, call, noContent, stripResponseBody } from 'virtual:marko-run/runtime/internal'; +import { GET, POST } from './src/routes/foo,(a,b).(c,d)+handler.get_post.marko'; const getHandler = normalize(GET); +const postHandler = normalize(POST); -export async function get8(context) { +export function get8(context) { return call(getHandler, noContent, context); } + +export function head8(context) { + return stripResponseBody(get8(context)); +} + +export function post8(context) { + return call(postHandler, noContent, context); +} ``` diff --git a/packages/run/src/vite/__tests__/fixtures/flat-routes/__snapshots__/flat-routes.expected.routetypes.d.ts b/packages/run/src/vite/__tests__/fixtures/flat-routes/__snapshots__/flat-routes.expected.routetypes.d.ts index 9d3cc84..f4c88a1 100644 --- a/packages/run/src/vite/__tests__/fixtures/flat-routes/__snapshots__/flat-routes.expected.routetypes.d.ts +++ b/packages/run/src/vite/__tests__/fixtures/flat-routes/__snapshots__/flat-routes.expected.routetypes.d.ts @@ -22,7 +22,7 @@ declare module "@marko/run" { }> {} } -declare module "./foo,(a,b).(c,d)+handler.get.marko" { +declare module "./foo,(a,b).(c,d)+handler.get_post.marko" { namespace MarkoRun { export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform }; export type Route = Run.Routes["/foo" | "/a/c" | "/a/d" | "/b/c" | "/b/d"]; @@ -57,11 +57,11 @@ declare module "./foo,$id,$$rest,+page.marko" { type Routes = { "/": { verb: "get"; }; - "/foo": { verb: "get"; }; + "/foo": { verb: "get" | "post"; }; "/$id": { verb: "get"; }; "/$$rest": { verb: "get"; }; - "/a/c": { verb: "get"; }; - "/a/d": { verb: "get"; }; - "/b/c": { verb: "get"; }; - "/b/d": { verb: "get"; }; + "/a/c": { verb: "get" | "post"; }; + "/a/d": { verb: "get" | "post"; }; + "/b/c": { verb: "get" | "post"; }; + "/b/d": { verb: "get" | "post"; }; } diff --git a/packages/run/src/vite/__tests__/fixtures/flat-routes/routes.txt b/packages/run/src/vite/__tests__/fixtures/flat-routes/routes.txt index 5c2b01a..7057726 100644 --- a/packages/run/src/vite/__tests__/fixtures/flat-routes/routes.txt +++ b/packages/run/src/vite/__tests__/fixtures/flat-routes/routes.txt @@ -1,3 +1,3 @@ foo,$id,$$rest,+page.marko -foo,(a,b).(c,d)+handler.get.marko +foo,(a,b).(c,d)+handler.get_post.marko $id,a.d+middleware.marko \ No newline at end of file diff --git a/packages/run/src/vite/__tests__/fixtures/multiple-sources/__snapshots__/multiple-sources.expected.router.js b/packages/run/src/vite/__tests__/fixtures/multiple-sources/__snapshots__/multiple-sources.expected.router.js index 323096b..af9f1ff 100644 --- a/packages/run/src/vite/__tests__/fixtures/multiple-sources/__snapshots__/multiple-sources.expected.router.js +++ b/packages/run/src/vite/__tests__/fixtures/multiple-sources/__snapshots__/multiple-sources.expected.router.js @@ -1,7 +1,7 @@ // @marko/run/router import { NotHandled, NotMatched, createContext } from 'virtual:marko-run/runtime/internal'; -import { get1 } from 'virtual:marko-run/__marko-run__route.js'; -import { get2 } from 'virtual:marko-run/__marko-run__route._routes.js'; +import { get1, head1 } from 'virtual:marko-run/__marko-run__route.js'; +import { get2, head2 } from 'virtual:marko-run/__marko-run__route._routes.js'; globalThis.__marko_run__ = { match, fetch, invoke }; @@ -22,6 +22,16 @@ export function match(method, pathname) { } return null; } + case 'HEAD': + case 'head': { + const len = pathname.length; + if (len === 1) return { handler: head1, params: {}, meta: {}, path: '/' }; // / + const i1 = pathname.indexOf('/', 1) + 1; + if (!i1 || i1 === len) { + if (decodeURIComponent(pathname.slice(1, i1 ? -1 : len)) === '+routes') return { handler: head2, params: {}, meta: {}, path: '/%2Broutes' }; // /%2Broutes + } + return null; + } } return null; } diff --git a/packages/run/src/vite/__tests__/fixtures/multiple-sources/__snapshots__/multiple-sources.expected.routes.md b/packages/run/src/vite/__tests__/fixtures/multiple-sources/__snapshots__/multiple-sources.expected.routes.md index c561f9a..beb5894 100644 --- a/packages/run/src/vite/__tests__/fixtures/multiple-sources/__snapshots__/multiple-sources.expected.routes.md +++ b/packages/run/src/vite/__tests__/fixtures/multiple-sources/__snapshots__/multiple-sources.expected.routes.md @@ -6,12 +6,16 @@ ### Handler ```js // virtual:marko-run/__marko-run__route.js -import { pageResponse } from 'virtual:marko-run/runtime/internal'; +import { pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import page from './src/routes/+page.marko?marko-server-entry'; -export async function get1(context, buildInput) { +export function get1(context, buildInput) { return pageResponse(page, buildInput()); } + +export function head1(context, buildInput) { + return stripResponseBody(get1(context, buildInput)); +} ``` --- ## Route `/%2Broutes` @@ -20,10 +24,14 @@ export async function get1(context, buildInput) { ### Handler ```js // virtual:marko-run/__marko-run__route._routes.js -import { pageResponse } from 'virtual:marko-run/runtime/internal'; +import { pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import page from './src/routes/+page.marko?marko-server-entry'; -export async function get2(context, buildInput) { +export function get2(context, buildInput) { return pageResponse(page, buildInput()); } + +export function head2(context, buildInput) { + return stripResponseBody(get2(context, buildInput)); +} ``` diff --git a/packages/run/src/vite/__tests__/fixtures/nested-dynamic/__snapshots__/nested-dynamic.expected.router.js b/packages/run/src/vite/__tests__/fixtures/nested-dynamic/__snapshots__/nested-dynamic.expected.router.js index 19d0c6b..65628a7 100644 --- a/packages/run/src/vite/__tests__/fixtures/nested-dynamic/__snapshots__/nested-dynamic.expected.router.js +++ b/packages/run/src/vite/__tests__/fixtures/nested-dynamic/__snapshots__/nested-dynamic.expected.router.js @@ -1,6 +1,6 @@ // @marko/run/router import { NotHandled, NotMatched, createContext } from 'virtual:marko-run/runtime/internal'; -import { get1 } from 'virtual:marko-run/__marko-run__route.foo.$fooId.bar.$bar Id.baz.$1bazId.$qux-Id.js'; +import { get1, head1 } from 'virtual:marko-run/__marko-run__route.foo.$fooId.bar.$bar Id.baz.$1bazId.$qux-Id.js'; globalThis.__marko_run__ = { match, fetch, invoke }; @@ -56,6 +56,51 @@ export function match(method, pathname) { } return null; } + case 'HEAD': + case 'head': { + const len = pathname.length; + if (len > 1) { + const i1 = pathname.indexOf('/', 1) + 1; + if (i1 && i1 !== len) { + if (pathname.slice(1, i1 - 1) === 'foo') { + const i2 = pathname.indexOf('/', 5) + 1; + if (i2 && i2 !== len) { + const s2 = decodeURIComponent(pathname.slice(5, i2 - 1)); + if (s2) { + const i3 = pathname.indexOf('/', i2) + 1; + if (i3 && i3 !== len) { + if (pathname.slice(i2, i3 - 1) === 'bar') { + const i4 = pathname.indexOf('/', i3) + 1; + if (i4 && i4 !== len) { + const s4 = decodeURIComponent(pathname.slice(i3, i4 - 1)); + if (s4) { + const i5 = pathname.indexOf('/', i4) + 1; + if (i5 && i5 !== len) { + if (pathname.slice(i4, i5 - 1) === 'baz') { + const i6 = pathname.indexOf('/', i5) + 1; + if (i6 && i6 !== len) { + const s6 = decodeURIComponent(pathname.slice(i5, i6 - 1)); + if (s6) { + const i7 = pathname.indexOf('/', i6) + 1; + if (!i7 || i7 === len) { + const s7 = decodeURIComponent(pathname.slice(i6, i7 ? -1 : len)); + if (s7) return { handler: head1, params: { fooId: s2, 'bar Id': s4, '1bazId': s6, 'qux-Id': s7 }, meta: {}, path: '/foo/:fooId/bar/:bar Id/baz/:1bazId/:qux-Id' }; // /foo/$fooId/bar/$bar Id/baz/$1bazId/$qux-Id + } + } + } + } + } + } + } + } + } + } + } + } + } + } + return null; + } } return null; } diff --git a/packages/run/src/vite/__tests__/fixtures/nested-dynamic/__snapshots__/nested-dynamic.expected.routes.md b/packages/run/src/vite/__tests__/fixtures/nested-dynamic/__snapshots__/nested-dynamic.expected.routes.md index 1e47ffb..ee07dc1 100644 --- a/packages/run/src/vite/__tests__/fixtures/nested-dynamic/__snapshots__/nested-dynamic.expected.routes.md +++ b/packages/run/src/vite/__tests__/fixtures/nested-dynamic/__snapshots__/nested-dynamic.expected.routes.md @@ -6,10 +6,14 @@ ### Handler ```js // virtual:marko-run/__marko-run__route.foo.$fooId.bar.$bar Id.baz.$1bazId.$qux-Id.js -import { pageResponse } from 'virtual:marko-run/runtime/internal'; +import { pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import page from './src/routes/foo/$fooId/bar/$bar Id/baz/$1bazId/$qux-Id/+page.marko?marko-server-entry'; -export async function get1(context, buildInput) { +export function get1(context, buildInput) { return pageResponse(page, buildInput()); } + +export function head1(context, buildInput) { + return stripResponseBody(get1(context, buildInput)); +} ``` diff --git a/packages/run/src/vite/__tests__/fixtures/optional-dynamic/__snapshots__/optional-dynamic.expected.router.js b/packages/run/src/vite/__tests__/fixtures/optional-dynamic/__snapshots__/optional-dynamic.expected.router.js index 7200fd6..69c5d45 100644 --- a/packages/run/src/vite/__tests__/fixtures/optional-dynamic/__snapshots__/optional-dynamic.expected.router.js +++ b/packages/run/src/vite/__tests__/fixtures/optional-dynamic/__snapshots__/optional-dynamic.expected.router.js @@ -1,9 +1,9 @@ // @marko/run/router import { NotHandled, NotMatched, createContext } from 'virtual:marko-run/runtime/internal'; -import { get1 } from 'virtual:marko-run/__marko-run__route.$foo.js'; -import { get2 } from 'virtual:marko-run/__marko-run__route.$foo.$bar.js'; -import { get3 } from 'virtual:marko-run/__marko-run__route.$foo.$$rest.js'; -import { get4 } from 'virtual:marko-run/__marko-run__route.$$rest.js'; +import { get1, head1 } from 'virtual:marko-run/__marko-run__route.$foo.js'; +import { get2, head2 } from 'virtual:marko-run/__marko-run__route.$foo.$bar.js'; +import { get3, head3 } from 'virtual:marko-run/__marko-run__route.$foo.$$rest.js'; +import { get4, head4 } from 'virtual:marko-run/__marko-run__route.$$rest.js'; globalThis.__marko_run__ = { match, fetch, invoke }; @@ -36,6 +36,28 @@ export function match(method, pathname) { } return { handler: get4, params: { rest: pathname.slice(1) }, meta: {}, path: '/:rest*' }; // /$$rest } + case 'HEAD': + case 'head': { + const len = pathname.length; + if (len > 1) { + const i1 = pathname.indexOf('/', 1) + 1; + if (!i1 || i1 === len) { + const s1 = decodeURIComponent(pathname.slice(1, i1 ? -1 : len)); + if (s1) return { handler: head1, params: { foo: s1 }, meta: {}, path: '/:foo' }; // /$foo + } else { + const s1 = decodeURIComponent(pathname.slice(1, i1 - 1)); + if (s1) { + const i2 = pathname.indexOf('/', i1) + 1; + if (!i2 || i2 === len) { + const s2 = decodeURIComponent(pathname.slice(i1, i2 ? -1 : len)); + if (s2) return { handler: head2, params: { foo: s1, bar: s2 }, meta: {}, path: '/:foo/:bar' }; // /$foo/$bar + } + return { handler: head3, params: { foo: s1, rest: pathname.slice(i1) }, meta: {}, path: '/:foo/:rest*' }; // /$foo/$$rest + } + } + } + return { handler: head4, params: { rest: pathname.slice(1) }, meta: {}, path: '/:rest*' }; // /$$rest + } } return null; } diff --git a/packages/run/src/vite/__tests__/fixtures/optional-dynamic/__snapshots__/optional-dynamic.expected.routes.md b/packages/run/src/vite/__tests__/fixtures/optional-dynamic/__snapshots__/optional-dynamic.expected.routes.md index acf846b..6266230 100644 --- a/packages/run/src/vite/__tests__/fixtures/optional-dynamic/__snapshots__/optional-dynamic.expected.routes.md +++ b/packages/run/src/vite/__tests__/fixtures/optional-dynamic/__snapshots__/optional-dynamic.expected.routes.md @@ -6,12 +6,16 @@ ### Handler ```js // virtual:marko-run/__marko-run__route.$foo.js -import { pageResponse } from 'virtual:marko-run/runtime/internal'; +import { pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import page from './src/routes/$foo,/$bar,$$rest/+page.marko?marko-server-entry'; -export async function get1(context, buildInput) { +export function get1(context, buildInput) { return pageResponse(page, buildInput()); } + +export function head1(context, buildInput) { + return stripResponseBody(get1(context, buildInput)); +} ``` --- ## Route `/$foo/$bar` @@ -20,12 +24,16 @@ export async function get1(context, buildInput) { ### Handler ```js // virtual:marko-run/__marko-run__route.$foo.$bar.js -import { pageResponse } from 'virtual:marko-run/runtime/internal'; +import { pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import page from './src/routes/$foo,/$bar,$$rest/+page.marko?marko-server-entry'; -export async function get2(context, buildInput) { +export function get2(context, buildInput) { return pageResponse(page, buildInput()); } + +export function head2(context, buildInput) { + return stripResponseBody(get2(context, buildInput)); +} ``` --- ## Route `/$foo/$$rest` @@ -34,12 +42,16 @@ export async function get2(context, buildInput) { ### Handler ```js // virtual:marko-run/__marko-run__route.$foo.$$rest.js -import { pageResponse } from 'virtual:marko-run/runtime/internal'; +import { pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import page from './src/routes/$foo,/$bar,$$rest/+page.marko?marko-server-entry'; -export async function get3(context, buildInput) { +export function get3(context, buildInput) { return pageResponse(page, buildInput()); } + +export function head3(context, buildInput) { + return stripResponseBody(get3(context, buildInput)); +} ``` --- ## Route `/$$rest` @@ -48,10 +60,14 @@ export async function get3(context, buildInput) { ### Handler ```js // virtual:marko-run/__marko-run__route.$$rest.js -import { pageResponse } from 'virtual:marko-run/runtime/internal'; +import { pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import page from './src/routes/$foo,/$bar,$$rest/+page.marko?marko-server-entry'; -export async function get4(context, buildInput) { +export function get4(context, buildInput) { return pageResponse(page, buildInput()); } + +export function head4(context, buildInput) { + return stripResponseBody(get4(context, buildInput)); +} ``` diff --git a/packages/run/src/vite/__tests__/fixtures/optional-static/__snapshots__/optional-static.expected.router.js b/packages/run/src/vite/__tests__/fixtures/optional-static/__snapshots__/optional-static.expected.router.js index 8248467..f94999d 100644 --- a/packages/run/src/vite/__tests__/fixtures/optional-static/__snapshots__/optional-static.expected.router.js +++ b/packages/run/src/vite/__tests__/fixtures/optional-static/__snapshots__/optional-static.expected.router.js @@ -1,13 +1,13 @@ // @marko/run/router import { NotHandled, NotMatched, createContext } from 'virtual:marko-run/runtime/internal'; -import { get1 } from 'virtual:marko-run/__marko-run__route.js'; -import { get2 } from 'virtual:marko-run/__marko-run__route.foo.js'; -import { get3 } from 'virtual:marko-run/__marko-run__route.foo.bar.js'; -import { get4 } from 'virtual:marko-run/__marko-run__route.foo.bar.baz.js'; -import { get5 } from 'virtual:marko-run/__marko-run__route.foo.baz.js'; -import { get6 } from 'virtual:marko-run/__marko-run__route.bar.js'; -import { get7 } from 'virtual:marko-run/__marko-run__route.bar.baz.js'; -import { get8 } from 'virtual:marko-run/__marko-run__route.baz.js'; +import { get1, head1 } from 'virtual:marko-run/__marko-run__route.js'; +import { get2, head2 } from 'virtual:marko-run/__marko-run__route.foo.js'; +import { get3, head3 } from 'virtual:marko-run/__marko-run__route.foo.bar.js'; +import { get4, head4 } from 'virtual:marko-run/__marko-run__route.foo.bar.baz.js'; +import { get5, head5 } from 'virtual:marko-run/__marko-run__route.foo.baz.js'; +import { get6, head6 } from 'virtual:marko-run/__marko-run__route.bar.js'; +import { get7, head7 } from 'virtual:marko-run/__marko-run__route.bar.baz.js'; +import { get8, head8 } from 'virtual:marko-run/__marko-run__route.baz.js'; globalThis.__marko_run__ = { match, fetch, invoke }; @@ -57,6 +57,45 @@ export function match(method, pathname) { } return null; } + case 'HEAD': + case 'head': { + const len = pathname.length; + if (len === 1) return { handler: head1, params: {}, meta: {}, path: '/' }; // / + const i1 = pathname.indexOf('/', 1) + 1; + if (!i1 || i1 === len) { + switch (pathname.slice(1, i1 ? -1 : len)) { + case 'foo': return { handler: head2, params: {}, meta: {}, path: '/foo' }; // /foo + case 'bar': return { handler: head6, params: {}, meta: {}, path: '/bar' }; // /bar + case 'baz': return { handler: head8, params: {}, meta: {}, path: '/baz' }; // /baz + } + } else { + switch (pathname.slice(1, i1 - 1)) { + case 'foo': { + const i2 = pathname.indexOf('/', 5) + 1; + if (!i2 || i2 === len) { + switch (pathname.slice(5, i2 ? -1 : len)) { + case 'bar': return { handler: head3, params: {}, meta: {}, path: '/foo/bar' }; // /foo/bar + case 'baz': return { handler: head5, params: {}, meta: {}, path: '/foo/baz' }; // /foo/baz + } + } else { + if (pathname.slice(5, i2 - 1) === 'bar') { + const i3 = pathname.indexOf('/', 9) + 1; + if (!i3 || i3 === len) { + if (pathname.slice(9, i3 ? -1 : len) === 'baz') return { handler: head4, params: {}, meta: {}, path: '/foo/bar/baz' }; // /foo/bar/baz + } + } + } + } break; + case 'bar': { + const i2 = pathname.indexOf('/', 5) + 1; + if (!i2 || i2 === len) { + if (pathname.slice(5, i2 ? -1 : len) === 'baz') return { handler: head7, params: {}, meta: {}, path: '/bar/baz' }; // /bar/baz + } + } break; + } + } + return null; + } } return null; } diff --git a/packages/run/src/vite/__tests__/fixtures/optional-static/__snapshots__/optional-static.expected.routes.md b/packages/run/src/vite/__tests__/fixtures/optional-static/__snapshots__/optional-static.expected.routes.md index 9df2ee3..ad1a4a1 100644 --- a/packages/run/src/vite/__tests__/fixtures/optional-static/__snapshots__/optional-static.expected.routes.md +++ b/packages/run/src/vite/__tests__/fixtures/optional-static/__snapshots__/optional-static.expected.routes.md @@ -6,12 +6,16 @@ ### Handler ```js // virtual:marko-run/__marko-run__route.js -import { pageResponse } from 'virtual:marko-run/runtime/internal'; +import { pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import page from './src/routes/foo,/bar,/,baz/+page.marko?marko-server-entry'; -export async function get1(context, buildInput) { +export function get1(context, buildInput) { return pageResponse(page, buildInput()); } + +export function head1(context, buildInput) { + return stripResponseBody(get1(context, buildInput)); +} ``` --- ## Route `/foo` @@ -20,12 +24,16 @@ export async function get1(context, buildInput) { ### Handler ```js // virtual:marko-run/__marko-run__route.foo.js -import { pageResponse } from 'virtual:marko-run/runtime/internal'; +import { pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import page from './src/routes/foo,/bar,/,baz/+page.marko?marko-server-entry'; -export async function get2(context, buildInput) { +export function get2(context, buildInput) { return pageResponse(page, buildInput()); } + +export function head2(context, buildInput) { + return stripResponseBody(get2(context, buildInput)); +} ``` --- ## Route `/foo/bar` @@ -34,12 +42,16 @@ export async function get2(context, buildInput) { ### Handler ```js // virtual:marko-run/__marko-run__route.foo.bar.js -import { pageResponse } from 'virtual:marko-run/runtime/internal'; +import { pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import page from './src/routes/foo,/bar,/,baz/+page.marko?marko-server-entry'; -export async function get3(context, buildInput) { +export function get3(context, buildInput) { return pageResponse(page, buildInput()); } + +export function head3(context, buildInput) { + return stripResponseBody(get3(context, buildInput)); +} ``` --- ## Route `/foo/bar/baz` @@ -48,12 +60,16 @@ export async function get3(context, buildInput) { ### Handler ```js // virtual:marko-run/__marko-run__route.foo.bar.baz.js -import { pageResponse } from 'virtual:marko-run/runtime/internal'; +import { pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import page from './src/routes/foo,/bar,/,baz/+page.marko?marko-server-entry'; -export async function get4(context, buildInput) { +export function get4(context, buildInput) { return pageResponse(page, buildInput()); } + +export function head4(context, buildInput) { + return stripResponseBody(get4(context, buildInput)); +} ``` --- ## Route `/foo/baz` @@ -62,12 +78,16 @@ export async function get4(context, buildInput) { ### Handler ```js // virtual:marko-run/__marko-run__route.foo.baz.js -import { pageResponse } from 'virtual:marko-run/runtime/internal'; +import { pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import page from './src/routes/foo,/bar,/,baz/+page.marko?marko-server-entry'; -export async function get5(context, buildInput) { +export function get5(context, buildInput) { return pageResponse(page, buildInput()); } + +export function head5(context, buildInput) { + return stripResponseBody(get5(context, buildInput)); +} ``` --- ## Route `/bar` @@ -76,12 +96,16 @@ export async function get5(context, buildInput) { ### Handler ```js // virtual:marko-run/__marko-run__route.bar.js -import { pageResponse } from 'virtual:marko-run/runtime/internal'; +import { pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import page from './src/routes/foo,/bar,/,baz/+page.marko?marko-server-entry'; -export async function get6(context, buildInput) { +export function get6(context, buildInput) { return pageResponse(page, buildInput()); } + +export function head6(context, buildInput) { + return stripResponseBody(get6(context, buildInput)); +} ``` --- ## Route `/bar/baz` @@ -90,12 +114,16 @@ export async function get6(context, buildInput) { ### Handler ```js // virtual:marko-run/__marko-run__route.bar.baz.js -import { pageResponse } from 'virtual:marko-run/runtime/internal'; +import { pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import page from './src/routes/foo,/bar,/,baz/+page.marko?marko-server-entry'; -export async function get7(context, buildInput) { +export function get7(context, buildInput) { return pageResponse(page, buildInput()); } + +export function head7(context, buildInput) { + return stripResponseBody(get7(context, buildInput)); +} ``` --- ## Route `/baz` @@ -104,10 +132,14 @@ export async function get7(context, buildInput) { ### Handler ```js // virtual:marko-run/__marko-run__route.baz.js -import { pageResponse } from 'virtual:marko-run/runtime/internal'; +import { pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import page from './src/routes/foo,/bar,/,baz/+page.marko?marko-server-entry'; -export async function get8(context, buildInput) { +export function get8(context, buildInput) { return pageResponse(page, buildInput()); } + +export function head8(context, buildInput) { + return stripResponseBody(get8(context, buildInput)); +} ``` diff --git a/packages/run/src/vite/__tests__/fixtures/optional-types/__snapshots__/optional-types.expected.router.js b/packages/run/src/vite/__tests__/fixtures/optional-types/__snapshots__/optional-types.expected.router.js index 6884758..47c6331 100644 --- a/packages/run/src/vite/__tests__/fixtures/optional-types/__snapshots__/optional-types.expected.router.js +++ b/packages/run/src/vite/__tests__/fixtures/optional-types/__snapshots__/optional-types.expected.router.js @@ -1,9 +1,9 @@ // @marko/run/router import { NotHandled, NotMatched, createContext } from 'virtual:marko-run/runtime/internal'; -import { get1 } from 'virtual:marko-run/__marko-run__route.aaa.$aId.js'; -import { get2 } from 'virtual:marko-run/__marko-run__route.aaa.$aId.bbb.$bId.js'; -import { get3 } from 'virtual:marko-run/__marko-run__route.aaa.$aId.bbb.$bId.ccc.$cId.js'; -import { get4 } from 'virtual:marko-run/__marko-run__route.aaa.$aId.ccc.$cId.js'; +import { get1, head1 } from 'virtual:marko-run/__marko-run__route.aaa.$aId.js'; +import { get2, head2 } from 'virtual:marko-run/__marko-run__route.aaa.$aId.bbb.$bId.js'; +import { get3, head3 } from 'virtual:marko-run/__marko-run__route.aaa.$aId.bbb.$bId.ccc.$cId.js'; +import { get4, head4 } from 'virtual:marko-run/__marko-run__route.aaa.$aId.ccc.$cId.js'; globalThis.__marko_run__ = { match, fetch, invoke }; @@ -68,6 +68,60 @@ export function match(method, pathname) { } return null; } + case 'HEAD': + case 'head': { + const len = pathname.length; + if (len > 1) { + const i1 = pathname.indexOf('/', 1) + 1; + if (i1 && i1 !== len) { + if (pathname.slice(1, i1 - 1) === 'aaa') { + const i2 = pathname.indexOf('/', 5) + 1; + if (!i2 || i2 === len) { + const s2 = decodeURIComponent(pathname.slice(5, i2 ? -1 : len)); + if (s2) return { handler: head1, params: { aId: s2 }, meta: {}, path: '/aaa/:aId' }; // /aaa/$aId + } else { + const s2 = decodeURIComponent(pathname.slice(5, i2 - 1)); + if (s2) { + const i3 = pathname.indexOf('/', i2) + 1; + if (i3 && i3 !== len) { + switch (pathname.slice(i2, i3 - 1)) { + case 'bbb': { + const i4 = pathname.indexOf('/', i3) + 1; + if (!i4 || i4 === len) { + const s4 = decodeURIComponent(pathname.slice(i3, i4 ? -1 : len)); + if (s4) return { handler: head2, params: { aId: s2, bId: s4 }, meta: {}, path: '/aaa/:aId/bbb/:bId' }; // /aaa/$aId/bbb/$bId + } else { + const s4 = decodeURIComponent(pathname.slice(i3, i4 - 1)); + if (s4) { + const i5 = pathname.indexOf('/', i4) + 1; + if (i5 && i5 !== len) { + if (pathname.slice(i4, i5 - 1) === 'ccc') { + const i6 = pathname.indexOf('/', i5) + 1; + if (!i6 || i6 === len) { + const s6 = decodeURIComponent(pathname.slice(i5, i6 ? -1 : len)); + if (s6) return { handler: head3, params: { aId: s2, bId: s4, cId: s6 }, meta: {}, path: '/aaa/:aId/bbb/:bId/ccc/:cId' }; // /aaa/$aId/bbb/$bId/ccc/$cId + } + } + } + } + } + } break; + case 'ccc': { + const i4 = pathname.indexOf('/', i3) + 1; + if (!i4 || i4 === len) { + const s4 = decodeURIComponent(pathname.slice(i3, i4 ? -1 : len)); + if (s4) return { handler: head4, params: { aId: s2, cId: s4 }, meta: {}, path: '/aaa/:aId/ccc/:cId' }; // /aaa/$aId/ccc/$cId + } + } break; + } + } + } + } + } + } + } + return null; + } } return null; } diff --git a/packages/run/src/vite/__tests__/fixtures/optional-types/__snapshots__/optional-types.expected.routes.md b/packages/run/src/vite/__tests__/fixtures/optional-types/__snapshots__/optional-types.expected.routes.md index ff37f68..d5006a2 100644 --- a/packages/run/src/vite/__tests__/fixtures/optional-types/__snapshots__/optional-types.expected.routes.md +++ b/packages/run/src/vite/__tests__/fixtures/optional-types/__snapshots__/optional-types.expected.routes.md @@ -26,18 +26,22 @@ import Page from '../../src/routes/aaa.$aId.(,bbb.$bId).(,ccc.$cId)/+page.marko' ### Handler ```js // virtual:marko-run/__marko-run__route.aaa.$aId.js -import { normalize, call, pageResponse } from 'virtual:marko-run/runtime/internal'; +import { normalize, call, pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import { mware2 } from 'virtual:marko-run/__marko-run__middleware.js'; import { GET } from './src/routes/aaa.$aId.(,bbb.$bId).(,ccc.$cId)/+handler.get.ts'; import page from './.marko/aaa.$aId.(,bbb.$bId).(,ccc.$cId)/route.marko?marko-server-entry'; const getHandler = normalize(GET); -export async function get1(context, buildInput) { +export function get1(context, buildInput) { const __page = () => pageResponse(page, buildInput()); const __getHandler = () => call(getHandler, __page, context); return call(mware2, __getHandler, context); } + +export function head1(context, buildInput) { + return stripResponseBody(get1(context, buildInput)); +} ``` --- ## Route `/aaa/$aId/bbb/$bId` @@ -56,18 +60,22 @@ import Page from '../../src/routes/aaa.$aId.(,bbb.$bId).(,ccc.$cId)/+page.marko' ### Handler ```js // virtual:marko-run/__marko-run__route.aaa.$aId.bbb.$bId.js -import { normalize, call, pageResponse } from 'virtual:marko-run/runtime/internal'; +import { normalize, call, pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import { mware2 } from 'virtual:marko-run/__marko-run__middleware.js'; import { GET } from './src/routes/aaa.$aId.(,bbb.$bId).(,ccc.$cId)/+handler.get.ts'; import page from './.marko/aaa.$aId.(,bbb.$bId).(,ccc.$cId)/route.marko?marko-server-entry'; const getHandler = normalize(GET); -export async function get2(context, buildInput) { +export function get2(context, buildInput) { const __page = () => pageResponse(page, buildInput()); const __getHandler = () => call(getHandler, __page, context); return call(mware2, __getHandler, context); } + +export function head2(context, buildInput) { + return stripResponseBody(get2(context, buildInput)); +} ``` --- ## Route `/aaa/$aId/bbb/$bId/ccc/$cId` @@ -86,18 +94,22 @@ import Page from '../../src/routes/aaa.$aId.(,bbb.$bId).(,ccc.$cId)/+page.marko' ### Handler ```js // virtual:marko-run/__marko-run__route.aaa.$aId.bbb.$bId.ccc.$cId.js -import { normalize, call, pageResponse } from 'virtual:marko-run/runtime/internal'; +import { normalize, call, pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import { mware2 } from 'virtual:marko-run/__marko-run__middleware.js'; import { GET } from './src/routes/aaa.$aId.(,bbb.$bId).(,ccc.$cId)/+handler.get.ts'; import page from './.marko/aaa.$aId.(,bbb.$bId).(,ccc.$cId)/route.marko?marko-server-entry'; const getHandler = normalize(GET); -export async function get3(context, buildInput) { +export function get3(context, buildInput) { const __page = () => pageResponse(page, buildInput()); const __getHandler = () => call(getHandler, __page, context); return call(mware2, __getHandler, context); } + +export function head3(context, buildInput) { + return stripResponseBody(get3(context, buildInput)); +} ``` --- ## Route `/aaa/$aId/ccc/$cId` @@ -116,16 +128,20 @@ import Page from '../../src/routes/aaa.$aId.(,bbb.$bId).(,ccc.$cId)/+page.marko' ### Handler ```js // virtual:marko-run/__marko-run__route.aaa.$aId.ccc.$cId.js -import { normalize, call, pageResponse } from 'virtual:marko-run/runtime/internal'; +import { normalize, call, pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import { mware2 } from 'virtual:marko-run/__marko-run__middleware.js'; import { GET } from './src/routes/aaa.$aId.(,bbb.$bId).(,ccc.$cId)/+handler.get.ts'; import page from './.marko/aaa.$aId.(,bbb.$bId).(,ccc.$cId)/route.marko?marko-server-entry'; const getHandler = normalize(GET); -export async function get4(context, buildInput) { +export function get4(context, buildInput) { const __page = () => pageResponse(page, buildInput()); const __getHandler = () => call(getHandler, __page, context); return call(mware2, __getHandler, context); } + +export function head4(context, buildInput) { + return stripResponseBody(get4(context, buildInput)); +} ``` diff --git a/packages/run/src/vite/__tests__/fixtures/param-optional-rest/__snapshots__/param-optional-rest.expected.router.js b/packages/run/src/vite/__tests__/fixtures/param-optional-rest/__snapshots__/param-optional-rest.expected.router.js index 223ca11..b4e4e9c 100644 --- a/packages/run/src/vite/__tests__/fixtures/param-optional-rest/__snapshots__/param-optional-rest.expected.router.js +++ b/packages/run/src/vite/__tests__/fixtures/param-optional-rest/__snapshots__/param-optional-rest.expected.router.js @@ -1,7 +1,7 @@ // @marko/run/router import { NotHandled, NotMatched, createContext } from 'virtual:marko-run/runtime/internal'; -import { get1 } from 'virtual:marko-run/__marko-run__route.$campaignId.js'; -import { get2 } from 'virtual:marko-run/__marko-run__route.$campaignId.$$rest.js'; +import { get1, head1 } from 'virtual:marko-run/__marko-run__route.$campaignId.js'; +import { get2, head2 } from 'virtual:marko-run/__marko-run__route.$campaignId.$$rest.js'; globalThis.__marko_run__ = { match, fetch, invoke }; @@ -29,6 +29,23 @@ export function match(method, pathname) { } return null; } + case 'HEAD': + case 'head': { + const len = pathname.length; + if (len > 1) { + const i1 = pathname.indexOf('/', 1) + 1; + if (!i1 || i1 === len) { + const s1 = decodeURIComponent(pathname.slice(1, i1 ? -1 : len)); + if (s1) return { handler: head1, params: { campaignId: s1 }, meta: {}, path: '/:campaignId' }; // /$campaignId + } else { + const s1 = decodeURIComponent(pathname.slice(1, i1 - 1)); + if (s1) { + return { handler: head2, params: { campaignId: s1, rest: pathname.slice(i1) }, meta: {}, path: '/:campaignId/:rest*' }; // /$campaignId/$$rest + } + } + } + return null; + } } return null; } diff --git a/packages/run/src/vite/__tests__/fixtures/param-optional-rest/__snapshots__/param-optional-rest.expected.routes.md b/packages/run/src/vite/__tests__/fixtures/param-optional-rest/__snapshots__/param-optional-rest.expected.routes.md index f0935a9..d0c244d 100644 --- a/packages/run/src/vite/__tests__/fixtures/param-optional-rest/__snapshots__/param-optional-rest.expected.routes.md +++ b/packages/run/src/vite/__tests__/fixtures/param-optional-rest/__snapshots__/param-optional-rest.expected.routes.md @@ -6,12 +6,16 @@ ### Handler ```js // virtual:marko-run/__marko-run__route.$campaignId.js -import { pageResponse } from 'virtual:marko-run/runtime/internal'; +import { pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import page from './src/routes/$campaignId/$$rest,/+page.marko?marko-server-entry'; -export async function get1(context, buildInput) { +export function get1(context, buildInput) { return pageResponse(page, buildInput()); } + +export function head1(context, buildInput) { + return stripResponseBody(get1(context, buildInput)); +} ``` --- ## Route `/$campaignId/$$rest` @@ -20,10 +24,14 @@ export async function get1(context, buildInput) { ### Handler ```js // virtual:marko-run/__marko-run__route.$campaignId.$$rest.js -import { pageResponse } from 'virtual:marko-run/runtime/internal'; +import { pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import page from './src/routes/$campaignId/$$rest,/+page.marko?marko-server-entry'; -export async function get2(context, buildInput) { +export function get2(context, buildInput) { return pageResponse(page, buildInput()); } + +export function head2(context, buildInput) { + return stripResponseBody(get2(context, buildInput)); +} ``` diff --git a/packages/run/src/vite/__tests__/fixtures/uri-encoded/__snapshots__/uri-encoded.expected.router.js b/packages/run/src/vite/__tests__/fixtures/uri-encoded/__snapshots__/uri-encoded.expected.router.js index 25c5265..7eae7ac 100644 --- a/packages/run/src/vite/__tests__/fixtures/uri-encoded/__snapshots__/uri-encoded.expected.router.js +++ b/packages/run/src/vite/__tests__/fixtures/uri-encoded/__snapshots__/uri-encoded.expected.router.js @@ -1,6 +1,6 @@ // @marko/run/router import { NotHandled, NotMatched, createContext } from 'virtual:marko-run/runtime/internal'; -import { get1 } from 'virtual:marko-run/__marko-run__route.a_b_c.$_id.js'; +import { get1, head1 } from 'virtual:marko-run/__marko-run__route.a_b_c.$_id.js'; globalThis.__marko_run__ = { match, fetch, invoke }; @@ -28,6 +28,23 @@ export function match(method, pathname) { } return null; } + case 'HEAD': + case 'head': { + const len = pathname.length; + if (len > 1) { + const i1 = pathname.indexOf('/', 1) + 1; + if (i1 && i1 !== len) { + if (decodeURIComponent(pathname.slice(1, i1 - 1)) === 'a/b/c') { + const i2 = pathname.indexOf('/', 11) + 1; + if (!i2 || i2 === len) { + const s2 = decodeURIComponent(pathname.slice(11, i2 ? -1 : len)); + if (s2) return { handler: head1, params: { $id: s2 }, meta: {}, path: '/a%2Fb%2Fc/:$id' }; // /a%2Fb%2Fc/$%24id + } + } + } + } + return null; + } } return null; } diff --git a/packages/run/src/vite/__tests__/fixtures/uri-encoded/__snapshots__/uri-encoded.expected.routes.md b/packages/run/src/vite/__tests__/fixtures/uri-encoded/__snapshots__/uri-encoded.expected.routes.md index ad66e1c..aff4968 100644 --- a/packages/run/src/vite/__tests__/fixtures/uri-encoded/__snapshots__/uri-encoded.expected.routes.md +++ b/packages/run/src/vite/__tests__/fixtures/uri-encoded/__snapshots__/uri-encoded.expected.routes.md @@ -6,10 +6,14 @@ ### Handler ```js // virtual:marko-run/__marko-run__route.a_b_c.$_id.js -import { pageResponse } from 'virtual:marko-run/runtime/internal'; +import { pageResponse, stripResponseBody } from 'virtual:marko-run/runtime/internal'; import page from './src/routes/a%2Fb%2Fc/$%24id/+page.marko?marko-server-entry'; -export async function get1(context, buildInput) { +export function get1(context, buildInput) { return pageResponse(page, buildInput()); } + +export function head1(context, buildInput) { + return stripResponseBody(get1(context, buildInput)); +} ``` diff --git a/packages/run/src/vite/__tests__/main.test.cts b/packages/run/src/vite/__tests__/main.test.ts similarity index 96% rename from packages/run/src/vite/__tests__/main.test.cts rename to packages/run/src/vite/__tests__/main.test.ts index 57db6a3..32573ca 100644 --- a/packages/run/src/vite/__tests__/main.test.cts +++ b/packages/run/src/vite/__tests__/main.test.ts @@ -1,7 +1,7 @@ import fs from "fs"; -// import url from 'url'; -import snap from "mocha-snap"; +import mochaSnap from "mocha-snap"; import path from "path"; +import url from "url"; import { prepareError } from "../../adapter/utils"; import { @@ -18,7 +18,8 @@ import type { BuiltRoutes, HttpVerb } from "../types"; import { createDirectory } from "./utils/fakeFS"; import { normalizeErrorStack } from "./utils/sanitize"; -// const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); +const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); +const snap = (mochaSnap as any).default as typeof mochaSnap; const FIXTURES = path.join(__dirname, "fixtures"); diff --git a/packages/run/src/vite/codegen/index.ts b/packages/run/src/vite/codegen/index.ts index 88ca921..c202401 100644 --- a/packages/run/src/vite/codegen/index.ts +++ b/packages/run/src/vite/codegen/index.ts @@ -115,12 +115,15 @@ export function renderRouteEntry(route: Route, entriesDir: string): string { if (handler || middleware.length) { runtimeImports.push("call"); } - if (!page || verbs.length > 1) { + if (!page || verbs.some((verb) => verb !== "get" && verb !== "head")) { runtimeImports.push("noContent"); } if (page) { runtimeImports.push("pageResponse"); } + if (verbs.includes("head")) { + runtimeImports.push("stripResponseBody"); + } if (runtimeImports.length) { imports.writeLines( @@ -175,29 +178,6 @@ export function renderRouteEntry(route: Route, entriesDir: string): string { return writer.end(); } -function writePageResponse(writer: Writer, wrapFn?: string): void { - writer.writeLines( - `${ - wrapFn ? `const ${wrapFn} = () =>` : `return` - } pageResponse(page, buildInput());`, - ); -} - -function writeMiddleware( - writer: Writer, - middleware: string, - next: string, - wrapFn?: string, -): void { - if (wrapFn) { - writer.writeLines( - `const ${wrapFn} = () => call(${middleware}, ${next}, context);`, - ); - } else { - writer.writeLines(`return call(${middleware}, ${next}, context);`); - } -} - function writeRouteEntryHandler( writer: Writer, route: Route, @@ -212,49 +192,77 @@ function writeRouteEntryHandler( writer.writeLines(""); - if (page) { + if (page && (verb === "get" || verb === "head")) { writer.writeBlockStart( - `export async function ${verb}${index}(context, buildInput) {`, + `export function ${verb}${index}(context, buildInput) {`, ); } else { - writer.writeBlockStart(`export async function ${verb}${index}(context) {`); + writer.writeBlockStart(`export function ${verb}${index}(context) {`); } const continuations = writer.branch("cont"); - if (page && verb === "get") { + if (page && (verb === "get" || verb === "head")) { currentName = "__page"; if (handler?.verbs?.includes(verb)) { const name = `${verb}Handler`; - writePageResponse(continuations, currentName); + continuations.writeLines( + `const ${currentName} = () => pageResponse(page, buildInput());`, + ); if (len) { nextName = currentName; currentName = `__${name}`; - writeMiddleware(continuations, name, nextName, currentName); + continuations.writeLines( + `const ${currentName} = () => call(${name}, ${nextName}, context);`, + ); } else { - writeMiddleware(writer, name, currentName); + if (verb === "head") { + writer.writeLines( + `return stripResponseBody(call(${name}, ${currentName}, context));`, + ); + } else { + writer.writeLines(`return call(${name}, ${currentName}, context);`); + } hasBody = true; } + } else if (verb === "head") { + writer.writeLines( + `return stripResponseBody(get${index}(context, buildInput));`, + ); + hasBody = true; } else if (len) { - writePageResponse(continuations, currentName); + continuations.writeLines( + `const ${currentName} = () => pageResponse(page, buildInput());`, + ); nextName = currentName; } else { - writePageResponse(continuations); + writer.writeLines(`return pageResponse(page, buildInput());`); hasBody = true; } - } else if (handler) { + } else if (handler?.verbs?.includes(verb)) { const name = `${verb}Handler`; currentName = `__${name}`; nextName = "noContent"; if (len) { - writeMiddleware(continuations, name, nextName, currentName); + continuations.writeLines( + `const ${currentName} = () => call(${name}, ${nextName}, context);`, + ); } else { - writeMiddleware(writer, name, nextName); + if (verb === "head") { + writer.writeLines( + `return stripResponseBody(call(${name}, ${nextName}, context));`, + ); + } else { + writer.writeLines(`return call(${name}, ${nextName}, context);`); + } hasBody = true; } + } else if (verb === "head" && route.handler?.verbs?.includes("get")) { + writer.writeLines(`return stripResponseBody(get${index}(context));`); + hasBody = true; } else { throw new Error(`Route ${key} has no handler for ${verb} requests`); } @@ -264,9 +272,19 @@ function writeRouteEntryHandler( while (i--) { const { id } = middleware[i]; const name = `mware${id}`; - nextName = currentName; + nextName = currentName!; currentName = i ? `__${name}` : ""; - writeMiddleware(continuations, name, nextName, currentName); + if (currentName) { + continuations.writeLines( + `const ${currentName} = () => call(${name}, ${nextName}, context);`, + ); + } else if (verb === "head") { + continuations.writeLines( + `return stripResponseBody(call(${name}, ${nextName}, context));`, + ); + } else { + continuations.writeLines(`return call(${name}, ${nextName}, context);`); + } } } diff --git a/packages/run/src/vite/constants.ts b/packages/run/src/vite/constants.ts index 9f3cf8a..ff64457 100644 --- a/packages/run/src/vite/constants.ts +++ b/packages/run/src/vite/constants.ts @@ -4,7 +4,16 @@ export const markoRunFilePrefix = "__marko-run__"; export const virtualFilePrefix = "virtual:marko-run"; -export const httpVerbs = ["get", "post", "put", "delete"] as const; +// no support for "connect" or "trace" verbs +export const httpVerbs = [ + "get", + "head", + "post", + "put", + "delete", + "patch", + "options", +] as const; // These need to match the Marko Vite plugin export const serverEntryQuery = "?marko-server-entry"; diff --git a/packages/run/src/vite/plugin.ts b/packages/run/src/vite/plugin.ts index 8149735..25663b3 100644 --- a/packages/run/src/vite/plugin.ts +++ b/packages/run/src/vite/plugin.ts @@ -575,7 +575,6 @@ export default function markoRun(opts: Options = {}): Plugin[] { }, async resolveId(importee, importer) { if (importee === "@marko/run/router") { - console.log("plugin resolveId router"); return path.resolve(root, ROUTER_FILENAME); } else if ( importee.endsWith(".marko") && diff --git a/packages/run/src/vite/routes/builder.ts b/packages/run/src/vite/routes/builder.ts index 86bf33a..5ab43af 100644 --- a/packages/run/src/vite/routes/builder.ts +++ b/packages/run/src/vite/routes/builder.ts @@ -117,7 +117,7 @@ export async function buildRoutes( filePath: path, relativePath, importPath: `${importPrefix}/${relativePath}`, - verbs: type === RoutableFileTypes.Page ? ["get"] : undefined, + verbs: type === RoutableFileTypes.Page ? ["get", "head"] : undefined, }; for (const dir of dirs) { diff --git a/packages/run/src/vite/utils/log.ts b/packages/run/src/vite/utils/log.ts index f7c33c4..e354f46 100644 --- a/packages/run/src/vite/utils/log.ts +++ b/packages/run/src/vite/utils/log.ts @@ -11,23 +11,24 @@ import type { OutputChunk, } from "rollup"; -import type { BuiltRoutes, Route } from "../types"; +import type { BuiltRoutes, HttpVerb, Route } from "../types"; import { getVerbs } from "./route"; const HttpVerbColors = { get: kleur.green, + head: kleur.dim().green, post: kleur.magenta, put: kleur.cyan, delete: kleur.red, - other: kleur.white, + patch: kleur.yellow, + options: kleur.grey, }; -const HttpVerbOrder = { - get: 0, - post: 1, - put: 2, - delete: 3, -}; +function verbColor(verb: HttpVerb) { + return verb in HttpVerbColors + ? HttpVerbColors[verb as keyof typeof HttpVerbColors] + : kleur.gray; +} export function logRoutesTable( routes: BuiltRoutes, @@ -65,25 +66,30 @@ export function logRoutesTable( for (const route of routes.list) { for (const path of route.paths) { - const verbs = getVerbs(route).sort( - (a, b) => HttpVerbOrder[a] - HttpVerbOrder[b], - ); + const verbs = getVerbs(route, true); let firstRow = true; for (const verb of verbs) { - let size = ""; const entryType: string[] = []; + let size = ""; + let verbCell = verbColor(verb)(verb.toUpperCase()); + + if (verb === "get" && !verbs.includes("head")) { + verbCell += kleur.dim(`,${verbColor(verb)("HEAD")}`); + } if (route.handler) { entryType.push(kleur.blue("handler")); } - if (verb === "get" && route.page) { + if (route.page && (verb === "get" || verb === "head")) { entryType.push(kleur.yellow("page")); - size = prettySize(computeRouteSize(getRouteChunkName(route), bundle)); + if (verb === "get") { + size = prettySize( + computeRouteSize(getRouteChunkName(route), bundle), + ); + } } - const row: any[] = [ - kleur.bold(HttpVerbColors[verb](verb.toUpperCase())), - ]; + const row: any[] = [verbCell]; if (verbs.length === 1 || firstRow) { row.push({ rowSpan: verbs.length, content: prettyPath(path.path) }); diff --git a/packages/run/src/vite/utils/route.ts b/packages/run/src/vite/utils/route.ts index 36d5b0a..17c4681 100644 --- a/packages/run/src/vite/utils/route.ts +++ b/packages/run/src/vite/utils/route.ts @@ -1,13 +1,29 @@ +import { httpVerbs } from "../constants"; import type { HttpVerb, Route } from "../types"; -export function getVerbs(route: Route) { - const verbs = route.handler?.verbs?.slice() || []; - if (route.page && !verbs.includes("get")) { - verbs.unshift("get"); +const httpVerbOrder = httpVerbs.reduce( + (order, verb, index) => { + order[verb] = index; + return order; + }, + {} as Record, +); + +export function getVerbs(route: Route, noAutoHead?: boolean): HttpVerb[] { + const verbs = new Set(route.handler?.verbs); + if (route.page) { + verbs.add("get"); + } + if (!noAutoHead && verbs.has("get")) { + verbs.add("head"); } - return verbs; + return [...verbs].sort((a, b) => httpVerbOrder[a] - httpVerbOrder[b]); } -export function hasVerb(route: Route, verb: HttpVerb) { - return (verb === "get" && route.page) || route.handler?.verbs?.includes(verb); +export function hasVerb(route: Route, verb: HttpVerb): boolean { + return ( + (verb === "get" && !!route.page) || + route.handler?.verbs?.includes(verb) || + (verb === "head" && hasVerb(route, "get")) + ); }