@endgame45/nodescript 1.0.1 → 1.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/package.json +6 -4
- package/src/nodescript.js +236 -33
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@endgame45/nodescript",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"main": "src/nodescript.js",
|
|
5
5
|
"files": [
|
|
6
6
|
"src"
|
|
@@ -24,8 +24,10 @@
|
|
|
24
24
|
"eslint": "^7.32.0"
|
|
25
25
|
},
|
|
26
26
|
"url": {
|
|
27
|
-
"repository": "npmjs.com/package/@endgame45/nodescript",
|
|
27
|
+
"repository": "https://npmjs.com/package/@endgame45/nodescript",
|
|
28
28
|
"type": "git",
|
|
29
|
-
"url": "github.com/horizon-std/nodescript"
|
|
30
|
-
}
|
|
29
|
+
"url": "https://github.com/horizon-std/nodescript"
|
|
30
|
+
},
|
|
31
|
+
"bugs": "https://github.com/horizon-std/nodescript/issues",
|
|
32
|
+
"readme": ""
|
|
31
33
|
}
|
package/src/nodescript.js
CHANGED
|
@@ -5,25 +5,75 @@ const SUPPORTED_FONTS = new Set([
|
|
|
5
5
|
'bold', 'dim', 'italic', 'underline', 'inverse', 'hidden', 'strikethrough'
|
|
6
6
|
]);
|
|
7
7
|
|
|
8
|
+
function clamp(v, a = 0, b = 255) {
|
|
9
|
+
return Math.min(b, Math.max(a, Math.round(Number(v) || 0)));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function hslToRgb(h, s, l) {
|
|
13
|
+
// h: 0-360, s,l: 0-1
|
|
14
|
+
h = ((h % 360) + 360) % 360;
|
|
15
|
+
s = Math.max(0, Math.min(1, s));
|
|
16
|
+
l = Math.max(0, Math.min(1, l));
|
|
17
|
+
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
18
|
+
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
|
|
19
|
+
const m = l - c / 2;
|
|
20
|
+
let r1 = 0; let g1 = 0; let b1 = 0;
|
|
21
|
+
if (h < 60) { r1 = c; g1 = x; b1 = 0; }
|
|
22
|
+
else if (h < 120) { r1 = x; g1 = c; b1 = 0; }
|
|
23
|
+
else if (h < 180) { r1 = 0; g1 = c; b1 = x; }
|
|
24
|
+
else if (h < 240) { r1 = 0; g1 = x; b1 = c; }
|
|
25
|
+
else if (h < 300) { r1 = x; g1 = 0; b1 = c; }
|
|
26
|
+
else { r1 = c; g1 = 0; b1 = x; }
|
|
27
|
+
return [ clamp((r1 + m) * 255), clamp((g1 + m) * 255), clamp((b1 + m) * 255) ];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function blendOverBg(r, g, b, a, bgR = 0, bgG = 0, bgB = 0) {
|
|
31
|
+
// alpha in 0..1
|
|
32
|
+
const alpha = Math.max(0, Math.min(1, Number(a) || 0));
|
|
33
|
+
const rr = Math.round((1 - alpha) * bgR + alpha * r);
|
|
34
|
+
const gg = Math.round((1 - alpha) * bgG + alpha * g);
|
|
35
|
+
const bb = Math.round((1 - alpha) * bgB + alpha * b);
|
|
36
|
+
return [clamp(rr), clamp(gg), clamp(bb)];
|
|
37
|
+
}
|
|
38
|
+
|
|
8
39
|
function parseColor(styler, color, isBg = false) {
|
|
9
40
|
if (!color) return styler;
|
|
10
41
|
const c = String(color).trim();
|
|
42
|
+
// hex
|
|
43
|
+
if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(c)) {
|
|
44
|
+
return isBg ? styler.bgHex(c) : styler.hex(c);
|
|
45
|
+
}
|
|
46
|
+
// rgb() or rgba()
|
|
47
|
+
const rgbMatch = c.match(/^rgba?\s*\(([^)]+)\)$/i);
|
|
48
|
+
if (rgbMatch) {
|
|
49
|
+
const parts = rgbMatch[1].split(',').map(s => s.trim());
|
|
50
|
+
const r = parseInt(parts[0], 10) || 0;
|
|
51
|
+
const g = parseInt(parts[1], 10) || 0;
|
|
52
|
+
const b = parseInt(parts[2], 10) || 0;
|
|
53
|
+
const a = parts[3] !== undefined ? parseFloat(parts[3]) : 1;
|
|
54
|
+
if (a >= 1) return isBg ? styler.bgRgb(r, g, b) : styler.rgb(r, g, b);
|
|
55
|
+
// blend alpha over background if provided via isBg===false and styler has bgColor; we don't know bg, assume black
|
|
56
|
+
const [rr, gg, bb] = blendOverBg(r, g, b, a, 0, 0, 0);
|
|
57
|
+
return isBg ? styler.bgRgb(rr, gg, bb) : styler.rgb(rr, gg, bb);
|
|
58
|
+
}
|
|
59
|
+
// hsl() or hsla()
|
|
60
|
+
const hslMatch = c.match(/^hsla?\s*\(([^)]+)\)$/i);
|
|
61
|
+
if (hslMatch) {
|
|
62
|
+
const parts = hslMatch[1].split(',').map(s => s.trim());
|
|
63
|
+
const h = parseFloat(parts[0]) || 0;
|
|
64
|
+
const s = (String(parts[1]).replace('%', '') || 0) / 100;
|
|
65
|
+
const l = (String(parts[2]).replace('%', '') || 0) / 100;
|
|
66
|
+
const a = parts[3] !== undefined ? parseFloat(parts[3]) : 1;
|
|
67
|
+
const [r, g, b] = hslToRgb(h, s, l);
|
|
68
|
+
if (a >= 1) return isBg ? styler.bgRgb(r, g, b) : styler.rgb(r, g, b);
|
|
69
|
+
const [rr, gg, bb] = blendOverBg(r, g, b, a, 0, 0, 0);
|
|
70
|
+
return isBg ? styler.bgRgb(rr, gg, bb) : styler.rgb(rr, gg, bb);
|
|
71
|
+
}
|
|
72
|
+
// fallback to CSS keyword (named color)
|
|
11
73
|
try {
|
|
12
|
-
if (/^#/.test(c)) {
|
|
13
|
-
return isBg ? styler.bgHex(c) : styler.hex(c);
|
|
14
|
-
}
|
|
15
|
-
if (/^rgb\(/i.test(c)) {
|
|
16
|
-
const nums = c
|
|
17
|
-
.replace(/^[^(]*\(/, '')
|
|
18
|
-
.replace(/\).*/, '')
|
|
19
|
-
.split(',')
|
|
20
|
-
.map(n => parseInt(n, 10) || 0);
|
|
21
|
-
return isBg ? styler.bgRgb(nums[0], nums[1], nums[2]) : styler.rgb(nums[0], nums[1], nums[2]);
|
|
22
|
-
}
|
|
23
|
-
// fallback to CSS keyword (named color)
|
|
24
74
|
return isBg ? styler.bgKeyword(c) : styler.keyword(c);
|
|
25
75
|
} catch (e) {
|
|
26
|
-
return styler;
|
|
76
|
+
return styler;
|
|
27
77
|
}
|
|
28
78
|
}
|
|
29
79
|
|
|
@@ -225,8 +275,35 @@ const nsc = {
|
|
|
225
275
|
textColor: null,
|
|
226
276
|
bgColor: null,
|
|
227
277
|
font: null,
|
|
278
|
+
namespace: null,
|
|
279
|
+
// logging level: error, warn, info, debug
|
|
280
|
+
level: 'info',
|
|
281
|
+
levels: { error: 0, warn: 1, info: 2, debug: 3 },
|
|
282
|
+
// auto color detection (respects NO_COLOR and TTY)
|
|
283
|
+
autoColor: true,
|
|
284
|
+
// timestamping options: { enabled: false, format: 'iso'|'local'|function }
|
|
285
|
+
timestamp: { enabled: false, format: 'iso' },
|
|
286
|
+
|
|
287
|
+
_colorEnabled() {
|
|
288
|
+
if (!this.autoColor) return false;
|
|
289
|
+
if (process && process.env && process.env.NO_COLOR) return false;
|
|
290
|
+
return Boolean(process && process.stdout && process.stdout.isTTY);
|
|
291
|
+
},
|
|
228
292
|
|
|
229
293
|
_getStyler() {
|
|
294
|
+
if (!this._colorEnabled()) {
|
|
295
|
+
// minimal no-color styler that mimics chalk chaining API
|
|
296
|
+
const noColor = (s) => String(s);
|
|
297
|
+
const fonts = ['bold', 'dim', 'italic', 'underline', 'inverse', 'hidden', 'strikethrough'];
|
|
298
|
+
for (const f of fonts) noColor[f] = noColor;
|
|
299
|
+
noColor.hex = () => noColor;
|
|
300
|
+
noColor.bgHex = () => noColor;
|
|
301
|
+
noColor.rgb = () => noColor;
|
|
302
|
+
noColor.bgRgb = () => noColor;
|
|
303
|
+
noColor.keyword = () => noColor;
|
|
304
|
+
noColor.bgKeyword = () => noColor;
|
|
305
|
+
return noColor;
|
|
306
|
+
}
|
|
230
307
|
return buildStyler({
|
|
231
308
|
textColor: this.textColor,
|
|
232
309
|
bgColor: this.bgColor,
|
|
@@ -234,36 +311,81 @@ const nsc = {
|
|
|
234
311
|
});
|
|
235
312
|
},
|
|
236
313
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
314
|
+
_formatPrefix(level) {
|
|
315
|
+
const parts = [];
|
|
316
|
+
if (this.timestamp && this.timestamp.enabled) {
|
|
317
|
+
const fmt = this.timestamp.format;
|
|
318
|
+
let ts = '';
|
|
319
|
+
if (typeof fmt === 'function') ts = String(fmt());
|
|
320
|
+
else if (fmt === 'local') ts = new Date().toLocaleString();
|
|
321
|
+
else ts = new Date().toISOString();
|
|
322
|
+
parts.push(ts);
|
|
323
|
+
}
|
|
324
|
+
if (this.namespace) parts.push(this.namespace);
|
|
325
|
+
if (level) parts.push(level.toUpperCase());
|
|
326
|
+
return parts.length ? '[' + parts.join('] [') + '] ' : '';
|
|
327
|
+
},
|
|
328
|
+
|
|
329
|
+
_shouldLog(level) {
|
|
330
|
+
const cur = this.levels[this.level] ?? 2;
|
|
331
|
+
const lvl = this.levels[level] ?? 2;
|
|
332
|
+
return lvl <= cur;
|
|
333
|
+
},
|
|
334
|
+
|
|
335
|
+
_printStyled(text, level) {
|
|
336
|
+
// choose effective color without mutating instance
|
|
337
|
+
let defaultColor;
|
|
338
|
+
switch (level) {
|
|
339
|
+
case 'error': defaultColor = 'red'; break;
|
|
340
|
+
case 'warn': defaultColor = 'yellow'; break;
|
|
341
|
+
case 'info': defaultColor = 'cyan'; break;
|
|
342
|
+
case 'debug': defaultColor = 'magenta'; break;
|
|
343
|
+
default: defaultColor = null; break;
|
|
344
|
+
}
|
|
345
|
+
let styler;
|
|
346
|
+
if (!this._colorEnabled()) {
|
|
347
|
+
const noColor = (s) => String(s);
|
|
348
|
+
const fonts = ['bold', 'dim', 'italic', 'underline', 'inverse', 'hidden', 'strikethrough'];
|
|
349
|
+
for (const f of fonts) noColor[f] = noColor;
|
|
350
|
+
noColor.hex = () => noColor;
|
|
351
|
+
noColor.bgHex = () => noColor;
|
|
352
|
+
noColor.rgb = () => noColor;
|
|
353
|
+
noColor.bgRgb = () => noColor;
|
|
354
|
+
noColor.keyword = () => noColor;
|
|
355
|
+
noColor.bgKeyword = () => noColor;
|
|
356
|
+
styler = noColor;
|
|
357
|
+
} else {
|
|
358
|
+
styler = buildStyler({
|
|
359
|
+
textColor: this.textColor || defaultColor,
|
|
360
|
+
bgColor: this.bgColor,
|
|
361
|
+
font: this.font
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
const out = styler(text);
|
|
365
|
+
console.log(out);
|
|
366
|
+
},
|
|
367
|
+
|
|
368
|
+
// low-level logging method used by level helpers
|
|
369
|
+
_log(level, ...args) {
|
|
370
|
+
if (level && !this._shouldLog(level)) return;
|
|
371
|
+
// support callback-mode where a function temporarily overrides console.log
|
|
242
372
|
if (args.length === 1 && typeof args[0] === 'function') {
|
|
243
373
|
const fn = args[0];
|
|
244
374
|
const originalLog = console.log;
|
|
375
|
+
const prefix = this._formatPrefix(level);
|
|
245
376
|
const styler = this._getStyler();
|
|
246
377
|
|
|
247
378
|
console.log = (...innerArgs) => {
|
|
248
379
|
const out = util.format(...innerArgs);
|
|
249
|
-
originalLog(styler(out));
|
|
380
|
+
originalLog(prefix + styler(out));
|
|
250
381
|
};
|
|
251
382
|
|
|
252
383
|
try {
|
|
253
384
|
const result = fn();
|
|
254
|
-
// support async callbacks (promises)
|
|
255
385
|
if (result && typeof result.then === 'function') {
|
|
256
|
-
return result
|
|
257
|
-
.
|
|
258
|
-
console.log = originalLog;
|
|
259
|
-
return res;
|
|
260
|
-
})
|
|
261
|
-
.catch(err => {
|
|
262
|
-
console.log = originalLog;
|
|
263
|
-
throw err;
|
|
264
|
-
});
|
|
386
|
+
return result.then((res) => { console.log = originalLog; return res; })
|
|
387
|
+
.catch((err) => { console.log = originalLog; throw err; });
|
|
265
388
|
}
|
|
266
|
-
// sync
|
|
267
389
|
console.log = originalLog;
|
|
268
390
|
return result;
|
|
269
391
|
} catch (err) {
|
|
@@ -272,10 +394,91 @@ const nsc = {
|
|
|
272
394
|
}
|
|
273
395
|
}
|
|
274
396
|
|
|
275
|
-
|
|
397
|
+
const prefix = this._formatPrefix(level);
|
|
398
|
+
const message = util.format(...args);
|
|
399
|
+
// combine prefix and message
|
|
400
|
+
const full = prefix + message;
|
|
401
|
+
this._printStyled(full, level);
|
|
402
|
+
},
|
|
403
|
+
|
|
404
|
+
// public API: default log (info-level)
|
|
405
|
+
log(...args) { return this._log('info', ...args); },
|
|
406
|
+
|
|
407
|
+
info(...args) { return this._log('info', ...args); },
|
|
408
|
+
warn(...args) { return this._log('warn', ...args); },
|
|
409
|
+
error(...args) { return this._log('error', ...args); },
|
|
410
|
+
debug(...args) { return this._log('debug', ...args); },
|
|
411
|
+
|
|
412
|
+
// create a namespaced logger instance with optional defaults
|
|
413
|
+
create(namespace, opts = {}) {
|
|
414
|
+
const inst = Object.create(this);
|
|
415
|
+
inst.namespace = namespace || null;
|
|
416
|
+
if (opts.textColor) inst.textColor = opts.textColor;
|
|
417
|
+
if (opts.bgColor) inst.bgColor = opts.bgColor;
|
|
418
|
+
if (opts.font) inst.font = opts.font;
|
|
419
|
+
if (opts.level) inst.level = opts.level;
|
|
420
|
+
if (typeof opts.autoColor === 'boolean') inst.autoColor = opts.autoColor;
|
|
421
|
+
if (opts.timestamp) inst.timestamp = Object.assign({}, inst.timestamp, opts.timestamp);
|
|
422
|
+
// bind methods to instance for convenience
|
|
423
|
+
inst.log = inst.log.bind(inst);
|
|
424
|
+
inst.info = inst.info.bind(inst);
|
|
425
|
+
inst.warn = inst.warn.bind(inst);
|
|
426
|
+
inst.error = inst.error.bind(inst);
|
|
427
|
+
inst.debug = inst.debug.bind(inst);
|
|
428
|
+
inst.create = inst.create.bind(inst);
|
|
429
|
+
return inst;
|
|
430
|
+
},
|
|
431
|
+
// theme registry
|
|
432
|
+
_themes: new Map(),
|
|
433
|
+
registerTheme(name, opts) {
|
|
434
|
+
if (!name || typeof name !== 'string') throw new TypeError('theme name must be a string');
|
|
435
|
+
if (!opts || typeof opts !== 'object') throw new TypeError('theme opts must be an object');
|
|
436
|
+
// store a shallow copy
|
|
437
|
+
this._themes.set(name, Object.assign({}, opts));
|
|
438
|
+
return this;
|
|
439
|
+
},
|
|
440
|
+
getTheme(name) {
|
|
441
|
+
return this._themes.get(name) || null;
|
|
442
|
+
},
|
|
443
|
+
// apply theme to this instance (mutates)
|
|
444
|
+
applyTheme(name) {
|
|
445
|
+
const t = this.getTheme(name);
|
|
446
|
+
if (!t) throw new Error('unknown theme: ' + name);
|
|
447
|
+
if (t.textColor) this.textColor = t.textColor;
|
|
448
|
+
if (t.bgColor) this.bgColor = t.bgColor;
|
|
449
|
+
if (t.font) this.font = t.font;
|
|
450
|
+
if (t.level) this.level = t.level;
|
|
451
|
+
if (t.timestamp) this.timestamp = Object.assign({}, this.timestamp, t.timestamp);
|
|
452
|
+
return this;
|
|
453
|
+
},
|
|
454
|
+
// return a new instance pre-configured with theme
|
|
455
|
+
useTheme(name) {
|
|
456
|
+
const t = this.getTheme(name);
|
|
457
|
+
if (!t) throw new Error('unknown theme: ' + name);
|
|
458
|
+
return this.create(null, t);
|
|
459
|
+
},
|
|
460
|
+
|
|
461
|
+
// tagged template helper: returns styled string (does not log)
|
|
462
|
+
style(strings, ...values) {
|
|
463
|
+
// combine template parts, evaluating functions
|
|
464
|
+
const parts = [];
|
|
465
|
+
for (let i = 0; i < strings.length; i++) {
|
|
466
|
+
parts.push(strings[i]);
|
|
467
|
+
if (i < values.length) {
|
|
468
|
+
const v = values[i];
|
|
469
|
+
if (typeof v === 'function') parts.push(String(v()));
|
|
470
|
+
else parts.push(String(v));
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
const full = parts.join('');
|
|
276
474
|
const styler = this._getStyler();
|
|
277
|
-
|
|
278
|
-
|
|
475
|
+
return styler(full);
|
|
476
|
+
},
|
|
477
|
+
|
|
478
|
+
// convenience: styled log via template tag
|
|
479
|
+
tagLog(strings, ...values) {
|
|
480
|
+
const s = this.style(strings, ...values);
|
|
481
|
+
console.log(s);
|
|
279
482
|
},
|
|
280
483
|
|
|
281
484
|
/**
|