@remix-run/static-middleware 0.0.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
- import { type FileResponseOptions } from '@remix-run/fetch-router/response-helpers';
2
1
  import type { Middleware } from '@remix-run/fetch-router';
3
- export type StaticFilesOptions = FileResponseOptions & {
2
+ import { type FileResponseOptions } from '@remix-run/fetch-router/response-helpers';
3
+ export interface StaticFilesOptions extends FileResponseOptions {
4
4
  /**
5
5
  * Filter function to determine which files should be served.
6
6
  *
@@ -8,7 +8,15 @@ export type StaticFilesOptions = FileResponseOptions & {
8
8
  * @returns Whether to serve the file
9
9
  */
10
10
  filter?: (path: string) => boolean;
11
- };
11
+ /**
12
+ * Files to try and serve as the index file when the request path targets a directory.
13
+ *
14
+ * - `true` (default): Use default index files `['index.html', 'index.htm']`
15
+ * - `false`: Disable index file serving
16
+ * - `string[]`: Custom list of index files to try in order
17
+ */
18
+ index?: boolean | string[];
19
+ }
12
20
  /**
13
21
  * Creates a middleware that serves static files from the filesystem.
14
22
  *
@@ -1 +1 @@
1
- {"version":3,"file":"static.d.ts","sourceRoot":"","sources":["../../src/lib/static.ts"],"names":[],"mappings":"AAEA,OAAO,EAAQ,KAAK,mBAAmB,EAAE,MAAM,0CAA0C,CAAA;AACzF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AAEzD,MAAM,MAAM,kBAAkB,GAAG,mBAAmB,GAAG;IACrD;;;;;OAKG;IACH,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;CACnC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,UAAU,CAsBtF"}
1
+ {"version":3,"file":"static.d.ts","sourceRoot":"","sources":["../../src/lib/static.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,0CAA0C,CAAA;AAEjD,MAAM,WAAW,kBAAmB,SAAQ,mBAAmB;IAC7D;;;;;OAKG;IACH,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;IAClC;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,EAAE,CAAA;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,UAAU,CA4DtF"}
@@ -1,5 +1,7 @@
1
- import { findFile } from '@remix-run/lazy-file/fs';
2
- import { file } from '@remix-run/fetch-router/response-helpers';
1
+ import * as path from 'node:path';
2
+ import * as fsp from 'node:fs/promises';
3
+ import { openFile } from '@remix-run/fs';
4
+ import { file as sendFile, } from '@remix-run/fetch-router/response-helpers';
3
5
  /**
4
6
  * Creates a middleware that serves static files from the filesystem.
5
7
  *
@@ -26,19 +28,59 @@ import { file } from '@remix-run/fetch-router/response-helpers';
26
28
  * })
27
29
  */
28
30
  export function staticFiles(root, options = {}) {
29
- let { filter, ...fileOptions } = options;
31
+ // Ensure root is an absolute path
32
+ root = path.resolve(root);
33
+ let { filter, index: indexOption, ...fileOptions } = options;
34
+ // Normalize index option
35
+ let index;
36
+ if (indexOption === false) {
37
+ index = [];
38
+ }
39
+ else if (indexOption === true || indexOption === undefined) {
40
+ index = ['index.html', 'index.htm'];
41
+ }
42
+ else {
43
+ index = indexOption;
44
+ }
30
45
  return async (context, next) => {
31
- if (context.request.method !== 'GET' && context.request.method !== 'HEAD') {
46
+ if (context.method !== 'GET' && context.method !== 'HEAD') {
32
47
  return next();
33
48
  }
34
49
  let relativePath = context.url.pathname.replace(/^\/+/, '');
35
50
  if (filter && !filter(relativePath)) {
36
51
  return next();
37
52
  }
38
- let fileToServe = await findFile(root, relativePath);
39
- if (!fileToServe) {
40
- return next();
53
+ let targetPath = path.join(root, relativePath);
54
+ let filePath;
55
+ try {
56
+ let stats = await fsp.stat(targetPath);
57
+ if (stats.isFile()) {
58
+ filePath = targetPath;
59
+ }
60
+ else if (stats.isDirectory()) {
61
+ // Try each index file in turn
62
+ for (let indexFile of index) {
63
+ let indexPath = path.join(targetPath, indexFile);
64
+ try {
65
+ let indexStats = await fsp.stat(indexPath);
66
+ if (indexStats.isFile()) {
67
+ filePath = indexPath;
68
+ break;
69
+ }
70
+ }
71
+ catch {
72
+ // Index file doesn't exist, continue to next
73
+ }
74
+ }
75
+ }
76
+ }
77
+ catch {
78
+ // Path doesn't exist or isn't accessible, fall through
79
+ }
80
+ if (filePath) {
81
+ let fileName = path.relative(root, filePath);
82
+ let file = openFile(filePath, { name: fileName });
83
+ return sendFile(file, context.request, fileOptions);
41
84
  }
42
- return file(fileToServe, context.request, fileOptions);
43
85
  };
44
86
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remix-run/static-middleware",
3
- "version": "0.0.0",
3
+ "version": "0.2.0",
4
4
  "description": "Middleware for serving static files from the filesystem",
5
5
  "author": "Michael Jackson <mjijackson@gmail.com>",
6
6
  "license": "MIT",
@@ -28,12 +28,16 @@
28
28
  "devDependencies": {
29
29
  "@types/node": "^24.6.0",
30
30
  "typescript": "^5.9.3",
31
- "@remix-run/fetch-router": "0.9.0",
32
- "@remix-run/lazy-file": "3.8.0"
31
+ "@remix-run/fetch-router": "0.10.0",
32
+ "@remix-run/form-data-middleware": "0.1.0",
33
+ "@remix-run/lazy-file": "4.0.0",
34
+ "@remix-run/fs": "0.1.0",
35
+ "@remix-run/method-override-middleware": "0.1.0"
33
36
  },
34
37
  "peerDependencies": {
35
- "@remix-run/fetch-router": "^0.9.0",
36
- "@remix-run/lazy-file": "^3.8.0"
38
+ "@remix-run/fetch-router": "^0.10.0",
39
+ "@remix-run/fs": "^0.1.0",
40
+ "@remix-run/lazy-file": "^4.0.0"
37
41
  },
38
42
  "keywords": [
39
43
  "fetch",
package/src/lib/static.ts CHANGED
@@ -1,9 +1,13 @@
1
- import { findFile } from '@remix-run/lazy-file/fs'
2
-
3
- import { file, type FileResponseOptions } from '@remix-run/fetch-router/response-helpers'
1
+ import * as path from 'node:path'
2
+ import * as fsp from 'node:fs/promises'
3
+ import { openFile } from '@remix-run/fs'
4
4
  import type { Middleware } from '@remix-run/fetch-router'
5
+ import {
6
+ file as sendFile,
7
+ type FileResponseOptions,
8
+ } from '@remix-run/fetch-router/response-helpers'
5
9
 
6
- export type StaticFilesOptions = FileResponseOptions & {
10
+ export interface StaticFilesOptions extends FileResponseOptions {
7
11
  /**
8
12
  * Filter function to determine which files should be served.
9
13
  *
@@ -11,6 +15,14 @@ export type StaticFilesOptions = FileResponseOptions & {
11
15
  * @returns Whether to serve the file
12
16
  */
13
17
  filter?: (path: string) => boolean
18
+ /**
19
+ * Files to try and serve as the index file when the request path targets a directory.
20
+ *
21
+ * - `true` (default): Use default index files `['index.html', 'index.htm']`
22
+ * - `false`: Disable index file serving
23
+ * - `string[]`: Custom list of index files to try in order
24
+ */
25
+ index?: boolean | string[]
14
26
  }
15
27
 
16
28
  /**
@@ -39,10 +51,23 @@ export type StaticFilesOptions = FileResponseOptions & {
39
51
  * })
40
52
  */
41
53
  export function staticFiles(root: string, options: StaticFilesOptions = {}): Middleware {
42
- let { filter, ...fileOptions } = options
54
+ // Ensure root is an absolute path
55
+ root = path.resolve(root)
56
+
57
+ let { filter, index: indexOption, ...fileOptions } = options
58
+
59
+ // Normalize index option
60
+ let index: string[]
61
+ if (indexOption === false) {
62
+ index = []
63
+ } else if (indexOption === true || indexOption === undefined) {
64
+ index = ['index.html', 'index.htm']
65
+ } else {
66
+ index = indexOption
67
+ }
43
68
 
44
69
  return async (context, next) => {
45
- if (context.request.method !== 'GET' && context.request.method !== 'HEAD') {
70
+ if (context.method !== 'GET' && context.method !== 'HEAD') {
46
71
  return next()
47
72
  }
48
73
 
@@ -52,12 +77,37 @@ export function staticFiles(root: string, options: StaticFilesOptions = {}): Mid
52
77
  return next()
53
78
  }
54
79
 
55
- let fileToServe = await findFile(root, relativePath)
80
+ let targetPath = path.join(root, relativePath)
81
+ let filePath: string | undefined
56
82
 
57
- if (!fileToServe) {
58
- return next()
83
+ try {
84
+ let stats = await fsp.stat(targetPath)
85
+
86
+ if (stats.isFile()) {
87
+ filePath = targetPath
88
+ } else if (stats.isDirectory()) {
89
+ // Try each index file in turn
90
+ for (let indexFile of index) {
91
+ let indexPath = path.join(targetPath, indexFile)
92
+ try {
93
+ let indexStats = await fsp.stat(indexPath)
94
+ if (indexStats.isFile()) {
95
+ filePath = indexPath
96
+ break
97
+ }
98
+ } catch {
99
+ // Index file doesn't exist, continue to next
100
+ }
101
+ }
102
+ }
103
+ } catch {
104
+ // Path doesn't exist or isn't accessible, fall through
59
105
  }
60
106
 
61
- return file(fileToServe, context.request, fileOptions)
107
+ if (filePath) {
108
+ let fileName = path.relative(root, filePath)
109
+ let file = openFile(filePath, { name: fileName })
110
+ return sendFile(file, context.request, fileOptions)
111
+ }
62
112
  }
63
113
  }