vite-plugin-files-server 1.0.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.
package/README.md ADDED
@@ -0,0 +1 @@
1
+ # vite-plugin-files-server
package/dist/index.cjs ADDED
@@ -0,0 +1,416 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ default: () => fileServerPlugin
34
+ });
35
+ module.exports = __toCommonJS(index_exports);
36
+ var import_fs = __toESM(require("fs"), 1);
37
+ var import_path = __toESM(require("path"), 1);
38
+
39
+ // src/temp.html?raw
40
+ var temp_default = `<!DOCTYPE html>\r
41
+ <html>\r
42
+ <head>\r
43
+ <meta charset="UTF-8" />\r
44
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />\r
45
+ <title>listing directory {{url}}</title>\r
46
+ <style>\r
47
+ /* body {\r
48
+ font-family: system-ui;\r
49
+ padding: 2rem;\r
50
+ max-width: 800px;\r
51
+ margin: 0 auto;\r
52
+ }\r
53
+ h2 {\r
54
+ border-bottom: 1px solid #eee;\r
55
+ padding-bottom: 10px;\r
56
+ }\r
57
+ table {\r
58
+ width: 100%;\r
59
+ border-collapse: collapse;\r
60
+ }\r
61
+ td {\r
62
+ padding: 8px;\r
63
+ border-bottom: 1px solid #f5f5f5;\r
64
+ }\r
65
+ a {\r
66
+ text-decoration: none;\r
67
+ color: #646cff;\r
68
+ }\r
69
+ a:hover {\r
70
+ text-decoration: underline;\r
71
+ }\r
72
+ ul,\r
73
+ li {\r
74
+ list-style: none;\r
75
+ margin: 0;\r
76
+ padding: 0;\r
77
+ } */\r
78
+ * {\r
79
+ margin: 0;\r
80
+ padding: 0;\r
81
+ outline: 0;\r
82
+ }\r
83
+ \r
84
+ body {\r
85
+ padding: 80px 100px;\r
86
+ font: 13px 'Helvetica Neue', 'Lucida Grande', 'Arial';\r
87
+ background: #ece9e9 -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ece9e9));\r
88
+ background: #ece9e9 -moz-linear-gradient(top, #fff, #ece9e9);\r
89
+ background-repeat: no-repeat;\r
90
+ color: #555;\r
91
+ -webkit-font-smoothing: antialiased;\r
92
+ }\r
93
+ h1,\r
94
+ h2,\r
95
+ h3 {\r
96
+ padding-left: 10px;\r
97
+ font-size: 22px;\r
98
+ color: #343434;\r
99
+ }\r
100
+ h1 em,\r
101
+ h2 em {\r
102
+ padding: 0 5px;\r
103
+ font-weight: normal;\r
104
+ }\r
105
+ h1 {\r
106
+ font-size: 60px;\r
107
+ }\r
108
+ h2 {\r
109
+ margin-top: 10px;\r
110
+ }\r
111
+ h3 {\r
112
+ margin: 5px 0 10px 0;\r
113
+ padding-bottom: 5px;\r
114
+ border-bottom: 1px solid #eee;\r
115
+ font-size: 18px;\r
116
+ }\r
117
+ ul li {\r
118
+ list-style: none;\r
119
+ }\r
120
+ ul li:hover {\r
121
+ cursor: pointer;\r
122
+ color: #2e2e2e;\r
123
+ }\r
124
+ ul li .path {\r
125
+ padding-left: 5px;\r
126
+ font-weight: bold;\r
127
+ }\r
128
+ ul li .line {\r
129
+ padding-right: 5px;\r
130
+ font-style: italic;\r
131
+ }\r
132
+ ul li:first-child .path {\r
133
+ padding-left: 0;\r
134
+ }\r
135
+ p {\r
136
+ line-height: 1.5;\r
137
+ }\r
138
+ a {\r
139
+ color: #555;\r
140
+ text-decoration: none;\r
141
+ }\r
142
+ a:hover {\r
143
+ color: #303030;\r
144
+ }\r
145
+ #stacktrace {\r
146
+ margin-top: 15px;\r
147
+ }\r
148
+ .directory h1 {\r
149
+ margin-bottom: 15px;\r
150
+ font-size: 18px;\r
151
+ }\r
152
+ ul#files {\r
153
+ width: 100%;\r
154
+ height: 100%;\r
155
+ overflow: hidden;\r
156
+ }\r
157
+ ul#files li {\r
158
+ float: left;\r
159
+ width: 30%;\r
160
+ line-height: 25px;\r
161
+ margin: 1px;\r
162
+ }\r
163
+ ul#files li a {\r
164
+ display: block;\r
165
+ height: 25px;\r
166
+ border: 1px solid transparent;\r
167
+ -webkit-border-radius: 5px;\r
168
+ -moz-border-radius: 5px;\r
169
+ border-radius: 5px;\r
170
+ overflow: hidden;\r
171
+ white-space: nowrap;\r
172
+ }\r
173
+ ul#files li a:focus,\r
174
+ ul#files li a:hover {\r
175
+ background: rgba(255, 255, 255, 0.65);\r
176
+ border: 1px solid #ececec;\r
177
+ }\r
178
+ ul#files li a.highlight {\r
179
+ -webkit-transition: background 0.4s ease-in-out;\r
180
+ background: #ffff4f;\r
181
+ border-color: #e9dc51;\r
182
+ }\r
183
+ #search {\r
184
+ display: block;\r
185
+ position: fixed;\r
186
+ top: 20px;\r
187
+ right: 20px;\r
188
+ width: 90px;\r
189
+ -webkit-transition: width ease 0.2s, opacity ease 0.4s;\r
190
+ -moz-transition: width ease 0.2s, opacity ease 0.4s;\r
191
+ -webkit-border-radius: 32px;\r
192
+ -moz-border-radius: 32px;\r
193
+ -webkit-box-shadow: inset 0px 0px 3px rgba(0, 0, 0, 0.25), inset 0px 1px 3px rgba(0, 0, 0, 0.7),\r
194
+ 0px 1px 0px rgba(255, 255, 255, 0.03);\r
195
+ -moz-box-shadow: inset 0px 0px 3px rgba(0, 0, 0, 0.25), inset 0px 1px 3px rgba(0, 0, 0, 0.7),\r
196
+ 0px 1px 0px rgba(255, 255, 255, 0.03);\r
197
+ -webkit-font-smoothing: antialiased;\r
198
+ text-align: left;\r
199
+ font: 13px 'Helvetica Neue', Arial, sans-serif;\r
200
+ padding: 4px 10px;\r
201
+ border: none;\r
202
+ background: transparent;\r
203
+ margin-bottom: 0;\r
204
+ outline: none;\r
205
+ opacity: 0.7;\r
206
+ color: #888;\r
207
+ }\r
208
+ #search:focus {\r
209
+ width: 120px;\r
210
+ opacity: 1;\r
211
+ }\r
212
+ \r
213
+ /*views*/\r
214
+ #files span {\r
215
+ display: inline-block;\r
216
+ overflow: hidden;\r
217
+ text-overflow: ellipsis;\r
218
+ text-indent: 10px;\r
219
+ }\r
220
+ #files .name {\r
221
+ background-repeat: no-repeat;\r
222
+ }\r
223
+ #files .icon .name {\r
224
+ text-indent: 28px;\r
225
+ }\r
226
+ \r
227
+ /*tiles*/\r
228
+ .view-tiles .name {\r
229
+ width: 100%;\r
230
+ background-position: 8px 5px;\r
231
+ }\r
232
+ .view-tiles .size,\r
233
+ .view-tiles .date {\r
234
+ display: none;\r
235
+ }\r
236
+ \r
237
+ /*details*/\r
238
+ ul#files.view-details li {\r
239
+ float: none;\r
240
+ display: block;\r
241
+ width: 90%;\r
242
+ }\r
243
+ ul#files.view-details li.header {\r
244
+ height: 25px;\r
245
+ background: #000;\r
246
+ color: #fff;\r
247
+ font-weight: bold;\r
248
+ }\r
249
+ .view-details .header {\r
250
+ border-radius: 5px;\r
251
+ }\r
252
+ .view-details .name {\r
253
+ width: 60%;\r
254
+ background-position: 8px 5px;\r
255
+ }\r
256
+ .view-details .size {\r
257
+ width: 10%;\r
258
+ }\r
259
+ .view-details .date {\r
260
+ width: 30%;\r
261
+ }\r
262
+ .view-details .size,\r
263
+ .view-details .date {\r
264
+ text-align: right;\r
265
+ direction: rtl;\r
266
+ }\r
267
+ \r
268
+ /*mobile*/\r
269
+ @media (max-width: 768px) {\r
270
+ body {\r
271
+ font-size: 13px;\r
272
+ line-height: 16px;\r
273
+ padding: 0;\r
274
+ }\r
275
+ #search {\r
276
+ position: static;\r
277
+ width: 100%;\r
278
+ font-size: 2em;\r
279
+ line-height: 1.8em;\r
280
+ text-indent: 10px;\r
281
+ border: 0;\r
282
+ border-radius: 0;\r
283
+ padding: 10px 0;\r
284
+ margin: 0;\r
285
+ }\r
286
+ #search:focus {\r
287
+ width: 100%;\r
288
+ border: 0;\r
289
+ opacity: 1;\r
290
+ }\r
291
+ .directory h1 {\r
292
+ font-size: 2em;\r
293
+ line-height: 1.5em;\r
294
+ color: #fff;\r
295
+ background: #000;\r
296
+ padding: 15px 10px;\r
297
+ margin: 0;\r
298
+ }\r
299
+ ul#files {\r
300
+ border-top: 1px solid #cacaca;\r
301
+ }\r
302
+ ul#files li {\r
303
+ float: none;\r
304
+ width: auto !important;\r
305
+ display: block;\r
306
+ border-bottom: 1px solid #cacaca;\r
307
+ font-size: 2em;\r
308
+ line-height: 1.2em;\r
309
+ text-indent: 0;\r
310
+ margin: 0;\r
311
+ }\r
312
+ ul#files li:nth-child(odd) {\r
313
+ background: #e0e0e0;\r
314
+ }\r
315
+ ul#files li a {\r
316
+ height: auto;\r
317
+ border: 0;\r
318
+ border-radius: 0;\r
319
+ padding: 15px 10px;\r
320
+ }\r
321
+ ul#files li a:focus,\r
322
+ ul#files li a:hover {\r
323
+ border: 0;\r
324
+ }\r
325
+ #files .header,\r
326
+ #files .size,\r
327
+ #files .date {\r
328
+ display: none !important;\r
329
+ }\r
330
+ #files .name {\r
331
+ float: none;\r
332
+ display: inline-block;\r
333
+ width: 100%;\r
334
+ text-indent: 0;\r
335
+ background-position: 0 50%;\r
336
+ }\r
337
+ #files .icon .name {\r
338
+ text-indent: 41px;\r
339
+ }\r
340
+ }\r
341
+ #files .icon-directory .name {\r
342
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAAWtQTFRFAAAA/PPQ9Nhc2q402qQ12qs2/PTX2pg12p81+/LM89NE9dto2q82+/fp2rM22qY39d6U+/bo2qo2/frx/vz32q812qs12qE279SU8c4w9NZP+/LK//367s9y7s925cp0/vzw9t92//342po2/vz25s1579B6+OSO2bQ0/v799NyT8tE79dld8Msm+OrC/vzx79KA2IYs7s6I9d6R4cJe9+OF/PLI/fry79OF/v30//328tWB89RJ8c9p8c0u9eCf//7+9txs6sts5Mdr+++5+u2z/vrv+/fq6cFz8dBs8tA57cpq+OaU9uGs27Y8//799NdX/PbY9uB89unJ//z14sNf+emh+emk+vDc+uys9+OL8dJy89NH+eic8tN5+OaV+OWR9N2n9dtl9t529+KF9+GB9Nue9NdU8tR/9t5y89qW9dpj89iO89eG/vvu2pQ12Y4z/vzy2Ict/vvv48dr/vzz4sNg///+2Igty3PqwQAAAAF0Uk5TAEDm2GYAAACtSURBVBjTY2AgA2iYlJWVhfohBPg0yx38y92dS0pKVOVBAqIi6sb2vsWWpfrFeTI8QAEhYQEta28nCwM1OVleZqCAmKCEkUdwYWmhQnFeOStQgL9cySqkNNDHVJGbiY0FKCCuYuYSGRsV5KgjxcXIARRQNncNj09JTgqw0ZbkZAcK5LuFJaRmZqfHeNnpSucDBQoiEtOycnIz4qI9bfUKQA6pKKqAgqIKQyK8BgAZ5yfODmnHrQAAAABJRU5ErkJggg==);\r
343
+ }\r
344
+ #files .icon-text-html .name {\r
345
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHtSURBVDjLjZM9T9tQFIYpQ5eOMBKlW6eWIQipa8RfQKQghEAKqZgKFQgmFn5AWyVDCipVQZC2EqBWlEqdO2RCpAssQBRsx1+1ndix8wFvfW6wcUhQsfTI0j33PD7n+N4uAF2E+/S5RFwG/8Njl24/LyCIOI6j1+v1y0ajgU64cSSTybdBSVAwSMmmacKyLB/DMKBpGkRRZBJBEJBKpXyJl/yABLTBtm1Uq1X2JsrlMnRdhyRJTFCpVEAfSafTTUlQoFs1luxBAkoolUqQZbmtJTYTT/AoHInOfpcwtVtkwcSBgrkDGYph+60oisIq4Xm+VfB0+U/P0Lvj3NwPGfHPTcHMvoyFXwpe7UmQtAqTUCU0D1VVbwTPVk5jY19Fe3ZfQny7CE51WJDXqpjeEUHr45ki9rIqa4dmQiJfMLItGEs/FcQ2ucbRmdnSYy5vYWyLx/w3EaMfLmBaDpMQvuDJ65PY8Dpnz3wpYmLtApzcrIAqmfrEgdZH1grY/a36w6Xz0DKD8ES25/niYS6+wWE8mWfByY8cXmYEJFYLkHUHtVqNQcltAvoLD3v7o/FUHsNvzlnwxfsCEukC/ho3yUHaBN5Buo17Ojtyl+DqrnvQgUtfcC0ZcAdkUeA+ye7eMru9AUGIJPe4zh509UP/AAfNypi8oj/mAAAAAElFTkSuQmCC);\r
346
+ }\r
347
+ </style>\r
348
+ </head>\r
349
+ <body class="directory">\r
350
+ <div class="wrapper">\r
351
+ <h1>{{headItem}}</h1>\r
352
+ <ul id="files" class="view-tiles">\r
353
+ {{listItems}}\r
354
+ </ul>\r
355
+ </div>\r
356
+ </body>\r
357
+ </html>\r
358
+ `;
359
+
360
+ // src/index.ts
361
+ function fileServerPlugin(options = {}) {
362
+ const { enable = true } = options;
363
+ return {
364
+ name: "vite-plugin-files-server",
365
+ // 必须唯一
366
+ // 核心钩子:配置开发服务器
367
+ configureServer(server) {
368
+ if (!enable) return;
369
+ server.middlewares.use((req, res, next) => {
370
+ const url = req.url ? decodeURIComponent(req.url.split("?")[0]) : "/";
371
+ const projectRoot = server.config.root;
372
+ const fullPath = import_path.default.join(projectRoot, url);
373
+ try {
374
+ if (import_fs.default.existsSync(fullPath) && import_fs.default.statSync(fullPath).isDirectory()) {
375
+ if (import_fs.default.existsSync(import_path.default.join(fullPath, "index.html"))) {
376
+ return next();
377
+ }
378
+ let urlSplitArray = url.split("/");
379
+ if (urlSplitArray[urlSplitArray.length - 1] == "") urlSplitArray = urlSplitArray.slice(0, -1);
380
+ let headItem = '<a href="/">\u{1F3E0}\uFE0E</a> / ';
381
+ urlSplitArray.forEach((p) => {
382
+ p && (headItem += `<a href="/${p}">${p}</a> / `);
383
+ });
384
+ const files = import_fs.default.readdirSync(fullPath);
385
+ const listItems = files.map((file) => {
386
+ const filePath = import_path.default.join(fullPath, file);
387
+ const isDir = import_fs.default.statSync(filePath).isDirectory();
388
+ const href = import_path.default.join(url, file).replace(/\\/g, "/");
389
+ const item = `
390
+ <li>
391
+ <a href="${href}" class="icon ${isDir ? "icon-directory" : "icon-html icon-text-html"}">
392
+ <span class="name">${file}</span>
393
+ </a>
394
+ </li>`;
395
+ return item;
396
+ }).join("");
397
+ urlSplitArray = urlSplitArray.slice(0, -1);
398
+ let backUrl = urlSplitArray.join("/");
399
+ const backItem = `
400
+ <li>
401
+ <a href="${backUrl}" class="icon icon-directory"><span class="name">..</span></a>
402
+ </li>`;
403
+ let fullList = urlSplitArray.length > 1 ? backItem + listItems : listItems;
404
+ const html = temp_default.replace(/{{title}}/g, `Index of ${url}`).replace(/{{headItem}}/g, headItem).replace(/{{url}}/g, url).replace(/{{listItems}}/g, fullList);
405
+ res.setHeader("Content-Type", "text/html");
406
+ res.end(html);
407
+ return;
408
+ }
409
+ } catch (e) {
410
+ console.error("File Server Plugin Error:", e);
411
+ }
412
+ next();
413
+ });
414
+ }
415
+ };
416
+ }
@@ -0,0 +1,9 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ interface FilesServerOptions {
4
+ enable?: boolean;
5
+ root?: string;
6
+ }
7
+ declare function fileServerPlugin(options?: FilesServerOptions): Plugin;
8
+
9
+ export { fileServerPlugin as default };
@@ -0,0 +1,9 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ interface FilesServerOptions {
4
+ enable?: boolean;
5
+ root?: string;
6
+ }
7
+ declare function fileServerPlugin(options?: FilesServerOptions): Plugin;
8
+
9
+ export { fileServerPlugin as default };
package/dist/index.js ADDED
@@ -0,0 +1,385 @@
1
+ // src/index.ts
2
+ import fs from "fs";
3
+ import path from "path";
4
+
5
+ // src/temp.html?raw
6
+ var temp_default = `<!DOCTYPE html>\r
7
+ <html>\r
8
+ <head>\r
9
+ <meta charset="UTF-8" />\r
10
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />\r
11
+ <title>listing directory {{url}}</title>\r
12
+ <style>\r
13
+ /* body {\r
14
+ font-family: system-ui;\r
15
+ padding: 2rem;\r
16
+ max-width: 800px;\r
17
+ margin: 0 auto;\r
18
+ }\r
19
+ h2 {\r
20
+ border-bottom: 1px solid #eee;\r
21
+ padding-bottom: 10px;\r
22
+ }\r
23
+ table {\r
24
+ width: 100%;\r
25
+ border-collapse: collapse;\r
26
+ }\r
27
+ td {\r
28
+ padding: 8px;\r
29
+ border-bottom: 1px solid #f5f5f5;\r
30
+ }\r
31
+ a {\r
32
+ text-decoration: none;\r
33
+ color: #646cff;\r
34
+ }\r
35
+ a:hover {\r
36
+ text-decoration: underline;\r
37
+ }\r
38
+ ul,\r
39
+ li {\r
40
+ list-style: none;\r
41
+ margin: 0;\r
42
+ padding: 0;\r
43
+ } */\r
44
+ * {\r
45
+ margin: 0;\r
46
+ padding: 0;\r
47
+ outline: 0;\r
48
+ }\r
49
+ \r
50
+ body {\r
51
+ padding: 80px 100px;\r
52
+ font: 13px 'Helvetica Neue', 'Lucida Grande', 'Arial';\r
53
+ background: #ece9e9 -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ece9e9));\r
54
+ background: #ece9e9 -moz-linear-gradient(top, #fff, #ece9e9);\r
55
+ background-repeat: no-repeat;\r
56
+ color: #555;\r
57
+ -webkit-font-smoothing: antialiased;\r
58
+ }\r
59
+ h1,\r
60
+ h2,\r
61
+ h3 {\r
62
+ padding-left: 10px;\r
63
+ font-size: 22px;\r
64
+ color: #343434;\r
65
+ }\r
66
+ h1 em,\r
67
+ h2 em {\r
68
+ padding: 0 5px;\r
69
+ font-weight: normal;\r
70
+ }\r
71
+ h1 {\r
72
+ font-size: 60px;\r
73
+ }\r
74
+ h2 {\r
75
+ margin-top: 10px;\r
76
+ }\r
77
+ h3 {\r
78
+ margin: 5px 0 10px 0;\r
79
+ padding-bottom: 5px;\r
80
+ border-bottom: 1px solid #eee;\r
81
+ font-size: 18px;\r
82
+ }\r
83
+ ul li {\r
84
+ list-style: none;\r
85
+ }\r
86
+ ul li:hover {\r
87
+ cursor: pointer;\r
88
+ color: #2e2e2e;\r
89
+ }\r
90
+ ul li .path {\r
91
+ padding-left: 5px;\r
92
+ font-weight: bold;\r
93
+ }\r
94
+ ul li .line {\r
95
+ padding-right: 5px;\r
96
+ font-style: italic;\r
97
+ }\r
98
+ ul li:first-child .path {\r
99
+ padding-left: 0;\r
100
+ }\r
101
+ p {\r
102
+ line-height: 1.5;\r
103
+ }\r
104
+ a {\r
105
+ color: #555;\r
106
+ text-decoration: none;\r
107
+ }\r
108
+ a:hover {\r
109
+ color: #303030;\r
110
+ }\r
111
+ #stacktrace {\r
112
+ margin-top: 15px;\r
113
+ }\r
114
+ .directory h1 {\r
115
+ margin-bottom: 15px;\r
116
+ font-size: 18px;\r
117
+ }\r
118
+ ul#files {\r
119
+ width: 100%;\r
120
+ height: 100%;\r
121
+ overflow: hidden;\r
122
+ }\r
123
+ ul#files li {\r
124
+ float: left;\r
125
+ width: 30%;\r
126
+ line-height: 25px;\r
127
+ margin: 1px;\r
128
+ }\r
129
+ ul#files li a {\r
130
+ display: block;\r
131
+ height: 25px;\r
132
+ border: 1px solid transparent;\r
133
+ -webkit-border-radius: 5px;\r
134
+ -moz-border-radius: 5px;\r
135
+ border-radius: 5px;\r
136
+ overflow: hidden;\r
137
+ white-space: nowrap;\r
138
+ }\r
139
+ ul#files li a:focus,\r
140
+ ul#files li a:hover {\r
141
+ background: rgba(255, 255, 255, 0.65);\r
142
+ border: 1px solid #ececec;\r
143
+ }\r
144
+ ul#files li a.highlight {\r
145
+ -webkit-transition: background 0.4s ease-in-out;\r
146
+ background: #ffff4f;\r
147
+ border-color: #e9dc51;\r
148
+ }\r
149
+ #search {\r
150
+ display: block;\r
151
+ position: fixed;\r
152
+ top: 20px;\r
153
+ right: 20px;\r
154
+ width: 90px;\r
155
+ -webkit-transition: width ease 0.2s, opacity ease 0.4s;\r
156
+ -moz-transition: width ease 0.2s, opacity ease 0.4s;\r
157
+ -webkit-border-radius: 32px;\r
158
+ -moz-border-radius: 32px;\r
159
+ -webkit-box-shadow: inset 0px 0px 3px rgba(0, 0, 0, 0.25), inset 0px 1px 3px rgba(0, 0, 0, 0.7),\r
160
+ 0px 1px 0px rgba(255, 255, 255, 0.03);\r
161
+ -moz-box-shadow: inset 0px 0px 3px rgba(0, 0, 0, 0.25), inset 0px 1px 3px rgba(0, 0, 0, 0.7),\r
162
+ 0px 1px 0px rgba(255, 255, 255, 0.03);\r
163
+ -webkit-font-smoothing: antialiased;\r
164
+ text-align: left;\r
165
+ font: 13px 'Helvetica Neue', Arial, sans-serif;\r
166
+ padding: 4px 10px;\r
167
+ border: none;\r
168
+ background: transparent;\r
169
+ margin-bottom: 0;\r
170
+ outline: none;\r
171
+ opacity: 0.7;\r
172
+ color: #888;\r
173
+ }\r
174
+ #search:focus {\r
175
+ width: 120px;\r
176
+ opacity: 1;\r
177
+ }\r
178
+ \r
179
+ /*views*/\r
180
+ #files span {\r
181
+ display: inline-block;\r
182
+ overflow: hidden;\r
183
+ text-overflow: ellipsis;\r
184
+ text-indent: 10px;\r
185
+ }\r
186
+ #files .name {\r
187
+ background-repeat: no-repeat;\r
188
+ }\r
189
+ #files .icon .name {\r
190
+ text-indent: 28px;\r
191
+ }\r
192
+ \r
193
+ /*tiles*/\r
194
+ .view-tiles .name {\r
195
+ width: 100%;\r
196
+ background-position: 8px 5px;\r
197
+ }\r
198
+ .view-tiles .size,\r
199
+ .view-tiles .date {\r
200
+ display: none;\r
201
+ }\r
202
+ \r
203
+ /*details*/\r
204
+ ul#files.view-details li {\r
205
+ float: none;\r
206
+ display: block;\r
207
+ width: 90%;\r
208
+ }\r
209
+ ul#files.view-details li.header {\r
210
+ height: 25px;\r
211
+ background: #000;\r
212
+ color: #fff;\r
213
+ font-weight: bold;\r
214
+ }\r
215
+ .view-details .header {\r
216
+ border-radius: 5px;\r
217
+ }\r
218
+ .view-details .name {\r
219
+ width: 60%;\r
220
+ background-position: 8px 5px;\r
221
+ }\r
222
+ .view-details .size {\r
223
+ width: 10%;\r
224
+ }\r
225
+ .view-details .date {\r
226
+ width: 30%;\r
227
+ }\r
228
+ .view-details .size,\r
229
+ .view-details .date {\r
230
+ text-align: right;\r
231
+ direction: rtl;\r
232
+ }\r
233
+ \r
234
+ /*mobile*/\r
235
+ @media (max-width: 768px) {\r
236
+ body {\r
237
+ font-size: 13px;\r
238
+ line-height: 16px;\r
239
+ padding: 0;\r
240
+ }\r
241
+ #search {\r
242
+ position: static;\r
243
+ width: 100%;\r
244
+ font-size: 2em;\r
245
+ line-height: 1.8em;\r
246
+ text-indent: 10px;\r
247
+ border: 0;\r
248
+ border-radius: 0;\r
249
+ padding: 10px 0;\r
250
+ margin: 0;\r
251
+ }\r
252
+ #search:focus {\r
253
+ width: 100%;\r
254
+ border: 0;\r
255
+ opacity: 1;\r
256
+ }\r
257
+ .directory h1 {\r
258
+ font-size: 2em;\r
259
+ line-height: 1.5em;\r
260
+ color: #fff;\r
261
+ background: #000;\r
262
+ padding: 15px 10px;\r
263
+ margin: 0;\r
264
+ }\r
265
+ ul#files {\r
266
+ border-top: 1px solid #cacaca;\r
267
+ }\r
268
+ ul#files li {\r
269
+ float: none;\r
270
+ width: auto !important;\r
271
+ display: block;\r
272
+ border-bottom: 1px solid #cacaca;\r
273
+ font-size: 2em;\r
274
+ line-height: 1.2em;\r
275
+ text-indent: 0;\r
276
+ margin: 0;\r
277
+ }\r
278
+ ul#files li:nth-child(odd) {\r
279
+ background: #e0e0e0;\r
280
+ }\r
281
+ ul#files li a {\r
282
+ height: auto;\r
283
+ border: 0;\r
284
+ border-radius: 0;\r
285
+ padding: 15px 10px;\r
286
+ }\r
287
+ ul#files li a:focus,\r
288
+ ul#files li a:hover {\r
289
+ border: 0;\r
290
+ }\r
291
+ #files .header,\r
292
+ #files .size,\r
293
+ #files .date {\r
294
+ display: none !important;\r
295
+ }\r
296
+ #files .name {\r
297
+ float: none;\r
298
+ display: inline-block;\r
299
+ width: 100%;\r
300
+ text-indent: 0;\r
301
+ background-position: 0 50%;\r
302
+ }\r
303
+ #files .icon .name {\r
304
+ text-indent: 41px;\r
305
+ }\r
306
+ }\r
307
+ #files .icon-directory .name {\r
308
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAAWtQTFRFAAAA/PPQ9Nhc2q402qQ12qs2/PTX2pg12p81+/LM89NE9dto2q82+/fp2rM22qY39d6U+/bo2qo2/frx/vz32q812qs12qE279SU8c4w9NZP+/LK//367s9y7s925cp0/vzw9t92//342po2/vz25s1579B6+OSO2bQ0/v799NyT8tE79dld8Msm+OrC/vzx79KA2IYs7s6I9d6R4cJe9+OF/PLI/fry79OF/v30//328tWB89RJ8c9p8c0u9eCf//7+9txs6sts5Mdr+++5+u2z/vrv+/fq6cFz8dBs8tA57cpq+OaU9uGs27Y8//799NdX/PbY9uB89unJ//z14sNf+emh+emk+vDc+uys9+OL8dJy89NH+eic8tN5+OaV+OWR9N2n9dtl9t529+KF9+GB9Nue9NdU8tR/9t5y89qW9dpj89iO89eG/vvu2pQ12Y4z/vzy2Ict/vvv48dr/vzz4sNg///+2Igty3PqwQAAAAF0Uk5TAEDm2GYAAACtSURBVBjTY2AgA2iYlJWVhfohBPg0yx38y92dS0pKVOVBAqIi6sb2vsWWpfrFeTI8QAEhYQEta28nCwM1OVleZqCAmKCEkUdwYWmhQnFeOStQgL9cySqkNNDHVJGbiY0FKCCuYuYSGRsV5KgjxcXIARRQNncNj09JTgqw0ZbkZAcK5LuFJaRmZqfHeNnpSucDBQoiEtOycnIz4qI9bfUKQA6pKKqAgqIKQyK8BgAZ5yfODmnHrQAAAABJRU5ErkJggg==);\r
309
+ }\r
310
+ #files .icon-text-html .name {\r
311
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHtSURBVDjLjZM9T9tQFIYpQ5eOMBKlW6eWIQipa8RfQKQghEAKqZgKFQgmFn5AWyVDCipVQZC2EqBWlEqdO2RCpAssQBRsx1+1ndix8wFvfW6wcUhQsfTI0j33PD7n+N4uAF2E+/S5RFwG/8Njl24/LyCIOI6j1+v1y0ajgU64cSSTybdBSVAwSMmmacKyLB/DMKBpGkRRZBJBEJBKpXyJl/yABLTBtm1Uq1X2JsrlMnRdhyRJTFCpVEAfSafTTUlQoFs1luxBAkoolUqQZbmtJTYTT/AoHInOfpcwtVtkwcSBgrkDGYph+60oisIq4Xm+VfB0+U/P0Lvj3NwPGfHPTcHMvoyFXwpe7UmQtAqTUCU0D1VVbwTPVk5jY19Fe3ZfQny7CE51WJDXqpjeEUHr45ki9rIqa4dmQiJfMLItGEs/FcQ2ucbRmdnSYy5vYWyLx/w3EaMfLmBaDpMQvuDJ65PY8Dpnz3wpYmLtApzcrIAqmfrEgdZH1grY/a36w6Xz0DKD8ES25/niYS6+wWE8mWfByY8cXmYEJFYLkHUHtVqNQcltAvoLD3v7o/FUHsNvzlnwxfsCEukC/ho3yUHaBN5Buo17Ojtyl+DqrnvQgUtfcC0ZcAdkUeA+ye7eMru9AUGIJPe4zh509UP/AAfNypi8oj/mAAAAAElFTkSuQmCC);\r
312
+ }\r
313
+ </style>\r
314
+ </head>\r
315
+ <body class="directory">\r
316
+ <div class="wrapper">\r
317
+ <h1>{{headItem}}</h1>\r
318
+ <ul id="files" class="view-tiles">\r
319
+ {{listItems}}\r
320
+ </ul>\r
321
+ </div>\r
322
+ </body>\r
323
+ </html>\r
324
+ `;
325
+
326
+ // src/index.ts
327
+ function fileServerPlugin(options = {}) {
328
+ const { enable = true } = options;
329
+ return {
330
+ name: "vite-plugin-files-server",
331
+ // 必须唯一
332
+ // 核心钩子:配置开发服务器
333
+ configureServer(server) {
334
+ if (!enable) return;
335
+ server.middlewares.use((req, res, next) => {
336
+ const url = req.url ? decodeURIComponent(req.url.split("?")[0]) : "/";
337
+ const projectRoot = server.config.root;
338
+ const fullPath = path.join(projectRoot, url);
339
+ try {
340
+ if (fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory()) {
341
+ if (fs.existsSync(path.join(fullPath, "index.html"))) {
342
+ return next();
343
+ }
344
+ let urlSplitArray = url.split("/");
345
+ if (urlSplitArray[urlSplitArray.length - 1] == "") urlSplitArray = urlSplitArray.slice(0, -1);
346
+ let headItem = '<a href="/">\u{1F3E0}\uFE0E</a> / ';
347
+ urlSplitArray.forEach((p) => {
348
+ p && (headItem += `<a href="/${p}">${p}</a> / `);
349
+ });
350
+ const files = fs.readdirSync(fullPath);
351
+ const listItems = files.map((file) => {
352
+ const filePath = path.join(fullPath, file);
353
+ const isDir = fs.statSync(filePath).isDirectory();
354
+ const href = path.join(url, file).replace(/\\/g, "/");
355
+ const item = `
356
+ <li>
357
+ <a href="${href}" class="icon ${isDir ? "icon-directory" : "icon-html icon-text-html"}">
358
+ <span class="name">${file}</span>
359
+ </a>
360
+ </li>`;
361
+ return item;
362
+ }).join("");
363
+ urlSplitArray = urlSplitArray.slice(0, -1);
364
+ let backUrl = urlSplitArray.join("/");
365
+ const backItem = `
366
+ <li>
367
+ <a href="${backUrl}" class="icon icon-directory"><span class="name">..</span></a>
368
+ </li>`;
369
+ let fullList = urlSplitArray.length > 1 ? backItem + listItems : listItems;
370
+ const html = temp_default.replace(/{{title}}/g, `Index of ${url}`).replace(/{{headItem}}/g, headItem).replace(/{{url}}/g, url).replace(/{{listItems}}/g, fullList);
371
+ res.setHeader("Content-Type", "text/html");
372
+ res.end(html);
373
+ return;
374
+ }
375
+ } catch (e) {
376
+ console.error("File Server Plugin Error:", e);
377
+ }
378
+ next();
379
+ });
380
+ }
381
+ };
382
+ }
383
+ export {
384
+ fileServerPlugin as default
385
+ };
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "vite-plugin-files-server",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "dev": "tsup --watch",
14
+ "build": "tsup",
15
+ "test:playground": "cd playground && npm run dev"
16
+ },
17
+ "packageManager": "npm@10.9.2",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/VarianLIn/vite-plugin-files-server.git"
21
+ },
22
+ "keywords": [
23
+ "vitejs",
24
+ "plugin"
25
+ ],
26
+ "author": "varianlin",
27
+ "license": "ISC",
28
+ "bugs": {
29
+ "url": "https://github.com/VarianLIn/vite-plugin-files-server/issues"
30
+ },
31
+ "homepage": "https://github.com/VarianLIn/vite-plugin-files-server#readme",
32
+ "devDependencies": {
33
+ "@types/node": "^24.10.2",
34
+ "tsup": "^8.5.1",
35
+ "typescript": "^5.9.3",
36
+ "vite": "^7.2.7"
37
+ }
38
+ }