-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Integration defined middleware (#8869)
* Rebase * Use an empty module if there is no real middleware * Add debug logging * Use normalizePath * Add a better example in the changesetp * Update .changeset/khaki-glasses-raise.md Co-authored-by: Bjorn Lu <bjornlu.dev@gmail.com> * Update .changeset/khaki-glasses-raise.md Co-authored-by: Bjorn Lu <bjornlu.dev@gmail.com> * Update .changeset/khaki-glasses-raise.md Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> * Update .changeset/khaki-glasses-raise.md Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> * Update .changeset/khaki-glasses-raise.md Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> * Update .changeset/khaki-glasses-raise.md Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> * Update packages/astro/src/core/middleware/vite-plugin.ts Co-authored-by: Emanuele Stoppa <my.burning@gmail.com> * Review comments * oops * Update .changeset/khaki-glasses-raise.md Co-authored-by: Bjorn Lu <bjornlu.dev@gmail.com> --------- Co-authored-by: Bjorn Lu <bjornlu.dev@gmail.com> Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> Co-authored-by: Emanuele Stoppa <my.burning@gmail.com>
- Loading branch information
1 parent
b093794
commit f5bdfa2
Showing
15 changed files
with
268 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
--- | ||
'astro': minor | ||
--- | ||
|
||
## Integration Hooks to add Middleware | ||
|
||
It's now possible in Astro for an integration to add middleware on behalf of the user. Previously when a third party wanted to provide middleware, the user would need to create a `src/middleware.ts` file themselves. Now, adding third-party middleware is as easy as adding a new integration. | ||
|
||
For integration authors, there is a new `addMiddleware` function in the `astro:config:setup` hook. This function allows you to specify a middleware module and the order in which it should be applied: | ||
|
||
```js | ||
// my-package/middleware.js | ||
import { defineMiddleware } from 'astro:middleware'; | ||
|
||
export const onRequest = defineMiddleware(async (context, next) => { | ||
const response = await next(); | ||
|
||
if(response.headers.get('content-type') === 'text/html') { | ||
let html = await response.text(); | ||
html = minify(html); | ||
return new Response(html, { | ||
status: response.status, | ||
headers: response.headers | ||
}); | ||
} | ||
|
||
return response; | ||
}); | ||
``` | ||
|
||
You can now add your integration's middleware and specify that it runs either before or after the application's own defined middleware (defined in `src/middleware.{js,ts}`) | ||
|
||
```js | ||
// my-package/integration.js | ||
export function myIntegration() { | ||
return { | ||
name: 'my-integration', | ||
hooks: { | ||
'astro:config:setup': ({ addMiddleware }) => { | ||
addMiddleware({ | ||
entrypoint: 'my-package/middleware', | ||
order: 'pre' | ||
}); | ||
} | ||
} | ||
}; | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import type { Plugin as VitePlugin } from 'vite'; | ||
import { normalizePath } from 'vite'; | ||
import { getOutputDirectory } from '../../prerender/utils.js'; | ||
import { MIDDLEWARE_PATH_SEGMENT_NAME } from '../constants.js'; | ||
import { addRollupInput } from '../build/add-rollup-input.js'; | ||
import type { BuildInternals } from '../build/internal.js'; | ||
import type { StaticBuildOptions } from '../build/types.js'; | ||
import type { AstroSettings } from '../../@types/astro.js'; | ||
|
||
export const MIDDLEWARE_MODULE_ID = '@astro-middleware'; | ||
const EMPTY_MIDDLEWARE = '\0empty-middleware'; | ||
|
||
export function vitePluginMiddleware({ | ||
settings | ||
}: { | ||
settings: AstroSettings | ||
}): VitePlugin { | ||
let isCommandBuild = false; | ||
let resolvedMiddlewareId: string | undefined = undefined; | ||
const hasIntegrationMiddleware = settings.middlewares.pre.length > 0 || settings.middlewares.post.length > 0; | ||
|
||
return { | ||
name: '@astro/plugin-middleware', | ||
|
||
config(opts, { command }) { | ||
isCommandBuild = command === 'build'; | ||
return opts; | ||
}, | ||
|
||
async resolveId(id) { | ||
if (id === MIDDLEWARE_MODULE_ID) { | ||
const middlewareId = await this.resolve( | ||
`${decodeURI(settings.config.srcDir.pathname)}${MIDDLEWARE_PATH_SEGMENT_NAME}` | ||
); | ||
if (middlewareId) { | ||
resolvedMiddlewareId = middlewareId.id; | ||
return MIDDLEWARE_MODULE_ID; | ||
} else if(hasIntegrationMiddleware) { | ||
return MIDDLEWARE_MODULE_ID; | ||
} else { | ||
return EMPTY_MIDDLEWARE; | ||
} | ||
} | ||
if (id === EMPTY_MIDDLEWARE) { | ||
return EMPTY_MIDDLEWARE; | ||
} | ||
}, | ||
|
||
async load(id) { | ||
if (id === EMPTY_MIDDLEWARE) { | ||
return 'export const onRequest = undefined'; | ||
} else if (id === MIDDLEWARE_MODULE_ID) { | ||
// In the build, tell Vite to emit this file | ||
if(isCommandBuild) { | ||
this.emitFile({ | ||
type: 'chunk', | ||
preserveSignature: 'strict', | ||
fileName: 'middleware.mjs', | ||
id, | ||
}); | ||
} | ||
|
||
const preMiddleware = createMiddlewareImports(settings.middlewares.pre, 'pre'); | ||
const postMiddleware = createMiddlewareImports(settings.middlewares.post, 'post'); | ||
|
||
const source = ` | ||
import { onRequest as userOnRequest } from '${resolvedMiddlewareId}'; | ||
import { sequence } from 'astro:middleware'; | ||
${preMiddleware.importsCode}${postMiddleware.importsCode} | ||
export const onRequest = sequence( | ||
${preMiddleware.sequenceCode}${preMiddleware.sequenceCode ? ',' : ''} | ||
userOnRequest${postMiddleware.sequenceCode ? ',' : ''} | ||
${postMiddleware.sequenceCode} | ||
); | ||
`.trim(); | ||
|
||
return source; | ||
} | ||
}, | ||
}; | ||
} | ||
|
||
function createMiddlewareImports(entrypoints: string[], prefix: string): { | ||
importsCode: string | ||
sequenceCode: string | ||
} { | ||
let importsRaw = ''; | ||
let sequenceRaw = ''; | ||
let index = 0; | ||
for(const entrypoint of entrypoints) { | ||
const name = `_${prefix}_${index}`; | ||
importsRaw += `import { onRequest as ${name} } from '${normalizePath(entrypoint)}';\n`; | ||
sequenceRaw += `${index > 0 ? ',' : ''}${name}` | ||
index++; | ||
} | ||
|
||
return { | ||
importsCode: importsRaw, | ||
sequenceCode: sequenceRaw | ||
}; | ||
} | ||
|
||
export function vitePluginMiddlewareBuild( | ||
opts: StaticBuildOptions, | ||
internals: BuildInternals | ||
): VitePlugin { | ||
return { | ||
name: '@astro/plugin-middleware-build', | ||
|
||
options(options) { | ||
return addRollupInput(options, [MIDDLEWARE_MODULE_ID]); | ||
}, | ||
|
||
writeBundle(_, bundle) { | ||
for (const [chunkName, chunk] of Object.entries(bundle)) { | ||
if (chunk.type !== 'asset' && chunk.fileName === 'middleware.mjs') { | ||
const outputDirectory = getOutputDirectory(opts.settings.config); | ||
internals.middlewareEntryPoint = new URL(chunkName, outputDirectory); | ||
} | ||
} | ||
}, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.