@nanostores/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.
- package/README.md +15 -1
- package/build-logger/index.d.ts +126 -0
- package/build-logger/index.js +94 -0
- package/creator-logger/index.d.ts +25 -0
- package/creator-logger/index.js +60 -0
- package/index.d.ts +3 -0
- package/index.js +3 -0
- package/logger/index.d.ts +4 -36
- package/logger/index.js +111 -231
- package/package.json +2 -2
- package/printer/index.d.ts +55 -0
- package/printer/index.js +78 -0
package/README.md
CHANGED
|
@@ -3,10 +3,24 @@
|
|
|
3
3
|
<img align="right" width="92" height="92" title="Nano Stores logo"
|
|
4
4
|
src="https://nanostores.github.io/nanostores/logo.svg">
|
|
5
5
|
|
|
6
|
-
Logger of lifecycles,
|
|
6
|
+
Logger of lifecycles, changes and actions for **[Nano Stores]**,
|
|
7
7
|
a tiny state manager with many atomic tree-shakable stores.
|
|
8
8
|
|
|
9
|
+
* **Clean.** All messages are stacked in compact, collapsible nested groups.
|
|
10
|
+
* **Descriptive.** Detailed descriptions with a clear comparison of the old and new values.
|
|
11
|
+
* **Pretty designed.** Compact logo and color badges for quick reading.
|
|
12
|
+
* **Flexible.** Ability to disable and filter certain types of messages.
|
|
13
|
+
* **Supports all types of stores**: Atom, Map and Deep Map.
|
|
14
|
+
|
|
9
15
|
[Nano Stores]: https://github.com/nanostores/nanostores/
|
|
10
16
|
|
|
17
|
+
<p align="center">
|
|
18
|
+
<picture>
|
|
19
|
+
<source media="(prefers-color-scheme: dark)" srcset="./img/dark.png">
|
|
20
|
+
<source media="(prefers-color-scheme: light)" srcset="./img/light.png">
|
|
21
|
+
<img alt="Nano Stores Logger" src="./img/light.png">
|
|
22
|
+
</picture>
|
|
23
|
+
</p>
|
|
24
|
+
|
|
11
25
|
## Docs
|
|
12
26
|
Read full docs **[here](https://github.com/nanostores/logger#readme)**.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { AnyStore, Store, StoreValue } from 'nanostores'
|
|
2
|
+
|
|
3
|
+
interface LoggerOptionsMessages {
|
|
4
|
+
/**
|
|
5
|
+
* Disable action logs.
|
|
6
|
+
*/
|
|
7
|
+
action?: boolean
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Disable change logs.
|
|
11
|
+
*/
|
|
12
|
+
change?: boolean
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Disable mount logs.
|
|
16
|
+
*/
|
|
17
|
+
mount?: boolean
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Disable unmount logs.
|
|
21
|
+
*/
|
|
22
|
+
unmount?: boolean
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface LoggerOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Disable logs of actions with a specific name.
|
|
28
|
+
*/
|
|
29
|
+
ignoreActions?: string[]
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Disable specific types of logs.
|
|
33
|
+
*/
|
|
34
|
+
messages?: LoggerOptionsMessages
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface EventPayloadBase {
|
|
38
|
+
storeName: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface EventChangePayload extends EventPayloadBase {
|
|
42
|
+
actionId?: number
|
|
43
|
+
actionName?: string
|
|
44
|
+
changed?: keyof StoreValue<Store>
|
|
45
|
+
newValue: any
|
|
46
|
+
oldValue?: any
|
|
47
|
+
valueMessage?: string
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface EventActionPayload extends EventPayloadBase {
|
|
51
|
+
actionId: number
|
|
52
|
+
actionName: string
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface EventActionStartPayload extends EventActionPayload {
|
|
56
|
+
args: any[]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface EventActionErrorPayload extends EventActionPayload {
|
|
60
|
+
error: Error
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface BuildLoggerEvents {
|
|
64
|
+
action?: {
|
|
65
|
+
end?: (payload: EventActionPayload) => void
|
|
66
|
+
error?: (payload: EventActionErrorPayload) => void
|
|
67
|
+
start?: (payload: EventActionStartPayload) => void
|
|
68
|
+
}
|
|
69
|
+
change?: (payload: EventChangePayload) => void
|
|
70
|
+
mount?: (payload: EventPayloadBase) => void
|
|
71
|
+
unmount?: (payload: EventPayloadBase) => void
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Builds logger for Nano Stores.
|
|
76
|
+
*
|
|
77
|
+
* ```js
|
|
78
|
+
* import { buildLogger } from '@nanostores/logger'
|
|
79
|
+
* import { $profile } from './stores/index.js'
|
|
80
|
+
*
|
|
81
|
+
* let destroy = buildLogger($profile, 'Profile', {
|
|
82
|
+
* mount: ({ storeName }) => {
|
|
83
|
+
* console.log(`${storeName} was mounted`)
|
|
84
|
+
* },
|
|
85
|
+
*
|
|
86
|
+
* unmount: ({ storeName }) => {
|
|
87
|
+
* console.log(`${storeName} was unmounted`)
|
|
88
|
+
* },
|
|
89
|
+
*
|
|
90
|
+
* change: ({ actionName, changed, newValue, oldValue, valueMessage }) => {
|
|
91
|
+
* let message = `${storeName} was changed`
|
|
92
|
+
* if (changed) message += `in the ${changed} key`
|
|
93
|
+
* if (oldValue) message += `from ${oldValue}`
|
|
94
|
+
* message += `to ${newValue}`
|
|
95
|
+
* if (actionName) message += `by action ${actionName}`
|
|
96
|
+
* console.log(message, valueMessage)
|
|
97
|
+
* },
|
|
98
|
+
*
|
|
99
|
+
* action: {
|
|
100
|
+
* start: ({ actionName, args }) => {
|
|
101
|
+
* let message = `${actionName} was started`
|
|
102
|
+
* if (args.length) message += 'with arguments'
|
|
103
|
+
* console.log(message, args)
|
|
104
|
+
* },
|
|
105
|
+
*
|
|
106
|
+
* error: ({ actionName, error }) => {
|
|
107
|
+
* console.log(`${actionName} was failed`, error)
|
|
108
|
+
* },
|
|
109
|
+
*
|
|
110
|
+
* end: ({ actionName }) => {
|
|
111
|
+
* console.log(`${actionName} was ended`)
|
|
112
|
+
* }
|
|
113
|
+
* }
|
|
114
|
+
* ```
|
|
115
|
+
*
|
|
116
|
+
* @param store Any Nano Store
|
|
117
|
+
* @param storeName Store name.
|
|
118
|
+
* @param events Events to log.
|
|
119
|
+
* @param opts Logger options.
|
|
120
|
+
*/
|
|
121
|
+
export function buildLogger(
|
|
122
|
+
store: AnyStore,
|
|
123
|
+
storeName: string,
|
|
124
|
+
events: BuildLoggerEvents,
|
|
125
|
+
opts?: LoggerOptions
|
|
126
|
+
): () => void
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import {
|
|
2
|
+
actionId,
|
|
3
|
+
lastAction,
|
|
4
|
+
onAction,
|
|
5
|
+
onMount,
|
|
6
|
+
onNotify,
|
|
7
|
+
onSet
|
|
8
|
+
} from 'nanostores'
|
|
9
|
+
|
|
10
|
+
const isAtom = store => store.setKey === undefined
|
|
11
|
+
const isDeepMapKey = key => /.+(\..+|\[\d+\.*])/.test(key)
|
|
12
|
+
|
|
13
|
+
function handleMount(store, storeName, messages, events) {
|
|
14
|
+
return onMount(store, () => {
|
|
15
|
+
if (messages.mount !== false) {
|
|
16
|
+
events.mount({ storeName })
|
|
17
|
+
}
|
|
18
|
+
return () => {
|
|
19
|
+
if (messages.unmount !== false) {
|
|
20
|
+
events.unmount({ storeName })
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function handleAction(store, storeName, ignoreActions, events) {
|
|
27
|
+
return onAction(store, ({ actionName, args, id, onEnd, onError }) => {
|
|
28
|
+
if (ignoreActions && ignoreActions.includes(actionName)) return
|
|
29
|
+
|
|
30
|
+
events.action.start({ actionId: id, actionName, args, storeName })
|
|
31
|
+
|
|
32
|
+
onError(({ error }) => {
|
|
33
|
+
events.action.error({ actionId: id, actionName, error, storeName })
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
onEnd(() => {
|
|
37
|
+
events.action.end({ actionId: id, actionName, storeName })
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function handleSet(store, storeName, messages, ignoreActions, events) {
|
|
43
|
+
return onSet(store, ({ changed }) => {
|
|
44
|
+
let currentActionId = store[actionId]
|
|
45
|
+
let currentActionName = store[lastAction]
|
|
46
|
+
|
|
47
|
+
if (messages.action === false && currentActionId) return
|
|
48
|
+
if (ignoreActions && ignoreActions.includes(currentActionName)) return
|
|
49
|
+
|
|
50
|
+
let oldValue = isAtom(store) ? store.value : { ...store.value }
|
|
51
|
+
oldValue = isDeepMapKey(changed) ? structuredClone(oldValue) : oldValue
|
|
52
|
+
let unbindNotify = onNotify(store, () => {
|
|
53
|
+
let newValue = store.value
|
|
54
|
+
let valueMessage
|
|
55
|
+
if (changed && !isDeepMapKey(changed)) {
|
|
56
|
+
valueMessage = `${oldValue[changed]} → ${newValue[changed]}`
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
events.change({
|
|
60
|
+
actionId: currentActionId,
|
|
61
|
+
actionName: currentActionName,
|
|
62
|
+
changed,
|
|
63
|
+
newValue,
|
|
64
|
+
oldValue,
|
|
65
|
+
storeName,
|
|
66
|
+
valueMessage
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
unbindNotify()
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function buildLogger(store, storeName, events, opts = {}) {
|
|
75
|
+
let ignoreActions = opts.ignoreActions
|
|
76
|
+
let messages = opts.messages || {}
|
|
77
|
+
let unbind = []
|
|
78
|
+
|
|
79
|
+
if (messages.mount !== false || messages.unmount !== false) {
|
|
80
|
+
unbind.push(handleMount(store, storeName, messages, events))
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (messages.action !== false) {
|
|
84
|
+
unbind.push(handleAction(store, storeName, ignoreActions, events))
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (messages.change !== false) {
|
|
88
|
+
unbind.push(handleSet(store, storeName, messages, ignoreActions, events))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return () => {
|
|
92
|
+
for (let i of unbind) i()
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { MapCreator, MapStore } from 'nanostores'
|
|
2
|
+
|
|
3
|
+
import type { LoggerOptions } from '../build-logger/index.js'
|
|
4
|
+
|
|
5
|
+
interface CreatorLoggerOptions extends LoggerOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Custom name getter for stores built with creators.
|
|
8
|
+
*
|
|
9
|
+
* @param creatorName Name of the creator.
|
|
10
|
+
* @param store Store built by the creator.
|
|
11
|
+
* @returns Custom store name.
|
|
12
|
+
*/
|
|
13
|
+
nameGetter: (creatorName: string, store: MapStore) => string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Displays Nano Stores events in browser console.
|
|
18
|
+
*
|
|
19
|
+
* @param creators Creators for logging.
|
|
20
|
+
* @param opts Logger options.
|
|
21
|
+
*/
|
|
22
|
+
export function creatorLogger(
|
|
23
|
+
creators: { [key: string]: MapCreator },
|
|
24
|
+
opts?: CreatorLoggerOptions
|
|
25
|
+
): () => void
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { logger } from '../logger/index.js'
|
|
2
|
+
import { log } from '../printer/index.js'
|
|
3
|
+
|
|
4
|
+
function onBuild(creator, listener) {
|
|
5
|
+
let originBuild = creator.build
|
|
6
|
+
creator.build = (...args) => {
|
|
7
|
+
let store = originBuild(...args)
|
|
8
|
+
listener(store)
|
|
9
|
+
return store
|
|
10
|
+
}
|
|
11
|
+
return () => {
|
|
12
|
+
creator.build = originBuild
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function createCreatorLogger(creator, creatorName, opts) {
|
|
17
|
+
let nameGetter = opts.nameGetter
|
|
18
|
+
delete opts.nameGetter
|
|
19
|
+
|
|
20
|
+
let unbind = []
|
|
21
|
+
unbind.push(
|
|
22
|
+
onBuild(creator, store => {
|
|
23
|
+
let storeName = nameGetter(creatorName, store)
|
|
24
|
+
|
|
25
|
+
log({
|
|
26
|
+
logo: true,
|
|
27
|
+
message: [
|
|
28
|
+
['bold', storeName],
|
|
29
|
+
['regular', 'store was built by'],
|
|
30
|
+
['bold', creatorName],
|
|
31
|
+
['regular', 'creator']
|
|
32
|
+
],
|
|
33
|
+
type: 'build'
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
unbind.push(logger({ [storeName]: store }, opts))
|
|
37
|
+
})
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
return () => {
|
|
41
|
+
for (let i of unbind) i()
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const defaultNameGetter = (creatorName, store) => {
|
|
46
|
+
return `${creatorName}:${store.value.id}`
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function creatorLogger(creators, opts = {}) {
|
|
50
|
+
opts = {
|
|
51
|
+
nameGetter: defaultNameGetter,
|
|
52
|
+
...opts
|
|
53
|
+
}
|
|
54
|
+
let unbind = Object.entries(creators).map(([creatorName, creator]) =>
|
|
55
|
+
createCreatorLogger(creator, creatorName, opts)
|
|
56
|
+
)
|
|
57
|
+
return () => {
|
|
58
|
+
for (let i of unbind) i()
|
|
59
|
+
}
|
|
60
|
+
}
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
package/logger/index.d.ts
CHANGED
|
@@ -1,49 +1,17 @@
|
|
|
1
1
|
import type { AnyStore } from 'nanostores'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Disable action logs.
|
|
6
|
-
*/
|
|
7
|
-
action?: boolean
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Disable change logs.
|
|
11
|
-
*/
|
|
12
|
-
change?: boolean
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Disable mount logs.
|
|
16
|
-
*/
|
|
17
|
-
mount?: boolean
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Disable unmount logs.
|
|
21
|
-
*/
|
|
22
|
-
unmount?: boolean
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface LoggerOptions {
|
|
26
|
-
/**
|
|
27
|
-
* Disable action logs with specific name.
|
|
28
|
-
*/
|
|
29
|
-
ignoreActions?: string[]
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Disable specific log types.
|
|
33
|
-
*/
|
|
34
|
-
messages?: LoggerOptionsMessages
|
|
35
|
-
}
|
|
3
|
+
import type { LoggerOptions } from '../build-logger/index.js'
|
|
36
4
|
|
|
37
5
|
/**
|
|
38
|
-
*
|
|
6
|
+
* Displays Nano Stores events in browser console.
|
|
39
7
|
*
|
|
40
8
|
* ```js
|
|
41
9
|
* import { logger } from '@nanostores/logger'
|
|
42
10
|
* import { $profile, $users } from './stores/index.js'
|
|
43
11
|
*
|
|
44
12
|
* let destroy = logger({
|
|
45
|
-
* 'Profile
|
|
46
|
-
* 'Users
|
|
13
|
+
* 'Profile': $profile,
|
|
14
|
+
* 'Users': $users
|
|
47
15
|
* })
|
|
48
16
|
* ```
|
|
49
17
|
*
|
package/logger/index.js
CHANGED
|
@@ -1,253 +1,133 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
lastAction,
|
|
4
|
-
onAction,
|
|
5
|
-
onMount,
|
|
6
|
-
onNotify,
|
|
7
|
-
onSet
|
|
8
|
-
} from 'nanostores'
|
|
1
|
+
import { buildLogger } from '../build-logger/index.js'
|
|
2
|
+
import { group, groupEnd, log } from '../printer/index.js'
|
|
9
3
|
|
|
10
|
-
function
|
|
11
|
-
|
|
12
|
-
padding: 0 5px 2px;
|
|
13
|
-
margin-right: 5px;
|
|
14
|
-
font-weight: 400;
|
|
15
|
-
color: white;
|
|
16
|
-
background-color: ${color};
|
|
17
|
-
`
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function borders(full) {
|
|
21
|
-
return `border-radius: ${full ? '4px' : '0 4px 4px 0'};`
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const STYLES = {
|
|
25
|
-
badges: {
|
|
26
|
-
action: badge('#5351A4'),
|
|
27
|
-
arguments: badge('#429BD7'),
|
|
28
|
-
change: badge('#0E8A00'),
|
|
29
|
-
error: badge('#c21f1f'),
|
|
30
|
-
mount: badge('#1F49E0'),
|
|
31
|
-
new: badge('#4FA574'),
|
|
32
|
-
old: badge('#a44f4f'),
|
|
33
|
-
unmount: badge('#5C5C5C'),
|
|
34
|
-
value: badge('#429BD7')
|
|
35
|
-
},
|
|
36
|
-
bold: 'font-weight: 700',
|
|
37
|
-
logo: `
|
|
38
|
-
padding: 0 5px 2px;
|
|
39
|
-
color: white;
|
|
40
|
-
background-color: black;
|
|
41
|
-
border-radius: 4px 0 0 4px;
|
|
42
|
-
`,
|
|
43
|
-
regular: 'font-weight: 400'
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function createLog({ logo, message, type, value }) {
|
|
47
|
-
let template = ''
|
|
48
|
-
let args = []
|
|
49
|
-
|
|
50
|
-
if (logo) {
|
|
51
|
-
template = `%c𝖓`
|
|
52
|
-
args.push(STYLES.logo)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
template += `%c${type}`
|
|
56
|
-
args.push(STYLES.badges[type] + borders(!logo))
|
|
57
|
-
|
|
58
|
-
if (message) {
|
|
59
|
-
if (Array.isArray(message)) {
|
|
60
|
-
message.forEach(([style, text]) => {
|
|
61
|
-
template += `%c ${text}`
|
|
62
|
-
args.push(STYLES[style])
|
|
63
|
-
})
|
|
64
|
-
} else {
|
|
65
|
-
template += `%c ${message}`
|
|
66
|
-
args.push(STYLES.text)
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (value) {
|
|
71
|
-
args.push(value)
|
|
72
|
-
}
|
|
4
|
+
function createLogger(store, storeName, opts) {
|
|
5
|
+
let queue = {}
|
|
73
6
|
|
|
74
|
-
|
|
7
|
+
return buildLogger(
|
|
8
|
+
store,
|
|
9
|
+
storeName,
|
|
10
|
+
{
|
|
11
|
+
action: {
|
|
12
|
+
end: ({ actionId }) => {
|
|
13
|
+
for (let i of queue[actionId]) i()
|
|
14
|
+
delete queue[actionId]
|
|
15
|
+
groupEnd()
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
error: ({ actionId, actionName, error }) => {
|
|
19
|
+
queue[actionId].push(() =>
|
|
20
|
+
log({
|
|
21
|
+
message: [
|
|
22
|
+
['bold', storeName],
|
|
23
|
+
['regular', 'store handled error in action'],
|
|
24
|
+
['bold', actionName]
|
|
25
|
+
],
|
|
26
|
+
type: 'error',
|
|
27
|
+
value: {
|
|
28
|
+
message: error.message
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
)
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
start: ({ actionId, actionName, args }) => {
|
|
35
|
+
queue[actionId] = []
|
|
36
|
+
|
|
37
|
+
let message = [
|
|
38
|
+
['bold', storeName],
|
|
39
|
+
['regular', 'store was changed by action'],
|
|
40
|
+
['bold', actionName]
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
queue[actionId].push(() =>
|
|
44
|
+
group({
|
|
45
|
+
logo: true,
|
|
46
|
+
message,
|
|
47
|
+
type: 'action'
|
|
48
|
+
})
|
|
49
|
+
)
|
|
50
|
+
if (args.length > 0) {
|
|
51
|
+
message.push(['regular', 'with arguments'])
|
|
52
|
+
queue[actionId].push(() =>
|
|
53
|
+
log({
|
|
54
|
+
type: 'arguments',
|
|
55
|
+
value: args
|
|
56
|
+
})
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
75
61
|
|
|
76
|
-
|
|
77
|
-
|
|
62
|
+
change: ({ actionId, changed, newValue, oldValue, valueMessage }) => {
|
|
63
|
+
let groupLog = {
|
|
64
|
+
logo: typeof actionId === 'undefined',
|
|
65
|
+
message: [
|
|
66
|
+
['bold', storeName],
|
|
67
|
+
['regular', 'store was changed']
|
|
68
|
+
],
|
|
69
|
+
type: 'change'
|
|
70
|
+
}
|
|
71
|
+
if (changed) {
|
|
72
|
+
groupLog.message.push(
|
|
73
|
+
['regular', 'in the'],
|
|
74
|
+
['bold', changed],
|
|
75
|
+
['regular', 'key']
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
let run = () => {
|
|
80
|
+
group(groupLog)
|
|
81
|
+
if (valueMessage) {
|
|
82
|
+
log({
|
|
83
|
+
message: valueMessage,
|
|
84
|
+
type: 'value'
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
log({
|
|
88
|
+
type: 'new',
|
|
89
|
+
value: newValue
|
|
90
|
+
})
|
|
91
|
+
if (oldValue) {
|
|
92
|
+
log({
|
|
93
|
+
type: 'old',
|
|
94
|
+
value: oldValue
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
groupEnd()
|
|
98
|
+
}
|
|
82
99
|
|
|
83
|
-
|
|
84
|
-
|
|
100
|
+
if (actionId) {
|
|
101
|
+
queue[actionId].push(run)
|
|
102
|
+
} else {
|
|
103
|
+
run()
|
|
104
|
+
}
|
|
105
|
+
},
|
|
85
106
|
|
|
86
|
-
|
|
87
|
-
return onMount(store, () => {
|
|
88
|
-
if (messages.mount !== false) {
|
|
89
|
-
log({
|
|
90
|
-
logo: true,
|
|
91
|
-
message: [
|
|
92
|
-
['bold', storeName],
|
|
93
|
-
['regular', 'store was mounted']
|
|
94
|
-
],
|
|
95
|
-
type: 'mount'
|
|
96
|
-
})
|
|
97
|
-
}
|
|
98
|
-
return () => {
|
|
99
|
-
if (messages.unmount !== false) {
|
|
107
|
+
mount: () => {
|
|
100
108
|
log({
|
|
101
109
|
logo: true,
|
|
102
110
|
message: [
|
|
103
111
|
['bold', storeName],
|
|
104
|
-
['regular', 'store was
|
|
112
|
+
['regular', 'store was mounted']
|
|
105
113
|
],
|
|
106
|
-
type: '
|
|
114
|
+
type: 'mount'
|
|
107
115
|
})
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
})
|
|
111
|
-
}
|
|
116
|
+
},
|
|
112
117
|
|
|
113
|
-
|
|
114
|
-
return onAction(store, ({ actionName, args, id, onEnd, onError }) => {
|
|
115
|
-
if (ignoreActions && ignoreActions.includes(actionName)) return
|
|
116
|
-
|
|
117
|
-
queue[id] = []
|
|
118
|
-
|
|
119
|
-
let message = [
|
|
120
|
-
['bold', storeName],
|
|
121
|
-
['regular', 'store was changed by action'],
|
|
122
|
-
['bold', actionName]
|
|
123
|
-
]
|
|
124
|
-
|
|
125
|
-
queue[id].push(() =>
|
|
126
|
-
group({
|
|
127
|
-
logo: true,
|
|
128
|
-
message,
|
|
129
|
-
type: 'action'
|
|
130
|
-
})
|
|
131
|
-
)
|
|
132
|
-
if (args.length > 0) {
|
|
133
|
-
message.push(['regular', 'with arguments'])
|
|
134
|
-
queue[id].push(() =>
|
|
135
|
-
log({
|
|
136
|
-
type: 'arguments',
|
|
137
|
-
value: args
|
|
138
|
-
})
|
|
139
|
-
)
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
onError(({ error }) => {
|
|
143
|
-
queue[id].push(() =>
|
|
118
|
+
unmount: () => {
|
|
144
119
|
log({
|
|
120
|
+
logo: true,
|
|
145
121
|
message: [
|
|
146
122
|
['bold', storeName],
|
|
147
|
-
['regular', 'store
|
|
148
|
-
['bold', actionName]
|
|
123
|
+
['regular', 'store was unmounted']
|
|
149
124
|
],
|
|
150
|
-
type: '
|
|
151
|
-
value: {
|
|
152
|
-
message: error.message
|
|
153
|
-
}
|
|
125
|
+
type: 'unmount'
|
|
154
126
|
})
|
|
155
|
-
)
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
onEnd(() => {
|
|
159
|
-
for (let i of queue[id]) i()
|
|
160
|
-
delete queue[id]
|
|
161
|
-
groupEnd()
|
|
162
|
-
})
|
|
163
|
-
})
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function handleSet(store, storeName, queue, messages, ignoreActions) {
|
|
167
|
-
return onSet(store, ({ changed }) => {
|
|
168
|
-
let currentActionId = store[actionId]
|
|
169
|
-
let currentActionName = store[lastAction]
|
|
170
|
-
|
|
171
|
-
if (messages.action === false && currentActionId) return
|
|
172
|
-
if (ignoreActions && ignoreActions.includes(currentActionName)) return
|
|
173
|
-
|
|
174
|
-
let groupLog = {
|
|
175
|
-
logo: typeof currentActionId === 'undefined',
|
|
176
|
-
message: [
|
|
177
|
-
['bold', storeName],
|
|
178
|
-
['regular', 'store was changed']
|
|
179
|
-
],
|
|
180
|
-
type: 'change'
|
|
181
|
-
}
|
|
182
|
-
if (changed) {
|
|
183
|
-
groupLog.message.push(
|
|
184
|
-
['regular', 'in the'],
|
|
185
|
-
['bold', changed],
|
|
186
|
-
['regular', 'key']
|
|
187
|
-
)
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
let oldValue = isAtom(store) ? store.value : { ...store.value }
|
|
191
|
-
oldValue = isDeepMapKey(changed) ? structuredClone(oldValue) : oldValue
|
|
192
|
-
let unbindNotify = onNotify(store, () => {
|
|
193
|
-
let newValue = store.value
|
|
194
|
-
let valueMessage
|
|
195
|
-
if (changed && !isDeepMapKey(changed)) {
|
|
196
|
-
valueMessage = `${oldValue[changed]} → ${newValue[changed]}`
|
|
197
127
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
if (valueMessage) {
|
|
202
|
-
log({
|
|
203
|
-
message: valueMessage,
|
|
204
|
-
type: 'value'
|
|
205
|
-
})
|
|
206
|
-
}
|
|
207
|
-
log({
|
|
208
|
-
type: 'new',
|
|
209
|
-
value: newValue
|
|
210
|
-
})
|
|
211
|
-
if (oldValue) {
|
|
212
|
-
log({
|
|
213
|
-
type: 'old',
|
|
214
|
-
value: oldValue
|
|
215
|
-
})
|
|
216
|
-
}
|
|
217
|
-
groupEnd()
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (currentActionId) {
|
|
221
|
-
queue[currentActionId].push(run)
|
|
222
|
-
} else {
|
|
223
|
-
run()
|
|
224
|
-
}
|
|
225
|
-
unbindNotify()
|
|
226
|
-
})
|
|
227
|
-
})
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
function createLogger(store, storeName, opts) {
|
|
231
|
-
let ignoreActions = opts.ignoreActions
|
|
232
|
-
let messages = opts.messages || {}
|
|
233
|
-
let unbind = []
|
|
234
|
-
let queue = {}
|
|
235
|
-
|
|
236
|
-
if (messages.mount !== false || messages.unmount !== false) {
|
|
237
|
-
unbind.push(handleMount(store, storeName, messages))
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
if (messages.action !== false) {
|
|
241
|
-
unbind.push(handleAction(store, storeName, queue, ignoreActions))
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
if (messages.change !== false) {
|
|
245
|
-
unbind.push(handleSet(store, storeName, queue, messages, ignoreActions))
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
return () => {
|
|
249
|
-
for (let i of unbind) i()
|
|
250
|
-
}
|
|
128
|
+
},
|
|
129
|
+
opts
|
|
130
|
+
)
|
|
251
131
|
}
|
|
252
132
|
|
|
253
133
|
export function logger(stores, opts = {}) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nanostores/logger",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Pretty logger of lifecycles, changes and actions for Nano Stores",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nanostores",
|
|
7
7
|
"devtools",
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
type LogType =
|
|
2
|
+
| 'action'
|
|
3
|
+
| 'arguments'
|
|
4
|
+
| 'build'
|
|
5
|
+
| 'change'
|
|
6
|
+
| 'error'
|
|
7
|
+
| 'mount'
|
|
8
|
+
| 'new'
|
|
9
|
+
| 'old'
|
|
10
|
+
| 'unmount'
|
|
11
|
+
| 'value'
|
|
12
|
+
| {
|
|
13
|
+
color: string
|
|
14
|
+
name: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type LogMessageStyle = 'bold' | 'regular'
|
|
18
|
+
|
|
19
|
+
interface CreateLogOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Whether to display the Nano Stores logo.
|
|
22
|
+
*/
|
|
23
|
+
logo?: boolean
|
|
24
|
+
/**
|
|
25
|
+
* Message in string or array format to be logged.
|
|
26
|
+
*/
|
|
27
|
+
message?: [LogMessageStyle, string][] | string
|
|
28
|
+
/**
|
|
29
|
+
* The type of log.
|
|
30
|
+
*/
|
|
31
|
+
type: LogType
|
|
32
|
+
/**
|
|
33
|
+
* Any value, object or array to be logged.
|
|
34
|
+
*/
|
|
35
|
+
value?: any
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Creates a log message.
|
|
40
|
+
*
|
|
41
|
+
* @param options Object with the options to create the log message.
|
|
42
|
+
*/
|
|
43
|
+
export function log(options: CreateLogOptions): void
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Creates a collapsed log message.
|
|
47
|
+
*
|
|
48
|
+
* @param options Object with the options to create the log message.
|
|
49
|
+
*/
|
|
50
|
+
export function group(options: CreateLogOptions): void
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Exits the current collapsed log message.
|
|
54
|
+
*/
|
|
55
|
+
export function groupEnd(): void
|
package/printer/index.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
function badge(color) {
|
|
2
|
+
return `
|
|
3
|
+
padding: 0 5px 2px;
|
|
4
|
+
margin-right: 5px;
|
|
5
|
+
font-weight: 400;
|
|
6
|
+
color: white;
|
|
7
|
+
background-color: ${color};
|
|
8
|
+
`
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function borders(full) {
|
|
12
|
+
return `border-radius: ${full ? '4px' : '0 4px 4px 0'};`
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const STYLES = {
|
|
16
|
+
badges: {
|
|
17
|
+
action: badge('#00899A'),
|
|
18
|
+
arguments: badge('#007281'),
|
|
19
|
+
build: badge('#BB5100'),
|
|
20
|
+
change: badge('#0E8A00'),
|
|
21
|
+
error: badge('#C30000'),
|
|
22
|
+
mount: badge('#0059D1'),
|
|
23
|
+
new: badge('#0C7800'),
|
|
24
|
+
old: badge('#943636'),
|
|
25
|
+
unmount: badge('#5E5E5E'),
|
|
26
|
+
value: badge('#8A6F00')
|
|
27
|
+
},
|
|
28
|
+
bold: 'font-weight: 700;',
|
|
29
|
+
logo: `
|
|
30
|
+
padding: 0 5px 2px;
|
|
31
|
+
color: white;
|
|
32
|
+
background-color: black;
|
|
33
|
+
border-radius: 4px 0 0 4px;
|
|
34
|
+
`,
|
|
35
|
+
regular: 'font-weight: 400;'
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function createLog({ logo, message, type, value }) {
|
|
39
|
+
let template = ''
|
|
40
|
+
let args = []
|
|
41
|
+
|
|
42
|
+
if (logo) {
|
|
43
|
+
template = `%c𝖓`
|
|
44
|
+
args.push(STYLES.logo)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (typeof type === 'string') {
|
|
48
|
+
template += `%c${type}`
|
|
49
|
+
args.push(STYLES.badges[type] + borders(!logo))
|
|
50
|
+
} else if (typeof type === 'object') {
|
|
51
|
+
template += `%c${type.name.toLowerCase()}`
|
|
52
|
+
args.push(badge(type.color) + borders(!logo))
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (message) {
|
|
56
|
+
if (Array.isArray(message)) {
|
|
57
|
+
message.forEach(([style, text]) => {
|
|
58
|
+
template += `%c ${text}`
|
|
59
|
+
args.push(STYLES[style])
|
|
60
|
+
})
|
|
61
|
+
} else {
|
|
62
|
+
template += `%c ${message}`
|
|
63
|
+
args.push(STYLES.text)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (value) {
|
|
68
|
+
args.push(value)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
args.unshift(template)
|
|
72
|
+
|
|
73
|
+
return args
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const log = args => console.log(...createLog(args))
|
|
77
|
+
export const group = args => console.groupCollapsed(...createLog(args))
|
|
78
|
+
export const groupEnd = () => console.groupEnd()
|