walking-log 0.0.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 (3) hide show
  1. package/README.md +41 -0
  2. package/index.js +272 -0
  3. package/package.json +41 -0
package/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # 🧟 walking-log
2
+
3
+ <p>
4
+ <div align="center" style="margin-bottom: 1rem;">
5
+ <a href="https://www.npmjs.com/package/walking-log" style="display: flex; align-items: endx; justify-content: center;">
6
+ <img src="https://koboyo.com/icons/svg/zombie.svg" width="200" alt="cartoon zombie from https://koboyo.com/icons?q=zombie" />
7
+
8
+ <img src="https://koboyo.com/icons/svg/cartoon-terminal-error-line.svg" alt="cartoon terminal error line from https://koboyo.com/icons?q=terminal" width="100" />
9
+ </a>
10
+ </div>
11
+ </p>
12
+
13
+ > **walking-log** is a colorful yet minimal console log for **Node.js only**.
14
+
15
+ > <img src="https://github.com/legend80s/my-npm-dashboard/raw/refs/heads/main/src/backend/utils/logger/assets/languages.svg" width="16" height="16" alt="Chinese:" /> 就是一个 Node.js 控制台彩色 console.log 而已,不涉及浏览器,仅为 Node.js CLI 提供纯粹、简单的信息输出。
16
+
17
+ ## Features
18
+
19
+ Zero dependencies, just one file with 120 LOC. Copy or install however you like.
20
+
21
+ <img src="https://github.com/legend80s/my-npm-dashboard/raw/refs/heads/main/src/backend/utils/logger/assets/languages.svg" width="16" height="16" alt="Chinese:" /> 零依赖,仅 120 行代码的单文件。复制或安装随意。
22
+
23
+ ## Quick Start
24
+
25
+ ```bash
26
+ npm install walking-log
27
+ ```
28
+
29
+ ```javascript
30
+ import { createLogger } from 'walking-log'
31
+
32
+ const logger = createLogger({ verbose: true })
33
+
34
+ logger.debug("I'm not the good guy anymore.")
35
+ logger.info("We are the walking dead.")
36
+ logger.warn("If you don't fight, you die.")
37
+ logger.success("We survive this by pulling together, not apart.")
38
+ logger.error("I didn't ask for this. I killed my best friend for you people.")
39
+ ```
40
+
41
+ [Want a more tailored one? 想要阅读更多?](./README-more.md)
package/index.js ADDED
@@ -0,0 +1,272 @@
1
+ // import { consola } from "consola";
2
+ /** @typedef {number} int */
3
+
4
+ import { styleText } from "node:util"
5
+
6
+ const BASH_COLORS = {
7
+ reset: "\x1b[0m",
8
+ bold: "\x1b[1m",
9
+ gray: "\x1b[90m",
10
+ red: "\x1b[31m",
11
+ green: "\x1b[32m",
12
+ yellow: "\x1b[33m",
13
+ blue: "\x1b[34m",
14
+ brightBlue: "\x1b[94m",
15
+ magenta: "\x1b[35m",
16
+ cyan: "\x1b[36m",
17
+ }
18
+
19
+ /**
20
+ * `trace (最低) < debug < info < warn < error < fatal (最高)`
21
+ *
22
+ * 当你设置一个日志级别(例如 info),只有优先级 ≥ info 的日志(即 info、warn、error、fatal)会被输出;而 trace 和 debug 级别的日志会被忽略。这正是日志级别配置的核心作用。
23
+ * none: 用于关闭所有日志输出(静默模式)。
24
+ */
25
+ export const LEVEL = /** @type {const} */ ({
26
+ DEBUG: 1,
27
+ INFO: 2,
28
+ get SUCCESS() {
29
+ return LEVEL.INFO
30
+ },
31
+ WARN: 3,
32
+ ERROR: 4,
33
+ NONE: 5,
34
+ })
35
+
36
+ /**
37
+ * @satisfies { { [key in LevelKey | 'success']: { emoji: string, color: string } } }
38
+ */
39
+ const decorations = /** @type {const} */ ({
40
+ debug: { emoji: "🐞", color: "" },
41
+ info: { emoji: styleText("blueBright", "ℹ"), color: BASH_COLORS.brightBlue },
42
+ warn: { emoji: "🟡", color: BASH_COLORS.yellow },
43
+ error: { emoji: "🔴", color: BASH_COLORS.red },
44
+ success: { emoji: styleText("green", "✔"), color: BASH_COLORS.green },
45
+ })
46
+
47
+ /** @typedef { typeof LEVEL[keyof typeof LEVEL] } LevelNumber */
48
+ /** @typedef { Exclude<Lowercase<keyof typeof LEVEL>, 'none'> } LevelKey */
49
+ /** @import { LoggerOptions } from './logger.type.js' */
50
+
51
+ export class Logger {
52
+ /** @type {int | null} */
53
+ #now = null
54
+ /**
55
+ *
56
+ * @param {LoggerOptions} opts
57
+ */
58
+ constructor(opts) {
59
+ this.level = opts.level
60
+ this.showTime = opts.showTime ?? true
61
+ this.showDiff = opts.showDiff ?? false
62
+ this.diffToHumanTime = opts.diffToHumanTime
63
+ this.#now = null
64
+ this.formatTimestamp = opts.formatTime ?? ((date) => date.toISOString())
65
+ this.color = opts.color ?? false
66
+ this.emoji = opts.emoji ?? false
67
+ this.formatLevel =
68
+ opts.formatLevel ??
69
+ // to format `[emoji level]`
70
+ ((level) => {
71
+ let emoji = this.emoji ? this.pickEmoji(level) : ""
72
+ emoji = emoji ? `${emoji} ` : ""
73
+ const levelWithEmoji = `[${emoji}${this.#makeColorLevel(level)}]`
74
+
75
+ return levelWithEmoji
76
+ })
77
+
78
+ /** @type {NonNullable<LoggerOptions['pickEmoji']>} */
79
+ this.pickEmoji = opts.pickEmoji ?? ((level) => decorations[level].emoji)
80
+ }
81
+
82
+ /**
83
+ *
84
+ * @param {LevelKey} level
85
+ * @returns {string}
86
+ */
87
+ #makeColorLevel(level) {
88
+ const upperCasedLevel = level.toUpperCase()
89
+ if (!this.color) {
90
+ return upperCasedLevel
91
+ }
92
+
93
+ /** @type {import('node:util').InspectColor[] | null} */
94
+ const color =
95
+ level === "success"
96
+ ? ["white", "bgGreen"]
97
+ : level === "warn"
98
+ ? ["black", "bgYellow"]
99
+ : level === "error"
100
+ ? ["white", "bgRed"]
101
+ : null
102
+
103
+ if (!color) {
104
+ return upperCasedLevel
105
+ }
106
+
107
+ // console.log("level:", { upperCasedLevel, color })
108
+
109
+ const colorLevel = styleText(color, ` ${upperCasedLevel} `)
110
+ return colorLevel
111
+ }
112
+
113
+ /**
114
+ * @param {unknown[]} args
115
+ */
116
+ debug = (...args) => {
117
+ if (this.level <= LEVEL.DEBUG) {
118
+ this.#dispatch("debug", args)
119
+ }
120
+ }
121
+
122
+ /**
123
+ * @param {...unknown} args
124
+ */
125
+ info(...args) {
126
+ if (this.level <= LEVEL.INFO) {
127
+ this.#dispatch("info", args)
128
+ }
129
+ }
130
+
131
+ /**
132
+ * @param {...unknown} args
133
+ */
134
+ warn(...args) {
135
+ if (this.level <= LEVEL.WARN) {
136
+ this.#dispatch("warn", args)
137
+ }
138
+ }
139
+
140
+ /**
141
+ * @param {...unknown} args
142
+ */
143
+ error(...args) {
144
+ if (this.level <= LEVEL.ERROR) {
145
+ this.#dispatch("error", args)
146
+ }
147
+ }
148
+
149
+ /**
150
+ * @param {unknown[]} args
151
+ */
152
+ success(...args) {
153
+ if (this.level <= LEVEL.INFO) {
154
+ this.#dispatch("success", args)
155
+ }
156
+ }
157
+
158
+ /**
159
+ *
160
+ * @param {LevelKey} level
161
+ * @param {unknown[]} args
162
+ * @param {Pick<LoggerOptions, 'formatLevel'>} pickEmoji
163
+ */
164
+ #dispatch(level, args, { formatLevel = this.formatLevel } = {}) {
165
+ const leadings = [
166
+ this.showTime && this.formatTimestamp(new Date()),
167
+ // showDiff
168
+ this.showDiff && this.#formatDiff(),
169
+ formatLevel(level),
170
+ ]
171
+ .filter(Boolean)
172
+ .join(" ")
173
+
174
+ if (!this.color) {
175
+ return console[level !== "success" ? level : "info"](leadings, ...args)
176
+ }
177
+
178
+ const color = decorations[level].color
179
+
180
+ return console[level !== "success" ? level : "info"](leadings + color, ...args, BASH_COLORS.reset)
181
+ }
182
+
183
+ #formatDiff() {
184
+ const diff = this.#now ? Date.now() - this.#now : 0
185
+ this.#now = Date.now()
186
+
187
+ const humanTime = this.#diffToHumanTime(diff)
188
+
189
+ return `${BASH_COLORS.yellow}+${humanTime}${BASH_COLORS.reset}`
190
+ }
191
+
192
+ /**
193
+ *
194
+ * @param {int} diff
195
+ * @returns
196
+ */
197
+ #diffToHumanTime(diff) {
198
+ if (typeof this.diffToHumanTime === "function") {
199
+ return this.diffToHumanTime(diff)
200
+ }
201
+
202
+ return `${diff.toLocaleString("en")}ms`
203
+ }
204
+ }
205
+
206
+ /**
207
+ *
208
+ * @param {{ verbose: undefined | boolean }} param0
209
+ */
210
+ export function createLogger({ verbose }) {
211
+ return new Logger({
212
+ level: verbose ? LEVEL.DEBUG : LEVEL.INFO,
213
+ showTime: true,
214
+ formatTime: (date) => date.toLocaleString(),
215
+ diffToHumanTime: (diff) => {
216
+ if (diff < 1000) {
217
+ return `${diff}ms`
218
+ }
219
+
220
+ return `${Math.floor(diff / 1000)}.${String(diff % 1000).padStart(3, "0")}s`
221
+ },
222
+ // formatLevel: (level) => `[${level.toUpperCase()}]`,
223
+ color: true,
224
+ // emoji: true,
225
+ showDiff: true,
226
+ })
227
+ }
228
+
229
+ const isMain = () => {
230
+ try {
231
+ return import.meta.main
232
+ } catch {
233
+ return false
234
+ }
235
+ }
236
+
237
+ if (isMain()) {
238
+ const logger = createLogger({ verbose: true })
239
+
240
+ logger.info("Hello from the wasteland.")
241
+ logger.warn("Watch out for walkers.")
242
+ logger.error("We lost another one.")
243
+
244
+ const { setTimeout: sleep } = await import("node:timers/promises")
245
+
246
+ logger.debug("Using consola 3.0.0")
247
+
248
+ await sleep(100)
249
+ logger.debug("Using consola", "3.0.0")
250
+
251
+ await sleep(100)
252
+ logger.debug("Using consola", "v", 3)
253
+
254
+ await sleep(100)
255
+ logger.info("Using consola", {
256
+ string: "3.0.0",
257
+ boolean: true,
258
+ number: 123,
259
+ array: [1, 2, 3],
260
+ object: { a: 1, b: 2 },
261
+ })
262
+
263
+ await sleep(100)
264
+ logger.warn("A new version of consola is available: 3.0.1")
265
+
266
+ await sleep(1000)
267
+ logger.success("Project built!")
268
+
269
+ setTimeout(() => {
270
+ logger.error(new Error("This is an example error. Everything is fine!"))
271
+ }, 1000)
272
+ }
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "walking-log",
3
+ "description": "A simple console logger only for printing messages in Node.js console with colors time, time diff and emojis. I use it in CLI app usually",
4
+ "private": false,
5
+ "version": "0.0.2",
6
+ "type": "module",
7
+ "files": [
8
+ "index.js",
9
+ "logger.type.js"
10
+ ],
11
+ "scripts": {
12
+ "test": "node --test",
13
+ "typecheck": "tsgo --noEmit",
14
+ "pub:patch": "npm version patch",
15
+ "pub:minor": "npm version minor",
16
+ "pub:major": "npm version major",
17
+ "preversion": "npm test && npm run typecheck",
18
+ "postversion": "npm publish && git push && git push --tags"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/legend80s/my-npm-dashboard/blob/main/src/backend/utils/logger"
23
+ },
24
+ "homepage": "https://github.com/legend80s/my-npm-dashboard/blob/main/src/backend/utils/logger/README.md",
25
+ "devDependencies": {},
26
+ "dependencies": {},
27
+ "keywords": [
28
+ "consola",
29
+ "console",
30
+ "log",
31
+ "logger",
32
+ "logging",
33
+ "debug",
34
+ "info",
35
+ "warn",
36
+ "error",
37
+ "fatal",
38
+ "node",
39
+ "nodejs"
40
+ ]
41
+ }