@vimcord/internal 0.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/README.md +22 -0
- package/dist/index.cjs +454 -0
- package/dist/index.d.cts +147 -0
- package/dist/index.d.ts +147 -0
- package/dist/index.js +405 -0
- package/package.json +31 -0
- package/src/index.ts +8 -0
- package/src/types/helpers.ts +18 -0
- package/src/utils/Logger.ts +417 -0
- package/src/utils/VimcordError.ts +9 -0
- package/src/utils/arr.ts +4 -0
- package/src/utils/import.ts +78 -0
- package/src/utils/obj.ts +30 -0
- package/src/utils/process.ts +12 -0
- package/src/utils/str.ts +9 -0
- package/tsconfig.json +10 -0
- package/tsup.config.ts +11 -0
package/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
██╗ ██╗██╗███╗ ███╗ ██████╗ ██████╗ ██████╗ ██████╗
|
|
5
|
+
██║ ██║██║████╗ ████║██╔════╝██╔═══██╗██╔══██╗██╔══██╗
|
|
6
|
+
██║ ██║██║██╔████╔██║██║ ██║ ██║██████╔╝██║ ██║
|
|
7
|
+
╚██╗ ██╔╝██║██║╚██╔╝██║██║ ██║ ██║██╔══██╗██║ ██║
|
|
8
|
+
╚████╔╝ ██║██║ ╚═╝ ██║╚██████╗╚██████╔╝██║ ██║██████╔╝
|
|
9
|
+
╚═══╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═════╝
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
**vhem-cord** — the Discord.js framework that actually respects your time
|
|
13
|
+
|
|
14
|
+
[](https://www.npmjs.com/package/vimcord)
|
|
15
|
+
[](https://www.npmjs.com/package/vimcord)
|
|
16
|
+
[](https://www.typescriptlang.org/)
|
|
17
|
+
[](LICENSE)
|
|
18
|
+
[](https://discord.js.org/)
|
|
19
|
+
|
|
20
|
+
[Installation](#installation) · [Quick Start](#quick-start) · [Features](#features) · [API](#api-reference) · [Examples](#examples)
|
|
21
|
+
|
|
22
|
+
</div>
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
DEFAULT_COLORS: () => DEFAULT_COLORS,
|
|
34
|
+
Logger: () => Logger,
|
|
35
|
+
VimcordError: () => VimcordError,
|
|
36
|
+
createHumanId: () => createHumanId,
|
|
37
|
+
createRandomId: () => createRandomId,
|
|
38
|
+
forceArray: () => forceArray,
|
|
39
|
+
getDevMode: () => getDevMode,
|
|
40
|
+
getPackageJson: () => getPackageJson,
|
|
41
|
+
getProcessDir: () => getProcessDir,
|
|
42
|
+
importModulesFromDir: () => importModulesFromDir,
|
|
43
|
+
isTSOrJS: () => isTSOrJS,
|
|
44
|
+
mergeDeep: () => mergeDeep,
|
|
45
|
+
stripAnsi: () => stripAnsi
|
|
46
|
+
});
|
|
47
|
+
module.exports = __toCommonJS(index_exports);
|
|
48
|
+
|
|
49
|
+
// src/utils/VimcordError.ts
|
|
50
|
+
var VimcordError = class extends Error {
|
|
51
|
+
constructor(message, code) {
|
|
52
|
+
super(message);
|
|
53
|
+
this.code = code;
|
|
54
|
+
this.name = "VimcordError";
|
|
55
|
+
}
|
|
56
|
+
code;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// src/utils/arr.ts
|
|
60
|
+
function forceArray(value) {
|
|
61
|
+
return Array.isArray(value) ? value : [value];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/utils/import.ts
|
|
65
|
+
var import_node_fs = require("fs");
|
|
66
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
67
|
+
var import_node_url = require("url");
|
|
68
|
+
function getProcessDir() {
|
|
69
|
+
const mainPath = process.argv[1];
|
|
70
|
+
if (!mainPath) return "";
|
|
71
|
+
return import_node_path.default.dirname(mainPath);
|
|
72
|
+
}
|
|
73
|
+
function isTSOrJS(filename, suffix) {
|
|
74
|
+
if (!suffix) return filename.endsWith(".ts") || filename.endsWith(".js");
|
|
75
|
+
if (Array.isArray(suffix)) {
|
|
76
|
+
return suffix.some((s) => filename.endsWith(`${s}.ts`) || filename.endsWith(`${s}.js`));
|
|
77
|
+
} else {
|
|
78
|
+
return filename.endsWith(`${suffix}.ts`) || filename.endsWith(`${suffix}.js`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async function importModulesFromDir(dir, suffix) {
|
|
82
|
+
const cwd = getProcessDir();
|
|
83
|
+
const MODULE_RELATIVE_PATH = import_node_path.default.join(cwd, dir);
|
|
84
|
+
const MODULE_LOG_PATH = dir;
|
|
85
|
+
const files = (() => {
|
|
86
|
+
if (!(0, import_node_fs.existsSync)(MODULE_RELATIVE_PATH)) return [];
|
|
87
|
+
const walk = (targetDir, base = "") => {
|
|
88
|
+
return (0, import_node_fs.readdirSync)(targetDir, { withFileTypes: true }).flatMap((entry) => {
|
|
89
|
+
const relativePath = base ? import_node_path.default.join(base, entry.name) : entry.name;
|
|
90
|
+
const fullPath = import_node_path.default.join(targetDir, entry.name);
|
|
91
|
+
if (entry.isDirectory()) return walk(fullPath, relativePath);
|
|
92
|
+
if (entry.isFile()) return [relativePath];
|
|
93
|
+
return [];
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
return walk(MODULE_RELATIVE_PATH);
|
|
97
|
+
})().filter((filename) => isTSOrJS(filename, suffix));
|
|
98
|
+
if (!files.length) return [];
|
|
99
|
+
const modules = await Promise.all(
|
|
100
|
+
files.map(async (fn) => {
|
|
101
|
+
const modulePath = import_node_path.default.join(MODULE_RELATIVE_PATH, fn);
|
|
102
|
+
const logPath = `./${import_node_path.default.join(MODULE_LOG_PATH, fn)}`;
|
|
103
|
+
try {
|
|
104
|
+
const moduleUrl = (0, import_node_url.pathToFileURL)(modulePath);
|
|
105
|
+
moduleUrl.searchParams.set("updated", Date.now().toString());
|
|
106
|
+
const importedModule = await import(moduleUrl.href);
|
|
107
|
+
return { module: importedModule, path: logPath };
|
|
108
|
+
} catch (err) {
|
|
109
|
+
console.warn(`Failed to import module at '${logPath}'`, err);
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
);
|
|
114
|
+
const filteredModules = modules.filter((m) => Boolean(m));
|
|
115
|
+
if (!filteredModules.length) {
|
|
116
|
+
console.warn(`No valid modules were found in directory '${dir}'`);
|
|
117
|
+
}
|
|
118
|
+
return filteredModules;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// src/utils/Logger.ts
|
|
122
|
+
var import_ansis = __toESM(require("ansis"), 1);
|
|
123
|
+
var import_unicode_animations = require("unicode-animations");
|
|
124
|
+
var LEVEL_PRIORITY = {
|
|
125
|
+
debug: 0,
|
|
126
|
+
info: 1,
|
|
127
|
+
success: 2,
|
|
128
|
+
warn: 3,
|
|
129
|
+
error: 4
|
|
130
|
+
};
|
|
131
|
+
var DEFAULT_COLORS = {
|
|
132
|
+
primary: "#5865F2",
|
|
133
|
+
success: "#57F287",
|
|
134
|
+
warn: "#FEE75C",
|
|
135
|
+
caution: "#FF9D00",
|
|
136
|
+
error: "#ED4245",
|
|
137
|
+
muted: "#747F8D",
|
|
138
|
+
info: "#87CEEB",
|
|
139
|
+
text: "#FFFFFF"
|
|
140
|
+
};
|
|
141
|
+
var SPINNER_FRAMES = import_unicode_animations.spinners.breathe.frames.map((f) => import_ansis.default.hex(DEFAULT_COLORS.muted)(f));
|
|
142
|
+
var SPINNER_INTERVAL = import_unicode_animations.spinners.breathe.interval;
|
|
143
|
+
var DEFAULT_LOADER_CYCLE_INTERVAL_MS = 3e3;
|
|
144
|
+
function stripAnsi(str) {
|
|
145
|
+
return str.replace(/\x1b\[[0-9;]*m/g, "");
|
|
146
|
+
}
|
|
147
|
+
function padVisible(str, length) {
|
|
148
|
+
const visible = stripAnsi(str).length;
|
|
149
|
+
return str + " ".repeat(Math.max(0, length - visible));
|
|
150
|
+
}
|
|
151
|
+
var Logger = class {
|
|
152
|
+
options;
|
|
153
|
+
activeLoaders = /* @__PURE__ */ new Map();
|
|
154
|
+
nextLoaderId = 0;
|
|
155
|
+
renderInterval = null;
|
|
156
|
+
frameIndex = 0;
|
|
157
|
+
constructor(options = {}) {
|
|
158
|
+
this.options = {
|
|
159
|
+
prefix: null,
|
|
160
|
+
prefixEmoji: null,
|
|
161
|
+
minLevel: "debug",
|
|
162
|
+
verbose: false,
|
|
163
|
+
...options,
|
|
164
|
+
colors: { ...DEFAULT_COLORS, ...options.colors }
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
formatPrefix() {
|
|
168
|
+
const { prefixEmoji, prefix, colors } = this.options;
|
|
169
|
+
if (!prefix) return "";
|
|
170
|
+
const emoji = prefixEmoji ? `${prefixEmoji} ` : "";
|
|
171
|
+
return import_ansis.default.bold.hex(colors.primary)(`${emoji}${prefix}`);
|
|
172
|
+
}
|
|
173
|
+
formatLevel(level) {
|
|
174
|
+
const { colors } = this.options;
|
|
175
|
+
switch (level) {
|
|
176
|
+
case "debug":
|
|
177
|
+
return import_ansis.default.hex(colors.muted)("DEBUG");
|
|
178
|
+
case "info":
|
|
179
|
+
return import_ansis.default.hex(colors.info)("INFO");
|
|
180
|
+
case "success":
|
|
181
|
+
return import_ansis.default.bold.hex(colors.success)("\u2713 SUCCESS");
|
|
182
|
+
case "warn":
|
|
183
|
+
return import_ansis.default.bold.hex(colors.warn)("\u26A0 WARN");
|
|
184
|
+
case "error":
|
|
185
|
+
return import_ansis.default.bold.hex(colors.error)("\u2715 ERROR");
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/** Checks if the given log level should be logged. */
|
|
189
|
+
shouldLog(level) {
|
|
190
|
+
return LEVEL_PRIORITY[level] >= LEVEL_PRIORITY[this.options.minLevel];
|
|
191
|
+
}
|
|
192
|
+
/** Returns a colored `[HH:mm:ss]` timestamp for log prefixes. */
|
|
193
|
+
timestamp() {
|
|
194
|
+
const { colors } = this.options;
|
|
195
|
+
const now = /* @__PURE__ */ new Date();
|
|
196
|
+
const time = now.toLocaleTimeString("en-US", {
|
|
197
|
+
hour12: false,
|
|
198
|
+
hour: "2-digit",
|
|
199
|
+
minute: "2-digit",
|
|
200
|
+
second: "2-digit"
|
|
201
|
+
});
|
|
202
|
+
return import_ansis.default.hex(colors.muted)(`[${time}]`);
|
|
203
|
+
}
|
|
204
|
+
/** Writes a line using the builtin `console`, keeping active TTY loaders pinned below the new output. */
|
|
205
|
+
write(stream, ...data) {
|
|
206
|
+
const loaderCount = this.activeLoaders.size;
|
|
207
|
+
const shouldRedrawLoaders = process.stdout.isTTY === true && loaderCount > 0;
|
|
208
|
+
if (shouldRedrawLoaders) process.stdout.write(`\x1B[${loaderCount}A\x1B[J`);
|
|
209
|
+
console[stream](...data);
|
|
210
|
+
if (!shouldRedrawLoaders) return;
|
|
211
|
+
const now = Date.now();
|
|
212
|
+
const frame = SPINNER_FRAMES[this.frameIndex];
|
|
213
|
+
for (const [, loader] of this.activeLoaders) {
|
|
214
|
+
if (loader.cycle && now - loader.lastCycleAt >= loader.cycleInterval) {
|
|
215
|
+
loader.message = loader.cycle();
|
|
216
|
+
loader.lastCycleAt = now;
|
|
217
|
+
}
|
|
218
|
+
process.stdout.write(`${this.timestamp()} ${this.formatPrefix()} ${frame} ${loader.message}
|
|
219
|
+
`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/** Logs a message without level filtering. */
|
|
223
|
+
log(...data) {
|
|
224
|
+
this.write("log", this.timestamp(), this.formatPrefix(), ...data);
|
|
225
|
+
}
|
|
226
|
+
logVerbose(...data) {
|
|
227
|
+
if (!this.options.verbose) return;
|
|
228
|
+
this.log(...data);
|
|
229
|
+
}
|
|
230
|
+
debug(message, ...data) {
|
|
231
|
+
if (!this.shouldLog("debug")) return;
|
|
232
|
+
this.write("log", this.timestamp(), this.formatPrefix(), this.formatLevel("debug"), import_ansis.default.dim(message), ...data);
|
|
233
|
+
}
|
|
234
|
+
debugVerbose(message, ...data) {
|
|
235
|
+
if (!this.options.verbose) return;
|
|
236
|
+
this.debug(message, ...data);
|
|
237
|
+
}
|
|
238
|
+
info(...data) {
|
|
239
|
+
if (!this.shouldLog("info")) return;
|
|
240
|
+
this.write("log", this.timestamp(), this.formatPrefix(), this.formatLevel("info"), ...data);
|
|
241
|
+
}
|
|
242
|
+
infoVerbose(...data) {
|
|
243
|
+
if (!this.options.verbose) return;
|
|
244
|
+
this.info(...data);
|
|
245
|
+
}
|
|
246
|
+
success(message, ...data) {
|
|
247
|
+
if (!this.shouldLog("success")) return;
|
|
248
|
+
this.write(
|
|
249
|
+
"log",
|
|
250
|
+
this.timestamp(),
|
|
251
|
+
this.formatPrefix(),
|
|
252
|
+
this.formatLevel("success"),
|
|
253
|
+
import_ansis.default.hex(this.options.colors.success)(message),
|
|
254
|
+
...data
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
successVerbose(message, ...data) {
|
|
258
|
+
if (!this.options.verbose) return;
|
|
259
|
+
this.success(message, ...data);
|
|
260
|
+
}
|
|
261
|
+
warn(message, ...data) {
|
|
262
|
+
if (!this.shouldLog("warn")) return;
|
|
263
|
+
this.write(
|
|
264
|
+
"warn",
|
|
265
|
+
this.timestamp(),
|
|
266
|
+
this.formatPrefix(),
|
|
267
|
+
this.formatLevel("warn"),
|
|
268
|
+
import_ansis.default.hex(this.options.colors.warn)(message),
|
|
269
|
+
...data
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
warnVerbose(message, ...data) {
|
|
273
|
+
if (!this.options.verbose) return;
|
|
274
|
+
this.warn(message, ...data);
|
|
275
|
+
}
|
|
276
|
+
error(message, error, ...data) {
|
|
277
|
+
if (!this.shouldLog("error")) return;
|
|
278
|
+
this.write(
|
|
279
|
+
"error",
|
|
280
|
+
this.timestamp(),
|
|
281
|
+
this.formatPrefix(),
|
|
282
|
+
this.formatLevel("error"),
|
|
283
|
+
import_ansis.default.hex(this.options.colors.error)(message),
|
|
284
|
+
...data
|
|
285
|
+
);
|
|
286
|
+
if (error?.stack) {
|
|
287
|
+
this.write("error", import_ansis.default.dim(error.stack));
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
errorVerbose(message, error, ...data) {
|
|
291
|
+
if (!this.options.verbose) return;
|
|
292
|
+
this.error(message, error, ...data);
|
|
293
|
+
}
|
|
294
|
+
// --- Structured Output ---
|
|
295
|
+
/** @deprecated */
|
|
296
|
+
table(title, data) {
|
|
297
|
+
const { colors } = this.options;
|
|
298
|
+
this.write("log", this.timestamp(), this.formatPrefix(), import_ansis.default.bold(title));
|
|
299
|
+
for (const [key, value] of Object.entries(data)) {
|
|
300
|
+
const formattedKey = padVisible(import_ansis.default.hex(colors.warn)(` ${key}`), 25);
|
|
301
|
+
const formattedValue = import_ansis.default.hex(colors.muted)(String(value));
|
|
302
|
+
this.write("log", `${formattedKey} ${formattedValue}`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
// TODO: Make this prettier, replace it with `header` using the textThroughLine helper
|
|
306
|
+
/** @deprecated */
|
|
307
|
+
section(title) {
|
|
308
|
+
const { colors } = this.options;
|
|
309
|
+
const titleVisible = stripAnsi(title);
|
|
310
|
+
const width = Math.max(30, titleVisible.length + 4);
|
|
311
|
+
const line = "\u2500".repeat(width);
|
|
312
|
+
const paddedTitle = padVisible(import_ansis.default.bold.hex(colors.text)(title), width);
|
|
313
|
+
this.write("log", import_ansis.default.hex(colors.muted)(`
|
|
314
|
+
\u250C\u2500${line}\u2500\u2510`));
|
|
315
|
+
this.write("log", import_ansis.default.hex(colors.muted)("\u2502 ") + paddedTitle + import_ansis.default.hex(colors.muted)(" \u2502"));
|
|
316
|
+
this.write("log", import_ansis.default.hex(colors.muted)(`\u2514\u2500${line}\u2500\u2518`));
|
|
317
|
+
}
|
|
318
|
+
loader(message, cycleOrOptions, interval = DEFAULT_LOADER_CYCLE_INTERVAL_MS) {
|
|
319
|
+
const { colors } = this.options;
|
|
320
|
+
const cycle = typeof cycleOrOptions === "function" ? cycleOrOptions : cycleOrOptions?.cycle;
|
|
321
|
+
const cycleInterval = typeof cycleOrOptions === "function" ? interval : cycleOrOptions?.interval ?? interval;
|
|
322
|
+
let stopped = false;
|
|
323
|
+
const id = this.nextLoaderId++;
|
|
324
|
+
const prefix = `${this.timestamp()} ${this.formatPrefix()}`;
|
|
325
|
+
this.activeLoaders.set(id, { cycle, cycleInterval, lastCycleAt: Date.now(), message });
|
|
326
|
+
if (process.stdout.isTTY !== true) {
|
|
327
|
+
this.write("log", prefix, message);
|
|
328
|
+
} else {
|
|
329
|
+
process.stdout.write(`${prefix} ${SPINNER_FRAMES[0]} ${message}
|
|
330
|
+
`);
|
|
331
|
+
if (!this.renderInterval) {
|
|
332
|
+
this.renderInterval = setInterval(() => {
|
|
333
|
+
const loaderCount = this.activeLoaders.size;
|
|
334
|
+
if (!loaderCount) return;
|
|
335
|
+
this.frameIndex = (this.frameIndex + 1) % SPINNER_FRAMES.length;
|
|
336
|
+
process.stdout.write(`\x1B[${loaderCount}A\x1B[J`);
|
|
337
|
+
const now = Date.now();
|
|
338
|
+
const frame = SPINNER_FRAMES[this.frameIndex];
|
|
339
|
+
for (const [, loader] of this.activeLoaders) {
|
|
340
|
+
if (loader.cycle && now - loader.lastCycleAt >= loader.cycleInterval) {
|
|
341
|
+
loader.message = loader.cycle();
|
|
342
|
+
loader.lastCycleAt = now;
|
|
343
|
+
}
|
|
344
|
+
process.stdout.write(`${this.timestamp()} ${this.formatPrefix()} ${frame} ${loader.message}
|
|
345
|
+
`);
|
|
346
|
+
}
|
|
347
|
+
}, SPINNER_INTERVAL);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return (finalMessage, clear = false) => {
|
|
351
|
+
if (stopped) return;
|
|
352
|
+
stopped = true;
|
|
353
|
+
this.activeLoaders.delete(id);
|
|
354
|
+
if (this.activeLoaders.size === 0 && this.renderInterval) {
|
|
355
|
+
clearInterval(this.renderInterval);
|
|
356
|
+
this.renderInterval = null;
|
|
357
|
+
this.frameIndex = 0;
|
|
358
|
+
}
|
|
359
|
+
if (process.stdout.isTTY !== true) {
|
|
360
|
+
if (clear) {
|
|
361
|
+
if (finalMessage) this.write("log", finalMessage);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
this.write(
|
|
365
|
+
"log",
|
|
366
|
+
`${this.timestamp()} ${this.formatPrefix()}`,
|
|
367
|
+
import_ansis.default.hex(colors.success)("\u2713"),
|
|
368
|
+
finalMessage ?? message
|
|
369
|
+
);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
process.stdout.write(`\x1B[${this.activeLoaders.size + 1}A\x1B[J`);
|
|
373
|
+
if (clear) {
|
|
374
|
+
if (finalMessage) process.stdout.write(`${finalMessage}
|
|
375
|
+
`);
|
|
376
|
+
} else {
|
|
377
|
+
const check = import_ansis.default.hex(colors.success)("\u2713");
|
|
378
|
+
process.stdout.write(`${this.timestamp()} ${this.formatPrefix()} ${check} ${finalMessage ?? message}
|
|
379
|
+
`);
|
|
380
|
+
}
|
|
381
|
+
const frame = SPINNER_FRAMES[this.frameIndex];
|
|
382
|
+
for (const [, loader] of this.activeLoaders) {
|
|
383
|
+
process.stdout.write(`${this.timestamp()} ${this.formatPrefix()} ${frame} ${loader.message}
|
|
384
|
+
`);
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
/** Sets the minimum log level required for level-filtered messages. */
|
|
389
|
+
setLevel(level) {
|
|
390
|
+
this.options.minLevel = level;
|
|
391
|
+
}
|
|
392
|
+
/** Enables or disables verbose-only log methods. */
|
|
393
|
+
setVerbose(verbose) {
|
|
394
|
+
this.options.verbose = verbose;
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
// src/utils/obj.ts
|
|
399
|
+
function isPlainObject(value) {
|
|
400
|
+
if (typeof value !== "object" || value === null) return false;
|
|
401
|
+
if (Array.isArray(value)) return false;
|
|
402
|
+
return Object.prototype.toString.call(value) === "[object Object]";
|
|
403
|
+
}
|
|
404
|
+
function mergeDeep(target, ...sources) {
|
|
405
|
+
for (const source of sources) {
|
|
406
|
+
if (!source) continue;
|
|
407
|
+
for (const key in source) {
|
|
408
|
+
if (!Object.prototype.hasOwnProperty.call(source, key)) continue;
|
|
409
|
+
const sourceValue = source[key];
|
|
410
|
+
const targetValue = target[key];
|
|
411
|
+
if (isPlainObject(sourceValue) && isPlainObject(targetValue)) {
|
|
412
|
+
mergeDeep(targetValue, sourceValue);
|
|
413
|
+
} else if (sourceValue !== void 0) {
|
|
414
|
+
target[key] = sourceValue;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return target;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// src/utils/process.ts
|
|
422
|
+
var import_node_fs2 = require("fs");
|
|
423
|
+
var import_node_path2 = require("path");
|
|
424
|
+
function getPackageJson() {
|
|
425
|
+
return JSON.parse((0, import_node_fs2.readFileSync)((0, import_node_path2.join)(process.cwd(), "package.json"), "utf-8"));
|
|
426
|
+
}
|
|
427
|
+
function getDevMode() {
|
|
428
|
+
return process.argv.includes("--dev");
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// src/utils/str.ts
|
|
432
|
+
var import_human_id = require("human-id");
|
|
433
|
+
function createRandomId() {
|
|
434
|
+
return `v-${Math.random().toString(36).split(".")[1]}`;
|
|
435
|
+
}
|
|
436
|
+
function createHumanId() {
|
|
437
|
+
return (0, import_human_id.humanId)({ separator: "-", capitalize: false });
|
|
438
|
+
}
|
|
439
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
440
|
+
0 && (module.exports = {
|
|
441
|
+
DEFAULT_COLORS,
|
|
442
|
+
Logger,
|
|
443
|
+
VimcordError,
|
|
444
|
+
createHumanId,
|
|
445
|
+
createRandomId,
|
|
446
|
+
forceArray,
|
|
447
|
+
getDevMode,
|
|
448
|
+
getPackageJson,
|
|
449
|
+
getProcessDir,
|
|
450
|
+
importModulesFromDir,
|
|
451
|
+
isTSOrJS,
|
|
452
|
+
mergeDeep,
|
|
453
|
+
stripAnsi
|
|
454
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep partial type utility - recursively makes all properties optional.
|
|
3
|
+
* Preserves function types to maintain their signatures.
|
|
4
|
+
*/
|
|
5
|
+
type PartialDeep<T> = T extends (...args: unknown[]) => unknown ? T : T extends object ? T extends ReadonlyArray<infer U> ? ReadonlyArray<PartialDeep<U>> : T extends Array<infer U> ? Array<PartialDeep<U>> : {
|
|
6
|
+
[K in keyof T]?: PartialDeep<T[K]>;
|
|
7
|
+
} : T;
|
|
8
|
+
/** A function used for indexing something. */
|
|
9
|
+
type IndexFn<T> = (module: T) => string | string[] | undefined;
|
|
10
|
+
|
|
11
|
+
declare class VimcordError extends Error {
|
|
12
|
+
readonly code: string;
|
|
13
|
+
constructor(message: string, code: string);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Ensures a value is always returned as an array */
|
|
17
|
+
declare function forceArray<T>(value: T | T[]): T[];
|
|
18
|
+
|
|
19
|
+
interface ImportedModule<T> {
|
|
20
|
+
module: T;
|
|
21
|
+
path: string;
|
|
22
|
+
}
|
|
23
|
+
declare function getProcessDir(): string;
|
|
24
|
+
declare function isTSOrJS(filename: string, suffix?: string | string[]): boolean;
|
|
25
|
+
declare function importModulesFromDir<T>(dir: string, suffix?: string | string[]): Promise<ImportedModule<T>[]>;
|
|
26
|
+
|
|
27
|
+
type LogLevel = "debug" | "info" | "success" | "warn" | "error";
|
|
28
|
+
type ColorScheme = {
|
|
29
|
+
primary: string;
|
|
30
|
+
success: string;
|
|
31
|
+
warn: string;
|
|
32
|
+
caution: string;
|
|
33
|
+
error: string;
|
|
34
|
+
muted: string;
|
|
35
|
+
info: string;
|
|
36
|
+
text: string;
|
|
37
|
+
};
|
|
38
|
+
type LoggerOptions = {
|
|
39
|
+
/** Emoji shown before the prefix. */
|
|
40
|
+
prefixEmoji?: string | null;
|
|
41
|
+
/** Prefix label shown before each message. */
|
|
42
|
+
prefix?: string | null;
|
|
43
|
+
/**
|
|
44
|
+
* Minimum log level to output.
|
|
45
|
+
* @default "debug"
|
|
46
|
+
*/
|
|
47
|
+
minLevel?: LogLevel;
|
|
48
|
+
/**
|
|
49
|
+
* Log verbose messages.
|
|
50
|
+
* @default false
|
|
51
|
+
*/
|
|
52
|
+
verbose?: boolean;
|
|
53
|
+
/** Color scheme to use. */
|
|
54
|
+
colors?: Partial<ColorScheme>;
|
|
55
|
+
};
|
|
56
|
+
type LoaderOptions = {
|
|
57
|
+
/** Returns the next loader message while the spinner is active. */
|
|
58
|
+
cycle?: () => string;
|
|
59
|
+
/** How often `cycle` should be called in milliseconds. */
|
|
60
|
+
interval?: number;
|
|
61
|
+
};
|
|
62
|
+
declare const DEFAULT_COLORS: ColorScheme;
|
|
63
|
+
/** Strips ANSI escape codes from a string for accurate length measurement */
|
|
64
|
+
declare function stripAnsi(str: string): string;
|
|
65
|
+
/** Timestamped console logger with level filtering, colors, and optional live terminal loaders. */
|
|
66
|
+
declare class Logger {
|
|
67
|
+
readonly options: Required<LoggerOptions> & {
|
|
68
|
+
colors: ColorScheme;
|
|
69
|
+
};
|
|
70
|
+
private activeLoaders;
|
|
71
|
+
private nextLoaderId;
|
|
72
|
+
private renderInterval;
|
|
73
|
+
private frameIndex;
|
|
74
|
+
constructor(options?: LoggerOptions);
|
|
75
|
+
private formatPrefix;
|
|
76
|
+
private formatLevel;
|
|
77
|
+
/** Checks if the given log level should be logged. */
|
|
78
|
+
private shouldLog;
|
|
79
|
+
/** Returns a colored `[HH:mm:ss]` timestamp for log prefixes. */
|
|
80
|
+
timestamp(): string;
|
|
81
|
+
/** Writes a line using the builtin `console`, keeping active TTY loaders pinned below the new output. */
|
|
82
|
+
write(stream: "log" | "warn" | "error", ...data: unknown[]): void;
|
|
83
|
+
/** Logs a message without level filtering. */
|
|
84
|
+
log(...data: unknown[]): void;
|
|
85
|
+
logVerbose(...data: unknown[]): void;
|
|
86
|
+
debug(message: string, ...data: unknown[]): void;
|
|
87
|
+
debugVerbose(message: string, ...data: unknown[]): void;
|
|
88
|
+
info(...data: unknown[]): void;
|
|
89
|
+
infoVerbose(...data: unknown[]): void;
|
|
90
|
+
success(message: string, ...data: unknown[]): void;
|
|
91
|
+
successVerbose(message: string, ...data: unknown[]): void;
|
|
92
|
+
warn(message: string, ...data: unknown[]): void;
|
|
93
|
+
warnVerbose(message: string, ...data: unknown[]): void;
|
|
94
|
+
error(message: string, error?: Error, ...data: unknown[]): void;
|
|
95
|
+
errorVerbose(message: string, error?: Error, ...data: unknown[]): void;
|
|
96
|
+
/** @deprecated */
|
|
97
|
+
table(title: string, data: Record<string, unknown>): void;
|
|
98
|
+
/** @deprecated */
|
|
99
|
+
section(title: string): void;
|
|
100
|
+
/**
|
|
101
|
+
* Starts a loader and returns a `stop` function.
|
|
102
|
+
*
|
|
103
|
+
* In an interactive TTY, loaders render as live spinners pinned below regular logs. In hosted logs, CI,
|
|
104
|
+
* Docker logs, and other non-TTY streams, loaders degrade to normal start/final lines without ANSI cursor codes.
|
|
105
|
+
* Always call `stop()` with `try/finally` so the render loop can be cleaned up.
|
|
106
|
+
*
|
|
107
|
+
* @param message Initial loader message.
|
|
108
|
+
* @param cycleOrOptions Optional message cycle function or options object.
|
|
109
|
+
* @param interval How often to call the cycle function in milliseconds. Defaults to `3000`.
|
|
110
|
+
* @returns A function that stops the loader. Pass `clear: true` to suppress the success line.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* const stop = logger.loader("Loading...")
|
|
115
|
+
* try {
|
|
116
|
+
* await doWork()
|
|
117
|
+
* stop("Done!")
|
|
118
|
+
* } catch (e) {
|
|
119
|
+
* stop("Failed")
|
|
120
|
+
* }
|
|
121
|
+
* ```
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```ts
|
|
125
|
+
* const stop = logger.loader("Starting...", () => "Still starting...", 1000)
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
loader(message: string, cycle?: () => string, interval?: number): (finalMessage?: string, clear?: boolean) => void;
|
|
129
|
+
loader(message: string, options?: LoaderOptions): (finalMessage?: string, clear?: boolean) => void;
|
|
130
|
+
/** Sets the minimum log level required for level-filtered messages. */
|
|
131
|
+
setLevel(level: LogLevel): void;
|
|
132
|
+
/** Enables or disables verbose-only log methods. */
|
|
133
|
+
setVerbose(verbose: boolean): void;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Deep merge objects - mutates target and returns it */
|
|
137
|
+
declare function mergeDeep<T extends object>(target: PartialDeep<T>, ...sources: Array<object | undefined>): T;
|
|
138
|
+
|
|
139
|
+
/** Reads the `package.json` file from the current working directory. */
|
|
140
|
+
declare function getPackageJson(): Record<string, unknown>;
|
|
141
|
+
/** Checks if the process was ran using the `--dev` flag. */
|
|
142
|
+
declare function getDevMode(): boolean;
|
|
143
|
+
|
|
144
|
+
declare function createRandomId(): string;
|
|
145
|
+
declare function createHumanId(): string;
|
|
146
|
+
|
|
147
|
+
export { type ColorScheme, DEFAULT_COLORS, type IndexFn, type LoaderOptions, type LogLevel, Logger, type LoggerOptions, type PartialDeep, VimcordError, createHumanId, createRandomId, forceArray, getDevMode, getPackageJson, getProcessDir, importModulesFromDir, isTSOrJS, mergeDeep, stripAnsi };
|