Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add unlimited option #78

Merged
merged 6 commits into from
Apr 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://standardjs.com/)

A small utility for generating consistent warning objects across your codebase.
It also exposes a utility for emitting those warnings, guaranteeing that they are issued only once.
It also exposes a utility for emitting those warnings, guaranteeing that they are issued only once (unless configured otherwise).

This module is used by the [Fastify](https://fastify.io) framework and it was called `fastify-warning` prior to version 1.0.0.

Expand All @@ -26,12 +26,14 @@ const warning = require('process-warning')()
#### Methods

```
warning.create(name, code, message)
warning.create(name, code, message[, options])
```

- `name` (`string`, required) - The error name, you can access it later with `error.name`. For consistency, we recommend prefixing module error names with `{YourModule}Warning`
- `code` (`string`, required) - The warning code, you can access it later with `error.code`. For consistency, we recommend prefixing plugin error codes with `{ThreeLetterModuleName}_`, e.g. `FST_`. NOTE: codes should be all uppercase.
- `message` (`string`, required) - The warning message. You can also use interpolated strings for formatting the message.
- `options` (`object`, optional) - Optional options with the following properties:
- `unlimited` (`boolean`, optional) - Should the warning be emitted more than once? Defaults to `false`.

The utility also contains an `emit` function that you can use for emitting the warnings you have previously created by passing their respective code. A warning is guaranteed to be emitted only once.

Expand Down Expand Up @@ -64,6 +66,14 @@ warning.emit('FST_ERROR_CODE', 'world')
console.log(warning.emitted.get('FST_ERROR_CODE')) // true
```

How to use an unlimited warning:
```js
const warning = require('process-warning')()
warning.create('FastifyWarning', 'FST_ERROR_CODE', 'Hello %s', { unlimited: true })
warning.emit('FST_ERROR_CODE', 'world') // will be emitted
warning.emit('FST_ERROR_CODE', 'world') // will be emitted again
```

## License

Licensed under [MIT](./LICENSE).
9 changes: 6 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ const { format } = require('util')
function processWarning () {
const codes = {}
const emitted = new Map()
const opts = Object.create(null)

function create (name, code, message) {
function create (name, code, message, { unlimited = false } = {}) {
if (!name) throw new Error('Warning name must not be empty')
if (!code) throw new Error('Warning code must not be empty')
if (!message) throw new Error('Warning message must not be empty')
if (typeof unlimited !== 'boolean') throw new Error('Warning opts.unlimited must be a boolean')

code = code.toUpperCase()

Expand Down Expand Up @@ -37,14 +39,15 @@ function processWarning () {
}
}

emitted.set(code, false)
Object.assign(opts, { unlimited })
emitted.set(code, unlimited)
codes[code] = buildWarnOpts

return codes[code]
}

function emit (code, a, b, c) {
if (emitted.get(code) === true) return
if (emitted.get(code) === true && opts.unlimited === false) return
if (codes[code] === undefined) throw new Error(`The code '${code}' does not exist`)
emitted.set(code, true)

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"types": "types/index.d.ts",
"scripts": {
"lint": "standard",
"lint:fix": "standard --fix",
"test": "npm run test:unit && npm run test:jest && npm run test:typescript",
"test:jest": "jest jest.test.js",
"test:unit": "tap",
Expand Down
34 changes: 34 additions & 0 deletions test/emit-unlimited.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use strict'

const test = require('tap').test
const build = require('..')

test('emit should emit a given code unlimited times', t => {
t.plan(50)

const { create, emit, emitted } = build()

let runs = 0
const expectedRun = []
const times = 10

process.on('warning', onWarning)
function onWarning (warning) {
t.equal(warning.name, 'FastifyDeprecation')
t.equal(warning.code, 'CODE')
t.equal(warning.message, 'Hello world')
t.ok(emitted.get('CODE'))
t.equal(runs++, expectedRun.shift())
}

create('FastifyDeprecation', 'CODE', 'Hello world', { unlimited: true })

for (let i = 0; i < times; i++) {
expectedRun.push(i)
emit('CODE')
}
setImmediate(() => {
process.removeListener('warning', onWarning)
t.end()
})
})
8 changes: 8 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,11 @@ test('Cannot reuse the same code more than once', t => {

t.throws(() => create('FastifyWarning', 'CODE', 'Not available'), new Error("The code 'CODE' already exist"))
})

test('Cannot set unlimited other than boolean', t => {
t.plan(1)

const { create } = build()

t.throws(() => create('FastifyWarning', 'CODE', 'Msg', { unlimited: 42 }), new Error('Warning opts.unlimited must be a boolean'))
})
6 changes: 5 additions & 1 deletion types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ type ProcessWarning = () => processWarning.Warning

declare namespace processWarning {
export interface Warning {
create(name: string, code: string, message: string): BuildWarnOptsFn,
create(name: string, code: string, message: string, opts?: ProcessWarningOptions): BuildWarnOptsFn,
emit(cod: string, a?: any, b?: any, c?: any): void,
emitted: Map<string, boolean>
}

export type BuildWarnOptsFn = (a?: any, b?: any, c?: any) => WarnOpts

export type ProcessWarningOptions = {
unlimited?: boolean,
}

export interface WarnOpts {
code: string;
name: string;
Expand Down
3 changes: 3 additions & 0 deletions types/index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ expectType<string>(opts.name)

expectType<void>(warning.emit('CODE'))
expectType<Map<string, boolean>>(warning.emitted)

const buildWarnUnlimited = warning.create('FastifyWarning', 'CODE', 'message', { unlimited: true })
expectType<BuildWarnOptsFn>(buildWarnUnlimited)