Skip to content

Commit f81856d

Browse files
committed
fix: 🔊 match logs with Next 13.5 style
1 parent 8329c66 commit f81856d

File tree

6 files changed

+220
-27
lines changed

6 files changed

+220
-27
lines changed

jest.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ module.exports = {
1414
'<rootDir>/*.js',
1515
'<rootDir>/build',
1616
'<rootDir>/examples',
17+
'<rootDir>/src/lib',
1718
],
1819
};

src/helpers/log.spec.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { error, event, info, prefixes, warn } from './log';
2+
3+
const logSpy = jest.spyOn(console, 'log');
4+
const warnSpy = jest.spyOn(console, 'warn');
5+
const errorSpy = jest.spyOn(console, 'error');
6+
7+
beforeAll(() => {
8+
logSpy.mockImplementation();
9+
warnSpy.mockImplementation();
10+
errorSpy.mockImplementation();
11+
});
12+
13+
afterAll(() => {
14+
logSpy.mockRestore();
15+
warnSpy.mockRestore();
16+
errorSpy.mockRestore();
17+
});
18+
19+
describe('error', () => {
20+
it('should log an error message', () => {
21+
error('foo');
22+
23+
expect(errorSpy).toHaveBeenCalledWith(
24+
` ${prefixes.error}`,
25+
'foo',
26+
'(next-runtime-env)',
27+
);
28+
});
29+
});
30+
31+
describe('warn', () => {
32+
it('should log a warning message', () => {
33+
warn('foo');
34+
35+
expect(warnSpy).toHaveBeenCalledWith(
36+
` ${prefixes.warn}`,
37+
'foo',
38+
'(next-runtime-env)',
39+
);
40+
});
41+
});
42+
43+
describe('info', () => {
44+
it('should log an info message', () => {
45+
info('foo');
46+
47+
expect(logSpy).toHaveBeenCalledWith(
48+
` ${prefixes.info}`,
49+
'foo',
50+
'(next-runtime-env)',
51+
);
52+
});
53+
});
54+
55+
describe('event', () => {
56+
it('should log an event message', () => {
57+
event('foo');
58+
59+
expect(logSpy).toHaveBeenCalledWith(
60+
` ${prefixes.event}`,
61+
'foo',
62+
'(next-runtime-env)',
63+
);
64+
});
65+
});

src/helpers/log.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { bold, green, red, white, yellow } from '../lib/picocolors';
2+
3+
export const prefixes = {
4+
error: red(bold('⨯')),
5+
warn: yellow(bold('⚠')),
6+
info: white(bold(' ')),
7+
event: green(bold('✓')),
8+
} as const;
9+
10+
const suffix = '(next-runtime-env)';
11+
12+
const LOGGING_METHOD = {
13+
log: 'log',
14+
warn: 'warn',
15+
error: 'error',
16+
} as const;
17+
18+
function prefixedLog(prefixType: keyof typeof prefixes, message: string) {
19+
const consoleMethod: keyof typeof LOGGING_METHOD =
20+
prefixType in LOGGING_METHOD
21+
? LOGGING_METHOD[prefixType as keyof typeof LOGGING_METHOD]
22+
: 'log';
23+
24+
const prefix = prefixes[prefixType];
25+
26+
// eslint-disable-next-line no-console
27+
console[consoleMethod](` ${prefix}`, message, suffix);
28+
}
29+
30+
export function error(message: string) {
31+
prefixedLog('error', message);
32+
}
33+
34+
export function warn(message: string) {
35+
prefixedLog('warn', message);
36+
}
37+
38+
export function info(message: string) {
39+
prefixedLog('info', message);
40+
}
41+
42+
export function event(message: string) {
43+
prefixedLog('event', message);
44+
}

src/lib/picocolors.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/* eslint-disable no-bitwise */
2+
/* eslint-disable prefer-template */
3+
4+
// ISC License
5+
6+
// Copyright (c) 2021 Alexey Raspopov, Kostiantyn Denysov, Anton Verinov
7+
8+
// Permission to use, copy, modify, and/or distribute this software for any
9+
// purpose with or without fee is hereby granted, provided that the above
10+
// copyright notice and this permission notice appear in all copies.
11+
12+
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13+
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14+
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15+
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16+
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17+
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18+
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19+
//
20+
// https://github.com/alexeyraspopov/picocolors/blob/b6261487e7b81aaab2440e397a356732cad9e342/picocolors.js#L1
21+
22+
const { env, stdout } = globalThis?.process ?? {};
23+
24+
const enabled =
25+
env &&
26+
!env.NO_COLOR &&
27+
(env.FORCE_COLOR || (stdout?.isTTY && !env.CI && env.TERM !== 'dumb'));
28+
29+
const replaceClose = (
30+
str: string,
31+
close: string,
32+
replace: string,
33+
index: number,
34+
): string => {
35+
const start = str.substring(0, index) + replace;
36+
const end = str.substring(index + close.length);
37+
const nextIndex = end.indexOf(close);
38+
return ~nextIndex
39+
? start + replaceClose(end, close, replace, nextIndex)
40+
: start + end;
41+
};
42+
43+
const formatter =
44+
(open: string, close: string, replace = open) =>
45+
(input: string) => {
46+
const string = '' + input;
47+
const index = string.indexOf(close, open.length);
48+
return ~index
49+
? open + replaceClose(string, close, replace, index) + close
50+
: open + string + close;
51+
};
52+
53+
export const reset = enabled ? (s: string) => `\x1b[0m${s}\x1b[0m` : String;
54+
export const bold = enabled
55+
? formatter('\x1b[1m', '\x1b[22m', '\x1b[22m\x1b[1m')
56+
: String;
57+
export const dim = enabled
58+
? formatter('\x1b[2m', '\x1b[22m', '\x1b[22m\x1b[2m')
59+
: String;
60+
export const italic = enabled ? formatter('\x1b[3m', '\x1b[23m') : String;
61+
export const underline = enabled ? formatter('\x1b[4m', '\x1b[24m') : String;
62+
export const inverse = enabled ? formatter('\x1b[7m', '\x1b[27m') : String;
63+
export const hidden = enabled ? formatter('\x1b[8m', '\x1b[28m') : String;
64+
export const strikethrough = enabled
65+
? formatter('\x1b[9m', '\x1b[29m')
66+
: String;
67+
export const black = enabled ? formatter('\x1b[30m', '\x1b[39m') : String;
68+
export const red = enabled ? formatter('\x1b[31m', '\x1b[39m') : String;
69+
export const green = enabled ? formatter('\x1b[32m', '\x1b[39m') : String;
70+
export const yellow = enabled ? formatter('\x1b[33m', '\x1b[39m') : String;
71+
export const blue = enabled ? formatter('\x1b[34m', '\x1b[39m') : String;
72+
export const magenta = enabled ? formatter('\x1b[35m', '\x1b[39m') : String;
73+
export const purple = enabled
74+
? formatter('\x1b[38;2;173;127;168m', '\x1b[39m')
75+
: String;
76+
export const cyan = enabled ? formatter('\x1b[36m', '\x1b[39m') : String;
77+
export const white = enabled ? formatter('\x1b[37m', '\x1b[39m') : String;
78+
export const gray = enabled ? formatter('\x1b[90m', '\x1b[39m') : String;
79+
export const bgBlack = enabled ? formatter('\x1b[40m', '\x1b[49m') : String;
80+
export const bgRed = enabled ? formatter('\x1b[41m', '\x1b[49m') : String;
81+
export const bgGreen = enabled ? formatter('\x1b[42m', '\x1b[49m') : String;
82+
export const bgYellow = enabled ? formatter('\x1b[43m', '\x1b[49m') : String;
83+
export const bgBlue = enabled ? formatter('\x1b[44m', '\x1b[49m') : String;
84+
export const bgMagenta = enabled ? formatter('\x1b[45m', '\x1b[49m') : String;
85+
export const bgCyan = enabled ? formatter('\x1b[46m', '\x1b[49m') : String;
86+
export const bgWhite = enabled ? formatter('\x1b[47m', '\x1b[49m') : String;

src/utils/make-env-public.spec.ts

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
import { makeEnvPublic } from './make-env-public';
22

3-
const warnSpy = jest.spyOn(console, 'warn');
4-
const infoSpy = jest.spyOn(console, 'info');
3+
const warnMock = jest.fn();
4+
const eventMock = jest.fn();
55

6-
beforeAll(() => {
7-
warnSpy.mockImplementation();
8-
infoSpy.mockImplementation();
9-
});
10-
11-
afterAll(() => {
12-
warnSpy.mockRestore();
13-
infoSpy.mockRestore();
14-
});
6+
jest.mock('../helpers/log', () => ({
7+
warn: (...args: unknown[]) => warnMock(...args),
8+
event: (...args: unknown[]) => eventMock(...args),
9+
}));
1510

1611
describe('makeEnvPublic()', () => {
1712
afterEach(() => {
13+
jest.clearAllMocks();
14+
1815
delete process.env.FOO;
1916
delete process.env.BAR;
2017
delete process.env.BAZ;
@@ -57,24 +54,24 @@ describe('makeEnvPublic()', () => {
5754
makeEnvPublic('FOO');
5855
makeEnvPublic(['BAR', 'BAZ']);
5956

60-
expect(infoSpy).toHaveBeenCalledWith(
61-
`prefixed environment variable 'FOO'.`,
57+
expect(eventMock).toHaveBeenCalledWith(
58+
`Prefixed environment variable 'FOO'`,
6259
);
6360

64-
expect(infoSpy).toHaveBeenCalledWith(
65-
`prefixed environment variable 'BAR'.`,
61+
expect(eventMock).toHaveBeenCalledWith(
62+
`Prefixed environment variable 'BAR'`,
6663
);
6764

68-
expect(infoSpy).toHaveBeenCalledWith(
69-
`prefixed environment variable 'BAZ'.`,
65+
expect(eventMock).toHaveBeenCalledWith(
66+
`Prefixed environment variable 'BAZ'`,
7067
);
7168
});
7269

7370
it('should warn when prefixing a variable that is not available in process.env', () => {
7471
makeEnvPublic('FOO');
7572

76-
expect(warnSpy).toHaveBeenCalledWith(
77-
`skipped prefixing environment variable 'FOO'. Variable not in process.env.`,
73+
expect(warnMock).toHaveBeenCalledWith(
74+
`Skipped prefixing environment variable 'FOO'. Variable not in process.env`,
7875
);
7976
});
8077

@@ -83,8 +80,8 @@ describe('makeEnvPublic()', () => {
8380

8481
makeEnvPublic('NEXT_PUBLIC_FOO');
8582

86-
expect(warnSpy).toHaveBeenCalledWith(
87-
`environment variable 'NEXT_PUBLIC_FOO' is already public.`,
83+
expect(warnMock).toHaveBeenCalledWith(
84+
`Environment variable 'NEXT_PUBLIC_FOO' is already public`,
8885
);
8986
});
9087
});

src/utils/make-env-public.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
1+
import { event, warn } from '../helpers/log';
2+
13
function prefixKey(key: string) {
24
// Check if key is available in process.env.
35
if (!process.env[key]) {
4-
// eslint-disable-next-line no-console
5-
console.warn(
6-
`skipped prefixing environment variable '${key}'. Variable not in process.env.`,
6+
warn(
7+
`Skipped prefixing environment variable '${key}'. Variable not in process.env`,
78
);
89

910
return;
1011
}
1112

1213
// Check if key is already public.
1314
if (/^NEXT_PUBLIC_/i.test(key)) {
14-
// eslint-disable-next-line no-console
15-
console.warn(`environment variable '${key}' is already public.`);
15+
warn(`Environment variable '${key}' is already public`);
1616
}
1717

1818
const prefixedKey = `NEXT_PUBLIC_${key}`;
1919

2020
process.env[prefixedKey] = process.env[key];
2121

2222
// eslint-disable-next-line no-console
23-
console.info(`prefixed environment variable '${key}'.`);
23+
event(`Prefixed environment variable '${key}'`);
2424
}
2525

2626
/**

0 commit comments

Comments
 (0)