astro
Version:
Astro is a modern site builder with web best practices, performance, and DX front-of-mind.
69 lines (68 loc) • 2.07 kB
JavaScript
import nodeFs from "node:fs";
import npath from "node:path";
import { slash } from "../core/path.js";
import { cleanUrl } from "../vite-plugin-utils/index.js";
const FALLBACK_FLAG = "astroFallbackFlag";
export default function loadFallbackPlugin({ fs, root }) {
// Only add this plugin if a custom fs implementation is provided.
// Also check for `fs.default` because `import * as fs from 'node:fs'` will
// export as so, which only it's `.default` would === `nodeFs`.
// @ts-expect-error check default
if (!fs || fs === nodeFs || fs.default === nodeFs) {
return [];
}
const tryLoadModule = async (id) => {
try {
// await is necessary for the catch
return await fs.promises.readFile(cleanUrl(id), "utf-8");
} catch {
try {
return await fs.promises.readFile(id, "utf-8");
} catch {
try {
const fullpath = new URL("." + id, root);
return await fs.promises.readFile(fullpath, "utf-8");
} catch {}
}
}
};
return [{
name: "astro:load-fallback",
enforce: "post",
async resolveId(id, parent) {
// See if this can be loaded from our fs
if (parent) {
const candidateId = npath.posix.join(npath.posix.dirname(slash(parent)), id);
try {
// Check to see if this file exists and is not a directory.
const stats = await fs.promises.stat(candidateId);
if (!stats.isDirectory()) {
const params = new URLSearchParams(FALLBACK_FLAG);
return `${candidateId}?${params.toString()}`;
}
} catch {}
}
},
load: {
filter: { id: new RegExp(`(?:\\?|&)${FALLBACK_FLAG}(?:&|=|$)`) },
async handler(id) {
const code = await tryLoadModule(id.slice(0, -(1 + FALLBACK_FLAG.length)));
if (code) {
return { code };
}
}
}
}, {
name: "astro:load-fallback-hmr",
enforce: "pre",
handleHotUpdate(context) {
// Wrap context.read so it checks our filesystem first.
const read = context.read;
context.read = async () => {
const source = await tryLoadModule(context.file);
if (source) return source;
return read.call(context);
};
}
}];
}