@variablelab/loggy 0.0.0-stage → 0.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.
- package/README.md +8 -2
- package/dist/index.d.mts +54 -0
- package/dist/index.mjs +111 -0
- package/package.json +44 -3
- package/src/const.ts +23 -0
- package/src/index.ts +2 -0
- package/src/loggy.ts +139 -0
- package/src/types.ts +43 -0
package/README.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Loggy
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Console wrapper to make logging fun again
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pnpm add @variablelab/loggy
|
|
9
|
+
```
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { FormatOptions } from "consola";
|
|
2
|
+
import createDebug from "debug";
|
|
3
|
+
|
|
4
|
+
//#region src/types.d.ts
|
|
5
|
+
type AnyLogFn = (...args: unknown[]) => void;
|
|
6
|
+
type LogFn = (opts: LogFnOptions | string, ...args: unknown[]) => void;
|
|
7
|
+
type Formatters = Record<string, (arg: unknown) => string>;
|
|
8
|
+
type AnyLogger = {
|
|
9
|
+
namespace: string;
|
|
10
|
+
debug: AnyLogFn;
|
|
11
|
+
error: AnyLogFn;
|
|
12
|
+
info: LogFn;
|
|
13
|
+
trace: LogFn;
|
|
14
|
+
warn: LogFn;
|
|
15
|
+
child: (options: CreateOptions) => AnyLogger;
|
|
16
|
+
subdebug: (namespace: string) => AnyLogFn;
|
|
17
|
+
start: LogFn;
|
|
18
|
+
success: LogFn;
|
|
19
|
+
};
|
|
20
|
+
type CreateOptions = {
|
|
21
|
+
namespace: string;
|
|
22
|
+
tag?: string;
|
|
23
|
+
formatOptions?: FormatOptions;
|
|
24
|
+
formatters?: Formatters;
|
|
25
|
+
};
|
|
26
|
+
type LoggerOptions = {
|
|
27
|
+
tag?: string;
|
|
28
|
+
namespace: string;
|
|
29
|
+
formatOptions: FormatOptions;
|
|
30
|
+
formatters: Formatters;
|
|
31
|
+
};
|
|
32
|
+
type LogFnOptions = {
|
|
33
|
+
tag: string;
|
|
34
|
+
message: string;
|
|
35
|
+
};
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/loggy.d.ts
|
|
38
|
+
declare class Loggy implements AnyLogger {
|
|
39
|
+
#private;
|
|
40
|
+
constructor(options: LoggerOptions);
|
|
41
|
+
get namespace(): string;
|
|
42
|
+
debug(...args: unknown[]): void;
|
|
43
|
+
error(...args: unknown[]): void;
|
|
44
|
+
info(opts: LogFnOptions | string, ...args: unknown[]): void;
|
|
45
|
+
trace(opts: LogFnOptions | string, ...args: unknown[]): void;
|
|
46
|
+
warn(opts: LogFnOptions | string, ...args: unknown[]): void;
|
|
47
|
+
child(options: CreateOptions): Loggy;
|
|
48
|
+
start(opts: LogFnOptions | string, ...args: unknown[]): void;
|
|
49
|
+
success(opts: LogFnOptions | string, ...args: unknown[]): void;
|
|
50
|
+
subdebug(namespace: string): createDebug.Debugger;
|
|
51
|
+
}
|
|
52
|
+
declare function createLoggy(options: CreateOptions): Loggy;
|
|
53
|
+
//#endregion
|
|
54
|
+
export { AnyLogFn, AnyLogger, CreateOptions, type FormatOptions, Formatters, LogFn, LogFnOptions, LoggerOptions, Loggy, createLoggy };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import util from "node:util";
|
|
2
|
+
import { createConsola } from "consola";
|
|
3
|
+
import { colors } from "consola/utils";
|
|
4
|
+
import createDebug from "debug";
|
|
5
|
+
//#region src/const.ts
|
|
6
|
+
const DEFAULT_FORMAT_OPTIONS = {
|
|
7
|
+
colors: true,
|
|
8
|
+
depth: 3,
|
|
9
|
+
maxArrayLength: 50,
|
|
10
|
+
breakLength: 80,
|
|
11
|
+
date: false
|
|
12
|
+
};
|
|
13
|
+
const DEFAULT_FORMATTERS = {
|
|
14
|
+
o: (arg) => {
|
|
15
|
+
return util.inspect(arg, DEFAULT_FORMAT_OPTIONS).split("\n").map((str) => str.trim()).join(" ");
|
|
16
|
+
},
|
|
17
|
+
O: (arg) => {
|
|
18
|
+
return util.inspect(arg, DEFAULT_FORMAT_OPTIONS);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/loggy.ts
|
|
23
|
+
function isLogFnOptions(arg) {
|
|
24
|
+
try {
|
|
25
|
+
return typeof arg.message === "string" && typeof arg.tag === "string";
|
|
26
|
+
} catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
var Loggy = class Loggy {
|
|
31
|
+
#options;
|
|
32
|
+
#debug;
|
|
33
|
+
#consola;
|
|
34
|
+
constructor(options) {
|
|
35
|
+
this.#options = options;
|
|
36
|
+
this.#debug = createDebug(`${options.namespace}:root`);
|
|
37
|
+
this.#consola = createConsola({ formatOptions: options.formatOptions });
|
|
38
|
+
}
|
|
39
|
+
get namespace() {
|
|
40
|
+
return this.#options.namespace;
|
|
41
|
+
}
|
|
42
|
+
debug(...args) {
|
|
43
|
+
this.#debug(...args);
|
|
44
|
+
}
|
|
45
|
+
error(...args) {
|
|
46
|
+
this.#consola.error(this.#format(...args));
|
|
47
|
+
}
|
|
48
|
+
info(opts, ...args) {
|
|
49
|
+
this.#consola.info(this.#format(opts, ...args));
|
|
50
|
+
}
|
|
51
|
+
trace(opts, ...args) {
|
|
52
|
+
this.#consola.trace(this.#format(opts, ...args));
|
|
53
|
+
}
|
|
54
|
+
warn(opts, ...args) {
|
|
55
|
+
this.#consola.warn(this.#format(opts, ...args));
|
|
56
|
+
}
|
|
57
|
+
child(options) {
|
|
58
|
+
return new Loggy({
|
|
59
|
+
...this.#options,
|
|
60
|
+
...options,
|
|
61
|
+
namespace: `${this.#options.namespace}:${options.namespace}`
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
start(opts, ...args) {
|
|
65
|
+
this.#consola.start(this.#format(opts, ...args));
|
|
66
|
+
}
|
|
67
|
+
success(opts, ...args) {
|
|
68
|
+
this.#consola.success(this.#format(opts, ...args));
|
|
69
|
+
}
|
|
70
|
+
subdebug(namespace) {
|
|
71
|
+
return createDebug(`${this.#options.namespace}:${namespace}`);
|
|
72
|
+
}
|
|
73
|
+
#format(...args) {
|
|
74
|
+
const [firstArg, ...restArgs] = args;
|
|
75
|
+
const tag = isLogFnOptions(firstArg) ? firstArg.tag : this.#options.tag;
|
|
76
|
+
const formattedArgs = isLogFnOptions(firstArg) ? [firstArg.message, ...restArgs] : args;
|
|
77
|
+
if (typeof formattedArgs[0] !== "string") formattedArgs.unshift("%O");
|
|
78
|
+
const [message, ...replacements] = formattedArgs;
|
|
79
|
+
let replacementIndex = -1;
|
|
80
|
+
if (typeof message !== "string") throw new TypeError("message must be a string");
|
|
81
|
+
let formattedMessage = message.replace(/%([a-zA-Z%])/g, (match, formatKey) => {
|
|
82
|
+
if (formatKey === "%") return "%";
|
|
83
|
+
replacementIndex++;
|
|
84
|
+
const formatter = this.#options.formatters[formatKey];
|
|
85
|
+
if (typeof formatter !== "function") return match;
|
|
86
|
+
const replacement = replacements[replacementIndex];
|
|
87
|
+
const formattedMatch = formatter(replacement);
|
|
88
|
+
replacements.splice(replacementIndex, 1);
|
|
89
|
+
replacementIndex--;
|
|
90
|
+
return formattedMatch;
|
|
91
|
+
}).split("\n").join(`\n${" ".repeat(2)}`);
|
|
92
|
+
formattedMessage = !tag ? formattedMessage : `${colors.cyan(`[${tag}]`)} ${formattedMessage}`;
|
|
93
|
+
return util.formatWithOptions(this.#options.formatOptions, formattedMessage, ...replacements);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
function createLoggy(options) {
|
|
97
|
+
return new Loggy({
|
|
98
|
+
namespace: options.namespace,
|
|
99
|
+
tag: options.tag,
|
|
100
|
+
formatOptions: {
|
|
101
|
+
...DEFAULT_FORMAT_OPTIONS,
|
|
102
|
+
...options.formatOptions
|
|
103
|
+
},
|
|
104
|
+
formatters: {
|
|
105
|
+
...DEFAULT_FORMATTERS,
|
|
106
|
+
...options.formatters
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
export { Loggy, createLoggy };
|
package/package.json
CHANGED
|
@@ -1,6 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@variablelab/loggy",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.2.2",
|
|
4
|
+
"description": "Console wrapper to make logging fun again",
|
|
5
|
+
"homepage": "https://github.com/withvariablelab/dx/tree/main/shared/loggy#readme",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/withvariablelab/dx/issues"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/withvariablelab/dx.git",
|
|
12
|
+
"directory": "shared/loggy"
|
|
13
|
+
},
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "rcrd <rcrd@variable.land>",
|
|
16
|
+
"type": "module",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.mts",
|
|
20
|
+
"default": "./dist/index.mjs"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"src",
|
|
26
|
+
"!src/**/__tests__",
|
|
27
|
+
"!src/**/*.test.*"
|
|
28
|
+
],
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=20.0.0"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"consola": "3.4.2",
|
|
37
|
+
"debug": "4.4.3"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/debug": "4.1.12",
|
|
41
|
+
"@rrlab/tsdown-config": "^0.1.1"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsdown",
|
|
45
|
+
"tscheck": "rr tsc"
|
|
46
|
+
}
|
|
6
47
|
}
|
package/src/const.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import util from "node:util";
|
|
2
|
+
import type { FormatOptions, Formatters } from "./types.ts";
|
|
3
|
+
|
|
4
|
+
export const DEFAULT_FORMAT_OPTIONS: FormatOptions = {
|
|
5
|
+
colors: true,
|
|
6
|
+
depth: 3,
|
|
7
|
+
maxArrayLength: 50,
|
|
8
|
+
breakLength: 80,
|
|
9
|
+
date: false,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const DEFAULT_FORMATTERS: Formatters = {
|
|
13
|
+
o: (arg: unknown) => {
|
|
14
|
+
return util
|
|
15
|
+
.inspect(arg, DEFAULT_FORMAT_OPTIONS)
|
|
16
|
+
.split("\n")
|
|
17
|
+
.map((str) => str.trim())
|
|
18
|
+
.join(" ");
|
|
19
|
+
},
|
|
20
|
+
O: (arg: unknown) => {
|
|
21
|
+
return util.inspect(arg, DEFAULT_FORMAT_OPTIONS);
|
|
22
|
+
},
|
|
23
|
+
};
|
package/src/index.ts
ADDED
package/src/loggy.ts
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import util from "node:util";
|
|
2
|
+
import { type ConsolaInstance, createConsola } from "consola";
|
|
3
|
+
import { colors } from "consola/utils";
|
|
4
|
+
import createDebug from "debug";
|
|
5
|
+
import { DEFAULT_FORMAT_OPTIONS, DEFAULT_FORMATTERS } from "./const.ts";
|
|
6
|
+
import type { AnyLogger, CreateOptions, LogFnOptions, LoggerOptions } from "./types.ts";
|
|
7
|
+
|
|
8
|
+
function isLogFnOptions(arg: unknown): arg is LogFnOptions {
|
|
9
|
+
try {
|
|
10
|
+
// @ts-expect-error
|
|
11
|
+
return typeof arg.message === "string" && typeof arg.tag === "string";
|
|
12
|
+
} catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class Loggy implements AnyLogger {
|
|
18
|
+
#options: LoggerOptions;
|
|
19
|
+
#debug: ReturnType<typeof createDebug>;
|
|
20
|
+
#consola: ConsolaInstance;
|
|
21
|
+
|
|
22
|
+
constructor(options: LoggerOptions) {
|
|
23
|
+
this.#options = options;
|
|
24
|
+
this.#debug = createDebug(`${options.namespace}:root`);
|
|
25
|
+
this.#consola = createConsola({
|
|
26
|
+
formatOptions: options.formatOptions,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get namespace() {
|
|
31
|
+
return this.#options.namespace;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
debug(...args: unknown[]) {
|
|
35
|
+
// @ts-expect-error - it really accepts this signature
|
|
36
|
+
this.#debug(...args);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
error(...args: unknown[]) {
|
|
40
|
+
this.#consola.error(this.#format(...args));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
info(opts: LogFnOptions | string, ...args: unknown[]) {
|
|
44
|
+
this.#consola.info(this.#format(opts, ...args));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
trace(opts: LogFnOptions | string, ...args: unknown[]) {
|
|
48
|
+
this.#consola.trace(this.#format(opts, ...args));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
warn(opts: LogFnOptions | string, ...args: unknown[]) {
|
|
52
|
+
this.#consola.warn(this.#format(opts, ...args));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
child(options: CreateOptions) {
|
|
56
|
+
return new Loggy({
|
|
57
|
+
...this.#options,
|
|
58
|
+
...options,
|
|
59
|
+
namespace: `${this.#options.namespace}:${options.namespace}`,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
start(opts: LogFnOptions | string, ...args: unknown[]) {
|
|
64
|
+
this.#consola.start(this.#format(opts, ...args));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
success(opts: LogFnOptions | string, ...args: unknown[]) {
|
|
68
|
+
this.#consola.success(this.#format(opts, ...args));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
subdebug(namespace: string) {
|
|
72
|
+
return createDebug(`${this.#options.namespace}:${namespace}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#format(...args: unknown[]) {
|
|
76
|
+
const [firstArg, ...restArgs] = args;
|
|
77
|
+
|
|
78
|
+
const tag = isLogFnOptions(firstArg) ? firstArg.tag : this.#options.tag;
|
|
79
|
+
const formattedArgs = isLogFnOptions(firstArg) ? [firstArg.message, ...restArgs] : args;
|
|
80
|
+
|
|
81
|
+
if (typeof formattedArgs[0] !== "string") {
|
|
82
|
+
formattedArgs.unshift("%O");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const [message, ...replacements] = formattedArgs;
|
|
86
|
+
let replacementIndex = -1;
|
|
87
|
+
|
|
88
|
+
if (typeof message !== "string") {
|
|
89
|
+
throw new TypeError("message must be a string");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
let formattedMessage = message
|
|
93
|
+
.replace(
|
|
94
|
+
/%([a-zA-Z%])/g, // matches %o, %O, %%, etc.
|
|
95
|
+
(match: string, formatKey: string) => {
|
|
96
|
+
if (formatKey === "%") {
|
|
97
|
+
return "%";
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
replacementIndex++;
|
|
101
|
+
|
|
102
|
+
const formatter = this.#options.formatters[formatKey];
|
|
103
|
+
|
|
104
|
+
if (typeof formatter !== "function") {
|
|
105
|
+
return match;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const replacement = replacements[replacementIndex];
|
|
109
|
+
const formattedMatch = formatter(replacement);
|
|
110
|
+
|
|
111
|
+
replacements.splice(replacementIndex, 1);
|
|
112
|
+
replacementIndex--;
|
|
113
|
+
|
|
114
|
+
return formattedMatch;
|
|
115
|
+
},
|
|
116
|
+
)
|
|
117
|
+
.split("\n")
|
|
118
|
+
.join(`\n${" ".repeat(2)}`);
|
|
119
|
+
|
|
120
|
+
formattedMessage = !tag ? formattedMessage : `${colors.cyan(`[${tag}]`)} ${formattedMessage}`;
|
|
121
|
+
|
|
122
|
+
return util.formatWithOptions(this.#options.formatOptions, formattedMessage, ...replacements);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function createLoggy(options: CreateOptions) {
|
|
127
|
+
return new Loggy({
|
|
128
|
+
namespace: options.namespace,
|
|
129
|
+
tag: options.tag,
|
|
130
|
+
formatOptions: {
|
|
131
|
+
...DEFAULT_FORMAT_OPTIONS,
|
|
132
|
+
...options.formatOptions,
|
|
133
|
+
},
|
|
134
|
+
formatters: {
|
|
135
|
+
...DEFAULT_FORMATTERS,
|
|
136
|
+
...options.formatters,
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { FormatOptions } from "consola";
|
|
2
|
+
|
|
3
|
+
export type AnyLogFn = (...args: unknown[]) => void;
|
|
4
|
+
|
|
5
|
+
export type LogFn = (opts: LogFnOptions | string, ...args: unknown[]) => void;
|
|
6
|
+
|
|
7
|
+
export type Formatters = Record<string, (arg: unknown) => string>;
|
|
8
|
+
|
|
9
|
+
export type AnyLogger = {
|
|
10
|
+
namespace: string;
|
|
11
|
+
debug: AnyLogFn;
|
|
12
|
+
error: AnyLogFn;
|
|
13
|
+
info: LogFn;
|
|
14
|
+
trace: LogFn;
|
|
15
|
+
warn: LogFn;
|
|
16
|
+
child: (options: CreateOptions) => AnyLogger;
|
|
17
|
+
// { extras
|
|
18
|
+
subdebug: (namespace: string) => AnyLogFn;
|
|
19
|
+
start: LogFn;
|
|
20
|
+
success: LogFn;
|
|
21
|
+
// }
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type CreateOptions = {
|
|
25
|
+
namespace: string;
|
|
26
|
+
tag?: string;
|
|
27
|
+
formatOptions?: FormatOptions;
|
|
28
|
+
formatters?: Formatters;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type LoggerOptions = {
|
|
32
|
+
tag?: string;
|
|
33
|
+
namespace: string;
|
|
34
|
+
formatOptions: FormatOptions;
|
|
35
|
+
formatters: Formatters;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type LogFnOptions = {
|
|
39
|
+
tag: string;
|
|
40
|
+
message: string;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type { FormatOptions };
|