where-log 0.1.1
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 +59 -0
- package/dist/index.cjs +145 -0
- package/dist/index.d.cts +32 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.mjs +109 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# callsite-log
|
|
2
|
+
|
|
3
|
+
Log a value with the caller file and line number.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install callsite-log
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { log } from "callsite-log";
|
|
15
|
+
|
|
16
|
+
const user = { id: 1, name: "Shovon" };
|
|
17
|
+
log(user);
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Output style:
|
|
21
|
+
|
|
22
|
+
```txt
|
|
23
|
+
user.tsx:44
|
|
24
|
+
{ id: 1, name: "Shovon" }
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Label overload:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
log("user", { id: 1, name: "Shovon" });
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Options:
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
log(user, { colors: false });
|
|
37
|
+
|
|
38
|
+
log("user", user, {
|
|
39
|
+
formatter: ({ location, label, value }) => ({
|
|
40
|
+
locationLine: `[${location}]`,
|
|
41
|
+
valueLine: `${label}: ${JSON.stringify(value)}`,
|
|
42
|
+
}),
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Notes
|
|
47
|
+
|
|
48
|
+
- Node.js: call-site detection is reliable with V8 stack traces.
|
|
49
|
+
- Browser/Next.js: call-site depends on source maps and bundler/devtools behavior.
|
|
50
|
+
- If stack parsing fails, the package prints `unknown:0` on line 1.
|
|
51
|
+
|
|
52
|
+
## API
|
|
53
|
+
|
|
54
|
+
- `log(value: unknown): void`
|
|
55
|
+
- `log(value: unknown, options?: LogOptions): void`
|
|
56
|
+
- `log(label: string, value: unknown, options?: LogOptions): void`
|
|
57
|
+
- `LogOptions`
|
|
58
|
+
- `colors?: boolean` (Node only, default `true`)
|
|
59
|
+
- `formatter?: (input) => { locationLine: unknown; valueLine: unknown }`
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
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
|
+
__internal: () => __internal,
|
|
34
|
+
log: () => log
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
37
|
+
var import_node_util = __toESM(require("util"), 1);
|
|
38
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
39
|
+
function isNodeRuntime() {
|
|
40
|
+
return typeof process !== "undefined" && !!process.versions?.node;
|
|
41
|
+
}
|
|
42
|
+
function safeToInt(input) {
|
|
43
|
+
const n = Number.parseInt(input, 10);
|
|
44
|
+
return Number.isFinite(n) ? n : 0;
|
|
45
|
+
}
|
|
46
|
+
function normalizeFile(filePath) {
|
|
47
|
+
const clean = filePath.replace(/^file:\/\//, "");
|
|
48
|
+
return import_node_path.default.basename(clean);
|
|
49
|
+
}
|
|
50
|
+
function parseFrameLine(frame) {
|
|
51
|
+
const cleaned = frame.trim();
|
|
52
|
+
if (!cleaned) return null;
|
|
53
|
+
const v8Match = cleaned.match(/(?:at\s+)?(?:.+\s+\()?(.+):(\d+):(\d+)\)?$/);
|
|
54
|
+
if (v8Match) {
|
|
55
|
+
return {
|
|
56
|
+
file: normalizeFile(v8Match[1]),
|
|
57
|
+
line: safeToInt(v8Match[2])
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const ffMatch = cleaned.match(/@(.+):(\d+):(\d+)$/);
|
|
61
|
+
if (ffMatch) {
|
|
62
|
+
return {
|
|
63
|
+
file: normalizeFile(ffMatch[1]),
|
|
64
|
+
line: safeToInt(ffMatch[2])
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
function getCallerFromStack(stack) {
|
|
70
|
+
if (!stack) return { file: "unknown", line: 0 };
|
|
71
|
+
const frames = stack.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
72
|
+
for (const frame of frames) {
|
|
73
|
+
if (frame.includes("getCallerFromStack")) continue;
|
|
74
|
+
if (frame.includes("at log")) continue;
|
|
75
|
+
if (frame.includes("at Object.log")) continue;
|
|
76
|
+
if (frame.includes("/src/index.ts")) continue;
|
|
77
|
+
if (frame.includes("\\src\\index.ts")) continue;
|
|
78
|
+
const parsed = parseFrameLine(frame);
|
|
79
|
+
if (parsed) return parsed;
|
|
80
|
+
}
|
|
81
|
+
return { file: "unknown", line: 0 };
|
|
82
|
+
}
|
|
83
|
+
function isLogOptions(value) {
|
|
84
|
+
if (!value || typeof value !== "object") return false;
|
|
85
|
+
const record = value;
|
|
86
|
+
return "colors" in record || "formatter" in record;
|
|
87
|
+
}
|
|
88
|
+
function formatValue(value, options) {
|
|
89
|
+
if (!isNodeRuntime()) {
|
|
90
|
+
return value;
|
|
91
|
+
}
|
|
92
|
+
return import_node_util.default.inspect(value, {
|
|
93
|
+
depth: null,
|
|
94
|
+
colors: options?.colors ?? true,
|
|
95
|
+
compact: false
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function formatLabeledValue(label, formattedValue) {
|
|
99
|
+
if (!label) return formattedValue;
|
|
100
|
+
if (typeof formattedValue === "string") {
|
|
101
|
+
return `${label}: ${formattedValue}`;
|
|
102
|
+
}
|
|
103
|
+
return `${label}: ${String(formattedValue)}`;
|
|
104
|
+
}
|
|
105
|
+
function log(arg1, arg2, arg3) {
|
|
106
|
+
let label;
|
|
107
|
+
let value;
|
|
108
|
+
let options;
|
|
109
|
+
if (typeof arg1 === "string" && arg2 !== void 0 && !isLogOptions(arg2)) {
|
|
110
|
+
label = arg1;
|
|
111
|
+
value = arg2;
|
|
112
|
+
options = arg3;
|
|
113
|
+
} else {
|
|
114
|
+
value = arg1;
|
|
115
|
+
options = isLogOptions(arg2) ? arg2 : void 0;
|
|
116
|
+
}
|
|
117
|
+
const stack = new Error().stack;
|
|
118
|
+
const caller = getCallerFromStack(stack);
|
|
119
|
+
const location = `${caller.file}:${caller.line}`;
|
|
120
|
+
const formattedValue = formatValue(value, options);
|
|
121
|
+
if (options?.formatter) {
|
|
122
|
+
const formatted = options.formatter({
|
|
123
|
+
location,
|
|
124
|
+
label,
|
|
125
|
+
value,
|
|
126
|
+
formattedValue
|
|
127
|
+
});
|
|
128
|
+
console.log(formatted.locationLine);
|
|
129
|
+
console.log(formatted.valueLine);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
console.log(location);
|
|
133
|
+
console.log(formatLabeledValue(label, formattedValue));
|
|
134
|
+
}
|
|
135
|
+
var __internal = {
|
|
136
|
+
getCallerFromStack,
|
|
137
|
+
parseFrameLine,
|
|
138
|
+
formatLabeledValue,
|
|
139
|
+
isLogOptions
|
|
140
|
+
};
|
|
141
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
142
|
+
0 && (module.exports = {
|
|
143
|
+
__internal,
|
|
144
|
+
log
|
|
145
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
type ParsedFrame = {
|
|
2
|
+
file: string;
|
|
3
|
+
line: number;
|
|
4
|
+
};
|
|
5
|
+
type FormatterInput = {
|
|
6
|
+
location: string;
|
|
7
|
+
label?: string;
|
|
8
|
+
value: unknown;
|
|
9
|
+
formattedValue: unknown;
|
|
10
|
+
};
|
|
11
|
+
type FormatterOutput = {
|
|
12
|
+
locationLine: unknown;
|
|
13
|
+
valueLine: unknown;
|
|
14
|
+
};
|
|
15
|
+
type LogOptions = {
|
|
16
|
+
colors?: boolean;
|
|
17
|
+
formatter?: (input: FormatterInput) => FormatterOutput;
|
|
18
|
+
};
|
|
19
|
+
declare function parseFrameLine(frame: string): ParsedFrame | null;
|
|
20
|
+
declare function getCallerFromStack(stack?: string): ParsedFrame;
|
|
21
|
+
declare function isLogOptions(value: unknown): value is LogOptions;
|
|
22
|
+
declare function formatLabeledValue(label: string | undefined, formattedValue: unknown): unknown;
|
|
23
|
+
declare function log(value: unknown, options?: LogOptions): void;
|
|
24
|
+
declare function log(label: string, value: unknown, options?: LogOptions): void;
|
|
25
|
+
declare const __internal: {
|
|
26
|
+
getCallerFromStack: typeof getCallerFromStack;
|
|
27
|
+
parseFrameLine: typeof parseFrameLine;
|
|
28
|
+
formatLabeledValue: typeof formatLabeledValue;
|
|
29
|
+
isLogOptions: typeof isLogOptions;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export { type FormatterInput, type FormatterOutput, type LogOptions, __internal, log };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
type ParsedFrame = {
|
|
2
|
+
file: string;
|
|
3
|
+
line: number;
|
|
4
|
+
};
|
|
5
|
+
type FormatterInput = {
|
|
6
|
+
location: string;
|
|
7
|
+
label?: string;
|
|
8
|
+
value: unknown;
|
|
9
|
+
formattedValue: unknown;
|
|
10
|
+
};
|
|
11
|
+
type FormatterOutput = {
|
|
12
|
+
locationLine: unknown;
|
|
13
|
+
valueLine: unknown;
|
|
14
|
+
};
|
|
15
|
+
type LogOptions = {
|
|
16
|
+
colors?: boolean;
|
|
17
|
+
formatter?: (input: FormatterInput) => FormatterOutput;
|
|
18
|
+
};
|
|
19
|
+
declare function parseFrameLine(frame: string): ParsedFrame | null;
|
|
20
|
+
declare function getCallerFromStack(stack?: string): ParsedFrame;
|
|
21
|
+
declare function isLogOptions(value: unknown): value is LogOptions;
|
|
22
|
+
declare function formatLabeledValue(label: string | undefined, formattedValue: unknown): unknown;
|
|
23
|
+
declare function log(value: unknown, options?: LogOptions): void;
|
|
24
|
+
declare function log(label: string, value: unknown, options?: LogOptions): void;
|
|
25
|
+
declare const __internal: {
|
|
26
|
+
getCallerFromStack: typeof getCallerFromStack;
|
|
27
|
+
parseFrameLine: typeof parseFrameLine;
|
|
28
|
+
formatLabeledValue: typeof formatLabeledValue;
|
|
29
|
+
isLogOptions: typeof isLogOptions;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export { type FormatterInput, type FormatterOutput, type LogOptions, __internal, log };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import util from "util";
|
|
3
|
+
import path from "path";
|
|
4
|
+
function isNodeRuntime() {
|
|
5
|
+
return typeof process !== "undefined" && !!process.versions?.node;
|
|
6
|
+
}
|
|
7
|
+
function safeToInt(input) {
|
|
8
|
+
const n = Number.parseInt(input, 10);
|
|
9
|
+
return Number.isFinite(n) ? n : 0;
|
|
10
|
+
}
|
|
11
|
+
function normalizeFile(filePath) {
|
|
12
|
+
const clean = filePath.replace(/^file:\/\//, "");
|
|
13
|
+
return path.basename(clean);
|
|
14
|
+
}
|
|
15
|
+
function parseFrameLine(frame) {
|
|
16
|
+
const cleaned = frame.trim();
|
|
17
|
+
if (!cleaned) return null;
|
|
18
|
+
const v8Match = cleaned.match(/(?:at\s+)?(?:.+\s+\()?(.+):(\d+):(\d+)\)?$/);
|
|
19
|
+
if (v8Match) {
|
|
20
|
+
return {
|
|
21
|
+
file: normalizeFile(v8Match[1]),
|
|
22
|
+
line: safeToInt(v8Match[2])
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const ffMatch = cleaned.match(/@(.+):(\d+):(\d+)$/);
|
|
26
|
+
if (ffMatch) {
|
|
27
|
+
return {
|
|
28
|
+
file: normalizeFile(ffMatch[1]),
|
|
29
|
+
line: safeToInt(ffMatch[2])
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
function getCallerFromStack(stack) {
|
|
35
|
+
if (!stack) return { file: "unknown", line: 0 };
|
|
36
|
+
const frames = stack.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
37
|
+
for (const frame of frames) {
|
|
38
|
+
if (frame.includes("getCallerFromStack")) continue;
|
|
39
|
+
if (frame.includes("at log")) continue;
|
|
40
|
+
if (frame.includes("at Object.log")) continue;
|
|
41
|
+
if (frame.includes("/src/index.ts")) continue;
|
|
42
|
+
if (frame.includes("\\src\\index.ts")) continue;
|
|
43
|
+
const parsed = parseFrameLine(frame);
|
|
44
|
+
if (parsed) return parsed;
|
|
45
|
+
}
|
|
46
|
+
return { file: "unknown", line: 0 };
|
|
47
|
+
}
|
|
48
|
+
function isLogOptions(value) {
|
|
49
|
+
if (!value || typeof value !== "object") return false;
|
|
50
|
+
const record = value;
|
|
51
|
+
return "colors" in record || "formatter" in record;
|
|
52
|
+
}
|
|
53
|
+
function formatValue(value, options) {
|
|
54
|
+
if (!isNodeRuntime()) {
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
return util.inspect(value, {
|
|
58
|
+
depth: null,
|
|
59
|
+
colors: options?.colors ?? true,
|
|
60
|
+
compact: false
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function formatLabeledValue(label, formattedValue) {
|
|
64
|
+
if (!label) return formattedValue;
|
|
65
|
+
if (typeof formattedValue === "string") {
|
|
66
|
+
return `${label}: ${formattedValue}`;
|
|
67
|
+
}
|
|
68
|
+
return `${label}: ${String(formattedValue)}`;
|
|
69
|
+
}
|
|
70
|
+
function log(arg1, arg2, arg3) {
|
|
71
|
+
let label;
|
|
72
|
+
let value;
|
|
73
|
+
let options;
|
|
74
|
+
if (typeof arg1 === "string" && arg2 !== void 0 && !isLogOptions(arg2)) {
|
|
75
|
+
label = arg1;
|
|
76
|
+
value = arg2;
|
|
77
|
+
options = arg3;
|
|
78
|
+
} else {
|
|
79
|
+
value = arg1;
|
|
80
|
+
options = isLogOptions(arg2) ? arg2 : void 0;
|
|
81
|
+
}
|
|
82
|
+
const stack = new Error().stack;
|
|
83
|
+
const caller = getCallerFromStack(stack);
|
|
84
|
+
const location = `${caller.file}:${caller.line}`;
|
|
85
|
+
const formattedValue = formatValue(value, options);
|
|
86
|
+
if (options?.formatter) {
|
|
87
|
+
const formatted = options.formatter({
|
|
88
|
+
location,
|
|
89
|
+
label,
|
|
90
|
+
value,
|
|
91
|
+
formattedValue
|
|
92
|
+
});
|
|
93
|
+
console.log(formatted.locationLine);
|
|
94
|
+
console.log(formatted.valueLine);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
console.log(location);
|
|
98
|
+
console.log(formatLabeledValue(label, formattedValue));
|
|
99
|
+
}
|
|
100
|
+
var __internal = {
|
|
101
|
+
getCallerFromStack,
|
|
102
|
+
parseFrameLine,
|
|
103
|
+
formatLabeledValue,
|
|
104
|
+
isLogOptions
|
|
105
|
+
};
|
|
106
|
+
export {
|
|
107
|
+
__internal,
|
|
108
|
+
log
|
|
109
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "where-log",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Log values with caller file and line number.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"private": false,
|
|
7
|
+
"type": "module",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"main": "./dist/index.cjs",
|
|
10
|
+
"module": "./dist/index.mjs",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.mjs",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup --config tsup.config.ts",
|
|
24
|
+
"typecheck": "tsc --noEmit",
|
|
25
|
+
"test": "vitest run --pool=threads",
|
|
26
|
+
"dev": "vitest --pool=threads",
|
|
27
|
+
"pack:dry-run": "npm pack --dry-run --cache ./.npm-cache",
|
|
28
|
+
"publish:dry-run": "npm publish --dry-run --cache ./.npm-cache",
|
|
29
|
+
"release:check": "npm run pack:dry-run && npm run publish:dry-run"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"logger",
|
|
33
|
+
"debug",
|
|
34
|
+
"callsite",
|
|
35
|
+
"typescript"
|
|
36
|
+
],
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^24.0.15",
|
|
42
|
+
"tsup": "^8.2.4",
|
|
43
|
+
"typescript": "^5.5.4",
|
|
44
|
+
"vitest": "^2.0.5"
|
|
45
|
+
}
|
|
46
|
+
}
|