aboutsummaryrefslogtreecommitdiff
path: root/packages/astro/src/vite-plugin-load-fallback/index.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/astro/src/vite-plugin-load-fallback/index.ts')
-rw-r--r--packages/astro/src/vite-plugin-load-fallback/index.ts82
1 files changed, 82 insertions, 0 deletions
diff --git a/packages/astro/src/vite-plugin-load-fallback/index.ts b/packages/astro/src/vite-plugin-load-fallback/index.ts
new file mode 100644
index 000000000..02385207d
--- /dev/null
+++ b/packages/astro/src/vite-plugin-load-fallback/index.ts
@@ -0,0 +1,82 @@
+import nodeFs from 'node:fs';
+import npath from 'node:path';
+import type * as vite from 'vite';
+import { slash } from '../core/path.js';
+import { cleanUrl } from '../vite-plugin-utils/index.js';
+
+type NodeFileSystemModule = typeof nodeFs;
+
+interface LoadFallbackPluginParams {
+ fs?: NodeFileSystemModule;
+ root: URL;
+}
+
+export default function loadFallbackPlugin({
+ fs,
+ root,
+}: LoadFallbackPluginParams): vite.Plugin[] | false {
+ // 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 false;
+ }
+
+ const tryLoadModule = async (id: string) => {
+ 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 {
+ // Let fall through to the next
+ }
+ }
+ }
+ };
+
+ 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()) {
+ return candidateId;
+ }
+ } catch {}
+ }
+ },
+ async load(id) {
+ const code = await tryLoadModule(id);
+ 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);
+ };
+ },
+ },
+ ];
+}