@guritso/terminal 1.2.1 → 1.2.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.
@@ -16,7 +16,7 @@ jobs:
16
16
 
17
17
  strategy:
18
18
  matrix:
19
- node-version: [18.x, 20.x, 22.x]
19
+ node-version: [20.x, 22.x, 24.x]
20
20
  # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
21
21
 
22
22
  steps:
@@ -14,10 +14,10 @@ jobs:
14
14
  - uses: actions/checkout@v4
15
15
  - uses: actions/setup-node@v4
16
16
  with:
17
- node-version: 22.x
17
+ node-version: 24.x
18
18
  - run: npm ci
19
- - run: npm test
20
19
  - run: npm run build --if-present
20
+ - run: npm test
21
21
 
22
22
  publish-npm:
23
23
  needs: build
@@ -29,9 +29,10 @@ jobs:
29
29
  - uses: actions/checkout@v4
30
30
  - uses: actions/setup-node@v4
31
31
  with:
32
- node-version: 22.x
32
+ node-version: 24.x
33
33
  registry-url: https://registry.npmjs.org/
34
34
  - run: npm ci
35
+ - run: npm run build --if-present
35
36
  - run: npm publish --provenance --access public
36
37
  env:
37
38
  NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
package/README.md CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  A terminal node utility for enhanced logging and error handling.
4
4
 
5
+ [![Node.js CI](https://github.com/guritso/terminal/actions/workflows/node.js.yml/badge.svg)](https://github.com/guritso/terminal/actions/workflows/node.js.yml)
6
+ [![NPM Package](https://github.com/guritso/terminal/actions/workflows/npm-publish.yml/badge.svg)](https://github.com/guritso/terminal/actions/workflows/npm-publish.yml)
7
+
5
8
  | example |
6
9
  | ------- |
7
10
  | ![example](./docs/guritso-terminal-preview.png) |
package/index.cjs CHANGED
@@ -1 +1 @@
1
- module.exports = require("./lib/cjs/terminal.cjs");
1
+ module.exports = require("./lib/dist/terminal.cjs");
package/lib/terminal.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
  * Terminal
3
- * Copyright (c) 2024 @GuriTso
3
+ * Copyright (c) 2024 @guritso
4
4
  * @license MIT
5
5
  */
6
6
 
@@ -8,6 +8,7 @@
8
8
 
9
9
  import { color as co, getErrorNames, formatUrl } from "./utils.js";
10
10
  import { readFileSync } from "fs";
11
+ import { resolve } from "path";
11
12
 
12
13
  /**
13
14
  * @typedef {Object} Terminal
@@ -24,31 +25,20 @@ import { readFileSync } from "fs";
24
25
  * @property {function(number): void} setVerbose
25
26
  * @property {function(any): boolean} isError
26
27
  * @property {Object} projectInfo
28
+ * @property {function(string): void} loadProjectInfo
27
29
  */
28
-
29
- /** @type {Terminal} */
30
30
  const terminal = {
31
31
  verbose: 2,
32
- levels: {
33
- info: co("%H100 INFO %H"),
34
- fail: co("%H41 FAIL %H"),
35
- pass: co("%H42 PASS %H"),
36
- },
32
+ levels: {
33
+ info: co("%H47 INFO %H"),
34
+ fail: co("%H41 FAIL %H"),
35
+ pass: co("%H42 PASS %H"),
36
+ },
37
37
  };
38
38
 
39
39
  const { stdout } = process;
40
40
 
41
- terminal.projectInfo = (() => {
42
- try {
43
- const { name, version } = JSON.parse(
44
- readFileSync("./package.json", "utf-8")
45
- );
46
-
47
- return { name, version };
48
- } catch (error) {
49
- return { name: "unknown", version: "unknown" };
50
- }
51
- })();
41
+ terminal.projectInfo = { name: "unknown", version: "unknown" };
52
42
 
53
43
  /**
54
44
  * display the project info and host, port
@@ -57,15 +47,18 @@ terminal.projectInfo = (() => {
57
47
  * @param {number} port - The port number to display
58
48
  */
59
49
  terminal.start = function start(host, port) {
50
+ if (terminal.projectInfo.name === "unknown") {
51
+ terminal.loadProjectInfo();
52
+ }
60
53
  const projectInfo = terminal.projectInfo;
61
54
  const hostInfo = formatUrl(host, port);
62
55
 
63
- const headLines = [
64
- `\n%H46 name:%H%H44 ${projectInfo.name} `,
65
- `%H105 version:%H%H41 ${projectInfo.version} %H\n`,
66
- hostInfo?.url ? `%H43 host:%H95 ${hostInfo.url}\n` : "",
67
- hostInfo?.port ? `%H45 port:%H94 ${hostInfo.port}\n` : "",
68
- ];
56
+ const headLines = [
57
+ `\n%H46 name:%H%H33 ${projectInfo.name}`,
58
+ `%H%H31 ${projectInfo.version} %H\n`,
59
+ hostInfo?.url ? `%H43 host:%H %H36 ${hostInfo.url}%H\n` : "",
60
+ hostInfo?.port ? `%H45 port:%H %H31 ${hostInfo.port}%H\n` : "",
61
+ ];
69
62
 
70
63
  for (const line of headLines) {
71
64
  stdout.write(co(line));
@@ -78,6 +71,8 @@ terminal.start = function start(host, port) {
78
71
  * @param {string} data
79
72
  */
80
73
  terminal.pass = function pass(data) {
74
+ if (data === null || data === undefined) return;
75
+
81
76
  const { verbose, levels } = terminal;
82
77
 
83
78
  if (verbose === 0) return;
@@ -125,6 +120,8 @@ terminal.log = function log(data) {
125
120
  }
126
121
  }
127
122
  };
123
+ // cache
124
+ let errorKeywords = []
128
125
 
129
126
  /**
130
127
  * Check if the data is an error
@@ -138,8 +135,10 @@ terminal.isError = (data) => {
138
135
  }
139
136
 
140
137
  if (typeof data === "string") {
141
- const errorKeywords = getErrorNames().map((name) => name);
142
- errorKeywords.push("Error:");
138
+ if (!errorKeywords.length) {
139
+ errorKeywords = getErrorNames().map((name) => name);
140
+ errorKeywords.push("Error:");
141
+ }
143
142
 
144
143
  return errorKeywords.some((keyword) => data.includes(keyword));
145
144
  }
@@ -201,4 +200,35 @@ terminal.setVerbose = (verbose) => {
201
200
  }
202
201
  };
203
202
 
203
+ /**
204
+ * Restore terminal.log function
205
+ * @return {boolean} if true = sucess
206
+ */
207
+ terminal.teardown = () => {
208
+ if (console.backup) {
209
+ console.error = console.backup;
210
+ delete console.backup;
211
+ return true;
212
+ }
213
+ return false;
214
+ };
215
+
216
+ /**
217
+ * Load project info from package.json
218
+ * @param {string} [pkgPath] - Optional path to package.json
219
+ */
220
+ terminal.loadProjectInfo = (pkgPath) => {
221
+ try {
222
+ const path = pkgPath || resolve(process.cwd(), "package.json");
223
+ const pkgData = JSON.parse(readFileSync(path, "utf-8"));
224
+
225
+ terminal.projectInfo = {
226
+ name: pkgData.name || "unknown",
227
+ version: pkgData.version || "unknown"
228
+ };
229
+ } catch (error) {
230
+ terminal.projectInfo = { name: "unknown", version: "unknown" };
231
+ }
232
+ };
233
+
204
234
  export default terminal;
package/lib/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
  * Terminal
3
- * Copyright (c) 2024 @GuriTso
3
+ * Copyright (c) 2024 @guritso
4
4
  * @license MIT
5
5
  */
6
6
 
@@ -25,7 +25,14 @@ function color(txt) {
25
25
 
26
26
  if (!n || !txt.includes(`%H${n}`) || isNaN(n)) continue;
27
27
 
28
- const mo = `\x1b[${n}m${w.join(" ")}\x1b[0m`;
28
+ let code = n;
29
+ if (n >= 40 && n <= 47) {
30
+ code = `90;${n}`;
31
+ } else if (n >= 100 && n <= 107) {
32
+ code = `97;${n}`;
33
+ }
34
+
35
+ const mo = `\x1b[${code}m${w.join(" ")}\x1b[0m`;
29
36
 
30
37
  res = res.replace(a, mo);
31
38
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@guritso/terminal",
3
3
  "description": "A terminal node utility for enhanced logging and error handling",
4
- "version": "1.2.1",
4
+ "version": "1.2.2",
5
5
  "main": "index.cjs",
6
6
  "module": "index.js",
7
7
  "types": "index.d.ts",
@@ -29,10 +29,10 @@
29
29
  "utility",
30
30
  "backend"
31
31
  ],
32
- "author": "GuriTso",
32
+ "author": "guritso",
33
33
  "devDependencies": {
34
- "jest": "^29.7.0",
35
- "rollup": "^4.38.0"
34
+ "jest": "^30.2.0",
35
+ "rollup": "^4.55.3"
36
36
  },
37
37
  "directories": {
38
38
  "lib": "lib",
package/rollup.config.js CHANGED
@@ -1,8 +1,8 @@
1
1
  export default {
2
2
  input: 'lib/terminal.js',
3
3
  output: {
4
- file: 'lib/cjs/terminal.cjs',
4
+ file: 'lib/dist/terminal.cjs',
5
5
  format: 'cjs',
6
6
  },
7
- external: ['fs', 'url']
7
+ external: ['fs', 'url', 'path']
8
8
  };
@@ -1,291 +0,0 @@
1
- 'use strict';
2
-
3
- var url = require('url');
4
- var fs = require('fs');
5
-
6
- /*!
7
- * Terminal
8
- * Copyright (c) 2024 @GuriTso
9
- * @license MIT
10
- */
11
-
12
-
13
- /**
14
- * This function is used to colorize the text
15
- * @param {string} txt - The text to colorize
16
- * @returns {string | any} - The colorized text
17
- * @example
18
- * color("Hello %H1 World") // "Hello \x1b[1mWorld\x1b[0m"
19
- * color("%h1 start here%H nothing here") // "\x1b[1m start here\x1b[0m nothing here"
20
- */
21
- function color(txt) {
22
- if (typeof txt !== "string") return txt;
23
-
24
- const arr = txt.split("%H");
25
- let res = arr.join("");
26
-
27
- for (const a of arr) {
28
- const [n, ...w] = a.split(" ");
29
-
30
- if (!n || !txt.includes(`%H${n}`) || isNaN(n)) continue;
31
-
32
- const mo = `\x1b[${n}m${w.join(" ")}\x1b[0m`;
33
-
34
- res = res.replace(a, mo);
35
- }
36
- return res;
37
- }
38
- /**
39
- * Get a list of common error names
40
- *
41
- * @returns {string[]}
42
- */
43
- function getErrorNames() {
44
- return Object.getOwnPropertyNames(global).filter((name) => {
45
- try {
46
- return (
47
- typeof global[name] === "function" &&
48
- global[name].prototype instanceof Error
49
- );
50
- } catch (e) {
51
- return false;
52
- }
53
- });
54
- }
55
- /**
56
- * Format the URL based on the provided host and port.
57
- *
58
- * @param {string} host - The host to format.
59
- * @param {number} port - The port to format.
60
- * @returns {string} - The formatted URL.
61
- */
62
- function formatUrl(host, port) {
63
- const locals = ["localhost", "127.0.0.1", "0.0.0.0"];
64
-
65
- try {
66
- if (typeof host !== "string") {
67
- return new Error("host must be a string");
68
- }
69
-
70
- const url$1 = new url.URL(host);
71
-
72
- if (port && port !== url$1.port) {
73
- url$1.port = port;
74
- }
75
-
76
- if (!url$1.protocol) {
77
- url$1.protocol = "http:";
78
- } else if (url$1.protocol !== "https:" && url$1.protocol !== "http:") {
79
- url$1.protocol = "http:";
80
- }
81
-
82
- return { url: url$1.toString(), port: url$1.port };
83
- } catch (error) {
84
- if (locals.includes(host)) {
85
- return formatUrl("http://localhost", port);
86
- } else {
87
- return error;
88
- }
89
- }
90
- }
91
-
92
- /*!
93
- * Terminal
94
- * Copyright (c) 2024 @GuriTso
95
- * @license MIT
96
- */
97
-
98
-
99
- /**
100
- * @typedef {Object} Terminal
101
- * @property {number} verbose
102
- * @property {Object} levels
103
- * @property {string} levels.info
104
- * @property {string} levels.fail
105
- * @property {string} levels.pass
106
- * @property {function(string, number): void} start
107
- * @property {function(string): void} pass
108
- * @property {function(string): void} log
109
- * @property {function(): void} setup
110
- * @property {function(): void} clear
111
- * @property {function(number): void} setVerbose
112
- * @property {function(any): boolean} isError
113
- * @property {Object} projectInfo
114
- */
115
-
116
- /** @type {Terminal} */
117
- const terminal = {
118
- verbose: 2,
119
- levels: {
120
- info: color("%H100 INFO %H"),
121
- fail: color("%H41 FAIL %H"),
122
- pass: color("%H42 PASS %H"),
123
- },
124
- };
125
-
126
- const { stdout } = process;
127
-
128
- terminal.projectInfo = (() => {
129
- try {
130
- const { name, version } = JSON.parse(
131
- fs.readFileSync("./package.json", "utf-8")
132
- );
133
-
134
- return { name, version };
135
- } catch (error) {
136
- return { name: "unknown", version: "unknown" };
137
- }
138
- })();
139
-
140
- /**
141
- * display the project info and host, port
142
- *
143
- * @param {string} host - The host to display
144
- * @param {number} port - The port number to display
145
- */
146
- terminal.start = function start(host, port) {
147
- const projectInfo = terminal.projectInfo;
148
- const hostInfo = formatUrl(host, port);
149
-
150
- const headLines = [
151
- `\n%H46 name:%H%H44 ${projectInfo.name} `,
152
- `%H105 version:%H%H41 ${projectInfo.version} %H\n`,
153
- hostInfo?.url ? `%H43 host:%H95 ${hostInfo.url}\n` : "",
154
- hostInfo?.port ? `%H45 port:%H94 ${hostInfo.port}\n` : "",
155
- ];
156
-
157
- for (const line of headLines) {
158
- stdout.write(color(line));
159
- }
160
- };
161
-
162
- /**
163
- * display successful messages
164
- *
165
- * @param {string} data
166
- */
167
- terminal.pass = function pass(data) {
168
- const { verbose, levels } = terminal;
169
-
170
- if (verbose === 0) return;
171
-
172
- if (typeof data === "object") {
173
- // skipcq: JS-0002
174
- console.log(color(`\r%H1 ${levels.pass}%H`), data);
175
- } else {
176
- terminal.clear();
177
- stdout.write(color(`\r%H1 ${levels.pass}%H ${String(data)}\n`));
178
- }
179
- };
180
-
181
- /**
182
- * display logs messages, error, info, etc.
183
- *
184
- * @param {any} data
185
- */
186
- terminal.log = function log(data) {
187
- const { verbose, levels } = terminal;
188
-
189
- let level = levels.info;
190
-
191
- if (verbose === 0) return;
192
-
193
- if (terminal.isError(data)) {
194
- level = levels.fail;
195
- }
196
-
197
- if (verbose === 1 && stdout.isTTY) {
198
- terminal.clear();
199
-
200
- stdout.write(color(`\r%H1 ${level}%H ${String(data)}`));
201
- } else {
202
- if (level === terminal.levels.info) {
203
- if (typeof data === "object") {
204
- // skipcq: JS-0002
205
- console.log(color(`%H1 ${level}%H`), data);
206
- } else {
207
- stdout.write(color(`%H1 ${level}%H ${String(data)}\n`));
208
- }
209
- } else {
210
- // skipcq: JS-0002
211
- console.log(color(`%H1 ${level}%H`), data);
212
- }
213
- }
214
- };
215
-
216
- /**
217
- * Check if the data is an error
218
- *
219
- * @param {any} data
220
- * @returns {boolean}
221
- */
222
- terminal.isError = (data) => {
223
- if (data instanceof Error) {
224
- return true;
225
- }
226
-
227
- if (typeof data === "string") {
228
- const errorKeywords = getErrorNames().map((name) => name);
229
- errorKeywords.push("Error:");
230
-
231
- return errorKeywords.some((keyword) => data.includes(keyword));
232
- }
233
-
234
- return false;
235
- };
236
-
237
- /**
238
- * Setup the console.error to use the terminal.log function
239
- *
240
- * @returns {boolean} - true if the setup was successful, false otherwise
241
- */
242
- terminal.setup = () => {
243
- if (typeof console === "object" && console.error) {
244
- if (typeof console.backup === "function") {
245
- return false;
246
- }
247
-
248
- // backup the original console.error
249
- console.backup = console.error;
250
- } else {
251
- throw new Error("console.error is not found");
252
- }
253
-
254
- // replace the console.error with the terminal.log
255
- console.error = terminal.log;
256
-
257
- return true;
258
- };
259
-
260
- /**
261
- * Clear the terminal
262
- */
263
- terminal.clear = () => {
264
- if (stdout.isTTY) {
265
- stdout.clearLine();
266
- return true;
267
- }
268
-
269
- return false;
270
- };
271
-
272
- /**
273
- * Set the verbose level
274
- *
275
- * @param {number} verbose - The verbose level
276
- */
277
- terminal.setVerbose = (verbose) => {
278
- if (!isNaN(Number(verbose))) {
279
- const verboseLevel = Number(verbose);
280
-
281
- if (verboseLevel < 0) {
282
- terminal.verbose = 0;
283
- } else if (verboseLevel > 2) {
284
- terminal.verbose = 2;
285
- } else {
286
- terminal.verbose = verboseLevel;
287
- }
288
- }
289
- };
290
-
291
- module.exports = terminal;