@t8/serve 0.1.38 → 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.
package/README.md CHANGED
@@ -2,34 +2,48 @@
2
2
 
3
3
  Simple static file server + bundler, primarily for demo apps and tests, manual or automated
4
4
 
5
- ## Use cases
5
+ Use cases:
6
6
 
7
7
  - Use the CLI-based server to launch a demo or test app with a single CLI command.
8
8
  - Use the code-based setup to launch an own server from multiple tests, each with its own app.
9
9
 
10
10
  ## CLI
11
11
 
12
- ```sh
13
- npx @t8/serve [url|port] [*] [app_dir] [...assets_dirs] [-b [bundle_input_path] [bundle_output_path] [bundle_output_dir]] [--watch] [--minify]
14
- # * = SPA mode: serve all unmatched paths as "/"
12
+ ```
13
+ npx @t8/serve <app_dir> [...optional flags]
14
+
15
+ Flag Usage
16
+
17
+ --bundle, -b -b [input_path [[output_path] [output_dir]]]
18
+ Defaults:
19
+ - input_path: "index.ts" (relative to <app_dir>)
20
+ - ouput_path: "index.js" (relative to <app_dir>/<output_dir>)
21
+ - output_dir: "dist"
22
+
23
+ --url, -u -u [<host>:]<port>
24
+ Default: "localhost:3000"
25
+
26
+ --spa, -s -s
27
+ Enables the SPA mode by handling all unmatched paths as "/".
28
+
29
+ --dirs --dirs assets public
30
+ To serve files from the listed subdirectories of <app_dir>.
31
+ By default, files are served from <app_dir>.
32
+
33
+ --watch --watch
34
+ To rebuild the bundled code if the source code changes.
15
35
 
16
- npx @t8/serve 3000 app
17
- npx @t8/serve 3000 app -b
18
- npx @t8/serve 3000 * app
19
- npx @t8/serve 3000 * app -b
20
- npx @t8/serve 3000 app public dist
21
- npx @t8/serve 127.0.0.1:3000 app public dist
22
- npx @t8/serve 3000 app public dist -b
23
- npx @t8/serve 3000 app public dist -b src/index.ts
36
+ --minify --minify
37
+ To minify the bundled code.
24
38
  ```
25
39
 
26
40
  <details>
27
- <summary>Example 1 (flat)</summary>
41
+ <summary>Example with Playwright #1 (flat)</summary>
28
42
 
29
43
  ```
30
44
  // package.json
31
45
  "scripts": {
32
- "play": "npx @t8/serve 3000 * playground -b"
46
+ "play": "npx @t8/serve playground -s -b"
33
47
  }
34
48
  ```
35
49
 
@@ -47,7 +61,6 @@ npm run play
47
61
  ```
48
62
 
49
63
  ```
50
- // With Playwright:
51
64
  // playwright.config.ts
52
65
  ...
53
66
  use: {
@@ -62,12 +75,12 @@ webServer: {
62
75
  </details>
63
76
 
64
77
  <details>
65
- <summary>Example 2 (nested)</summary>
78
+ <summary>Example with Playwright #2 (nested)</summary>
66
79
 
67
80
  ```
68
81
  // package.json
69
82
  "scripts": {
70
- "play": "npx @t8/serve 3000 * playground -b src/index.tsx"
83
+ "play": "npx @t8/serve playground -s -b src/index.tsx"
71
84
  }
72
85
  ```
73
86
 
@@ -87,7 +100,6 @@ npm run play
87
100
  ```
88
101
 
89
102
  ```
90
- // With Playwright:
91
103
  // playwright.config.ts
92
104
  ...
93
105
  use: {
@@ -102,7 +114,7 @@ webServer: {
102
114
  </details>
103
115
 
104
116
  <details>
105
- <summary>Example 3 (index.html)</summary>
117
+ <summary>Example with the watch mode</summary>
106
118
 
107
119
  ```
108
120
  /app
@@ -119,7 +131,7 @@ webServer: {
119
131
  {
120
132
  "name": "app",
121
133
  "scripts": {
122
- "start": "npx @t8/serve 80 . -b src/index.ts --watch"
134
+ "start": "npx @t8/serve . -u 80 -b src/index.ts --watch"
123
135
  }
124
136
  }
125
137
  ```
@@ -137,9 +149,10 @@ import { serve } from "@t8/serve";
137
149
 
138
150
  // Start
139
151
  let server = await serve({
140
- port: 3000,
152
+ host: "localhost", // default
153
+ port: 3000, // default
141
154
  path: "app",
142
- bundle: true, // or input path, or `{ input, output, dir }`
155
+ bundle: true, // or input path, or { input, output, dir }
143
156
  spa: true,
144
157
  });
145
158
 
@@ -148,7 +161,7 @@ server.close();
148
161
  ```
149
162
 
150
163
  <details>
151
- <summary>Example 1 (flat)</summary>
164
+ <summary>Example with Playwright 1 (flat)</summary>
152
165
 
153
166
  ```
154
167
  /playground
@@ -182,7 +195,7 @@ test.afterAll(() => {
182
195
  </details>
183
196
 
184
197
  <details>
185
- <summary>Example 2 (nested)</summary>
198
+ <summary>Example with Playwright 2 (nested)</summary>
186
199
 
187
200
  ```
188
201
  /tests/x
@@ -220,7 +233,7 @@ test.afterAll(() => {
220
233
  </details>
221
234
 
222
235
  <details>
223
- <summary>Example 3 (simple API)</summary>
236
+ <summary>Example with an API mock</summary>
224
237
 
225
238
  ```ts
226
239
  import { serve } from "@t8/serve";
package/dist/index.cjs CHANGED
@@ -90,6 +90,13 @@ const mimeTypes = {
90
90
  md: "text/markdown"
91
91
  };
92
92
 
93
+ /**
94
+ * Starts a static server as configured by the `config` parameter and
95
+ * returns the `Server` object.
96
+ *
97
+ * Bundles the code before starting the server, if configured with
98
+ * `config.bundle` to do so.
99
+ */
93
100
  async function serve(config = {}) {
94
101
  let stop = await bundle(config);
95
102
  return new Promise((resolve) => {
package/dist/index.d.ts CHANGED
@@ -68,6 +68,13 @@ type Config = {
68
68
  };
69
69
 
70
70
  type Server = ReturnType<typeof createServer>;
71
+ /**
72
+ * Starts a static server as configured by the `config` parameter and
73
+ * returns the `Server` object.
74
+ *
75
+ * Bundles the code before starting the server, if configured with
76
+ * `config.bundle` to do so.
77
+ */
71
78
  declare function serve(config?: Config): Promise<Server>;
72
79
 
73
80
  export { BundleConfig, Config, Server, serve };
package/dist/index.mjs CHANGED
@@ -90,6 +90,13 @@ const mimeTypes = {
90
90
  md: "text/markdown"
91
91
  };
92
92
 
93
+ /**
94
+ * Starts a static server as configured by the `config` parameter and
95
+ * returns the `Server` object.
96
+ *
97
+ * Bundles the code before starting the server, if configured with
98
+ * `config.bundle` to do so.
99
+ */
93
100
  async function serve(config = {}) {
94
101
  let stop = await bundle(config);
95
102
  return new Promise((resolve) => {
package/dist/run.mjs CHANGED
@@ -1,4 +1,118 @@
1
1
  #!/usr/bin/env node
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 __commonJS = (cb, mod) => function __require() {
9
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+
28
+ // node_modules/args-json/dist/index.cjs
29
+ var require_dist = __commonJS({
30
+ "node_modules/args-json/dist/index.cjs"(exports) {
31
+ function toCamelCase(x) {
32
+ let s = x.replace(/^[-_.\s~+]|[-_.\s~+]$/g, "");
33
+ if (!/[-_.\s~+]/.test(s)) return s.slice(0, 1).toLowerCase() + s.slice(1);
34
+ return s.toLowerCase().replace(/[-_.\s~+](\S)/g, (_, match) => match.toUpperCase());
35
+ }
36
+ function toKey(x) {
37
+ if (x) {
38
+ if (x.startsWith("--") && x.length > 2) return toCamelCase(x.slice(2));
39
+ if (x.startsWith("-") && x.length === 2) return toCamelCase(x.slice(1));
40
+ }
41
+ }
42
+ function split(x) {
43
+ let words = [], word = "";
44
+ let hasOpenSingleQuote = false;
45
+ let hasOpenDoubleQuote = false;
46
+ for (let i = 0; i < x.length; i++) {
47
+ let c = x[i];
48
+ if (/^\s/.test(c) && !hasOpenSingleQuote && !hasOpenDoubleQuote) {
49
+ if (word) words.push(word);
50
+ word = "";
51
+ continue;
52
+ }
53
+ if (c === "'" && x[i - 1] !== "\\") hasOpenSingleQuote = !hasOpenSingleQuote;
54
+ if (c === '"' && x[i - 1] !== "\\") hasOpenDoubleQuote = !hasOpenDoubleQuote;
55
+ word += c;
56
+ }
57
+ if (word) words.push(word);
58
+ return words;
59
+ }
60
+ function getDefaultInput() {
61
+ return typeof process === "undefined" ? [] : process.argv;
62
+ }
63
+ function parseArgs2(input, map) {
64
+ let normalizedInput;
65
+ let normalizedMap;
66
+ if (input === void 0) normalizedInput = getDefaultInput();
67
+ else if (typeof input === "string") normalizedInput = split(input);
68
+ else if (Array.isArray(input)) normalizedInput = input.map((x) => String(x));
69
+ else if (input !== null && typeof input === "object") {
70
+ normalizedInput = getDefaultInput();
71
+ normalizedMap = input;
72
+ } else normalizedInput = [];
73
+ normalizedInput = normalizedInput.flatMap((item) => {
74
+ let normalizedItem = item.trim();
75
+ let k = normalizedItem.indexOf("=");
76
+ if (k === -1) return normalizedItem;
77
+ return [normalizedItem.slice(0, k), normalizedItem.slice(k + 1)];
78
+ });
79
+ if (map) normalizedMap = map;
80
+ let key = "";
81
+ let parsedArgs = {};
82
+ for (let rawValue of normalizedInput) {
83
+ rawValue = rawValue.trim();
84
+ if (rawValue.startsWith('"') && rawValue.endsWith('"')) rawValue = rawValue.slice(1, -1);
85
+ else if (rawValue.startsWith("'") && rawValue.endsWith("'")) rawValue = rawValue.slice(1, -1);
86
+ let parsedKey = toKey(rawValue);
87
+ if (parsedKey !== void 0) {
88
+ let nextKey = normalizedMap?.[parsedKey] ?? parsedKey;
89
+ if (key && nextKey !== key && parsedArgs[key] === void 0) parsedArgs[key] = true;
90
+ key = nextKey;
91
+ continue;
92
+ }
93
+ let parsedValue;
94
+ if (rawValue) try {
95
+ parsedValue = JSON.parse(rawValue);
96
+ } catch {
97
+ parsedValue = rawValue;
98
+ }
99
+ else parsedValue = true;
100
+ let prevValue = parsedArgs[key];
101
+ let value;
102
+ if (prevValue === void 0) value = key === "" ? [parsedValue] : parsedValue;
103
+ else if (Array.isArray(prevValue)) value = [...prevValue, parsedValue];
104
+ else value = [prevValue, parsedValue];
105
+ parsedArgs[key] = value;
106
+ }
107
+ if (key && parsedArgs[key] === void 0) parsedArgs[key] = true;
108
+ return parsedArgs;
109
+ }
110
+ exports.parseArgs = parseArgs2;
111
+ }
112
+ });
113
+
114
+ // src/run.ts
115
+ var import_args_json = __toESM(require_dist(), 1);
2
116
 
3
117
  // src/serve.ts
4
118
  import { createReadStream } from "node:fs";
@@ -143,47 +257,23 @@ async function serve(config = {}) {
143
257
 
144
258
  // src/run.ts
145
259
  async function run() {
146
- let [url, ...args] = process.argv.slice(2);
147
- let spa = false;
148
- let watch = false;
149
- let minify = false;
150
- if (args[0] === "*") {
151
- spa = true;
152
- args.shift();
153
- }
154
- while (args.at(-1)?.startsWith("--")) {
155
- let arg = args.pop();
156
- switch (arg) {
157
- case "--watch":
158
- watch = true;
159
- break;
160
- case "--minify":
161
- minify = true;
162
- break;
163
- }
164
- }
165
- let bundleFlagIndex = args.indexOf("-b");
166
- let path = args[0];
167
- let dirs;
168
- let bundle2;
169
- if (bundleFlagIndex === -1) dirs = args.slice(1);
170
- else {
171
- dirs = args.slice(1, bundleFlagIndex);
172
- bundle2 = {
173
- input: args[bundleFlagIndex + 1] || void 0,
174
- output: args[bundleFlagIndex + 2] || void 0,
175
- dir: args[bundleFlagIndex + 3] || void 0
176
- };
177
- }
178
- await serve({
179
- url,
180
- path,
181
- dirs,
182
- spa,
260
+ let {
261
+ "": unkeyedArgs,
183
262
  bundle: bundle2,
184
- log: true,
185
- watch,
186
- minify
263
+ dirs,
264
+ ...args
265
+ } = (0, import_args_json.parseArgs)(process.argv.slice(2), {
266
+ b: "bundle",
267
+ u: "url",
268
+ s: "spa"
187
269
  });
270
+ let config = {
271
+ path: unkeyedArgs?.[0],
272
+ dirs: Array.isArray(dirs) ? dirs : dirs && [dirs],
273
+ bundle: Array.isArray(bundle2) ? { input: bundle2[0], output: bundle2[1], dir: bundle2[2] } : bundle2,
274
+ log: true,
275
+ ...args
276
+ };
277
+ await serve(config);
188
278
  }
189
279
  run();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@t8/serve",
3
- "version": "0.1.38",
3
+ "version": "0.2.0",
4
4
  "description": "Simple static file server + bundler, primarily for demo apps and tests, manual or automated",
5
5
  "keywords": [
6
6
  "node",
@@ -9,7 +9,7 @@
9
9
  "bundler"
10
10
  ],
11
11
  "bin": {
12
- "serve": "dist/run.mjs"
12
+ "serve": "./dist/run.mjs"
13
13
  },
14
14
  "repository": {
15
15
  "type": "git",
@@ -27,9 +27,10 @@
27
27
  "shape": "npx codeshape"
28
28
  },
29
29
  "devDependencies": {
30
- "@types/node": "^24.5.2"
30
+ "@types/node": "^25.0.3"
31
31
  },
32
32
  "dependencies": {
33
- "esbuild": "^0.27.1"
33
+ "args-json": "^1.2.10",
34
+ "esbuild": "^0.27.2"
34
35
  }
35
36
  }
package/src/run.ts CHANGED
@@ -1,57 +1,36 @@
1
1
  #!/usr/bin/env node
2
+ import { parseArgs } from "args-json";
2
3
  import type { Config } from "./Config.ts";
3
4
  import { serve } from "./serve.ts";
4
5
 
5
- async function run() {
6
- let [url, ...args] = process.argv.slice(2);
7
- let spa = false;
8
- let watch = false;
9
- let minify = false;
10
-
11
- if (args[0] === "*") {
12
- spa = true;
13
- args.shift();
14
- }
15
-
16
- while (args.at(-1)?.startsWith("--")) {
17
- let arg = args.pop();
18
-
19
- switch (arg) {
20
- case "--watch":
21
- watch = true;
22
- break;
23
- case "--minify":
24
- minify = true;
25
- break;
26
- }
27
- }
28
-
29
- let bundleFlagIndex = args.indexOf("-b");
30
- let path = args[0];
31
-
32
- let dirs: string[];
33
- let bundle: Config["bundle"];
34
-
35
- if (bundleFlagIndex === -1) dirs = args.slice(1);
36
- else {
37
- dirs = args.slice(1, bundleFlagIndex);
38
- bundle = {
39
- input: args[bundleFlagIndex + 1] || undefined,
40
- output: args[bundleFlagIndex + 2] || undefined,
41
- dir: args[bundleFlagIndex + 3] || undefined,
42
- };
43
- }
6
+ type CLIConfig = Omit<Config, "bundle" | "onRequest"> & {
7
+ ""?: string[];
8
+ bundle?: string | string[];
9
+ };
44
10
 
45
- await serve({
46
- url,
47
- path,
48
- dirs,
49
- spa,
11
+ async function run() {
12
+ let {
13
+ "": unkeyedArgs,
50
14
  bundle,
51
- log: true,
52
- watch,
53
- minify,
15
+ dirs,
16
+ ...args
17
+ } = parseArgs<CLIConfig>(process.argv.slice(2), {
18
+ b: "bundle",
19
+ u: "url",
20
+ s: "spa",
54
21
  });
22
+
23
+ let config: Config = {
24
+ path: unkeyedArgs?.[0],
25
+ dirs: Array.isArray(dirs) ? dirs : dirs && [dirs],
26
+ bundle: Array.isArray(bundle)
27
+ ? { input: bundle[0], output: bundle[1], dir: bundle[2] }
28
+ : bundle,
29
+ log: true,
30
+ ...args,
31
+ };
32
+
33
+ await serve(config);
55
34
  }
56
35
 
57
36
  run();
package/src/serve.ts CHANGED
@@ -9,6 +9,13 @@ import { mimeTypes } from "./mimeTypes.ts";
9
9
 
10
10
  export type Server = ReturnType<typeof createServer>;
11
11
 
12
+ /**
13
+ * Starts a static server as configured by the `config` parameter and
14
+ * returns the `Server` object.
15
+ *
16
+ * Bundles the code before starting the server, if configured with
17
+ * `config.bundle` to do so.
18
+ */
12
19
  export async function serve(config: Config = {}): Promise<Server> {
13
20
  let stop = await bundle(config);
14
21