I'm experimenting with Nuxt 3, and was wondering if it was possible to override a particular part of Nitro's runtime behaviour. The Nuxt docs suggest extending Nitro's runtime behaviour is possible through server plugins, it's just a little thin on the details at the moment.
Nuxt will automatically read any files in the ~/server/plugins directory and register them as Nitro plugins. This allows extending Nitro's runtime behavior and hooking into lifecycle events.
What I'm trying to achieve:
When a request is made for a static asset, Nitro attempts to serve it from <app root>/public/
. If the asset isn't found, Nitro returns a 404. I'd like to have Nitro attempt to serve the same static asset from a different sub directory of <app root>/public/
, return it if found there, throw the 404 otherwise.
How this could work:
After running a build doing some digging, I found the code block responsible for serving assets seems to live in .output/server/chunks/node-server.mjs
, as follows:
const _f4b49z = eventHandler((event) => {
if (event.req.method && !METHODS.includes(event.req.method)) {
return;
}
let id = decodeURIComponent(withLeadingSlash(withoutTrailingSlash(parseURL(event.req.url).pathname)));
let asset;
const encodingHeader = String(event.req.headers["accept-encoding"] || "");
const encodings = encodingHeader.split(",").map((e) => EncodingMap[e.trim()]).filter(Boolean).sort().concat([""]);
if (encodings.length > 1) {
event.res.setHeader("Vary", "Accept-Encoding");
}
for (const encoding of encodings) {
for (const _id of [id + encoding, joinURL(id, "index.html" + encoding)]) {
const _asset = getAsset(_id);
if (_asset) {
asset = _asset;
id = _id;
break;
}
}
}
if (!asset) {
if (isPublicAssetURL(id)) {
throw createError({
statusMessage: "Cannot find static asset " + id,
statusCode: 404
});
}
return;
}
const ifNotMatch = event.req.headers["if-none-match"] === asset.etag;
if (ifNotMatch) {
event.res.statusCode = 304;
event.res.end();
return;
}
const ifModifiedSinceH = event.req.headers["if-modified-since"];
if (ifModifiedSinceH && asset.mtime) {
if (new Date(ifModifiedSinceH) >= new Date(asset.mtime)) {
event.res.statusCode = 304;
event.res.end();
return;
}
}
if (asset.type && !event.res.getHeader("Content-Type")) {
event.res.setHeader("Content-Type", asset.type);
}
if (asset.etag && !event.res.getHeader("ETag")) {
event.res.setHeader("ETag", asset.etag);
}
if (asset.mtime && !event.res.getHeader("Last-Modified")) {
event.res.setHeader("Last-Modified", asset.mtime);
}
if (asset.encoding && !event.res.getHeader("Content-Encoding")) {
event.res.setHeader("Content-Encoding", asset.encoding);
}
if (asset.size && !event.res.getHeader("Content-Length")) {
event.res.setHeader("Content-Length", asset.size);
}
return readAsset(id);
});
Is it possible to override that specific method? I'd basically like to change:
for (const encoding of encodings) {
for (const _id of [id + encoding, joinURL(id, "index.html" + encoding)]) {
const _asset = getAsset(_id);
if (_asset) {
asset = _asset;
id = _id;
break;
}
}
}
In to:
for (const encoding of encodings) {
for (let _id of [id + encoding, joinURL(id, "index.html" + encoding)]) {
let _asset = getAsset(_id);
// if the asset wasn't found at 'public/', try 'public/subDir'
if (!_asset) {
_asset = assets['/subDir' + _id]
_id = '/subDir' + _id
}
if (_asset) {
asset = _asset;
id = _id;
break;
}
}
}
I'm exploring how serving multiple sites from a single Nuxt app might work, without having to change anything outside of the code base. I've managed to get along so far, but I need a solution for static assets (robots.txt, well-known URI, etc).