@cley_faye/boilerplate 3.18.0 → 3.20.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
@@ -443,6 +443,7 @@ appStart({
443
443
  port: 3000,
444
444
  shutdownFunction: () => {;},
445
445
  logger: consoleLogger,
446
+ noReady: false,
446
447
  }).then(({port, server}) => {;});
447
448
  ```
448
449
 
@@ -453,8 +454,14 @@ The settings are:
453
454
  - port: port to listen to. Can be 0 to use a random available port
454
455
  - shutdownFunction: a function to call after the server stop listening
455
456
  - logger: a winston logger to log that the server started listening
457
+ - noReady: if true, does not call `process.send("ready");` (used by PM2)
456
458
 
457
- The function returns a promise that resolve with the actual port used for listening when the server is started.
459
+ The shutdown function can return a promise, and if it returns/resolve with `true` the process is
460
+ left alive.
461
+ Otherwise `process.exit()` is called after `shutdownFunction()` resolves/returns.
462
+
463
+ The `appStart()` function returns a promise that resolve with the actual port used for listening
464
+ when the server is started.
458
465
 
459
466
  The server is automatically registered using the `autoclose` part to stop when a SIGINT signal is
460
467
  received.
@@ -53,16 +53,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
53
53
  };
54
54
  Object.defineProperty(exports, "__esModule", { value: true });
55
55
  const path = __importStar(require("node:path"));
56
+ const node_fs_1 = require("node:fs");
56
57
  const express_1 = __importDefault(require("express"));
57
58
  const minimist_1 = __importDefault(require("minimist"));
58
59
  const singlepageapp_js_1 = require("../express/middlewares/singlepageapp.js");
59
60
  const express_js_1 = require("../express.js");
60
61
  const winston_js_1 = require("../winston.js");
61
62
  const ARGV_START_OFFSET = 2;
62
- const { port, root, index, help, static: staticDirs } = (0, minimist_1.default)(process.argv.slice(ARGV_START_OFFSET));
63
+ const { port, root, index, help, staticlist, static: staticDirs } = (0, minimist_1.default)(process.argv.slice(ARGV_START_OFFSET));
63
64
  const DEFAULT_PORT = 3000;
64
65
  const DEFAULT_PATH = path.join("dist", "webapp");
65
66
  const DEFAULT_INDEX = "index.html";
67
+ const DEFAULT_STATICFILE = "spa_static.config";
66
68
  const DEFAULT_STATIC = ["js", "css", "img"];
67
69
  if (help) {
68
70
  winston_js_1.consoleLogger.info(`
@@ -72,20 +74,34 @@ serve_spa \\
72
74
  [--port=${DEFAULT_PORT}] \\
73
75
  [--root=${DEFAULT_PATH}] \\
74
76
  [--index=${DEFAULT_INDEX}] \\
77
+ [--staticlist=${DEFAULT_STATICFILE}] \\
75
78
  [--static=js…] \\
76
79
  [--help]
77
80
 
78
81
  The --static argument can be repeated to add more directories to it.
79
82
  It defaults to ${JSON.stringify(DEFAULT_STATIC)}.
83
+
84
+ The staticfile value is a text file with one static directory line.
85
+ If present, they are merged with the CLI arguments.
80
86
  `.trim());
81
87
  // eslint-disable-next-line no-process-exit
82
88
  process.exit(0);
83
89
  }
84
90
  const getPort = () => port ?? DEFAULT_PORT;
85
91
  const getStatic = () => {
86
- if (!staticDirs)
87
- return;
88
- return Array.isArray(staticDirs) ? staticDirs : [staticDirs];
92
+ const result = [];
93
+ if (staticDirs)
94
+ result.push(...(Array.isArray(staticDirs) ? staticDirs : [staticDirs]));
95
+ if (staticlist) {
96
+ for (const staticsrc of (Array.isArray(staticlist) ? staticlist : [staticlist])) {
97
+ if (!(0, node_fs_1.existsSync)(staticsrc))
98
+ continue;
99
+ result.push(...(0, node_fs_1.readFileSync)(staticsrc, "utf8")
100
+ .split("\n")
101
+ .filter(c => c));
102
+ }
103
+ }
104
+ return result.length > 0 ? result : undefined;
89
105
  };
90
106
  const app = (0, express_1.default)();
91
107
  app.use((0, express_js_1.createPipeline)({
package/lib/express.d.ts CHANGED
@@ -104,6 +104,15 @@ export declare const createPipeline: (settings: PipelineSettings, debugLog?: Log
104
104
  * Configure the views rendering engine for an ExpressJS App
105
105
  */
106
106
  export declare const setViewEngine: (app: express.Express, viewEngine: string, viewsDirectory: string, extraLocals?: Record<string, string | number>) => void;
107
+ /**
108
+ * Function to call when the server stops.
109
+ *
110
+ * This is usually triggered by a SIGTERM.
111
+ *
112
+ * If the function returns "true", it prevent the process from stopping.
113
+ * Otherwise `process.exit()` is called.
114
+ */
115
+ type ShutdownFunction = () => Promise<boolean> | boolean | undefined;
107
116
  export interface StartDefinition {
108
117
  app: express.Application;
109
118
  /** @deprecated use listenInterface instead */
@@ -117,8 +126,9 @@ export interface StartDefinition {
117
126
  */
118
127
  listenInterface?: boolean | string;
119
128
  port?: number;
120
- shutdownFunction?: () => void;
129
+ shutdownFunction?: ShutdownFunction;
121
130
  logger?: winston.Logger;
131
+ noReady?: boolean;
122
132
  }
123
133
  export interface StartResult {
124
134
  port: number;
@@ -144,4 +154,5 @@ export interface StartResult {
144
154
  *
145
155
  * @property server
146
156
  */
147
- export declare const appStart: ({ app, allowNonLocal, listenInterface, port, shutdownFunction, logger, }: StartDefinition) => Promise<StartResult>;
157
+ export declare const appStart: ({ app, allowNonLocal, listenInterface, port, shutdownFunction, logger, noReady, }: StartDefinition) => Promise<StartResult>;
158
+ export {};
package/lib/express.js CHANGED
@@ -149,6 +149,19 @@ const setViewEngine = (app, viewEngine, viewsDirectory, extraLocals) => {
149
149
  }
150
150
  };
151
151
  exports.setViewEngine = setViewEngine;
152
+ const handleShutdown = (logger, shutdownFunction) => () => {
153
+ const fn = async () => {
154
+ const noExit = shutdownFunction ? Boolean(await shutdownFunction()) : false;
155
+ if (noExit)
156
+ return;
157
+ // We *want* to exit on server shutdown
158
+ // eslint-disable-next-line no-process-exit
159
+ process.exit();
160
+ };
161
+ fn().catch(err => {
162
+ logger?.error(err);
163
+ });
164
+ };
152
165
  /** Start the server and register signal for automatic closing.
153
166
  *
154
167
  * @param app
@@ -169,7 +182,7 @@ exports.setViewEngine = setViewEngine;
169
182
  *
170
183
  * @property server
171
184
  */
172
- const appStart = ({ app, allowNonLocal, listenInterface, port, shutdownFunction, logger, }) => new Promise(resolve => {
185
+ const appStart = ({ app, allowNonLocal, listenInterface, port, shutdownFunction, logger, noReady, }) => new Promise(resolve => {
173
186
  let server = null;
174
187
  const readyCallback = () => {
175
188
  if (server === null) {
@@ -185,11 +198,13 @@ const appStart = ({ app, allowNonLocal, listenInterface, port, shutdownFunction,
185
198
  port: effectivePort,
186
199
  server,
187
200
  });
201
+ if (!noReady && process.send)
202
+ process.send("ready");
188
203
  };
189
204
  if (allowNonLocal !== undefined && listenInterface !== undefined) {
190
205
  throw new Error("You can't specify both allowNonLocal and listenInterface at the same time");
191
206
  }
192
- if (allowNonLocal || listenInterface === true) {
207
+ if (allowNonLocal ?? listenInterface === true) {
193
208
  server = app.listen(port ?? 0, readyCallback);
194
209
  }
195
210
  else if (typeof listenInterface === "string") {
@@ -198,8 +213,6 @@ const appStart = ({ app, allowNonLocal, listenInterface, port, shutdownFunction,
198
213
  else {
199
214
  server = app.listen(port ?? 0, "localhost", readyCallback);
200
215
  }
201
- if (shutdownFunction) {
202
- server.on("close", shutdownFunction);
203
- }
216
+ server.on("close", handleShutdown(logger, shutdownFunction));
204
217
  });
205
218
  exports.appStart = appStart;
package/lib/grunt/args.js CHANGED
@@ -37,7 +37,7 @@ var OptType;
37
37
  OptType["NUMBER"] = "number";
38
38
  // eslint-disable-next-line @typescript-eslint/naming-convention
39
39
  OptType["BOOLEAN"] = "boolean";
40
- })(OptType = exports.OptType || (exports.OptType = {}));
40
+ })(OptType || (exports.OptType = OptType = {}));
41
41
  /**
42
42
  * Process a string-type argument
43
43
  */
@@ -47,7 +47,7 @@ var HandlerType;
47
47
  HandlerType["SASS"] = "sass";
48
48
  // eslint-disable-next-line @typescript-eslint/naming-convention
49
49
  HandlerType["COPY"] = "copy";
50
- })(HandlerType = exports.HandlerType || (exports.HandlerType = {}));
50
+ })(HandlerType || (exports.HandlerType = HandlerType = {}));
51
51
  const gatherTasks = (handlers, gruntConfig, targetName, options) => Object.keys(handlers)
52
52
  .reduce((acc, cur) => acc.concat((options?.[cur] && options[cur]?.disabled)
53
53
  ? []
package/lib/winston.js CHANGED
@@ -69,7 +69,7 @@ exports.filterStacktrace = filterStacktrace;
69
69
  * Prefix all lines with the level and the timestamp if needed
70
70
  */
71
71
  const prefixOutput = (level, message, timestamp) => {
72
- const timestampPrefix = timestamp
72
+ const timestampPrefix = (timestamp ?? (level.toLowerCase() === "error"))
73
73
  ? `${new Date().toISOString()} `
74
74
  : "";
75
75
  const levelPrefix = `${level.toString().padStart(LOG_LEVEL_PADDING_SIZE)}: `;
@@ -119,12 +119,7 @@ const addAxiosContent = (error) => {
119
119
  res.push(JSON.stringify(error.response.data));
120
120
  }
121
121
  catch {
122
- try {
123
- res.push(error.response.data.toString());
124
- }
125
- catch {
126
- res.push("<cannot output body>");
127
- }
122
+ res.push("<cannot output body>");
128
123
  }
129
124
  }
130
125
  }
@@ -145,7 +140,7 @@ const getCause = (error) => {
145
140
  if (cause instanceof Error)
146
141
  return cause;
147
142
  try {
148
- return new Error(cause.toString());
143
+ return new Error(JSON.stringify(cause));
149
144
  }
150
145
  catch {
151
146
  return new Error("Unknown cause");
@@ -205,15 +200,10 @@ const getErrorFromTransformable = (info) => {
205
200
  }
206
201
  };
207
202
  const customFormat = (timestamp, collapseStacktrace, stacktraceFilter) => winston_1.default.format.printf((info) => {
208
- const effectiveTimestamp = timestamp === undefined
209
- ? logConfig.timestamp
210
- : timestamp;
211
- const effectiveCollapse = collapseStacktrace === undefined
212
- ? logConfig.collapseNodeModules || logConfig.collapseStacktrace
213
- : collapseStacktrace;
214
- const effectiveStacktraceFilter = stacktraceFilter === undefined
215
- ? logConfig.stacktraceFilter
216
- : stacktraceFilter;
203
+ const effectiveTimestamp = timestamp ?? logConfig.timestamp;
204
+ const effectiveCollapse = collapseStacktrace
205
+ ?? (logConfig.collapseNodeModules || logConfig.collapseStacktrace);
206
+ const effectiveStacktraceFilter = stacktraceFilter ?? logConfig.stacktraceFilter;
217
207
  const error = getErrorFromTransformable(info);
218
208
  const message = error
219
209
  ? (0, exports.filterError)(error, effectiveCollapse, effectiveStacktraceFilter)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cley_faye/boilerplate",
3
- "version": "3.18.0",
3
+ "version": "3.20.0",
4
4
  "description": "Boilerplate code generator for generic projects",
5
5
  "repository": {
6
6
  "type": "git",
@@ -21,19 +21,19 @@
21
21
  },
22
22
  "homepage": "https://github.com/CleyFaye/boilerplate#readme",
23
23
  "devDependencies": {
24
- "@keeex/eslint-config": "^1.9.3",
25
- "@types/express": "^4.17.17",
26
- "@types/grunt": "^0.4.27",
27
- "@types/http-errors": "^2.0.1",
28
- "@types/minimist": "^1.2.2",
29
- "@types/node": "^18.15.7",
30
- "@typescript-eslint/eslint-plugin": "^5.56.0",
31
- "@typescript-eslint/parser": "^5.56.0",
32
- "eslint": "^8.36.0",
33
- "eslint-plugin-deprecation": "^1.3.3",
34
- "eslint-plugin-import": "^2.27.5",
24
+ "@keeex/eslint-config": "^1.11.5",
25
+ "@types/express": "^4.17.18",
26
+ "@types/grunt": "^0.4.28",
27
+ "@types/http-errors": "^2.0.2",
28
+ "@types/minimist": "^1.2.3",
29
+ "@types/node": "^18.18.3",
30
+ "@typescript-eslint/eslint-plugin": "^6.7.4",
31
+ "@typescript-eslint/parser": "^6.7.4",
32
+ "eslint": "^8.50.0",
33
+ "eslint-plugin-deprecation": "^2.0.0",
34
+ "eslint-plugin-import": "^2.28.1",
35
35
  "eslint-plugin-promise": "^6.1.1",
36
- "eslint-webpack-plugin": "^4.0.0",
36
+ "eslint-webpack-plugin": "^4.0.1",
37
37
  "express-winston": "^4.2.0",
38
38
  "grunt": "^1.6.1",
39
39
  "grunt-banner": "^0.6.0",
@@ -41,9 +41,9 @@
41
41
  "grunt-run": "^0.8.1",
42
42
  "load-grunt-tasks": "^5.1.0",
43
43
  "logform": "^2.5.1",
44
- "typescript": "^4.9.5",
45
- "webpack": "^5.76.3",
46
- "winston": "^3.8.2"
44
+ "typescript": "^5.2.2",
45
+ "webpack": "^5.88.2",
46
+ "winston": "^3.10.0"
47
47
  },
48
48
  "peerDependencies": {
49
49
  "@babel/core": "7.x",