/
rorre.js
132 lines (111 loc) · 3.54 KB
/
rorre.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/* eslint-disable class-methods-use-this */
/* eslint-disable default-case */
/* eslint-disable max-classes-per-file */
/* eslint-disable no-underscore-dangle */
let _DICTIONARY;
let _ERROR;
let _NAME;
/**
* RorreError customizes the default Error:
* - make #name property compulsory (nonexistent in Node and only optional in a browser),
* - add an #index property in order to generate error indexes within Rorre dictionary.
*/
class RorreError extends Error {
constructor(message, name) {
switch (true) {
case typeof message !== "string" || message.length === 0:
throw new Error(`RorreError(): The <message> must be a non-empty string.`);
case typeof name !== "string" || name.length === 0:
throw new Error(`RorreError(): The <name> must be a non-empty string.`);
}
super(message);
this.name = name;
}
}
class Rorre {
/**
* Get the complete error dictionary indexed object.
*
* @description
* This is an enumed mapping: for each error, both its #message
* and generated #index exist as a key, and a value as well.
*/
get dictionary() {
if (_DICTIONARY === undefined) {
throw new Error(
`Rorre#dictionary: You need to declare your dictionary first, in order to call this getter.`
);
}
return _DICTIONARY;
}
/**
* Instanciate (but do NOT throw) a RorreError and return it.
*/
get error() {
if (_DICTIONARY === undefined) {
throw new Error(
`Rorre#code: You need to declare your dictionary first, in order to call this getter.`
);
}
return _ERROR;
}
/**
* Get an enum of the dictionary errors' name.
*
* @description
* This is a reverse mapping: for each error, both its #name
* and generated #index exist as a key, and a value as well.
*/
get name() {
if (_DICTIONARY === undefined) {
throw new Error(
`Rorre#code: You need to declare your dictionary first, in order to call this getter.`
);
}
return _NAME;
}
}
const customExports = {
/**
* Declare the complete errors dictionary.
*
* @description
* This method can and must only be called once.
*/
declare: dictionary => {
switch (true) {
case _DICTIONARY !== undefined:
return Object.seal(new Rorre());
case Object.prototype.toString.call(dictionary) !== "[object Object]":
throw new Error(`Rorre#declare(): Your <dictionary> must be a pure object: { ... }.`);
case Object.entries(dictionary).length === 0:
throw new Error(`Rorre#declare(): Your <dictionary> can't be empty.`);
case Object.entries(dictionary).filter(([, m]) => typeof m !== "string" || m.length === 0)
.length !== 0:
throw new Error(
`Rorre#declare(): Your <dictionary> values (= messages) must be non-empty strings.`
);
}
// Iitialize the "private properties"
_DICTIONARY = {};
_ERROR = {};
_NAME = {};
// Fill the "private properties"
// eslint-disable-next-line guard-for-in, no-restricted-syntax
for (const name in dictionary) {
_NAME[name] = name;
_DICTIONARY[name] = dictionary[name];
Object.defineProperty(_ERROR, name, {
get: () => new RorreError(dictionary[name], name)
});
}
// Freeze the "private properties"
_DICTIONARY = Object.freeze(_DICTIONARY);
_ERROR = Object.freeze(_ERROR);
_NAME = Object.freeze(_NAME);
return Object.seal(new Rorre());
}
};
// Enable Typescript default importation
customExports.default = customExports;
module.exports = Object.seal(customExports);