vite-plugin-serve-static 1.2.0 → 2.1.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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023-2024 Drew Davis
3
+ Copyright (c) 2023-2026 Drew Davis
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -2,30 +2,33 @@
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/vite-plugin-serve-static/latest)](https://www.npmjs.com/package/vite-plugin-serve-static)
4
4
  [![vite](https://img.shields.io/npm/dependency-version/vite-plugin-serve-static/peer/vite)](https://github.com/vitejs/vite)
5
- [![license: MIT](https://img.shields.io/npm/l/vite-plugin-serve-static)](https://github.com/reifiedbeans/vite-plugin-serve-static/blob/main/LICENSE)
5
+ [![license: MIT](https://img.shields.io/npm/l/vite-plugin-serve-static)](https://github.com/typeparameter/vite-plugin-serve-static/blob/main/LICENSE)
6
6
 
7
7
  A simple Vite plugin for serving arbitrary static files that aren't in your `public` directory.
8
8
 
9
9
  ```typescript
10
10
  // vite.config.ts
11
11
  import path from "path";
12
+
12
13
  import { defineConfig } from "vite";
13
14
  import serveStatic from "vite-plugin-serve-static";
14
15
 
15
- const serveStaticPlugin = serveStatic([
16
- {
17
- pattern: /^\/metadata\.json/,
18
- resolve: "./metadata.json",
19
- },
20
- {
21
- pattern: /^\/dog-photos\/.*/,
22
- resolve: ([match]) => path.join("..", "dog-photos", match),
23
- },
24
- {
25
- pattern: /^\/author-photos\/(.*)/,
26
- resolve: (groups) => path.join("..", "authors", groups[1]) + ".jpg",
27
- },
28
- ]);
16
+ const serveStaticPlugin = serveStatic({
17
+ rules: [
18
+ {
19
+ pattern: /^\/metadata\.json/,
20
+ resolve: path.join(".", "metadata.json"),
21
+ },
22
+ {
23
+ pattern: /^\/dog-photos\/.*/,
24
+ resolve: ([match]) => path.join("..", "dog-photos", match),
25
+ },
26
+ {
27
+ pattern: /^\/author-photos\/(.*)/,
28
+ resolve: (groups) => path.join("..", "authors", groups[1]) + ".jpg",
29
+ },
30
+ ],
31
+ });
29
32
 
30
33
  export default defineConfig({
31
34
  plugins: [serveStaticPlugin],
@@ -34,12 +37,12 @@ export default defineConfig({
34
37
 
35
38
  ## Config
36
39
 
37
- The configuration is defined as an array of objects defining which patterns to intercept and how to resolve them.
40
+ The configuration is provided as an object with `rules`, plus an optional global `contentType`.
38
41
 
39
- Each `pattern` is defined as a [regular expression]. The `resolve` property can either be a string containing the path to a single file or a function that returns a string given the result of executing the `pattern` against the request path.
42
+ Each rule defines which patterns to intercept and how to resolve them. Each `pattern` is defined as a [regular expression]. The `resolve` property can either be a string containing the path to a single file or a function that returns a string given the result of executing the `pattern` against the request path.
40
43
 
41
44
  ## License
42
45
 
43
- Licensed under the [MIT License](https://github.com/reifiedbeans/vite-plugin-serve-static/blob/main/LICENSE).
46
+ Licensed under the [MIT License](https://github.com/typeparameter/vite-plugin-serve-static/blob/main/packages/vite-plugin-serve-static/LICENSE).
44
47
 
45
48
  [regular expression]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
@@ -0,0 +1,27 @@
1
+ import { Plugin } from "vite";
2
+ import http from "http";
3
+
4
+ //#region lib/config.d.ts
5
+ type ResolveFn = (match: RegExpExecArray) => string;
6
+ type RuleConfig = {
7
+ readonly pattern: RegExp;
8
+ readonly resolve: string | ResolveFn;
9
+ readonly headers?: http.OutgoingHttpHeaders;
10
+ };
11
+ type Config = RuleConfig[] | {
12
+ readonly rules: RuleConfig[];
13
+ readonly contentType?: string;
14
+ };
15
+ declare function normalizeConfig(config: Config): {
16
+ contentType?: string;
17
+ rules: {
18
+ headers: http.OutgoingHttpHeaders;
19
+ pattern: RegExp;
20
+ resolve: string | ResolveFn;
21
+ }[];
22
+ };
23
+ //#endregion
24
+ //#region lib/index.d.ts
25
+ declare function serveStatic(config: Config): Plugin;
26
+ //#endregion
27
+ export { Config, ResolveFn, serveStatic as default, normalizeConfig };
package/dist/index.mjs ADDED
@@ -0,0 +1,105 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import corsMiddleware from "cors";
4
+ import * as mime from "mime-types";
5
+
6
+ //#region lib/config.ts
7
+ function normalizeConfig(config) {
8
+ const { rules, ...rest } = Array.isArray(config) ? { rules: config } : config;
9
+ return {
10
+ rules: rules.map((rule) => ({
11
+ ...rule,
12
+ headers: normalizeHeaders(rule.headers)
13
+ })),
14
+ ...rest
15
+ };
16
+ }
17
+ function normalizeHeaders(headers) {
18
+ if (!headers) return {};
19
+ const entries = Object.entries(headers).filter(([, value]) => value !== void 0).map(([key, value]) => [key.toLowerCase(), value]);
20
+ return Object.fromEntries(entries);
21
+ }
22
+
23
+ //#endregion
24
+ //#region lib/utils.ts
25
+ function isDevServer(server) {
26
+ return "pluginContainer" in server;
27
+ }
28
+ function setupLogger(logger) {
29
+ const defaultOptions = { timestamp: true };
30
+ function applyDefaultOptions(log) {
31
+ return function(msg, options) {
32
+ log(msg, {
33
+ ...defaultOptions,
34
+ ...options
35
+ });
36
+ };
37
+ }
38
+ return {
39
+ clearScreen: logger.clearScreen,
40
+ hasErrorLogged: logger.hasErrorLogged,
41
+ hasWarned: logger.hasWarned,
42
+ info: applyDefaultOptions(logger.info),
43
+ warn: applyDefaultOptions(logger.warn),
44
+ warnOnce: applyDefaultOptions(logger.warnOnce),
45
+ error: applyDefaultOptions(logger.error)
46
+ };
47
+ }
48
+
49
+ //#endregion
50
+ //#region lib/middleware.ts
51
+ function createMiddleware(pluginConfig, rawLogger) {
52
+ const log = setupLogger(rawLogger);
53
+ const config = normalizeConfig(pluginConfig);
54
+ return function serveStaticMiddleware(req, res, next) {
55
+ if (!req.url) return next();
56
+ for (const { pattern, resolve, headers } of config.rules) {
57
+ const match = pattern.exec(req.url);
58
+ if (match) {
59
+ const filePath = typeof resolve === "string" ? resolve : resolve(match);
60
+ const stats = fs.statSync(filePath, { throwIfNoEntry: false });
61
+ if (!stats || !stats.isFile()) {
62
+ res.writeHead(404);
63
+ res.end("Not found");
64
+ log.error(`File ${filePath} is not a file`);
65
+ return;
66
+ }
67
+ const contentType = headers["content-type"] || config.contentType || mime.contentType(path.basename(filePath)) || "application/octet-stream";
68
+ res.writeHead(200, {
69
+ "content-length": stats.size,
70
+ "content-type": contentType,
71
+ ...headers
72
+ });
73
+ fs.createReadStream(filePath).pipe(res);
74
+ return;
75
+ }
76
+ }
77
+ return next();
78
+ };
79
+ }
80
+ function applyMiddleware(server, pluginConfig) {
81
+ const pluginMiddleware = createMiddleware(pluginConfig, server.config.logger);
82
+ const corsConfig = isDevServer(server) ? server.config.server.cors : server.config.preview.cors;
83
+ if (corsConfig !== false) {
84
+ const config = typeof corsConfig === "boolean" ? {} : corsConfig;
85
+ server.middlewares.use(corsMiddleware(config));
86
+ }
87
+ server.middlewares.use(pluginMiddleware);
88
+ }
89
+
90
+ //#endregion
91
+ //#region lib/index.ts
92
+ function serveStatic(config) {
93
+ return {
94
+ name: "serve-static",
95
+ configureServer(server) {
96
+ applyMiddleware(server, config);
97
+ },
98
+ configurePreviewServer(server) {
99
+ applyMiddleware(server, config);
100
+ }
101
+ };
102
+ }
103
+
104
+ //#endregion
105
+ export { serveStatic as default, normalizeConfig };
package/package.json CHANGED
@@ -1,30 +1,31 @@
1
1
  {
2
2
  "name": "vite-plugin-serve-static",
3
- "version": "1.2.0",
3
+ "version": "2.1.0",
4
4
  "description": "A Vite plugin for serving static files during local development",
5
5
  "repository": {
6
6
  "type": "git",
7
- "url": "git+https://github.com/reifiedbeans/vite-plugin-serve-static.git"
7
+ "url": "git+https://github.com/typeparameter/vite-plugin-serve-static.git"
8
8
  },
9
9
  "license": "MIT",
10
- "homepage": "https://github.com/reifiedbeans/vite-plugin-serve-static",
10
+ "homepage": "https://github.com/typeparameter/vite-plugin-serve-static",
11
11
  "keywords": [
12
12
  "vite-plugin"
13
13
  ],
14
14
  "type": "module",
15
+ "engines": {
16
+ "node": ">=20"
17
+ },
15
18
  "files": [
16
19
  "dist"
17
20
  ],
18
- "main": "dist/index.js",
19
- "types": "dist/index.d.ts",
21
+ "main": "dist/index.mjs",
22
+ "types": "dist/index.d.mts",
20
23
  "scripts": {
21
- "build": "tsc && tsup",
24
+ "build": "tsdown",
22
25
  "clean": "rimraf dist",
23
- "format": "prettier --write .",
24
- "format:check": "prettier --check .",
25
- "lint": "eslint --cache --max-warnings=0 .",
26
- "lint:fix": "eslint --fix --cache --max-warnings=0 .",
27
- "prepack": "npm-run-all build test lint format:check",
26
+ "lint": "tsc && eslint --cache --max-warnings=0 .",
27
+ "lint:fix": "tsc && eslint --fix --cache --max-warnings=0 .",
28
+ "prepack": "pnpm build",
28
29
  "test": "vitest --run"
29
30
  },
30
31
  "peerDependencies": {
@@ -35,25 +36,15 @@
35
36
  "mime-types": "^2.1.35"
36
37
  },
37
38
  "devDependencies": {
38
- "@trivago/prettier-plugin-sort-imports": "^5.2.0",
39
39
  "@types/cors": "^2.8.17",
40
40
  "@types/mime-types": "^2.1.4",
41
- "@typescript-eslint/eslint-plugin": "^8.18.0",
42
- "@typescript-eslint/parser": "^8.18.0",
43
- "conventional-changelog-conventionalcommits": "^8.0.0",
44
- "eslint": "^8.57.1",
45
- "eslint-config-prettier": "^9.1.0",
46
- "npm-run-all": "^4.1.5",
47
- "prettier": "^3.4.2",
48
41
  "rimraf": "^6.0.1",
49
- "tsup": "~8.4.0",
42
+ "tsdown": "^0.18.4",
50
43
  "typescript": "~5.7.2",
51
- "vitest": "^3.0.8"
44
+ "vitest": "^4.0.16"
52
45
  },
53
46
  "publishConfig": {
54
- "access": "public"
55
- },
56
- "release": {
57
- "preset": "conventionalcommits"
47
+ "access": "public",
48
+ "provenance": true
58
49
  }
59
50
  }
package/dist/index.d.ts DELETED
@@ -1,11 +0,0 @@
1
- import { Plugin } from 'vite';
2
-
3
- type ResolveFn = (match: RegExpExecArray) => string;
4
- type Config = {
5
- readonly pattern: RegExp;
6
- readonly resolve: string | ResolveFn;
7
- }[];
8
-
9
- declare function serveStatic(config: Config): Plugin;
10
-
11
- export { type Config, type ResolveFn, serveStatic as default };
package/dist/index.js DELETED
@@ -1,84 +0,0 @@
1
- // lib/middleware.ts
2
- import * as mime from "mime-types";
3
- import corsMiddleware from "cors";
4
- import fs from "fs";
5
- import path from "path";
6
-
7
- // lib/utils.ts
8
- function isDevServer(server) {
9
- return "pluginContainer" in server;
10
- }
11
- function setupLogger(logger) {
12
- const defaultOptions = { timestamp: true };
13
- function applyDefaultOptions(log) {
14
- return function(msg, options) {
15
- log(msg, { ...defaultOptions, ...options });
16
- };
17
- }
18
- return {
19
- clearScreen: logger.clearScreen,
20
- hasErrorLogged: logger.hasErrorLogged,
21
- hasWarned: logger.hasWarned,
22
- info: applyDefaultOptions(logger.info),
23
- warn: applyDefaultOptions(logger.warn),
24
- warnOnce: applyDefaultOptions(logger.warnOnce),
25
- error: applyDefaultOptions(logger.error)
26
- };
27
- }
28
-
29
- // lib/middleware.ts
30
- function createMiddleware(config, rawLogger) {
31
- const log = setupLogger(rawLogger);
32
- return function serveStaticMiddleware(req, res, next) {
33
- if (!req.url) {
34
- return next();
35
- }
36
- for (const { pattern, resolve } of config) {
37
- const match = pattern.exec(req.url);
38
- if (match) {
39
- const filePath = typeof resolve === "string" ? resolve : resolve(match);
40
- const stats = fs.statSync(filePath, { throwIfNoEntry: false });
41
- if (!stats || !stats.isFile()) {
42
- res.writeHead(404);
43
- res.end("Not found");
44
- log.error(`File ${filePath} is not a file`);
45
- return;
46
- }
47
- const type = mime.contentType(path.basename(filePath));
48
- res.writeHead(200, {
49
- "Content-Length": stats.size,
50
- "Content-Type": type || "application/octet-stream"
51
- });
52
- const stream = fs.createReadStream(filePath);
53
- stream.pipe(res);
54
- return;
55
- }
56
- }
57
- return next();
58
- };
59
- }
60
- function applyMiddleware(server, pluginConfig) {
61
- const pluginMiddleware = createMiddleware(pluginConfig, server.config.logger);
62
- const corsConfig = isDevServer(server) ? server.config.server.cors : server.config.preview.cors;
63
- if (corsConfig !== false) {
64
- const config = typeof corsConfig === "boolean" ? {} : corsConfig;
65
- server.middlewares.use(corsMiddleware(config));
66
- }
67
- server.middlewares.use(pluginMiddleware);
68
- }
69
-
70
- // lib/index.ts
71
- function serveStatic(config) {
72
- return {
73
- name: "serve-static",
74
- configureServer(server) {
75
- applyMiddleware(server, config);
76
- },
77
- configurePreviewServer(server) {
78
- applyMiddleware(server, config);
79
- }
80
- };
81
- }
82
- export {
83
- serveStatic as default
84
- };