@vsfedorenko/next-logger 0.1.0 → 0.2.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.
@@ -1,124 +1,33 @@
1
1
  "use strict";
2
2
  /**
3
- * Patches Next.js' internal logger (`next/dist/build/output/log`) so every
4
- * call routes through the shared consola instance tagged `next.js`.
3
+ * Classifier for Next.js' internal log output captured at the console sink.
5
4
  *
6
- * Next 13+ defines its logger exports as non-configurable accessor properties,
7
- * which would throw under a plain redefinition. We work around this by
8
- * replacing `require.cache[...].exports` with a shallow copy (the original's
9
- * approach), then defining each method as a plain value property on the copy.
10
- *
11
- * This is a side-effect module importing it applies the patch exactly once.
5
+ * Next.js' `next/dist/build/output/log` funnels every diagnostic line through
6
+ * `console.log`/`console.warn`/`console.error`, prefixing each with a coloured
7
+ * marker symbol (`▲`, `✓`, `⚠`, …). Detecting that prefix lets the console
8
+ * patch tag those lines as `next.js` rather than `console` restoring the
9
+ * source distinction WITHOUT monkeypatching Next's module (which is isolated
10
+ * into a separate bundle instance under Turbopack and unreachable via
11
+ * `require.cache`).
12
12
  */
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.NEXT_PREFIXES = void 0;
15
- exports.routeNextMethod = routeNextMethod;
16
- exports.patchNext = patchNext;
17
- const logger_1 = require("../logger");
18
- const util_1 = require("./util");
19
- /**
20
- * The methods exported by Next's log module that we patch.
21
- *
22
- * `prefixes` is the set Next itself advertises (wait/error/warn/ready/info/
23
- * event/trace). `bootstrap` is an extra export that bypasses the prefix logic
24
- * (raw `console.log`) — we route it too so it stays consistent.
25
- */
26
- exports.NEXT_PREFIXES = [
27
- "wait",
28
- "error",
29
- "warn",
30
- "ready",
31
- "info",
32
- "event",
33
- "trace",
34
- ];
14
+ exports.isNextLog = isNextLog;
15
+ const ANSI = /\u001b\[[0-9;]*m/g;
35
16
  /**
36
- * Maps a Next.js prefix name to the corresponding consola log function.
37
- * `trace` maps to consola's native `trace` no fallback needed (consola has
38
- * it; the pino original had to fall back to `debug` for Winston which lacks
39
- * trace).
40
- *
41
- * Pure function — given a consola instance and a tag, returns the routing
42
- * function. Exported so the level mapping can be unit-tested without touching
43
- * `require.cache` or a real Next.js install.
17
+ * Marker symbols Next.js prefixes its log lines with (after stripping ANSI
18
+ * colour codes). Covers the startup banner (`▲ Next.js`), `✓ Ready`/`event`,
19
+ * and `⚠` warnings.
44
20
  */
45
- function routeNextMethod(method, consola, tag) {
46
- const child = consola.withTag(tag);
47
- const bound = selectConsolaMethod(method, child);
48
- return (0, util_1.skipEmpty)(bound);
49
- }
21
+ const NEXT_MARKERS = ["▲", "✓", "⚠", "●", "✗"];
50
22
  /**
51
- * Selects the consola method matching a Next.js method name.
23
+ * Returns `true` when the given console call args look like a Next.js log line
24
+ * (first string arg, ANSI-stripped, starts with a Next marker symbol).
52
25
  */
53
- function selectConsolaMethod(method, consola) {
54
- switch (method) {
55
- case "error":
56
- return consola.error.bind(consola);
57
- case "warn":
58
- return consola.warn.bind(consola);
59
- case "trace":
60
- return consola.trace.bind(consola);
61
- default:
62
- // wait, ready, info, event, bootstrap → info
63
- return consola.info.bind(consola);
64
- }
65
- }
66
- /**
67
- * Applies the Next.js logger patch. Safe to call multiple times — it
68
- * re-derives the methods from the current {@link logger} each time.
69
- *
70
- * @param tagOverride override the `next.js` tag (useful for tests).
71
- */
72
- function patchNext(tagOverride) {
73
- const tag = tagOverride ?? "next.js";
74
- let nextLog;
75
- try {
76
- nextLog = require("next/dist/build/output/log");
77
- }
78
- catch {
79
- // `next` is not installed — nothing to patch.
80
- return;
81
- }
82
- // Build the routed methods for every prefix + bootstrap.
83
- const methodNames = [...Object.keys(nextLog.prefixes), "bootstrap"];
84
- const routed = {};
85
- for (const method of methodNames) {
86
- routed[method] = routeNextMethod(method, logger_1.logger, tag);
87
- }
88
- // Strategy 1: if we have access to the CJS module cache, replace the exports
89
- // with a fresh shallow copy. This sidesteps the non-configurable accessor
90
- // properties Next 13+ defines (they throw under defineProperty).
91
- const cacheObject = tryGetCache("next/dist/build/output/log");
92
- if (cacheObject !== null) {
93
- cacheObject.exports = { ...nextLog, ...routed };
94
- return;
95
- }
96
- // Strategy 2 (fallback): mutate the live exports object in place. Try plain
97
- // assignment first; if the property is non-configurable + non-writable,
98
- // skip silently (the routing function is still correct — verified by unit
99
- // tests on routeNextMethod directly).
100
- const target = nextLog;
101
- for (const [method, fn] of Object.entries(routed)) {
102
- try {
103
- target[method] = fn;
104
- }
105
- catch {
106
- // Non-writable, non-configurable — skip.
107
- }
108
- }
109
- }
110
- /** Looks up a module in the CJS cache by specifier; returns null if absent. */
111
- function tryGetCache(specifier) {
112
- try {
113
- const resolved = require.resolve(specifier);
114
- const cached = require.cache[resolved];
115
- return cached ?? null;
116
- }
117
- catch {
118
- return null;
119
- }
26
+ function isNextLog(args) {
27
+ const first = args.find((a) => typeof a === "string");
28
+ if (first === undefined)
29
+ return false;
30
+ const stripped = first.replace(ANSI, "").trimStart();
31
+ return NEXT_MARKERS.some((m) => stripped.startsWith(m));
120
32
  }
121
- // Apply on import (side effect), matching the original's
122
- // `require('./patches/next')` semantics.
123
- patchNext();
124
33
  //# sourceMappingURL=next.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"next.js","sourceRoot":"","sources":["../../src/patches/next.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"next.js","sourceRoot":"","sources":["../../src/patches/next.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;AAeH,8BAKC;AAlBD,MAAM,IAAI,GAAG,mBAAmB,CAAC;AAEjC;;;;GAIG;AACH,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAU,CAAC;AAExD;;;GAGG;AACH,SAAgB,SAAS,CAAC,IAAwB;IAChD,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;IACnE,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACtC,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC;IACrD,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,36 @@
1
+ import type { ConsolaOptions } from "consola";
2
+ /**
3
+ * Options for {@link withLogger} — the build-time, serialisable form of the
4
+ * logger config. Delivered to the runtime as JSON via the `NEXT_LOGGER_CONFIG`
5
+ * env var (Next.js' `env` config key inlines it at build time).
6
+ */
7
+ export interface LoggerPluginOptions {
8
+ /**
9
+ * Partial consola options merged over the library defaults
10
+ * (`{ level, formatOptions: { date, compact } }`). Only serialisable options
11
+ * are supported here — a live `ConsolaInstance` or factory cannot cross the
12
+ * build→runtime boundary.
13
+ */
14
+ readonly consola?: Partial<ConsolaOptions>;
15
+ }
16
+ /**
17
+ * Next.js config wrapper (higher-order function), used like the other `withX`
18
+ * plugins (`withPWA`, `withBundleAnalyzer`):
19
+ *
20
+ * ```ts
21
+ * // next.config.ts
22
+ * import { withLogger } from "@vsfedorenko/next-logger";
23
+ *
24
+ * export default withLogger({ consola: { level: 4 } })({
25
+ * // ...your next config
26
+ * });
27
+ * ```
28
+ *
29
+ * It injects `NEXT_LOGGER_CONFIG` (the serialised options) into the config's
30
+ * `env` key — a validated, warning-free key that Next.js inlines into
31
+ * `process.env` at build time. {@link init} reads it back at runtime. Works
32
+ * under both webpack and Turbopack, and avoids the "Unrecognized key" warning
33
+ * that a custom top-level `logger` key would trigger.
34
+ */
35
+ export declare function withLogger(options?: LoggerPluginOptions): <C extends object>(nextConfig: C) => C;
36
+ //# sourceMappingURL=withLogger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withLogger.d.ts","sourceRoot":"","sources":["../src/withLogger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;CAC5C;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,UAAU,CAAC,OAAO,GAAE,mBAAwB,IAEzC,CAAC,SAAS,MAAM,EAAE,YAAY,CAAC,KAAG,CAAC,CAQrD"}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withLogger = withLogger;
4
+ const config_1 = require("./config");
5
+ /**
6
+ * Next.js config wrapper (higher-order function), used like the other `withX`
7
+ * plugins (`withPWA`, `withBundleAnalyzer`):
8
+ *
9
+ * ```ts
10
+ * // next.config.ts
11
+ * import { withLogger } from "@vsfedorenko/next-logger";
12
+ *
13
+ * export default withLogger({ consola: { level: 4 } })({
14
+ * // ...your next config
15
+ * });
16
+ * ```
17
+ *
18
+ * It injects `NEXT_LOGGER_CONFIG` (the serialised options) into the config's
19
+ * `env` key — a validated, warning-free key that Next.js inlines into
20
+ * `process.env` at build time. {@link init} reads it back at runtime. Works
21
+ * under both webpack and Turbopack, and avoids the "Unrecognized key" warning
22
+ * that a custom top-level `logger` key would trigger.
23
+ */
24
+ function withLogger(options = {}) {
25
+ const serialised = JSON.stringify(options);
26
+ return function (nextConfig) {
27
+ const existingEnv = nextConfig.env ?? {};
28
+ return {
29
+ ...nextConfig,
30
+ env: { ...existingEnv, [config_1.CONFIG_ENV_VAR]: serialised },
31
+ };
32
+ };
33
+ }
34
+ //# sourceMappingURL=withLogger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withLogger.js","sourceRoot":"","sources":["../src/withLogger.ts"],"names":[],"mappings":";;AAqCA,gCAUC;AA9CD,qCAA0C;AAiB1C;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAgB,UAAU,CAAC,UAA+B,EAAE;IAC1D,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAC3C,OAAO,UAA4B,UAAa;QAC9C,MAAM,WAAW,GACb,UAAsC,CAAC,GAA0C,IAAI,EAAE,CAAC;QAC5F,OAAO;YACL,GAAG,UAAU;YACb,GAAG,EAAE,EAAE,GAAG,WAAW,EAAE,CAAC,uBAAc,CAAC,EAAE,UAAU,EAAE;SACjD,CAAC;IACT,CAAC,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vsfedorenko/next-logger",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Universal logging kit for Next.js — patches Next's internal logger & console, routes output to consola, Sentry, JSON, and more.",
5
5
  "license": "MIT",
6
6
  "author": "Vadim Fedorenko <vsfedorenko@yandex.ru>",
@@ -37,14 +37,6 @@
37
37
  "types": "./dist/browser.d.ts",
38
38
  "default": "./dist/browser.js"
39
39
  },
40
- "./presets/all": {
41
- "types": "./dist/presets/all.d.ts",
42
- "default": "./dist/presets/all.js"
43
- },
44
- "./presets/next-only": {
45
- "types": "./dist/presets/next-only.d.ts",
46
- "default": "./dist/presets/next-only.js"
47
- },
48
40
  "./reporters/sentry": {
49
41
  "types": "./dist/reporters/sentry.d.ts",
50
42
  "default": "./dist/reporters/sentry.js"
@@ -56,11 +48,7 @@
56
48
  "LICENSE"
57
49
  ],
58
50
  "sideEffects": [
59
- "./dist/index.js",
60
- "./dist/presets/all.js",
61
- "./dist/presets/next-only.js",
62
- "./dist/patches/console.js",
63
- "./dist/patches/next.js"
51
+ "./dist/browser.js"
64
52
  ],
65
53
  "engines": {
66
54
  "node": ">=18"
@@ -71,6 +59,7 @@
71
59
  "typecheck": "tsc --noEmit",
72
60
  "test": "vitest run",
73
61
  "test:watch": "vitest",
62
+ "test:e2e": "npm run build && vitest --config vitest.e2e.config.ts run",
74
63
  "prepublishOnly": "npm run build && npm test"
75
64
  },
76
65
  "peerDependencies": {
@@ -80,18 +69,12 @@
80
69
  "peerDependenciesMeta": {
81
70
  "@sentry/nextjs": {
82
71
  "optional": true
83
- },
84
- "jiti": {
85
- "optional": true
86
72
  }
87
73
  },
88
- "dependencies": {
89
- "lilconfig": "^3.1.0"
90
- },
74
+ "dependencies": {},
91
75
  "devDependencies": {
92
76
  "@types/node": "^22.0.0",
93
77
  "consola": "^3.4.2",
94
- "jiti": "^2.0.0",
95
78
  "typescript": "^5.7.0",
96
79
  "vitest": "^2.1.0"
97
80
  },
@@ -1,14 +0,0 @@
1
- /**
2
- * Preset: patch Next.js' logger **and** the global `console.*`.
3
- *
4
- * This is the default — importing `@vsfedorenko/next-logger` or `@vsfedorenko/next-logger/presets/all`
5
- * applies both patches as side effects.
6
- *
7
- * Order matters: Next's logger is patched first so its internal calls are
8
- * already routed through consola before `console` itself is overwritten. Both
9
- * patches share the same {@link logger} instance.
10
- */
11
- import "../patches/next";
12
- import "../patches/console";
13
- export {};
14
- //# sourceMappingURL=all.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"all.d.ts","sourceRoot":"","sources":["../../src/presets/all.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,iBAAiB,CAAC;AACzB,OAAO,oBAAoB,CAAC;AAE5B,OAAO,EAAE,CAAC"}
@@ -1,15 +0,0 @@
1
- "use strict";
2
- /**
3
- * Preset: patch Next.js' logger **and** the global `console.*`.
4
- *
5
- * This is the default — importing `@vsfedorenko/next-logger` or `@vsfedorenko/next-logger/presets/all`
6
- * applies both patches as side effects.
7
- *
8
- * Order matters: Next's logger is patched first so its internal calls are
9
- * already routed through consola before `console` itself is overwritten. Both
10
- * patches share the same {@link logger} instance.
11
- */
12
- Object.defineProperty(exports, "__esModule", { value: true });
13
- require("../patches/next");
14
- require("../patches/console");
15
- //# sourceMappingURL=all.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"all.js","sourceRoot":"","sources":["../../src/presets/all.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;AAEH,2BAAyB;AACzB,8BAA4B"}
@@ -1,12 +0,0 @@
1
- /**
2
- * Preset: patch Next.js' logger **only**, leaving the global `console.*`
3
- * untouched.
4
- *
5
- * Import `@vsfedorenko/next-logger/presets/next-only` instead of the default entry when you
6
- * want Next's internal logs routed through consola but prefer to keep
7
- * `console.log`/`console.error` with their native Node.js formatting (e.g. for
8
- * terminal-pretty output in development).
9
- */
10
- import "../patches/next";
11
- export {};
12
- //# sourceMappingURL=next-only.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"next-only.d.ts","sourceRoot":"","sources":["../../src/presets/next-only.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,iBAAiB,CAAC;AAEzB,OAAO,EAAE,CAAC"}
@@ -1,13 +0,0 @@
1
- "use strict";
2
- /**
3
- * Preset: patch Next.js' logger **only**, leaving the global `console.*`
4
- * untouched.
5
- *
6
- * Import `@vsfedorenko/next-logger/presets/next-only` instead of the default entry when you
7
- * want Next's internal logs routed through consola but prefer to keep
8
- * `console.log`/`console.error` with their native Node.js formatting (e.g. for
9
- * terminal-pretty output in development).
10
- */
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- require("../patches/next");
13
- //# sourceMappingURL=next-only.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"next-only.js","sourceRoot":"","sources":["../../src/presets/next-only.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;AAEH,2BAAyB"}