From 4e81c6fc295ad7664704ee357c678dd7a6223959 Mon Sep 17 00:00:00 2001 From: Spencer Tuft Date: Fri, 20 Sep 2024 15:19:03 -0600 Subject: [PATCH 1/4] chore: support esm syntax --- src/util.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/util.ts b/src/util.ts index 1876265..154b92c 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,5 +1,9 @@ +import { createRequire } from 'node:module' import Pino from 'pino' +// @ts-ignore +const nodeRequire = createRequire(import.meta.url) + const ENV_LEVELS: Record = { production: 'info', test: 'silent', @@ -16,7 +20,7 @@ export function isProduction (): boolean { export function isInstalled (name: string): boolean { try { - return require(name) != null + return nodeRequire(name) !== null } catch (e) { if (isRecord(e) && hasProperty(e, 'code') && e.code === 'MODULE_NOT_FOUND') { return false From 08f3f7bd23a39f0382cb8c98563711a63c6f088f Mon Sep 17 00:00:00 2001 From: Spencer Tuft Date: Fri, 20 Sep 2024 15:24:09 -0600 Subject: [PATCH 2/4] 1.0.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 051fe43..ed2c6c0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@byu-oit/logger", - "version": "1.0.1", + "version": "1.0.2", "description": "Default configuration for pino logger", "engines": { "node": ">=18" From 41fbdec3956d00c21fa44573861bd727a8eb9add Mon Sep 17 00:00:00 2001 From: rhettjay Date: Wed, 9 Oct 2024 01:01:07 -0600 Subject: [PATCH 3/4] refactor: cleanup and migrate test runner --- package.json | 30 ++++------- test/production.spec.ts | 112 ++++++++++++++++++++++------------------ test/test.spec.ts | 41 +++++++-------- 3 files changed, 90 insertions(+), 93 deletions(-) diff --git a/package.json b/package.json index ed2c6c0..896df4c 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "@byu-oit/logger", - "version": "1.0.2", + "version": "2.0.0", "description": "Default configuration for pino logger", "engines": { - "node": ">=18" + "node": ">=20" }, "contributors": [ { @@ -35,14 +35,14 @@ "dist" ], "scripts": { - "build": "rimraf dist && concurrently \"npm:build:*\" -c cyanBright", + "build": "npm run clean && concurrently \"npm:build:*\" -c cyanBright", "build:cjs": "tsc -p tsconfig.cjs.json && echo-cli \"{\\\"type\\\": \\\"commonjs\\\"}\" > dist/cjs/package.json", "build:esm": "tsc -p tsconfig.json && echo-cli \"{\\\"type\\\": \\\"module\\\"}\" > dist/esm/package.json", "clean": "rimraf dist", - "coverage": "c8 ava", + "coverage": "c8 npm run test", "lint": "npx ts-standard | snazzy", "lint:fix": "npx ts-standard --fix | snazzy", - "test": "ava", + "test": "node --import tsimp --test test/*.spec.ts", "prepublishOnly": "npm run build" }, "author": "Brigham Young University - Office of Information Technology", @@ -60,32 +60,20 @@ "pino-http": "^10.2.0" }, "devDependencies": { - "@tsconfig/node18": "^18.2.4", - "@types/node": "^18.19.42", - "@types/sinon": "^17.0.3", - "ava": "^6.1.3", + "@tsconfig/node-lts": "^20.1.3", + "@tsconfig/node20": "^20.1.4", + "@types/node": "^22.7.5", "c8": "^10.1.2", "concurrently": "^8.2.2", "echo-cli": "^2.0.0", "pino-pretty": "^11.2.2", "rimraf": "^6.0.1", - "sinon": "^18.0.0", "snazzy": "^9.0.0", + "ts-standard": "^12.0.2", "tsimp": "^2.0.11", "typescript": "^5.5.4" }, "optionalDependencies": { "pino-pretty": ">=7" - }, - "lint-staged": { - "*.ts": "npm run lint:fix" - }, - "ava": { - "extensions": { - "ts": "module" - }, - "nodeArguments": [ - "--import=tsimp" - ] } } diff --git a/test/production.spec.ts b/test/production.spec.ts index a3304a3..01d99b9 100644 --- a/test/production.spec.ts +++ b/test/production.spec.ts @@ -1,82 +1,94 @@ -import ava, { TestFn } from 'ava' -import sinon, { SinonFakeTimers } from 'sinon' +import { describe, mock, test } from 'node:test' +import * as assert from 'node:assert' import { Logger } from 'pino' import { ByuLogger } from '../src/logger.js' interface Context { logged: string logger: Logger - clock: SinonFakeTimers now: Date } -const test = ava as TestFn +const ctx: Context = { + logged: '', + logger: ByuLogger(), + now: new Date(1000) +} test.before((t) => { /* Stub the date time */ - const jan1st = new Date(2021, 0, 1) - t.context.now = jan1st - t.context.clock = sinon.useFakeTimers(jan1st.getTime()) + mock.timers.enable({ apis: ['Date'] }) + mock.timers.setTime(1000) }) test.beforeEach((t) => { /* Capture the stdout pipe */ process.stdout.write = (buffer: string) => { - t.context.logged += buffer + ctx.logged += buffer return true } - process.env.NODE_ENV = 'production' - t.context.logged = '' - t.context.logger = ByuLogger() + process.env.NODE_ENV = 'test' + ctx.logged = '' + ctx.logger = ByuLogger() + ctx.now = new Date(1000) }) test.after((t) => { - t.context.clock.restore() + mock.timers.reset() }) -test.serial('default logger should default to info level', (t) => { - t.context.logger.debug('debug does not work') - - t.is(t.context.logger.level, 'info') - t.is(t.context.logged, '') // no logs should have happened +void describe('Default logger level is info', () => { + void test('default logger should default to info level', (context) => { + try { + ctx.logger.debug('debug does not work') + assert.equal(ctx.logger.level, 'info') + assert.equal(ctx.logged, '') // no logs should have happened + } catch (e) { + console.log(e) + assert.fail('Logger level not info') + } + }) }) -test.serial('default logger displays logs in JSON format', (t) => { - t.context.logger.info('json works') - - try { - const parsedLog = JSON.parse(t.context.logged) - t.truthy(parsedLog.message) - t.truthy(parsedLog.level) - t.truthy(parsedLog.time) - } catch (e) { - t.log(e) - t.fail('The log format should be stringified JSON but parsing failed. See the logged error for details.') - } +void describe('Default logger in JSON', () => { + void test('default logger displays logs in JSON format', (context) => { + ctx.logger.info('json works') + try { + const parsedLog = JSON.parse(ctx.logged) + assert.ok(parsedLog.message) + assert.ok(parsedLog.level) + assert.ok(parsedLog.time) + } catch (e) { + console.log(e) + assert.fail('No json output') + } + }) }) -test.serial('default logger should display info logs', (t) => { - t.context.logger.info('info works') - - try { - const parsedLog = JSON.parse(t.context.logged) - t.is(parsedLog.message, 'info works') - t.is(parsedLog.level, 'info') - } catch (e) { - t.log(e) - t.fail('The log format should be stringified JSON but parsing failed. See the logged error for details.') - } +void describe('Default logger contains "info"', () => { + void test('default logger should display info logs', (context) => { + ctx.logger.info('info works') + try { + const parsedLog = JSON.parse(ctx.logged) + assert.equal(parsedLog.message, 'info works') + assert.equal(parsedLog.level, 'info') + } catch (e) { + console.log(e) + assert.fail('Incorrect logger level') + } + }) }) -test.serial('default logger displays logs with epoch datetime format', (t) => { - t.context.logger.info('iso date works') - - try { - const parsedLog = JSON.parse(t.context.logged) - t.is(parsedLog.time, t.context.now.getTime()) - } catch (e) { - t.log(e) - t.fail('The log format should be stringified JSON but parsing failed. See the logged error for details.') - } +void describe('Display correct date format', () => { + void test('default logger displays logs with epoch datetime format', (context) => { + ctx.logger.info('iso date works') + try { + const parsedLog = JSON.parse(ctx.logged) + assert.equal(parsedLog.time, Date.now()) + } catch (e) { + console.log(e) + assert.fail('Incorrect date format logged') + } + }) }) diff --git a/test/test.spec.ts b/test/test.spec.ts index 8c98003..2ec73ff 100644 --- a/test/test.spec.ts +++ b/test/test.spec.ts @@ -1,49 +1,46 @@ -import ava, { TestFn } from 'ava' -import sinon, { SinonFakeTimers } from 'sinon' +import { test, mock } from 'node:test' +import assert from 'node:assert' import { Logger } from 'pino' import { ByuLogger } from '../src/logger.js' interface Context { logged: string logger: Logger - clock: SinonFakeTimers - now: Date } -const test = ava as TestFn +const context: Context = { + logged: '', + logger: ByuLogger() +} test.before((t) => { /* Stub the date time */ - const jan1st = new Date(2021, 0, 1) - t.context.now = jan1st - t.context.clock = sinon.useFakeTimers(jan1st.getTime()) + mock.timers.enable({ apis: ['Date'] }) + mock.timers.setTime(1000) }) test.beforeEach((t) => { /* Capture the stdout pipe */ process.stdout.write = (buffer: string) => { - t.context.logged += buffer + context.logged += buffer return true } - process.env.NODE_ENV = 'test' - t.context.logged = '' - t.context.logger = ByuLogger() + context.logged = '' + context.logger = ByuLogger() }) test.after((t) => { - t.context.clock.restore() + mock.timers.reset() }) -test.serial('default logger should default to silent level', (t) => { - t.context.logger.debug('debug does not work') - - t.is(t.context.logger.level, 'silent') - t.is(t.context.logged, '') // no logs should have happened +void test('default logger should default to silent level', (t) => { + context.logger.debug('debug does not work') + assert.equal(context.logger.level, 'silent') + assert.equal(context.logged, '') // no logs should have happened }) -test.serial('default logger should not display logs', (t) => { - t.context.logger.info('info works') - - t.is(t.context.logged, '') +void test('default logger should not display logs', (t) => { + context.logger.info('info works') + assert.equal(context.logged, '') }) From 60ebf25fc2eaa2f29c15636c76b6b04ad89a7497 Mon Sep 17 00:00:00 2001 From: rhettjay Date: Wed, 9 Oct 2024 01:01:30 -0600 Subject: [PATCH 4/4] refactor: remove unnecessary checks --- src/logger.ts | 6 +++--- src/util.ts | 15 --------------- tsconfig.cjs.json | 9 +++++++-- tsconfig.json | 8 +++++++- 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/logger.ts b/src/logger.ts index 98504ba..33ec299 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,6 +1,6 @@ import deepmerge from 'deepmerge' import { Logger, LoggerOptions, pino } from 'pino' -import { getLevel, isInstalled, isProduction } from './util.js' +import { getLevel, isProduction } from './util.js' export function ByuLogger (options?: LoggerOptions): Logger { const defaultOptions: LoggerOptions = { @@ -18,10 +18,10 @@ export function ByuLogger (options?: LoggerOptions): Logger { // if in local environment and not running tests try to pretty print logs // jest and pretty-print don't get along (causes open handles and sometimes doesn't close), // so we'll default to not include pretty-print if running tests - ...!isProduction() && process.env.NODE_ENV !== 'test' && isInstalled('pino-pretty') && { + ...!isProduction() && process.env.NODE_ENV !== 'test' && { transport: { target: 'pino-pretty', - options: { translateTime: 'UTC:yyyy-mm-dd\'T\'HH:MM:ss.l\'Z\'' } + options: { sync: true, translateTime: 'UTC:yyyy-mm-dd\'T\'HH:MM:ss.l\'Z\'' } } } } diff --git a/src/util.ts b/src/util.ts index 154b92c..750cf39 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,9 +1,5 @@ -import { createRequire } from 'node:module' import Pino from 'pino' -// @ts-ignore -const nodeRequire = createRequire(import.meta.url) - const ENV_LEVELS: Record = { production: 'info', test: 'silent', @@ -18,17 +14,6 @@ export function isProduction (): boolean { return process.env.NODE_ENV === 'production' } -export function isInstalled (name: string): boolean { - try { - return nodeRequire(name) !== null - } catch (e) { - if (isRecord(e) && hasProperty(e, 'code') && e.code === 'MODULE_NOT_FOUND') { - return false - } - throw e - } -} - export function isRecord (value: unknown): value is Record { return typeof value === 'object' && value !== null } diff --git a/tsconfig.cjs.json b/tsconfig.cjs.json index d79581a..da26bd4 100644 --- a/tsconfig.cjs.json +++ b/tsconfig.cjs.json @@ -1,12 +1,17 @@ { - "extends": "@tsconfig/node18/tsconfig.json", + "extends": "@tsconfig/node-lts/tsconfig.json", "compilerOptions": { + "target": "es2022", "declaration": true, "declarationMap": true, "module": "CommonJS", "moduleResolution": "node", "declarationDir": "dist/cjs", - "outDir": "dist/cjs" + "outDir": "dist/cjs", + "esModuleInterop": true, + "importHelpers": true, + "strict": true, + "skipLibCheck": true }, "include": [ "src" diff --git a/tsconfig.json b/tsconfig.json index c624ec8..65c7d5f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,12 @@ { - "extends": "@tsconfig/node18/tsconfig.json", "compilerOptions": { + "lib": ["es2023"], + "module": "node16", + "target": "es2022", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "moduleResolution": "node16", "declaration": true, "declarationDir": "dist/esm", "outDir": "dist/esm"