appstage 0.3.0 → 0.3.2

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.
Files changed (51) hide show
  1. package/package.json +2 -6
  2. package/src/controllers/files.ts +0 -250
  3. package/src/controllers/redirect.ts +0 -29
  4. package/src/controllers/unhandledError.ts +0 -15
  5. package/src/controllers/unhandledRoute.ts +0 -14
  6. package/src/lib/lang/getEffectiveLocale.ts +0 -52
  7. package/src/lib/lang/getLocales.ts +0 -10
  8. package/src/lib/lang/toLanguage.ts +0 -3
  9. package/src/lib/logger/LogOptions.ts +0 -8
  10. package/src/lib/logger/ansiEscapeCodes.ts +0 -6
  11. package/src/lib/logger/levelColors.ts +0 -4
  12. package/src/lib/logger/log.ts +0 -82
  13. package/src/middleware/init.ts +0 -22
  14. package/src/middleware/lang.ts +0 -83
  15. package/src/middleware/requestEvents.ts +0 -29
  16. package/src/scripts/bin.ts +0 -40
  17. package/src/scripts/build.ts +0 -80
  18. package/src/scripts/cli.ts +0 -43
  19. package/src/scripts/const/commonBuildOptions.ts +0 -13
  20. package/src/scripts/const/entryExtensions.ts +0 -1
  21. package/src/scripts/types/BuildParams.ts +0 -22
  22. package/src/scripts/utils/buildClient.ts +0 -42
  23. package/src/scripts/utils/buildServer.ts +0 -36
  24. package/src/scripts/utils/buildServerCSS.ts +0 -40
  25. package/src/scripts/utils/createPostbuildPlugins.ts +0 -66
  26. package/src/scripts/utils/getEntries.ts +0 -22
  27. package/src/scripts/utils/getEntryPoints.ts +0 -25
  28. package/src/scripts/utils/getFirstAvailable.ts +0 -22
  29. package/src/scripts/utils/setEntriesExport.ts +0 -30
  30. package/src/scripts/utils/toImportPath.ts +0 -12
  31. package/src/types/Controller.ts +0 -4
  32. package/src/types/ErrorController.ts +0 -3
  33. package/src/types/LogEventPayload.ts +0 -12
  34. package/src/types/LogLevel.ts +0 -1
  35. package/src/types/Middleware.ts +0 -7
  36. package/src/types/MiddlewareSet.ts +0 -3
  37. package/src/types/RenderStatus.ts +0 -9
  38. package/src/types/ReqCtx.ts +0 -11
  39. package/src/types/TransformContent.ts +0 -11
  40. package/src/types/express.d.ts +0 -15
  41. package/src/types/global.d.ts +0 -19
  42. package/src/utils/createApp.ts +0 -45
  43. package/src/utils/cspNonce.ts +0 -6
  44. package/src/utils/emitLog.ts +0 -18
  45. package/src/utils/getEntries.ts +0 -22
  46. package/src/utils/getServerURL.ts +0 -38
  47. package/src/utils/getStatusMessage.ts +0 -5
  48. package/src/utils/injectNonce.ts +0 -7
  49. package/src/utils/renderStatus.ts +0 -20
  50. package/src/utils/serializeState.ts +0 -3
  51. package/src/utils/servePipeableStream.ts +0 -32
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appstage",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -15,10 +15,6 @@
15
15
  "shape": "npx codeshape",
16
16
  "typecheck": "npx codeshape typecheck"
17
17
  },
18
- "repository": {
19
- "type": "git",
20
- "url": "git+https://github.com/axtk/appstage.git"
21
- },
22
18
  "author": "axtk",
23
19
  "license": "MIT",
24
20
  "peerDependencies": {
@@ -26,7 +22,7 @@
26
22
  },
27
23
  "devDependencies": {
28
24
  "@types/express": "^5.0.6",
29
- "@types/node": "^25.4.0"
25
+ "@types/node": "^25.9.2"
30
26
  },
31
27
  "dependencies": {
32
28
  "args-json": "^1.4.3",
@@ -1,250 +0,0 @@
1
- import { lstat, readFile } from "node:fs/promises";
2
- import { basename, extname, join, resolve as resolveAbsPath } from "node:path";
3
- import type { Request } from "express";
4
- import type { Controller } from "../types/Controller.ts";
5
- import type { TransformContent } from "../types/TransformContent.ts";
6
- import { emitLog } from "../utils/emitLog.ts";
7
-
8
- type StringMatcher =
9
- | string
10
- | RegExp
11
- | (string | RegExp)[]
12
- | ((x: string) => boolean)
13
- | null;
14
-
15
- const maxLanguages = 3;
16
-
17
- async function resolve(...parts: string[]) {
18
- let fullPath = join(...parts);
19
- try {
20
- if ((await lstat(fullPath)).isFile()) return resolveAbsPath(fullPath);
21
- } catch {}
22
- return null;
23
- }
24
-
25
- // ["en-US", "ru"] > ["en-US", "en", "ru"]
26
- function getLanguageList(req: Request) {
27
- let langParam = req.query.lang;
28
-
29
- if (langParam) return [String(langParam)];
30
-
31
- let acceptedLanguages = req.acceptsLanguages();
32
- let langs = new Set<string>();
33
-
34
- for (let i = 0; i < acceptedLanguages.length && i < maxLanguages; i++) {
35
- let s = acceptedLanguages[i];
36
- let [lang] = s.split(/[-_]/);
37
-
38
- if (s === lang) langs.add(s);
39
- else {
40
- langs.add(s);
41
- langs.add(lang);
42
- }
43
- }
44
-
45
- return Array.from(langs);
46
- }
47
-
48
- function matches(x: string, matcher: StringMatcher | undefined) {
49
- if (matcher === null || matcher === undefined) return true;
50
-
51
- if (typeof matcher === "function") return matcher(x);
52
-
53
- let patterns = Array.isArray(matcher) ? matcher : [matcher];
54
-
55
- for (let pattern of patterns) {
56
- if (pattern instanceof RegExp) {
57
- if (pattern.test(x)) return true;
58
- } else if (pattern === x) return true;
59
- }
60
-
61
- return false;
62
- }
63
-
64
- export type FilesParams = {
65
- base: string | string[];
66
- path?: string | ((req: Request) => string);
67
- /** Specifies which paths should be accepted. */
68
- matches?: StringMatcher;
69
- /**
70
- * @default ["html", "htm"]
71
- */
72
- extensions?: string[];
73
- languages?: (req: Request) => string[];
74
- /**
75
- * Assumed file language if unspecified in the file name.
76
- *
77
- * @default "en"
78
- */
79
- defaultLanguage?: string | ((req: Request) => string);
80
- transform?: TransformContent[];
81
- fallthrough?: boolean;
82
- };
83
-
84
- const defaultExtensions = ["html", "htm"];
85
- const defaultPath = (req: Request) => decodeURIComponent(req.path);
86
-
87
- /**
88
- * Serves files from the specified directory path or paths in a locale-aware
89
- * fashion after applying optional transforms.
90
- */
91
- export const files: Controller<string | FilesParams> = (params) => {
92
- let p: FilesParams = typeof params === "string" ? { base: params } : params;
93
-
94
- let bases = Array.isArray(p.base) ? p.base : [p.base];
95
- let exts = p.extensions ?? defaultExtensions;
96
- let fallthrough = p.fallthrough ?? true;
97
-
98
- return async (req, res, next) => {
99
- let urlPath =
100
- typeof p.path === "string" ? p.path : (p.path ?? defaultPath)(req);
101
-
102
- let defaultLanguage =
103
- typeof p.defaultLanguage === "function"
104
- ? p.defaultLanguage(req)
105
- : (p.defaultLanguage ?? "en");
106
-
107
- if (!matches(urlPath, p.matches)) {
108
- if (fallthrough) next();
109
- else {
110
- emitLog(req.app, "Unmatched path", {
111
- data: { urlPath },
112
- req,
113
- res,
114
- });
115
-
116
- res.status(404).send(
117
- await req.app.renderStatus?.(req, res, {
118
- code: "unmatched_path",
119
- urlPath,
120
- }),
121
- );
122
- }
123
-
124
- return;
125
- }
126
-
127
- if (urlPath.includes("../")) {
128
- if (fallthrough) next();
129
- else {
130
- emitLog(req.app, "Invalid path (potential traversal attempt)", {
131
- data: { urlPath },
132
- req,
133
- res,
134
- });
135
-
136
- res.status(400).send(
137
- await req.app.renderStatus?.(req, res, {
138
- code: "invalid_path",
139
- urlPath,
140
- }),
141
- );
142
- }
143
-
144
- return;
145
- }
146
-
147
- let filePath: string | null = null;
148
- let urlExt = extname(urlPath);
149
-
150
- let langs = (p.languages ?? getLanguageList)(req);
151
- let defaultLanguageIndex = langs.indexOf(defaultLanguage);
152
-
153
- let suffixes = langs.map((s) => `.${s}`);
154
-
155
- // Check the suffixless path right after the default language suffix.
156
- if (defaultLanguageIndex === -1) suffixes.push("");
157
- else suffixes.splice(defaultLanguageIndex + 1, 0, "");
158
-
159
- // Example:
160
- // path = /x, langs = [en, ru], default lang: en, exts = [html, htm]
161
- for (let k = 0; k < bases.length && filePath === null; k++) {
162
- let base = bases[k];
163
-
164
- if (!urlPath.endsWith("/")) {
165
- if (filePath === null && urlExt) {
166
- let urlPathBase = urlPath.slice(0, -urlExt.length);
167
-
168
- // /x.en.ext /x.ext /x.ru.ext
169
- for (let i = 0; i < suffixes.length && filePath === null; i++)
170
- filePath = await resolve(
171
- base,
172
- `${urlPathBase}${suffixes[i]}${urlExt}`,
173
- );
174
- }
175
-
176
- // /x.en /x /x.ru
177
- for (let i = 0; i < suffixes.length && filePath === null; i++)
178
- filePath = await resolve(base, `${urlPath}${suffixes[i]}`);
179
-
180
- // /x.en.html /x.en.htm /x.html /x.htm /x.ru.html /x.ru.htm
181
- for (let i = 0; i < suffixes.length && filePath === null; i++) {
182
- for (let j = 0; j < exts.length && filePath === null; j++)
183
- filePath = await resolve(
184
- base,
185
- `${urlPath}${suffixes[i]}.${exts[j]}`,
186
- );
187
- }
188
- }
189
-
190
- // /x/index.en.html /x/index.en.htm /x/index.html /x/index.htm /x/index.ru.html /x/index.ru.htm
191
- for (let i = 0; i < suffixes.length && filePath === null; i++) {
192
- for (let j = 0; j < exts.length && filePath === null; j++)
193
- filePath = await resolve(
194
- base,
195
- urlPath,
196
- `index${suffixes[i]}.${exts[j]}`,
197
- );
198
- }
199
- }
200
-
201
- if (filePath === null) {
202
- if (fallthrough) next();
203
- else {
204
- emitLog(req.app, "Unknown path", {
205
- data: { urlPath },
206
- req,
207
- res,
208
- });
209
-
210
- res.status(404).send(
211
- await req.app.renderStatus?.(req, res, {
212
- code: "unknown_path",
213
- urlPath,
214
- }),
215
- );
216
- }
217
-
218
- return;
219
- }
220
-
221
- emitLog(req.app, "File path resolved", {
222
- data: {
223
- filePath,
224
- },
225
- req,
226
- res,
227
- });
228
-
229
- if (!p.transform?.length) {
230
- res.sendFile(filePath);
231
- return;
232
- }
233
-
234
- let content = (await readFile(filePath)).toString();
235
- let fileExt = extname(filePath);
236
- let fileName = basename(filePath, fileExt);
237
-
238
- for (let transform of p.transform) {
239
- let result = transform(req, res, {
240
- content,
241
- path: filePath,
242
- name: fileName,
243
- });
244
-
245
- content = result instanceof Promise ? await result : result;
246
- }
247
-
248
- res.type(fileExt.slice(1)).send(content);
249
- };
250
- };
@@ -1,29 +0,0 @@
1
- import type { Controller } from "../types/Controller.ts";
2
- import { emitLog } from "../utils/emitLog.ts";
3
-
4
- export type RedirectParams = {
5
- url: string;
6
- status?: number;
7
- };
8
-
9
- export const redirect: Controller<string | RedirectParams> = (params) => {
10
- let { url, status = 302 } =
11
- typeof params === "string" ? { url: params } : params;
12
-
13
- return (req, res) => {
14
- let search = req.originalUrl.split("?")[1] ?? "";
15
-
16
- if (search) search = `${url.includes("?") ? "&" : "?"}${search}`;
17
-
18
- emitLog(req.app, "Redirecting", {
19
- data: {
20
- from: req.originalUrl,
21
- to: `${url}${search}`,
22
- },
23
- req,
24
- res,
25
- });
26
-
27
- res.redirect(status, `${url}${search}`);
28
- };
29
- };
@@ -1,15 +0,0 @@
1
- import type { ErrorController } from "../types/ErrorController.ts";
2
- import { emitLog } from "../utils/emitLog.ts";
3
-
4
- export const unhandledError: ErrorController = () => async (err, req, res) => {
5
- emitLog(req.app, "Unhandled error", {
6
- level: "error",
7
- data: err,
8
- req,
9
- res,
10
- });
11
-
12
- res
13
- .status(500)
14
- .send(await req.app.renderStatus?.(req, res, "unhandled_error"));
15
- };
@@ -1,14 +0,0 @@
1
- import type { Controller } from "../types/Controller.ts";
2
- import { emitLog } from "../utils/emitLog.ts";
3
-
4
- export const unhandledRoute: Controller = () => async (req, res) => {
5
- emitLog(req.app, "Unhandled route", {
6
- level: "debug",
7
- req,
8
- res,
9
- });
10
-
11
- res
12
- .status(404)
13
- .send(await req.app.renderStatus?.(req, res, "unhandled_route"));
14
- };
@@ -1,52 +0,0 @@
1
- import { toLanguage } from "./toLanguage.ts";
2
-
3
- type Match = {
4
- index?: number;
5
- locale?: string | undefined;
6
- };
7
-
8
- export function getEffectiveLocale(
9
- preferredLocales: string[] | undefined,
10
- supportedLocales: string[] | undefined,
11
- ): string | undefined {
12
- if (!supportedLocales || supportedLocales.length === 0) return undefined;
13
-
14
- if (!preferredLocales || preferredLocales.length === 0)
15
- return supportedLocales[0];
16
-
17
- let exactMatch: Match = {};
18
-
19
- for (let i = 0; i < preferredLocales.length && !exactMatch.locale; i++) {
20
- let k = supportedLocales.indexOf(preferredLocales[i]);
21
-
22
- if (k !== -1) {
23
- exactMatch.index = i;
24
- exactMatch.locale = supportedLocales[k];
25
- }
26
- }
27
-
28
- let languageMatch: Match = {};
29
-
30
- let supportedLanguages = supportedLocales.map(toLanguage);
31
- let preferredLanguages = preferredLocales.map(toLanguage);
32
-
33
- for (let i = 0; i < preferredLanguages.length && !languageMatch.locale; i++) {
34
- let k = supportedLanguages.indexOf(preferredLanguages[i]);
35
-
36
- if (k !== -1) {
37
- languageMatch.index = i;
38
- languageMatch.locale = supportedLocales[k];
39
- }
40
- }
41
-
42
- // if both an exact match and a language match are found, prefer
43
- // the one with an index closer to the beginning of the preferred
44
- // locale list
45
- if (
46
- exactMatch.locale &&
47
- (!languageMatch.locale || exactMatch.index! < languageMatch.index!)
48
- )
49
- return exactMatch.locale;
50
-
51
- return languageMatch.locale ?? supportedLocales[0];
52
- }
@@ -1,10 +0,0 @@
1
- /**
2
- * Parses a language range string (typically a value of the 'Accept-Language'
3
- * HTTP request header) and returns a corresponding array of locales
4
- * @example 'fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5'
5
- */
6
- export function getLocales(languageRange: string | undefined): string[] {
7
- return (languageRange ?? "")
8
- .split(/[,;]\s*/)
9
- .filter((s) => !s.startsWith("q=") && s !== "*");
10
- }
@@ -1,3 +0,0 @@
1
- export function toLanguage(locale: string): string {
2
- return locale.split(/[-_]/)[0];
3
- }
@@ -1,8 +0,0 @@
1
- import type { Request } from "express";
2
-
3
- export type LogOptions = {
4
- timestamp?: number;
5
- level?: "debug" | "info" | "warn" | "error";
6
- data?: unknown;
7
- req?: Request;
8
- };
@@ -1,6 +0,0 @@
1
- export const ansiEscapeCodes: Record<string, string> = {
2
- reset: "\x1b[0m",
3
- dim: "\x1b[2m",
4
- red: "\x1b[31m",
5
- yellow: "\x1b[33m",
6
- };
@@ -1,4 +0,0 @@
1
- export const levelColors: Record<string, string> = {
2
- error: "red",
3
- warn: "yellow",
4
- };
@@ -1,82 +0,0 @@
1
- import { formatDate, formatDuration } from "dateshape";
2
- import { ansiEscapeCodes } from "./ansiEscapeCodes.ts";
3
- import type { LogOptions } from "./LogOptions.ts";
4
- import { levelColors } from "./levelColors.ts";
5
-
6
- function isEmpty(x: unknown) {
7
- if (x === null || x === undefined || x === "") return true;
8
- if (Array.isArray(x) && x.length === 0) return true;
9
- if (typeof x === "object" && Object.keys(x).length === 0) return true;
10
-
11
- return false;
12
- }
13
-
14
- export function log(
15
- message: string | Error | undefined = "",
16
- { timestamp, level, data, req }: LogOptions = {},
17
- ): void {
18
- let currentTime = timestamp ?? Date.now();
19
- let error: Error | null = null;
20
-
21
- if (message instanceof Error) {
22
- error = message;
23
- message = error.message;
24
-
25
- data = {
26
- error,
27
- data,
28
- };
29
-
30
- if (!level) level = "error";
31
- }
32
-
33
- if (data instanceof Error) {
34
- error = data;
35
-
36
- if (data.message)
37
- message = `${message ? `${message} - ` : ""}${data.message}`;
38
-
39
- if (!level) level = "error";
40
- }
41
-
42
- if (!level) level = "info";
43
-
44
- let levelCode = ansiEscapeCodes[levelColors[level]] ?? "";
45
- let requestTarget = req ? `${req.method} ${req.originalUrl}` : "";
46
-
47
- let { startTime, id: sessionId } = req?.ctx ?? {};
48
-
49
- console[level]();
50
-
51
- console[level](
52
- levelCode +
53
- ansiEscapeCodes.dim +
54
- formatDate(currentTime, "{isoDate} {isoTimeMs} {tz}") +
55
- (sessionId ? ` <${sessionId}>` : "") +
56
- (startTime === undefined
57
- ? ""
58
- : ` +${formatDuration(currentTime - startTime)}`) +
59
- ansiEscapeCodes.reset,
60
- );
61
-
62
- console[level](
63
- levelCode +
64
- requestTarget +
65
- (requestTarget && message && !message.startsWith("\n") ? " - " : "") +
66
- message +
67
- ansiEscapeCodes.reset,
68
- );
69
-
70
- if (!isEmpty(data))
71
- console[level](
72
- levelCode +
73
- ansiEscapeCodes.dim +
74
- (typeof data === "string" ? data : JSON.stringify(data, null, 2)) +
75
- ansiEscapeCodes.reset,
76
- );
77
-
78
- if (error?.stack)
79
- console[level](
80
- levelCode + ansiEscapeCodes.dim + error.stack + ansiEscapeCodes.reset,
81
- );
82
- }
@@ -1,22 +0,0 @@
1
- import { randomBytes } from "node:crypto";
2
- import type { Middleware } from "../types/Middleware.ts";
3
- import { emitLog } from "../utils/emitLog.ts";
4
-
5
- /**
6
- * Initializes the request context on `req.ctx`.
7
- */
8
- export const init: Middleware = () => (req, res, next) => {
9
- req.ctx = {
10
- ...req.ctx,
11
- id: randomBytes(16).toString("hex"),
12
- nonce: randomBytes(8).toString("hex"),
13
- startTime: Date.now(),
14
- };
15
-
16
- emitLog(req.app, "Inited", {
17
- req,
18
- res,
19
- });
20
-
21
- next();
22
- };
@@ -1,83 +0,0 @@
1
- import type { CookieOptions } from "express";
2
- import { getEffectiveLocale } from "../lib/lang/getEffectiveLocale.ts";
3
- import { getLocales } from "../lib/lang/getLocales.ts";
4
- import { toLanguage } from "../lib/lang/toLanguage.ts";
5
- import type { Middleware } from "../types/Middleware.ts";
6
- import { emitLog } from "../utils/emitLog.ts";
7
-
8
- const defaultLangCookieOptions: CookieOptions = {
9
- maxAge: 90 * 86_400_000,
10
- };
11
-
12
- export type LangParams = {
13
- supportedLocales?: string[];
14
- shouldSetCookie?: boolean;
15
- shouldRedirect?: boolean;
16
- langCookieOptions?: CookieOptions;
17
- };
18
-
19
- export const lang: Middleware<LangParams | void> = ({
20
- supportedLocales = [],
21
- shouldSetCookie = true,
22
- shouldRedirect = true,
23
- langCookieOptions = defaultLangCookieOptions,
24
- } = {}) => {
25
- let langSet = new Set(supportedLocales.map(toLanguage));
26
- let localeSet = new Set(supportedLocales);
27
-
28
- return (req, res, next) => {
29
- let langParam = req.query.lang as string[] | string | undefined;
30
- let lang = (Array.isArray(langParam) ? langParam[0] : langParam) ?? "";
31
-
32
- if (localeSet.has(lang) || langSet.has(lang)) {
33
- if (shouldSetCookie) {
34
- emitLog(req.app, `Set lang cookie: ${JSON.stringify(lang)}`, {
35
- req,
36
- res,
37
- });
38
-
39
- res.cookie("lang", lang, langCookieOptions);
40
- }
41
-
42
- if (shouldRedirect) {
43
- let { originalUrl } = req;
44
- let nextUrl = originalUrl.replace(/[?&]lang=[^&]+/g, "");
45
-
46
- if (nextUrl !== originalUrl) {
47
- emitLog(req.app, "Strip lang param and redirect", {
48
- req,
49
- res,
50
- });
51
-
52
- res.redirect(nextUrl);
53
- return;
54
- }
55
- }
56
- }
57
-
58
- let langCookie = shouldSetCookie
59
- ? (req.cookies.lang as string | undefined)
60
- : undefined;
61
-
62
- let userAgentLocales = getLocales(req.get("accept-language"));
63
- let preferredLocales = langCookie
64
- ? [langCookie, ...userAgentLocales]
65
- : userAgentLocales;
66
-
67
- let effectiveLang = getEffectiveLocale(preferredLocales, supportedLocales);
68
-
69
- if (req.ctx && effectiveLang) req.ctx.lang = effectiveLang;
70
-
71
- emitLog(req.app, `Detected lang: ${JSON.stringify(effectiveLang)}`, {
72
- req,
73
- res,
74
- data: {
75
- userAgentLocales,
76
- langCookie,
77
- lang: effectiveLang,
78
- },
79
- });
80
-
81
- next();
82
- };
83
- };
@@ -1,29 +0,0 @@
1
- import type { Middleware } from "../types/Middleware.ts";
2
- import { emitLog } from "../utils/emitLog.ts";
3
- import { getStatusMessage } from "../utils/getStatusMessage.ts";
4
-
5
- /**
6
- * Adds event handlers, like logging, to essential request phases.
7
- */
8
- export const requestEvents: Middleware = () => (req, res, next) => {
9
- let finished = false;
10
-
11
- res.on("finish", () => {
12
- finished = true;
13
-
14
- emitLog(req.app, getStatusMessage("Finished", res.statusCode), {
15
- req,
16
- res,
17
- });
18
- });
19
-
20
- res.on("close", () => {
21
- if (!finished)
22
- emitLog(req.app, getStatusMessage("Closed", res.statusCode), {
23
- req,
24
- res,
25
- });
26
- });
27
-
28
- next();
29
- };
@@ -1,40 +0,0 @@
1
- #!/usr/bin/env node
2
- import { hasKey, isKey } from "args-json";
3
- import { cli } from "./cli.ts";
4
-
5
- const availableScriptNames = new Set(["build", "dev", "prod"]);
6
-
7
- const nodeEnvMap: Record<string, string> = {
8
- dev: "development",
9
- prod: "production",
10
- };
11
-
12
- async function run() {
13
- let [scriptName, ...args] = process.argv.slice(2);
14
-
15
- if (!availableScriptNames.has(scriptName)) {
16
- console.error("Provide a script name: 'build', 'dev', or 'prod'");
17
- return;
18
- }
19
-
20
- if (scriptName === "build") return await cli(args);
21
-
22
- let nodeEnv = nodeEnvMap[scriptName];
23
-
24
- if (nodeEnv !== undefined) process.env.NODE_ENV = nodeEnv;
25
-
26
- if (args.length !== 0 && args[0] && !isKey(args[0])) {
27
- process.env.SERVER_URL = args[0];
28
- args.shift();
29
- }
30
-
31
- if (!hasKey("--client-dir")) args.push("--client-dir", "src/public/-");
32
-
33
- await cli(
34
- nodeEnv === "development"
35
- ? ["--clean", "--start", "--watch", ...args]
36
- : ["--clean", "--start", "--silent", ...args],
37
- );
38
- }
39
-
40
- await run();